- Add tui/main.go, tui/go.mod, tui/go.sum (Go + Bubble Tea TUI) - Add CLAUDE.md with project instructions - Add bin/trailboss-preview script - Update bin/trailboss, bin/trailboss-start, bin/trailboss-watch - Update PROGRESS.md and docs/plan/plan.md to reflect completion - Add .gitignore entries for build artifacts (tui/tui, bin/trailboss-tui) - Remove stray root main.go placeholder and ~ directory Bead-Id: tb-25j Co-Authored-By: Claude <noreply@anthropic.com>
7.7 KiB
Trail Boss — Marathon Progress
Phase 1: Probe PermissionRequest ✅ COMPLETE
Done: Confirmed PermissionRequest fires for gate types and captured payload shape.
Findings documented in docs/research/claude-code-mechanics.md:
PermissionRequestfires when a tool requires permission (tested withEdit)- Payload includes
tool_name+tool_input— exactly what the queue needs to display the proposed operation $TMUX_PANEis available in hook environment (confirmed via%461in probe)permission_mode,session_id,transcript_path,cwdall present- Permission block emits
PermissionRequestand noStop(confirmed)
Phase 2: Emitter ✅ COMPLETE
Done: Built trailboss-emit.sh that forwards hook payloads to collector and injects $TMUX_PANE. Wired all hooks in .claude/settings.json.
Verified: Stub collector logged POSTs with session_id, cwd, and pane_id. Demonstrated bare curl drops $TMUX_PANE — proving the wrapper is required.
Files:
.claude/trailboss-emit.sh— emitter script.claude/settings.json— hook wiring
Phase 3: Daemon (IN PROGRESS)
Goal: Build the daemon with ingest endpoint, SQLite state, self-healing registry, transcript reconcile loop, and FIFO queue.
Done:
- Chose Bun + SQLite stack
- Designed normalized stuck/unstuck adapter contract (claude-adapter.ts)
- Implemented ingest endpoint on loopback only (127.0.0.1:4000/event)
- Implemented SQLite state with session_id → pane registry
- Implemented transcript reconcile loop (runs every 5s)
- Implemented FIFO queue with /next and /skip endpoints
- Added /status and /queue endpoints for health and listing
Verified via synthetic event testing:
- Sessions enter queue on Stop/PermissionRequest and leave on UserPromptSubmit
- Second event with new pane updates registry (self-heal)
- Reconcile loop dequeues sessions whose transcript advanced
- GET /next returns oldest-ready pane id (respects skip cooldown)
- State survives daemon restart
Files:
daemon/index.ts— main HTTP serverdaemon/types.ts— normalized event typesdaemon/claude-adapter.ts— Claude Code hook adapterdaemon/db.ts— SQLite state layerdaemon/reconcile.ts— transcript reconcile loopdaemon/schema.sql— database schemapackage.json— dependenciestest-daemon.sh— basic synthetic event teststest-daemon-phase3.sh— Phase 3 exit criteria tests
Tech stack decision: Bun + SQLite
- Fast startup, low memory footprint
- Built-in SQLite support (no native module compilation)
- Modern TypeScript/JavaScript ecosystem
- Single-file deployment easy
Next:
- Phase 4: Navigation — implement trailboss jump-next that switches to the pane
Phase 4: Navigation ✅ COMPLETE
Done: Implemented trailboss CLI with jump-next and skip commands.
Verified:
jump-nextqueries/nextand navigates viaswitch-client/select-window/select-paneskipposts/skipthen jumps to new head- Exit criterion passed: navigation lands operator on pane returned by
/next
Files:
bin/trailboss— main CLI (jump-next, skip, popup stub)test-navigation.sh— synthetic event test verifying navigation
Next:
- Phase 5: Presentation — tmux keybindings, display-popup queue picker, status-line segment
Phase 5: Presentation ✅ COMPLETE
Done: Implemented presentation layer — popup queue picker, keybindings, and status-line segment.
Verified:
trailboss popupdisplays FIFO list with index-based selection- Keybindings configured (prefix+Tab for Next, prefix+s for Skip, prefix+g for Popup)
- Status-line segment shows "⚠ N" when stuck sessions exist
- Exit criterion passed: all presentation components work
Files:
bin/trailboss-popup— queue picker with python3 JSON parsing, box drawing UIbin/trailboss-status— status-line segment showing stuck counttmux.conf— keybindings for Next/Skip/Popup
Next:
- Phase 6: Walking Skeleton — end-to-end acceptance scenarios (AS-1 through AS-7)
Phase 6: Walking Skeleton ✅ COMPLETE
Done: All 7 acceptance scenarios pass end-to-end.
Verified:
test-walking-skeleton.shpasses all scenarios (AS-1 through AS-7)- AS-1: Single permission block — enqueue, navigate, approve, dequeue
- AS-2: FIFO ordering — session A before B, depletes oldest-first
- AS-3: Answered-in-pane reconcile — transcript advance dequeues without UI action
- AS-4: Dropped-event recovery — collector down during Stop, reconcile rebuilds queue
- AS-5: Skip + cooldown — skip moves to tail, cooldown makes queue empty until expiry
- AS-6: No forced focus-steal — resolving a session does NOT auto-switch client
- AS-7: Pane reuse regression — new session in old pane: navigation targets current owner
Files:
test-walking-skeleton.sh— comprehensive acceptance scenario test suite (399 lines)notes/tb-4mq.md— phase 6 completion notes
Next:
- Phase 7: Iterate — polish, edge cases, and enhancement loop
Phase 7: Iterate (IN PROGRESS)
Done:
- Dashboard labels:
trailboss-watchnow resolves pane_id → tmux session name (alpha, bravo, etc.) via a singletmux list-panes -acall per render, falling back to CWD last component - Fixed
prefix+sconflict with tmux's built-in session chooser → moved skip toprefix+S trailboss-startauto-creates thetrail-bosstmux session if it doesn't exist- Confirmed real hooks firing and dequeuing bootstrap entries correctly (ended/unstuck events in daemon log)
Verified working end-to-end:
- Daemon running in
trail-boss:daemonwindow (stable, survives shell exits) - Dashboard showing NATO session names (alpha → mike) + telegram-bridge panes
- Real Claude Code hooks (Stop, UserPromptSubmit) dequeuing sessions as they become active
Phase 9: Supervisor Split View ✅ COMPLETE
Done: trail-boss:dashboard is now a 2-pane split — TUI on top (60%), live preview on bottom (40%).
Implemented:
bin/trailboss-preview— bash loop: reads/tmp/trailboss-preview-target, renderstmux capture-paneoutput every 0.5stui/client.go—WritePreviewTarget()writes selected pane_id to target filetui/model.go—syncPreview()called on every cursor movement + queue updatebin/trailboss-start— kill-session + fresh session; uses stable window ID (@NNN) + pane ID (%NNN) to avoid pane-base-index and auto-rename pitfalls; passes command directly tosplit-window(no send-keys race)tmux.conf—prefix+Balways returns totrail-boss:dashboardbin/trailboss—returncommand targetstrail-boss:dashboard
Phase 8: TUI Rewrite ✅ COMPLETE
Done: Replaced bash-based trailboss-watch/trailboss-popup with a proper Go + Bubble Tea TUI.
Implemented:
tui/client.go— HTTP client for daemon, tmux pane map, JumpToPane with origin trackingtui/theme.go— Dracula palette Lipgloss styles with dumb-terminal fallbacktui/model.go— Full Bubble Tea model: split layout, vim keys, auto-refresh, help overlaytui/main.go— Entry point with AltScreen + mouse supportbin/trailboss-tui— Pre-built binary (10MB, Go 1.24.2)bin/trailboss-start— Updated toexec "$TB_DIR/bin/trailboss-tui"
Features:
- Split layout: list (40%) + detail (60%) for width≥100, list-only for narrow terminals
- Color: stopped=yellow
#F1FA8C, permission=red#FF5555, selected=purple#BD93F9 - Keys: j/k, gg/G, ctrl+d/ctrl+u, Enter (jump), l/Tab (detail focus), s (skip), r (refresh), J/K (detail scroll), 1-9 (direct jump), ? (help), q (quit)
- Auto-refresh every 3s; graceful daemon-unreachable display in status bar
- Detail pane: word-wrapped last_message, CWD, relative stuck time
- Help overlay centered with rounded border