hook-development
Canonical guide to authoring Claude Code hooks, both prompt-based and command-based. PROACTIVELY activate for: (1) creating a hook, (2) adding PreToolUse or PostToolUse hooks, (3) validating tool use, (4) implementing prompt-based hooks, (5) blocking dangerous commands, (6) event-driven automation, (7) configuring hooks.json, (8) using ${CLAUDE_PLUGIN_ROOT}, (9) hook matchers and filtering, (10) debugging hooks that do not fire. Provides: hooks.json schema, event reference, security guidance, and examples.
What this skill does
# Hook Development for Claude Code Plugins
## Overview
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:**
- **Prompt-based** (recommended): LLM-driven, context-aware decisions
- **Command-based**: Shell commands for fast, deterministic checks
## Hook Events Reference
| 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 |
## Configuration Formats
### Plugin hooks.json (in `hooks/hooks.json`)
**Uses wrapper format with `hooks` field:**
```json
{
"description": "What these hooks do (optional)",
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 10
}
]
}
]
}
}
```
### User settings format (in `.claude/settings.json`)
**Direct format, no wrapper:**
```json
{
"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.
## Prompt-Based Hooks (Recommended)
Use LLM reasoning for context-aware decisions:
```json
{
"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.
## Command Hooks
Execute shell commands for deterministic checks:
```json
{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/validate.sh",
"timeout": 60
}
```
**Always use `${CLAUDE_PLUGIN_ROOT}` for portable paths.**
### Exit Codes
| Code | Meaning |
|------|---------|
| 0 | Success (stdout shown in transcript) |
| 2 | Blocking error (stderr fed back to Claude) |
| Other | Non-blocking error |
## Matchers
Control which tools trigger hooks:
```json
"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.
## Hook Input/Output
### Input (all hooks receive via stdin)
```json
{
"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`
### Output
**Standard (all hooks):**
```json
{
"continue": true,
"suppressOutput": false,
"systemMessage": "Message for Claude"
}
```
**PreToolUse decisions:**
```json
{
"hookSpecificOutput": {
"permissionDecision": "allow|deny|ask",
"updatedInput": { "field": "modified_value" }
}
}
```
**Stop/SubagentStop decisions:**
```json
{
"decision": "approve|block",
"reason": "Explanation"
}
```
## Environment Variables
| 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:**
```bash
echo "export PROJECT_TYPE=nodejs" >> "$CLAUDE_ENV_FILE"
```
## Common Patterns
### Validate file writes (PreToolUse)
```json
{
"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."
}]
}]
}
```
### Auto-test after changes (PostToolUse)
```json
{
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "npm test -- --bail",
"timeout": 60
}]
}]
}
```
### Verify task completion (Stop)
```json
{
"Stop": [{
"matcher": "*",
"hooks": [{
"type": "prompt",
"prompt": "Verify: tests run, build succeeded, all questions answered. Return 'approve' to stop or 'block' with reason to continue."
}]
}]
}
```
### Load project context (SessionStart)
```json
{
"SessionStart": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "bash ${CLAUDE_PLUGIN_ROOT}/scripts/load-context.sh",
"timeout": 10
}]
}]
}
```
## Security Best Practices
### In command hook scripts:
```bash
#!/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"
```
## Lifecycle and Limitations
**Hooks load at session start.** Changes to hook configuration require restarting Claude Code.
- Editing `hooks/hooks.json` won't affect the current session
- Adding new hook scripts won't be recognized until restart
- All matching hooks run **in parallel** (not sequentially)
- Hooks don't see each other's output - design for independence
**To test changes:** Exit Claude Code, restart with `claude` or `claude --debug`.
## Debugging
```bash
# 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
```
## Validation Checklist
- [ ] hooks.json uses correct format (plugin wrapper or settings direct)
- [ ] All script paths use `${CLAUDE_PLUGIN_ROOT}` (no hardcoded paths)
- [ ] Scripts are executable and handle errors (`set -euo pipefail`)
- [ ] Scripts validate all inputs and quote all variables
- [ ] Matchers are specific (avoid `*` unless necessary)
- [ ] Timeouts are set appropriately (default: command 60s, prompt 30s)
- [ ] Hook output is valid JSON
- [ ] Tested with `claude --debug`
Related in AI Agents
skill-development
IncludedComprehensive meta-skill for creating, managing, validating, auditing, and distributing Claude Code skills and slash commands (unified in v2.1.3+). Provides skill templates, creation workflows, validation patterns, audit checklists, naming conventions, YAML frontmatter guidance, progressive disclosure examples, and best practices lookup. Use when creating new skills, validating existing skills, auditing skill quality, understanding skill architecture, needing skill templates, learning about YAML frontmatter requirements, progressive disclosure patterns, tool restrictions (allowed-tools), skill composition, skill naming conventions, troubleshooting skill activation issues, creating custom slash commands, configuring command frontmatter, using command arguments ($ARGUMENTS, $1, $2), bash execution in commands, file references in commands, command namespacing, plugin commands, MCP slash commands, Skill tool configuration, or deciding between skills vs slash commands. Delegates to docs-management skill for official documentation.
reprompter
IncludedTransform messy prompts into well-structured, effective prompts — single or multi-agent. Use when: "reprompt", "reprompt this", "clean up this prompt", "structure my prompt", rough text needing XML tags and best practices, "reprompter teams", "repromptception", "run with quality", "smart run", "smart agents", multi-agent tasks, audits, parallel work, anything going to agent teams. Don't use when: simple Q&A, pure chat, immediate execution-only tasks. See "Don't Use When" section for details. Outputs: Structured XML/Markdown prompt, quality score (before/after), optional team brief + per-agent sub-prompts, agent team output files. Success criteria: Single mode quality score ≥ 7/10; Repromptception per-agent prompt quality score 8+/10; all required sections present, actionable and specific.
adaptive-compaction
IncludedAdaptive add-on policy and recovery layer that decides WHEN to compact, prune, snapshot, or fork -- replacing fixed-percent auto-compaction across Claude Code, Codex, and MCP-capable hosts. Trigger on auto-compact timing or damage: "when should I compact", "is it safe to compact now or start a fresh session", "auto-compact fires too early/mid-task", "switching to an unrelated task but the window still has space", "context rot", "answers get worse the longer the session runs", "the agent forgot the plan or my decisions after it summarized", "add a layer on top that manages context without changing the agent", raising autoCompactWindow to give the policy room, or installing/tuning a cross-tool compaction policy or PreCompact hook -- even when "compaction" is never said but the problem is context-window pressure or post-summarization memory loss. Do NOT use to summarize a conversation, build RAG, write a summarization prompt (decides WHEN not HOW), or answer max-context-length trivia.
agent-skill-creator
IncludedCreate cross-platform agent skills from workflow descriptions. Activates when users ask to create an agent, automate a repetitive workflow, create a custom skill, or need advanced agent creation. Triggers on phrases like create agent for, automate workflow, create skill for, every day I have to, daily I need to, turn process into agent, need to automate, create a cross-platform skill, validate this skill, export this skill, migrate this skill. Supports single skills, multi-agent suites, transcript processing, template-based creation, interactive configuration, cross-platform export, and spec validation.
llm-wiki
IncludedUse when building or maintaining a persistent personal knowledge base (second brain) in Obsidian where an LLM incrementally ingests sources, updates entity/concept pages, maintains cross-references, and keeps a synthesis current. Triggers include "second brain", "Obsidian wiki", "personal knowledge management", "ingest this paper/article/book", "build a research wiki", "compound knowledge", "Memex", or whenever the user wants knowledge to accumulate across sessions instead of being re-derived by RAG on every query.
skill-master
IncludedAgent Skills authoring, evaluation, and optimization. Create, edit, validate, benchmark, and improve skills following the agentskills.io specification. Use when designing SKILL.md files, structuring skill folders (references, scripts, assets), ingesting external documentation into skills, running trigger evals, benchmarking skill quality, optimizing descriptions, or performing blind A/B comparisons. Keywords: agentskills.io, SKILL.md, skill authoring, eval, benchmark, trigger optimization.