mirror of
https://github.com/jarrodwatts/claude-hud.git
synced 2026-05-13 10:29:10 +00:00
ADR 001: State Management - Custom hooks + useReducer - Extract state logic from app.tsx into domain-specific hooks - useReducer for predictable state transitions - Each hook independently testable ADR 002: Data Flow - Event-driven with minimal polling - Primary: Hook events via FIFO (real-time) - Secondary: Single consolidated poll (git/mcp only) - Eliminate redundant polling sources that cause flickering ADR 003: Shell vs TypeScript - Minimal shell, logic in TS - Hooks must be shell scripts (Claude Code requirement) - Keep shell scripts minimal (extract data, write FIFO) - Complex logic moves to testable TypeScript ADR 004: Session Handling - Track session ID, graceful reset - Session ID in all events for change detection - Reset state on session change (/new, /exit, /resume) - Exponential backoff reconnection strategy 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
5.4 KiB
5.4 KiB
ADR 003: Shell Scripts vs TypeScript
Status
Accepted
Context
Current implementation uses shell scripts for:
session-start.sh- Spawns HUD, creates FIFO, manages split panecapture-event.sh- Processes hook events, writes to FIFOcleanup.sh- Kills process, removes FIFOverify-install.sh- Checks installation
Questions:
- Should we keep shell scripts or rewrite in TypeScript/Node?
- What logic belongs where?
Decision
Keep shell scripts for hooks (required), but make them minimal. Move logic to TypeScript.
Why Shell Scripts Are Required for Hooks
Claude Code hooks must be shell scripts. From the plugin spec:
commandfield specifies the shell command to run- Environment variables passed to the script
- JSON input via stdin, output via stdout
There is no option to use Node.js directly for hooks.
The Minimal Shell Script Pattern
Shell scripts should do exactly two things:
- Parse/extract essential data from hook input
- Write to FIFO or perform one simple action
#!/bin/bash
# capture-event.sh - MINIMAL
set -uo pipefail
# Extract just what we need
event_name="$HOOK_EVENT_NAME"
session_id="$SESSION_ID"
timestamp=$(date +%s)
# Read stdin (JSON from Claude Code)
input=$(cat)
# Write to FIFO (non-blocking)
if [[ -p "$FIFO_PATH" ]]; then
echo "{\"event\":\"$event_name\",\"session\":\"$session_id\",\"ts\":$timestamp,\"data\":$input}" > "$FIFO_PATH" &
fi
What Moves to TypeScript
| Currently in Shell | Move to TypeScript? | Reason |
|---|---|---|
| FIFO creation | Yes | Better error handling, cross-platform |
| Split pane creation | Keep in shell | AppleScript/osascript required |
| Event processing | Yes | Complex logic should be testable |
| JSON parsing | Minimal in shell | jq for extraction, full parsing in TS |
| Process management | Keep in shell | PID files, signals are shell-native |
| Data enrichment | Yes | TypeScript has better tooling |
New Architecture
┌─────────────────────────────────────────────────────────────┐
│ Shell Scripts (Hooks) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ capture-event.sh - Extract data, write raw to FIFO │ │
│ │ session-start.sh - Create FIFO, spawn HUD process │ │
│ │ cleanup.sh - Signal process, remove FIFO │ │
│ └──────────────────────────────────────────────────────┘ │
├─────────────────────────────────────────────────────────────┤
│ TypeScript (TUI) │
│ ┌──────────────────────────────────────────────────────┐ │
│ │ EventReader - Read FIFO, parse JSON, validate │ │
│ │ EventProcessor - Enrich events, compute derived data │ │
│ │ State Hooks - Update UI state from processed events │ │
│ │ Components - Render UI │ │
│ └──────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
TypeScript Event Processing
// lib/event-processor.ts
interface RawEvent {
event: string;
session: string;
ts: number;
data: unknown;
}
interface ProcessedEvent {
type: 'tool_start' | 'tool_end' | 'agent_stop' | 'session_change' | ...;
timestamp: Date;
sessionId: string;
payload: ToolEvent | AgentEvent | SessionEvent | ...;
}
function processEvent(raw: RawEvent): ProcessedEvent {
// Validation, type narrowing, enrichment
// All testable in TypeScript
}
Consequences
Positive
- Testability: TypeScript logic is fully unit testable
- Type safety: Catch errors at compile time
- Maintainability: Complex logic in familiar language
- Cross-platform: TypeScript works everywhere Node runs
- Debugging: Better stack traces, source maps
Negative
- Two languages: Shell for hooks, TypeScript for logic
- Coordination: Shell must output format TypeScript expects
- Still need shell knowledge: Hooks can't be avoided
Shell Script Guidelines
- No complex logic - If you need
if/then/elsemore than once, reconsider - No error recovery - Let it fail fast, TypeScript handles gracefully
- Non-blocking writes - Always use
&for FIFO writes - Timeouts for commands - Use
timeout 1s commandfor any external call - Minimal dependencies - Only rely on POSIX + jq
Implementation Notes
- Simplify capture-event.sh to raw data extraction only
- Create
lib/event-processor.tsfor event enrichment - Add comprehensive tests for event processing
- Keep session-start.sh for platform-specific terminal handling