Skip to content
hook-capabilities logo

Claude Code Hook Capabilities

hook-capabilities

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

SKILL.md

Full skill instructions

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

RoleDescriptionExamples
GateBlock/​allow tool executionPrevent dangerous commands, workflow precondition checks
Side EffectAuto-actions after tool executionFormatters, linters, auto-commit
State ManagerWorkflow state managementState file creation/​deletion, phase tracking
External IntegratorExternal system integrationMCP calls, HTTP API, WebSocket, Slack
Context InjectorSession context injectionLoad project settings, activate services

Event Types and Characteristics

EventBlockSpecial FeaturesVerification
SessionStartNosource (compact/​new)Verified
UserPromptSubmitYesstdout auto-injects into Claude contextVerified
PreToolUseYesupdatedInput modifies input, tool_use_idVerified
PermissionRequestYesallow/​deny/​ask + input modificationUnverified
PostToolUseNotool_response (access results)Verified
StopYesstop_hook_active (loop prevention)Verified
SubagentStopYesparent-child correlation via tool_use_idUnverified
NotificationNoincludes notification_typeVerified
PreCompactNotrigger (auto/​manual)Verified
SessionEndNoOn session endUnverified

22 Universal Approaches

Control Patterns

ApproachDescriptionEvent
Iteration ControlTrack iteration count + max limitStop
Force ContinuationUse exit 2 to continue Claude workStop
Promise DetectionDetect Claude response patterns, conditional exitStop
Infinite Loop PreventionPrevent recursion via parent_tool_use_idUserPromptSubmit
Threshold BranchingBranch based on error/​warning countStop

Input Manipulation

ApproachDescriptionEvent
Input ModificationModify tool input via updatedInputPreToolUse, PermissionRequest
Path NormalizationAuto-convert relative to absolute pathsPreToolUse
Environment InjectionAuto-inject environment variablesPreToolUse
Dry-run EnforcementAuto-add --dry-run to dangerous commandsPreToolUse

Context Management

ApproachDescriptionEvent
Context Injectionstdout auto-injects into Claude contextUserPromptSubmit
Progressive LoadingLoad context/​skills on demandUserPromptSubmit
Skill Auto-ActivationKeywords trigger skill suggestionsUserPromptSubmit
Transcript ParsingRead and analyze previous responsesStop
Transcript BackupBackup session transcriptPreCompact

State Management

ApproachDescriptionEvent
Session CacheAccumulate per-session state + aggregate resultsPostToolUse
Session LifecycleInitialize/​cleanup state via SessionStart/​EndSessionStart/​End
Checkpoint CommitCheckpoint on every change, then squashPostToolUse, Stop
Session BranchingAuto-isolate Git branches per sessionPre/​PostToolUse

External Integration

ApproachDescriptionEvent
Notification ForwardingForward notifications to Slack/​Discord/​externalNotification
Desktop/​Audio Alertosascript, notify-send, TTSNotification
Subagent CorrelationTrack parent-child via tool_use_idSubagentStop

Security & Compliance

ApproachDescriptionEvent
Auto-ApprovalAuto-approve specific tools/​commandsPermissionRequest
Secret ScanningDetect and block API keys/​secretsPreToolUse
Compliance AuditCompliance logging + violation detectionPostToolUse

Implementation Techniques

ApproachDescriptionEvent
TypeScript DelegationDelegate complex logic to .tsAny
Hook ChainingExecute multiple hooks sequentiallyAny
Background ExecutionAsync via run_in_backgroundAny
Argument Pattern MatchingMatch arguments like Bash(npm test*)PreToolUse
MCP Tool MatchingMatch MCP like mcp__memory__.*PreToolUse
Prompt-Type HookLLM evaluation via type: "prompt"Any

Capabilities vs Limitations

PossibleNot Possible
File create/​delete/​modifyBlock in PostToolUse
MCP/​HTTP/​WebSocket callsDirect Claude context modification
UserPromptSubmit stdout to contextDelete existing context
PreToolUse/​PermissionRequest input modificationCancel already-executed tools
Continue work from StopUnlimited 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 CodeMeaningBehavior
0Success/​AllowNormal proceed
1ErrorHook failure, show warning
2Block/​ContinueVaries by event

Exit 2 behavior by event:

Eventexit 2 Behavior
PreToolUseBlock tool execution
PostToolUseIgnore result (prompt retry)
PermissionRequestDeny permission request
StopForce Claude to continue
UserPromptSubmitAbort 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

MistakeProblemSolution
Not reading stdinMissing JSON inputINPUT=$(cat) required
stdout debug outputContext pollutionUse stderr (>&2)
exit 1 vs exit 2 confusionUnintended behaviorexit 1=error, exit 2=block
Parsing without jqUnstableInstall and use jq

References