Files
claude-hud/docs/adr/003-shell-vs-typescript.md

139 lines
5.4 KiB
Markdown
Raw Normal View History

# ADR 003: Shell Scripts vs TypeScript
## Status
Accepted
## Context
Current implementation uses shell scripts for:
1. `session-start.sh` - Spawns HUD, creates FIFO, manages split pane
2. `capture-event.sh` - Processes hook events, writes to FIFO
3. `cleanup.sh` - Kills process, removes FIFO
4. `verify-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:
- `command` field 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:
1. Parse/extract essential data from hook input
2. Write to FIFO or perform one simple action
```bash
#!/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
```typescript
// 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
1. **No complex logic** - If you need `if/then/else` more than once, reconsider
2. **No error recovery** - Let it fail fast, TypeScript handles gracefully
3. **Non-blocking writes** - Always use `&` for FIFO writes
4. **Timeouts for commands** - Use `timeout 1s command` for any external call
5. **Minimal dependencies** - Only rely on POSIX + jq
## Implementation Notes
1. Simplify capture-event.sh to raw data extraction only
2. Create `lib/event-processor.ts` for event enrichment
3. Add comprehensive tests for event processing
4. Keep session-start.sh for platform-specific terminal handling