Complete reference for Claude Code hooks system (January 2026). Use when creating hooks, understanding hook events, matchers, exit codes, JSON output control, environment variables, plugin hooks, or implementing hook scripts.
Instalar com Codex ou Claude Copie este prompt, cole no Codex, Claude ou outro assistente e deixe que ele revise a página da skill e instale para você.
Um comando direto ignora o prompt de revisão. Verifique a origem antes de executá-lo.
Complete reference for Claude Code hooks system (January 2026). Use when creating hooks, understanding hook events, matchers, exit codes, JSON output control, environment variables, plugin hooks, or implementing hook scripts.
user-invocable
true
Claude Code Hooks System - Complete Reference (January 2026)
Hooks execute custom commands or prompts in response to Claude Code events. Use for automation, validation, formatting, and security.
All Hook Events
Event
When Fired
Matcher Applies
Common Uses
PreToolUse
Before tool execution
Yes
Validation, blocking
PermissionRequest
When user shown permission dialog
Yes
Auto-approval policies
PostToolUse
After successful tool execution
Yes
Formatting, linting
PostToolUseFailure
After tool fails
Yes
Error handling
Notification
When Claude wants attention
Yes
Custom notifications
UserPromptSubmit
User submits prompt
No
Input validation
Stop
Claude finishes response
No
Cleanup, final checks
SubagentStart
When spawning a subagent
No
Subagent initialization
SubagentStop
Subagent (Task tool) completes
No
Result validation
PreCompact
Before context compaction
Yes
State backup
Setup
Repository setup/maintenance
Yes
One-time operations
SessionStart
Session begins or resumes
Yes
Environment setup
SessionEnd
Session ends
No
Cleanup, persistence
Configuration
Configuration Locations (Precedence highest to lowest)
Managed - managed-settings.json (enterprise)
Local - .claude/settings.local.json (gitignored)
Project - .claude/settings.json (shared via git)
User - ~/.claude/settings.json (personal)
Plugin - hooks/hooks.json or frontmatter
Capability - Skill/Command/Agent frontmatter
Note: Enterprise administrators can use allowManagedHooksOnly to block user, project, and plugin hooks.
Structure
Hooks are organized by matchers, where each matcher can have multiple hooks:
Plugins can provide hooks that integrate with user and project hooks. For complete plugin documentation including plugin.json schema, directory structure, and component integration, see ./claude-plugins-reference-2026/SKILL.md.
How Plugin Hooks Work
Plugin hooks defined in hooks/hooks.json or custom path via hooks field in plugin.json
When plugin enabled, its hooks merge with user and project hooks
Multiple hooks from different sources can respond to same event
Plugin hooks run alongside custom hooks in parallel
Plugin Hook Configuration
Hooks can be configured in hooks/hooks.json or inline in plugin.json:
${CLAUDE_PLUGIN_ROOT}: Absolute path to the plugin directory
${CLAUDE_PROJECT_DIR}: Project root directory
All standard environment variables available
Hooks in Skills, Agents, and Slash Commands
Hooks can be defined in frontmatter. These are scoped to the component's lifecycle. For complete skill documentation, see ./claude-skills-reference-2026/SKILL.md.
{"session_id":"abc123","transcript_path":"/path/to/session.jsonl","cwd":"/path/to/project","permission_mode":"default","hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"psql -c 'SELECT * FROM users'","description":"Query the users table","timeout":120000},"tool_use_id":"toolu_01ABC123"}
{"session_id":"abc123","transcript_path":"/path/to/session.jsonl","cwd":"/path/to/project","permission_mode":"default","hook_event_name":"Notification","message":"Claude needs your permission to use Bash","notification_type":"permission_prompt"}
UserPromptSubmit Input
{"session_id":"abc123","transcript_path":"/path/to/session.jsonl","cwd":"/path/to/project","permission_mode":"default","hook_event_name":"UserPromptSubmit","prompt":"Write a function to calculate factorial"}
reason values: clear, logout, prompt_input_exit, other
Hook Output
Exit Codes
Code
Behavior
0
Success. stdout processed (JSON or plain text)
2
Blocking error. stderr used as error message, fed back to Claude
Other
Non-blocking error. stderr shown in verbose mode (Ctrl+O)
Important: Claude Code does not see stdout if exit code is 0, except for UserPromptSubmit and SessionStart where stdout is added to context.
Exit Code 2 Behavior Per Event
Event
Exit Code 2 Behavior
PreToolUse
Blocks tool call, shows stderr to Claude
PermissionRequest
Denies permission, shows stderr to Claude
PostToolUse
Shows stderr to Claude (tool already ran)
PostToolUseFailure
Shows stderr to Claude (tool already failed)
Notification
Shows stderr to user only
UserPromptSubmit
Blocks prompt, erases it, shows stderr to user
Stop
Blocks stoppage, shows stderr to Claude
SubagentStart
Shows stderr to user only
SubagentStop
Blocks stoppage, shows stderr to Claude subagent
PreCompact
Shows stderr to user only
Setup
Shows stderr to user only
SessionStart
Shows stderr to user only
SessionEnd
Shows stderr to user only
JSON Output Control
Important: JSON output only processed with exit code 0. Exit code 2 uses stderr only.
Common JSON Fields (All Events)
{"continue":true,"stopReason":"Message shown when continue is false","suppressOutput":false,"systemMessage":"Optional warning message shown to user"}
Field
Type
Effect
continue
boolean
false stops Claude (takes precedence over all)
stopReason
string
Shown to user when continue is false
suppressOutput
boolean
Hide stdout from transcript mode
systemMessage
string
Warning message shown to user
Precedence: continue: false takes precedence over any decision: "block" output.
PreToolUse JSON Output
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"Auto-approved documentation file","updatedInput":{"field_to_modify":"new value"},"additionalContext":"Current environment: production. Proceed with caution."}}
Field
Values
Effect
permissionDecision
allow, deny, ask
Controls tool execution
permissionDecisionReason
string
Shown to user (allow/ask) or Claude (deny)
updatedInput
object
Modifies tool input before execution
additionalContext
string
Added to Claude's context
Note: decision and reason fields are deprecated. Use hookSpecificOutput.permissionDecision and hookSpecificOutput.permissionDecisionReason. Deprecated "approve" and "block" map to "allow" and "deny".
PermissionRequest JSON Output
Allow with modified input:
{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow","updatedInput":{"command":"npm run lint"}}}}
Deny with message:
{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"deny","message":"Command not allowed by policy","interrupt":true}}}
PostToolUse JSON Output
{"decision":"block","reason":"Explanation for decision","hookSpecificOutput":{"hookEventName":"PostToolUse","additionalContext":"Additional information for Claude"}}
Note: Multiple hooks' additionalContext values are concatenated.
Prompt-Based Hooks
LLM-evaluated decisions using a fast model (Haiku). Also known as "agent hooks" for complex verification tasks.
How Prompt-Based Hooks Work
Send the hook input and your prompt to Haiku
The LLM responds with structured JSON containing a decision
Claude Code processes the decision automatically
Configuration
{"type":"prompt","prompt":"Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete.","timeout":30}
Alternatively, use "type": "agent" for complex verification tasks that require tool access.
Field
Required
Description
type
Yes
"prompt" for LLM evaluation, "agent" for tools
prompt
Yes
Prompt text sent to LLM
timeout
No
Seconds (default: 30 for prompt, 60 for agent)
Response Schema
The LLM must respond with JSON:
{"ok":true,"reason":"Explanation for the decision"}
Field
Type
Description
ok
boolean
true allows the action, false prevents it
reason
string
Required when ok is false. Shown to Claude
$ARGUMENTS Placeholder
Use $ARGUMENTS in prompt to include hook input JSON. If omitted, input is appended to the prompt.
Example: Intelligent Stop Hook
{"hooks":{"Stop":[{"hooks":[{"type":"prompt","prompt":"You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.","timeout":30}]}]}}
Example: SubagentStop Validation
{"hooks":{"SubagentStop":[{"hooks":[{"type":"prompt","prompt":"Evaluate if this subagent should stop. Input: $ARGUMENTS\n\nCheck if:\n- The subagent completed its assigned task\n- Any errors occurred that need fixing\n- Additional context gathering is needed\n\nReturn: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"explanation\"} to continue."}]}]}}
Best Use Cases
Event
Use Case
Stop
Intelligent task completion detection
SubagentStop
Verify subagent completed task
UserPromptSubmit
Context-aware prompt validation
PreToolUse
Complex permission decisions
PermissionRequest
Intelligent allow/deny dialogs
Comparison with Command Hooks
Feature
Command Hooks
Prompt Hooks
Execution
Runs bash script
Queries LLM
Decision logic
You implement in code
LLM evaluates context
Setup complexity
Requires script file
Configure prompt only
Context awareness
Limited to script
Natural language understanding
Performance
Fast (local)
Slower (API call)
Use case
Deterministic rules
Context-aware decisions
Best Practices for Prompt Hooks
Be specific in prompts - Clearly state what you want the LLM to evaluate
Include decision criteria - List the factors the LLM should consider
Test your prompts - Verify the LLM makes correct decisions for your use cases
Set appropriate timeouts - Default is 30 seconds, adjust if needed
Use for complex decisions - Bash hooks are better for simple, deterministic rules
Important Hook Events
Setup Hook
Runs when Claude Code is invoked with repository setup and maintenance flags (--init, --init-only, or --maintenance).
Use Setup hooks for:
One-time or occasional operations (dependency installation, migrations, cleanup)
Operations you don't want on every session start
Matchers:
init - Invoked from --init or --init-only flags
maintenance - Invoked from --maintenance flag
Key characteristics:
Requires explicit flags because running automatically would slow down every session start
Has access to CLAUDE_ENV_FILE for persisting environment variables
Output added to Claude's context
SessionStart Hook
Runs when Claude Code starts a new session or resumes an existing session.
Use SessionStart hooks for:
Loading development context (existing issues, recent changes)
Setting up environment variables
Important: For one-time operations like installing dependencies or running migrations, use Setup hooks instead. SessionStart runs on every session, so keep these hooks fast.
Matchers:
startup - New sessions
resume - Resumed sessions (from --resume, --continue, or /resume)
clear - After /clear command
compact - After auto or manual compact
PostToolUseFailure Hook
Runs immediately after a tool fails (returns an error). This complements PostToolUse, which only runs on successful tool execution.
Use PostToolUseFailure hooks for:
Error recovery actions
Logging tool failures
Custom error handling and reporting
Recognizes the same matcher values as PreToolUse and PostToolUse.
SubagentStart Hook
Runs when a Claude Code subagent (Task tool call) is spawned.
Use SubagentStart hooks for:
Subagent initialization
Logging subagent creation
Context injection for specific agent types
Input includes:
agent_id: Unique identifier for the subagent
agent_type: Agent name (built-in like "Bash", "Explore", "Plan", or custom agent names)
USE AT YOUR OWN RISK: Claude Code hooks execute arbitrary shell commands on your system automatically. By using hooks, you acknowledge that:
You are solely responsible for the commands you configure
Hooks can modify, delete, or access any files your user account can access
Malicious or poorly written hooks can cause data loss or system damage
Anthropic provides no warranty and assumes no liability for any damages
You should thoroughly test hooks in a safe environment before production use
Security Best Practices
Validate and sanitize inputs - Never trust input data blindly
Always quote shell variables - Use "$VAR" not $VAR
Block path traversal - Check for .. in file paths
Use absolute paths via the $CLAUDE_PROJECT_DIR variable - Specify full paths for scripts (use $CLAUDE_PROJECT_DIR)
Skip sensitive files - Avoid .env, .git/, keys, etc.
Configuration Safety
Direct edits to hooks in settings files don't take effect immediately:
Hooks snapshot captured at startup
Snapshot used throughout the session
Warns if hooks are modified externally
Requires review in /hooks menu for changes to apply
This prevents malicious hook modifications from affecting your current session.
Debugging
Enable Debug Mode
claude --debug
claude --debug "hooks"# Filter to hooks only
Debug Output Example
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 60000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
Basic Troubleshooting
Check configuration - Run /hooks to see if your hook is registered
Verify syntax - Ensure your JSON settings are valid
Test commands - Run hook commands manually first
Check permissions - Make sure scripts are executable
Review logs - Use claude --debug to see hook execution details
Validate plugin hooks - Use claude plugin validate or /plugin validate for plugin-level hooks
Common Issues
Problem
Cause
Fix
Hook not running
Wrong matcher pattern
Check case-sensitivity, regex
Command not found
Relative path
Use $CLAUDE_PROJECT_DIR
JSON not processed
Non-zero exit code
Exit 0 for JSON processing
Hook times out
Slow script
Optimize or increase timeout
Quotes breaking
Unescaped in JSON
Use \" inside JSON strings
Plugin hook not load
Invalid plugin.json hooks config
Validate with claude plugin validate .
Path not found
Missing ${CLAUDE_PLUGIN_ROOT}
Use variable for plugin scripts
Validation Commands
For plugin hooks:
# CLI (from terminal)
claude plugin validate .
claude plugin validate ./path/to/plugin
# In Claude Code session
/plugin validate .
/plugin validate ./path/to/plugin