13 KiB
13 KiB
Pi Dashboard — Decisive Build Plan
Architecture Overview
┌─────────────────────────────────────────────────────────────────┐
│ 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) │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 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 │
└─────────────────────────────────────────────────────────────────┘
Key Decisions
| 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 |
What Gets Built (Phase 1)
1. ~/.pi/agent/extensions/dashboard.ts (~150 lines)
Pi extension that collects state and writes files.
Event hooks:
agent_start→ record: agent type, model, timestamp, status=runningagent_end→ record: duration, token count, cost, status=donetool_call→ record: tool name, duration, input/output tokens (for cost)
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
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
Notifications (Phase 1):
- On
agent_end: spawncurlto NTFY server with completion summary - On blocked: spawn
curlto NTFY with blocking prompt and session name - On error: spawn
curlto NTFY with error details
State file format (<session>.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/"
}
2. ~/bin/pi-dashboard (Go + Bubble Tea, ~400 lines)
Standalone TUI binary.
Rendering: Agent status table with columns:
╔══════════════════════════════════════════════════════════════════╗
║ ⬡ 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] ║
╚══════════════════════════════════════════════════════════════════╝
Keyboard controls:
| 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 |
Config file (~/.config/pi-dashboard.yaml):
poll_interval: 2s
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 You DON'T Build
| 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 (~1.5 hours)
- 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.customwrappers - Add NTFY/Apprise notification on
agent_endand 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 (~3 hours)
- 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 + polish (~2 hours)
- Test SSH pull from .51 and .13
- Handle timeouts, unreachable machines, auth errors gracefully
- Test
Enterjumping to remote agents (ssh -t) - Test
d→ Hunk diff opens correctly - Test
Tab→ Tuxedo opens with task file
Step 4: Try Herdr (~1 hour)
nix shell nixpkgs#herdror 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: HTTP collector + web UI (~2 hours, optional Phase 2)
- 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, 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
| 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 |
Prerequisites
- ✅ 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)