ARTICLE DETAIL

资讯详情

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

新手上路(五):Claude Code 总是自作主张?用 Hooks 写 10 条规则,拦截删文件、护秘钥、强测

新手上路(五):Claude Code 总是自作主张?用 Hooks 写 10 条规则,拦截删文件、护秘钥、强测 1. 为什么你的 Claude Code 需要一套 Hooks 规则Claude Code 能自主调用 Bash、Edit、Write 等工具这既是效率来源也是风险来源。它可能在重构时顺手rm -rf掉一个目录可能为了帮你补全配置直接改写.env也可能在没跑任何测试的情况下告诉你任务完成。这些行为不是模型故意捣乱而是它默认没有项目级的硬约束。Hooks 就是补上这层硬约束的机制。它是在 Claude Code 生命周期特定节点上注册的自定义脚本事件触发时Claude Code 通过 stdin 把 JSON 上下文喂给你的脚本脚本用退出码和 stdout 回话——放行、拦截、修改输入、注入上下文。和 Skills、MCP 最大的区别在于Hooks 能真正阻止一次工具调用而不只是给模型提建议。这篇面向刚上手 Claude Code 的新手围绕settings.json里的PreToolUse和Stop两类钩子给出 10 条可直接复制的规则骨架重点覆盖三件事拦截rm删文件、阻止读写.env秘钥、强制跑测试才能收工。每条规则都配了验证动作你照着敲一遍就能看到拦截效果。同时说明如何用 TaoToken 统一 Key 和 API 通道接入避免在多个环境变量之间来回切换。适合谁已经装好 Claude Code CLI、跑过/init、项目根目录有CLAUDE.md但还没配过任何 Hook 的同学。读完你能独立写出项目级 Hook并知道每条规则为什么这么写。2. 前置准备用 TaoToken 统一 Key 与 API 通道在写 Hook 之前先把模型接入这条链路理顺。Claude Code 需要读取 API Key 和 Base URL如果你同时用多个模型供应商环境变量会变得很乱。TaoToken 的作用是把 Key 和 API 通道统一到一处Claude Code、Coding Plan、模型对话都走同一个入口。官网地址https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI 入口不加 UTMhttps://taotoken.net/api操作顺序建议这样走先注册并登录进入控制台创建 API Key然后在 Claude Code 侧配置环境变量。控制台地址https://taotoken.net/console?utm_sourcetaotoken_aicg_blog_endutm_contentconsoleutm_campaignrewrite创建 Key 的页面在https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite拿到 Key 之后在项目里配置环境变量。Windows PowerShell 下这样写$env:ANTHROPIC_BASE_URL https://taotoken.net/api $env:ANTHROPIC_API_KEY sk-你的KeymacOS / Linux 下export ANTHROPIC_BASE_URLhttps://taotoken.net/api export ANTHROPIC_API_KEYsk-你的Key如果你希望长期生效把这两行写进~/.bashrc或~/.zshrc。Windows 用户可以在系统环境变量里添加避免每次开新终端都要重设。注意Hook 脚本里不要硬编码 API Key。所有涉及密钥的读取都通过环境变量脚本本身只做逻辑判断。这也是后面护秘钥规则能成立的前提——秘钥只存在于环境变量和.env文件里不进代码、不进 Hook。接入文档在https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite配置完成后用一次简单对话验证通道是否通。模型对话入口https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite如果你打算长期用 Claude Code 做编码和 Agent 任务Coding Plan 会更省心https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewrite3. 可复制配置settings.json 与 10 条规则骨架Hook 配置写在.claude/settings.json里结构是三层嵌套事件名 → 匹配器组 → 处理器列表。匹配器matcher过滤工具名if字段在处理器级别进一步过滤参数。退出码 2 表示拦截stderr 会展示给 Claude。先建目录mkdir -p .claude/hooks下面 10 条规则分成三组全部可以复制进.claude/settings.json。为便于阅读我按组拆开你合并到同一个hooks对象即可。3.1 拦截删文件规则 1–4规则 1拦截rm -rf和rm -r -f。脚本.claude/hooks/block-rm-rf.sh#!/bin/bash COMMAND$(jq -r .tool_input.command // /dev/stdin) if echo $COMMAND | grep -qE rm[[:space:]]-rf|rm[[:space:]]-r[[:space:]]-f; then echo Blocked: rm -rf is not allowed by project hook 2 exit 2 fi exit 0规则 2拦截rm删除非临时目录。脚本.claude/hooks/block-rm-outside-tmp.sh#!/bin/bash COMMAND$(jq -r .tool_input.command // /dev/stdin) if echo $COMMAND | grep -qE ^rm[[:space:]] ! echo $COMMAND | grep -qE /tmp/|/temp/; then echo Blocked: rm outside /tmp requires manual approval 2 exit 2 fi exit 0规则 3拦截dd if、mkfs、format等破坏性命令。脚本.claude/hooks/block-destructive.sh#!/bin/bash COMMAND$(jq -r .tool_input.command // /dev/stdin) if echo $COMMAND | grep -qE dd[[:space:]]if|mkfs|format[[:space:]][A-Z]:; then echo Blocked: destructive disk command 2 exit 2 fi exit 0规则 4拦截git push --force到主分支。脚本.claude/hooks/block-force-push.sh#!/bin/bash COMMAND$(jq -r .tool_input.command // /dev/stdin) if echo $COMMAND | grep -qE git[[:space:]]push.*--force echo $COMMAND | grep -qE main|master; then echo Blocked: force push to main/master 2 exit 2 fi exit 0这四条对应的PreToolUse配置{ hooks: { PreToolUse: [ { matcher: Bash, hooks: [ { type: command, if: Bash(rm *), command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/block-rm-rf.sh\ }, { type: command, if: Bash(rm *), command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/block-rm-outside-tmp.sh\ }, { type: command, if: Bash(dd *), command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/block-destructive.sh\ }, { type: command, if: Bash(git push *), command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/block-force-push.sh\ } ] } ] } }if字段的作用是减少进程启动次数只有命令以rm、dd、git push开头时才跑脚本其他 Bash 调用直接跳过。3.2 护秘钥规则 5–7规则 5阻止读取.env。脚本.claude/hooks/block-read-env.sh#!/bin/bash FILE_PATH$(jq -r .tool_input.file_path // /dev/stdin) if echo $FILE_PATH | grep -qE \.env$|\.env\.; then echo Blocked: reading .env is not allowed 2 exit 2 fi exit 0规则 6阻止编辑.env、credentials.json、secrets.yaml。脚本.claude/hooks/protect-secrets.sh#!/bin/bash FILE_PATH$(jq -r .tool_input.file_path // /dev/stdin) if echo $FILE_PATH | grep -qE \.env$|credentials|secrets|\.pem$|\.key$; then echo Blocked: sensitive file $FILE_PATH 2 exit 2 fi exit 0规则 7阻止把秘钥写进代码。脚本.claude/hooks/block-secret-in-code.sh#!/bin/bash CONTENT$(jq -r .tool_input.content // .tool_input.new_string // /dev/stdin) if echo $CONTENT | grep -qE sk-[A-Za-z0-9]{20,}|AKIA[0-9A-Z]{16}; then echo Blocked: possible secret detected in code 2 exit 2 fi exit 0对应配置{ hooks: { PreToolUse: [ { matcher: Read, hooks: [ { type: command, command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/block-read-env.sh\ } ] }, { matcher: Edit|Write|MultiEdit, hooks: [ { type: command, command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/protect-secrets.sh\ }, { type: command, command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/block-secret-in-code.sh\ } ] } ] } }注意Read工具的输入字段是file_pathEdit/Write也是file_path但写入内容在content或new_string。脚本里用//做空值兜底避免字段缺失时报错。3.3 强测规则 8–10规则 8Stop时检查本会话是否跑过测试。脚本.claude/hooks/require-tests.sh#!/bin/bash TRANSCRIPT$(jq -r .transcript_path // /dev/stdin) if [ -n $TRANSCRIPT ] [ -f $TRANSCRIPT ]; then if grep -qE (npm test|pytest|cargo test|go test|vitest) $TRANSCRIPT 2/dev/null; then exit 0 fi fi echo Tests have not been run in this session. Please run tests before stopping. 2 exit 2规则 9Stop时检查是否有未提交的调试代码。脚本.claude/hooks/block-debug-leftover.sh#!/bin/bash if git diff --name-only 2/dev/null | xargs grep -lE console\.log\(|debugger;|print\(DEBUG 2/dev/null | grep -q .; then echo Blocked: debug statements left in changed files 2 exit 2 fi exit 0规则 10PostToolUse编辑后自动跑测试异步不阻塞。配置{ hooks: { Stop: [ { hooks: [ { type: command, command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/require-tests.sh\, timeout: 10 }, { type: command, command: bash \$CLAUDE_PROJECT_DIR/.claude/hooks/block-debug-leftover.sh\, timeout: 10 } ] } ], PostToolUse: [ { matcher: Write|Edit, hooks: [ { type: command, command: cd \$CLAUDE_PROJECT_DIR\ npm test 21 | tail -20, async: true, timeout: 120 } ] } ] } }Stop事件退出码 2 的效果是阻止 Claude 停止它看到 stderr 后会继续对话通常会主动去跑测试。async: true让测试在后台跑不阻塞下一次工具调用。4. 验证请求逐条确认拦截生效配置写完必须逐条验证否则你不知道是规则没生效还是模型恰好没触发。启动 Claude Code 时加--debugHook 的输入 JSON、退出码、stdout、stderr 都会打到日志里。claude --debug验证规则 1让 Claude 执行删除帮我删除 /tmp/build 目录rm -rf /tmp/build预期终端出现Blocked: rm -rf is not allowed by project hook验证规则 5让 Claude 读.env读取项目根目录的 .env 文件告诉我里面有哪些变量预期被拦截stderr 显示Blocked: reading .env is not allowed。验证规则 8先不跑测试直接让 Claude 收工这个任务完成了你可以停止了预期 Claude 不会停下而是收到Tests have not been run in this session的提示然后去执行测试命令。验证规则 10让 Claude 改一个文件把 src/utils.js 里的 formatDate 函数加一行注释编辑完成后后台会自动跑npm test你可以在另一个终端看到测试输出。提示/hooks菜单可以查看所有已注册的 Hook按事件分类展示来源User / Project / Local / Plugin。如果某条规则没出现在菜单里先检查settings.json的 JSON 语法。5. 本篇常见错排查Hook 完全不执行没有任何输出。最常见的原因是 matcher 大小写不对。Bash和bash是两个不同的匹配器后者永远不匹配。其次是脚本路径没加引号$CLAUDE_PROJECT_DIR展开后如果含空格路径会断裂。正确写法是bash \$CLAUDE_PROJECT_DIR/.claude/hooks/xxx.sh\。报错JSON validation failed。说明 Hook 脚本的 stdout 混入了非 JSON 文本。检查.bashrc或.bash_profile里有没有echo语句它们会在脚本执行时一起输出。Python 脚本里的print(debug)也要改成print(debug, filesys.stderr)。Windows 上报bash: No such file or directory或jq is not recognized。前者是 Git Bash 不在 PATH 里安装 Git for Windows 时勾选Add to PATH后者是没装 jq用winget install jqlang.jq或scoop install jq。更省事的做法是把脚本改成 Python用json.load(sys.stdin)解析跨平台一致。Hook 超时导致 Claude 卡顿。默认 timeout 是 600 秒对格式化、路径检查这类操作太长。给每条规则设合理值简单判断 5–10 秒格式化 30 秒跑测试 120–300 秒。脚本内部也可以用timeout 20 ruff format限制外部工具执行时间。additionalContext没注入到 Claude 上下文。检查hookEventName是否和实际事件名完全一致PostToolUse不能写成posttooluse。另外 stdout 必须是纯 JSON前面有任何 echo 输出都会导致整个 JSON 被忽略。disableAllHooks不生效。如果 Hook 是通过 managed policy 下发的settings.json里的disableAllHooks管不了它必须在managed-settings.json里设置。排障过程中如果怀疑是 API 通道问题先去 API Keys 页面确认 Key 状态https://taotoken.net/api-keys?utm_sourcetaotoken_aicg_blog_endutm_contentapi-keysutm_campaignrewrite接入细节对照文档https://taotoken.net/doc?utm_sourcetaotoken_aicg_blog_endutm_contentdocutm_campaignrewrite6. 把规则跑通之后10 条规则全部验证通过后你的 Claude Code 就有了一层项目级护栏。接下来可以做的把.claude/settings.json提交到 Git让团队共享这套规则把.claude/settings.local.json加进.gitignore放个人偏好用PostToolUse加自动格式化和审计日志用SessionStart把当前分支和未提交变更注入上下文让 Claude 一上来就知道项目状态。Hook 脚本本身不复杂难的是想清楚哪些操作必须硬拦、哪些可以放行。我的建议是从拦截删文件和护秘钥开始这两类一旦出事代价最大强测规则可以稍后加因为它会改变 Claude 的收工行为需要适应。如果你还在选模型通道模型对话入口可以先试https://taotoken.net/chat?utm_sourcetaotoken_aicg_blog_endutm_contentmodel-chatutm_campaignrewrite长期跑编码和 Agent 任务Coding Plan 更合适https://taotoken.net/coding-plan?utm_sourcetaotoken_aicg_blog_endutm_contentcoding-planutm_campaignrewriteClaude Code 专项接入参考https://taotoken.net/claude-code-anthropic?utm_sourcetaotoken_aicg_blog_endutm_contentclaudecodeutm_campaignrewrite
返回列表