用 Hooks 把 Claude Code 管起来

导读: 你有没有遇到过这种情况------Claude 写完代码,你还得手动跑一遍格式化;Claude 执行了 rm -rf,你只能在事后后悔;Claude 跑了一个小时任务,你完全不知道它在干嘛。这些问题,Hooks 全部可以解决。本文是 Claude Code 系列的第四篇,带你彻底搞懂什么是 Hooks,它能做什么,以及真实开发中的五个必备场景。


一个让你恍然大悟的比喻

想象你雇了一个实习生来帮你写代码。

他非常聪明,代码质量不错------但有一个问题:你总需要在他每次提交前亲自检查格式、提醒他跑测试、还要在旁边盯着他别误删重要文件。

如果你能提前给他定一套工作流程:

"每次你改完文件,自动 跑一遍 lint。每次你想删文件,先问我 。每次你完成一个任务,发条消息通知我。"

这就是 Hooks 在 Claude Code 里干的事情。

Hooks 让你不再需要守着 Claude,而是把监督和规则变成系统自动执行的机制


Hooks 是什么

Hooks 是在 Claude Code 运行周期特定节点自动触发的用户自定义脚本。

更技术性地说:当 Claude Code 执行到某个生命周期事件(比如"即将调用 Bash 工具"),系统会暂停,把当前的上下文信息以 JSON 格式通过 stdin 传给你的脚本,脚本执行完后根据退出码和 stdout 的 JSON 输出来决定是否继续、如何继续。

这套机制的本质是在 Claude 的行动链路中,插入确定性的、不依赖 AI 判断的人类定义逻辑。

Prompt/CLAUDE.md 约束 Hooks 约束
执行方式 模型"记住"并遵守 系统强制拦截
可靠性 概率性(模型可能忘记) 确定性(代码必然执行)
适合场景 行为风格、输出格式 安全规则、自动化流程
可调试性 难以追踪 日志清晰,可排查

Hooks 的生命周期

Claude Code 的运行可以被分解为一系列事件节点,Hooks 就挂载在这些节点上:

复制代码
用户输入 → [UserPromptSubmit] → Claude 思考 → 决定用工具
              ↓
         [PreToolUse] → 工具执行 → [PostToolUse] → Claude 继续思考
                                        ↓
                                   [Stop] → 返回结果给用户

按节点分类,共有以下几类:

事件类型 触发时机 能否拦截
SessionStart 会话开始或恢复时 否(只注入上下文)
UserPromptSubmit 用户提交 prompt 之前
PreToolUse 工具调用之前 是(最强拦截点)
PostToolUse 工具调用成功后 否(但可向 Claude 反馈)
Stop Claude 完成一轮回答前 是(可强制继续)
Notification 系统发出通知时
FileChanged 被监视的文件发生变化

最核心的三个节点:PreToolUse (事前拦截)、PostToolUse (事后处理)、Stop(完成检查)。


配置在哪里

Hooks 写在 JSON 配置文件里,有两个位置:

复制代码
~/.claude/settings.json          ← 个人全局配置(不进 git)
.claude/settings.json            ← 项目级配置(可以进 git,团队共享)

两个文件同时生效,不冲突的配置项会合并。

基础结构如下:

json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash ~/.claude/hooks/guard.sh"
          }
        ]
      }
    ],
    "PostToolUse": [...],
    "Stop": [...],
    "Notification": [...]
  }
}

三层嵌套结构:

  1. 事件名PreToolUseStop 等)
  2. matcher(匹配哪些工具,支持正则,省略则匹配全部)
  3. hooks 数组(要执行的脚本列表)

Hook 脚本怎么工作

每个 hook 脚本通过 stdin 收到一个 JSON 对象,包含当前事件的上下文信息。

PreToolUse + Bash 工具为例,脚本收到的 stdin 大概是这样:

json 复制代码
{
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_use_id": "abc123",
  "tool_input": {
    "command": "rm -rf /tmp/build",
    "description": "Clean build artifacts"
  }
}

脚本执行后,通过退出码stdout 的 JSON 来控制 Claude 的行为:

退出码 含义
0 正常,解析 stdout 里的 JSON 指令
2 拦截! stderr 的内容作为原因告知 Claude
其他 非阻断性错误,执行继续

通过 stdout 输出 JSON,还可以显式告诉 Claude 如何决策:

json 复制代码
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "生产数据库写操作需要人工确认"
  }
}

permissionDecision 可以是 allowdenyaskdefer


五个真实场景

