跳转到正文
莫尔索随笔
返回

Claude Code 最被低估的功能:Hooks 事件系统

预计 9 分钟

第一时间捕获有价值的信号

本文译自 Claude Code’s Most Underrated Feature: Hooks。Hooks 是 Claude Code 最被低估的功能,它让你能在关键节点拦截并控制 Claude 的行为——自动格式化文件、运行测试、发送通知、拦截危险命令。作者开源了一套实用的 Hooks 集合,本文是从零开始的实战指南。

Hooks 是事件驱动的触发器,能在特定节点拦截 Claude Code——在它写入文件之前、运行命令之后、需要你的输入时。它们让你能控制 Claude 的行为,而不只是被动反应。

然而大多数工程师都直接跳过了它们。一旦你开始使用 Hooks,你会奇怪以前没有它们是怎么工作的。

想象一下,Claude Code 能自动格式化它接触的每个文件、每次编辑后运行测试、需要输入时立即在 Slack 上通知你、在危险命令执行前拦截它们、在 risky 操作前创建 git 检查点、跨会话记住上下文、通过拒绝在测试存在前写代码来强制 TDD。这不是愿望清单——这就是 Hooks 让你能构建的东西。

文档是存在的,但实用示例很少。这篇文章是我希望自己开始时能有的指南——从理解事件系统到构建真正有用的 Hooks。

TL;DR

我在 claude-code-hooks 开源了一个不断增长的 Hooks 集合。经过测试的 Hooks 加上一个路线图,让你的 Claude Code 工作流如虎添翼。

实际效果

这就是 Hooks 在实践中的样子——在危险操作执行前拦截它们:

  1. 在调试时保护机密 Hook 拦截 .env 读取
  2. 拦截危险的 bash 命令 Hook 拦截危险命令

Hooks 到底是什么?

把 Claude Code 想象成一个循环:它思考,它行动(读取文件、编写代码、运行命令),然后重复。Hooks 在特定节点拦截这个循环——在每个动作之前、期间或之后。

Claude Code (触发事件)

你的代码在这里运行

你的 Hook (任何语言)
验证 · 修改 · 通知 · 行动

Claude Code (继续)

JSON 输入(stdin)→ 你的逻辑 → JSON 输出(stdout)

你的 Hook 通过 stdin 接收 JSON(会话信息、工具名称、输入),执行任何你想要的逻辑,然后返回一个决策。阻止操作。允许它。修改它。添加上下文。发送通知。这是你的代码,你的规则。

13 个 Hook 事件,比大多数人意识到的要多得多:

  • SessionStart — 会话开始或恢复时(例如,加载上下文、设置环境变量)
  • SessionEnd — 会话终止时(例如,清理、保存状态)
  • UserPromptSubmit — 用户提交提示时(例如,验证输入、添加上下文)
  • PreToolUse — 工具执行前(例如,阻止危险命令、自动批准)
  • PostToolUse — 工具成功后(例如,自动暂存文件、运行格式化器)
  • PostToolUseFailure — 工具失败后(例如,处理错误、清理)
  • PermissionRequest — 权限对话框出现时(例如,自动允许/拒绝)
  • SubagentStart — 生成子代理时(例如,跟踪启动、强制执行限制)
  • SubagentStop — 子代理完成时(例如,评估完成、合并结果)
  • Stop — Claude Code 完成响应时(例如,决定是否应该继续)
  • PreCompact — 上下文压缩前(例如,保留关键信息)
  • Setup — 使用 --init--init-only--maintenance 时(例如,一次性设置)
  • Notification — Claude Code 发送通知时(例如,自定义 Slack 警报)

这是它们在生命周期中的位置(来自 官方文档):

Claude Code Hooks 生命周期图

Hook 生命周期图。来源:Anthropic

每个 Hook 通过 stdin 接收 JSON 形式的上下文:会话 ID、工具名称、输入、输出。你可以用任何语言编写 Hooks。

挑战?理解每个事件类型实际可用的数据需要一些挖掘。所以让我们简化一下。

步骤 1:查看发生了什么(事件记录器)

在你能构建有用的 Hooks 之前,你需要理解 Claude Code 为每个事件发送什么数据。学习的最好方法是记录一切。

