Building Hooks Skill
You are an expert at creating Claude Code event hooks. Hooks are event-driven automation that execute in response to specific events like tool invocations, user prompts, or session lifecycle events.
When to Create Hooks
Use HOOKS when:
- You need event-driven automation
- You want to validate or block tool usage
- You need to enforce policies automatically
- You want to log or audit Claude's actions
- You need pre/post-processing for tool invocations
Use COMMANDS instead when:
- The user explicitly triggers an action
- You need manual invocation
Use AGENTS/SKILLS instead when:
- You need Claude's reasoning and generation
- The task requires LLM capabilities
Hook Schema & Structure
File Location
- Project-level:
.claude/hooks.json - Project settings:
.claude/settings.json(hooks section) - Directory-specific:
.claude-hooks.json(in any directory) - Plugin-level:
plugin-dir/hooks/hooks.json
File Format
JSON configuration file.
Schema Structure
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "bash command to execute"
}
]
}
]
}
}
Event Types
Events WITH Matchers (Tool-Specific)
PreToolUse: Before a tool runs
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{"type": "command", "command": "bash validate.sh"}]
}
]
}
}
PostToolUse: After a tool completes successfully
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{"type": "command", "command": "bash format.sh"}]
}
]
}
}
Events WITHOUT Matchers (Lifecycle Events)
UserPromptSubmit: When user submits a prompt
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [{"type": "command", "command": "bash log-prompt.sh"}]
}
]
}
}
Stop: When Claude finishes responding
{
"hooks": {
"Stop": [
{
"hooks": [{"type": "command", "command": "bash cleanup.sh"}]
}
]
}
}
SessionStart: When session starts
{
"hooks": {
"SessionStart": [
{
"hooks": [{"type": "command", "command": "bash setup.sh"}]
}
]
}
}
Other Events:
- Notification: When Claude sends an alert
- SubagentStop: When a subagent completes
- PreCompact: Before transcript compaction
Matcher Patterns
For PreToolUse and PostToolUse events:
| Pattern | Matches | Example |
|---|---|---|
"Write" | Exact tool name | Matches only Write tool |
"Edit|Write" | Regex OR | Matches Edit or Write |
"Bash" | Single tool | Matches Bash tool |
"*" | Wildcard | Matches ALL tools |
"Notebook.*" | Regex pattern | Matches NotebookEdit, etc. |
"" | Empty (for non-tool events) | For lifecycle events |
Hook Types
Type 1: Command Hook
Execute a bash command:
{
"type": "command",
"command": "bash /path/to/script.sh"
}
Use for:
- Validation scripts
- Formatting tools
- Logging and auditing
- File system operations
Type 2: Prompt Hook (LLM-based)
Use LLM for evaluation:
{
"type": "prompt",
"prompt": "Analyze the tool usage and determine if it's safe"
}
Use for:
- Complex policy evaluation
- Context-aware decisions
- Natural language analysis
Hook Return Values
Hooks can return structured JSON to control behavior:
{
"continue": true,
"decision": "approve",
"reason": "Explanation for the decision",
"suppressOutput": false,
"systemMessage": "Optional message shown to user",
"hookSpecificOutput": {
"permissionDecision": "approve",
"permissionDecisionReason": "Safe operation",
"additionalContext": "Extra context for Claude"
}
}
Key Fields
continue:trueto proceed,falseto stopdecision:"approve","block", or"warn"reason: Explanation for the decisionsuppressOutput: Hide hook output from transcriptsystemMessage: Message displayed to userpermissionDecision: For tool permission hooksadditionalContext: Context added to Claude's knowledge
Exit Codes
0: Success (stdout shown in transcript mode)2: Blocking error (stderr fed to Claude)- Other: Non-blocking error
Common Hook Patterns
Pattern 1: Validation Hook (PreToolUse)
Validate tool usage before execution:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash /path/to/validate-write.sh"
}
]
}
]
}
}
Example validate-write.sh:
#!/bin/bash
# Check if writing to protected directory
FILE_PATH="$1"
if [[ "$FILE_PATH" == /protected/* ]]; then
echo '{"decision": "block", "reason": "Cannot write to protected directory"}'
exit 2
fi
echo '{"decision": "approve", "reason": "Path is valid"}'
exit 0
Pattern 2: Formatting Hook (PostToolUse)
Auto-format files after writing:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash /path/to/format-file.sh"
}
]
}
]
}
}
Example format-file.sh:
#!/bin/bash
FILE_PATH="$1"
if [[ "$FILE_PATH" == *.py ]]; then
black "$FILE_PATH"
elif [[ "$FILE_PATH" == *.js ]]; then
prettier --write "$FILE_PATH"
fi
exit 0
Pattern 3: Logging Hook (All Tools)
Log all tool usage:
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash /path/to/log-tool.sh"
}
]
}
]
}
}
Pattern 4: Security Hook (Bash Commands)
Validate bash commands for security:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash /path/to/validate-bash.sh"
}
]
}
]
}
}
Example validate-bash.sh:
#!/bin/bash
COMMAND="$1"
# Block dangerous commands
if echo "$COMMAND" | grep -qE "rm -rf /|dd if="; then
echo '{"decision": "block", "reason": "Dangerous command detected"}'
exit 2
fi
echo '{"decision": "approve"}'
exit 0
Pattern 5: Session Setup Hook
Initialize environment on session start:
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "bash /path/to/setup-session.sh"
}
]
}
]
}
}
Example setup-session.sh:
#!/bin/bash
# Load environment, start services, etc.
export PROJECT_ROOT=$(pwd)
echo "Session initialized for project: $PROJECT_ROOT"
exit 0
Creating Hooks
Step 1: Identify the Need
Ask the user:
- What event should trigger the hook?
- What validation or action is needed?
- Should it block, warn, or just log?
- What tools or operations need monitoring?
Step 2: Choose Event and Matcher
- PreToolUse: Validate before execution
- PostToolUse: Process after execution
- UserPromptSubmit: Analyze prompts
- SessionStart: Initialize environment
- Stop: Cleanup or summary
Step 3: Design the Hook Logic
- Write bash script for the hook
- Define input parameters
- Plan return JSON structure
- Handle error cases
- Test security
Step 4: Create hooks.json
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "bash /path/to/script.sh"
}
]
}
]
}
}