Have you ever wondered: can you automatically run some checks before Claude Code performs certain actions? Or auto-commit code when a session ends?
The Hooks system does exactly this. It lets you “jump in” at key lifecycle points of the AI Agent to execute custom logic.
What Are Hooks? Custom Interception Points
Hooks are like airport security checkpoints: set up inspection points at specific locations, and qualified passengers (operations) must pass inspection to proceed.
Claude Code supports 26 Hook events, covering the entire lifecycle:
Session Lifecycle:
SessionStart: Session beginsSetup: Repository initializationSessionEnd: Session ends
Tool Execution Lifecycle:
PreToolUse: Before tool executionPostToolUse: Tool executed successfullyPostToolUseFailure: Tool execution failedPermissionRequest: When permission requestedPermissionDenied: Permission denied
Response Lifecycle:
UserPromptSubmit: User submits promptStop: AI about to end responseStopFailure: Round ended due to API error
Multi-Agent Lifecycle:
SubagentStart: Subagent startsSubagentStop: Subagent endsTeammateIdle: Teammate becomes idleTaskCreated/Completed: Task created/completed
File and Config Changes:
FileChanged: File change detectedCwdChanged: Working directory changedConfigChange: Config changedInstructionsLoaded: CLAUDE.md loaded
Other:
PreCompact/PostCompact: Before/after compactionElicitation/ElicitationResult: MCP interactionWorktreeCreate/Remove: Worktree operations
Four Hook Types
Hooks support four execution methods:
1. command Type: Shell Commands
The most basic approach, executes shell scripts:
{
"type": "command",
"command": "echo 'Hello from hook'",
"shell": "bash",
"timeout": 60,
"if": "Bash(git *)"
}
Characteristics:
- Supports
bashandpowershell - Can use
ifcondition filtering (permission rule syntax) - Default timeout is 10 minutes,
SessionEnddefaults to 1.5 seconds
2. prompt Type: LLM Evaluation
Sends Hook input to a lightweight LLM for evaluation:
{
"type": "prompt",
"prompt": "Check if this operation is safe: $ARGUMENTS",
"model": "claude-haiku-4-5"
}
$ARGUMENTS gets replaced with Hook input JSON.
3. agent Type: Agent Validator
Starts a complete Agent loop to verify conditions:
{
"type": "agent",
"prompt": "Check if unit tests pass",
"timeout": 120,
"model": "claude-sonnet-4-6"
}
This is the most powerful type, can execute complex validations.
4. http Type: Webhook
POSTs to specified URL:
{
"type": "http",
"url": "https://example.com/hook",
"headers": {"Authorization": "Bearer $TOKEN"},
"allowedEnvVars": ["TOKEN"]
}
Note: HTTP Hook doesn’t support SessionStart and Setup events.
Exit Code Semantics: How Hooks Communicate with Claude Code
Hooks pass decisions through exit codes:
| Exit Code | Meaning | Behavior |
|---|---|---|
| 0 | Success/Allow | stdout/stderr not shown (or only in transcript mode) |
| 2 | Blocking error | stderr sent to model, blocks current operation |
| Other | Non-blocking error | stderr only shown to user, operation continues |
Different events have special behaviors with exit code 2:
- PreToolUse: Blocks tool call, stderr to model
- Stop: stderr to model, but continues conversation (implements “keep coding” mode)
- UserPromptSubmit: Blocks processing, erases original prompt, only shows stderr
- SessionStart/Setup: Blocking errors ignored (can’t block startup)
JSON Output Protocol
Beyond exit codes, Hooks can output JSON via stdout to pass structured information:
{
"continue": false,
"decision": "block",
"reason": "Code format doesn't match prettier",
"systemMessage": "Please format code before submitting"
}
For PreToolUse events, can also modify tool input:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"updatedInput": {
"file_path": "/new/path.ts"
}
}
}
Practical Configuration Examples
Example 1: PreToolUse Format Check
Automatically check format before writing TypeScript files:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "FILE=$(echo $ARGUMENTS | jq -r '.file_path') && prettier --check \"$CLAUDE_PROJECT_DIR/$FILE\" || echo '{\"decision\":\"block\",\"reason\":\"Format check failed\"}'",
"if": "Write(*.ts)",
"statusMessage": "Checking format..."
}
]
}
]
}
}
Example 2: Auto Commit
Auto-commit unsaved changes when session ends:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Check for uncommitted changes, if any, create appropriate commit and push",
"timeout": 120
}
]
}
]
}
}
Example 3: Environment Initialization
Set up development environment when session starts:
{
"hooks": {
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "echo 'export NODE_ENV=development' >> $CLAUDE_ENV_FILE",
"statusMessage": "Setting up development environment..."
}
]
}
]
}
}
Async and Background Execution
Hooks can execute in background in two ways:
Configuration Declaration
{
"type": "command",
"command": "long-running-task",
"async": true
}
Or:
{
"asyncRewake": true
}
asyncRewake special: when a background Hook exits with code 2, it wakes the model to continue processing.
Runtime Declaration
Hook outputs {"async": true} on first line, immediately enters background.
Trust Gating
Hook execution requires trust confirmation:
- Non-interactive mode (SDK): Trust implied, executes directly
- Interactive mode: Requires trust dialog confirmation
This is defense in depth: even if someone maliciously configures dangerous hooks, users won’t execute them if they don’t click “Trust.”
Configuration Sources and Merging
Hooks can come from multiple sources:
- Config snapshot (settings.json)
- Registered Hooks (SDK callbacks, plugin native hooks)
- Session Hooks (Agent frontmatter registration)
- Session function Hooks (structured output enforcer)
Sources merge by priority, support allowManagedHooksOnly policy to restrict.
Pattern Extraction
Pattern 1: Exit Code as Protocol
Shell commands and host processes need lightweight semantic communication. Define clear exit code contracts:
- 0 = Success/Allow
- 2 = Blocking error
- Other = Non-blocking error
Pattern 2: Config Snapshot Isolation
Config files may be modified at runtime. Capture snapshot at startup, use snapshot at runtime instead of reading live. Update snapshot when user explicitly modifies.
Pattern 3: Namespace Deduplication
Same Hook may appear in multiple config sources. Deduplication key includes source context, same command in different plugins stays independent, merges at same source level.
Summary
The Hooks system is the extensibility core of Claude Code:
- 26 event points: Cover complete lifecycle
- Four types: command, prompt, agent, http
- Exit code semantics: Simple effective communication protocol
- Trust gating: Secure defaults
- Config merging: Multi-source support
It’s like:
- Airport security (interception checkpoints)
- Autonomous driving takeover mechanism (human intervention)
- CI/CD pipelines (automation flow)
Understanding Hooks lets you:
- Customize Claude Code behavior
- Implement project-specific automation
- Design extension points in your own AI Agent
Next up: Agent Clusters - When AI Starts “Group Battling.”
