Claude Code Hook Capabilities
Reference for what hooks can do in Claude Code.
Why Hooks Matter: The Only Guarantee
Hook = 100% execution guarantee (event-based)
Skill/Agent/MCP = ~20-80% (Claude's judgment)
Key insight: Hooks are the ONLY mechanism that executes without Claude's decision. See orchestration-patterns.md for forcing skill/agent activation.
5 Hook Roles
| Role | Description | Examples | |------|-------------|----------| | Gate | Block/allow tool execution | Prevent dangerous commands, workflow precondition checks | | Side Effect | Auto-actions after tool execution | Formatters, linters, auto-commit | | State Manager | Workflow state management | State file creation/deletion, phase tracking | | External Integrator | External system integration | MCP calls, HTTP API, WebSocket, Slack | | Context Injector | Session context injection | Load project settings, activate services |
Event Types and Characteristics
| Event | Block | Special Features | Verification | |-------|-------|------------------|--------------| | SessionStart | No | source (compact/new) | Verified | | UserPromptSubmit | Yes | stdout auto-injects into Claude context | Verified | | PreToolUse | Yes | updatedInput modifies input, tool_use_id | Verified | | PermissionRequest | Yes | allow/deny/ask + input modification | Unverified | | PostToolUse | No | tool_response (access results) | Verified | | Stop | Yes | stop_hook_active (loop prevention) | Verified | | SubagentStop | Yes | parent-child correlation via tool_use_id | Unverified | | Notification | No | includes notification_type | Verified | | PreCompact | No | trigger (auto/manual) | Verified | | SessionEnd | No | On session end | Unverified |
22 Universal Approaches
Control Patterns
| Approach | Description | Event | |----------|-------------|-------| | Iteration Control | Track iteration count + max limit | Stop | | Force Continuation | Use exit 2 to continue Claude work | Stop | | Promise Detection | Detect Claude response patterns, conditional exit | Stop | | Infinite Loop Prevention | Prevent recursion via parent_tool_use_id | UserPromptSubmit | | Threshold Branching | Branch based on error/warning count | Stop |
Input Manipulation
| Approach | Description | Event | |----------|-------------|-------| | Input Modification | Modify tool input via updatedInput | PreToolUse, PermissionRequest | | Path Normalization | Auto-convert relative to absolute paths | PreToolUse | | Environment Injection | Auto-inject environment variables | PreToolUse | | Dry-run Enforcement | Auto-add --dry-run to dangerous commands | PreToolUse |
Context Management
| Approach | Description | Event | |----------|-------------|-------| | Context Injection | stdout auto-injects into Claude context | UserPromptSubmit | | Progressive Loading | Load context/skills on demand | UserPromptSubmit | | Skill Auto-Activation | Keywords trigger skill suggestions | UserPromptSubmit | | Transcript Parsing | Read and analyze previous responses | Stop | | Transcript Backup | Backup session transcript | PreCompact |
State Management
| Approach | Description | Event | |----------|-------------|-------| | Session Cache | Accumulate per-session state + aggregate results | PostToolUse | | Session Lifecycle | Initialize/cleanup state via SessionStart/End | SessionStart/End | | Checkpoint Commit | Checkpoint on every change, then squash | PostToolUse, Stop | | Session Branching | Auto-isolate Git branches per session | Pre/PostToolUse |
External Integration
| Approach | Description | Event | |----------|-------------|-------| | Notification Forwarding | Forward notifications to Slack/Discord/external | Notification | | Desktop/Audio Alert | osascript, notify-send, TTS | Notification | | Subagent Correlation | Track parent-child via tool_use_id | SubagentStop |
Security & Compliance
| Approach | Description | Event | |----------|-------------|-------| | Auto-Approval | Auto-approve specific tools/commands | PermissionRequest | | Secret Scanning | Detect and block API keys/secrets | PreToolUse | | Compliance Audit | Compliance logging + violation detection | PostToolUse |
Implementation Techniques
| Approach | Description | Event |
|----------|-------------|-------|
| TypeScript Delegation | Delegate complex logic to .ts | Any |
| Hook Chaining | Execute multiple hooks sequentially | Any |
| Background Execution | Async via run_in_background | Any |
| Argument Pattern Matching | Match arguments like Bash(npm test*) | PreToolUse |
| MCP Tool Matching | Match MCP like mcp__memory__.* | PreToolUse |
| Prompt-Type Hook | LLM evaluation via type: "prompt" | Any |
Capabilities vs Limitations
| Possible | Not Possible | |----------|--------------| | File create/delete/modify | Block in PostToolUse | | MCP/HTTP/WebSocket calls | Direct Claude context modification | | UserPromptSubmit stdout to context | Delete existing context | | PreToolUse/PermissionRequest input modification | Cancel already-executed tools | | Continue work from Stop | Unlimited forcing (infinite loop risk) |
Data Passing Methods (Important)
stdin JSON (Verified)
All session/project info is passed via stdin JSON:
session_id- Session UUIDcwd- Project directorytranscript_path- Session log file pathtool_use_id- Tool call ID (PreToolUse/PostToolUse)
stdin JSON Structure by Event
# UserPromptSubmit
{"prompt": "user message", "session_id": "...", "cwd": "/path"}
# PreToolUse / PostToolUse
{"tool_name": "Bash", "tool_input": {"command": "npm test"}, "session_id": "..."}
# PermissionRequest
{"tool_name": "Bash", "tool_input": {...}, "permission_type": "execute"}
# Stop
{"stop_reason": "end_turn", "session_id": "..."}
# SubagentStop
{"agent_name": "backend-dev", "result": "...", "session_id": "..."}
Environment Variables (Verified)
CLAUDE_PROJECT_DIR, CLAUDE_SESSION_ID etc. are NOT environment variables!
Actually set environment variables:
CLAUDE_CODE_ENABLE_CFC="false"
CLAUDE_CODE_ENTRYPOINT="cli"
Settings Reload
settings.jsonchanges only apply in new sessions
Exit Code Reference
| Exit Code | Meaning | Behavior | |-----------|---------|----------| | 0 | Success/Allow | Normal proceed | | 1 | Error | Hook failure, show warning | | 2 | Block/Continue | Varies by event |
Exit 2 behavior by event:
| Event | exit 2 Behavior | |-------|-----------------| | PreToolUse | Block tool execution | | PostToolUse | Ignore result (prompt retry) | | PermissionRequest | Deny permission request | | Stop | Force Claude to continue | | UserPromptSubmit | Abort prompt processing |
Hook Execution Order
Multiple hooks on same event → Sequential execution (definition order)
One hook exits 2 → Subsequent hooks don't run
Timeout Setting
{"type": "command", "command": "script.sh", "timeout": 10000}
Default: 60000ms (1 minute)
Common Mistakes
| Mistake | Problem | Solution |
|---------|---------|----------|
| Not reading stdin | Missing JSON input | INPUT=$(cat) required |
| stdout debug output | Context pollution | Use stderr (>&2) |
| exit 1 vs exit 2 confusion | Unintended behavior | exit 1=error, exit 2=block |
| Parsing without jq | Unstable | Install and use jq |
References
- Event Details - 10 event specifications
- Pattern Details - Role, usage, examples
- Orchestration Patterns - Force skill/agent activation
- Real-World Examples - Implementation case studies
- Advanced Patterns - Complex combinations