Lesson 1 / 6

Hooks 入门

你的第一个自动化钩子

Mission 关联:作为一个进阶用户,你已经习惯了"手动发指令"的工作模式。Hooks 是你迈向"自动化"的第一步——让 Claude Code 在特定事件发生时自动执行你定义的操作,不再需要每次手动提醒。

什么是 Hooks?

Hooks 是 Claude Code 的生命周期事件触发器。它们让你在特定事件发生时自动执行脚本、HTTP 请求、或者注入提示词。 你可以把它们想象成"编程中的事件监听器"——当某件事发生时,自动触发一个函数。

CLAUDE.md 中的文字说明不同(那是建议性的,Claude 可能不遵循),Hook 是强制性的——每次事件匹配都会执行,没有商量的余地。

💡 核心概念 Hook = 事件 + 匹配规则 + 动作。 当事件发生时,如果满足匹配规则,就执行动作

Hook 的三种核心元素

一个 Hook 配置由三层组成:

层级 说明 示例
事件 (Event) 触发的时机 PreToolUse(使用工具前)
PostToolUse(使用工具后)
匹配器 (Matcher) 过滤条件 "Edit|Write"(仅在编辑文件时)
动作 (Action) 执行的命令 shell 命令HTTP 请求prompt

五种 Hook 类型

类型 用途 推荐场景
command 执行 shell 命令 运行 linter、发送通知、文件检查
http 发送 HTTP POST 通知外部系统、触发 CI
prompt 注入提示词给 Claude 在特定事件前注入额外指令
agent 启动子 agent 复杂验证、多步骤检查
mcp_tool 调用 MCP 工具 从 MCP 服务获取数据做检查

配置结构

Hooks 写在 settings.json 中。你可以在以下位置配置:

位置 作用域 特点
~/.claude/settings.json 全局(所有项目) 个人配置,不共享
.claude/settings.json 项目级别 可提交到仓库,团队共享
.claude/settings.local.json 项目级别 被 gitignore,个人覆盖

基本格式如下:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "node scripts/lint-check.mjs",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

退出代码的意义

对于 command 类型的 hook,退出代码决定了行为:

退出码 含义 行为
0 成功 继续执行 —— 通过
1 非阻塞错误 Claude 看到报错但继续执行
2 阻塞/拒绝 阻止操作,stderr 反馈给 Claude

常见 Hook 事件速查

事件名 触发时机 匹配对象
PreToolUse 使用工具之前 工具名 (Bash, Edit, Write...)
PostToolUse 工具执行完后 工具名
Notification Claude 需要输入 通知类型
UserPromptSubmit 你提交提示词 始终触发
SessionStart 会话开始 启动方式
Stop Claude 停止响应 始终触发
PostToolUseFailure 工具失败后 工具名

动手练习:创建你的第一个 Hook

🎯 练习:写一个"文件编辑后自动格式化"的 Hook

场景:每次 Claude 用 EditWrite 工具修改了文件后,自动运行 Prettier 格式化。

步骤:

  1. 找到 settings 文件位置

    在你的项目根目录(或全局 ~/.claude/)找到 settings.json。如果不存在,创建一个 .claude/settings.json

  2. 写入 Hook 配置
    {
      "hooks": {
        "PostToolUse": [
          {
            "matcher": "Edit|Write",
            "hooks": [
              {
                "type": "command",
                "command": "echo '文件已编辑,可以在这里运行格式化命令'",
                "timeout": 10
              }
            ]
          }
        ]
      }
    }
  3. 换成真实的格式化命令

    command 换成项目中实际用的格式化工具,比如:

    "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"

    注:jq '.tool_input.file_path' 是从 hook 接收的 stdin JSON 中提取被编辑的文件路径。

  4. 测试!

    在同一个项目中启动 Claude Code,让它帮你编辑一个文件。观察终端输出,看看你的 hook 是否被触发。

更进一步:做个"破坏性操作守卫"

这可能是最有用的 hook 之一 —— 在 PreToolUse 阶段拦截危险的 Bash 命令:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "node -e \"
  const input = require('fs').readFileSync('/dev/stdin','utf8');
  const cmd = JSON.parse(input).tool_input?.command || '';
  const blocked = ['rm -rf', 'git push --force', 'DROP TABLE'];
  if (blocked.some(b => cmd.includes(b))) {
    console.error('🚫 检测到高危命令已阻止:', cmd);
    process.exit(2);
  }
\"",
            "timeout": 5
          }
        ]
      }
    ]
  }
}

这个 hook 会在每次 Bash 工具执行前检查命令内容,如果包含 rm -rfgit push --force 等危险操作,就返回退出码 2 阻止执行。

课后推荐阅读

📌 别忘了

有任何不清楚的地方,随时问我!Hook 的 matcher 语法、事件选择、或者调试技巧,都可以继续深聊。