Back to skills
extension
Category: Development & EngineeringNo API key required

hook-capabilities

Claude Code Hook system reference for capabilities, possibilities, and limitations. Use when you want to know what hooks can do.

personAuthor: jakexiaohubgithub

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 UUID
  • cwd - Project directory
  • transcript_path - Session log file path
  • tool_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.json changes 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