claude-code:hook
npx machina-cli add skill bendrucker/claude/hook --openclawClaude Code Hooks
Reference for creating and configuring Claude Code hooks. When uncertain about syntax or features, use the Task tool with subagent_type='claude-code-guide' to consult official documentation.
Hook Types
| Type | Trigger | Use Cases |
|---|---|---|
| PreToolUse | Before tool execution | Validate inputs, block operations, modify parameters |
| PostToolUse | After tool completes | Check results, run linters, provide feedback |
| UserPromptSubmit | When user sends message | Pre-process input, add context |
| Stop | Session ends | Cleanup, save state |
| SubagentStop | Subagent completes | Process results |
| PreCompact | Before context compaction | Save important state |
| Notification | System notification | Log events |
Configuration Files
~/.claude/settings.json- User-level (global).claude/settings.json- Project-level.claude/settings.local.json- Local (not committed)- Plugin hooks:
plugins/<name>/hooks/hooks.json
Hook Structure
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bun ./hooks/biome"
}
]
}
]
}
}
Matcher Patterns:
- Simple:
"Write","Edit" - Multiple:
"Edit|Write|MultiEdit" - With args:
"Bash(npm:*)","Bash(osascript:*)|Bash(open:*)" - MCP tools:
"mcp__linear__create_issue" - Plugin MCP tools:
"mcp__plugin_<plugin>_<namespace>__<tool>" - Both patterns:
"mcp__linear__create_issue|mcp__plugin_linear_linear__create_issue"
Hook Input
Commands receive JSON on stdin:
{
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.ts",
"content": "..."
},
"cwd": "/project/root",
"session_id": "...",
"transcript_path": "..."
}
Parse in shell:
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path')
Or use @constellos/claude-code-kit/runners:
import { readStdinJson, writeStdoutJson } from "@constellos/claude-code-kit/runners";
const input = await readStdinJson<PreToolUseHookInput>();
Hook Output
PreToolUse - Control execution:
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Use gh cli instead"}}
{"hookSpecificOutput": {"hookEventName": "PreToolUse", "updatedInput": {"state": "Todo"}}}
PostToolUse - Provide feedback:
{"hookSpecificOutput": {"hookEventName": "PostToolUse", "additionalContext": "Lint errors found..."}}
Exit with no output to allow without modification.
Script Storage
Store complex hooks in .claude/hooks/ or project hooks/ directory:
.claude/
├── settings.json
└── hooks/
└── my-hook.ts
Reference with:
"command": "bun $CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.ts"
Examples
See these repositories for hook implementations:
- Input modification: plugins/linear/hooks/
- Permission decisions: plugins/github/scripts/
- PostToolUse feedback: .claude/hooks/biome/
Debugging
For troubleshooting hook failures, see debugging.
Source
git clone https://github.com/bendrucker/claude/blob/main/plugins/claude-code/skills/hook/SKILL.mdView on GitHub Overview
This skill covers configuring, creating, and debugging Claude Code hooks across PreToolUse, PostToolUse, and UserPromptSubmit. It helps automate workflows within Claude Code and resolve issues like hooks not firing or partial execution.
How This Skill Works
Hooks are configured in JSON files stored under ~/.claude/settings.json, .claude/settings.json, or plugin-specific hooks.json. Supported hook types include PreToolUse, PostToolUse, UserPromptSubmit, Stop, SubagentStop, PreCompact, and Notification, each with matcher patterns and a command to run. Hook inputs and outputs are exchanged via JSON on stdin/stdout, enabling precise control over tool execution and feedback.
When to Use It
- You want to run validations before a tool executes (PreToolUse).
- You need to run post-execution checks or linting (PostToolUse).
- You're debugging why a hook isn't firing or only some hooks ran.
- You want to format or transform JSON output with a hook (e.g., using jq).
- You want to automate workflows by storing complex hooks in .claude/hooks or a project hooks directory.
Quick Start
- Step 1: Create or edit a hook JSON in the appropriate directory (e.g., plugins/<name>/hooks/hooks.json) to register PreToolUse or PostToolUse and specify the command to run.
- Step 2: Write the hook script under .claude/hooks/ (for example .claude/hooks/my-hook.ts) and reference it in the hook JSON with a command like "bun $CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.ts".
- Step 3: Trigger tool actions to test, then inspect the hookOutput for fields like hookEventName, permissionDecision, or updatedInput to verify behavior.
Best Practices
- Define clear matcher patterns (e.g., Edit|Write) to target specific tools.
- Test hooks in a controlled environment before enabling them in production.
- Prefer non-destructive PreToolUse hooks and use updatedInput to pass changes downstream.
- Store large or complex hooks in .claude/hooks/ or project hooks/ and reference them via a bun command.
- Document hook behavior with explicit permissionDecision and reason for easy auditing.
Example Use Cases
- Input modification: see plugins/linear/hooks/ for adjusting user input before processing.
- Permission decisions: plugins/github/scripts/ define rules to allow or block actions.
- PostToolUse feedback: .claude/hooks/biome/ provide lint results or summaries after tool runs.
- Hook structure: a PostToolUse entry with a matcher and a bun command like bun ./hooks/biome.
- Hook storage and reference: store complex hooks in .claude/hooks/ or project hooks/ and reference with a bun path.