mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-03-02 15:23:19 +08:00
16 KiB
16 KiB
Claude Code Hooks 设计指南(参考文档)
基于 2026 年最新官方文档整理,涵盖 18 种 Hook 事件、4 种 Handler 类型
目录
- 概念概述
- Hook 事件类型(18 种)
- 配置格式与位置
- 匹配器规则(Matcher)
- Handler 类型(4 种)
- 环境变量
- 输入/输出 JSON 格式
- 响应决策控制
- 实用模式与示例
- 高级特性
- 故障排除
1. 概念概述
Hooks 是用户定义的自动化机制,在 Claude Code 生命周期的特定节点自动执行。类似 Git Hooks,但作用于 AI 辅助开发流程。
核心区别:
| 机制 | 作用 | 执行保证 |
|---|---|---|
| CLAUDE.md | 引导 Claude 的行为偏好 | 建议性,Claude 可能不遵守 |
| Hooks | 强制执行确定性规则 | 确定性,100% 执行 |
| Skills | 可复用的指令集 | 按需触发 |
| Subagents | 复杂委托任务 | 隔离上下文 |
适用场景:文件保护、代码格式化、危险操作拦截、审计日志、桌面通知、上下文注入等。
2. Hook 事件类型
共 18 种事件,按生命周期分组:
会话生命周期
| 事件 | 触发时机 | Matcher 匹配字段 |
|---|---|---|
SessionStart |
会话启动或恢复 | startup / resume / clear / compact |
SessionEnd |
会话终止 | clear / logout / prompt_input_exit / bypass_permissions_disabled |
PreCompact |
上下文压缩前 | manual / auto |
ConfigChange |
配置文件外部修改 | user_settings / project_settings / local_settings / policy_settings / skills |
用户交互
| 事件 | 触发时机 | Matcher 匹配字段 |
|---|---|---|
UserPromptSubmit |
用户提交提示词后(处理前) | 无(始终触发) |
Notification |
Claude 需要用户注意 | permission_prompt / idle_prompt / auth_success / elicitation_dialog |
工具执行
| 事件 | 触发时机 | Matcher 匹配字段 |
|---|---|---|
PreToolUse |
工具执行前 | 工具名:Bash / Edit / Write / Read / mcp__.* |
PostToolUse |
工具执行成功后 | 同上 |
PostToolUseFailure |
工具执行失败后 | 同上 |
PermissionRequest |
权限确认弹窗时 | 同上 |
Agent 与任务
| 事件 | 触发时机 | Matcher 匹配字段 |
|---|---|---|
SubagentStart |
子代理启动 | Agent 类型:Bash / Explore / Plan / 自定义名 |
SubagentStop |
子代理完成 | 同上 |
TeammateIdle |
团队成员空闲 | 无 |
TaskCompleted |
任务标记完成 | 无 |
Stop |
Claude 完成回复 | 无 |
Worktree
| 事件 | 触发时机 | Matcher 匹配字段 |
|---|---|---|
WorktreeCreate |
工作树创建 | 无 |
WorktreeRemove |
工作树移除 | 无 |
3. 配置格式与位置
配置结构
{
"hooks": {
"EventName": [
{
"matcher": "regex_pattern",
"hooks": [
{
"type": "command",
"command": "shell command here",
"timeout": 30,
"async": false
}
]
}
]
},
"disableAllHooks": false,
"allowedHttpHookUrls": ["https://hooks.example.com/*"],
"httpHookAllowedEnvVars": ["MY_TOKEN"]
}
配置位置与优先级
| 位置 | 作用域 | 可提交 Git | 优先级 |
|---|---|---|---|
~/.claude/settings.json |
全局(所有项目) | 否 | 最低 (5) |
.claude/settings.json |
单项目 | 是 | 中 (4) |
.claude/settings.local.json |
单项目(本地) | 否(gitignore) | 高 (3) |
| 托管策略设置 | 组织级 | 管理员控制 | 最高 (1) |
Plugin hooks/hooks.json |
插件启用时 | 随插件 | N/A |
| Skill/Agent frontmatter | 组件活跃时 | 随文件 | N/A |
配置方式
- 交互式:在 Claude Code 中输入
/hooks - 手动编辑:直接修改 settings.json
- 插件:在 Plugin manifest 中包含
hooks/hooks.json
4. 匹配器规则
Matcher 是正则表达式,根据事件类型匹配不同字段。
匹配示例
// 精确匹配单个工具
"matcher": "Bash"
// 匹配多个工具(正则 OR)
"matcher": "Edit|Write"
// 匹配所有 MCP 工具
"matcher": "mcp__.*"
// 匹配特定 MCP 服务器的工具
"matcher": "mcp__github__.*"
// 空匹配器 = 始终触发
"matcher": ""
MCP 工具命名规范
mcp__<server_name>__<tool_name>
示例:
- mcp__github__search_repositories
- mcp__filesystem__read_file
- mcp__ace-tool__search_context
5. Handler 类型
四种 Handler
| 类型 | 说明 | 适用场景 |
|---|---|---|
command |
本地 Shell 命令 | 最常用:验证、格式化、通知 |
http |
POST 到 HTTP 端点 | 外部服务、团队审计 |
prompt |
单轮 LLM 评估 | 是/否判断、模糊决策 |
agent |
多轮子代理(有工具访问权限) | 复杂验证、文件检查 |
command 类型
{
"type": "command",
"command": "bash .claude/hooks/validate.sh",
"timeout": 30,
"async": false
}
执行特点:
- 输入:JSON 通过 stdin 传入
- 输出:exit code + stdout/stderr
- 工作目录:会话的当前目录
- 去重:相同命令只执行一次
- 并行:同一事件的多个 Hook 并行运行
- 超时:默认 10 分钟
http 类型
{
"type": "http",
"url": "https://hooks.company.com/claude-events",
"headers": {
"Authorization": "Bearer $HOOK_TOKEN"
},
"allowedEnvVars": ["HOOK_TOKEN"],
"timeout": 15
}
- 仅
allowedEnvVars中的变量会被解析 - 需在
allowedHttpHookUrls中预先授权 URL
prompt 类型
{
"type": "prompt",
"prompt": "检查此操作是否安全。返回 {\"ok\": true} 或 {\"ok\": false, \"reason\": \"...\"}",
"model": "haiku"
}
agent 类型
{
"type": "agent",
"prompt": "验证所有单元测试通过: $ARGUMENTS",
"timeout": 120,
"model": "opus"
}
6. 环境变量
Hook 脚本可用变量
| 变量 | 说明 | 示例 |
|---|---|---|
CLAUDE_PROJECT_DIR |
项目根目录绝对路径 | /Users/user/myproject |
CLAUDE_CODE_REMOTE |
是否远程模式 | true / false |
CLAUDE_ENV_FILE |
设置会话环境变量的文件路径 | /tmp/claude-env-xyz |
CLAUDE_SESSION_ID |
当前会话唯一 ID | abc123def456 |
CLAUDE_FILE_PATHS |
文件相关 Hook 的文件路径 | /path/to/file.ts |
在 SessionStart 中设置环境变量
#!/bin/bash
# 追加到 CLAUDE_ENV_FILE 为会话设置变量
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export DEBUG=1' >> "$CLAUDE_ENV_FILE"
Shell Profile 注意事项
Hooks 在非交互式 Shell 中运行,但会 source ~/.zshrc 或 ~/.bashrc。若 Profile 中有无条件 echo,会污染 JSON 输出:
# 修复方式:包裹在交互式检测中
if [[ $- == *i* ]]; then
echo "Shell ready" # 仅交互式 Shell 执行
fi
7. 输入/输出 JSON 格式
通用输入字段(所有事件)
{
"session_id": "abc123",
"cwd": "/Users/sarah/myproject",
"hook_event_name": "PreToolUse",
"timestamp": "2026-02-27T14:30:00Z"
}
各事件输入示例
PreToolUse(Bash):
{
"session_id": "abc123",
"cwd": "/Users/sarah/myproject",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}
PreToolUse(Edit):
{
"hook_event_name": "PreToolUse",
"tool_name": "Edit",
"tool_input": {
"file_path": "/project/src/app.ts",
"old_string": "const x = 1;",
"new_string": "const x = 2;"
}
}
UserPromptSubmit:
{
"hook_event_name": "UserPromptSubmit",
"prompt": "Fix the authentication bug"
}
PostToolUse:
{
"hook_event_name": "PostToolUse",
"tool_name": "Edit",
"tool_input": { "file_path": "src/app.ts" },
"tool_response": "File edited successfully"
}
SessionStart:
{
"hook_event_name": "SessionStart",
"source": "resume",
"cwd": "/path/to/project"
}
SessionEnd:
{
"hook_event_name": "SessionEnd",
"exit_reason": "clear",
"duration_seconds": 1234
}
Notification:
{
"hook_event_name": "Notification",
"notification_type": "permission_prompt",
"message": "Claude Code needs your attention"
}
8. 响应决策控制
Exit Code 含义
| Code | 含义 | 行为 |
|---|---|---|
| 0 | 成功 | 操作继续。stdout 解析为 JSON 决策 |
| 2 | 阻止 | 操作被阻止。stderr 作为反馈发送给 Claude |
| 其他 | 部分失败 | 操作继续。stderr 仅在 verbose 模式记录 |
PreToolUse 决策
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "命令安全,自动放行",
"updatedInput": {
"command": "rg pattern ."
}
}
}
| 决策值 | 效果 |
|---|---|
"allow" |
绕过权限系统,直接执行 |
"deny" |
阻止工具,将原因反馈给 Claude |
"ask" |
向用户显示权限确认弹窗 |
updatedInput 可选字段:可修改工具的输入参数(如将 grep 替换为 rg)。
PostToolUse / Stop 决策
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"decision": "block",
"reason": "代码需要审查后才能继续"
}
}
PermissionRequest 决策
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "approve"
}
}
}
TaskCompleted 决策
{
"hookSpecificOutput": {
"hookEventName": "TaskCompleted",
"decision": "block",
"reason": "任务缺少必要的测试"
}
}
SessionStart / UserPromptSubmit 输出
#!/bin/bash
# stdout 内容会注入到 Claude 的上下文中
echo "提醒:使用 Bun 而非 npm。当前冲刺:auth 重构。"
exit 0
9. 实用模式与示例
模式 1:阻止危险操作
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/validate-bash.sh"
}
]
}
]
}
}
validate-bash.sh:
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command')
# 阻止危险命令
DANGEROUS_PATTERNS=("rm -rf" "git push --force" "git reset --hard" "DROP TABLE" "mkfs")
for pattern in "${DANGEROUS_PATTERNS[@]}"; do
if [[ "$CMD" == *"$pattern"* ]]; then
echo "Blocked: 检测到危险命令 '$pattern'" >&2
exit 2
fi
done
exit 0
模式 2:编辑后自动格式化
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
}
]
}
]
}
}
模式 3:桌面通知
macOS:
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude 需要你的注意\" with title \"Claude Code\"'"
}
]
}
]
}
}
Windows(PowerShell):
{
"hooks": {
"Notification": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "powershell -Command \"[System.Windows.Forms.MessageBox]::Show('Claude needs attention')\""
}
]
}
]
}
}
模式 4:保护文件不被修改
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/protect-files.sh"
}
]
}
]
}
}
protect-files.sh:
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED=(".env" "package-lock.json" ".git/" "credentials")
for pattern in "${PROTECTED[@]}"; do
if [[ "$FILE" == *"$pattern"* ]]; then
echo "Blocked: $FILE 匹配保护规则 '$pattern'" >&2
exit 2
fi
done
exit 0
模式 5:审计日志
{
"hooks": {
"PreToolUse": [
{
"matcher": "",
"hooks": [
{
"type": "http",
"url": "https://audit.company.com/claude-events",
"headers": {
"Authorization": "Bearer $AUDIT_TOKEN"
},
"allowedEnvVars": ["AUDIT_TOKEN"],
"async": true
}
]
}
]
}
}
模式 6:压缩后重新注入上下文
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "cat .claude/context-reminder.txt"
}
]
}
]
}
}
模式 7:自动放行安全命令
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash .claude/hooks/auto-approve-safe.sh"
}
]
}
]
}
}
auto-approve-safe.sh:
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command')
SAFE_PREFIXES=("npm test" "npm run lint" "npx prettier" "git status" "git log" "git diff" "ls " "cat ")
for prefix in "${SAFE_PREFIXES[@]}"; do
if [[ "$CMD" == "$prefix"* ]]; then
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"Safe command auto-approved"}}'
exit 0
fi
done
exit 0 # 非安全命令走正常权限流程
模式 8:工具输入修改(grep → rg)
#!/bin/bash
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command')
if [[ "$CMD" == grep* ]]; then
MODIFIED=$(echo "$CMD" | sed 's/^grep/rg/')
echo "{\"hookSpecificOutput\":{\"hookEventName\":\"PreToolUse\",\"permissionDecision\":\"allow\",\"updatedInput\":{\"command\":\"$MODIFIED\"}}}"
exit 0
fi
exit 0
10. 高级特性
异步 Hook
{
"type": "command",
"command": "node background-task.js",
"async": true,
"timeout": 30
}
- 异步执行,不阻塞 Claude
- 完成后若返回包含
systemMessage或additionalContext的 JSON,会在下一轮传递给 Claude
HTTP Hook 安全配置
{
"allowedHttpHookUrls": ["https://hooks.example.com/*"],
"httpHookAllowedEnvVars": ["HOOK_TOKEN", "HOOK_SECRET"],
"allowManagedHooksOnly": false
}
Stop Hook 防循环
{
"hook_event_name": "Stop",
"stop_hook_active": true // 此字段为 true 时表示当前在 Stop Hook 链中
}
Hook 脚本应检查此字段避免无限循环:
#!/bin/bash
INPUT=$(cat)
ACTIVE=$(echo "$INPUT" | jq -r '.stop_hook_active // false')
if [[ "$ACTIVE" == "true" ]]; then
exit 0 # 已在 Stop Hook 链中,不再触发
fi
# ... 正常逻辑
11. 故障排除
| 问题 | 解决方案 |
|---|---|
| Hook 未触发 | 检查 /hooks 菜单;验证 matcher 正则(区分大小写);确认事件类型正确 |
| "command not found" | 使用绝对路径或 $CLAUDE_PROJECT_DIR |
| JSON 校验失败 | 修复 Shell Profile(无条件 echo);包裹在 if [[ $- == *i* ]] 中 |
| Hook 超时 | 增加 timeout 字段(秒);优化脚本性能 |
| Hook 错误显示 | 手动测试:echo '{"tool_name":"Bash"}' | ./hook.sh |
| Stop Hook 死循环 | 检查 stop_hook_active 字段;为 true 时提前退出 |
| 异步输出丢失 | 确保 Hook 退出时输出包含 additionalContext 的有效 JSON |
| 权限不足 | 确保脚本有执行权限:chmod +x .claude/hooks/*.sh |