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
## 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`
A pi extension. One file. Runs inside each pi session.
**What it does:**
- Listens to `agent_start` / `agent_end` / `tool_call` events
- Accumulates: agent type, model, status, duration, token count, last tool call
- 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.
---
| Feature | Handled by | Why not build it |
|---------|-----------|------------------|
| 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. |
| Push notifications | **NTFY** | Already running. Extension just POSTs to it. |
| Multi-route notifications | **Apprise** | Already available. Extension calls `apprise -b "msg"` to fan-out to Slack/Telegram/email. |
| Web UI | **Phase 2** | TUI + SSH from Termux covers Phase 1. |
## 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 the extension in `~/.pi/agent/extensions/dashboard.ts`
- [ ] Add event handlers: `agent_start`, `agent_end`, `tool_call`
- [ ] Add blocked detection (intercept `ctx.ui.select/confirm/input`)
- [ ] Add task tracking via tool registration
- [ ] Write state to `~/.pi/agent/dashboard/<session>.json` atomically
- [ ] Test: launch a pi session, verify state file updates
- [ ] Commit to Gitea
- Write `~/.pi/agent/extensions/dashboard.ts`
- Add event listeners: `agent_start`, `agent_end`, `tool_call`
- Add task tool registrations: `register_task`, `complete_task`, `list_tasks`
- Add blocked detection via `ui.custom` wrappers
- Add NTFY/Apprise notification on `agent_end` and blocked
- Write state atomically to `~/.pi/agent/dashboard/<session>.json`
- Write tasks to `~/.pi/agent/dashboard/tasks/todo.txt`
- 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/`
- [ ] Add Bubble Tea dependency
- [ ] Implement state file reader (local + SSH)
- [ ] Render agent table with colors and status indicators
- [ ] Add keyboard controls (nav, attach, filter, quit)
- [ ] Add polling loop (2s interval)
- [ ] Build binary to `~/bin/pi-dashboard`
- [ ] Test: launch 2-3 background pi sessions, verify dashboard shows them
- Init Go module at `~/src/pi-dashboard/`
- Implement state file reader (local + SSH with `golang.org/x/crypto/ssh`)
- Implement YAML config reader
- Build Bubble Tea model with agent list (bubbletea + bubbles list)
- Add keyboard controls
- Build binary to `~/bin/pi-dashboard`
- Test with 2-3 background pi sessions on .27
### Step 3: Cross-machine (SSH pull)
### Step 3: Cross-machine + polish (~2 hours)
- [ ] Add `~/.config/pi-dashboard.yaml` config file support
- [ ] Implement SSH state reader with timeout and error handling
- [ ] Test: verify agents on .51 and .13 appear in .27's dashboard
- [ ] Test: `Enter` on remote agent → SSH + tmux attach
- Test SSH pull from .51 and .13
- Handle timeouts, unreachable machines, auth errors gracefully
- Test `Enter` jumping to remote agents (ssh -t)
- 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)
- [ ] Show tasks grouped by status (pending / in_progress / done)
- [ ] Show which agent is assigned to which task
- [ ] Integrate with existing TaskCreate/TaskUpdate APIs in pi
- `nix shell nixpkgs#herdr` or download
- Create simple Herdr layout for WS 2
- Run pi session inside Herdr
- 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
- [ ] Create a simple Herdr layout for WS 2
- [ ] Run a pi session inside Herdr
- [ ] Verify dashboard picks it up from state files
- [ ] 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
---
- Add POST to collector endpoint in dashboard.ts
- Write collector (Go, simple HTTP server)
- Update viewer to try collector first, fall back to SSH
- If web UI wanted: Svelte frontend served by collector
## Files to Create
| File | Location | Purpose |
|------|----------|---------|
| `dashboard.ts` | `~/.pi/agent/extensions/` | Pi extension: collects state, writes files, tracks tasks |
| `pi-dashboard/` | `~/src/pi-dashboard/` | Go module for the TUI viewer |
| `pi-dashboard/main.go` | entry point | Bubble Tea model, view, update |
| `pi-dashboard/config.go` | config | YAML config reader for local paths + SSH remotes |
| `pi-dashboard/state.go` | state reader | Reads JSON files, polls, SSH fetch |
| `pi-dashboard/tui.go` | TUI rendering | Table layout, colors, keybindings |
| `pi-dashboard/go.mod` | module | Dependencies |
| `dashboard.ts` | `~/.pi/agent/extensions/` | Pi extension: collects state, writes files, tracks tasks, sends notifications |
| `pi-dashboard/main.go` | `~/src/pi-dashboard/` | Entry point |
| `pi-dashboard/config.go` | `~/src/pi-dashboard/` | YAML config reader |
| `pi-dashboard/state.go` | `~/src/pi-dashboard/` | State file reader + SSH fetcher |
| `pi-dashboard/tui.go` | `~/src/pi-dashboard/` | Bubble Tea model, view, update |
| `pi-dashboard/go.mod` | `~/src/pi-dashboard/` | Dependencies |
| `~/.config/pi-dashboard.yaml` | user config | Machine list, paths, polling interval |
| `~/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)
- 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)
## Prerequisites
---
## Open Question for You
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)?
- ✅ Go (will install via nix)
- ✅ Bubble Tea (go get)
- ✅ NTFY server (already running)
- ✅ Apprise (already available)
- ✅ Tuxedo (will install via brew/nix)
- ✅ Hunk (will install via npm/nix)
- ✅ tmux (already available)
- ✅ SSH (already configured)
- ✅ Gitea (for syncing the extension)