ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

CLAUDE.md 遵守率只有 80%?用 Hooks 的 PreToolUse/PostToolUse/Stop 补齐关键 20% 场景

CLAUDE.md 遵守率只有 80%?用 Hooks 的 PreToolUse/PostToolUse/Stop 补齐关键 20% 场景 1. 为什么 CLAUDE.md 只能管住 80% 的场景如果你用 Claude Code 做过一段时间的工程化落地大概率会遇到这种落差CLAUDE.md 里明明写了「提交前必须跑测试」「不要动 .env」「格式化用 prettier」但 Claude 该忘还是忘。我自己的体感是遵守率大概在 80% 上下——大部分时候它很听话可一旦任务变长、上下文被压缩、或者它连续改了好几个文件那 20% 的关键约束就开始漏。这不是 Claude 不聪明而是机制决定的。CLAUDE.md 本质是一份「说明书」它进入的是模型的上下文靠的是语言理解和自觉执行。模型在生成下一步动作时会权衡当前任务目标、上下文里的各种信息CLAUDE.md 只是其中一条软约束。当它和「快速完成任务」冲突时软约束经常被牺牲。Hooks 则完全不同。它是 Claude Code 运行时直接执行的命令不经过模型判断。你配了 PreToolUse 拦截rm -rf那这个工具调用在真正执行前就会被脚本拦下来返回退出码 2Claude 收到拒绝信号后必须换方案。这个过程没有「模型愿不愿意」的空间是 100% 强制的。所以正确的分工是CLAUDE.md 管意图、风格、通用指导比如「这个项目用 pnpm 不用 npm」「注释写中文」「优先复用 utils 里的函数」。Hooks 管底线、自动化检查、强制流程比如「危险命令必须拦」「改完代码必须格式化」「PR 创建前测试必须全过」。前者覆盖 80% 的日常后者补齐那 20% 一旦出事就很痛的场景。这篇就按这个思路把 PreToolUse、PostToolUse、Stop 三类钩子的可复制配置给你并演示一次「拦截危险命令 自动格式化 收尾校验」的完整流程。如果你还没配好 Claude Code 的接入环境可以先用 TaoToken 的 API 把模型通道打通再回来配 Hooks这样调试时不会两头卡。2. TaoToken 前置先把 Claude Code 的模型通道配好Hooks 是 Claude Code 客户端侧的能力跟模型走哪个通道没关系。但如果你现在还没跑通 Claude Code直接配 Hooks 会很难判断问题出在哪——是钩子没生效还是模型请求本身就没通。所以这一步先把接入做干净。TaoToken 提供的是兼容 Anthropic 协议的 API 通道Claude Code 可以直接对接。你需要三样东西Base URL、API Key、Model ID。Base URL 用https://taotoken.net/apiAPI Key 在控制台的 API Keys 页面创建Model ID 按你实际要用的模型填。先创建 Key。打开 https://taotoken.net/api-keys 新建一个 Key 并复制保存。注意 Key 只在创建时完整显示一次丢了就得重建。然后配置 Claude Code 的环境变量。最直接的方式是在 shell 配置文件里写export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的key export ANTHROPIC_MODELclaude-sonnet-4-5-20250929如果你用的是 Claude Code 的 settings 方式也可以写进~/.claude/settings.json{ env: { ANTHROPIC_BASE_URL: https://taotoken.net/api, ANTHROPIC_API_KEY: sk-你的key, ANTHROPIC_MODEL: claude-sonnet-4-5-20250929 } }配完执行claude进交互模式随便问一句「你好」能正常回复就说明通道通了。这一步别跳过因为后面 Hooks 调试时你会频繁让 Claude 执行 Bash 命令如果模型通道本身不稳你会误以为是钩子的问题。关于模型选择日常编码用 Sonnet 系列性价比高复杂重构或长链路 Agent 任务可以切 Opus。切换模型只改ANTHROPIC_MODEL就行不用动其他配置。如果你打算长期跑编码任务可以看下 Coding Plan 的额度方案比按量付费更适合高频使用。通道通了之后我们进入正题Hooks 的配置结构。3. 可复制配置PreToolUse / PostToolUse / Stop 三件套Hooks 的配置文件按优先级从高到低有三个位置.claude/settings.local.json本地私有不提交 Git、.claude/settings.json项目级团队共享、~/.claude/settings.json用户级全局生效。团队协作的强制约束建议放项目级个人习惯放用户级。触发时机有三个关键点。PreToolUse 在工具调用前执行用来拦截PostToolUse 在工具调用后执行用来做格式化、lint、测试Stop 在 Claude 完成一次回复时执行用来做收尾校验或自动提交。先看完整的 settings 结构这是可以直接复制改的{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: .claude/hooks/block-dangerous.sh }, { type: command, command: .claude/hooks/log-commands.sh } ] }, { matcher: Edit|Write, hooks: [ { type: command, command: .claude/hooks/protect-files.sh } ] } ], PostToolUse: [ { matcher: Write|Edit, hooks: [ { type: command, command: jq -r .tool_input.file_path | xargs npx prettier --write 2/dev/null; exit 0 }, { type: command, command: npx eslint --fix $(jq -r .tool_input.file_path) 21 | tail -10; exit 0 } ] } ], Stop: [ { matcher: , hooks: [ { type: command, command: .claude/hooks/final-check.sh } ] } ] } }matcher 是工具名匹配Bash匹配所有 Bash 调用Edit|Write匹配编辑和写入mcp__github__create_pull_request匹配特定 MCP 工具。Stop 的 matcher 留空表示匹配所有停止事件。关键点在于退出码。PreToolUse 的钩子脚本返回 0 表示放行返回 2 表示阻断并把 stderr 反馈给 Claude让它重新尝试。返回其他非零值通常只记录不阻断。这个 2 是拦截的核心很多人配了钩子没效果就是脚本里忘了exit 2。下面写拦截危险命令的脚本.claude/hooks/block-dangerous.sh#!/usr/bin/env bash input$(cat) cmd$(echo $input | jq -r .tool_input.command // empty) if echo $cmd | grep -qE rm[[:space:]]-rf[[:space:]]/|mkfs|dd[[:space:]]if|:\(\)\{; then echo BLOCKED: 检测到危险命令请改用更安全的替代方案例如先备份再删除具体路径。 2 exit 2 fi exit 0保护敏感文件的脚本.claude/hooks/protect-files.sh#!/usr/bin/env bash input$(cat) path$(echo $input | jq -r .tool_input.file_path // empty) if echo $path | grep -qE \.env$|\.env\.|secrets\.|id_rsa; then echo BLOCKED: $path 是敏感文件如需修改请说明必要性并手动操作。 2 exit 2 fi exit 0收尾校验脚本.claude/hooks/final-check.sh#!/usr/bin/env bash if ! git diff --quiet; then echo 提示当前有未提交改动建议检查后再结束。 2 fi exit 0写完记得给执行权限chmod x .claude/hooks/*.sh。日志文件.claude/command-log.txt记得加进.gitignore别把命令历史提交上去。4. 验证请求跑一次完整流程看钩子是否生效配置写完不验证等于没配。我们用一个具体任务走一遍让 Claude 尝试执行危险命令、改一个文件触发格式化、然后结束触发收尾校验。第一步验证 PreToolUse 拦截。在 Claude Code 里输入帮我清理一下项目执行 rm -rf /tmp/test-build正常情况下Claude 会尝试调用 Bash 工具但钩子在执行前拦截返回退出码 2。你会看到 Claude 收到拒绝信息后改变策略比如回复「检测到危险命令被拦截我改用更安全的方式」或者询问你具体要删哪个目录。如果它真的执行了说明钩子没生效检查脚本路径和权限。第二步验证 PostToolUse 格式化。让 Claude 改一个 JS 文件把 src/utils/format.js 里的 formatDate 函数改成用 dayjs 实现Claude 用 Edit 工具改完后PostToolUse 钩子会自动跑 prettier 和 eslint。你去看文件格式应该已经被统一了。如果 prettier 没跑检查npx prettier是否在项目里装了以及 jq 是否可用。第三步验证 Stop 收尾。让 Claude 结束当前任务比如输入「好了先到这里」。Stop 钩子触发如果工作区有未提交改动你会看到提示信息。这一步不会阻断只是提醒。整个流程跑通后你可以把日志打开看命令记录cat .claude/command-log.txt应该能看到刚才 Claude 尝试执行的命令和时间戳。这个日志在排查「Claude 到底执行过什么」时特别有用尤其是它说「我已经改好了」但你不确定它改了什么的时候。验证通过后你就有了一个「拦截 格式化 收尾」的最小闭环。接下来可以按项目需要往上加比如 PostToolUse 里加测试、PreToolUse 里加 PR 前测试检查。5. 本篇常见错排查401、local proxy failed、reading choices配 Hooks 和接入通道时报错基本集中在几个地方。我按实际遇到的频率排一下。401 Unauthorized。这个几乎都是 API Key 的问题。检查ANTHROPIC_API_KEY是否完整复制、有没有多余空格、是不是在 TaoToken 控制台被删了。还有一种情况是 Key 配在了 shell 里但 Claude Code 读的是 settings.json两边不一致。统一用一处配置别混着来。local proxy failed / connection refused。这个通常是 Base URL 写错比如漏了/api或者写成了带 UTM 的完整链接。Base URL 就用https://taotoken.net/api不要加别的路径。另外检查本地网络是否能正常访问该地址公司网络有出口限制的话需要找运维确认。reading choices / unexpected response format。这个报错说明请求发出去了但返回结构不对常见原因是 Model ID 填错或者用了不兼容的模型名。确认ANTHROPIC_MODEL是有效的模型标识别自己拼。如果刚改过配置重启一下 Claude Code 让环境变量重新加载。Hooks 不生效。先确认脚本有执行权限ls -l .claude/hooks/看有没有 x。再确认 settings.json 的 JSON 格式没问题可以用jq . .claude/settings.json校验。最后确认 matcher 写对了Bash和bash不一样工具名是大小写敏感的。OAuth 相关报错。如果你之前用官方登录方式配过 Claude Code环境变量和 OAuth 凭证可能冲突。清掉旧的凭证缓存统一走 API Key 方式。具体就是删掉~/.claude下跟认证相关的缓存文件重新用环境变量启动。退出码 2 没阻断。检查脚本是不是在最后无条件exit 0了或者exit 2写在了子 shell 里没传出来。用bash -x .claude/hooks/block-dangerous.sh手动喂一个 JSON 进去调试看走到哪个分支。排查顺序建议先确认模型通道通能正常对话再确认钩子脚本单独能跑最后确认 settings 挂载正确。三层分开验证比一上来就怀疑配置要快得多。6. 把关键约束从文档升级为机制回到开头那个 80% 的问题。CLAUDE.md 该写还得写它负责让 Claude 理解你的项目意图、代码风格、协作习惯这部分是软性的、需要模型判断的Hooks 替代不了。但那些「一旦漏了就出事」的约束比如危险命令、敏感文件、提交前测试、格式化必须用 Hooks 兜底。我的建议是先从两个钩子开始PreToolUse 拦危险命令PostToolUse 自动格式化。这两个收益最高、门槛最低配完立刻能感觉到差别。跑顺了再加 Stop 收尾校验和 PR 前的测试检查。配置过程中如果模型通道还没打通或者想换个更稳的接入方式可以从 API Keys 页面拿 Key接入文档里有完整的参数说明。需要验证模型响应是否正常时用模型对话页面直接测一句最快。长期跑编码和 Agent 任务的话Coding Plan 的额度模式比按量更省心。最后留一个实用习惯每次加新钩子都用bash -x手动喂 JSON 测一遍确认退出码符合预期再挂到 settings 里。钩子这东西配错了不报错、只是静默不生效比报错更难查。
返回列表