From plugin-master
Guides development of event-driven hooks for Claude Code plugins using prompt-based and command-based configurations in hooks.json for events like PreToolUse, PostToolUse, Stop, and SessionStart to validate tools and automate workflows.
How this skill is triggered — by the user, by Claude, or both
Slash command
/plugin-master:hook-developmentThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Hooks are event-driven automation that execute in response to Claude Code events. Use hooks to validate operations, enforce policies, load context, and integrate external tools.
Hooks are event-driven automation that execute in response to Claude Code events. Use hooks to validate operations, enforce policies, load context, and integrate external tools.
Two hook types:
| Event | When | Common Use |
|---|---|---|
| PreToolUse | Before tool executes | Validate, approve/deny, modify input |
| PostToolUse | After tool completes | Test, lint, log, provide feedback |
| Stop | Main agent stopping | Verify task completeness |
| SubagentStop | Subagent stopping | Validate subagent work |
| UserPromptSubmit | User sends prompt | Add context, validate, preprocess |
| SessionStart | Session begins | Load context, set environment |
| SessionEnd | Session ends | Cleanup, logging |
| PreCompact | Before context compaction | Preserve critical information |
| Notification | Notification shown | Custom alert reactions |
hooks/hooks.json)Uses wrapper format with hooks field:
{
"description": "What these hooks do (optional)",
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 10
}
]
}
]
}
}
.claude/settings.json)Direct format, no wrapper:
{
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "script.sh" }]
}
]
}
Critical difference: Plugin hooks.json wraps events inside {"hooks": {...}}. Settings format puts events at top level.
Use LLM reasoning for context-aware decisions:
{
"type": "prompt",
"prompt": "Evaluate if this tool use is appropriate. Check for: system paths, credentials, path traversal. Return 'approve' or 'deny'.",
"timeout": 30
}
Supported events: PreToolUse, PostToolUse, Stop, SubagentStop, UserPromptSubmit
Benefits: Context-aware, flexible, better edge case handling, easier to maintain.
Execute shell commands for deterministic checks:
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 60
}
Always use ${CLAUDE_PLUGIN_ROOT} for portable paths.
| Code | Meaning |
|---|---|
| 0 | Success (stdout shown in transcript) |
| 2 | Blocking error (stderr fed back to Claude) |
| Other | Non-blocking error |
Control which tools trigger hooks:
"matcher": "Write" // Exact match
"matcher": "Write|Edit|Bash" // Multiple tools
"matcher": "mcp__.*__delete.*" // Regex (all MCP delete tools)
"matcher": "*" // All tools (use sparingly)
Matchers are case-sensitive.
{
"session_id": "abc123",
"transcript_path": "/path/to/transcript.txt",
"cwd": "/current/working/dir",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": { "file_path": "/path/to/file" }
}
Event-specific fields: tool_name, tool_input, tool_result, user_prompt, reason
Access in prompts: $TOOL_INPUT, $TOOL_RESULT, $USER_PROMPT
Standard (all hooks):
{
"continue": true,
"suppressOutput": false,
"systemMessage": "Message for Claude"
}
PreToolUse decisions:
{
"hookSpecificOutput": {
"permissionDecision": "allow|deny|ask",
"updatedInput": { "field": "modified_value" }
}
}
Stop/SubagentStop decisions:
{
"decision": "approve|block",
"reason": "Explanation"
}
| Variable | Available | Purpose |
|---|---|---|
$CLAUDE_PLUGIN_ROOT | All hooks | Plugin directory (portable paths) |
$CLAUDE_PROJECT_DIR | All hooks | Project root path |
$CLAUDE_ENV_FILE | SessionStart only | Persist env vars for session |
SessionStart can persist variables:
echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"
{
"PreToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "prompt",
"prompt": "Check if this file write is safe. Deny writes to: .env, credentials, system paths, or files with path traversal (..). Return 'approve' or 'deny' with reason."
}]
}]
}
{
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "npm test -- --bail",
"timeout": 60
}]
}]
}
{
"Stop": [{
"matcher": "*",
"hooks": [{
"type": "prompt",
"prompt": "Verify: tests run, build succeeded, all questions answered. Return 'approve' to stop or 'block' with reason to continue."
}]
}]
}
{
"SessionStart": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh",
"timeout": 10
}]
}]
}
#!/bin/bash
set -euo pipefail
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
# Always validate inputs
if [[ ! "$file_path" =~ ^[a-zA-Z0-9_./-]+$ ]]; then
echo '{"decision": "deny", "reason": "Invalid path"}' >&2
exit 2
fi
# Block path traversal
if [[ "$file_path" == *".."* ]]; then
echo '{"decision": "deny", "reason": "Path traversal detected"}' >&2
exit 2
fi
# Block sensitive files
if [[ "$file_path" == *".env"* ]]; then
echo '{"decision": "deny", "reason": "Sensitive file"}' >&2
exit 2
fi
# Always quote variables
echo "$file_path"
Hooks load at session start. Changes to hook configuration require restarting Claude Code.
hooks/hooks.json won't affect the current sessionTo test changes: Exit Claude Code, restart with claude or claude --debug.
# Enable debug mode to see hook execution
claude --debug
# Test hook scripts directly
echo '{"tool_name": "Write", "tool_input": {"file_path": "/test"}}' | \
bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh
# Validate hook JSON output
output=$(./hook-script.sh < test-input.json)
echo "$output" | jq .
# View loaded hooks in session
# Use /hooks command
${CLAUDE_PLUGIN_ROOT} (no hardcoded paths)set -euo pipefail)* unless necessary)claude --debugnpx claudepluginhub josiahsiegel/claude-plugin-marketplace --plugin plugin-masterGuides creation of Claude Code plugin hooks with prompt-based and bash command types for PreToolUse, PostToolUse, Stop, and other events. Covers plugin hooks.json and settings.json formats.
Guides creation of Claude Code plugin hooks for event-driven automation, including prompt-based and command hooks to validate tool use, enforce policies, and integrate external tools.
Guides creating, configuring, and debugging Claude Code hooks for event-driven automation, command validation, workflow customization, and notifications on events like PreToolUse and UserPromptSubmit.