@sairahul1: https://x.com/sairahul1/status/2069710540654645550

X AI KOLs Timeline 工具

摘要

一份全面指南,解释Claude Code Hooks,这些是可编程的检查点,在操作前运行以强制执行规则并阻止或允许工具调用,比CLAUDE.md指令提供更可靠的控制。

https://t.co/EQMeGoBoyy
查看原文
查看缓存全文

缓存时间: 2026/06/24 14:26

Claude Code Hooks:最强大却无人使用的功能

CLAUDE.md 告诉 Claude 该如何表现。

Hooks 让 Claude 真正服从。

这就是大多数人忽略的差别。

你可以在 CLAUDE.md 里写“不要修改 prod.env“。

但 Claude 是否遵循,完全取决于它那一刻的判断。

Hooks 绕过了这种判断。

它们是可编程的检查点,运行在 Claude Code 的执行流程中 —— 在动作发生之前,而非之后。

这是完整的指南。

Hooks 解决的问题

Claude Code 很强大。

它能读取文件、编写代码、执行命令、调用 API。

你给它的越多,它就越有用。

但也越危险。

是什么阻止它在凌晨 2 点修改生产配置?

是什么每次都强制执行你的 lint 规则?

是什么记录每一次敏感文件读取,而不依赖 Claude 记住去做?

CLAUDE.md 是指导。

Hooks 是保证。

区别在于:Hooks 运行真实代码。它们的逻辑不依赖于模型是否理解或记住规则。而取决于你提前编写的代码。

Hook 究竟是什么

Hook 不是提示词。

也不是另一种注入上下文的方式。

它是一个可编程的控制机制,运行在 Claude Code 的执行流程内部。

当 Claude Code 即将调用工具、写入文件或执行命令时 —— Hook 在动作发生之前介入并决定:

→ 允许 → 阻止 → 询问人类确认

决定由你提前编写的代码做出。

而非由模型做出。

以下是 settings.json 中最精简的 Hook 配置:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'about to write a file'"
          }
        ]
      }
    ]
  }
}

“hooks” 这个词出现了三次。每次含义不同。

让我一次性拆解这三层。

第一层 —— 事件注册表。

外层的 hooks 对象。每个键是一个事件名称 —— PreToolUse、PostToolUse、Stop。你想在哪个点干预?在此注册该事件。

第二层 —— 匹配规则。

每个事件下:一个 matcher 对象数组。matcher:“Write” 表示这一组只在 Claude 尝试调用 Write 工具时触发。没有 matcher = 匹配所有。

第三层 —— 真正的 Hook。

每个 matcher 内部:hooks 数组。这是真正的逻辑。type: command 运行 shell 脚本。type: http 调用 URL。type: mcp_tool 调用 MCP 工具。

hooks ← 整个系统的入口点 └── PreToolUse ← 事件:在任何工具调用前触发 └── matcher ← 过滤器:只匹配 “Write” 工具 └── hook ← 动作:运行此 shell 命令

Hook 事件系统

Claude Code 有 28 个 Hook 事件。

它们覆盖了 Claude 执行的每个关键点:

→ 会话开始和结束 → 工具调用前后 → 权限请求 → 文件变更 → 子代理任务 → 通知

大多数人搞错的一件事:事件是同级的,不是父子关系。

PreToolUse 和 PermissionRequest 经常接连触发。这让人以为一个导致另一个。

实际上并非如此。它们是完全独立的干预点。每个都有自己的匹配规则,互不干扰地执行。

事件分为两种类型:

主流程事件 —— 沿核心执行路径运行。这些可以阻止 Claude。PreToolUse、PermissionRequest、PostToolUse、Stop。

旁路事件 —— 观察和通知通道。它们在正确时刻触发,但不改变主流程。Notification、ConfigChange。

它们之间的关键区别:

阻塞型: Hook 的结果决定 Claude 下一步做什么。主流程暂停直到 Hook 返回。 → 非阻塞型: Hook 运行,但 Claude 不等待它。适用于日志、推送通知、同步状态。

如何真正阻止 Claude

这是文档埋藏的部分。

有两种方法可以阻塞一个阻塞型事件。它们含义完全不同。

退出码 2 → 系统错误

Claude 认为出了故障。某个工具不可用、资源缺失、环境损坏。它可能试图理解错误,可能尝试变通方案。

#!/bin/bash
# 使用系统错误信号阻止
if [[ "$TOOL_INPUT" == *"prod.env"* ]]; then
    echo "Environment error: target file locked" >&2
    exit 2
fi

退出码 0 + JSON → 策略拒绝

Claude 将其理解为明确业务规则禁止该操作。它不会试图绕过,而是接受决定并调整行为。

#!/bin/bash
# 使用策略决定阻止(对规则而言好得多)
TOOL_INPUT=$(cat)
FILE=$(echo "$TOOL_INPUT" | jq -r '.path // ""')

if [[ "$FILE" == *"prod.env"* ]]; then
    echo '{
        "decision": "deny",
        "reason": "Writing to prod.env is not allowed. Use staging.env instead."
    }'
    exit 0
