Claude Code Hooks: Complete Guide to All 12 Lifecycle Events
Master Claude Code hooks with exit codes, JSON output, and production patterns. Stop clicking approve and fully automate your workflow.
Problem: You're deep in flow state, building a feature. Claude needs to write a file. Click approve. Run a command. Click approve. Format code. Click approve. Twenty interruptions later, you've forgotten what you were building.
Quick Win: Add this to .claude/settings.json and never approve a Prettier format again:
Every file Claude writes now auto-formats. Zero clicks. Zero context switches.
The 12 Hook Lifecycle Events
Hooks intercept Claude Code events and execute shell commands or LLM prompts. Here's the complete list:
| Hook | When It Fires | Can Block? | Best Use |
|---|---|---|---|
| SessionStart | Session begins or resumes | NO | Load context, set env vars |
| UserPromptSubmit | You hit enter | YES | Context injection, validation |
| PreToolUse | Before tool runs | YES | Security blocking, auto-approve (extends permission system) |
| PermissionRequest | Permission dialog appears | YES | Auto-approve/deny |
| PostToolUse | After tool succeeds | NO* | Auto-format, lint, log |
| PostToolUseFailure | After tool fails | NO | Error handling |
| SubagentStart | Spawning subagent | NO | Subagent initialization |
| SubagentStop | Subagent finishes | YES | Subagent validation |
| Stop | Claude finishes responding | YES | Task enforcement |
| PreCompact | Before compaction | NO | Transcript backup |
| Setup | With --init/--maintenance | NO | One-time setup |
| SessionEnd | Session terminates | NO | Cleanup, logging |
| Notification | Claude sends notification | NO | Desktop alerts, TTS |
*PostToolUse can prompt Claude with feedback but cannot undo the tool execution.
Exit Codes: The Control Mechanism
Every hook communicates via exit codes:
| Exit Code | What Happens |
|---|---|
| 0 | Success - hook ran, stdout processed for JSON |
| 2 | Block - operation stopped, stderr sent to Claude |
| Other | Error - stderr shown to user, execution continues |
Exit code 2 is your power tool. A PreToolUse hook that exits 2 stops the tool. A Stop hook that exits 2 forces Claude to keep working.
Hook Types: Command vs Prompt
Command hooks run bash scripts:
Prompt hooks use LLM evaluation (great for Stop/SubagentStop):
The LLM responds with {"ok": true} or {"ok": false, "reason": "..."}.
Async Hooks (Non-Blocking) New - Jan 2026
Add async: true to run hooks in the background without blocking Claude's execution. Released by Anthropic in January 2026:
Best for:
- Logging and analytics
- Backup creation (PreCompact)
- Notifications
- Any side-effect that shouldn't slow things down
Not suitable for:
- Security blocking (PreToolUse with exit code 2)
- Auto-approve decisions (PermissionRequest)
- Any hook where Claude needs the result
JSON Output: Advanced Control
Beyond exit codes, hooks can return structured JSON for precise control.
PreToolUse Decisions
"allow": Bypasses permission system"deny": Blocks tool, tells Claude why"ask": Prompts user for confirmationupdatedInput: Modify tool parameters before execution
PermissionRequest Decisions
Stop/SubagentStop Enforcement
Hook 1: Auto-Format on Save
Run formatters after every file write:
Multiple hooks run in parallel. Format AND lint before Claude's response appears.
Hook 2: Session Context Injection
Load context when sessions start:
Persist Environment Variables
SessionStart and Setup hooks can set environment variables for the session:
Hook 3: Security Blocking
Block dangerous operations with PreToolUse:
Hook 4: Auto-Approve Safe Commands
Use PermissionRequest to auto-approve without prompts:
Hook 5: Transcript Backup
Save transcripts before compaction with PreCompact. Use async: true since backups don't need to block Claude:
Use matchers manual or auto to distinguish between /compact and auto-compact.
Hook 6: Task Completion Enforcement
Use Stop hooks to ensure work is complete:
See the Stop Hook guide for command-based patterns.
Hook 7: Skill Activation
The Skill Activation Hook intercepts prompts and appends skill recommendations:
Hook 8: Threshold-Based Backups via StatusLine
StatusLine is the only mechanism that receives live context metrics. Use it to trigger backups at thresholds:
Critical: The remaining_percentage field includes the 22.5% autocompact buffer. To get actual "free until autocompact":
Trigger backups when crossing thresholds:
Unlike PreCompact (which triggers on compaction), StatusLine-based backups capture state proactively at configurable thresholds.
Architecture: Three-File Structure
The backup system uses a clean separation of concerns:
| File | Trigger | Responsibility |
|---|---|---|
backup-core.mjs | Called by others | Parse transcript, format markdown, save file, update state |
statusline-monitor.mjs | StatusLine (continuous) | Monitor context %, detect thresholds, display status |
conv-backup.mjs | PreCompact hook | Handle pre-compaction event |
This architecture means backup logic lives in one place. Changes to formatting, file naming, or state management only need to be made in backup-core.mjs.
Backup File Naming
Backups use numbered filenames with timestamps for history:
StatusLine Display
When context drops below 30%, the statusline shows the current backup path:
This tells you exactly which file to load after compaction.
State Tracking
Both StatusLine and PreCompact hooks update a shared state file at ~/.claude/claudefast-statusline-state.json:
Recommended workflow: When compaction occurs, run /clear to start a fresh session, then load the backup file shown in the statusline. This avoids confusion from having both compaction summary and injected context.
Hook 9: Setup Hooks for Installation and Maintenance
Setup hooks run before your session starts, triggered by special CLI flags:
Configure them with matchers in settings.json:
The power move: Pass a prompt after the flag:
The hook runs first (deterministic), then the /install command executes (agentic). This combines predictable script execution with intelligent agent oversight.
See the complete Setup Hooks Guide for the full pattern including interactive onboarding and justfile integration.
Configuration Locations
| Location | Scope | Priority |
|---|---|---|
| Managed policy | Enterprise | Highest |
.claude/settings.json | Project (shared) | High |
.claude/settings.local.json | Project (personal) | Medium |
~/.claude/settings.json | All projects | Lowest |
Disabling and Restricting Hooks
Disable All Hooks
If hooks are causing issues or you want a clean baseline, set disableAllHooks to true in your settings:
This turns off all hooks across every scope (user, project, and local settings). Useful for debugging or when a hook causes unexpected behavior.
Managed Hook Restrictions
For organizations that need centralized control, administrators can set allowManagedHooksOnly to true in managed settings:
When enabled, only hooks defined in the managed settings file and SDK hooks are allowed. User-level, project-level, and plugin hooks are all blocked. This prevents developers from adding hooks that could bypass organizational security policies.
This pairs well with allowManagedPermissionRulesOnly, which applies the same restriction to permission rules. Together, these settings give administrators full control over both the permission system and its hook-based extensions.
Matcher Syntax
| Pattern | Matches |
|---|---|
"" or omitted | All tools |
"Bash" | Only Bash (exact, case-sensitive) |
"Write|Edit" | Write OR Edit (regex) |
"mcp__memory__.*" | All memory MCP tools |
Critical: No spaces around |. Matchers are case-sensitive.
Event-Specific Matchers
SessionStart: startup, resume, clear, compact
PreCompact: manual, auto
Setup: init, maintenance
Notification: permission_prompt, idle_prompt, auth_success
Environment Variables
| Variable | Description |
|---|---|
CLAUDE_PROJECT_DIR | Project root (all hooks) |
CLAUDE_ENV_FILE | Persist env vars (SessionStart, Setup) |
CLAUDE_CODE_REMOTE | "true" if web, empty if CLI |
Debugging
Hook not triggering?
- Check matcher syntax (case-sensitive, no spaces)
- Verify settings file location
- Test:
echo '{"session_id":"test"}' | python your-hook.py
Command failing?
- Add logging:
command 2>&1 | tee ~/.claude/hook-debug.log - Run with debug:
claude --debug
Infinite loops with Stop?
- Always check
stop_hook_activeflag first
Start With One Hook
Pick your biggest friction point:
- Constant formatting? PostToolUse formatter
- Approving safe commands? PermissionRequest auto-approve
- Missing context? SessionStart injection
- Losing progress? PreCompact backup
- Incomplete tasks? Stop enforcement
One hook. One friction point eliminated. Then iterate.
Next Steps
- Configure Setup Hooks for automated onboarding and maintenance
- Configure the Stop Hook to enforce task completion
- Install the Permission Hook for intelligent auto-approval
- Set up Skill Activation for automatic skill loading
- Configure Context Recovery to survive compaction
- Explore Session Lifecycle Hooks for setup and cleanup
- Master permission rules and modes for static permission control alongside hooks
- Learn configuration basics for complete Claude Code setup
Last updated on