0
· CLAUDE-CODE-FEATURE-RADAR · 2026.05.13 · 18 MIN ·

Hooks:在 Claude Code 生命周期上挂触发器

22 个事件 + 5 种 handler 类型 —— Claude Code 里唯一确定性的扩展机制,把 Claude 从建议者变成被守门员看着的执行者。 · by 思扬
AI · HERO seed:3120260513 22 个事件 + 5 种 handler 类型 —— Claude Code 里唯一确定性的扩展机制,把 Claude 从建议者变成被守门员看着的执行者。
FIG.00 — cover · ai-generated · placeholder

原文:https://code.claude.com/docs/en/hooks 官方定位:User-defined shell commands, HTTP endpoints, LLM prompts, or MCP tools that execute automatically at specific points in Claude Code’s lifecycle.

🔥 影响力卡片

维度数据
事件总数22 个 + lifecycle 事件类(本机文档枚举:SessionStart, SessionEnd, UserPromptSubmit, UserPromptExpansion, PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch, PermissionRequest, PermissionDenied, Stop, StopFailure, SubagentStart, SubagentStop, TaskCreated, TaskCompleted, Notification, CwdChanged, FileChanged, WorktreeCreate, WorktreeRemove, PreCompact, PostCompact, InstructionsLoaded, ConfigChange, Elicitation, ElicitationResult, Setup)
5 种 handler 类型commandhttpmcp_toolprompt(LLM yes/no)、agent(subagent verify)
v2.1.139 新增args: [] exec form(免 shell 直接 spawn)+ continueOnBlock(PostToolUse 拒绝原因喂回 Claude 继续 turn)+ hook 不再带 terminal access(避免污染 UI)
安全配置allowManagedHooksOnly 让管理员限制 hook 来源;disableAllHooks 临时全关
已有第三方 hook 生态superpowers plugin 里有 hookify 等 plugin 已经在做”hook 配置即代码”

🎯 为什么必读

1. 这是 Claude Code 里唯一”确定性”的扩展机制。

Skill 是 LLM 决定要不要调,subagent 是 LLM 决定要不要派,plugin 是 LLM 决定要不要用。Hook 不。Hook 是事件触发的确定性程序:你写了 PreToolUse Bash hook,就保证每个 Bash 调用前都跑你的脚本 — 无视 Claude 的判断。

2. 它是把 Claude 从”建议者”变成”被守门员看着的执行者”的关键。

  • PreToolUse 可以阻止危险操作(rm -rf 之前 hook 看一眼)
  • PostToolUse 可以事后审计(写文件后跑 lint)
  • Stop 可以强制继续(Claude 想停,hook 说”还没完”)
  • UserPromptSubmit 可以改写你的 prompt 或追加 context

3. v2.1.139 这一波改进让 hook 真正可用

  • args: [] exec form → 路径里有空格也不会被 shell 拆;${CLAUDE_PROJECT_DIR}/hooks/check.sh 不再用引号
  • continueOnBlock → PostToolUse 拒绝后,Claude 不再”卡死”,拒绝原因变成 context 让 Claude 自己改
  • hook 不再有 terminal access → 避免 hook 写 stdout 污染 UI

一句话总结

Hook = settings.json 里的一段 JSON,把”在某事件发生时跑某命令”做成可声明的;支持 5 种 handler(shell / HTTP / MCP / LLM prompt / subagent);可决定性地 allow / deny / 改写工具调用。

💎 金句墙

“Exit code 2 blocks; exit code 1 is non-blocking. JSON output on exit 0 enables structured control.” “退出码 2 阻止,退出码 1 不阻止。退出 0 时输出 JSON 能做结构化决策。” —— 🟢 整个 hook 协议的硬约束 — 必背

“Exec form (args: []) recommended for path placeholders to avoid shell injection.” (v2.1.139) “exec 形式(args: [])推荐用于带路径变量的场景,避免 shell 注入。” —— 🟢 安全性建议升级 — 老格式还能用但新写法应该用 args:[]

“Hooks now run without terminal access” (v2.1.139 changelog) “Hook 现在跑时不带 terminal 访问权。” —— 🟢 修了一个隐蔽 bug:之前 hook 写 stdout 可能污染交互式 prompt 显示;现在彻底隔离

📋 核心精读

1. Hook 在 5 个位置可定义(优先级 enterprise > 其他)

位置路径范围
Managed policyenterprise IT 推整组织(可强制)
用户全局~/.claude/settings.json你所有项目
项目共享.claude/settings.json项目所有人(进 git)
项目本地.claude/settings.local.json仅本机(gitignored)
Plugin<plugin>/hooks/hooks.jsonplugin 启用时
Skill / Agentfrontmatter hooks: 字段skill/agent active 时