fi

# 允许其他一切
echo '{"decision": "allow"}'
exit 0

JSON 方式还能做更多:

→ 附上可读的拒绝理由 → 通过 updatedInput 在 Claude 使用前修改工具输入参数 → 通过 “decision”: “ask” 请求人类确认而非自动拒绝

每个人都犯的错误:

退出码 1 什么也不做。在 Unix 惯例中表示失败。但在 Claude Code 的 Hook 系统中,退出码 1 是非阻塞的。Claude 忽略它并继续执行。

只有退出码 2 或退出码 0 + JSON 真正影响流程。

Hooks 存放在哪里

Hooks 不仅仅存在于你的个人 settings.json 中。

它们可以注册在 4 个不同的地方。每个具有不同的作用域和生命周期。

1. 设置级 Hooks —— 常驻 Hooks。

写在用户级、项目级或本地级的 settings.json 中。从 Claude Code 启动到结束一直活跃。任务之间不会清理。

~/.claude/settings.json ← 用户级,你的机器 .claude/settings.json ← 项目级,与团队共享 .claude/settings.local.json ← 本地覆盖,不提交

2. 插件 Hooks —— 随插件加载。

插件捆绑了自己的 CLAUDE.md、Skills 和 Hooks。当 Claude Code 加载插件时,其 Hooks 与主 Hooks 合并并平等运行。没有优先级区别 —— 它们一起参与。

一个硬性限制:插件子代理不能定义 Hooks。 这是故意的。子代理是受限的执行单元。允许它注册 Hooks 将使低权限角色能够修改执行流程控制。这会破坏安全模型。

3. Skill Hooks —— 限定在 Skill 作用域内。

在 Skill 被调用时注册。Skill 完成后自动清理。它们不会污染全局环境。

---
name: planning-with-files
hooks:
  PreToolUse:
    - matcher: "Write|Edit|Bash|Read"
      hooks:
        - type: command
          command: "cat task_plan.md 2>/dev/null | head -30 || true"
  PostToolUse:
    - matcher: "Write|Edit"
      hooks:
        - type: command
          command: "echo 'File updated. Update task_plan.md status.'"
  Stop:
    - hooks:
        - type: command
          command: "./scripts/final-check.sh"
---

每次此 Skill 调用 Write、Edit 或 Bash 时,它首先打印任务计划的前 30 行。每次文件写入后,它提醒 Claude 更新计划状态。Skill 结束时,运行最终检查。

4. 子代理 Hooks —— 限定在子代理作用域内。

与 Skill Hooks 相同。临时、自动清理。一个额外行为:如果在子代理的前置元数据中注册 Stop Hook,它会在运行时自动转换为 SubagentStop。因为结束的是子代理,而不是整个会话。

多个 Hooks 如何合并

在任何时刻,来自多个层的 Hooks 可能同时活跃。

当一个 Write 操作触发 PreToolUse 时,它可能匹配来自用户设置、项目配置和当前活跃 Skill 的 Hooks —— 同时发生。

有三条规则支配会发生什么。

规则 1:并行执行。

所有匹配的 Hooks 同时运行。不是串行,也不是按优先级顺序。

你的日志 Hook 和安全检查 Hook 同时启动并独立完成。Claude 等待所有 Hook 返回后才做决定。

规则 2:自动去重。

如果两个层注册了完全相同的 Hook —— 相同事件、相同匹配器、相同命令字符串 —— Claude Code 只保留一份副本并运行一次。

这在实践中很重要:你不必担心同一个脚本出现在多个配置文件中会运行两次。但反过来:如果你想让同一个脚本在两个不同条件下分别运行,确保它们的命令字符串不同。

规则 3:最严格的结果胜出。

当多个 Hooks 返回不同决定时,Claude 选择最严格的那个。

deny > ask > allow

一个 deny 就足够了。不管它来自哪个层。

用户级 Hook: allow (普通写入没问题) 项目级 Hook: ask (.env 文件需要确认) 插件 Hook: deny (prod.env 被禁止) ───────────────────────────── 最终决定: deny (一个否决就够了)

为什么这种设计有意义:在安全系统中,允许需要所有人同意。拒绝只需要一个否决。这是每个严肃安全模型的工作方式。

两个值得研究的真实 Hooks

理论很容易。让我们看看两个生产环境中的 Hooks,理解它们为什么被设计成那样。

案例 1:Superpowers 插件 —— 在会话启动时注入上下文

superpowers 插件提供了一套完整的 Skill 系统用于工程纪律:需求澄清、计划、测试驱动开发、代码审查。

但它只注册了一个 Hook。

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|clear|compact",
        "hooks": [
          {
            "type": "command",
            "command": "\"${CLAUDE_PLUGIN_ROOT}/hooks/run-hook.cmd\" session-start"
          }
        ]
      }
    ]
  }
}

一个事件。一个 Hook。它做什么?

它将 Superpowers skill 指令注入到每个会话中作为附加上下文。每次会话启动时,Claude 自动获得正确的初始上下文。

洞察:Hooks 不一定总是要阻止或批准。有时在正确时刻带来正确的信息就是全部工作。