场景一:自动格式化,再也不手动跑 Prettier

Claude 写完代码,你总得手动格式化一遍?配上这个,Claude 每次写文件后自动格式化:

json 复制代码
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\"",
            "async": true
          }
        ]
      }
    ]
  }
}

async: true 让格式化在后台跑,不阻塞 Claude 继续工作。$CLAUDE_TOOL_INPUT_FILE_PATH 是系统自动注入的环境变量,指向刚才修改的文件。


场景二:危险命令防火墙,再也不怕 rm -rf

这是最有价值的 Hook 场景之一。在 PreToolUse 挂一个守卫脚本,检测危险命令模式:

.claude/hooks/guard.sh

bash 复制代码
#!/bin/bash

# 从 stdin 读取 JSON
input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // ""')

# 检测危险模式
dangerous_patterns=("rm -rf /" "rm -rf ~" "DROP TABLE" "truncate" "> /dev/sda")

for pattern in "${dangerous_patterns[@]}"; do
  if echo "$command" | grep -qi "$pattern"; then
    echo "检测到高危命令:$pattern" >&2
    exit 2
  fi
done

exit 0

配置

json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash ${CLAUDE_PROJECT_DIR}/.claude/hooks/guard.sh",
            "timeout": 10
          }
        ]
      }
    ]
  }
}

触发时,Claude 会看到 stderr 的原因,并中止操作、告知用户。


场景三:任务完成通知,不用盯着屏幕等

让 Claude 跑复杂任务时,你大可以去做别的事,完成后自动收到通知:

macOS 系统通知

json 复制代码
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude 完成任务了\" with title \"Claude Code\" sound name \"Glass\"'"
          }
        ]
      }
    ]
  }
}

或者通过 Stop Hook 强制检查测试通过后再停止

bash 复制代码
#!/bin/bash
# verify-before-stop.sh

cd $CLAUDE_PROJECT_DIR

# 跑测试
if npm test 2>&1 | grep -q "FAIL"; then
  echo "测试未通过,请先修复再结束" >&2
  exit 2
fi

exit 0
json 复制代码
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash ${CLAUDE_PROJECT_DIR}/.claude/hooks/verify-before-stop.sh"
          }
        ]
      }
    ]
  }
}

当 Claude 想"结束回答"时,系统先跑测试,测试不通过则 Claude 必须继续修复------直到测试绿了才能停。


场景四:全程操作审计日志

在生产相关操作或者合规场景下,你想留下 Claude 执行过哪些命令的完整记录:

json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{ts: now | todate, cmd: .tool_input.command}' >> /tmp/claude-audit.log",
            "async": true
          }
        ]
      }
    ]
  }
}

async: true 确保日志写入在后台进行,不影响速度。日志格式清晰:

复制代码
{"ts":"2026-05-26T10:23:11Z","cmd":"npm run build"}
{"ts":"2026-05-26T10:23:45Z","cmd":"git add ."}
{"ts":"2026-05-26T10:23:46Z","cmd":"git commit -m 'feat: add auth'"}

场景五:代码写完立刻跑 lint,把规范变成强制

告诉 Claude "遵循我们的代码规范"------这是 Prompt 约束,它可能遵守也可能忘记。把 lint 挂进 PostToolUse------这是机制约束,必然执行:

json 复制代码
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "cd ${CLAUDE_PROJECT_DIR} && npx eslint --fix \"$CLAUDE_TOOL_INPUT_FILE_PATH\" 2>&1 | tail -5",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

如果 lint 发现无法自动修复的问题,会通过 stderr 把信息反馈给 Claude,Claude 会看到并主动去修复。


Hook 的四种类型

上面的例子都用了 "type": "command",这是最常见的。Claude Code 还支持另外三种:

类型 作用 适用场景
command 执行本地 shell 命令 绝大多数场景
http 发送 POST 请求到 HTTP 端点 团队共享策略、远程服务验证
prompt 让 Claude 模型判断(返回 yes/no) 语义判断,难以用规则表达的条件
agent 启动子 Agent 做复杂验证 需要读文件、搜索代码来判断的场景

http 类型特别适合团队场景------把安全策略部署在中央服务器,所有成员的 Claude Code 都通过这个端点校验,确保整个团队执行一致的规则:

json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/pre-tool-use",
            "timeout": 10,
            "headers": { "Authorization": "Bearer $TEAM_TOKEN" },
            "allowedEnvVars": ["TEAM_TOKEN"]
          }
        ]
      }
    ]
  }
}

