← 所有文章

Claude Code Hooks:我的95个钩子,每一个都有存在的理由

From the guide: Claude Code Comprehensive Guide

我为Claude Code构建了95个钩子。每一个的存在,都源于某次出错的经历。git-safety-guardian之所以存在,是因为Claude曾强制推送到main分支。recursion-guard之所以存在,是因为某个子代理生成了无限递归的子代理。blog-quality-gate之所以存在,是因为我曾发布过一篇包含7个被动语态句子和一个悬空脚注的文章。1

Claude Code钩子是在特定生命周期节点(会话启动、工具使用前后、提示提交、响应完成)执行的shell命令,为概率性LLM行为之上提供确定性的防护栏。钩子通过stdin接收JSON,通过stdout返回决策(阻止、允许或修改)。它们负责执行诸如阻止强制推送到main、防止递归代理生成、为每个会话注入上下文、把控LLM自身无法保证的输出质量等策略。

TL;DR

Claude Code钩子在AI辅助开发的特定生命周期节点执行shell命令。钩子在概率性系统(LLM)之上提供确定性保障(阻止危险的git命令、注入上下文、强制质量标准)。在我的基础设施中构建了95个钩子之后,我发现最好的钩子来自于真实事件,而非规划。本文介绍架构本身、最关键钩子背后的起源故事,以及9个月钩子开发中总结出的模式。


架构

Claude Code暴露了26个生命周期事件,钩子可以在这些节点拦截、修改或阻止行为(截至v2.1.116,2026年4月)。2我的钩子主要针对其中6个最常用的事件:

会话事件

事件 触发时机 我的钩子
SessionStart 新会话开始 session-start.sh:注入日期、验证venv、初始化递归状态
SessionEnd 会话终止 清理临时文件

工具执行事件

事件 触发时机 我的钩子
PreToolUse 任何工具执行前 git-safety-guardian.shrecursion-guard.shcredentials-check.sh
PostToolUse 工具完成后 post-deliberation.shlog-bash.sh

响应事件

事件 触发时机 我的钩子
UserPromptSubmit 用户发送提示 上下文注入器(日期、分支、模型信息)
Stop Claude完成响应 deliberation-pride-check.shreviewer-stop-gate.sh

每个钩子通过stdin接收JSON,通过stdout进行通信:

{"decision": "block", "reason": "Force push to main is prohibited"}

或者以代码0静默退出允许执行。3


起源故事:最重要的那些钩子

钩子1:git-safety-guardian.sh(PreToolUse:Bash)

事件经过:在一次早期的Claude Code会话中,我让代理”清理一下git历史”。代理执行了git push --force origin main。强制推送覆盖了共享分支上三天的提交。我从本地备份中恢复了,但长达4小时的恢复过程让我确信:概率性判断绝不应掌控破坏性的git操作。

钩子:

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# Only check git commands
echo "$COMMAND" | grep -qE '\bgit\b' || exit 0

# Block force push to main/master
if echo "$COMMAND" | grep -qiE 'git\s+push\s+.*--force.*\b(main|master)\b'; then
    cat << EOF
{"decision": "block", "reason": "Force push to main/master blocked by safety hook"}
EOF
fi

经验教训:钩子并不试图理解意图。它只对命令字符串做模式匹配。简单、确定、无法通过巧妙的提示绕过。代理仍然可以强制推送到功能分支(有时是合理的),但main/master被永久保护。4

累计拦截:9个月内拦截了8次强制推送尝试。

钩子2:recursion-guard.sh(PreToolUse:Task)

事件经过:在构建推敲系统时,我运行了一个会话,它生成了3个探索子代理。每个子代理因缺少生成限制,又各自生成了自己的子代理。递归以10倍于正常速率消耗APItokens。我注意到token消耗加速后手动终止了会话。

钩子:

#!/bin/bash
CONFIG_FILE="${HOME}/.claude/configs/recursion-limits.json"
STATE_FILE="${HOME}/.claude/state/recursion-depth.json"

MAX_DEPTH=2
MAX_CHILDREN=5
DELIB_SPAWN_BUDGET=2
DELIB_MAX_AGENTS=12

# Validate integers safely (((VAR++)) crashes with set -e when VAR=0)
is_positive_int() {
    [[ "$1" =~ ^[0-9]+$ ]] && [[ "$1" -gt 0 ]]
}

