| name | hook-creator |
| description | Create event handler hooks for Claude Code plugins that respond to tool use, sessions, and notifications. Use when creating hooks, event handlers, or automation for plugins. |
Hook Creator
Create event handler hooks for Claude Code plugins that execute automatically at specific points in the workflow.
When to Use This Skill
Use when:
- Creating hooks for plugins
- User requests "create a hook" or "add event handler"
- Automating responses to tool use
- Setting up validation or formatting workflows
What Hooks Are
Hooks are shell commands that execute automatically at various points in Claude Code's lifecycle:
- PreToolUse: Before tool calls (can block them)
- PostToolUse: After tool calls complete
- UserPromptSubmit: When user submits prompt
- Notification: When notifications are sent
- Stop: When Claude finishes responding
- SubagentStop: When subagents complete
- PreCompact: Before compaction operations
- SessionStart: When session starts/resumes
- SessionEnd: When session ends
Hook Configuration Format
Hooks are configured in hooks/hooks.json:
{
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here",
"timeout": 60
}
]
}
]
}
For plugins, hooks can also be defined inline in plugin.json.
Instructions
Step 1: Identify Hook Trigger
Determine which event should trigger the hook:
Tool-related:
- PreToolUse - Before tools execute (can block)
- PostToolUse - After tools complete
Session-related:
- SessionStart - At session beginning
- SessionEnd - At session end
User interaction:
- UserPromptSubmit - When user submits prompt
- Notification - When notifications sent
Workflow:
- Stop - When Claude stops
- SubagentStop - When subagent stops
- PreCompact - Before compaction
Step 2: Define Matcher (For Tool Hooks)
For PreToolUse and PostToolUse:
Specify which tools to match:
- Exact:
"Write"matches only Write tool - Multiple:
"Edit|Write"matches Edit or Write - Pattern:
"Notebook.*"matches NotebookEdit, etc. - All:
"*"or""matches all tools
For other events: Matcher can be omitted or used for event subtypes.
Step 3: Create Hook Command
Write shell command or script to execute:
Simple command:
{
"type": "command",
"command": "echo 'Tool used' >> log.txt"
}
Script execution:
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.py"
}
With timeout:
{
"type": "command",
"command": "python script.py",
"timeout": 30
}
Step 4: Create Hook Configuration
Add to hooks/hooks.json:
{
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
"timeout": 30
}
]
}
]
}
Step 5: Handle Hook Input/Output
Input: Hooks receive JSON via stdin
Exit codes:
- 0: Success (stdout shown to user in transcript mode)
- 2: Blocking error (stderr sent to Claude)
- Other: Non-blocking error (stderr to user)
JSON output: For advanced control
{
"decision": "approve"|"block",
"reason": "Explanation",
"continue": false,
"stopReason": "Message to user"
}
Step 6: Use Environment Variables
Available variables:
${CLAUDE_PLUGIN_ROOT}- Plugin directory path${CLAUDE_PROJECT_DIR}- Project root directory${CLAUDE_ENV_FILE}- (SessionStart only) File for env vars
Example:
{
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/check.py"
}
Hook Event Reference
PreToolUse
When: Before tool parameters are processed
Use for:
- Validate tool inputs
- Block dangerous operations
- Auto-approve safe operations
- Modify tool parameters
Exit code 2 behavior: Blocks tool, shows stderr to Claude
Example:
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python scripts/validate_bash.py"
}
]
}
]
}
PostToolUse
When: After tool completes successfully
Use for:
- Format generated code
- Run linters
- Log operations
- Provide feedback
Exit code 2 behavior: Shows stderr to Claude (tool already ran)
Example:
{
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/format-code.sh"
}
]
}
]
}
SessionStart
When: Session starts or resumes
Use for:
- Load development context
- Install dependencies
- Set environment variables
- Initialize state
Special: Can persist env vars via ${Claude_ENV_FILE}
Example:
{
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/setup.sh"
}
]
}
]
}
Other Events
UserPromptSubmit: Validate/augment user prompts
Notification: Custom notifications
Stop/SubagentStop: Prevent premature stopping
PreCompact: Pre-compaction operations
SessionEnd: Cleanup and logging
Prompt-Based Hooks
For Stop and SubagentStop, hooks can use LLM evaluation:
{
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop: $ARGUMENTS",
"timeout": 30
}
]
}
]
}
LLM responds with decision JSON automatically.
Example Hooks
Auto-Format Code
{
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | { read file; if echo \"$file\" | grep -q '\\.ts$'; then npx prettier --write \"$file\"; fi; }"
}
]
}
]
}
Validate Bash Commands
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate_bash.py",
"timeout": 10
}
]
}
]
}
Load Session Context
{
"SessionStart": [
{
"matcher": "startup",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/load_context.sh"
}
]
}
]
}
Log Commands
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.command' >> ~/.claude/bash-log.txt"
}
]
}
]
}
Plugin Hooks
Plugin hooks in hooks/hooks.json or inline in plugin.json:
File-based:
{
"hooks": "./hooks/hooks.json"
}
Inline:
{
"hooks": {
"PostToolUse": [...]
}
}
With description:
{
"description": "Automatic code formatting",
"hooks": {
"PostToolUse": [...]
}
}
Best Practices
- Use appropriate events: Choose the right trigger point
- Handle errors gracefully: Exit codes and stderr
- Set reasonable timeouts: Default 60s, adjust as needed
- Use environment variables: ${Claude_PLUGIN_ROOT} for portability
- Test thoroughly: Verify hooks work as expected
- Document behavior: Explain what hooks do
- Avoid blocking operations: Keep hooks fast
Security Considerations
IMPORTANT:
- Hooks execute with your credentials
- Malicious hooks can exfiltrate data
- Always review hook code before adding
- Validate inputs in hook scripts
- Use restricted permissions where possible
Troubleshooting
Hook not executing:
- Check event name is correct
- Verify matcher pattern
- Ensure script is executable
- Check command path
- Review timeout setting
Hook blocks incorrectly:
- Check exit code logic
- Verify stderr output
- Test hook script standalone
- Review matcher specificity
Hook too slow:
- Reduce timeout
- Optimize script
- Move to background if possible
- Consider async operations
Reference
For complete hook documentation, see references/hook-format.md.