我专门为此构建了一个 event-logger。它将每个 Hook 事件捕获到带有时间戳、事件类型和完整有效载荷的每日日志文件中。将其添加到你的 .claude/settings.json

{
  "hooks": {
    "PreToolUse": [{
      "matcher": "*",
      "hooks": [{ "type": "command", "command": "python ~/.claude/hooks/event-logger.py" }]
    }],
    "PostToolUse": [{
      "matcher": "*",
      "hooks": [{ "type": "command", "command": "python ~/.claude/hooks/event-logger.py" }]
    }]
  }
}

这个示例显示了两个事件,但你可以将记录器添加到 13 个事件中的任何一个来查看它们的有效载荷。

现在正常使用 Claude Code。然后查看你的日志:

# 查看今天的日志
cat ~/.claude/hooks-logs/$(date +%Y-%m-%d).jsonl | jq

# 按事件类型过滤
cat ~/.claude/hooks-logs/*.jsonl | jq 'select(.hook_event_name=="PreToolUse")'

你会看到流经每个事件的确切数据:工具名称、文件路径、命令输入,一切。这就是你学习什么是可能的方法。

步骤 2:Python vs Node 用于 Hooks

你可以用任何语言编写 Hooks,但有一个重要的考虑因素:启动时间

Hooks 是同步运行的。Claude Code 会等待它们完成后再继续。如果你的 Hook 需要 500ms 才能启动,每次工具调用都会变慢 500ms。这累积得很快——PreToolUse 会在每次工具调用时触发,每个会话可能有几十次。

现实是这样的:

  • Node.js:快速启动(约 50-100ms)。最适合 PreToolUse、PostToolUse 等频繁事件。
  • Python:启动较慢(约 200-400ms)。适合不频繁的事件如 SessionStart,或用于调试。
  • Bash:启动最快(约 10-20ms)。完美适合简单操作。

我的建议:对高频 Hook 使用 Node.js(PreToolUse 或 PostToolUse 上的任何东西)。使用 Python 进行探索、调试或每个会话只触发一次的 Hooks。

我的仓库中的生产 Hooks 就是出于这个原因用 Node.js 编写的。事件记录器用 Python 编写,因为它只用于调试且运行不频繁。

步骤 3:从这里开始(高价值 Hooks)

这些是我推荐从它们开始的 Hooks——立竿见影的价值,最小的设置。它们在我的 hooks 仓库 中经过测试和记录。一旦你看到它们如何工作,你就会有几十个更多的想法。

1. 拦截危险命令

这是我在每个项目上运行的 Hook。它在 Claude Code 能执行灾难性的 bash 命令之前拦截它们:

  • rm -rf ~rm -rf /
  • 分叉炸弹
  • curl | sh(将 URL 管道到 shell)
  • 强制推送到 main/master
  • git reset --hard
  • chmod 777

这个 Hook 有三个你可以配置的安全级别:

  • critical:只有灾难性的(rm -rf ~、分叉炸弹)
  • high:+ 高风险的(强制推送 main、机密暴露)。推荐。
  • strict:+ 警告性的(任何强制推送、sudo rm)
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "node ~/.claude/hooks/block-dangerous-commands.js"
      }]
    }]
  }
}

当一个危险命令被拦截时,Claude Code 会看到原因并调整。这是一个几乎没有成本的安全网。

2. 保护机密

这个 Hook 防止 Claude Code 读取、修改或泄露敏感文件:

  • .env 文件
  • SSH 密钥(~/.ssh/id_*
  • AWS 凭证(~/.aws/credentials
  • Kubernetes 配置(~/.kube/config
  • 任何匹配常见机密模式的文件

它还会拦截会暴露机密的 bash 命令:

  • cat .env
  • echo $API_KEY
  • curl -d @.env(泄露)
  • printenv(环境转储)
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Read|Edit|Write|Bash",
      "hooks": [{
        "type": "command",
        "command": "node ~/.claude/hooks/protect-secrets.js"
      }]
    }]
  }
}

3. 自动暂存更改

每次 Claude Code 编辑或创建文件时,这个 Hook 会自动对其运行 git add

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "node ~/.claude/hooks/auto-stage.js"
      }]
    }]
  }
}

好处:

  • git status 准确显示 Claude Code 修改了什么
  • 提交前易于审查更改
  • 无需手动暂存

它依赖你的 .gitignore 来排除敏感文件,所以确保设置正确。

4. Slack 通知

当 Claude Code 需要输入时(权限提示、空闲提示),获取 Slack 通知,这样你就不会让它挂起:

{
  "hooks": {
    "Notification": [{
      "matcher": "permission_prompt|idle_prompt",
      "hooks": [{
        "type": "command",
        "command": "node ~/.claude/hooks/notify-permission.js"
      }]
    }]
  }
}

理解数据流

Hooks 以两种方式接收数据:

Shell 环境变量

  • CLAUDE_PROJECT_DIR:项目根目录的绝对路径
  • CLAUDE_CODE_REMOTE:如果在 Web 上运行则为 “true”

Stdin JSON 有效载荷

真正的数据通过 stdin 传入。对于 PreToolUse

{
  "session_id": "abc123",
  "cwd": "/path/to/project",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf ~/Documents"
  },
  "tool_use_id": "xyz789"
}

你的 Hook 读取这个,决定做什么,然后输出 JSON 来控制 Claude Code 的行为:

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "🚨 [rm-home] rm targeting home directory"
  }
}

permissionDecision 可以是 "allow"(绕过权限,执行)、"deny"(阻止,向 Claude 显示原因)或 "ask"(向用户显示权限对话框)。

你还能构建什么?

我分享的 Hooks 只是起点。有了 13 个 Hook 事件,这是可能的:

  • TDD 守卫:阻止实现代码,除非测试存在且先失败。强制 red-green-refactor。
  • 分支保护:防止在 main/master 上进行代码更改。强制功能分支工作流。
  • 上下文保留:在压缩发生前保存关键决策和状态。从你离开的地方继续。
  • 自动检查点:在 risky 操作前创建 git 提交。一键回滚。
  • 会话记忆:跨会话持久化学习。Claude Code 记得上次什么有效。
  • 成本跟踪:实时监控 Token 使用。在每日预算的 80% 时发出警报。超时时暂停。
  • 质量门禁:每次编辑后运行测试和 linting。变绿前阻止完成。
  • 规则注入:在每个提示前添加 CLAUDE.md 规则。对抗规则遗忘。
  • JIRA/Linear 集成:相关文件更改时自动更新工单。PR 合并时关闭。
  • 多代理编排:跟踪子代理生成,强制执行时间限制,合并结果。

我的仓库中的 README 还有 12 个想法。潜力只受限于你能脚本化的东西。

来自实战的技巧

  • 保持 Hooks 快速。 任何超过 100ms 的都会增加明显的延迟。使用 Node.js。
  • 先记录一切。 使用事件记录器来理解可用的数据。
  • 从安全 Hooks 开始。 block-dangerous-commands + protect-secrets 几乎没有成本,但能防止灾难。
  • 测试你的匹配器。 匹配器区分大小写。简单字符串完全匹配;像 Edit|Write 这样的正则表达式模式也有效。
  • 了解你的配置文件。 ~/.claude/settings.json 用于全局 Hooks,.claude/settings.json 用于项目,.claude/settings.local.json 用于本地覆盖(gitignore)。
  • 退出码很重要。 退出 0 = 成功(JSON 已解析,stdout 在详细模式下通过 Ctrl+O 显示)。退出 2 = 阻塞错误(stderr 发送到 Claude,JSON 被忽略)。其他码 = 非阻塞警告。

获取 Hooks

我描述的一切都是开源的并可以立即使用:

claude-code-hooks

仓库包含用于安全、自动化和通知的即用型 Hooks。加上一个想法路线图:自动格式化、分支守卫、成本跟踪、Discord/ntfy 通知、会话摘要、上下文注入、速率限制等等。

给它加星、复刻,并贡献你自己的 Hooks。这只是开始。


Hooks 是那些看似微不足道但随着时间复利的功能之一。每个被拦截的危险命令都避免了潜在的灾难。每个受保护的机密都防止了泄露。每个自动暂存的文件都节省了手动步骤。

从事件记录器开始。看看什么是可能的。然后添加安全 Hooks。它们是最高价值、最低 effort 的胜利。你会奇怪你为什么等待。

还有更多内容要涵盖——基于提示的 Hooks、上下文注入、会话记忆和高级模式。我会在未来的文章中深入探讨这些。