agents-hooks
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseClaude Code Hooks — Meta Reference
Claude Code 钩子 — 元参考文档
This skill provides the definitive reference for creating Claude Code hooks. Use this when building automation that triggers on Claude Code events.
本技能提供了创建Claude Code钩子的权威参考。在构建基于Claude Code事件触发的自动化时可使用本文档。
When to Use This Skill
何时使用本技能
- Building event-driven automation for Claude Code
- Creating PreToolUse guards to block dangerous commands
- Implementing PostToolUse formatters, linters, or auditors
- Adding Stop hooks for testing or notifications
- Setting up SessionStart/SessionEnd for environment management
- Integrating Claude Code with CI/CD pipelines (headless mode)
- 为Claude Code构建事件驱动型自动化
- 创建PreToolUse防护机制以阻止危险命令
- 实现PostToolUse格式化工具、代码检查器或审计器
- 添加用于测试或通知的Stop钩子
- 设置SessionStart/SessionEnd以进行环境管理
- 将Claude Code与CI/CD流水线集成(无头模式)
Quick Reference
快速参考
| Event | Trigger | Use Case |
|---|---|---|
| Session begins/resumes | Initialize environment |
| User submits prompt | Preprocess/validate input |
| Before tool execution | Validate, block dangerous commands |
| Permission dialog shown | Auto-allow/deny permissions |
| After tool succeeds | Format, audit, notify |
| After tool fails | Capture failures, add guidance |
| Subagent spawns | Inspect subagent metadata |
| When Claude finishes | Run tests, summarize |
| Subagent finishes | Verify subagent completion |
| On notifications | Alert integrations |
| Before context compaction | Preserve critical context |
| | Initialize repo/env |
| Session ends | Cleanup, save state |
| 事件 | 触发时机 | 适用场景 |
|---|---|---|
| 会话开始/恢复 | 初始化环境 |
| 用户提交提示词 | 预处理/验证输入 |
| 工具执行前 | 验证、阻止危险命令 |
| 显示权限对话框时 | 自动允许/拒绝权限 |
| 工具执行成功后 | 格式化、审计、通知 |
| 工具执行失败后 | 捕获失败、添加指导 |
| 子Agent生成时 | 检查子Agent元数据 |
| Claude完成任务时 | 运行测试、生成总结 |
| 子Agent完成任务时 | 验证子Agent执行结果 |
| 收到通知时 | 触发集成告警 |
| 上下文压缩前 | 保留关键上下文 |
| 执行 | 初始化仓库/环境 |
| 会话结束时 | 清理、保存状态 |
Hook Structure
钩子结构
text
.claude/hooks/
├── pre-tool-validate.sh
├── post-tool-format.sh
├── post-tool-audit.sh
├── stop-run-tests.sh
└── session-start-init.shtext
.claude/hooks/
├── pre-tool-validate.sh
├── post-tool-format.sh
├── post-tool-audit.sh
├── stop-run-tests.sh
└── session-start-init.shConfiguration
配置
settings.json
settings.json
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-format.sh"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-validate.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-run-tests.sh"
}
]
}
]
}
}json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/post-tool-format.sh"
}
]
}
],
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-tool-validate.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/stop-run-tests.sh"
}
]
}
]
}
}Execution Model (Jan 2026)
执行模型(2026年1月)
- Hooks receive a JSON payload via stdin (treat it as untrusted input) and run with your user permissions (outside the Bash tool sandbox).
- Default timeout is 60s per hook command; all matching hooks run in parallel; identical commands are deduplicated.
- 钩子通过stdin接收JSON负载(需将其视为不可信输入),并以你的用户权限运行(在Bash工具沙箱之外)。
- 默认超时时间为每个钩子命令60秒;所有匹配的钩子并行运行;重复命令会被自动去重。
Hook Input (stdin)
钩子输入(stdin)
json
{
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "ls -la"
}
}json
{
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "ls -la"
}
}Environment Variables (shell)
环境变量(Shell)
| Variable | Description |
|---|---|
| Absolute project root where Claude Code started |
| Plugin root (plugin hooks only) |
| |
| File path to persist |
| 变量 | 描述 |
|---|---|
| Claude Code启动时的项目根目录绝对路径 |
| 插件根目录(仅适用于插件钩子) |
| 在远程/网页环境中为 |
| 用于持久化 |
Exit Codes
退出码
| Code | Meaning | Notes |
|---|---|---|
| Success | JSON written to stdout is parsed for structured control |
| Blocking error | |
| Other | Non-blocking error | Execution continues; |
Stdout injection note: for , , and , non-JSON stdout (exit 0) is injected into Claude’s context; most other events show stdout only in verbose mode.
UserPromptSubmitSessionStartSetup| 代码 | 含义 | 说明 |
|---|---|---|
| 成功 | 标准输出中的JSON会被解析以用于结构化控制 |
| 阻塞性错误 | 标准错误输出会作为提示信息;标准输出中的JSON会被忽略 |
| 其他 | 非阻塞性错误 | 执行会继续;标准错误输出仅在 verbose 模式下可见 |
标准输出注入说明:对于、和事件,非JSON格式的标准输出(退出码0)会被注入到Claude的上下文中;大多数其他事件仅在verbose模式下显示标准输出。
UserPromptSubmitSessionStartSetupDecision Control + Input Modification (v2.0.10+)
决策控制 + 输入修改(v2.0.10+)
PreToolUse hooks can allow/deny/ask and optionally modify the tool input via .
updatedInputPreToolUse钩子可通过字段允许/拒绝/询问权限,还可选择性修改工具输入。
updatedInputHook Output Schema
钩子输出 Schema
json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Reason shown to user (and to Claude on deny)",
"updatedInput": { "command": "echo 'modified'" },
"additionalContext": "Extra context added before tool runs"
}
}Note: older / fields are deprecated; prefer the fields.
decisionreasonhookSpecificOutput.*json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "显示给用户的理由(拒绝时也会显示给Claude)",
"updatedInput": { "command": "echo 'modified'" },
"additionalContext": "工具运行前添加的额外上下文"
}
}注意:旧版的/字段已被弃用;建议使用字段。
decisionreasonhookSpecificOutput.*Example: Redirect Sensitive File Edits
示例:重定向敏感文件编辑
bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"Redirect package-lock.json edits to /dev/null
Redirect package-lock.json edits to /dev/null
if [[ "$FILE_PATH" == *"package-lock.json" ]]; then
UPDATED_INPUT="$(echo "$INPUT" | jq -c '.tool_input | .file_path = "/dev/null"')"
jq -cn --argjson updatedInput "$UPDATED_INPUT" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "allow",
permissionDecisionReason: "Redirected write to /dev/null",
updatedInput: $updatedInput
}
}'
exit 0
fi
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
undefinedif [[ "$FILE_PATH" == *"package-lock.json" ]]; then
UPDATED_INPUT="$(echo "$INPUT" | jq -c '.tool_input | .file_path = "/dev/null"')"
jq -cn --argjson updatedInput "$UPDATED_INPUT" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "allow",
permissionDecisionReason: "Redirected write to /dev/null",
updatedInput: $updatedInput
}
}'
exit 0
fi
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'
undefinedExample: Strip Sensitive Files from Git Add
示例:从Git Add中移除敏感文件
bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"
if [[ "$TOOL_NAME" == "Bash" && "$CMD" =~ ^git[[:space:]]+add ]]; then
# Remove .env files from staging
SAFE_CMD="$(echo "$CMD" | sed 's/\.env[^ ]*//g')"
if [[ "$SAFE_CMD" != "$CMD" ]]; then
echo '{}' | jq -cn --arg cmd "$SAFE_CMD" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "allow",
permissionDecisionReason: "Removed .env from git add",
updatedInput: { command: $cmd }
}
}'
exit 0
fi
fi
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"
if [[ "$TOOL_NAME" == "Bash" && "$CMD" =~ ^git[[:space:]]+add ]]; then
# Remove .env files from staging
SAFE_CMD="$(echo "$CMD" | sed 's/\.env[^ ]*//g')"
if [[ "$SAFE_CMD" != "$CMD" ]]; then
echo '{}' | jq -cn --arg cmd "$SAFE_CMD" '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "allow",
permissionDecisionReason: "Removed .env from git add",
updatedInput: { command: $cmd }
}
}'
exit 0
fi
fi
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow"}}'Prompt-Based Hooks
基于提示词的钩子
For complex decisions, use LLM-evaluated hooks () instead of bash scripts. They are most useful for and decisions.
type: "prompt"StopSubagentStop对于复杂决策,可使用LLM评估的钩子()替代Bash脚本。它们在和决策场景中最为实用。
type: "prompt"StopSubagentStopConfiguration
配置
json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate whether Claude should stop. Context JSON: $ARGUMENTS. Return {\"ok\": true} if all tasks are complete, otherwise {\"ok\": false, \"reason\": \"what remains\"}.",
"timeout": 30
}
]
}
]
}
}json
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate whether Claude should stop. Context JSON: $ARGUMENTS. Return {\"ok\": true} if all tasks are complete, otherwise {\"ok\": false, \"reason\": \"what remains\"}.",
"timeout": 30
}
]
}
]
}
}Response Schema
响应 Schema
- Allow:
{"ok": true} - Block:
{"ok": false, "reason": "Explanation shown to Claude"}
- 允许:
{"ok": true} - 阻止:
{"ok": false, "reason": "显示给Claude的解释"}
Combining Command and Prompt Hooks
组合命令型与提示词型钩子
Use command hooks for fast, deterministic checks. Use prompt hooks for nuanced decisions:
json
{
"Stop": [
{
"hooks": [
{ "type": "command", "command": ".claude/hooks/quick-check.sh" },
{ "type": "prompt", "prompt": "Verify code quality meets standards" }
]
}
]
}使用命令型钩子进行快速、确定性检查。使用提示词型钩子进行精细化决策:
json
{
"Stop": [
{
"hooks": [
{ "type": "command", "command": ".claude/hooks/quick-check.sh" },
{ "type": "prompt", "prompt": "Verify code quality meets standards" }
]
}
]
}Permission Prompt Fatigue Reduction Pattern
权限提示疲劳缓解模式
Use this when frequent approval dialogs slow down repeated safe workflows.
当频繁的审批对话框拖慢重复的安全工作流时,可使用此模式。
Strategy
策略
- Identify repetitive, low-risk command prefixes (for example: test runners, read-only diagnostics).
- Approve narrow prefixes instead of full commands.
- Keep destructive or broad shells (,
rm, generic interpreters with arbitrary input) out of auto-approval rules.git reset --hard - Re-check approved prefixes periodically; remove stale ones.
- 识别重复出现的低风险命令前缀(例如:测试运行器、只读诊断工具)。
- 仅批准范围狭窄的前缀,而非完整命令。
- 禁止将破坏性或宽泛的Shell命令(、
rm、可处理任意输入的通用解释器)加入自动批准规则。git reset --hard - 定期重新检查已批准的前缀;移除不再使用的规则。
Practical Guardrails
实用防护措施
- Allow only task-scoped prefixes (example: ), not unrestricted executors.
npm run test:e2e - Keep separate policy for write-outside-workspace actions.
- Pair allow-rules with deny-rules for dangerous patterns.
- 仅允许任务范围明确的前缀(例如:),而非无限制的执行器。
npm run test:e2e - 为工作区外的写入操作设置单独的策略。
- 将允许规则与针对危险模式的拒绝规则配对使用。
Outcome
效果
This reduces repeated permission interruptions while preserving high-safety boundaries.
此模式可减少重复的权限中断,同时保留高安全边界。
Hook Templates
钩子模板
Pre-Tool Validation
工具执行前验证
bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"
if [[ "$TOOL_NAME" == "Bash" ]]; then
# Block rm -rf /
if echo "$CMD" | grep -qE 'rm\s+-rf\s+/'; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Dangerous rm command detected"
}
}'
exit 0
fi
# Block force push to main
if echo "$CMD" | grep -qE 'git\s+push.*--force.*(main|master)'; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Force push to main/master not allowed"
}
}'
exit 0
fi
# Soft-warning: possible credential exposure
if echo "$CMD" | grep -qE '(password|secret|api_key)\s*='; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: "Possible credential exposure in command",
additionalContext: "Command may include a secret. Confirm intent and avoid committing secrets."
}
}'
exit 0
fi
fi
exit 0bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
CMD="$(echo "$INPUT" | jq -r '.tool_input.command // empty')"
if [[ "$TOOL_NAME" == "Bash" ]]; then
# Block rm -rf /
if echo "$CMD" | grep -qE 'rm\s+-rf\s+/'; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Dangerous rm command detected"
}
}'
exit 0
fi
# Block force push to main
if echo "$CMD" | grep -qE 'git\s+push.*--force.*(main|master)'; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "deny",
permissionDecisionReason: "Force push to main/master not allowed"
}
}'
exit 0
fi
# Soft-warning: possible credential exposure
if echo "$CMD" | grep -qE '(password|secret|api_key)\s*='; then
echo '{}' | jq -cn '{
hookSpecificOutput: {
hookEventName: "PreToolUse",
permissionDecision: "ask",
permissionDecisionReason: "Possible credential exposure in command",
additionalContext: "Command may include a secret. Confirm intent and avoid committing secrets."
}
}'
exit 0
fi
fi
exit 0Post-Tool Formatting
工具执行后格式化
bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
case "$FILE_PATH" in
*.js|*.ts|*.jsx|*.tsx|*.json|*.md)
npx prettier --write "$FILE_PATH" 2>/dev/null || true
;;
*.py)
ruff format "$FILE_PATH" 2>/dev/null || true
;;
*.go)
gofmt -w "$FILE_PATH" 2>/dev/null || true
;;
*.rs)
rustfmt "$FILE_PATH" 2>/dev/null || true
;;
esac
fi
exit 0bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
case "$FILE_PATH" in
*.js|*.ts|*.jsx|*.tsx|*.json|*.md)
npx prettier --write "$FILE_PATH" 2>/dev/null || true
;;
*.py)
ruff format "$FILE_PATH" 2>/dev/null || true
;;
*.go)
gofmt -w "$FILE_PATH" 2>/dev/null || true
;;
*.rs)
rustfmt "$FILE_PATH" 2>/dev/null || true
;;
esac
fi
exit 0Post-Tool Security Audit
工具执行后安全审计
bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
# Check for hardcoded secrets
if grep -qE '(password|secret|api_key|token)\s*[:=]\s*["\x27][^"\x27]+["\x27]' "$FILE_PATH"; then
echo "WARNING: Possible hardcoded secret in $FILE_PATH" >&2
fi
# Check for console.log in production code
if [[ "$FILE_PATH" =~ \.(ts|js|tsx|jsx)$ ]] && grep -q 'console.log' "$FILE_PATH"; then
echo "NOTE: console.log found in $FILE_PATH" >&2
fi
fi
exit 0bash
#!/bin/bash
set -euo pipefail
INPUT="$(cat)"
TOOL_NAME="$(echo "$INPUT" | jq -r '.tool_name')"
FILE_PATH="$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')"
if [[ "$TOOL_NAME" =~ ^(Edit|Write)$ && -n "$FILE_PATH" && -f "$FILE_PATH" ]]; then
# Check for hardcoded secrets
if grep -qE '(password|secret|api_key|token)\s*[:=]\s*["\x27][^"\x27]+["\x27]' "$FILE_PATH"; then
echo "WARNING: Possible hardcoded secret in $FILE_PATH" >&2
fi
# Check for console.log in production code
if [[ "$FILE_PATH" =~ \.(ts|js|tsx|jsx)$ ]] && grep -q 'console.log' "$FILE_PATH"; then
echo "NOTE: console.log found in $FILE_PATH" >&2
fi
fi
exit 0Stop Hook (Run Tests)
Stop钩子(运行测试)
bash
#!/bin/bash
set -euo pipefailbash
#!/bin/bash
set -euo pipefailRun tests after Claude finishes
Run tests after Claude finishes
cd "$CLAUDE_PROJECT_DIR"
cd "$CLAUDE_PROJECT_DIR"
Detect test framework
Detect test framework
if [[ -f "package.json" ]]; then
if grep -q '"vitest"' package.json; then
npm run test 2>&1 | head -50
elif grep -q '"jest"' package.json; then
npm test 2>&1 | head -50
fi
elif [[ -f "pytest.ini" ]] || [[ -f "pyproject.toml" ]]; then
pytest --tb=short 2>&1 | head -50
fi
exit 0
undefinedif [[ -f "package.json" ]]; then
if grep -q '"vitest"' package.json; then
npm run test 2>&1 | head -50
elif grep -q '"jest"' package.json; then
npm test 2>&1 | head -50
fi
elif [[ -f "pytest.ini" ]] || [[ -f "pyproject.toml" ]]; then
pytest --tb=short 2>&1 | head -50
fi
exit 0
undefinedSession Start
会话开始
bash
#!/bin/bash
set -euo pipefail
cd "$CLAUDE_PROJECT_DIR"bash
#!/bin/bash
set -euo pipefail
cd "$CLAUDE_PROJECT_DIR"Check git status
Check git status
echo "=== Git Status ==="
git status --short
echo "=== Git Status ==="
git status --short
Check for uncommitted changes
Check for uncommitted changes
if ! git diff --quiet; then
echo "WARNING: Uncommitted changes detected"
fi
if ! git diff --quiet; then
echo "WARNING: Uncommitted changes detected"
fi
Verify dependencies
Verify dependencies
if [[ -f "package.json" ]]; then
if [[ ! -d "node_modules" ]]; then
echo "NOTE: node_modules missing, run npm install"
fi
fi
exit 0
---if [[ -f "package.json" ]]; then
if [[ ! -d "node_modules" ]]; then
echo "NOTE: node_modules missing, run npm install"
fi
fi
exit 0
---Matchers
匹配器
Matchers filter which tool triggers the hook:
- Exact match: matches only the Write tool
Write - Regex: or
Edit|WriteNotebook.* - Match all: (also works with
*or omitted matcher)""
匹配器用于筛选触发钩子的工具:
- 精确匹配:仅匹配Write工具
Write - 正则匹配:或
Edit|WriteNotebook.* - 匹配所有:(
*或省略matcher字段也可实现)""
Security Best Practices
安全最佳实践
text
HOOK SECURITY CHECKLIST
[ ] Validate all inputs with regex
[ ] Quote all variables: "$VAR" not $VAR
[ ] Use absolute paths
[ ] No eval with untrusted input
[ ] Set -euo pipefail at top
[ ] Keep hooks fast (<1 second)
[ ] Log actions for audit
[ ] Test manually before deployingtext
HOOK SECURITY CHECKLIST
[ ] Validate all inputs with regex
[ ] Quote all variables: "$VAR" not $VAR
[ ] Use absolute paths
[ ] No eval with untrusted input
[ ] Set -euo pipefail at top
[ ] Keep hooks fast (<1 second)
[ ] Log actions for audit
[ ] Test manually before deployingHook Composition
钩子组合
Multiple Hooks on Same Event
同一事件绑定多个钩子
json
{
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": ".claude/hooks/format.sh" },
{ "type": "command", "command": ".claude/hooks/audit.sh" },
{ "type": "command", "command": ".claude/hooks/notify.sh" }
]
}
]
}All matching hooks run in parallel. If you need strict ordering (format → lint → test), make one wrapper script that runs them sequentially.
json
{
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": ".claude/hooks/format.sh" },
{ "type": "command", "command": ".claude/hooks/audit.sh" },
{ "type": "command", "command": ".claude/hooks/notify.sh" }
]
}
]
}所有匹配的钩子会并行运行。如果需要严格的执行顺序(格式化 → 代码检查 → 测试),可编写一个包装脚本按顺序执行它们。
Debugging Hooks
调试钩子
bash
undefinedbash
undefinedTest a PostToolUse hook manually (stdin JSON)
Test a PostToolUse hook manually (stdin JSON)
export CLAUDE_PROJECT_DIR="$(pwd)"
echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"'"$(pwd)"'/src/app.ts"}}'
| bash .claude/hooks/post-tool-format.sh
| bash .claude/hooks/post-tool-format.sh
export CLAUDE_PROJECT_DIR="$(pwd)"
echo '{"hook_event_name":"PostToolUse","tool_name":"Edit","tool_input":{"file_path":"'"$(pwd)"'/src/app.ts"}}'
| bash .claude/hooks/post-tool-format.sh
| bash .claude/hooks/post-tool-format.sh
Check exit code
Check exit code
echo $?
---echo $?
---Navigation
导航
Resources
资源
- references/hook-patterns.md — Common patterns
- references/hook-security.md — Security guide
- data/sources.json — Documentation links
- references/hook-patterns.md — 常见模式
- references/hook-security.md — 安全指南
- data/sources.json — 文档链接
Related Skills
相关技能
- ../agents-subagents/SKILL.md — Agent creation
- ../agents-skills/SKILL.md — Skill creation
- ../ops-devops-platform/SKILL.md — CI/CD integration
- ../agents-subagents/SKILL.md — Agent创建
- ../agents-skills/SKILL.md — 技能创建
- ../ops-devops-platform/SKILL.md — CI/CD集成