| name | hook-creator |
| description | Create and configure Claude Code hooks for customizing agent behavior. Use when the user wants to (1) create a new hook, (2) configure automatic formatting, logging, or notifications, (3) add file protection or custom permissions, (4) set up pre/post tool execution actions, (5) configure session initialization or cleanup (SessionStart/SessionEnd), (6) auto- approve or deny permission requests (PermissionRequest), or (7) asks about any of the 10 hook events. |
Hook Creator
Create Claude Code hooks that execute shell commands at specific lifecycle events.
Hook Creation Workflow
- Identify the use case - Determine what the hook should accomplish
- Select the appropriate event - Choose from available hook events
(see references/hook-events.md)
- Design the hook command - Write shell command that processes JSON
input from stdin
- Configure the matcher - Set tool/event filter (use
* for all,
or specific tool names like Bash, Edit|Write)
- Choose storage location - User settings (
~/.claude/settings.json)
or project (.claude/settings.json)
- Test the hook - Verify behavior with a simple test case
Hook Configuration Structure
{
"hooks": {
"<EventName>": [
{
"matcher": "<ToolPattern>",
"hooks": [
{
"type": "command",
"command": "<shell-command>",
"once": true
}
]
}
]
}
}
Hook Types
type: "command" - Execute shell command (default)
type: "prompt" - LLM-based evaluation. Supported for: Stop,
SubagentStop, UserPromptSubmit, PreToolUse, PermissionRequest
Hook Options
once - Run only once per session (optional, default: false)
Common Patterns
Reading Input Data
Hooks receive JSON via stdin. Use jq to extract fields:
jq -r '.tool_input.file_path'
jq -r '.tool_input.description // "No description"'
jq -r 'if .tool_input.file_path
then .tool_input.file_path else empty end'
Exit Codes for PreToolUse
0 - Allow the tool to proceed
2 - Block the tool and provide feedback to Claude
Matcher Patterns
* - Match all tools
Bash - Match only Bash tool
Edit|Write - Match Edit or Write tools
Read - Match Read tool
Quick Examples
Log all bash commands:
jq -r '"\(.tool_input.command)"' >> ~/.claude/bash-log.txt
Auto-format TypeScript after edit:
jq -r '.tool_input.file_path' | {
read f
[[ "$f" == *.ts ]] && npx prettier --write "$f"
}
Block edits to .env files:
python3 -c "
import json, sys
p = json.load(sys.stdin).get('tool_input', {}).get('file_path', '')
sys.exit(2 if '.env' in p else 0)
"
Prompt-Based Hooks
Use type: "prompt" for LLM-based evaluation. The prompt receives
context and returns a decision.
Example - Stop hook with evaluation:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Review if all user requirements are met. Context: {{context}}. Respond with JSON: {\"decision\": \"approve\" or \"block\", \"reason\": \"explanation\"}"
}
]
}
]
}
}
Supported events: Stop, SubagentStop, UserPromptSubmit, PreToolUse,
PermissionRequest
Hooks in Skills/Agents/Commands
Define hooks directly in frontmatter for scoped lifecycle hooks:
---
name: secure-operations
description: Perform operations with security checks
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/security-check.sh"
once: true
---
Supported events in frontmatter: PreToolUse, PostToolUse, Stop
Hooks defined in Skills/Agents/Commands are scoped to that execution context
and automatically cleaned up when finished.
Resources
Triggers
This skill activates when users want to:
- Create a new hook for Claude Code
- Configure automatic formatting, linting, or code quality tools
- Set up logging or notification systems for tool usage
- Add file protection or access control rules
- Configure pre/post tool execution actions
- Set up session initialization or cleanup (SessionStart/SessionEnd)
- Auto-approve or deny permission requests (PermissionRequest)
- Understand hook events, input schemas, or exit codes
Anti-Patterns
Avoid these common mistakes when creating hooks:
- Blocking without feedback: Using exit code 2 in PreToolUse without
providing stdout message leaves Claude confused about why the tool was
blocked
- Ignoring stdin: Not reading JSON from stdin causes hooks to fail
silently or behave unexpectedly
- Heavy synchronous operations: Running slow commands in PreToolUse
blocks the entire workflow; prefer async logging or use PostToolUse
- Hardcoded paths: Using absolute paths like
/home/user/... instead
of $HOME or relative paths breaks portability
- Missing error handling: Not handling
jq parse failures or missing
fields causes cryptic errors
- Over-matching: Using
* matcher when only specific tools need the
hook wastes resources and may cause side effects
Extension Points
- Custom event handlers: Add new hook configurations in settings.json
for any of the 10 hook events
- External integrations: Extend hooks to call external services
(Slack, Discord, webhooks) via curl or custom scripts
- Project-specific overrides: Layer project
.claude/settings.json
hooks over user ~/.claude/settings.json defaults
- Script libraries: Create reusable shell scripts in project that
hooks can call for complex logic
Design Rationale
Why JSON via stdin? Passing structured data through stdin allows hooks
to access any tool input field without parsing command-line arguments.
This is more flexible and handles special characters safely.
Why exit codes for control flow? Exit codes are the Unix-standard way
to signal success/failure. Using 0/2 for PreToolUse allows simple shell
commands to control tool execution without complex IPC.
Why separate PreToolUse and PostToolUse? Pre-hooks can block operations
(validation, access control) while post-hooks handle side effects
(formatting, logging). Mixing these concerns would complicate hook logic.
Why 10 distinct events? Each event represents a unique lifecycle moment
with different input schemas and use cases. Fine-grained events allow
precise hook targeting without over-triggering.