Implement Claude Code hooks for deterministic control over agent behavior. Use when creating custom hooks for notifications, auto-formatting, logging, feedback, permissions, or lifecycle events.
Hooks are shell commands that execute at specific points in Claude Code's lifecycle, providing deterministic control over behavior.
| Event | When it runs | Can block? |
|---|---|---|
PreToolUse |
Before tool execution | Yes |
PermissionRequest |
When permission dialog shown | Yes |
PostToolUse |
After tool completes | Feedback only |
UserPromptSubmit |
When user submits prompt | Yes |
Notification |
When notification sent | No |
Stop |
When agent finishes | Can continue |
SubagentStop |
When subagent finishes | Can continue |
PreCompact |
Before compact operation | No |
SessionStart |
Session starts/resumes | No |
SessionEnd |
Session ends | No |
Hooks are defined in settings files:
~/.claude/settings.json - User settings (all projects).claude/settings.json - Project settings.claude/settings.local.json - Local settings (not committed){
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here",
"timeout": 60
}
]
}
]
}
}
Matcher patterns:
Write, Bash, ReadEdit|Write, Notebook.*, mcp__memory__.** or ""Hooks receive JSON via stdin:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/current/directory",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": { "file_path": "/path/to/file", "content": "..." },
"tool_use_id": "toolu_01ABC..."
}
{
"decision": "block",
"reason": "Explanation for Claude",
"continue": true,
"stopReason": "Message when continue=false",
"systemMessage": "Warning for user"
}
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.claude/bash.log"
}]
}
]
}
}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs -I{} sh -c 'echo \"{}\" | grep -q \"\\.ts$\" && npx prettier --write \"{}\"'"
}]
}
]
}
}
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [{
"type": "command",
"command": "jq -e '.tool_input.file_path | test(\"\\\\.env|secrets|credentials\")' > /dev/null && echo 'Cannot modify sensitive files' >&2 && exit 2 || exit 0"
}]
}
]
}
}
CLAUDE_PROJECT_DIR - Absolute path to project rootCLAUDE_PLUGIN_ROOT - Plugin directory (for plugin hooks)CLAUDE_ENV_FILE - File to persist env vars (SessionStart only)CLAUDE_CODE_REMOTE - "true" if running in remote/web environment