Claude Code 3.7 Hooks实战:AI编程自律全攻略

2026-06-01阅读 0热度 0
Claude

Claude Code Hooks 实战指南:让 AI 编程助手掌握"自控"能力

设想一个常见场景——你在 CLAUDE.md 中郑重写下三条铁律:

Claude Code -3.7  Hooks 实战指南:让 AI 编程助手学会

- 永远不要修改 .env 文件- 每次改完代码自动跑 prettier- Bash 命令不允许 rm -rf /

但实际运行中,Claude Code 在一次长会话里,经过上下文压缩后直接"失忆"——它擅自修改了 .env,改完代码没触发格式化,提交了一堆格式混乱的代码,甚至执行了 rm -rf /tmp/build。虽然这次没造成重大损失,但谁能担保下一次不会酿成大祸?

问题的根本原因一针见血:CLAUDE.md 只是"建议",Hooks 才是硬性"规则"。LLM 可能选择性忽略建议,但无法绕过系统层级的强制执行机制。

特性CLAUDE.mdHooks
执行方式LLM 自主决定是否遵从系统强制触发,每次必执行
可靠性依赖模型"自我约束"确定性保障
能力仅可写文本指令可运行脚本、调用 API、执行逻辑判断
适用场景通用规范、个人偏好安全红线、自动化工序、质量门禁

Hooks 生命周期——20+ 事件分层速查

不少文章宣称 Hooks 只有 8 个核心事件,但官方文档支持的事件远多于这个数目。按实际使用频率,可划分为三个层次。

高频事件(日常开发几乎必用)

事件触发时机核心用途
PreToolUse工具执行前拦截危险命令、保护敏感文件、参数校验
PostToolUse工具执行后自动格式化、运行测试、变更审计
StopClaude 完成回复时质量门禁(测试是否通过、任务是否完成)
NotificationClaude 发送通知时桌面提醒、转发到 Slack/飞书
UserPromptSubmit用户提交输入前输入验证、注入上下文

中频事件(特定场景很有用)

事件触发时机核心用途
SessionStart会话启动/恢复时初始化环境、压缩后重新注入上下文
SessionEnd会话结束时清理临时文件、记录统计
SubagentStart / SubagentStop子袋里启动/完成时子任务分发监控与验收
PostToolUseFailure工具执行失败时自动重试、错误上报
PermissionRequest权限弹窗时自动审批/拒绝特定权限
PreCompact / PostCompact上下文压缩前后压缩前保存关键信息/压缩后重新注入

低频但实用的事件

事件触发时机
ConfigChange配置文件变更时 — 审计/阻止未授权修改
CwdChanged工作目录切换时 — 自动加载 direnv 等环境变量
FileChanged监听文件变化 — 监听 .envrc/.env 变更自动重载
InstructionsLoadedCLAUDE.md/rules 加载时 — 追踪上下文加载情况
TaskCreated / TaskCompleted任务创建/完成时 — 生命周期追踪
WorktreeCreate / WorktreeRemoveworktree 创建/删除时
StopFailureAPI 错误导致停止时 — 错误恢复/降级处理
TeammateIdleagent team 成员空闲时 — 协调团队任务
Setup--init-only 启动时 — CI 一次性初始化
PermissionDenied工具调用被自动拒绝时 — 返回 {retry: true} 允许重试
UserPromptExpansion命令展开为 prompt 前 — 可拦截命令展开

四种 Hook 类型

Hooks 不只限于执行 shell 命令,官方支持 4 种类型,各自适用于不同场景。

1. Command Hook(最常用)

运行 shell 命令,通过 stdin 接收 JSON 上下文,通过 exit code 和 stdout 返回决策结果。

{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }

适用场景:确定性规则——格式化、拦截、日志记录。

2. Prompt Hook

将 hook 输入加上你的 prompt 发送给一个 Claude 模型(默认 Haiku),让模型做出判断,返回 {"ok": true/false, "reason": "..."}

{"type": "prompt","prompt": "Check if all tasks are complete. If not, respond with {"ok": false, "reason": "what remains to be done"}."}

适用场景:需要理解语义的判断——任务是否完成、代码质量是否达标。

3. Agent Hook(实验性)

与 Prompt Hook 类似,但会 spawn 一个子袋里,可以读文件、搜索代码、运行命令来做更深入的验证。默认 60 秒超时,最多 50 次工具调用。

