Hooked Documentation
Voice announcements and session-scoped until loops for Claude Code.
Table of Contents
- Introduction
- Installation
- Quick Start
- Speak (Voice)
- Until Loops
- History & Dashboard
- CLI Reference
- Configuration
- Troubleshooting
- FAQ
Introduction
Hooked solves two problems with Claude Code:
- Missed prompts — You're in another app, Claude needs permission, you don't notice for 10 minutes
- Premature stops — Claude says "done" but tests are failing, build is broken, work isn't actually complete
Hooked adds:
- Voice announcements via SpeakEasy when Claude needs your attention
- Session-scoped until loops that keep Claude working toward your objective
How It Works
Hooked installs Claude Code hooks that intercept events:
- Notification hooks — Trigger voice announcements on permission requests, errors, or when Claude is waiting
- Stop hooks — Evaluate whether Claude should continue working based on your objective or check command
Key Feature: Session-Scoped Until Loops
Until loops are bound to specific Claude sessions using a "lazy binding" pattern:
- You set a pending until loop (objective or check command)
- The next Claude session that tries to stop claims it
- That session keeps working until the objective is complete or check passes
- Other sessions are unaffected
Requirements
- Claude Code installed and configured
- Node.js 18+
- macOS or Linux
- (Optional) SpeakEasy for voice announcements
Installation
One-liner (recommended)
curl -fsSL https://raw.githubusercontent.com/arach/hooked/master/install.sh | bash
This will automatically:
- Download and set up hooked in
~/.hooked/ - Configure Claude Code's
settings.jsonwith hook definitions - Install the
/hookedslash command
npm / npx
# Run directly with npx (no install)
npx @arach/hooked init
# Or install globally
npm install -g @arach/hooked
hooked init
For contributors
Clone the repo to develop or customize hooked:
git clone https://github.com/arach/hooked.git
cd hooked
pnpm install
pnpm run hooked:init
(Optional) Set up SpeakEasy for voice announcements
npm install -g @arach/speakeasy
speakeasy config
Follow the prompts to configure your TTS provider (ElevenLabs recommended).
Quick Start
Start an until loop
# Keep Claude working toward an objective
hooked until "API docs are 100% coverage"
# Or keep working until a command passes
hooked until check "pnpm test"
What happens next
- You set a pending until loop
- Next time Claude tries to stop, it claims the loop
- Voice announces: "Loop started. API docs are 100% coverage"
- Claude keeps working, announcing each round: "Round 2. Objective: API docs are 100% coverage"
- When you're satisfied, run
hooked off - Voice announces: "Mission complete."
End the loop
hooked off
Speak (Voice)
Voice announcements notify you audibly when Claude needs attention or continuation state changes.
What triggers voice
| Event | Voice Message |
|---|---|
| Permission request | "In {project}, Claude needs your permission" |
| Waiting for input | "In {project}, Claude is waiting for you" |
| Loop started | "In {project}, loop started. {objective}" |
| Each round | "In {project}, round N. Objective: {objective}" |
| Check passed | "In {project}, check passed. Loop complete." |
| Check failed | "In {project}, check failed. Keep working." |
| Mission complete | "Mission complete." |
| Paused | "In {project}, pausing as requested." |
Toggle speak
# Turn off voice
hooked speak off
# Turn on voice
hooked speak on
# Check current status
hooked speak
SpeakEasy setup
-
Install SpeakEasy globally:
npm install -g @arach/speakeasy -
Configure your TTS provider:
speakeasy config -
Test it works:
speakeasy say "Hello from hooked"
Supported providers: ElevenLabs, OpenAI TTS, Azure, Google Cloud TTS, and more.
Until Loops
Until loops keep Claude working toward your goal. Two modes are available:
Manual Mode
Keep Claude working toward a stated objective until you say stop.
hooked until "API docs are 100% coverage"
Claude will keep working, announcing each round, until you run hooked off.
Check Mode
Keep Claude working until a command passes.
hooked until check "pnpm test"
hooked until check "pnpm build"
hooked until check "pnpm typecheck"
When Claude tries to stop:
- The hook runs your check command
- If check fails → Claude continues working
- If check passes → Loop auto-clears, Claude stops
Session-Scoped Binding
Until loops are bound to specific sessions:
Terminal 1: hooked until "100% test coverage"
→ Creates pending.json
Session A: Claude tries to stop
→ Claims pending → state/sessionA.json
→ Speaks "Loop started"
→ Blocks
Session B: Claude tries to stop
→ No pending, no state/sessionB.json
→ Approves (unaffected)
Session A: User runs: hooked off
→ Clears state/sessionA.json
→ Speaks "Mission complete"
Pausing vs Stopping
hooked off— Immediately clears all until loopshooked pause— Gracefully stops after the current cycle completes
History & Dashboard
Hooked logs all events to a SQLite database for review and analysis.
View Recent Events
hooked history # Last 20 events
hooked history 50 # Last 50 events
hooked history --full # Include full Claude payload
Search & Stats
hooked history stats # Event counts by project
hooked history search "auth" # Search messages
Export & Cleanup
hooked history export json > backup.json # Export to JSON
hooked history export csv > backup.csv # Export to CSV
hooked history prune 30 # Delete events older than 30 days
Web Dashboard
View your event history in a browser with filtering, search, and live updates:
hooked web # Opens http://localhost:3456 (auto-closes in 10m, background)
hooked web 8080 30 # Custom port + timeout
hooked web --foreground # Keep attached to the terminal
The dashboard shows:
- Real-time event stream with auto-refresh
- Filter by event type (notification, spoken, continuation, etc.)
- Search across all events
- Project statistics
CLI Reference
Status
hooked status
hooked s
Show current speak setting, pending until loops, and active sessions.
Speak
hooked speak on # Enable voice
hooked speak off # Disable voice
hooked speak # Show current status
hooked sp on # Short form
Until
# Manual mode - keep working toward objective
hooked until "implement feature X"
hooked u "implement feature X"
# Check mode - keep working until command passes
hooked until check "pnpm test"
hooked u check "pnpm build"
# Clear all until loops
hooked until off
hooked u off
# Pause after next cycle
hooked until pause
hooked u pause
History
hooked history [n] # Show recent events (default: 20)
hooked history stats # Event counts by project
hooked history search <q> # Search events
hooked history export [json|csv] # Export all events
hooked history prune [days] # Delete old events (default: 30)
hooked h 50 # Short form
Web Dashboard
hooked web [port] [mins] # Open dashboard (auto-closes in 10m, background)
hooked web --foreground # Keep attached to the terminal
hooked dashboard # Alias for web
Shortcuts
hooked off # Same as: hooked until off
hooked pause # Same as: hooked until pause
Using the Slash Command
From within Claude Code:
/hooked status
/hooked until "my objective"
/hooked off
Configuration
Configuration is stored in ~/.hooked/config.json:
{
"flags": {
"speak": true,
"logging": true
},
"templates": {
"loopStarted": "In {project}, loop started. {goal}",
"checkPassed": "In {project}, check passed. Loop complete.",
"checkFailed": "In {project}, check failed. Keep working.",
"pausing": "In {project}, pausing as requested.",
"manualRound": "In {project}, round {round}. Objective: {objective}",
"missionComplete": "Mission complete."
}
}
Options
| Key | Type | Default | Description |
|---|---|---|---|
flags.speak | boolean | true | Enable voice announcements |
flags.logging | boolean | true | Enable event logging |
Voice Templates
Customize what Claude says at each event. Available variables:
| Template | Variables | When |
|---|---|---|
loopStarted | {project}, {goal} | Loop claimed by session |
checkPassed | {project} | Check command succeeded |
checkFailed | {project} | Check command failed |
pausing | {project} | User requested pause |
manualRound | {project}, {round}, {objective} | Each manual mode iteration |
missionComplete | (none) | All loops cleared |
File locations
~/.hooked/
├── config.json # Configuration
├── pending.json # Pending until loop (waiting to be claimed)
├── sessions.json # Session registry (project → session mapping)
├── history.sqlite # SQLite event history database
├── pause # Pause flag (if present, next cycle stops)
├── state/ # Session-bound state files
│ ├── {sessionId}.json
│ └── ...
└── src/ # Hook source files
~/.claude/
├── commands/
│ └── hooked.md # Slash command definition
└── settings.json # Claude Code hook configuration
Troubleshooting
Voice not working
-
Check SpeakEasy is installed:
speakeasy --version -
Test SpeakEasy directly:
speakeasy say "Test message" -
Check speak flag is enabled:
hooked speak -
Check logs for errors:
tail -f ~/logs/claude-hooks/notification.log
Until loop not triggering
-
Check status:
hooked status -
Verify a pending loop exists:
cat ~/.hooked/pending.json -
Re-run setup:
pnpm run hooked:init -
Restart Claude Code session (hooks are loaded at session start)
Claude keeps working forever
-
Check if there's an active session:
hooked status -
Force disable:
hooked off -
For check mode, verify your command:
pnpm test # Does it actually pass?
Hook errors in Claude Code
-
Check hook logs:
tail -f ~/logs/claude-hooks/notification.log -
Test stop hook manually:
echo '{"session_id":"test","transcript_path":"/test"}' | ~/.hooked/node_modules/.bin/tsx ~/.hooked/src/stop-hook.ts
FAQ
Is hooked safe to use?
Yes. Hooked only adds hooks that:
- Read events (notifications)
- Run your own commands (for check mode)
- Return continue/stop decisions
It doesn't modify Claude's behavior beyond what hooks are designed for.
Does it work with other package managers?
Yes. You can specify any command:
hooked until check "npm test"
hooked until check "yarn test"
hooked until check "make test"
Can I have multiple until loops at once?
Yes! Each Claude session gets its own loop. Set a pending, it binds to the next session that stops.
What happens if my check command hangs?
The check command has a timeout (60 seconds). If it hangs, it's treated as a failure and Claude continues working.
Does it work with remote/SSH sessions?
Voice requires local audio output. Until loops work anywhere Claude Code runs.
Can I customize the voice?
Yes, through SpeakEasy. Run speakeasy config to choose different voices, providers, and settings.
How do I uninstall?
# Remove hooked files
rm -rf ~/.hooked
# Remove slash command
rm ~/.claude/commands/hooked.md
# Remove hook configuration from ~/.claude/settings.json
# (edit manually to remove the hooks entries)
Resources
Default: OFF — Until loops only activate when you explicitly set one. Voice is always on (if SpeakEasy is configured).
