1. 为什么你的 CLAUDE.md 总是被 AI 当耳旁风用 Claude Code 写代码的人迟早会撞上同一堵墙你在 CLAUDE.md 里白纸黑字写了「改完代码必须跑 prettier」结果它十次里有三次忘得干干净净你写了「禁止直接改 migrations 目录」它照样手一抖把迁移文件覆盖了。这不是模型不听话而是你搞错了工具的性质——CLAUDE.md 是写给模型看的建议而模型是概率性的它可能看漏、可能被上下文挤掉、可能自作主张。Claude Code Hooks 就是来解决这个确定性问题的。简单说Hooks 是 Claude Code 在特定生命周期节点自动触发的程序级回调AI 动文件前、动完后、跑命令前、跑命令后、提交输入时、压缩上下文前都会按你预设的规则执行脚本。它不依赖模型记不记得而是由 Claude Code 运行时强制执行100% 触发零例外。一句话概括CLAUDE.md 是员工手册Hooks 是流水线闸门。这篇面向本地开发和 CI 场景把 Hooks 的配置语法、PreToolUse/PostToolUse 触发机制讲透再给你 8 个可以直接复制粘贴的生产级配方。每个配方都配了可复制的 settings 片段和逐条验证动作最后说明怎么把 endpoint 统一改到 TaoToken 的 Key/API 通道让鉴权和调用入口收敛到一处。适合已经在用 Claude Code、想让 AI 干活更可控的开发者也适合想把团队规范固化进工具链的技术负责人。2. TaoToken 前置把 Hooks 的调用入口统一到一条通道在动手写 Hooks 之前先把「AI 从哪调、用哪个 Key」这件事定下来。很多人 Hooks 配得挺漂亮结果 endpoint 散落在各个项目、各个 shell 环境变量里团队一协作就乱A 同事的 Key 额度用完了B 同事的配置里还硬编码着旧地址CI 上又是另一套。这种碎片化在 Hooks 场景下会被放大——因为 Hooks 会在每次工具调用前后自动跑一旦鉴权出问题报错会高频刷屏排查成本很高。我的做法是把 Claude Code 的 API 入口统一到 TaoToken。它的官网是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content API 基址是 https://taotoken.net/api 。你只需要在环境变量里配一次 Base URL 和 KeyClaude Code 以及它触发的所有 Hooks 脚本就都走同一条通道鉴权、额度、日志都在一处看。具体来说Claude Code 读取的是ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY这两个环境变量。把它们指向 TaoToken 的 API 地址和你在控制台生成的 Key就完成了统一入口。这样做有三个实际好处第一Hooks 脚本里如果需要发 HTTP 请求比如配方 8 的失败通知可以复用同一套鉴权思路不用再单独维护一份凭证第二团队协作时大家用同一个 Base URL只有 Key 各自管理配置模板可以直接提交到仓库第三CI 环境里注入环境变量即可不需要在流水线里散落多个 endpoint。这里要提醒一句Key 不要硬编码进 settings.json 或 Hooks 脚本用环境变量注入。Hooks 脚本本身也是代码一旦提交到仓库硬编码的 Key 就等于泄露。你可以把 Base URL 写进项目级配置把 Key 放在本地 shell 的.zshrc或 CI 的 secrets 里。配好之后先别急着写复杂 Hooks用一条最简单的请求验证通道是通的再往下走。3. 可复制配置settings.json 结构与 8 大配方Hooks 全部写在settings.json的hooks字段里结构是三层嵌套事件名 → 匹配器 → Hook 动作。作用域分三级~/.claude/settings.json是全局所有项目生效不提交仓库.claude/settings.json是项目级提交仓库团队共享.claude/settings.local.json是本地不提交个人偏好。推荐把安全类放全局工程类放项目级并提交个人偏好放 local。先看最小可用结构这是所有配方的骨架{ hooks: { PostToolUse: [ { matcher: Write|Edit|MultiEdit, hooks: [ { type: command, command: npx prettier --write ${tool_input.file_path}, timeout: 30000 } ] } ] } }Hook 类型有五种command跑 shell90% 场景够用、prompt注入提示词、agent调子 Agent 决策、http发请求、mcp_tool调 MCP 工具。可用变量包括${tool_name}、${tool_input.file_path}、${tool_input.command}等command 类型还能从 stdin 读完整 JSON。返回值通过 stdout 输出 JSON 控制行为PreToolUse 返回allow: false即拦截。下面是 8 个配方按优先级排序直接复制即可。配方 1拦截危险 Bash 命令全局必配{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, command: python3 ~/.claude/hooks/dangerous-command-check.py, timeout: 5000 } ] } ] } }脚本~/.claude/hooks/dangerous-command-check.py#!/usr/bin/env python3 import sys, json, re hook_input json.loads(sys.stdin.read()) command hook_input.get(tool_input, {}).get(command, ) patterns [rrm\s-rf\s/, rgit\spush\s.*--force, rgit\sreset\s--hard, rDROP\sTABLE, rcurl\s.*\|\s*(sh|bash)] for p in patterns: if re.search(p, command, re.IGNORECASE): print(json.dumps({allow: False, reason: f危险命令已拦截: {command[:100]}})) sys.exit(0) print(json.dumps({allow: True}))配方 2改完自动格式化 Lint项目必配{ hooks: { PostToolUse: [ { matcher: Write|Edit|MultiEdit, if: tool_input.file_path (tool_input.file_path.endsWith(.ts) || tool_input.file_path.endsWith(.tsx)), hooks: [ { type: command, command: npx prettier --write ${tool_input.file_path} npx eslint --fix ${tool_input.file_path}, timeout: 30000 } ] } ] } }配方 3禁止修改敏感文件{ hooks: { PreToolUse: [ { matcher: Write|Edit|MultiEdit, if: tool_input.file_path (tool_input.file_path.includes(migrations/) || tool_input.file_path.includes(.env)), hooks: [ { type: prompt, prompt: 警告你正在修改敏感文件 ${tool_input.file_path}必须人工审查。请停止并说明原因。 } ] } ] } }配方 4改完自动跑相关测试{ hooks: { PostToolUse: [ { matcher: Write|Edit|MultiEdit, if: tool_input.file_path tool_input.file_path.startsWith(src/), hooks: [ { type: command, command: FILE${tool_input.file_path}; TEST\${FILE%.ts}.test.ts\; if [ -f \$TEST\ ]; then npx jest \$TEST\ --no-coverage 21 | tail -20; else echo 无对应测试跳过; fi, timeout: 60000 } ] } ] } }配方 5自动注入 Git 上下文{ hooks: { UserPromptSubmit: [ { hooks: [ { type: command, command: BRANCH$(git rev-parse --abbrev-ref HEAD 2/dev/null); echo \{\\\additionalContext\\\:\\\当前分支: $BRANCH\\\}\, timeout: 3000 } ] } ] } }配方 6自动批准安全命令{ hooks: { PermissionRequest: [ { if: tool_input.command tool_input.command.trim().startsWith(npm test), hooks: [ { type: command, command: echo {\allow\: true, \reason\: \安全命令自动批准\}, timeout: 1000 } ] } ] } }配方 7压缩后保留关键记忆{ hooks: { PreCompact: [ { hooks: [ { type: command, command: if [ -f .claude/key-decisions.md ]; then echo \{\\\additionalContext\\\:\\\$(cat .claude/key-decisions.md | tr \\n )\\\}\; else echo {}; fi, timeout: 3000 } ] } ] } }配方 8命令失败自动通知{ hooks: { PostToolUse: [ { matcher: Bash, if: tool_result.exit_code ! 0, hooks: [ { type: http, url: ${SLACK_WEBHOOK_URL}, method: POST, headers: { Content-Type: application/json }, body: {\text\: \命令失败: ${tool_input.command}\} } ] } ] } }4. 验证请求逐条确认 Hook 真的生效了配置写完不等于生效Hooks 最容易踩的坑就是「以为配了其实没触发」。下面给你一套逐条验证动作从最简单到最完整照着做一遍心里就有底了。第一步验证通道和基础触发。先写一个最笨的 Hook只做一件事把输入打到日志文件。在项目级.claude/settings.json里加{ hooks: { PostToolUse: [ { matcher: Write, hooks: [ { type: command, command: echo \$(date) triggered\ /tmp/hook-test.log, timeout: 3000 } ] } ] } }然后让 Claude Code 写一个任意文件比如「创建一个 test.txt」。写完后执行cat /tmp/hook-test.log如果看到带时间戳的triggered说明 Hook 注册成功、事件触发正常。这一步过了再往下加逻辑。第二步验证 PreToolUse 拦截。把配方 1 的脚本放好chmod x给执行权限然后让 Claude Code 执行rm -rf /tmp/test-dir。正常情况下它会返回拦截信息命令不会真的执行。你可以先建一个/tmp/test-dir目录拦截后确认目录还在就证明拦截生效了。第三步验证 PostToolUse 的格式化。故意让 Claude Code 写一段格式混乱的 TS 代码比如缩进全乱、分号缺失。写完后打开文件看如果格式被 prettier 自动对齐了说明配方 2 生效。如果没变检查if条件里的文件后缀是否匹配、prettier 是否在项目里装了。第四步验证返回值 JSON 合法。这是最容易翻车的地方。Hook 脚本的 stdout 必须是合法 JSON多一个字符、少一个引号都会被忽略等于没生效。你可以手动测echo {tool_input:{command:rm -rf /}} | python3 ~/.claude/hooks/dangerous-command-check.py看输出是不是{allow: false, ...}这样的标准 JSON。如果报错或输出乱码先修脚本再谈其他。第五步验证上下文注入。配方 5 生效后你随便问一句「我现在在哪个分支」Claude Code 应该能直接答出来而不需要你手动告诉它。如果它答不上来检查git rev-parse是否在项目目录下能正常执行、JSON 转义是否正确。第六步验证压缩保留。这个需要长对话才能测。先往.claude/key-decisions.md写一条关键决策比如「本项目统一用 pnpm不用 npm」然后触发/compact压缩后再问「本项目用哪个包管理器」如果它答 pnpm说明配方 7 生效。整套验证下来你会发现一个规律Hooks 的问题几乎都出在「脚本输出不是合法 JSON」和「匹配器/if 条件写错」这两类。把这两点盯死成功率能到九成以上。5. 本篇常见错排查401、local proxy failed、reading choices、OAuthHooks 跑起来之后报错会以两种形式出现一种是 Hook 脚本自身的错一种是底层 API 调用的错。后者往往更隐蔽因为 Hooks 高频触发会把错误刷屏。下面按真实报错逐条拆。401 Unauthorized。这个最常见通常是 Base URL 或 Key 没配对。检查ANTHROPIC_BASE_URL是否指向 https://taotoken.net/api ANTHROPIC_API_KEY是否是控制台生成的有效 Key。注意 Base URL 结尾不要多加斜杠也不要写成官网首页地址。如果是在 CI 里报 401多半是 secrets 没注入到运行环境echo $ANTHROPIC_API_KEY确认一下。local proxy failed。这个报错说明 Claude Code 尝试走本地代理但连不上。先确认你的环境变量里没有残留的代理配置比如HTTP_PROXY、HTTPS_PROXY指向了一个已经关掉的本地端口。清掉这些变量让请求直连 TaoToken 的 API 地址即可。如果你在 Hooks 脚本里也用了 curl同样要确保它不继承这些失效的代理变量。reading choices 相关报错。这类错误通常出现在 Hook 脚本解析 API 响应时。如果你在 Hooks 里发了 HTTP 请求比如配方 8返回体结构和你预期的不一致就会在读取字段时报错。解决办法是先把原始响应打到日志里看结构再按实际字段解析别照抄网上的示例。OAuth 相关报错。Claude Code 某些版本会走 OAuth 流程如果你用的是 API Key 模式要确保没有混用两套鉴权。检查配置里是否同时存在 OAuth token 和 API Key二者留一个即可。用 TaoToken 统一通道时走 API Key 模式最省心。Hook 脚本输出不是合法 JSON。这个不报错但 Hook 静默失效。表现是「配置明明写了就是没反应」。用第 4 节的echo | python3手动测一遍确认输出是标准 JSON。变量没转义导致 shell 炸。文件名带空格或特殊字符时${tool_input.file_path}直接拼进命令会断。统一用${tool_input.file_path}包起来。Hook 互相打架死循环。A Hook 改了文件触发 B HookB 又触发 A。避免方式是让 Post 类 Hook 只做只读或幂等操作别在 PostToolUse 里再触发写文件。排查时记住一个顺序先确认 Hook 有没有触发看日志再确认脚本输出合不合法手动测最后确认底层 API 通不通看 401/proxy 类报错。三层分开查比一锅乱炖快得多。6. 把 Hooks 接进你的工作流从单点到流水线单个 Hook 是单点能力组合起来才是完整工作流。一个典型前端项目的全链路是这样的你说「帮我加个登录接口」UserPromptSubmit 先注入当前分支和项目规范AI 分析需求开始写代码PreToolUse 检查是不是在改敏感文件是就拦AI 写文件PostToolUse 依次跑 prettier、eslint --fix、相关单测测试挂了 AI 自动看报错再修PermissionRequest 把安全命令自动放行全部通过后继续下一步。你只需要说需求、审结果中间的格式、lint、测试全自动。组合原则很清晰Pre 类做护栏拦危险、禁敏感、校验输入Post 类做善后格式化、测试、生成文档UserPromptSubmit 做上下文增强PermissionRequest 做审批自动化。安全类配置放全局~/.claude/settings.json工程类放项目级并提交仓库个人偏好放 local。最后说几个我踩过的坑。Hook 脚本权限记得收紧chmod 700别让别人随便改Key 一律走环境变量别硬编码timeout 别设太大长任务单独配改完配置有些需要重启会话才生效。把 endpoint 统一到 TaoToken 之后团队协作时配置模板可以直接提交只有 Key 各自管理CI 里注入环境变量即可。需要生成 Key 或查看调用日志去控制台 https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 想先验证模型通道是否通用模型对话 https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 发一条测试长期跑编码和 Agent 任务用 Coding Plan https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 更划算接入细节看文档 https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content Key 管理在 API Keys https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_content 。