{"type": "agent","prompt": "Verify that all unit tests pass. Run the test suite and check the results.","timeout": 120}

适用场景:需要实际验证代码状态的场景。生产环境建议先用 command hook。

4. HTTP Hook

POST 事件数据到 HTTP 端点,适合接入审计服务、Webhook 等。

{"type": "http","url": "http://localhost:8080/hooks/tool-use","headers": { "Authorization": "Bearer $MY_TOKEN" },"allowedEnvVars": ["MY_TOKEN"]}

适用场景:团队审计、外部系统集成。

选型速查:

场景推荐类型
格式化 / lint / 拦截command
判断任务是否完成prompt
需要跑测试验证agent
团队审计 / 外部通知http

Hook 通信协议

理解 Hook 与 Claude Code 之间的通信机制,是编写高质量 Hook 的基础。

输入:stdin 收到 JSON

每个 Hook 都通过 stdin 接收事件数据。例如 PreToolUse 事件:

{"session_id": "abc123","cwd": "/Users/sarah/myproject","hook_event_name": "PreToolUse","tool_name": "Bash","tool_input": {"command": "npm test"}}

你的脚本解析这些字段来做判断。UserPromptSubmit 收到 prompt 文本,SessionStart 收到 source(startup/resume/compact),不同事件的输入字段不同。

输出:exit code + stdout/stderr

Exit Code含义行为
0无异议正常流程继续;stdout 内容注入 Claude 上下文
2阻止动作被拦截,stderr 作为反馈发给 Claude
其他出错动作继续,stderr 显示为 hook error

JSON 结构化输出(更精细的控制)

Exit code 只有"放行/阻止"两种状态。要更精细的控制,exit 0 并向 stdout 输出 JSON:

PreToolUse 拦截并给理由:

{"hookSpecificOutput": {"hookEventName": "PreToolUse","permissionDecision": "deny","permissionDecisionReason": "Use rg instead of grep for better performance"}}

permissionDecision 三种值:

  • "allow":跳过交互式权限弹窗
  • "deny":拦截工具调用,reason 反馈给 Claude
  • "ask":正常弹出权限确认

需要特别注意的是:exit 2 和 JSON 输出不能混用。Exit 2 时 Claude Code 会忽略 JSON;要用 JSON 输出必须 exit 0。

实战案例:一份完整配置的逐行拆解

与其给你一堆零散的示例,不如把一份真实可用的配置拆透。以下配置来自实际项目,覆盖了 Hooks 最核心的三个场景:拦截 → 保护 → 校验。

完整配置

{"permissions": {"allow": ["Read","Bash(ls *)", "Bash(cat *)", "Bash(head *)", "Bash(tail *)","Bash(wc *)", "Bash(find *)", "Bash(grep *)", "Bash(echo *)","Bash(mkdir *)","Bash(git status)", "Bash(git log *)", "Bash(git diff *)","Bash(git branch *)", "Bash(git show *)","Bash(node --version)", "Bash(npm --version)","Bash(npx tsc --noEmit)"],"deny": ["Bash(rm -rf *)", "Bash(sudo *)", "Bash(curl *)", "Bash(wget *)","Read(./.env*)", "Read(./secrets/**)", "Read(./**/credentials*)","Edit(./.env*)", "Edit(./secrets/**)","WebFetch"],"ask": ["Bash(git push *)", "Bash(npm install *)"],"defaultMode": "acceptEdits"},"model": "ark-code-latest","hooks": {"PreToolUse": [{"matcher": "Bash","hooks": [{"type": "command","command": "python3 -c "import json,sys; d=json.load(sys.stdin); cmd=d.get('tool_input',{}).get('command',''); blocked=['rm -rf /','DROP TABLE','DROP DATABASE',':(){:|:&};:']; sys.exit(2) if any(b in cmd for b in blocked) else sys.exit(0)""}]},{"matcher": "Edit|Write|MultiEdit","hooks": [{"type": "command","command": "python3 -c "import json,sys; d=json.load(sys.stdin); p=d.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(x in p for x in ['.env','package-lock.json','.git/']) else 0)""}]}],"PostToolUse": [{"matcher": "Edit|Write|MultiEdit","hooks": [{"type": "command","command": "node .claude/contracts/cli/post-edit-check.js","onFailure": "notify"}]}]},"enabledPlugins": {"code-review@claude-plugins-official": false,"security-guidance@claude-plugins-official": true},"language": "chinese","alwaysThinkingEnabled": true,"effortLevel": "medium"}

