Claude Code Hooks 实战:6个自动化脚本,省掉每天30分钟重复操作

Pragmatic Engineer 今年3月的调查数据:Claude Code 发布8个月,已经是开发者用得最多的 AI 编程工具,超过了 GitHub Copilot 和 Cursor。

但大部分人只把它当"智能补全"用。其实 Claude Code 有个被低估的功能叫 Hooks——你可以在它的运行周期里插入自定义脚本,让格式化、测试、通知、权限拦截这些事情自动发生,不用每次手动敲命令。

这篇文章给你6个拿来就能用的 Hook 配置,每个都附完整 JSON 和脚本。

Hooks 是什么

一句话:Hooks 是在 Claude Code 特定时机自动执行的 shell 命令。

比如 Claude 改完一个文件后,你想自动跑 prettier 格式化。正常流程是你手动运行 npx prettier --write xxx.js。用 Hook 的话,只要在配置里写一条规则,Claude 每次改完文件就自动格式化,你什么都不用管。

Hooks 支持这些触发时机:

  • PreToolUse:工具调用前(比如写文件前检查是否是受保护文件)
  • PostToolUse:工具调用后(比如写完文件自动格式化)
  • Notification:Claude 等你输入时(发桌面通知)
  • Stop:Claude 完成一轮回答后(跑测试、发通知)
  • SessionStart:会话启动时(加载环境变量)
  • PostCompact:上下文压缩后(重新注入关键信息)

配置写在 ~/.claude/settings.json 里,格式长这样:

{
  "hooks": {
    "事件名": [
      {
        "matcher": "匹配条件",
        "hooks": [
          {
            "type": "command",
            "command": "要执行的命令"
          }
        ]
      }
    ]
  }
}

下面直接上实战。

1. 文件保存后自动格式化

这是用得最多的场景。Claude 改完代码文件,你不想每次手动跑 prettier 或 eslint --fix。

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null || true"
          }
        ]
      }
    ]
  }
}

matcher 设成 Edit|Write,意思是只在 Claude 用 Edit 或 Write 工具改文件时触发。stdin 会收到一段 JSON,里面有 tool_input.file_path 字段,用 jq 提取出来传给 prettier。

末尾加 || true 是因为有些文件 prettier 不认(比如 .md),报错也不影响流程。

实测下来,这个 Hook 每次执行不到 200ms,基本感觉不到延迟。

2. 保护关键文件不被误改

项目里总有些文件不能随便动:.env.productiondocker-compose.yml、CI 配置之类的。可以用 PreToolUse Hook 拦截:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | grep -qE '(\\.env\\.production|docker-compose\\.yml|\\.(github|gitlab)/|Makefile)' && echo '{\"decision\": \"block\", \"reason\": \"受保护文件,请确认后手动修改\"}' || echo '{\"decision\": \"allow\"}'"
          }
        ]
      }
    ]
  }
}

原理很简单:从 stdin 读文件路径,用 grep 匹配保护列表,命中就返回 {"decision": "block"},Claude 会收到拒绝信息,不会执行这次修改。

我在项目里加上这条之后,Claude 再也没有误改过 CI 配置。之前它"好心"帮我改 .github/workflows/deploy.yml,结果把部署搞挂了一次,从那以后就加了这个。

3. 任务完成桌面通知

Claude 有时候跑一个复杂任务要几分钟,你切到别的窗口干活,不知道它什么时候完事。加个通知 Hook:

macOS 版:

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code 任务完成,等你回来\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Linux 版用 notify-send

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude Code 任务完成,等你回来'"
          }
        ]
      }
    ]
  }
}

matcher 设为空字符串表示所有通知都触发。如果你觉得太频繁,可以在 command 里加个过滤逻辑,只在特定类型的通知时弹窗。

4. 每轮任务结束自动跑测试

这个很实用。Claude 改完代码后自动跑一遍测试,如果挂了你马上就知道:

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$(jq -r '.cwd')\" && npm test --silent 2>&1 | tail -20 | jq -Rs '{output_to_claude: .}'"
          }
        ]
      }
    ]
  }
}

Stop 事件在 Claude 完成一轮回答时触发。这里做了三件事:

  1. cd 到项目目录
  2. npm test,取最后20行输出
  3. 把结果通过 output_to_claude 字段反馈给 Claude