案例 2:claude-code-warp 插件 —— 将 Claude 的生命周期桥接到终端

Warp 是一个终端。当你在 Warp 中使用 Claude Code 时,Warp 不知道 Claude 在做什么 —— 工作、等待、请求权限、完成。

claude-code-warp 插件通过 6 个 Hooks 解决了这个问题:

SessionStart → 向 Warp 发送初始化信息 UserPromptSubmit → 告诉 Warp:Claude 开始工作 PostToolUse → 告诉 Warp:阻塞状态已清除 Notification → 当 Claude 空闲时触发 Warp 通知 PermissionRequest → 向 Warp 发送工具名称 + 输入预览 Stop → 读取会话,提取摘要,发送完成通知

Stop Hook 是最有趣的。

它不仅仅说“完成“。它读取当前会话内容,提取最后一条用户提示和 Claude 的响应,截断到适合通知的长度,并向 Warp 的通知中心发送结构化摘要。

这一个 Hook 将 Claude Code 的内部会话记录变成了用户真正可读的完成通知。

洞察:Hooks 可以作为事件桥接 —— 将 Claude Code 的执行状态同步到本质上对 Claude 没有感知的外部系统。

一个你今天就能用的实用 Hook

保护你的生产环境文件。一个脚本。复制粘贴即可。

创建 .claude/hooks/protect-prod.sh:

#!/bin/bash

# 从标准输入读取工具输入
TOOL_INPUT=$(cat)

# 从输入 JSON 中提取文件路径
FILE_PATH=$(echo "$TOOL_INPUT" | jq -r '.path // .file_path // ""')

# 检查目标是否为生产配置文件
PROTECTED_PATTERNS=("prod.env" ".env.production" "prod-secrets" "production.yaml")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
    if [[ "$FILE_PATH" == *"$pattern"* ]]; then
        echo '{
            "decision": "deny",
            "reason": "'"$FILE_PATH"' is a protected production file. Use staging environment instead. If you genuinely need to edit production config, do it manually."
        }'
        exit 0
    fi
done

# 不是受保护的文件 — 允许
echo '{"decision": "allow"}'
exit 0

在 .claude/settings.json 中注册:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/protect-prod.sh"
          }
        ]
      }
    ]
  }
}

使其可执行:

chmod +x .claude/hooks/protect-prod.sh

现在每个针对生产配置的 Write 和 Edit 都会被阻止 —— 并附上明确理由 —— 在 Claude 触及文件之前。不是因为 Claude 记住了规则,而是因为代码强制执行。

让 Hooks 变得直观的心智模型

把 Claude Code 的执行流程想象成一条管道。

没有 Hooks:Claude 运行整条管道。CLAUDE.md 引导它。Skills 组织它。但执行本身是不间断的。

有 Hooks:你在管道中的任意点插入检查点。每个检查点运行真实代码。它可以检查 Claude 即将做什么,决定是否允许,甚至可以在 Claude 使用前修改输入。

这个系统有三层协同工作:

CLAUDE.md —— 告诉 Claude 如何理解项目 → Skills —— 将 Claude 组织成可靠的工作流 → Hooks —— 在关键执行点守卫边界

没有任何一层能替代其他层。

没有 Hooks 的 CLAUDE.md:良好的指导,不一致的强制。 没有 CLAUDE.md 的 Hooks:对模型不理解的规则的严格强制。 两者结合:每个层面都可靠的行为。

在你开始构建 Hooks 之前有一个警告。

Hooks 运行真实代码。效果是即时的,有时不可逆。

一个具有错误退出码的脚本可能意外中断流程。

一个 Stop Hook 如果退出逻辑处理不当,可能将会话困在循环中。

像测试生产代码一样测试每个 Hook。

处理边缘情况。处理错误路径。显式记录失败。

Hooks 的力量在于它们不能被忽略。

同样的属性使得 Hooks 中的错误代价高昂。

5 件要记住的事

→ 1. Hooks 不是提示词。它们是代码。无论模型处于什么状态都会运行。

→ 2. 退出码 1 什么也不做。系统错误用退出码 2。策略决定用退出码 0 + JSON。

→ 3. 事件是同级的。PreToolUse 和 PermissionRequest 独立触发。一个不会导致另一个。

→ 4. 最严格的结果胜出。来自任何层的一个 deny 就阻止操作。Allow 需要所有人同意。

→ 5. Skill 和子代理 Hooks 是临时的。它们在调用时注册,在完成时清理。它们不会污染全局状态。

如果这有用:

→ 转发与每个使用 Claude Code 构建的开发者分享 → 关注 @sairahul1 获取更多类似深入内容 → 收藏本文

我写关于 AI、产品构建以及无需你参与也能工作的系统的文章。

相似文章

luongnv89/claude-howto

GitHub Trending (daily)

一份全面、结构化的指南,帮助掌握 Claude Code 的功能,包括斜杠命令、钩子、技能、MCP 服务器和子代理,配有可视化教程、复制粘贴模板,以及从新手到高级用户的分阶段学习路径。