| name | km.hooks-creator |
| description | Use when creating, configuring, or debugging Claude Code hooks. Triggers when users mention hooks, hook events (PreToolUse, PostToolUse, SessionStart, etc.), writing hook scripts, hook configuration files (.claude/settings.json), or automating actions at specific lifecycle points like permission requests, tool execution, session start/end, file changes, or directory changes. |
Hooks Creator
Overview
Hooks are user-defined scripts that execute automatically at specific points in Claude Code's lifecycle. They enable automation of repetitive tasks, enforcement of policies, and integration with external systems.
Hook Architecture
Hook Resolution Flow
Event Fires → Matcher Checks → Hook Handler Runs → Decision Returned → Claude Acts
Core Concepts
- Hook Event: The lifecycle point (e.g.,
PreToolUse, SessionStart)
- Matcher: Regex filter for when the hook fires (e.g.,
"Bash", "Edit|Write")
- Hook Handler: The actual command, HTTP endpoint, prompt, or agent that runs
- Decision: What happens next (allow, deny, block, add context)
Configuration Structure
Hooks live in .claude/settings.json:
{
"hooks": {
"<hook-event>": [
{
"matcher": "<regex-pattern>",
"hooks": [
{
"type": "command|http|prompt|agent",
"command|url|prompt": "...",
"timeout": 600,
"async": false
}
]
}
]
}
}
Hook Events Quick Reference
| Event | Fires When | Can Block? |
|---|
SessionStart | Session begins/resumes | No |
UserPromptSubmit | User submits prompt | Yes (block) |
PreToolUse | Before tool executes | Yes (deny/allow) |
PostToolUse | After tool succeeds | Yes (block) |
PostToolUseFailure | After tool fails | No |
PermissionRequest | Permission dialog shown | Yes (allow/deny) |
Stop | Claude stops responding | Yes (block) |
ConfigChange | Settings file changes | Yes (block) |
FileChanged | Watched file changes | No |
CwdChanged | Working directory changes | No |
Notification | Claude sends notification | No |
Creating Hooks
Step 1: Choose the Right Event
Ask: "When do I need to intervene?"
- Block dangerous commands:
PreToolUse with Bash matcher
- Log all actions:
PostToolUse with broad matcher
- Enforce policies:
PermissionRequest or PreToolUse
- Setup on start:
SessionStart
- React to file changes:
FileChanged + CwdChanged
Step 2: Write the Hook Script
#!/bin/bash
INPUT=$(cat)
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf\|DROP TABLE'; then
jq -n '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Dangerous command blocked"
}
}'
exit 0
fi
exit 0
Step 3: Output Formats
Exit Codes:
0: Success, action proceeds
2: Blocking error, action is blocked
JSON Output (exit 0):
{
"continue": true,
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"additionalContext": "Environment check passed"
}
}
For UserPromptSubmit / Stop / PostToolUse:
{
"decision": "block",
"reason": "Tests must pass first"
}
Common Patterns
Pattern 1: Block Dangerous Commands
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/block-dangerous.sh"
}
]
}
]
}
}
Pattern 2: Async Logging
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/log-changes.sh",
"async": true
}
]
}
]
}
}
Pattern 3: Session Environment Setup
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/setup-env.sh"
}
]
}
]
}
}
Pattern 4: Permission Auto-Approve
{
"hooks": {
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/auto-approve-safe.sh"
}
]
}
]
}
}
Writing Hook Scripts
Best Practices
- Always quote variables:
"$VAR" not $VAR
- Validate input: Never trust JSON structure blindly
- Handle errors: Exit 2 for blocking errors
- Keep it fast: Use
async: true for non-blocking tasks
- Use absolute paths: Reference scripts via
$CLAUDE_PROJECT_DIR
Environment Variables
Hooks have access to:
$CLAUDE_PROJECT_DIR: Project root
$CLAUDE_CODE_REMOTE: "true" if in remote web environment
$CLAUDE_ENV_FILE: File to persist env vars (SessionStart, CwdChanged, FileChanged)
JSON Input Access
For PreToolUse on Bash:
{
"session_id": "abc123",
"cwd": "/project",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite",
"timeout": 120000
}
}
Troubleshooting
Hook Not Firing
- Check matcher regex is correct
- Verify event name spelling
- Use
/hooks menu to inspect configuration
JSON Validation Failed
- Ensure stdout contains ONLY JSON on exit 0
- Check shell profile isn't printing extra text
- Use
jq -n to construct output
Blocking Doesn't Work
- Exit code must be 0 for JSON decision
- Exit code 2 blocks but shows stderr as error
- Use
hookSpecificOutput for PreToolUse
File Locations
| Scope | Location | Shareable |
|---|
| User | ~/.claude/settings.json | No |
| Project | .claude/settings.json | Yes |
| Local | .claude/settings.local.json | No (gitignored) |
| Plugin | <plugin>/hooks/hooks.json | Yes |
Testing Hooks
Debug Mode
claude --debug
Shows hook execution details, matching, exit codes.
Manual Testing
- Create the hook script
- Add minimal config
- Trigger the event
- Check
/hooks menu for status
Verbose Output
Press Ctrl+O to see hook progress in transcript.