This skill should be used when creating, modifying, or debugging Claude Code hooks. Triggers on phrases like "create a hook", "add a hook", "make a PreToolUse hook", "automate before tool runs", "block tool execution", "validate tool input", "log tool output", "hook for Write/Edit/Bash", "PostToolUse handler", "session start hook", or when working with files in .claude/hooks/ directory.
/plugin marketplace add cmtkdot/xanbzs-toolkit/plugin install cmtkdot-xanbzs-toolkit@cmtkdot/xanbzs-toolkitThis skill inherits all available tools. When active, it can use any tool Claude has access to.
reference/event-payloads.mdreference/hook-types.mdreference/hooks-config.mdreference/matcher-types.mdreference/response-patterns.mdreference/tdd-testing-workflow.mdreference/templates/output-to-user/README.mdreference/templates/output-to-user/compact-info.shreference/templates/output-to-user/dashboard-rich.shreference/templates/output-to-user/error-warning.shreference/templates/output-to-user/minimal-status.shreference/templates/output-to-user/progress-steps.shreference/templates/output-to-user/style-flags.shreference/templates/output-to-user/table-data.shreference/templates/verbose-comparison.shreference/test-template.sh| Resource | Path |
|---|---|
| Shared Library | .claude/hooks/_shared/hook-lib.sh |
| Config | .claude/hooks/hooks-config.json |
| Settings | .claude/settings.json |
Reference Files: Refer to and read fully with progressive disclosure
Skip this step if the optimal path is clear from the user's request. Otherwise, ask clarifying questions:
| Category | Question | Why Ask |
|---|---|---|
| Event Type | "Should this BLOCK the action or just OBSERVE/LOG it?" | PreToolUse blocks, PostToolUse observes |
| Event Type | "Should this happen BEFORE or AFTER the tool runs?" | Determines PreToolUse vs PostToolUse |
| Matcher | "Should this apply to ALL tools or specific ones?" | Prevents performance issues from broad matchers |
| Response | "If validation fails, should Claude RETRY with guidance or ABANDON the action?" | Exit 2 (retry) vs deny (abandon) |
| Response | "Should Claude see the result (costs tokens) or just the user?" | additionalContext vs systemMessage |
| Output | "Verbose logs, minimal notifications, or silent execution?" | Determines logging strategy |
| Modification | "Should this MODIFY the input or BLOCK the action?" | updatedInput vs permissionDecision |
| Performance | "Is this critical path (<50ms) or can it be slower?" | Determines timeout and optimization needs |
| Failure | "If this hook fails, should the action be BLOCKED or ALLOWED?" | Fail-secure vs fail-safe |
Tip: Prioritize questions that impact architecture (event type, blocking behavior) over cosmetic ones (output verbosity).
1. Choose Hook Type:
| Type | Use When | Timeout |
|---|---|---|
command (default) | Block, format, log, validate | seconds (default 60) |
prompt | Intelligent completion check | seconds (default 30) |
⚠️
prompttype only works forStopandSubagentStopevents
2. Determine Event Type (Keywords in user request): IMPORTANT: First read event-payloads.md & matcher-types.md (PreToolUse/PostToolUse/PermissionRequest only) to understand the event types and matcher patterns
mcp-cli tools | mcp-cli grep "keyword" | mcp-cli info server/tool| Keywords | Event Type | Can Block | Can Modify |
|---|---|---|---|
| "before", "block", "prevent", "validate" | PreToolUse | ✓ exit 2 | ✓ updatedInput |
| "after", "format", "log", "analyze result" | PostToolUse | - | ✓ additionalContext |
| "tool failed", "handle error" | PostToolUseFailure | - | ✓ feedback |
| "permission", "allow", "deny dialog" | PermissionRequest | ✓ decision | ✓ updatedInput |
| "prompt", "enhance prompt", "add context" | UserPromptSubmit | ✓ exit 2 | ✓ additionalContext |
| "startup", "initialize", "env vars" | SessionStart | - | ✓ stdout→Claude |
| "cleanup", "commit", "session end" | SessionEnd | - | - |
| "notification", "alert" | Notification | - | - |
| "stop", "check completion" | Stop | ✓ exit 2 | - |
| "subagent start", "task start" | SubagentStart | - | ✓ stdout→subagent |
| "subagent stop", "task complete" | SubagentStop | ✓ exit 2 | - |
| "compact", "before compaction" | PreCompact | ✓ exit 2 | ✓ stdout→instructions |
| Tool Type | Pattern Example |
|---|---|
| Single tool | "Bash", "Edit", "Write" |
| Multiple tools | "Edit|Write" |
| MCP tools | "mcp__supabase__.*" |
| All tools | "*" |
3. Define Response Strategy:
| Goal | Method |
|---|---|
| Block tool (retry with guidance) | exit 2 + stderr message |
| Block tool (permanently) | JSON permissionDecision: "deny" |
| Allow and modify input | JSON permissionDecision: "allow" + updatedInput |
| Add context to Claude | JSON additionalContext in hookSpecificOutput |
| Silent pass-through | exit 0 with no output |
4. Determine User Output Strategy:
5. DOs and DON'Ts:
| DO | Why | Implementation |
|---|---|---|
| Read stdin once & cache | Stream consumed on read | input=$(cat) |
| Use Exit 0 for success | Only Exit 0 parses JSON | exit 0 |
| Use Exit 2 for blocking | Blocks tool, stderr → Claude | echo "Reason" >&2; exit 2 |
| Quote shell variables | Prevent injection | "$var" |
Use jq for JSON | Safe parsing | jq -r '.field' |
| Validate JSON output | Invalid JSON fails silently | echo "$json" | jq empty |
Check stop_hook_active | Prevent infinite loops | Stop hooks only |
| Use absolute paths | Relative paths fail | $CLAUDE_PROJECT_DIR |
| Keep PreToolUse fast | Delays execution | Target <100ms |
| DON'T | Why | Fix |
|---|---|---|
| Output JSON on Exit 2 | Stdout ignored on error | Use stderr text only |
| Use "deny" for retry | Abandons tool completely | Use Exit 2 instead |
| Assume fields exist | Payloads vary | Check with jq -e |
| Use deprecated fields | decision/reason gone | Use permissionDecision |
| Parse JSON with grep | Fragile/Unsafe | Always use jq |
| Hardcode paths | Breaks envs | Use env vars |
| Trust user input | Security risk | Validate everything |
| Mix Exit codes & JSON | Exit 2 overrides JSON | Pick one strategy |
| DO | Why |
|---|---|
Source hook-lib.sh | Shared library access |
Use is_feature_enabled | Config toggle support |
Use return_* functions | Standardized output |
Use json_get wrapper | Safe jq abstraction |
Log with log_* | Standardized logging |
6. Verify & Ask (Gap Analysis): If details are ambiguous, ask the user (prioritize questions that impact architecture):
| Ambiguity Category | Key Question | Why Ask |
|---|---|---|
| Event Type | "Should this BLOCK the action (PreToolUse) or just OBSERVE/LOG it (PostToolUse)?" | Critical for security/validation vs logging |
| Matcher Scope | "Should this apply to ALL tools, or only specific ones (e.g., just 'Write' or 'Bash')?" | Prevents performance issues from broad matchers |
| Response Strategy | "If validation fails, should Claude be blocked (Exit 2) or told to retry (JSON deny)?" | Determines exit code vs JSON strategy |
| Output Verbosity | "Do you want verbose logs, minimal notifications, or silent execution?" | Impacts user experience/noise |
| Performance | "Is this a critical path operation (must be <50ms) or background task?" | Determines timeout and async needs |
Show: hook purpose, event type, target tools, expected behavior.
Do NOT read reference docs yet - wait for RED phase to reveal what you actually need.
Goal: See what Claude does WITHOUT the hook. Document exact failures.
Now read the docs - targeted by what you observed in RED:
.claude/hooks/utils/{eventType}/{feature-name}.sh:#!/usr/bin/env bash
# Hook: {feature-name} | Event: {EventType} | Matcher: {pattern}
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "${SCRIPT_DIR}/../../_shared/hook-lib.sh"
[[ "$(is_feature_enabled "{eventType}.{featureName}")" == "true" ]] || return_silent
payload="$(cat)"
tool_name="$(json_get "$payload" ".tool_name")"
# --- HOOK LOGIC ---
return_silent
Copy reference/test-template.sh and customize.
bash .claude/hooks/utils/{eventType}/{feature-name}.test.sh
bash .claude/hooks/tests/ensure-implementation.sh .claude/hooks/utils/{eventType}/{feature-name}.sh
{
"hooks": {
"{EventType}": [{
"matcher": "Write|Edit",
"hooks": [{"type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/utils/{eventType}/{feature-name}.sh", "timeout": 5}]
}]
}
}
bash .claude/hooks/tests/run-tests.sh --allPLAN (Steps 1-3):
TDD TEST (Steps 4-6):
FINALIZE (Steps 7-9):
run-tests.sh --all passesThis skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.