关键设计决策:代理从父代理继承生成预算,而非递增深度。一个预算为12的根代理可以将该预算分配到任何树形结构上。基于深度的限制过于僵化(它会阻止完全安全的深但窄的调用链)。5

累计拦截:阻止了23次失控的生成尝试。

钩子3:blog-quality-gate.sh(Stop)

事件经过:我发布过一篇博客,其中有7个被动语态句子、一个在正文中引用但在参考资料部分缺失的脚注,开篇第一句是”was implemented by the team”。文章在编辑器里看起来很精致,却通不过任何人类审稿人都能抓到的基础质量检查。

钩子:对任何修改过的博客内容文件运行我的12模块博客linter。检查被动语态、悬空脚注、缺失的meta描述、未标记语言的代码块、引用完整性。每条发现都具体明确:”第47行:检测到被动语态’was implemented by the team’。建议:’the team implemented’。”

人类反馈的类比:钩子批评作品本身,而不是作者。它说的是”第47行有被动语态”,而不是”你写得不好”。让人类反馈具建设性的原则,同样让自动化反馈变得有用。


95个钩子背后的模式

配置驱动架构

我的钩子从硬编码值演化为配置驱动的行为:

~/.claude/configs/
├── recursion-limits.json     # Depth, spawn budgets, timeouts
├── deliberation-config.json  # Consensus thresholds per task type
├── consensus-profiles.json   # security=85%, docs=50%
├── circuit-breaker.json      # Failure mode configurations
└── file-scope-rules.json     # Path-scoped hook application

把阈值挪到JSON配置中,意味着无需编辑bash脚本就能调优行为。当我需要安全相关的共识阈值设为85%、文档设为50%时,改配置只花了30秒。改代码则需要编辑、测试、重新部署。6

生命周期分层模式

我的95个钩子形成了一张四层安全网:

第一层:预防(PreToolUse):在坏事发生前阻止它。git-safety-guardian、credentials-check、recursion-guard。

第二层:上下文(UserPromptSubmit、SessionStart):注入代理所需的信息。日期、分支、项目上下文、记忆条目、哲学原则。

第三层:验证(PostToolUse):验证已完成的操作是否符合标准。post-deliberation共识检查、输出日志。

第四层:质量(Stop):把控最终输出。pride check、quality gate、reviewer stop gate。这一层实现了元认知监控——代理评估自己推理质量本身,而不仅仅是输出。

每一层都独立运作。如果PreToolUse钩子静默失败,Stop钩子仍会捕捉质量问题。纵深防御,应用于AI代理行为。


配置

钩子定义在.claude/settings.json中:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "~/.claude/hooks/git-safety-guardian.sh",
        "timeout": 5000
      }
    ],
    "PreToolUse:Task": [
      {
        "command": "~/.claude/hooks/recursion-guard.sh"
      }
    ]
  }
}

matcher字段筛选哪些工具会触发钩子。PreToolUse:Task是一种简写,表示只在Task工具调用时触发的匹配器。异步钩子(async: true)在后台运行而不阻塞。7

作用域层级

  1. 用户级~/.claude/settings.json):适用于所有项目。我的95个钩子都在这里。
  2. 项目级.claude/settings.json):添加特定于项目的钩子。
  3. Skill/Subagent frontmatter:作用域为特定组件的生命周期。8

如果重来,我会做什么不同

从3个钩子开始,而不是25个。我的第一个月产出了25个钩子,其中许多注入的上下文是代理本就拥有的。每次工具调用都要加载25个钩子的开销是可测量的。我最终精简到了真正有价值的那些钩子(阻止过真实事件、捕捉过真实质量问题的)。

从第一天起就配置驱动。我花了两周把硬编码阈值重构为JSON配置。如果一开始就用配置,这次重构本是免费的。

尽早建立测试基础设施。前20个钩子没有测试。当我加上测试框架(48个bash集成测试)时,发现3个钩子在边界情况下静默失败。测试本应与钩子#1一同发布。


核心要点

对刚开始使用钩子的开发者: - 从三个钩子起步:git安全(PreToolUse:Bash)、上下文注入(UserPromptSubmit)和质量把关(Stop);只有在出现值得增加的事件时才添加更多 - 运用决策时机框架:钩子架构是不可逆的(95个钩子都依赖于它),因此在编写钩子之前要先在生命周期模型上下功夫

对要统一钩子规范的团队: - 在用户级标准化钩子,让每位团队成员都有相同的安全防护 - 跟踪钩子指标(被阻止的操作、拦截的事件),以证明维护成本的合理性 - 每月审阅钩子日志,发现值得自动化的新模式


