This document captures all architectural decisions, implementation details, and next steps for continuing development.
Goal: A terminal UI to manage multiple concurrent Claude Code sessions, inspired by claude-canvas TUI patterns.
User workflow: Running 5-7 Claude Code sessions simultaneously in VS Code terminals. Needs to know which sessions need attention without context-switching constantly.
Design principles:
- "Little bits of design delight" — playful, game-like feel
- Terminal-agnostic (works in VS Code, tmux, iTerm2)
- Low-friction setup with Claude Code hooks
-
Dashboard TUI (
src/components/Dashboard.tsx)- List view with session rows
- Kanban view with columns (Needs You / Busy / Chilling)
- Detail view with session info
- Animated spinners for working sessions
- Walking crab animation on empty state
-
Hook System (
scripts/*.sh)session-start.sh— Registers session with terminal contextsession-end.sh— Removes sessionnotification.sh— Sets alerting state (Claude needs input)tool-use.sh— Clears alert, updates status to workinguser-prompt.sh— Ensures session is registered on any message
-
Server (
src/server.ts)- HTTP server on port 7432
POST /event— Receives hook eventsPOST /action— Sends quick actions (tmux only)GET /sessions— Lists all sessionsGET /health— Health check
-
Persistence (
src/store.ts)- SQLite database at
~/.claude-session-manager.db - Sessions survive dashboard restarts
- In-memory cache for fast reads
- Pub/sub for UI updates
- SQLite database at
-
Quick Actions (tmux only)
yto approve (sends "y" + Enter)nto deny (sends "n" + Enter)rto type custom response- Uses
tmux send-keys -t <pane>
claude-session-manager/
├── src/
│ ├── cli.ts # CLI entry point with Commander.js
│ ├── server.ts # HTTP server for hook events
│ ├── store.ts # SQLite-backed session store
│ ├── types.ts # TypeScript types
│ └── components/
│ └── Dashboard.tsx # Main Ink TUI component
├── scripts/
│ ├── session-start.sh # Hook: SessionStart
│ ├── session-end.sh # Hook: SessionEnd
│ ├── notification.sh # Hook: Notification
│ ├── tool-use.sh # Hook: PostToolUse
│ └── user-prompt.sh # Hook: UserPromptSubmit
├── CLAUDE.md # Project conventions for AI assistants
├── README.md # User documentation
└── HANDOFF.md # This file
// Session status
type SessionStatus = 'idle' | 'working' | 'needs_input' | 'error' | 'completed';
// Terminal context for quick actions
interface TerminalContext {
type: 'tmux' | 'vscode' | 'iterm2' | 'unknown';
id: string; // tmux pane target, iTerm session ID, etc.
shellPid?: number;
ttyPath?: string;
}
// Full session object
interface ClaudeSession {
id: string;
name: string;
status: SessionStatus;
projectPath?: string;
currentTask?: string;
pendingMessage?: string;
lastActivity: Date;
alerting: boolean;
terminal: TerminalContext;
}- Claude Code fires hook event
- Hook script receives JSON on stdin:
{ "session_id": "abc123", "cwd": "/path/to/project", "hook_event_name": "SessionStart" } - Script extracts fields and detects terminal context
- Script POSTs to
http://localhost:7432/event - Server updates SQLite + cache
- Store notifies subscribers
- React component re-renders
- Parent-child tracking via
parentIdfield on sessions - Expandable tree rows with ▼/▶ indicators
- Status bubbling — if any child needs input, parent shows "!" indicator
- Keyboard navigation — ←/→ to collapse/expand, Enter toggles tree, Space opens detail
interface ClaudeSession {
// ... existing fields
parentId?: string; // If this is a subagent, the parent's session ID
}
interface AggregatedStatus {
status: SessionStatus;
alerting: boolean;
childCount: number;
alertingChildCount: number;
}SQLite migration (auto-applied):
ALTER TABLE sessions ADD COLUMN parent_id TEXT;
CREATE INDEX IF NOT EXISTS idx_parent_id ON sessions(parent_id);getChildren(parentId)— Get direct children of a sessiongetRootSessions()— Get sessions without parentsgetAggregatedStatus(sessionId)— Get status including all descendantsgetDescendants(sessionId)— Get all children recursivelysortedRoots()— Get root sessions sorted by aggregated urgency
Added SubagentStop hook (scripts/subagent-stop.sh) that:
- Captures subagent completion
- Links subagent to parent via
parent_session_id - Marks subagent as "completed" status
Added subagent_start and subagent_stop event types in server.
| Key | Action |
|---|---|
← |
Collapse selected session |
→ |
Expand selected session |
Enter |
Toggle expand/collapse (if has children) or open detail |
Space |
Always open detail view |
Currently relies on SubagentStop hook which fires when subagent completes. To detect subagents at spawn time:
- PostToolUse with Task tool — When
tool_name === 'Task', could extract subagent session_id from tool response - Session naming patterns — Claude Code may use predictable subagent IDs
- Transcript parsing — Parse transcript files to find Task tool invocations
To send input to VS Code integrated terminals, we need a VS Code extension that:
- Exposes an HTTP endpoint or socket
- Receives commands like
{ terminalId: "xyz", text: "y" } - Uses
vscode.window.terminals[i].sendText()API
Architecture sketch:
Dashboard → HTTP POST → VS Code Extension → terminal.sendText()
The extension would need to:
- Track terminal IDs and map them to session IDs
- Listen on a port (or use IPC)
- Handle authentication (only accept localhost requests)
iTerm2 can be controlled via AppleScript:
tell application "iTerm2"
tell current session of current window
write text "y"
end tell
end tellOr via its Python API. Need to:
- Detect iTerm2 session ID in hook (
$ITERM_SESSION_ID) - Use
osascriptto send text to specific session
Could integrate with:
afplayfor macOS soundsterminal-notifierfor notifications- Existing
ambient-alertsplugin (user already has this)
The transcript files at transcript_path contain full conversation history. Could:
- Parse and display recent messages
- Show tool usage timeline
- Enable searching across sessions
- Built-in SQLite support (
bun:sqlite) - Fast startup time for CLI
- Native TypeScript support
- Used by claude-canvas (inspiration project)
- Sessions need to survive dashboard restarts
- Simple file-based storage, no external database
- Bun has excellent SQLite integration
- Single file at
~/.claude-session-manager.db
- Hooks are reliable and official
- Get accurate session state (not guessing from process activity)
- Terminal context available in hook environment
- Works regardless of terminal type
- Catches sessions that were running before hooks were installed
- Every message registers the session — no manual restart needed
- Idempotent — safe to call multiple times
Instead of technical terms:
- "idle" → "Chilling", "Lounging", "Daydreaming"
- "working" → "Cooking", "Scheming", "Tinkering"
- "needs_input" → "Waiting", "Your turn", "Paging you"
Each session gets a consistent verb (based on session ID hash) for variety without randomness.
Based on project name keywords:
bio,lab,health→ 🧬grant,money,finance→ 💰ai,ml,claude→ 🤖- Default → rotating sea creatures (🦀, 🐙, 🦑, etc.)
# Start dashboard
bun run src/cli.ts
# Simulate session in another terminal
curl -X POST http://localhost:7432/event \
-H "Content-Type: application/json" \
-d '{"event":"session_start","session_id":"test-123","cwd":"/tmp/test-project"}'
# Simulate notification
curl -X POST http://localhost:7432/event \
-H "Content-Type: application/json" \
-d '{"event":"notification","session_id":"test-123","message":"Approve file write?"}'
# Check sessions
curl http://localhost:7432/sessions | jq .No tests written yet. Would recommend:
- Unit tests for
store.ts(SQLite operations) - Integration tests for
server.ts(HTTP endpoints) - Component tests for Dashboard (with mocked store)
-
Session names show truncated IDs — Happens when hooks don't send
cwdor name derivation fails. Runbun run src/cli.ts clearand re-register sessions. -
Quick actions don't work in VS Code terminal — Expected. Needs VS Code extension (see Future Features).
-
Stale sessions accumulate — Sessions that crashed or were killed without SessionEnd hook stay in database. Use
bun run src/cli.ts prune -m 60to clean up.
This project was created collaboratively with Claude Opus 4.5. The original conversation context may be summarized, but this handoff document captures all essential information for continuing development.