如果测试挂了,Claude 会看到失败信息,你可以直接让它修复。整个过程是闭环的——改代码、跑测试、看结果、修bug,全在一个对话里完成。

注意:如果你的测试跑得慢(超过30秒),建议改成只跑受影响文件的测试,不要全量跑。

5. 上下文压缩后自动恢复关键信息

Claude Code 对话长了会压缩上下文,有时候压缩完会"忘记"一些重要信息,比如你在对话开头告诉它的项目约定。PostCompact Hook 可以在压缩后自动注入关键上下文:

{
  "hooks": {
    "PostCompact": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"output_to_claude\": \"提醒:本项目使用 TypeScript strict 模式,所有函数必须有返回类型声明。API 路由在 src/routes/ 下,数据库模型在 src/models/ 下。测试文件和源文件放同一目录,命名用 .test.ts 后缀。\"}'"
          }
        ]
      }
    ]
  }
}

如果规则比较多,可以从文件读取:

{
  "hooks": {
    "PostCompact": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "cat .claude/project-context.txt | jq -Rs '{output_to_claude: .}'"
          }
        ]
      }
    ]
  }
}

把项目约定写在 .claude/project-context.txt 里,压缩后自动重新喂给 Claude。我实际用下来,加了这个 Hook 之后 Claude 在长对话中"跑偏"的频率降了不少。

6. 会话启动时自动加载环境

每次开新会话都要告诉 Claude 项目背景信息?用 SessionStart Hook 自动搞定:

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"output_to_claude\": \"当前 Node 版本:'$(node -v)',npm 版本:'$(npm -v)',Git 分支:'$(git branch --show-current)',最近3条commit:'$(git log --oneline -3 | tr '\\n' '; ')'\"}'"
          }
        ]
      }
    ]
  }
}

这样 Claude 一启动就知道你在哪个分支、用什么版本、最近改了什么。不用你每次手动交代。

完整配置参考

把上面6个 Hook 合在一起,一个完整的 ~/.claude/settings.json 长这样(只展示 hooks 部分):

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null || true"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | grep -qE '(\\.env\\.production|docker-compose\\.yml|\\.(github|gitlab)/)' && echo '{\"decision\": \"block\", \"reason\": \"受保护文件\"}' || echo '{\"decision\": \"allow\"}'"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code 等你回来\" with title \"Claude Code\"'"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$(jq -r '.cwd')\" && npm test --silent 2>&1 | tail -20 | jq -Rs '{output_to_claude: .}'"
          }
        ]
      }
    ],
    "PostCompact": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "cat .claude/project-context.txt | jq -Rs '{output_to_claude: .}'"
          }
        ]
      }
    ],
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"output_to_claude\": \"Node: '$(node -v)', Branch: '$(git branch --show-current)'\"}'"
          }
        ]
      }
    ]
  }
}

踩坑记录

用 Hooks 这几个月踩过一些坑,记下来供参考:

jq 必须装好。 Hooks 的输入输出走 JSON 格式,jq 是刚需。macOS 上 brew install jq,Ubuntu 上 apt install jq。没装 jq 的话 Hook 会静默失败,排查半天找不到原因。

命令超时默认10秒。 如果你的 Hook 执行时间长(比如全量跑测试),可能会被杀掉。长任务建议用异步 Hook 或者只跑增量测试。

stderr 输出会被吞掉。 Hook 的 stderr 不会显示在 Claude 界面里。调试的时候建议把 stderr 重定向到文件:2>/tmp/hook-debug.log

配置改完要重启。 修改 settings.json 后,当前会话不会自动加载新 Hook,需要退出重新进。

路径问题。 Hook 里的相对路径是相对于 Claude Code 启动目录的,不是 settings.json 的位置。跨项目用的 Hook 建议写绝对路径。

总结

Hooks 的思路很简单:把重复的操作自动化,把危险的操作拦住,把重要的信息自动喂给 Claude。

上面6个配置覆盖了最常见的场景。你可以直接复制到自己的 settings.json 里,改改路径和命令就能用。如果你有其他好用的 Hook 配置,评论区见。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