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.
This requires:
Optional:
This demo captures all 13 Claude Code hook lifecycle events with their JSON payloads:
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
Fires: Before any tool execution
Payload: tool_name, tool_input parameters
Enhanced: Blocks dangerous commands (rm -rf, .env access)
Fires: After successful tool completion
Payload: tool_name, tool_input, tool_response with results
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)
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)
Fires: When Claude Code subagents (Task tools) finish responding
Payload: stop_hook_active boolean flag
Enhanced: TTS playback - "Subagent Complete"
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
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)
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)
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)
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
Fires: When a subagent (Task tool) spawns
Payload: agent_id, agent_type, session info
Enhanced: Subagent spawn logging with optional TTS announcement
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
logs/ directoryWarning: 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.
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:
This approach ensures your hooks remain functional across different environments without polluting your main project's dependency tree.
.claude/settings.json - Hook configuration with permissions.claude/hooks/ - Python scripts using uv for each hook type
user_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 hooks
ruff_validator.py - Python linting via Ruff (PostToolUse)ty_validator.py - Python type checking (PostToolUse)utils/ - Intelligent TTS and LLM utility scripts
tts/ - 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 displays
status_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 configurations
genui.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 commands
prime.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 configurations
crypto/ - Cryptocurrency analysis agentsteam/ - Team-based workflow agents
builder.md - Implementation agent (all tools)validator.md - Read-only validation agenthello-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 generatorlogs/ - JSON logs of all hook executions
user_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 resources
cc_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 documentationruff.toml - Ruff linter configuration for Python code qualityty.toml - Type checker configuration for Python type validationHooks provide deterministic control over Claude Code behavior without relying on LLM decisions.
Run any Claude Code command to see hooks in action via the logs/ files.
Claude Code hooks provide powerful mechanisms to control execution flow and provide feedback through exit codes and structured JSON output.
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 |
Each hook type has different capabilities for blocking and controlling Claude Code's behavior:
user_prompt_submit.py logs all prompts and can validate thempre_tool_use.py blocks rm -rf commands 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
subagent_stop.py logs events and announces completionpre_compact.py creates transcript backups before compactionsession_start.py loads git status, recent issues, and context filesBeyond simple exit codes, hooks can return structured JSON for sophisticated control:
{
"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)
}
{
"decision": "approve" | "block" | undefined,
"reason": "Explanation for decision"
}
reason shown to userreason shown to Claudereason ignored{
"decision": "block" | undefined,
"reason": "Explanation for decision"
}
reasonreason ignored{
"decision": "block" | undefined,
"reason": "Must be provided when blocking Claude from stopping"
}
reason tells Claude how to proceedreason ignoredWhen multiple control mechanisms are used, they follow this priority:
"continue": false - Takes precedence over all other controls"decision": "block" - Hook-specific blocking behavior# 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)
# 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)
# 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)
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.
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"
}
Dangerous prompts are blocked before Claude can act on them:
User: "rm -rf / --no-preserve-root"
Hook: BLOCKED: Dangerous system deletion command detected
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"
Try these prompts to see UserPromptSubmit in action:
Normal prompt: "What files are in this directory?"
logs/user_prompt_submit.jsonWith validation enabled (add --validate flag):
Check the logs:
cat logs/user_prompt_submit.json | jq '.'
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 promptsstop_hook_active flag in Stop hooksWatch this YouTube video to see how to create and use Claude Code sub-agents effectively.
See the Claude Code Sub-Agents documentation for more details.
Claude Code supports specialized sub-agents that handle specific tasks with custom system prompts, tools, and separate context windows. Sub-agents are AI assistants that your primary Claude Code agent can delegate tasks to.
Critical Concept: The content in agent files (.claude/agents/*.md) are system prompts that configure the sub-agent's behavior. They are NOT user prompts. This is the #1 misunderstanding when creating agents.
Information Flow:
You (User) → Primary Agent → Sub-Agent → Primary Agent → You (User)
Key Points:
description field tells the primary agent WHEN to use the sub-agentThis repository demonstrates various agent configurations:
Project Agents (.claude/agents/):
.claude/agents/ ├── crypto/ # Cryptocurrency analysis agents │ ├── crypto-coin-analyzer-haiku.md │ ├── crypto-coin-analyzer-opus.md │ ├── crypto-coin-analyzer-sonnet.md │ ├── crypto-investment-plays-*.md │ ├── crypto-market-agent-*.md │ ├── crypto-movers-haiku.md │ └── macro-crypto-correlation-scanner-*.md ├── hello-world-agent.md # Simple greeting agent ├── llm-ai-agents-and-eng-research.md # AI research specialist ├── meta-agent.md # Agent that creates agents └── work-completion-summary.md # Audio summary generator
Storage Hierarchy:
.claude/agents/ (higher priority, project-specific)~/.claude/agents/ (lower priority, available across all projects)Agent File Structure:
---
name: agent-name
description: When to use this agent (critical for automatic delegation)
tools: Tool1, Tool2, Tool3 # Optional - inherits all tools if omitted
color: Cyan # Visual identifier in terminal
model: opus # Optional - haiku | sonnet | opus - defaults to sonnet
---
# Purpose
You are a [role definition].
## Instructions
1. Step-by-step instructions
2. What the agent should do
3. How to report results
## Report/Response Format
Specify how the agent should communicate results back to the primary agent.
Sub-agents enable:
Two Critical Mistakes to Avoid:
Misunderstanding the System Prompt - What you write in agent files is the system prompt, not a user prompt. This changes how you structure instructions and what information is available to the agent.
Ignoring Information Flow - Sub-agents respond to your primary agent, not to you. Your primary agent prompts sub-agents based on your original request, and sub-agents report back to the primary agent, which then reports to you.
Best Practices:
description field to tell your primary agent when and how to prompt sub-agentsClaude Code can intelligently chain multiple sub-agents together for complex tasks:
For example:
This chaining allows you to build sophisticated workflows while maintaining clean separation of concerns.
The meta-agent (.claude/agents/meta-agent.md) is a specialized sub-agent that generates new sub-agents from descriptions. It's the "agent that builds agents" - a critical tool for scaling your agent development velocity.
Why Meta-Agent Matters:
Using 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
The meta-agent follows the principle: "Figure out how to scale it up. Build the thing that builds the thing." This compound effect accelerates your engineering capabilities exponentially.
Watch the walkthrough: See agent teams and the
/plan_w_teamworkflow in action at https://youtu.be/4_2j5wgt_ds
This repository includes a powerful build/validate workflow pattern using the Claude Code task system to orchestrate specialized agent teams.
The /plan_w_team command (.claude/commands/plan_w_team.md) is not an ordinary prompt—it has three powerful components:
The prompt includes embedded hooks in its front matter that validate its own output:
hooks:
stop:
- command: "uv run $CLAUDE_PROJECT_DIR/.claude/hooks/validators/validate_new_file.py specs/*.md"
- command: "uv run $CLAUDE_PROJECT_DIR/.claude/hooks/validators/validate_file_contains.py"
After the planning agent finishes, these validators ensure:
If validation fails, the agent receives feedback and continues working until the output meets criteria.
The prompt leverages Claude Code's task system to build and coordinate agent teams:
| Task Tool | Purpose |
|---|---|
TaskCreate | Create new tasks with owners, descriptions, dependencies |
TaskUpdate | Update status, add blockers, communicate completion |
TaskList | View all tasks and their current state |
TaskGet | Retrieve full task details |
How it works:
This enables longer-running threads of work because the task system handles coordination—no bash sleep loops needed.
/plan_w_team is a template meta prompt—a prompt that generates prompts in a specific, vetted format:
## Plan Format (embedded in the meta prompt)
### {{PLAN_NAME}}
**Task:** {{TASK_DESCRIPTION}}
**Objective:** {{OBJECTIVE}}
### Team Orchestration
{{TEAM_MEMBERS}}
### Step-by-Step Tasks
{{TASKS}}
The generated plan follows your engineering patterns exactly. This is the difference between agentic engineering and "vibe coding"—you know the outcome your agent will generate because you've templated the format.
| Agent | File | Tools | Self-Validation | Purpose |
|---|---|---|---|---|
| Builder | team/builder.md | All tools | Ruff + Ty on .py files | Execute implementation tasks, build the thing |
| Validator | team/validator.md | Read-only (no Write/Edit) | None | Verify builder's work meets acceptance criteria |
This two-agent pairing increases compute to increase trust that work was delivered correctly.
PostToolUse validators automatically enforce code quality:
| Validator | File | Trigger | Action |
|---|---|---|---|
| Ruff | ruff_validator.py | Write/Edit on .py files | Blocks on lint errors |
| Ty | ty_validator.py | Write/Edit on .py files | Blocks on type errors |
# 1. Create a plan with team orchestration
/plan_w_team
# User prompt: "Update the hooks documentation and add missing status lines"
# Orchestration prompt: "Create groups of agents for each hook, one builder and one validator"
# 2. Plan is generated with:
# - Team members (session_end_builder, session_end_validator, etc.)
# - Step-by-step tasks with dependencies
# - Validation commands
# 3. Execute the plan
/build
# 4. Watch agents work in parallel:
# - Builders implement features
# - Validators verify completion
# - Task system coordinates everything
ruff.toml - Ruff linter rulesty.toml - Type checker settings.claude/agents/team/ - Team agent definitionsWatch the walkthrough: See these features in action at https://youtu.be/mJhsWrEv-Go
This project includes a comprehensive collection of custom output styles (.claude/output-styles/) that transform how Claude Code communicates responses. See the official documentation for complete details on how output styles work.
| Style | Description | Best For |
|---|---|---|
| genui ⭐ | Generates beautiful HTML with embedded styling | Interactive visual outputs, instant browser preview |
| table-based | Organizes all information in markdown tables | Comparisons, structured data, status reports |
| yaml-structured | Formats responses as YAML configuration | Settings, hierarchical data, API responses |
| bullet-points | Clean nested lists with dashes and numbers | Action items, documentation, task tracking |
| ultra-concise | Minimal words, maximum speed | Experienced devs, rapid prototyping |
| html-structured | Semantic HTML5 with data attributes | Web documentation, rich formatting |
| markdown-focused | Leverages all markdown features optimally | Complex documentation, mixed content |
| tts-summary | Announces task completion via ElevenLabs TTS | Audio feedback, accessibility |
Usage: Run /output-style [name] to activate any style (e.g., /output-style table-based)
Location:
.claude/output-styles/*.md (this repo)~/.claude/output-styles/*.md (global)Output styles modify Claude's system prompt to change response formatting without affecting core functionality. Each style is a markdown file with YAML frontmatter defining the name, description, and formatting instructions.
Watch the walkthrough: See these features in action at https://youtu.be/mJhsWrEv-Go
This project includes enhanced Claude Code status lines that display real-time conversation context. Status lines provide dynamic information at the bottom of your terminal during Claude Code sessions. See the official documentation for complete details.
Location: .claude/status_lines/
| Version | File | Description | Features |
|---|---|---|---|
| v1 | status_line.py | Basic MVP | Git branch, directory, model info |
| v2 | status_line_v2.py | Smart prompts | Latest prompt (250 chars), color-coded by task type |
| v3 | status_line_v3.py | Agent sessions | Agent name, model, last 3 prompts |
| v4 | status_line_v4.py | Extended metadata | Agent name, model, latest prompt, custom key-value pairs |
| v5 | status_line_v5.py | Cost tracking | Model, cost ($), line changes (+/-), session duration |
| v6 | status_line_v6.py | Context window | Visual usage bar, percentage, tokens remaining |
| v7 | status_line_v7.py | Duration timer | Session time, start time, optional end time |
| v8 | status_line_v8.py | Token/cache stats | Input/output tokens, cache creation/read stats |
| v9 | status_line_v9.py | Powerline minimal | Stylized segments with powerline separators, git branch, % used |
Status lines leverage session data stored in .claude/data/sessions/<session_id>.json:
{
"session_id": "unique-session-id",
"prompts": ["first prompt", "second prompt", ...],
"agent_name": "Phoenix", // Auto-generated unique name
"extras": { // v4: Custom metadata (optional)
"project": "myapp",
"status": "debugging",
"environment": "prod"
}
}
Agent Naming:
--name-agent flag in user_prompt_submit.pyCustom Metadata (v4):
/update_status_line command to add custom key-value pairs/update_status_line <session_id> project myappSet your preferred status line in .claude/settings.json:
{
"statusLine": {
"type": "command",
"command": "uv run $CLAUDE_PROJECT_DIR/.claude/status_lines/status_line_v3.py"
}
}
Status Line Features:
Task Type Indicators (v2/v3):
Prepare for the future of software engineering
Learn tactical agentic coding patterns with Tactical Agentic Coding
Follow the IndyDevDan YouTube channel to improve your agentic coding advantage.