逐层拆解

第一层:Permissions — 静态权限规则

permissions 是 Claude Code 的静态防线——无需执行任何脚本,纯规则匹配,零延迟:

  • allow:白名单放行。只允许读文件和安全的 Bash 命令(lscatgit status 等)
  • deny:黑名单拦截。rm -rfsudocurlwget 一律禁止;.envsecrets/ 目录既不能读也不能改
  • ask:需要确认。git pushnpm install 影响较大,每次都弹确认框

permissions vs hooks 的分工:

维度permissionshooks
判断方式静态规则匹配(glob 模式)动态脚本执行(可编程)
延迟零延迟需要脚本执行时间
能力只能按命令模式匹配可以解析命令内容、检查文件路径、调 API
例子Bash(rm -rf *) 拦截所有 rm -rf检查 rm -rf /tmp/build 是安全的,但 rm -rf / 必须拦截

这里有个关键设计思路:permissions 做粗粒度的静态拦截,hooks 做细粒度的动态判断。两者互补,不是替代关系。

第二层:PreToolUse — 工具执行前的两道关卡

关卡 1:Bash 危险命令检测

{"matcher": "Bash","hooks": [{"type": "command","command": "python3 -c "import json,sys; d=json.load(sys.stdin); cmd=d.get('tool_input',{}).get('command',''); blocked=['rm -rf /','DROP TABLE','DROP DATABASE',':(){:|:&};:']; sys.exit(2) if any(b in cmd for b in blocked) else sys.exit(0)""}]}

逐行拆解:

  1. matcher: "Bash" — 只在 Claude 要执行 Bash 命令时触发
  2. 从 stdin 读 JSON → 提取 tool_input.command 字段
  3. 检查命令是否包含 4 种危险模式:
    • rm -rf / — 根目录删除
    • DROP TABLE / DROP DATABASE — 数据库破坏
    • :(){:|:&};: — fork bomb
  4. 命中任何一个 → exit 2(拦截),Claude 收到拦截反馈
  5. 都没命中 → exit 0(放行),走正常的 permission 流程

你可能会问:为什么 permissions 里已经有 Bash(rm -rf *) 了还要加这个 Hook?答案在于精细度。permissions 只能做 glob 匹配——Bash(rm -rf *) 会拦截所有 rm -rf 开头的命令,包括 rm -rf /tmp/build 这种安全的清理操作。而 Hook 可以检查命令内容,只拦截真正危险的 rm -rf /

关卡 2:敏感文件保护

{"matcher": "Edit|Write|MultiEdit","hooks": [{"type": "command","command": "python3 -c "import json,sys; d=json.load(sys.stdin); p=d.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(x in p for x in ['.env','package-lock.json','.git/']) else 0)""}]}

  1. matcher: "Edit|Write|MultiEdit" — 覆盖所有文件编辑工具(管道符表示"或")
  2. 提取 tool_input.file_path
  3. 文件路径包含 .envpackage-lock.json.git/ 任一 → exit 2 拦截
  4. 否则 → exit 0 放行

这里有个双重保险:permissions 的 deny 规则里已经有 Edit(./.env*) 做静态拦截,即使 Hook 放行了,deny 规则仍然生效。

第三层:PostToolUse — 编辑后的自动化校验

{"matcher": "Edit|Write|MultiEdit","hooks": [{"type": "command","command": "node .claude/contracts/cli/post-edit-check.js","onFailure": "notify"}]}

  1. 文件编辑完成后自动触发
  2. 运行 post-edit-check.js 做校验(TypeScript 类型检查、lint 等)
  3. "onFailure": "notify" — Hook 执行失败时通知用户,而不是静默忽略

post-edit-check.js 实现参考:

const { execSync } = require('child_process');const input = JSON.parse(require('fs').readFileSync('/dev/stdin', 'utf8'));const filePath = input.tool_input?.file_path || '';// 只检查 .ts/.tsx 文件if (!filePath.match(/.(ts|tsx)$/)) {process.exit(0);}try {execSync('npx tsc --noEmit', { stdio: 'pipe', timeout: 30000 });process.exit(0);} catch (e) {console.error(`TypeScript check failed after editing ${filePath}`);process.exit(0); // 不阻止,只是通知}

