1. 为什么要在 Claude Code 里折腾 hooks
Claude Code 用久了会发现一个尴尬:它写代码很快,但有些动作你并不想让它自动做。比如它顺手rm -rf掉一个目录、把带SECRET的字符串写进源码、或者改完文件后忘了跑格式化。这些事靠人盯着不现实,靠提示词约束也不稳,真正靠谱的做法是挂 hooks——在工具调用前后插入你自己的脚本,让规则由代码执行,而不是靠模型自觉。
hooks 是什么?简单说就是 Claude Code 在特定生命周期节点触发的回调。它支持 PreToolUse(工具执行前)、PostToolUse(工具执行后)、UserPromptSubmit(提交提示前)、Stop(响应结束)等事件。你可以在这些节点上挂一段 shell 命令,命令返回非零退出码就能拦截动作,返回 0 就放行。适合谁?适合已经把 Claude Code 当日常编码工具、想让自动化流程更可控的开发者,尤其是团队里想统一规则、又不想每次口头提醒的人。
零成本怎么理解?不是指模型免费,而是指你不需要额外买服务、不需要自建网关,用一份统一的 Key 和 API 通道就能把 hooks 链路跑通。我试过在本地把 hooks 和统一 Key 接在一起,整个链路从配置到验证大概十几分钟,踩的坑主要集中在配置层级和退出码上。这篇就按「先讲场景 → 接 Key → 写配置 → 验证 → 排错 → 收尾」的顺序,把可复制的片段都给你,目标是一次跑通。
核心检索词先点明:Claude Code hooks 是一套事件回调机制,能做什么?能在工具执行前后拦截或追加动作;适合谁?适合想让编码流程自动化、可审计的开发者。下面进入实操。
2. TaoToken 统一 Key 与 API 通道前置准备
在写 hooks 之前,得先让 Claude Code 能稳定调用模型。这里用 TaoToken 做统一入口,好处是一个 Key 覆盖多种模型通道,hooks 里如果要调用模型做二次判断(比如让模型判断某条命令是否危险),也能复用同一个通道,不用再维护第二套凭证。
先拿 Key。打开 https://taotoken.net/api-keys ,登录后创建一个 API Key,复制保存。注意 Key 只在创建时完整显示一次,丢了就重建。拿到 Key 后,Claude Code 侧需要两个环境变量:ANTHROPIC_BASE_URL和ANTHROPIC_API_KEY。Base URL 用https://taotoken.net/api,不要带任何多余路径。
环境变量写法分两种。临时生效,直接在终端里 export:
export ANTHROPIC_BASE_URL="https://taotoken.net/api" export ANTHROPIC_API_KEY="sk-你的Key"永久生效,写进 shell 配置文件。zsh 用户改~/.zshrc,bash 用户改~/.bashrc:
echo 'export ANTHROPIC_BASE_URL="https://taotoken.net/api"' >> ~/.zshrc echo 'export ANTHROPIC_API_KEY="sk-你的Key"' >> ~/.zshrc source ~/.zshrc验证环境变量是否生效:
echo $ANTHROPIC_BASE_URL echo $ANTHROPIC_API_KEY两条都应输出对应值。如果ANTHROPIC_API_KEY为空,说明配置文件没 source 或者写错了文件。这一步别跳过,后面 hooks 里如果调用模型,读的就是这两个变量。
关于模型 ID,Claude Code 默认会用一个模型名,如果你在 TaoToken 侧想指定具体模型,可以在 settings 里配model字段。常见写法是claude-sonnet-4-5这类 ID,具体以你账号下可用模型为准。三件套记牢:Base URL、Key、Model ID,缺一个都可能报 401 或模型不存在。
注意:Key 不要提交到 Git,不要写进项目里的 settings.json 明文。项目级配置建议用环境变量引用,或者放在
settings.local.json并加入.gitignore。
前置准备做完,Claude Code 应该能正常对话了。可以先跑一句简单请求确认通道通,再进入 hooks 配置。如果这一步就报错,先解决通道问题,别急着写 hooks,否则排错会混在一起。
3. 可复制的 settings.json hooks 配置片段
Claude Code 的 hooks 配置放在 settings 文件里,层级有三个:项目级.claude/settings.json、用户级~/.claude/settings.json、本地不共享.claude/settings.local.json。团队共享的规则放项目级,个人偏好放用户级,含密钥或临时调试的放 local。hooks 字段的结构是「事件名 → 匹配器数组 → 命令」。
下面给一份可直接复制的项目级.claude/settings.json,覆盖 PreToolUse 和 PostToolUse 两个最常用事件。路径就是项目根目录下的.claude/settings.json,原文一致,别放错。
{ "model": "claude-sonnet-4-5", "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "python3 .claude/hooks/guard_bash.py" } ] } ], "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true" } ] } ] } }逐段解释。model指定走 TaoToken 时用的模型 ID,按你账号可用模型填。PreToolUse下matcher是Bash,意思是只对 Bash 工具调用触发;hooks数组里type为command,command是要执行的脚本路径。PostToolUse的matcher是Edit|Write,用竖线表示匹配多个工具,编辑或写入文件后触发 prettier 格式化。
$CLAUDE_FILE_PATH是 Claude Code 注入的环境变量,指向被操作的文件路径。不同版本变量名可能略有差异,如果格式化没生效,先打印一下所有CLAUDE_开头的变量确认:
env | grep CLAUDE_guard_bash.py 是拦截脚本,放在.claude/hooks/下。它从 stdin 读 JSON,里面包含工具名和参数,判断命令是否危险,危险就退出码 1 拦截,安全就退出 0 放行。示例:
#!/usr/bin/env python3 import json import sys import re def main(): raw = sys.stdin.read() try: payload = json.loads(raw) except json.JSONDecodeError: sys.exit(0) command = payload.get("tool_input", {}).get("command", "") danger = [r"rm\s+-rf\s+~", r"rm\s+-rf\s+/", r":\(\)\{.*\};:"] for pattern in danger: if re.search(pattern, command): print(f"blocked dangerous command: {command}", file=sys.stderr) sys.exit(1) sys.exit(0) if __name__ == "__main__": main()给脚本执行权限:
chmod +x .claude/hooks/guard_bash.py如果你用 Cline MCP 或 Codex 的 auth.json 体系,思路一样:Base URL 填https://taotoken.net/api,Key 填你的 Key,Model ID 填可用模型。三件套对齐,hooks 里调模型才不会 401。CC Switch 这类切换工具也是同样三个字段,别只填 Key 漏了 Base URL。
提示:hooks 命令默认超时 60 秒,复杂脚本要拆分或加超时控制。格式化这种快操作没问题,跑全量测试就可能超时。
配置保存后 Claude Code 会提示复审设置变更,确认即可。hooks 不需要重启,保存即生效。这一步做完,链路已经搭好一半,接下来验证。
4. 触发验证与成功结果确认
配置写完不验证等于没写。验证分三层:脚本本身能跑、hooks 被触发、拦截和放行都符合预期。
先单独测脚本。手动喂一段 JSON 给 guard_bash.py:
echo '{"tool_input":{"command":"rm -rf ~/test"}}' | python3 .claude/hooks/guard_bash.py echo "exit code: $?"预期输出blocked dangerous command: rm -rf ~/test,退出码 1。再喂一条安全命令:
echo '{"tool_input":{"command":"ls -la"}}' | python3 .claude/hooks/guard_bash.py echo "exit code: $?"预期无输出,退出码 0。脚本层通过后,再验证 Claude Code 是否真的调用它。
在 Claude Code 里输入一句会触发 Bash 的请求,比如「帮我删除 ~/test 目录」。如果 hooks 生效,你会看到命令被拦截,Claude Code 提示 hook 返回非零退出码,动作没有执行。这就是 PreToolUse 拦截成功的标志。
再验证 PostToolUse。让 Claude Code 编辑一个.js文件,故意写成乱格式:
const a={b:1,c:2}保存后如果 prettier hook 生效,文件会被自动格式化成:
const a = { b: 1, c: 2 };打开文件确认格式变了,说明 PostToolUse 触发成功。如果没变,先确认 prettier 是否安装、$CLAUDE_FILE_PATH是否有值。
验证模型通道是否走 TaoToken,可以在 Claude Code 里问一句「你当前使用的模型 ID 是什么」,或者看请求日志。更直接的方式是临时改错 Key,看是否报 401,确认请求确实打到 TaoToken。确认后改回正确 Key。
成功结果长这样:危险命令被拦、安全命令放行、编辑后自动格式化、模型请求正常返回。四件事都过,hooks 链路就算跑通。任何一件不过,进下一节排错。
5. 常见报错与失败排查清单
排错按「报错信息 → 可能原因 → 处理」来,下面都是真实会遇到的。
401 Unauthorized。原因通常是 Key 错、Key 过期、或者 Base URL 写错。检查echo $ANTHROPIC_API_KEY是否有值,echo $ANTHROPIC_BASE_URL是否为https://taotoken.net/api。如果 Key 是从别处复制的,注意有没有多余空格或换行。重建一个 Key 再试是最快的排除法。
local proxy failed 或连接被拒。这类多半是 Base URL 带了多余路径,比如写成https://taotoken.net/api/v1。正确写法就是https://taotoken.net/api,不要加后缀。另外检查本机网络是否能正常访问该域名,公司网络限制的话换网络环境再试。
reading choices 相关报错。通常是响应结构不符合预期,可能模型 ID 填错,或者请求打到了不兼容的端点。确认model字段是你账号下真实可用的 ID,别照抄网上的示例。三件套 Base URL、Key、Model ID 再对一遍。
OAuth 相关报错。如果你之前用 OAuth 登录过别的通道,环境变量可能被覆盖。检查 shell 配置文件里有没有旧的ANTHROPIC_变量,清掉再 source。env | grep ANTHROPIC能列出所有相关变量,逐个核对。
hooks 不触发。先确认 settings 文件路径对不对:项目级是.claude/settings.json,不是项目根目录的settings.json。再确认 JSON 语法合法,用python3 -m json.tool .claude/settings.json校验。matcher 写错也会导致不触发,比如工具名大小写不对。
hook 脚本报权限错误。chmod +x给过权限了吗?如果脚本用 python3 调用,确认 python3 在 PATH 里。脚本第一行 shebang 写对,#!/usr/bin/env python3。
hook 超时。默认 60 秒,格式化、lint 这类快操作没问题,跑测试或构建容易超时。把重操作拆成异步,或者只对特定文件类型触发,减少执行次数。
格式化没生效。$CLAUDE_FILE_PATH可能为空,先env | grep CLAUDE_确认变量名。prettier 没装的话npx prettier会失败,加|| true只是吞掉错误,不解决根本问题,先本地装好。
拦截太激进。guard 脚本正则写太宽,把正常命令也拦了。先用echo打印实际收到的 command 字段,确认匹配逻辑,再收窄正则。宁可先松后紧,别一上来就全拦。
排查顺序建议:先通道(401/连接)→ 再配置(JSON 语法/路径)→ 再脚本(权限/退出码)→ 最后匹配器(matcher/变量)。一层层过,别跳。
6. 把 hooks 用起来的几个实用建议
链路跑通后,别急着堆一堆规则。先从一两条有即时反馈的开始,比如格式化 hook 和危险命令拦截,这两类效果肉眼可见,调试也直观。规则多了之后,每个 hook 的退出码和输出都要能看懂,否则出问题不知道是哪条拦的。
自动生成的规则文件,哪怕是工具帮你写的,也要人工过一遍。正则匹配范围、退出码逻辑、有没有误伤正常操作,这些机器判断不了。把.claude/hooks/和 settings 纳入 Git 管理,团队共享规则的同时也能追溯谁改了什么。
hooks 能执行任意 shell 命令,权限跟你的用户一致,配置不当有风险。别从不可信来源直接复制 hook 脚本,尤其是带网络请求或文件删除的。Claude Code 在设置变动后会提示复审,认真看,别一路回车。
需要长期跑编码任务或 Agent 流程的,可以了解下 Coding Plan,把 hooks 和统一通道结合起来用,规则和凭证都集中管理。验证模型通道是否正常,用模型对话页面快速试一句就行。接入细节和更多配置示例在接入文档里,排障时对照着看比盲猜快。
最后一句实操经验:hooks 的调试成本主要在「不知道有没有触发」,所以每个脚本开头加一行日志到临时文件,触发时写入时间戳和参数,排查时一目了然。跑通之后把这行日志去掉,保持干净。