sam-4screen-desktop 2026-7-29:12:44:15

This commit is contained in:
2026-07-29 12:44:15 +10:00
parent 0f67846c95
commit fab7b02dc3

View File

@@ -1,165 +1,265 @@
# Pi Dashboard — Decisive Build Plan # Pi Dashboard — Decisive Build Plan
## Decision 1: Architecture — SSH Pull (Phase 1) → HTTP Collector (Phase 2) ## Architecture Overview
**Decision: Phase 1 uses SSH pull. Phase 2 adds HTTP collector on .13.** ```
┌─────────────────────────────────────────────────────────────────┐
│ pi sessions (each machine) │
│ │
│ pi (with dashboard.ts extension) │
│ ├── agent_start → track: status, model, duration, tokens │
│ ├── agent_end → notify: NTFY/Apprise + mark complete │
│ ├── tool_call → track: last tool, tokens consumed │
│ ├── blocked → mark: waiting, save prompt text │
│ └── register_task / complete_task → write to todo.txt │
│ │ │
│ └── Writes to: │
│ ~/.pi/agent/dashboard/<session>.json (agent state) │
│ ~/.pi/agent/dashboard/tasks/todo.txt (task list) │
│ │
│ On agent_end / blocked: │
│ └── curl → NTFY server (phone notification) │
│ └── apprise → Slack/Telegram/email (configured routes) │
└─────────────────────────────────────────────────────────────────┘
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ pi-dashboard viewer (Go + Bubble Tea) │
│ │
│ Reads: ~/.pi/agent/dashboard/*.json (local + SSH remote) │
│ Renders: agent status table with cost, duration, location │
│ Controls: │
│ Enter → tmux attach -t <session> (jump to agent) │
│ Tab → switch to Tuxedo (task list in todo.txt) │
│ d → hunk diff (review agent's worktree changes) │
│ q → quit │
│ │
│ Config: ~/.config/pi-dashboard.yaml │
│ (local paths, SSH remotes, polling interval) │
└─────────────────────────────────────────────────────────────────┘
Rationale: SSH pull requires zero infrastructure. Every machine already has SSH set up. The Go binary SSHes into each machine and reads state files. Later, the HTTP collector on .13 (alongside OmniRoute) becomes the single source of truth — extensions POST state to it, the viewer reads from it. This gives resilience when machines sleep. ┌─────────────────────────────────────────────────────────────────┐
│ Companion tools (existing, not building) │
│ │
│ Tuxedo → TUI todo.txt manager │
│ Reads ~/.pi/agent/dashboard/tasks/todo.txt │
│ Vim keys, phone capture QR, project/context filters │
│ │
│ Hunk → TUI diff viewer │
│ hunk diff --watch → review agent changes live │
│ hunk show → review last commit from agent │
│ │
│ NTFY → Push notification server (already running) │
│ curl → ntfy.sh/pi-alerts → phone notification │
│ │
│ Apprise → Multi-route notification dispatcher │
│ One command → Slack + Telegram + Email + NTFY │
└─────────────────────────────────────────────────────────────────┘
```
## Decision 2: TUI Tech Stack — Go + Bubble Tea ## Key Decisions
**Decision: Go binary, Bubble Tea v2 for rendering.** | Decision | Choice | Why |
|----------|--------|-----|
| State transfer | SSH pull Phase 1 → HTTP collector on .13 Phase 2 | Zero infra to start, resilient later |
| TUI framework | Go + Bubble Tea | Single binary, fast, portable |
| Web UI | Phase 2, optional | SSH from Termux works for Phase 1 |
| Task board | **Use Tuxedo** — don't build custom | Tuxedo is already a polished todo.txt TUI with phone capture, vim keys, filters |
| Diff review | **Use Hunk** — don't build custom | Hunk is purpose-built for reviewing agent changesets |
| Notifications | **Use NTFY + Apprise** — don't build custom | Both already available, extension just calls `curl` / `apprise` |
| Polling | 2s intervals, no file watcher | Simple, no edge cases |
| Session naming | `pi-<role>` (pi-work, pi-explore, etc.) | Consistent jump target |
| Herdr | Try alongside, no special integration | Dashboard is multiplexer-agnostic |
Rationale: Single portable binary (~10MB), instant startup, no npm/Node dependency. The viewer runs from `~/bin/pi-dashboard` on any machine, including Termux. Bubble Tea is actively maintained (v2.0.8, July 2026). The viewer is simple enough (~400 lines) that Go's learning curve isn't a barrier. ## What Gets Built (Phase 1)
## Decision 3: Web UI — Phase 2, Optional ### 1. `~/.pi/agent/extensions/dashboard.ts` (~150 lines)
**Decision: Not building in Phase 1. Termux SSH → TUI binary is the mobile path.** Pi extension that collects state and writes files.
Rationale: SSH from Termux renders the TUI perfectly. Web UI is a convenience for glanceable status from a browser — nice but not essential. If built in Phase 2, it's a small Svelte/Vue app served by the dashboard binary itself (`pi-dashboard --web` opens :9877). **Event hooks:**
- `agent_start` → record: agent type, model, timestamp, status=running
- `agent_end` → record: duration, token count, cost, status=done
- `tool_call` → record: tool name, duration, input/output tokens (for cost)
## Decision 4: Task List Integration — Yes, Phase 1 **Blocked detection:**
- Intercept `ctx.ui.confirm()`, `ctx.ui.select()`, `ctx.ui.input()` calls
- When these fire and return is pending → mark status=blocked, save the prompt text
**Decision: The dashboard shows a task board alongside agent status.** **Task integration:**
- Register tool `register_task(title, project, assignee)` → append line to todo.txt
- Register tool `complete_task(id)` → mark line as done in todo.txt
- Register tool `list_tasks()` → read todo.txt, return formatted list
Rationale: The sub-agent system already has TaskCreate/TaskUpdate/TaskList/TaskExecute. The extension hooks into these and mirrors task state. The dashboard renders them as a tiered list sorted by status. Sub-agents call `complete_task()` via tool calls. This makes the dashboard a work tracker, not just a status panel. **Notifications (Phase 1):**
- On `agent_end`: spawn `curl` to NTFY server with completion summary
- On blocked: spawn `curl` to NTFY with blocking prompt and session name
- On error: spawn `curl` to NTFY with error details
## Decision 5: Polling — 2-second interval, no file watcher **State file format (`<session>.json`):**
```json
{
"session": "pi-work",
"machine": "nixos-desktop",
"host": "192.168.20.13",
"agent_type": "coder-basic",
"model": "opencode-go/deepseek-v4-flash",
"status": "running",
"started_at": "2026-07-27T10:23:00Z",
"current_task": "Refactor auth middleware",
"duration_seconds": 742,
"tokens_in": 28100,
"tokens_out": 20100,
"cost": 0.09,
"last_tool": "edit",
"last_tool_at": "2026-07-27T10:34:22Z",
"blocked": false,
"blocked_prompt": null,
"connection_type": "tmux",
"connection_name": "pi-work",
"worktree": "/tmp/pi/worktrees/auth-refactor/"
}
```
**Decision: Poll state directory every 2 seconds.** ### 2. `~/bin/pi-dashboard` (Go + Bubble Tea, ~400 lines)
Rationale: Simpler and more robust than fsnotify file watchers (which miss mid-write files, have edge cases on NFS/SSH mounts). A 2-second poll is invisible to the user and uses negligible CPU. Standalone TUI binary.
## Decision 6: Session Naming — `pi-<role>` convention **Rendering:** Agent status table with columns:
**Decision: tmux sessions named `pi-work`, `pi-explore`, `pi-research`, etc.** ```
╔══════════════════════════════════════════════════════════════════╗
║ ⬡ Pi Dashboard 3 machines · 5 agents ║
║ Total: $0.52 · Running: 2 · Blocked: 1 · Done: 2 ║
╠══════════════════════════════════════════════════════════════════╣
║ MACH SESSION TYPE MODEL STATUS DUR $ ║
║ ───────────────────────────────────────────────────────────────║
║ .27 pi-work coder oc/dsv4-flash ● run 12:34 0.09 ║
║ .27 pi-explore explore oc/dsv4-flash ● run 03:12 0.02 ║
║ .13 pi-resrch rsrc gemini-flash ○ idle 00:47 0.00 ║
║ .51 pi-bugfix coder ds/v4-pro ⚠ blk 01:23 0.11 ║
║ └─ ⚡ Allow rm -rf src/? (y/N) ║
╠══════════════════════════════════════════════════════════════════╣
║ [Enter] Attach [d] Hunk diff [Tab] Tuxedo [r] Refresh [q] ║
╚══════════════════════════════════════════════════════════════════╝
```
Rationale: The dashboard uses the session name as the connection key. Enter → `tmux attach -t pi-work`. Consistent naming lets the viewer jump without config. **Keyboard controls:**
## Decision 7: Herdr — Try alongside, dashboard is multiplexer-agnostic | Key | Action | Implementation |
|-----|--------|----------------|
| `↑↓` | Select agent | Bubble Tea list model |
| `Enter` | Attach to agent | `os.Exec("tmux attach -t <session>")` or `ssh -t user@host tmux attach -t <session>` |
| `d` | Hunk diff | `os.Exec("cd <worktree> && hunk diff --watch")` |
| `Tab` | Launch Tuxedo | `os.Exec("tuxedo --file ~/.pi/agent/dashboard/tasks/todo.txt")` |
| `r` | Force refresh | Clear cache, re-poll |
| `q` | Quit | Exit |
**Decision: The dashboard works with any multiplexer (tmux, Zellij, Herdr, none). No Herdr-specific features in Phase 1.** **Config file (`~/.config/pi-dashboard.yaml`):**
```yaml
poll_interval: 2s
Rationale: The dashboard reads state files and shows connection info. It doesn't care what runs the agent. Herdr can be tried in parallel on WS 2 without affecting the dashboard. local:
path: /home/sam/.pi/agent/dashboard/
--- remote:
- host: 192.168.20.13
user: sam
path: /home/sam/.pi/agent/dashboard/
- host: 192.168.20.51
user: sam
path: /home/sam/.pi/agent/dashboard/
```
## What Each Component Is ## What You DON'T Build
### 1. `~/.pi/agent/extensions/dashboard.ts` | Feature | Handled by | Why not build it |
|---------|-----------|------------------|
A pi extension. One file. Runs inside each pi session. | Task board UI | **Tuxedo** | Already a polished todo.txt TUI with vim keys, phone capture, project/context filters. The extension just writes tasks to a standard format. |
| Diff review | **Hunk** | Already purpose-built for reviewing agent changesets with watch mode, AI annotations, split/stack layouts. |
**What it does:** | Push notifications | **NTFY** | Already running. Extension just POSTs to it. |
- Listens to `agent_start` / `agent_end` / `tool_call` events | Multi-route notifications | **Apprise** | Already available. Extension calls `apprise -b "msg"` to fan-out to Slack/Telegram/email. |
- Accumulates: agent type, model, status, duration, token count, last tool call | Web UI | **Phase 2** | TUI + SSH from Termux covers Phase 1. |
- Detects blocked state (when `ctx.ui.confirm()` or `ctx.ui.select()` fires and waits)
- Tracks tasks via tool calls (`register_task`, `complete_task`)
- Writes `~/.pi/agent/dashboard/<session-name>.json` on each event change
- Optionally: POSTs state to HTTP collector on .13 (Phase 2)
**Not needed:** Any TUI rendering. The extension just collects and writes.
### 2. `~/bin/pi-dashboard` (Go binary)
Standalone TUI. One binary.
**What it does:**
- Reads all `~/.pi/agent/dashboard/*.json` (local + SSH from remote machines)
- Renders interactive table with agent status, tasks, costs
- Keyboard controls: navigate, attach, filter, quit
- `Enter` → `tmux attach` or shows Zellij tab/pane location
- Polls every 2s for changes
**Not needed:** HTTP server in Phase 1.
### 3. `.pi/agent/mcp.json` entry (optional, Phase 2)
If the HTTP collector exists, the viewer can also be an MCP client querying the collector.
---
## Implementation Order ## Implementation Order
### Step 1: dashboard.ts extension ### Step 1: dashboard.ts extension (~1.5 hours)
- [x] Correctly identify that the state file avoids reading the existing subagent code - Write `~/.pi/agent/extensions/dashboard.ts`
- [ ] Write the extension in `~/.pi/agent/extensions/dashboard.ts` - Add event listeners: `agent_start`, `agent_end`, `tool_call`
- [ ] Add event handlers: `agent_start`, `agent_end`, `tool_call` - Add task tool registrations: `register_task`, `complete_task`, `list_tasks`
- [ ] Add blocked detection (intercept `ctx.ui.select/confirm/input`) - Add blocked detection via `ui.custom` wrappers
- [ ] Add task tracking via tool registration - Add NTFY/Apprise notification on `agent_end` and blocked
- [ ] Write state to `~/.pi/agent/dashboard/<session>.json` atomically - Write state atomically to `~/.pi/agent/dashboard/<session>.json`
- [ ] Test: launch a pi session, verify state file updates - Write tasks to `~/.pi/agent/dashboard/tasks/todo.txt`
- [ ] Commit to Gitea - Test with one pi session
- Commit to Gitea, pull on .13 and .51
### Step 2: Go TUI viewer ### Step 2: Go TUI viewer (~3 hours)
- [ ] Initialize Go module at `~/src/pi-dashboard/` - Init Go module at `~/src/pi-dashboard/`
- [ ] Add Bubble Tea dependency - Implement state file reader (local + SSH with `golang.org/x/crypto/ssh`)
- [ ] Implement state file reader (local + SSH) - Implement YAML config reader
- [ ] Render agent table with colors and status indicators - Build Bubble Tea model with agent list (bubbletea + bubbles list)
- [ ] Add keyboard controls (nav, attach, filter, quit) - Add keyboard controls
- [ ] Add polling loop (2s interval) - Build binary to `~/bin/pi-dashboard`
- [ ] Build binary to `~/bin/pi-dashboard` - Test with 2-3 background pi sessions on .27
- [ ] Test: launch 2-3 background pi sessions, verify dashboard shows them
### Step 3: Cross-machine (SSH pull) ### Step 3: Cross-machine + polish (~2 hours)
- [ ] Add `~/.config/pi-dashboard.yaml` config file support - Test SSH pull from .51 and .13
- [ ] Implement SSH state reader with timeout and error handling - Handle timeouts, unreachable machines, auth errors gracefully
- [ ] Test: verify agents on .51 and .13 appear in .27's dashboard - Test `Enter` jumping to remote agents (ssh -t)
- [ ] Test: `Enter` on remote agent → SSH + tmux attach - Test `d` → Hunk diff opens correctly
- Test `Tab` → Tuxedo opens with task file
### Step 4: Task list view ### Step 4: Try Herdr (~1 hour)
- [ ] Add task board panel (toggle with Tab) - `nix shell nixpkgs#herdr` or download
- [ ] Show tasks grouped by status (pending / in_progress / done) - Create simple Herdr layout for WS 2
- [ ] Show which agent is assigned to which task - Run pi session inside Herdr
- [ ] Integrate with existing TaskCreate/TaskUpdate APIs in pi - Verify dashboard picks it up
- Evaluate whether migration from Zellij is worth it
### Step 5: Try Herdr ### Step 5: HTTP collector + web UI (~2 hours, optional Phase 2)
- [ ] `nix shell nixpkgs#herdr` or download binary - Add POST to collector endpoint in dashboard.ts
- [ ] Create a simple Herdr layout for WS 2 - Write collector (Go, simple HTTP server)
- [ ] Run a pi session inside Herdr - Update viewer to try collector first, fall back to SSH
- [ ] Verify dashboard picks it up from state files - If web UI wanted: Svelte frontend served by collector
- [ ] Evaluate: is the pain of migration worth Herdr's native visibility?
### Step 6: HTTP collector + web UI (Phase 2, optional)
- [ ] Add POST endpoint to dashboard.ts extension
- [ ] Write collector server (Go, small binary on .13)
- [ ] Update viewer to prefer collector over SSH
- [ ] If web UI wanted: Svelte frontend served by collector
---
## Files to Create ## Files to Create
| File | Location | Purpose | | File | Location | Purpose |
|------|----------|---------| |------|----------|---------|
| `dashboard.ts` | `~/.pi/agent/extensions/` | Pi extension: collects state, writes files, tracks tasks | | `dashboard.ts` | `~/.pi/agent/extensions/` | Pi extension: collects state, writes files, tracks tasks, sends notifications |
| `pi-dashboard/` | `~/src/pi-dashboard/` | Go module for the TUI viewer | | `pi-dashboard/main.go` | `~/src/pi-dashboard/` | Entry point |
| `pi-dashboard/main.go` | entry point | Bubble Tea model, view, update | | `pi-dashboard/config.go` | `~/src/pi-dashboard/` | YAML config reader |
| `pi-dashboard/config.go` | config | YAML config reader for local paths + SSH remotes | | `pi-dashboard/state.go` | `~/src/pi-dashboard/` | State file reader + SSH fetcher |
| `pi-dashboard/state.go` | state reader | Reads JSON files, polls, SSH fetch | | `pi-dashboard/tui.go` | `~/src/pi-dashboard/` | Bubble Tea model, view, update |
| `pi-dashboard/tui.go` | TUI rendering | Table layout, colors, keybindings | | `pi-dashboard/go.mod` | `~/src/pi-dashboard/` | Dependencies |
| `pi-dashboard/go.mod` | module | Dependencies |
| `~/.config/pi-dashboard.yaml` | user config | Machine list, paths, polling interval | | `~/.config/pi-dashboard.yaml` | user config | Machine list, paths, polling interval |
| `~/bin/pi-dashboard` | binary | Built Go binary | | `~/bin/pi-dashboard` | binary | Built Go binary |
--- ## Total Effort
## What You Don't Need | Step | Time | What you get |
|------|------|-------------|
| 1. Extension | 1.5h | State files appearing, NTFY notifications on completion |
| 2. TUI viewer | 3h | See all agents in one terminal, attach to any of them |
| 3. Cross-machine | 2h | Agents on .51 and .13 visible from .27 |
| 4. Herdr trial | 1h | Know if Herdr is worth migrating to |
| **Phase 1 total** | **~7.5h** | Full visibility across all machines |
| 5. HTTP + web | 2h | Single source of truth, browser view |
- A database (state files are JSON, small, temporary) ## Prerequisites
- A daemon process (the viewer is stateless, polls files)
- Node.js on the viewer machine (Go binary is self-contained)
- Web framework (Phase 1 is TUI-only)
- Herdr, Orca, Buzz integration (dashboard is agnostic)
--- - ✅ Go (will install via nix)
- ✅ Bubble Tea (go get)
## Open Question for You - ✅ NTFY server (already running)
- ✅ Apprise (already available)
The task list integration: do you want the task board to be a separate screen (Tab to toggle between agent view and task board), or inline within the agent table (tasks shown under each agent row)? - ✅ Tuxedo (will install via brew/nix)
- ✅ Hunk (will install via npm/nix)
- ✅ tmux (already available)
- ✅ SSH (already configured)
- ✅ Gitea (for syncing the extension)