2. 22+ 事件 — 真正高频的只有 6 个

事件何时触发是否能 block实际用途
PreToolUse工具调用✅ deny / ask / 改写守门 — 阻 rm -rf、改写命令
PostToolUse工具调用(成功)✅ + continueOnBlock(v2.1.139)写完文件自动 lint / format
UserPromptSubmit你按回车后✅ block改写 prompt / 注入 context
StopClaude 想结束 turn✅ block强制继续(“还没跑测试”)
SessionStartsession 启动/恢复仅注入 context给 session 注入 git status / branch 等
FileChanged外部改了文件.env、自动重载配置

其他事件多为 logging / side effect 用,不能阻断行为。

3. 第一个 hook — 阻止 rm -rf

.claude/hooks/block-rm.sh:

#!/bin/bash
COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)
if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive rm -rf blocked by hook"
    }
  }'
else
  exit 0
fi

.claude/settings.json:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

执行流程:

  1. Claude 想跑 rm -rf foo/
  2. PreToolUse fire → matcher Bash 命中 → if 匹配 Bash(rm *) 命中
  3. hook 命令跑你的脚本 → 检测到 rm -rf → 输出 JSON 拒绝
  4. Bash 调用被 deny,Claude 看到拒绝原因换方法

关键细节:

  • matcher 在前(粗筛工具名)
  • if 在 hook 内部(细筛命令具体内容)
  • 退出 0 + JSON = 结构化决策;退出 2 = 直接阻
  • ${CLAUDE_PROJECT_DIR} 是项目根的占位符
  • args: [] 触发 exec form(免 shell)

4. 第二个 hook — 写文件后自动 lint(最常用)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npm run lint:fix",
            "continueOnBlock": true
          }
        ]
      }
    ]
  }
}
  • 工具 EditWrite 调用后触发
  • hook 从 stdin 读 JSON,提取 tool_input.file_path
  • xargs 把它喂给 npm run lint:fix
  • 如果 lint 报错(退出 2),continueOnBlock: true 让 Claude 看到错误自己继续修,而不是卡死

5. 第三个 hook — SessionStart 注入 git 状态

.claude/hooks/inject-git.sh:

#!/bin/bash
branch=$(git branch --show-current 2>/dev/null || echo "(no git)")
uncommitted=$(git status --short 2>/dev/null | head -5)

jq -n --arg b "$branch" --arg u "$uncommitted" '{
  hookSpecificOutput: {
    hookEventName: "SessionStart",
    additionalContext: "Branch: \($b)\nUncommitted:\n\($u)"
  }
}'
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "startup|resume",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/inject-git.sh",
            "args": []
          }
        ]
      }
    ]
  }
}

效果:每个 session 启动时,Claude 立刻知道当前 git 状态,不用问你或自己跑 git 命令。

6. 第四个 hook — HTTP webhook(给团队 / 给 Slack 推)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/audit/bash",
            "timeout": 5,
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

适用场景:

  • 内部审计 — 把所有 Bash 调用日志发给团队 webhook
  • 团队 Slack 通知 — “Claude just ran npm publish
  • 监控集成 — Sentry / Datadog 接收 hook 事件

注意:HTTP 200 不算 block;要 block 必须返回 200 + JSON 拒绝。

7. 第五个 hook — LLM prompt(让另一个 Claude 当审查员)

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Is this command safe to run on a production-adjacent dev machine? Command: $ARGUMENTS\n\nReturn JSON: {\"safe\": true|false, \"reason\": \"...\"}",
            "model": "claude-haiku-4-5",
            "timeout": 15
          }
        ]
      }
    ]
  }
}

效果:每个 Bash 调用前,先让 Haiku 审一遍。Haiku 便宜,延迟 1-2 秒,但能拦掉 LLM 主线没看到的危险命令。

8. 第六个 hook — MCP tool(用第三方安全扫描器)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "mcp_tool",
            "server": "aikido-security",
            "tool": "security_scan",
            "input": { "file_path": "${tool_input.file_path}" }
          }
        ]
      }
    ]
  }
}

效果:每次 Claude 写文件,自动调 Aikido security MCP server 扫一遍。这是 plugin marketplace 里第三方安全公司(42crunch / Aikido / Endor Labs)的真实卖点。

9. v2.1.139 引入的 2 个改进 — 关键细节

A. args: [] exec form

旧:

{
  "type": "command",
  "command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/check.js --fix"
}

新(推荐):

{
  "type": "command",
  "command": "node",
  "args": ["${CLAUDE_PLUGIN_ROOT}/scripts/check.js", "--fix"]
}

