mirror of
https://github.com/jarrodwatts/claude-hud.git
synced 2026-05-13 10:29:10 +00:00
139 lines
5.4 KiB
Markdown
139 lines
5.4 KiB
Markdown
|
|
# 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
|