Hook Development for Claude Code Plugins
Hook Types
Prompt-Based Hooks (LLM-Driven)
{
"type": "prompt",
"prompt": "Evaluate if this tool use is appropriate: $TOOL_INPUT",
"timeout": 30
}
See: references/prompt-hooks.md
Command Hooks (Bash-Driven)
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 60
}
See: references/command-hooks.md
Hook Configuration
Plugin hooks use hooks/hooks.json format:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "prompt",
"prompt": "Validate file write safety"
}
]
}
],
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "prompt",
"prompt": "Verify task completion"
}
]
}
]
}
}
See: references/configuration.md
Hook Events
| Event | When | Purpose | Decision | |-------|------|---------|----------| | PreToolUse | Before tool | Validation/modification | allow/deny/ask | | PostToolUse | After tool | Feedback/logging | None | | Stop | Agent stops | Completion check | approve/block | | SessionStart | Session begins | Setup/context | None | | SessionEnd | Session ends | Cleanup | None | | UserPromptSubmit | User input | Validation | None | | SubagentStop | Subagent done | Task validation | approve/block |
See: references/events.md
Input/Output Formats
All hooks receive JSON via stdin:
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/current/working/directory",
"permission_mode": "ask|allow|bypass",
"hook_event_name": "PreToolUse"
}
See: references/io-formats.md
Output Format
{
"continue": true,
"suppressOutput": false,
"systemMessage": "Message for Claude"
}
Exit Codes
- 0 - Success, continue, show stdout
- 1 - Non-blocking error, continue, log stderr
- 2 - Blocking error, halt operation
- 124 - Timeout
See: references/exit-codes.md
Environment Variables
Available in command hooks:
$CLAUDE_PROJECT_DIR- Project root directory$CLAUDE_PLUGIN_ROOT- Plugin directory (use for portability)$CLAUDE_ENV_FILE- SessionStart only$CLAUDE_CODE_REMOTE- Set if running remotely
Always use ${CLAUDE_PLUGIN_ROOT} for portability.
Matchers
Exact: "Write"
Multiple: "Write|Edit|Read"
Wildcard: "*"
Regex: "mcp__.*__delete.*" (all MCP delete tools)
Security Best Practices
DO
- Use prompt hooks for complex validation
- Use
${CLAUDE_PLUGIN_ROOT}for all paths - Validate all inputs in command hooks
- Quote all bash variables
- Set appropriate timeouts (5-10s quick, 30s standard, 60s complex)
DON'T
- Hardcode absolute paths
- Trust user input without validation
- Create long-running hooks (>60s)
- Rely on hook execution order (hooks run in parallel)
- Log sensitive information
Reference Materials
Core Documentation:
references/prompt-hooks.md- Prompt-based implementationreferences/command-hooks.md- Command-based implementationreferences/configuration.md- Configuration guidereferences/events.md- All events with examplesreferences/io-formats.md- Input/output formatsreferences/security.md- Security practicesreferences/performance.md- Performance optimizationreferences/troubleshooting.md- Common issues
Implementation Workflow:
- Identify requirements (what events? what validation?)
- Choose hook types (prompt for complex, command for fast)
- Write hook scripts (use set -euo pipefail)
- Configure hooks (edit hooks/hooks.json)
- Validate configuration
- Test hooks with sample data
- Document hooks
Focus: Security (prompt hooks), Performance (command hooks), Usability, Maintainability
Conclusion
Use prompt-based hooks for complex, context-aware validation and command hooks for fast, deterministic checks. Always validate inputs, use proper timeouts, and test thoroughly.
Next Steps:
- Review
references/for detailed documentation - Examine
examples/for working implementations - Test with minimal configuration first