Back
Hooked Logo
Docs

Hooked Documentation

Voice announcements and session-scoped until loops for Claude Code.

Table of Contents


Introduction

Hooked solves two problems with Claude Code:

  1. Missed prompts — You're in another app, Claude needs permission, you don't notice for 10 minutes
  2. 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:

  1. You set a pending until loop (objective or check command)
  2. The next Claude session that tries to stop claims it
  3. That session keeps working until the objective is complete or check passes
  4. Other sessions are unaffected

Requirements

  • Claude Code installed and configured
  • Node.js 18+
  • macOS or Linux
  • (Optional) SpeakEasy for voice announcements

Installation

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.json with hook definitions
  • Install the /hooked slash 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

  1. You set a pending until loop
  2. Next time Claude tries to stop, it claims the loop
  3. Voice announces: "Loop started. API docs are 100% coverage"
  4. Claude keeps working, announcing each round: "Round 2. Objective: API docs are 100% coverage"
  5. When you're satisfied, run hooked off
  6. 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

EventVoice 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

  1. Install SpeakEasy globally:

    npm install -g @arach/speakeasy
    
  2. Configure your TTS provider:

    speakeasy config
    
  3. 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:

  1. The hook runs your check command
  2. If check fails → Claude continues working
  3. 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 loops
  • hooked 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

KeyTypeDefaultDescription
flags.speakbooleantrueEnable voice announcements
flags.loggingbooleantrueEnable event logging

Voice Templates

Customize what Claude says at each event. Available variables:

TemplateVariablesWhen
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

  1. Check SpeakEasy is installed:

    speakeasy --version
    
  2. Test SpeakEasy directly:

    speakeasy say "Test message"
    
  3. Check speak flag is enabled:

    hooked speak
    
  4. Check logs for errors:

    tail -f ~/logs/claude-hooks/notification.log
    

Until loop not triggering

  1. Check status:

    hooked status
    
  2. Verify a pending loop exists:

    cat ~/.hooked/pending.json
    
  3. Re-run setup:

    pnpm run hooked:init
    
  4. Restart Claude Code session (hooks are loaded at session start)

Claude keeps working forever

  1. Check if there's an active session:

    hooked status
    
  2. Force disable:

    hooked off
    
  3. For check mode, verify your command:

    pnpm test  # Does it actually pass?
    

Hook errors in Claude Code

  1. Check hook logs:

    tail -f ~/logs/claude-hooks/notification.log
    
  2. 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).