hooks-builder
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHooks Builder
钩子构建器
A comprehensive guide for creating Claude Code hooks — event-driven automation that monitors and controls Claude's actions.
为Claude Code创建钩子的综合指南——用于监控和控制Claude操作的事件驱动型自动化工具。
Quick Reference
快速参考
The 10 Hook Events
10种钩子事件
| Event | When It Fires | Can Block? | Supports Matchers? |
|---|---|---|---|
| PreToolUse | Before tool executes | YES | YES (tool names) |
| PermissionRequest | Permission dialog shown | YES | YES (tool names) |
| PostToolUse | After tool succeeds | No | YES (tool names) |
| Notification | Claude sends notification | No | YES |
| UserPromptSubmit | User submits prompt | YES | No |
| Stop | Claude finishes responding | Can force continue | No |
| SubagentStop | Subagent finishes | Can force continue | No |
| PreCompact | Before context compaction | No | YES (manual/auto) |
| SessionStart | Session begins | No | YES (startup/resume/clear/compact) |
| SessionEnd | Session ends | No | No |
| 事件 | 触发时机 | 是否可阻止? | 是否支持匹配器? |
|---|---|---|---|
| PreToolUse | 工具执行前 | 是 | 是(工具名称) |
| PermissionRequest | 显示权限对话框时 | 是 | 是(工具名称) |
| PostToolUse | 工具执行成功后 | 否 | 是(工具名称) |
| Notification | Claude发送通知时 | 否 | 是 |
| UserPromptSubmit | 用户提交提示词时 | 是 | 否 |
| Stop | Claude完成响应时 | 可强制继续 | 否 |
| SubagentStop | 子代理完成时 | 可强制继续 | 否 |
| PreCompact | 上下文压缩前 | 否 | 是(手动/自动) |
| SessionStart | 会话开始时 | 否 | 是(启动/恢复/清除/压缩) |
| SessionEnd | 会话结束时 | 否 | 否 |
Exit Code Semantics
退出代码语义
| Exit Code | Meaning | Effect |
|---|---|---|
| 0 | Success | stdout parsed as JSON for control |
| 2 | Blocking error | VETO — stderr shown to Claude |
| Other | Non-blocking error | stderr logged in debug mode |
| 退出代码 | 含义 | 效果 |
|---|---|---|
| 0 | 成功 | stdout解析为JSON用于控制 |
| 2 | 阻塞错误 | 否决 —— stderr会显示给Claude |
| 其他 | 非阻塞错误 | stderr在调试模式下记录 |
Configuration Locations
配置文件位置
~/.claude/settings.json → Personal hooks (all projects)
.claude/settings.json → Project hooks (team, committed)
.claude/settings.local.json → Local overrides (not committed)~/.claude/settings.json → Personal hooks (all projects)
.claude/settings.json → Project hooks (team, committed)
.claude/settings.local.json → Local overrides (not committed)Essential Environment Variables
关键环境变量
| Variable | Description |
|---|---|
| Project root directory |
| Remote/local indicator |
| Environment persistence path (SessionStart) |
| Plugin directory (plugin hooks) |
| 变量 | 描述 |
|---|---|
| 项目根目录 |
| 远程/本地指示器 |
| 环境持久化路径(SessionStart) |
| 插件目录(插件钩子) |
Key Commands
核心命令
bash
/hooks # View active hooks
claude --debug # Enable debug logging
chmod +x script.sh # Make script executablebash
/hooks # View active hooks
claude --debug # Enable debug logging
chmod +x script.sh # Make script executable6-Phase Workflow
六阶段工作流
Phase 1: Requirements Gathering
阶段1:需求收集
Use AskUserQuestion to clarify:
-
What event should trigger this hook?
- Tool execution (Pre/Post/Permission) → PreToolUse, PostToolUse, PermissionRequest
- User input → UserPromptSubmit
- Response completion → Stop, SubagentStop
- Session lifecycle → SessionStart, SessionEnd
- Context management → PreCompact
- Notifications → Notification
-
What should happen when triggered?
- Observe only (logging, metrics)
- Block/allow based on conditions
- Modify inputs before execution
- Add context to prompts
- Force continuation
-
Should it block, modify, or just observe?
- Observer: PostToolUse, Notification, SessionEnd (can't block)
- Gatekeeper: PreToolUse, PermissionRequest, UserPromptSubmit (can block)
- Transformer: PreToolUse with updatedInput (can modify)
- Controller: Stop, SubagentStop (can force continue)
-
What are the security implications?
- Will it handle untrusted input?
- Could it expose sensitive data?
- Does it need to access external systems?
使用AskUserQuestion来明确以下内容:
-
钩子应该由什么事件触发?
- 工具执行(预处理/后处理/权限)→ PreToolUse、PostToolUse、PermissionRequest
- 用户输入 → UserPromptSubmit
- 响应完成 → Stop、SubagentStop
- 会话生命周期 → SessionStart、SessionEnd
- 上下文管理 → PreCompact
- 通知 → Notification
-
触发后应该执行什么操作?
- 仅观察(日志记录、指标统计)
- 根据条件阻止/允许
- 执行前修改输入
- 为提示词添加上下文
- 强制继续执行
-
它应该是阻塞、修改还是仅观察?
- 观察者:PostToolUse、Notification、SessionEnd(不可阻止)
- 守门人:PreToolUse、PermissionRequest、UserPromptSubmit(可阻止)
- 转换器:带updatedInput的PreToolUse(可修改)
- 控制器:Stop、SubagentStop(可强制继续)
-
有哪些安全影响?
- 是否会处理不可信输入?
- 是否可能泄露敏感数据?
- 是否需要访问外部系统?
Phase 2: Event Selection
阶段2:事件选择
Match event to use case:
| Use Case | Best Event |
|---|---|
| Block dangerous operations | PreToolUse |
| Auto-format code after writes | PostToolUse |
| Validate user prompts | UserPromptSubmit |
| Setup environment | SessionStart |
| Ensure task completion | Stop |
| Log all tool usage | PostToolUse with |
| Protect sensitive files | PreToolUse for Write/Edit |
| Add project context | UserPromptSubmit |
Determine if matchers are needed:
- Specific tools? → Use matcher:
"Write|Edit" - All tools? → Use or omit matcher
"*" - MCP tools? → Use pattern
mcp__server__tool - Bash commands? → Use pattern
Bash(git:*)
根据用例匹配事件:
| 用例 | 最佳事件 |
|---|---|
| 阻止危险操作 | PreToolUse |
| 写入后自动格式化代码 | PostToolUse |
| 验证用户提示词 | UserPromptSubmit |
| 环境设置 | SessionStart |
| 确保任务完成 | Stop |
| 记录所有工具使用情况 | 带 |
| 保护敏感文件 | 针对Write/Edit的PreToolUse |
| 添加项目上下文 | UserPromptSubmit |
确定是否需要匹配器:
- 特定工具? → 使用匹配器:
"Write|Edit" - 所有工具? → 使用或省略匹配器
"*" - MCP工具? → 使用模式
mcp__server__tool - Bash命令? → 使用模式
Bash(git:*)
Phase 3: Matcher Design
阶段3:匹配器设计
Matcher Pattern Syntax:
json
// Exact match (case-sensitive!)
"matcher": "Write"
// OR pattern
"matcher": "Write|Edit"
// Prefix match
"matcher": "Notebook.*"
// Contains match
"matcher": ".*Read.*"
// All tools
"matcher": "*"
// MCP tools
"matcher": "mcp__memory__.*"
// Bash sub-patterns
"matcher": "Bash(git:*)"Common Matcher Patterns:
| Pattern | Matches |
|---|---|
| Only Write tool |
| Write OR Edit |
| All Bash commands |
| Only git commands |
| Only npm commands |
| All MCP tools |
| Everything |
匹配器模式语法:
json
// Exact match (case-sensitive!)
"matcher": "Write"
// OR pattern
"matcher": "Write|Edit"
// Prefix match
"matcher": "Notebook.*"
// Contains match
"matcher": ".*Read.*"
// All tools
"matcher": "*"
// MCP tools
"matcher": "mcp__memory__.*"
// Bash sub-patterns
"matcher": "Bash(git:*)"常见匹配器模式:
| 模式 | 匹配对象 |
|---|---|
| 仅Write工具 |
| Write或Edit |
| 所有Bash命令 |
| 仅git命令 |
| 仅npm命令 |
| 所有MCP工具 |
| 所有对象 |
Phase 4: Implementation
阶段4:实现
Choose implementation approach:
-
Inline command (simple, no external file):json
{ "type": "command", "command": "echo \"$(date) | $tool_name\" >> ~/.claude/audit.log" } -
External script (complex logic, reusable):json
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validate.sh" } -
Prompt-based (LLM evaluation, intelligent decisions):json
{ "type": "prompt", "prompt": "Analyze if all tasks are complete: $ARGUMENTS", "timeout": 30 }
Script Template (Bash):
bash
#!/bin/bash
set -euo pipefail选择实现方式:
-
内联命令(简单,无需外部文件):json
{ "type": "command", "command": "echo \"$(date) | $tool_name\" >> ~/.claude/audit.log" } -
外部脚本(复杂逻辑,可复用):json
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/validate.sh" } -
基于提示词(大语言模型评估,智能决策):json
{ "type": "prompt", "prompt": "Analyze if all tasks are complete: $ARGUMENTS", "timeout": 30 }
脚本模板(Bash):
bash
#!/bin/bash
set -euo pipefailRead JSON input from stdin
Read JSON input from stdin
input=$(cat)
input=$(cat)
Parse fields with jq
Parse fields with jq
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
tool_name=$(echo "$input" | jq -r '.tool_name // empty')
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')
Your logic here
Your logic here
if [[ "$file_path" == ".env" ]]; then
echo "BLOCKED: Cannot modify .env files" >&2
exit 2
fi
if [[ "$file_path" == ".env" ]]; then
echo "BLOCKED: Cannot modify .env files" >&2
exit 2
fi
Success - output decision
Success - output decision
echo '{"decision": "approve"}'
exit 0
**Script Template (Python):**
```python
#!/usr/bin/env python3
import sys
import jsonecho '{"decision": "approve"}'
exit 0
**脚本模板(Python):**
```python
#!/usr/bin/env python3
import sys
import jsonRead JSON input from stdin
Read JSON input from stdin
data = json.load(sys.stdin)
data = json.load(sys.stdin)
Extract fields
Extract fields
tool_name = data.get('tool_name', '')
tool_input = data.get('tool_input', {})
file_path = tool_input.get('file_path', '')
tool_name = data.get('tool_name', '')
tool_input = data.get('tool_input', {})
file_path = tool_input.get('file_path', '')
Your logic here
Your logic here
if '.env' in file_path:
print("BLOCKED: Cannot modify .env files", file=sys.stderr)
sys.exit(2)
if '.env' in file_path:
print("BLOCKED: Cannot modify .env files", file=sys.stderr)
sys.exit(2)
Success - output decision
Success - output decision
output = {"decision": "approve"}
print(json.dumps(output))
sys.exit(0)
undefinedoutput = {"decision": "approve"}
print(json.dumps(output))
sys.exit(0)
undefinedPhase 5: Security Hardening
阶段5:安全加固
CRITICAL: Hooks execute shell commands with YOUR permissions.
Security Checklist:
- All variables quoted: not
"$VAR"$VAR - JSON parsed with jq or json.load (not grep/sed)
- Paths validated (no , normalized)
.. - No sensitive data in logs/output
- No sudo or privilege escalation
- Script tested manually first
- Project hooks audited before running
- Timeout set appropriately
- Error handling for all failure modes
Secure Patterns:
bash
undefined重要提示:钩子会以你的权限执行Shell命令。
安全检查清单:
- 所有变量都加引号:而非
"$VAR"$VAR - 使用jq或json.load解析JSON(而非grep/sed)
- 路径验证(无,已规范化)
.. - 日志/输出中无敏感数据
- 无sudo或权限提升操作
- 脚本先手动测试
- 项目钩子运行前先审计
- 设置适当的超时时间
- 所有失败模式都有错误处理
安全模式示例:
bash
undefinedUNSAFE - injection risk
UNSAFE - injection risk
rm $file_path
rm $file_path
SAFE - quoted, prevents flag injection
SAFE - quoted, prevents flag injection
rm -- "$file_path"
rm -- "$file_path"
UNSAFE - parsing risk
UNSAFE - parsing risk
cat "$input" | grep "field"
cat "$input" | grep "field"
SAFE - proper JSON parsing
SAFE - proper JSON parsing
echo "$input" | jq -r '.field'
**Defense in Depth:**
1. Input validation (parse JSON properly)
2. Path sanitization (normalize, check boundaries)
3. Output sanitization (no sensitive data)
4. Fail-safe defaults (block on error, not allow)
5. Timeout protection (prevent infinite loops)echo "$input" | jq -r '.field'
**深度防御措施:**
1. 输入验证(正确解析JSON)
2. 路径清理(规范化、检查边界)
3. 输出清理(无敏感数据)
4. 故障安全默认值(错误时阻塞而非允许)
5. 超时保护(防止无限循环)Phase 6: Testing
阶段6:测试
Step 1: Manual Script Testing
bash
undefined步骤1:手动脚本测试
bash
undefinedCreate mock input
Create mock input
cat > /tmp/mock-input.json << 'EOF'
{
"session_id": "test-123",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "test content"
}
}
EOF
cat > /tmp/mock-input.json << 'EOF'
{
"session_id": "test-123",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "test content"
}
}
EOF
Test script
Test script
cat /tmp/mock-input.json | ./my-hook.sh
echo "Exit code: $?"
**Step 2: Edge Case Testing**
- Empty inputs: `{}`
- Missing fields: `{"tool_name": "Write"}`
- Malicious inputs: `{"tool_input": {"file_path": "; rm -rf /"}}`
- Large inputs: 10KB+ content
- Unicode: paths with special characters
**Step 3: Integration Testing**
```bashcat /tmp/mock-input.json | ./my-hook.sh
echo "Exit code: $?"
**步骤2:边缘情况测试**
- 空输入:`{}`
- 缺失字段:`{"tool_name": "Write"}`
- 恶意输入:`{"tool_input": {"file_path": "; rm -rf /"}}`
- 大输入:10KB+内容
- Unicode:含特殊字符的路径
**步骤3:集成测试**
```bashStart Claude with debug mode
Start Claude with debug mode
claude --debug
claude --debug
Trigger the tool your hook targets
Trigger the tool your hook targets
Watch debug output for hook execution
Watch debug output for hook execution
**Step 4: Verification**
```bash
**步骤4:验证**
```bashCheck hooks are registered
Check hooks are registered
/hooks
/hooks
Watch hook execution
Watch hook execution
claude --debug 2>&1 | grep -i hook
---claude --debug 2>&1 | grep -i hook
---Hook Patterns
钩子模式
Observer Pattern
观察者模式
Log without blocking — use PostToolUse or Notification.
json
{
"hooks": {
"PostToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "echo \"$(date) | $tool_name\" >> ~/.claude/audit.log"
}]
}]
}
}仅记录不阻塞 —— 使用PostToolUse或Notification。
json
{
"hooks": {
"PostToolUse": [{
"matcher": "*",
"hooks": [{
"type": "command",
"command": "echo \"$(date) | $tool_name\" >> ~/.claude/audit.log"
}]
}]
}
}Gatekeeper Pattern
守门人模式
Block dangerous actions — use PreToolUse or PermissionRequest.
json
{
"hooks": {
"PreToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "python3 ~/.claude/hooks/file-protector.py"
}]
}]
}
}阻止危险操作 —— 使用PreToolUse或PermissionRequest。
json
{
"hooks": {
"PreToolUse": [{
"matcher": "Write|Edit",
"hooks": [{
"type": "command",
"command": "python3 ~/.claude/hooks/file-protector.py"
}]
}]
}
}Transformer Pattern
转换器模式
Modify inputs before execution — use PreToolUse with updatedInput.
python
undefined执行前修改输入 —— 使用带updatedInput的PreToolUse。
python
undefinedIn script, output:
In script, output:
output = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"content": add_license_header(original_content)
}
}
}
print(json.dumps(output))
undefinedoutput = {
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"content": add_license_header(original_content)
}
}
}
print(json.dumps(output))
undefinedOrchestrator Pattern
编排器模式
Coordinate multiple events — combine SessionStart + PreToolUse + PostToolUse.
json
{
"hooks": {
"SessionStart": [{
"matcher": "startup",
"hooks": [{"type": "command", "command": "~/.claude/hooks/setup-env.sh"}]
}],
"PreToolUse": [{
"matcher": "Write|Edit",
"hooks": [{"type": "command", "command": "~/.claude/hooks/validate.sh"}]
}],
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{"type": "command", "command": "~/.claude/hooks/format.sh"}]
}]
}
}协调多个事件 —— 组合SessionStart + PreToolUse + PostToolUse。
json
{
"hooks": {
"SessionStart": [{
"matcher": "startup",
"hooks": [{"type": "command", "command": "~/.claude/hooks/setup-env.sh"}]
}],
"PreToolUse": [{
"matcher": "Write|Edit",
"hooks": [{"type": "command", "command": "~/.claude/hooks/validate.sh"}]
}],
"PostToolUse": [{
"matcher": "Write|Edit",
"hooks": [{"type": "command", "command": "~/.claude/hooks/format.sh"}]
}]
}
}Common Pitfalls
常见陷阱
1. Forgetting Exit Code 2 for Blocking
1. 忘记用退出代码2来阻塞
bash
undefinedbash
undefinedWRONG - exit 1 doesn't block
WRONG - exit 1 doesn't block
echo "Error" >&2
exit 1
echo "Error" >&2
exit 1
RIGHT - exit 2 blocks Claude
RIGHT - exit 2 blocks Claude
echo "BLOCKED: reason" >&2
exit 2
undefinedecho "BLOCKED: reason" >&2
exit 2
undefined2. Case Sensitivity in Matchers
2. 匹配器的大小写敏感性
json
// WRONG - won't match "Write" tool
"matcher": "write"
// RIGHT - case-sensitive match
"matcher": "Write"json
// WRONG - won't match "Write" tool
"matcher": "write"
// RIGHT - case-sensitive match
"matcher": "Write"3. Unquoted Variables (Injection Risk)
3. 未加引号的变量(注入风险)
bash
undefinedbash
undefinedWRONG - command injection vulnerability
WRONG - command injection vulnerability
rm $file_path
rm $file_path
RIGHT - properly quoted
RIGHT - properly quoted
rm -- "$file_path"
undefinedrm -- "$file_path"
undefined4. Missing Shebang in Scripts
4. 脚本中缺少Shebang
bash
undefinedbash
undefinedWRONG - no shebang, may fail
WRONG - no shebang, may fail
set -euo pipefail
set -euo pipefail
RIGHT - explicit interpreter
RIGHT - explicit interpreter
#!/bin/bash
set -euo pipefail
undefined#!/bin/bash
set -euo pipefail
undefined5. Not Making Scripts Executable
5. 未设置脚本可执行权限
bash
undefinedbash
undefinedDon't forget!
Don't forget!
chmod +x ~/.claude/hooks/my-hook.sh
undefinedchmod +x ~/.claude/hooks/my-hook.sh
undefined6. Forgetting to Quote Paths in JSON
6. JSON中路径未加引号
json
// WRONG - spaces in path will break
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/script.sh"
// RIGHT - quoted path
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/script.sh"json
// WRONG - spaces in path will break
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/script.sh"
// RIGHT - quoted path
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/script.sh"7. No Error Handling
7. 缺少错误处理
bash
undefinedbash
undefinedWRONG - silent failures
WRONG - silent failures
input=$(cat)
tool=$(echo "$input" | jq -r '.tool_name')
input=$(cat)
tool=$(echo "$input" | jq -r '.tool_name')
RIGHT - handle errors
RIGHT - handle errors
input=$(cat) || { echo "Failed to read input" >&2; exit 1; }
tool=$(echo "$input" | jq -r '.tool_name') || { echo "Failed to parse JSON" >&2; exit 1; }
undefinedinput=$(cat) || { echo "Failed to read input" >&2; exit 1; }
tool=$(echo "$input" | jq -r '.tool_name') || { echo "Failed to parse JSON" >&2; exit 1; }
undefined8. Logging Sensitive Data
8. 记录敏感数据
bash
undefinedbash
undefinedWRONG - may log secrets
WRONG - may log secrets
echo "Processing: $input" >> /tmp/debug.log
echo "Processing: $input" >> /tmp/debug.log
RIGHT - sanitize before logging
RIGHT - sanitize before logging
echo "Processing tool: $tool_name" >> /tmp/debug.log
---echo "Processing tool: $tool_name" >> /tmp/debug.log
---When to Use Hooks
钩子的适用场景
USE hooks for:
- Security enforcement (block dangerous operations)
- Code quality automation (format, lint on save)
- Compliance and auditing (log all actions)
- Environment setup (consistent configuration)
- Workflow automation (notifications, integrations)
- Input validation (prompt checking)
- Task completion verification
DON'T use hooks for:
- Adding new capabilities (use Skills)
- Delegating complex work (use Agents)
- User-invoked prompts (use Commands)
- Simple one-off tasks (just ask Claude)
适合使用钩子的场景:
- 安全实施(阻止危险操作)
- 代码质量自动化(保存时格式化、 lint检查)
- 合规与审计(记录所有操作)
- 环境设置(一致的配置)
- 工作流自动化(通知、集成)
- 输入验证(提示词检查)
- 任务完成验证
不适合使用钩子的场景:
- 添加新功能(使用Skills)
- 委托复杂工作(使用Agents)
- 用户调用的提示词(使用Commands)
- 简单一次性任务(直接询问Claude即可)
Files in This Skill
本Skill包含的文件
Templates (Progressive Complexity)
模板(复杂度递进)
- — Single event, inline command
templates/basic-hook.md - — External shell scripts
templates/with-scripts.md - — Permission control, input modification
templates/with-decisions.md - — LLM-based evaluation
templates/with-prompts.md - — Complete multi-event system
templates/production-hooks.md
- —— 单事件、内联命令
templates/basic-hook.md - —— 外部Shell脚本
templates/with-scripts.md - —— 权限控制、输入修改
templates/with-decisions.md - —— 基于大语言模型的评估
templates/with-prompts.md - —— 完整的多事件系统
templates/production-hooks.md
Examples (18 Complete Hooks)
示例(18个完整钩子)
- — Protection, validation, auditing
examples/security-hooks.md - — Formatting, linting, testing
examples/quality-hooks.md - — Setup, context, notifications
examples/workflow-hooks.md
- —— 保护、验证、审计
examples/security-hooks.md - —— 格式化、Lint检查、测试
examples/quality-hooks.md - —— 设置、上下文、通知
examples/workflow-hooks.md
Reference
参考文档
- — Complete JSON schemas, all events
reference/syntax-guide.md - — Security, design, team deployment
reference/best-practices.md - — 10 common issues, testing methodology
reference/troubleshooting.md
- —— 完整JSON schema、所有事件
reference/syntax-guide.md - —— 安全、设计、团队部署
reference/best-practices.md - —— 10个常见问题、测试方法
reference/troubleshooting.md