hook-developer
Complete Claude Code hooks reference - input/output schemas, registration, testing patterns
By parcadei · 499 installs
npx skills add parcadei/continuous-claude-v3 --skill hook-developer
Source repository · Upstream listing
Hook Developer
Complete reference for developing Claude Code hooks. Use this to write hooks with correct input/output schemas.
When to Use
Creating a new hook
Debugging hook input/output format
Understanding what fields are available
Setting up hook registration in settings.json
Learning what hooks can block vs inject context
Quick Reference
Hook Fires When Can Block? Primary Use
PreToolUse Before tool executes YES Block/modify tool calls
PostToolUse After tool completes Partial React to tool results
UserPromptSubmit User sends prompt YES Validate/inject context
PermissionRequest Permission dialog shows YES Auto approve/deny
SessionStart Session begins NO Load context, set env vars
SessionEnd Session ends NO Cleanup/save state
Stop Agent finishes YES Force continuation
SubagentStart Subagent spawns NO Pattern coordination
SubagentStop Subagent finishes YES Force continuation
PreCompact Before compaction NO Save state
Notification Notification sent NO Custom alerts
Hook type options: type: "command" (bash) or type: "prompt" (LLM evaluation)
Hook Input/Output Schemas
PreToolUse
Purpose: Block or modify tool execution before it happens.
Input:
Output (JSON):
Exit code 2: Blocks tool, stderr shown to Claude.
Common matchers: Bash , Edit Write , Read , Task , mcp .
PostToolUse
Purpose: React to tool execution results, provide feedback to Claude.
Input:
CRITICAL: The response field is tool response , NOT tool result .
Output (JSON):
Blocking: "decision": "block" with "reason" prompts Claude to address the issue.
Common matchers: Edit Write , Bash
UserPromptSubmit
Purpose: Validate user prompts, inject context before Claude processes.
Input:
Output (Plain text):
Output (JSON):
Blocking: "decision": "block" erases prompt, shows "reason" to user only (not Claude).
Exit code 2: Blocks prompt, shows stderr to user only.
PermissionRequest
Purpose: Automate permission dialog decisions.
Input:
Output:
SessionStart
Purpose: Initialize session, load context, set environment variables.
Input:
Environment variable: CLAUDE ENV FILE write export VAR=value to persist env vars.
Output (Plain text or JSON):
Plain text stdout is added as context.
SessionEnd
Purpose: Cleanup, save state, log session.
Input:
Output: Cannot affect session (already ending). Use for cleanup only.
Stop
Purpose: Control when Claude stops, force continuation.
Input:
CRITICAL: Check stop hook active: true to prevent infinite loops!
Output:
Blocking: "decision": "block" forces Claude to continue with "reason" as prompt.
SubagentStart
Purpose: Run when a subagent (Task tool) is spawned.
Input:
Output: Context injection only (cannot block).
SubagentStop
Purpose: Control when subagents (Task tool) stop.
Input:
Output: Same as Stop.
PreCompact
Purpose: Save state before context compaction.
Input:
Matchers: manual , auto
Output:
Notification
Purpose: Custom notification handling.
Input:
Matchers: permission prompt , idle prompt , auth success , elicitation dialog ,
Output:
Registration in settings.json
Standard Structure
Matcher Patterns
Pattern Matches
Bash Exactly Bash tool
Edit\ Write Edit OR Write
Read. Regex: Read
mcp . write. MCP write tools
All tools
Case sensitive: Bash ≠ bash
Events Requiring Matchers
PreToolUse YES (required)
PostToolUse YES (required)
PermissionRequest YES (required)
Notification YES (optional)
SessionStart YES ( startup resume clear compact )
PreCompact YES ( manual auto )
Events Without Matchers
Hook Types
Command Hooks (type: "command")
Default type. Executes bash commands or scripts.
Prompt Based Hooks (type: "prompt")
Uses LLM (Haiku) for context aware decisions. Best for Stop/SubagentStop.
Response schema:
MCP Tool Naming
MCP tools use pattern mcp <server <tool :
Pattern Matches
mcp memory . All memory server tools
mcp . write. All MCP write tools
mcp github . All GitHub tools
Environment Variables
Available to All Hooks
Variable Description
CLAUDE PROJECT DIR Absolute path to project root
CLAUDE CODE REMOTE "true" if remote/web, empty if local CLI
SessionStart Only
Variable Description
CLAUDE ENV FILE Path to write export VAR=value lines
Plugin Hooks Only
Variable Description
CLAUDE PLUGIN ROOT Absolute path to plugin directory
Exit Codes
Exit Code Behavior stdout stderr
0 Success JSON processed Ignored
2 Blocking error IGNORED Error message
Other Non blocking error Ignored Verbose mode
Exit Code 2 by Hook
Hook Effect
PreToolUse Blocks tool, stderr to Claude
PostToolUse stderr to Claude (tool already ran)
UserPromptSubmit Blocks prompt, stderr to user only
Stop Blocks stop, stderr to Claude
Shell Wrapper Pattern
Or for bundled:
TypeScript Handler Pattern
Testing Hooks
Manual Test Commands
Rebuild After TypeScript Edits
Common Patterns
Block Dangerous Files (PreToolUse)
Auto Format Files (PostToolUse)
Inject Git Context (UserPromptSubmit)
Force Test Verification (Stop)
Debugging Checklist
[ ] Hook registered in settings.json?
[ ] Shell script has +x permission?
[ ] Bundle rebuilt after TS changes?
[ ] Using tool response not tool result ?
[ ] Output is valid JSON (or plain text)?
[ ] Checking stop hook active in Stop hooks?
[ ] Using $CLAUDE PROJECT DIR for paths?
Key Learnings from Past Sessions
1. Field names matter tool response not tool result
2. Output format decision: "block" + reason for blocking
3. Exit code 2 stderr goes to Claude/user, stdout IGNORED
4. Rebuild bundles TypeScript source edits don't auto apply
5. Test manually echo '{}' ./hook.sh before relying on it
6. Check outputs first ls .claude/cache/ before editing code
7. Detached spawn hides errors add logging to debug
See Also
/debug hooks Systematic debugging workflow
.claude/rules/hooks.md Hook development rules