优点:

  • 路径里有空格不会被错切
  • 不走 shell → 没有 $VAR 注入风险
  • 占位符 ${...} 不需要引号

B. continueOnBlock: true(PostToolUse 专用)

{
  "type": "command",
  "command": "./hooks/lint.sh",
  "continueOnBlock": true
}

效果:hook 退出 2 时,拒绝原因被喂回 Claude,Claude 继续 turn 自己改,而不是 turn 停。对自动修复流程意义巨大

10. 安全模型(必背)

风险缓解
Hook 脚本本身被改.claude/hooks/*.sh 进 git,代码审查
Plugin hook 偷偷加新 deny ruleallowManagedHooksOnly 让管理员强制只允许 managed 来源
hook 写文件污染项目hook 内部要做最小权限,不该改源码
团队级 hook 强制(PreCompact block 等)同上,managed settings
shell injection改用 args: [] exec form

11. /hooks 菜单调试

输入 /hooks 进交互式菜单:

  • 列出所有已注册 hook
  • 按 source 过滤(User / Project / Local / Plugin / Session / Built-in)
  • 看每个 hook 上次执行结果 / 错误

12. 通用输入字段(每个 hook 都收到)

通过 stdin JSON 收到:

{
  "session_id": "...",
  "transcript_path": "...",
  "cwd": "...",
  "permission_mode": "default",
  "effort": { "level": "medium" },
  "hook_event_name": "PreToolUse",
  "agent_id": "...",       // 如果是 subagent 触发
  "agent_type": "..."
}

加上事件特定字段(如 tool_nametool_input)。

${CLAUDE_EFFORT} 环境变量也能直接读 effort 级别 — 给”高 effort 时跑慢测试,低 effort 跳过”留口。

🟢 译者总评

1. Hook 是 “AI 工具进入企业” 的关键之一。

没有 hook,Claude Code 在 enterprise 场景就是个 black box — IT 不知道它会跑啥、写啥。有了 hook + managed settings,IT 能:

  • 强制审计每个 Bash 调用
  • 强制扫描每个 Write
  • 强制限制只允许某些工具(Skill(...)Edit(...) 规则)
  • 强制 SessionStart 注入合规上下文(如 PII handling rules)

没有这套,Anthropic 拿不到 Fortune 500。

2. 对独立开发者,hook 的”边际”价值反而不在守门

你一个人写代码,不需要太多 deny rules。但你可以用 hook 做:

  • SessionStart 注入项目状态(branch、todos、TICK_LOG.md 进度)— 让 Claude 不用问你”现在做到哪了”
  • PostToolUse 自动跑 lint / format / typecheck — 跑 CI 的 Claude 版
  • Stop hook 强制 “未跑测试不许结束” — 防自己懒
  • FileChanged 监 .env / package.json — 一改就提示 reload

3. v2.1.139 把 hook 推到了”日常可用”的阈值

之前 args: [] 没有,路径处理是大坑(WSL 用户尤其);现在干净了。 之前 PostToolUse block 等于 turn 卡死,Claude 看不到错误;现在有 continueOnBlock,hook 拒绝 = 启动 self-fix 循环

如果你过去试过 hook 然后放弃,值得再试一次

4. 一条建议:从 SessionStart + 注入 git 状态 入门

这是最低风险、最直接见效的 hook。配好后:

  • 每次 claude 启动,Claude 立刻知道 branch / uncommitted
  • 不用每天重复说”先 git status 看一下”
  • 一次写好,所有项目共用(~/.claude/settings.json 全局)

写一次,享一年。

🔗 延伸阅读

  • 同系列:05-skills-system.md(skill frontmatter 里也能定义 hook)、04-plugins-marketplace.md(plugin 可以打包 hook)
  • 同系列:07-mcp-ecosystem.md(type: mcp_tool hook 调 MCP server,衔接外部服务)
  • 官方:/en/settings#settings-files — managed settings 结构
  • 官方:/en/permissionsif 字段使用的 permission rule 语法
  • 第三方:anthropics/claude-plugins-officialhookify plugin — 通过 hook 模板化做事

🔗 调研来源(可校验)

  • 主原文:https://code.claude.com/docs/en/hooks
  • changelog v2.1.139:Added hook args: string[] field (exec form) + Added hook continueOnBlock config option for PostToolUse + hook writing to terminal could corrupt an on-screen interactive prompt; hooks now run without terminal access(本机 raw/scan/CHANGELOG.md 第 9-11 行 + 第 39 行)
  • 已知第三方 hook 生态:anthropics/claude-plugins-official marketplace 里 hookify plugin
  • 安全审计场景:42Crunch / Aikido / Endor Labs 几个安全公司 plugin 都内嵌了 hook