一些实用细节

matcher 支持正则模式,可以精准匹配:

json 复制代码
"matcher": "Bash(git *)"    // 只匹配 git 相关命令
"matcher": "Write|Edit"     // 匹配写文件或编辑
"matcher": "mcp__*"         // 匹配所有 MCP 工具

省略 matcher 则匹配所有工具调用。

路径变量,在 hook 命令里可以直接用:

复制代码
$CLAUDE_PROJECT_DIR         项目根目录
$CLAUDE_TOOL_INPUT_FILE_PATH  刚才操作的文件路径(Write/Edit 有效)

临时关闭所有 Hooks,不想删配置但临时禁用:

json 复制代码
{
  "disableAllHooks": true,
  "hooks": { ... }
}

超时控制,防止 hook 脚本卡住:

json 复制代码
{
  "type": "command",
  "command": "bash slow-check.sh",
  "timeout": 30
}

默认超时 60 秒,UserPromptSubmit 事件默认 30 秒。


配置一个完整的项目 Hooks

把上面几个场景组合起来,一个典型项目的 .claude/settings.json 长这样:

json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash ${CLAUDE_PROJECT_DIR}/.claude/hooks/guard.sh",
            "timeout": 10,
            "statusMessage": "安全检查中..."
          },
          {
            "type": "command",
            "command": "jq -c '{ts: now | todate, cmd: .tool_input.command}' >> /tmp/claude-audit.log",
            "async": true
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\" 2>/dev/null",
            "async": true
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"任务完成\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

这套配置实现了:每次 Bash 命令先过安全检查 + 写审计日志,每次文件修改自动格式化,任务完成发桌面通知。


从 Prompt 约束到机制约束的思维转变

这是 Hooks 背后最重要的工程理念:

告诉 Claude "不要删数据库表" ← Prompt 约束,概率性服从

在 PreToolUse 里加一个拦截脚本 ← 机制约束,确定性执行

前者依赖 Claude 在执行时"记得"这条规则,后者是在系统层面物理拦截------Claude 根本没有机会犯这个错误。

这和 Harness Engineering 的核心思想完全一致(参见本系列第一篇):把"概率性合规"变成"确定性强制"。Claude 越强大、做的事越复杂,你就越需要一套 Hooks 来兜底。

适合用 Prompt 约束的 适合用 Hooks 约束的
输出语言风格 禁止执行的命令
代码注释密度 文件写入后的格式化
回答结构偏好 任务完成后的测试验证
对话态度 操作日志记录

总结

Hooks 是 Claude Code 里最被低估的功能之一。它让你从"每次都要守着 Claude 操作"升级到"把监督规则提前固化进系统"。

四个核心收益:

  1. 安全保障:危险命令物理拦截,不依赖 AI 的自我约束
  2. 自动化流程:格式化、测试、通知,全部免手动
  3. 操作可审计:完整的执行日志,生产环境合规必备
  4. 完成验证:任务没做完就不让 Claude 停,强制质量把关

配置很简单,门槛极低------一个 JSON 文件 + 几行 Shell 脚本就能开始。

从今天起,把你最频繁手动执行的那个操作,写成第一个 Hook。


AI 相关资源

整理了一些关于 AI 学习资料,持续更新中,希望能帮到大家更好地学习 AI:

点击查看 → AI 教程合集

加粗样式

相关推荐
一切皆是因缘际会2 小时前
人工智能价值重构与发展破局
人工智能·百度·ai·重构
GISer_Jing2 小时前
Claude Code Tool System 与 Permission 机制深度解析
ai·系统架构·前端框架·ai编程
运维栈记2 小时前
Remotion + Claude Code:用自然语言创作视频的革命性突破
人工智能·ai·音视频
Jing_jing_X2 小时前
从 Prompt 对话到 OpenClaw:Agent 是怎么一步步发展出来的?
ai·prompt·个人开发·ai应用开发
踏着七彩祥云的小丑2 小时前
AI学习——记忆系统
人工智能·学习·ai
大强同学2 小时前
我用 Claude Code,把 NotebookLM 变成了 Obsidian 插件
人工智能·agent·claude·skill·notebooklm
Likelong~2 小时前
Agent vs Workflow
ai
俊哥V3 小时前
每日 AI 研究简报 · 2026-05-30
人工智能·ai
青山如墨雨如画3 小时前
【Claude】Win11系统VSCode下的Claude使用方法
vscode·aigc·claude·vibe coding·authropic