第四层:Plugins — 安全插件兜底

"enabledPlugins": {"code-review@claude-plugins-official": false,"security-guidance@claude-plugins-official": true}

security-guidance 插件启用后,会在 Claude 的工具调用链中注入安全审查——它内部就是用 Hooks 实现的,每次工具调用前后跑一个独立模型做安全评估。这相当于在你手动配置的 Hooks 之外,再加一层官方维护的安全防线。

code-review 关掉是因为项目已有自定义的代码审查规则,避免重复。

Matcher 机制详解

Matcher 控制 Hook 何时触发,是精准配置的关键。

各事件的 Matcher 过滤字段

事件Matcher 过滤什么示例值
PreToolUse / PostToolUse工具名BashEdit|Writemcp__.*
SessionStart会话启动来源startupresumecompact
SessionEnd会话结束原因clearresumelogout
Notification通知类型permission_promptidle_prompt
SubagentStart / SubagentStop袋里类型ExplorePlan、自定义袋里名
ConfigChange配置来源user_settingsproject_settings
Stop / UserPromptSubmit不支持 matcher始终触发

if 字段:更精细的过滤(v2.1.85+)

Matcher 只能按工具名过滤。if 字段用权限规则语法,可以同时匹配工具名和参数:

{"matcher": "Bash","hooks": [{"type": "command","if": "Bash(git *)","command": ".claude/hooks/check-git-policy.sh"}]}

只在 Bash 命令是 git 子命令时才触发 Hook,其他 Bash 命令忽略。if 只对工具事件有效(PreToolUse、PostToolUse 等)。

踩坑记录

坑 1:Stop Hook 有 8 次上限

Stop Hook 连续 8 次返回 block 后,Claude Code 会强制停止。这是防无限循环的安全机制。

解法:检查 stop_hook_active 字段:

INPUT=$(cat)if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; thenexit 0# 已触发多次,允许停止fi

坑 2:PostToolUse 无法撤销操作

工具已经执行完了才触发 PostToolUse。需要拦截的必须用 PreToolUse。

坑 3:PermissionRequest 在非交互模式不触发

claude -p 跑非交互模式时,PermissionRequest Hook 不触发。改用 PreToolUse

坑 4:Hook 输出的 JSON 被 shell profile 污染

~/.bashrc~/.zshrc 里的 echo "Shell ready" 会被 prepend 到 Hook 的 JSON 输出前面,导致解析失败。

解法:用交互检测包裹:

if [[ $- == *i* ]]; thenecho "Shell ready"fi

坑 5:Hook allow 不能覆盖 deny 规则

PreToolUse Hook 返回 "allow" 可以跳过交互式权限弹窗,但如果 settings.json 里有对应的 deny 规则,工具调用仍然会被阻止。Hook 可以收紧限制,但不能放松限制。这是一条安全底线——即使 Hook 代码有 bug,deny 规则仍然兜底。

坑 6:exit 2 和 JSON 输出不能混用

Exit 2 时 Claude Code 忽略 stdout 的 JSON;要用结构化 JSON 输出控制行为,必须 exit 0。

速查表

操作命令/配置
查看 Hook/hooks
禁用所有 Hook"disableAllHooks": true
Hook 超时默认 10 分钟(command),30 秒(prompt),60 秒(agent)
Exit 0放行,stdout 注入上下文
Exit 2阻止,stderr 反馈给 Claude
Exit 其他放行,stderr 显示为 hook error
JSON 输出Exit 0 + stdout 写 JSON(不能和 Exit 2 混用)
matcher 语法管道分隔:Edit|Write;正则:mcp__.*
if 字段权限规则语法:Bash(git *),仅工具事件可用
环境变量$CLAUDE_PROJECT_DIR 指向项目根目录
调试claude --debug-file /tmp/claude.log/debug
Hook 配置位置~/.claude/settings.json(全局)/ .claude/settings.json(项目)/ .claude/settings.local.json(本地)
免责声明

本网站新闻资讯均来自公开渠道,力求准确但不保证绝对无误,内容观点仅代表作者本人,与本站无关。若涉及侵权,请联系我们处理。本站保留对声明的修改权,最终解释权归本站所有。

相关阅读

更多
欢迎回来 登录或注册后,可保存提示词和历史记录
登录后可同步收藏、历史记录和常用模板
注册即表示同意服务条款与隐私政策