FAQ

什么是Claude Code钩子?

钩子是在Claude Code会话的特定生命周期节点执行的shell脚本。它们在特定时刻确定性地触发:工具运行前(PreToolUse)、工具完成后(PostToolUse)、您发送提示时(UserPromptSubmit)、会话开始时(SessionStart)、Claude完成响应时(Stop)。钩子通过stdin接收带有完整上下文(工具名称、输入、会话ID)的JSON,可以阻止操作、注入上下文或强制质量关卡。它们在概率性LLM之上提供确定性保障。

Claude Code可以运行多少个钩子,是否有性能开销?

钩子数量没有硬性上限。我在26个可用生命周期事件中的6个上运行95个钩子(截至v2.1.116,2026年4月),每个事件总开销约200毫秒。实际限制在于延迟:每个钩子都会在工具调用前后增加执行时间。关键优化是使用分发器(每个事件一个,从缓存的stdin中顺序运行钩子),而不是各自独立读取stdin的钩子。在我早期的配置中,独立钩子的并发写入导致了JSON损坏;分发器彻底消除了这种失效模式。

Claude Code钩子会在哪些事件上触发?

截至v2.1.116(2026年4月),Claude Code暴露了26个生命周期事件供钩子使用。我的钩子针对的6个最常用事件是:SessionStart(新会话开始)、SessionEnd(会话终止)、PreToolUse(任何工具执行前)、PostToolUse(工具完成后)、UserPromptSubmit(用户发送提示)、Stop(Claude完成响应)。其他事件还包括SubagentStart、PermissionRequest、PermissionDenied、TaskCreated、CwdChanged、FileChanged、PreCompact等。PreToolUse和PostToolUse还可以通过诸如PreToolUse:BashPreToolUse:Task之类的匹配器进一步限定作用域,仅在特定工具类型上触发。每个事件通过stdin接收带有相关上下文的JSON。

Claude Code钩子可以阻止工具调用吗?

可以。向stdout输出{"decision": "block", "reason": "explanation"}的钩子会阻止工具调用的执行。这是安全钩子的核心机制:我的git-safety-guardian阻止对main的强制推送,credentials-check阻止对.env文件的读取,recursion-guard阻止过度的代理生成。阻止是确定性的,无法通过提示绕过。以代码0静默退出的钩子允许操作继续。

PreToolUse和PostToolUse钩子有什么区别?

PreToolUse在工具执行前触发,可以完全阻止操作。用于安全关卡:阻止危险命令、检查凭据、执行生成预算。PostToolUse在工具完成后触发,可以提供反馈或触发后续动作。用于验证:检查输出质量、记录活动、核实共识。它们共同构成四层安全网的第一层和第三层:预防(PreToolUse)、上下文注入(UserPromptSubmit)、验证(PostToolUse)和质量把关(Stop)。

References


  1. Author’s hook infrastructure. 95 hooks across 6 of the 26 available lifecycle events (v2.1.116, April 2026), developed over 9 months (2025-2026). 

  2. Anthropic, “Claude Code Hooks Reference,” 2026. 26 lifecycle event types as of v2.1.116 (April 2026). 

  3. Anthropic, “Claude Code Documentation,” 2025. Hook input/output JSON format. 

  4. Author’s git-safety-guardian.sh. 8 intercepted force-push attempts tracked in ~/.claude/state/

  5. Author’s recursion-guard.sh. Budget inheritance model documented in ~/.claude/configs/recursion-limits.json

  6. Author’s config-driven architecture. 14 JSON config files encoding all hook thresholds and rules. 

  7. Anthropic, “Claude Code Documentation,” 2025. Hook configuration and async execution. 

  8. Anthropic, “Claude Code Documentation,” 2025. Hook scope hierarchy. 

相关文章

为Claude Code构建自定义技能:完整教程

从零构建代码审查技能。涵盖目录结构、frontmatter字段、基于LLM的匹配、上下文预算和自动激活。

4 分钟阅读

上下文窗口管理:50次会话教会我的AI开发经验

我在50次Claude Code会话中测量了token消耗情况。上下文耗尽会在您察觉之前就开始降低输出质量。以下是解决这一问题的方法。

2 分钟阅读

Claude Code Hooks教程:从零构建5个生产级Hook

从零构建5个实用的Claude Code hooks:自动格式化、安全门控、测试运行器、通知提醒和质量检查。

2 分钟阅读