STARTER_CHARACTER = 🪝
Setup
First, update the reference docs to get the latest from Anthropic:
python ~/.claude/skills/creating-hooks/scripts/update-docs.py
What Hooks Are
Shell commands that execute at lifecycle points in Claude Code. Unlike prompts, hooks are deterministic—they always run when triggered.
Configuration
Hooks live in settings files:
-
~/.claude/settings.json
-
User settings (all projects)
-
.claude/settings.json
-
Project settings (shared via git)
-
.claude/settings.local.json
-
Local project settings (not committed)
{ "hooks": { "EventName": [ { "matcher": "ToolPattern", "hooks": [ { "type": "command", "command": "your-command-here" } ] } ] } }
Matcher: Pattern to match tool names (case-sensitive)
-
Exact match: Write
-
Regex: Edit|Write
-
All tools: * or omit
Environment variables:
-
$CLAUDE_PROJECT_DIR
-
Absolute path to project root
-
$CLAUDE_ENV_FILE
-
File path for persisting env vars (SessionStart only)
Hook Events
Tool events (matcher applies):
-
PreToolUse
-
Before tool executes
-
PostToolUse
-
After tool completes
-
PermissionRequest
-
Permission dialog shown
Session events:
-
SessionStart
-
Session begins/resumes (matcher: startup/resume/clear/compact)
-
SessionEnd
-
Session ends
-
PreCompact
-
Before compaction (matcher: manual/auto)
Other events:
-
UserPromptSubmit
-
User submits prompt
-
Stop
-
Agent finishes
-
SubagentStop
-
Subagent finishes
-
Notification
-
Alerts sent (matcher: notification type)
Exit Codes
-
0: Success. stdout shown in verbose mode. For UserPromptSubmit /SessionStart , stdout added to context.
-
2: Block. stderr fed to Claude as error message. Blocks the action.
-
Other: Non-blocking error. stderr shown to user.
JSON Output
For advanced control, return JSON to stdout with exit code 0:
{ "continue": false, "stopReason": "Message shown when stopping" }
PreToolUse Control
{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "allow", "permissionDecisionReason": "Auto-approved", "updatedInput": { "field": "modified value" } } }
Decisions: "allow" (bypass permission), "deny" (block), "ask" (prompt user)
PostToolUse Feedback
{ "decision": "block", "reason": "Explanation fed to Claude" }
Stop/SubagentStop Control
{ "decision": "block", "reason": "Must fix X before stopping" }
Hook Input
Hooks receive JSON via stdin:
{ "session_id": "abc123", "transcript_path": "/path/to/transcript.jsonl", "cwd": "/current/dir", "permission_mode": "default", "hook_event_name": "PreToolUse", "tool_name": "Write", "tool_input": { "file_path": "/path", "content": "..." } }
Common Patterns
Auto-format after edit:
{ "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 {}'" }] }] } }
Block dangerous commands:
{ "hooks": { "PreToolUse": [{ "matcher": "Bash", "hooks": [{ "type": "command", "command": ""$CLAUDE_PROJECT_DIR"/.claude/hooks/validate-bash.py" }] }] } }
Inject context on prompt:
{ "hooks": { "UserPromptSubmit": [{ "hooks": [{ "type": "command", "command": "echo '[REMINDER: Follow TDD]'" }] }] } }
Desktop notification:
{ "hooks": { "Notification": [{ "hooks": [{ "type": "command", "command": "osascript -e 'display notification "Claude needs input" with title "Claude Code"'" }] }] } }
Hook Scripts
For complex logic, use external scripts. UV single-file format works well:
#!/usr/bin/env -S uv run --script
/// script
dependencies = []
requires-python = ">=3.11"
///
import json import sys
data = json.load(sys.stdin) tool_input = data.get("tool_input", {})
Validation logic here
if should_block: print("Error message", file=sys.stderr) sys.exit(2)
sys.exit(0)
Anti-Patterns
-
Using exit code 2 without stderr message (Claude gets no feedback)
-
Forgetting to handle JSON parsing errors in scripts
-
Blocking without explaining why (Claude will retry the same thing)
-
Long-running hooks without timeout (default is 60s)
-
Modifying files in PreToolUse (use PostToolUse for modifications)
Reference
-
references/anthropic-hooks.md - Complete reference (input schemas, prompt hooks, MCP tools)
-
references/anthropic-hooks-guide.md - Quickstart and examples