{/* TRANSLATED — 已翻译为中文 */}

> ## 文档索引
> 获取完整文档索引：https://code.claude.com/docs/llms.txt
> 使用此文件发现所有可用页面后再继续探索。

# 使用钩子自动化工作流

> 当 Claude Code 编辑文件、完成任务或需要输入时自动运行 shell 命令。格式化代码、发送通知、验证命令和强制执行项目规则。

钩子是用户定义的 shell 命令，在 Claude Code 生命周期的特定时间点执行。它们为 Claude Code 的行为提供确定性控制，确保某些操作总是发生，而不是依赖 LLM 选择运行它们。使用钩子强制执行项目规则、自动化重复任务，并将 Claude Code 与现有工具集成。

对于需要判断而非确定性规则的决策，你也可以使用[基于提示词的钩子](#prompt-based-hooks)或[基于代理的钩子](#agent-based-hooks)，使用 Claude 模型评估条件。

有关扩展 Claude Code 的其他方式，请参阅[技能](/en/skills)了解如何给 Claude 额外的指令和可执行命令，[子代理](/en/sub-agents)了解在隔离上下文中运行任务，以及[插件](/en/plugins)了解打包跨项目共享的扩展。

<Tip>
  本指南涵盖常见用例和入门方法。有关完整的事件 schema、JSON 输入/输出格式以及异步钩子和 MCP 工具钩子等高级功能，请参阅[钩子参考](/en/hooks)。
</Tip>

## 设置你的第一个钩子

要创建钩子，将 `hooks` 块添加到[设置文件](#configure-hook-location)。本教程创建一个桌面通知钩子，以便 Claude 等待输入时你会收到提醒，而不用盯着终端。

<Steps>
  <Step title="将钩子添加到设置中">
    打开 `~/.claude/settings.json` 并添加一个 `Notification` 钩子。以下示例使用 macOS 的 `osascript`；Linux 和 Windows 命令请参阅[Claude 需要输入时获取通知](#get-notified-when-claude-needs-input)。

    ```json theme={null}
    {
      "hooks": {
        "Notification": [
          {
            "matcher": "",
            "hooks": [
              {
                "type": "command",
                "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
              }
            ]
          }
        ]
      }
    }
    ```

    如果你的设置文件已有 `hooks` 键，将 `Notification` 作为现有事件键的同级添加，而不是替换整个对象。每个事件名是单个 `hooks` 对象内的键：

    ```json theme={null}
    {
      "hooks": {
        "PostToolUse": [
          {
            "matcher": "Edit|Write",
            "hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
          }
        ],
        "Notification": [
          {
            "matcher": "",
            "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'" }]
          }
        ]
      }
    }
    ```

    你也可以在 CLI 中描述你想要的内容，让 Claude 为你编写钩子。
  </Step>

  <Step title="验证配置">
    输入 `/hooks` 打开钩子浏览器。你会看到所有可用钩子事件的列表，每个有钩子配置的事件旁边有计数。选择 `Notification` 确认你的新钩子出现在列表中。选择钩子会显示其详情：事件、匹配器、类型、源文件和命令。
  </Step>

  <Step title="测试钩子">
    按 `Esc` 返回 CLI。让 Claude 做一些需要权限的事情，然后切换离开终端。你应该会收到桌面通知。
  </Step>
</Steps>

<Tip>
  `/hooks` 菜单是只读的。要添加、修改或删除钩子，请直接编辑设置 JSON 或让 Claude 进行更改。
</Tip>

## 你可以自动化什么

钩子让你在 Claude Code 生命周期的关键点运行代码：编辑后格式化文件、执行前阻止命令、Claude 需要输入时发送通知、会话开始时注入上下文等。完整的钩子事件列表请参阅[钩子参考](/en/hooks#hook-lifecycle)。

每个示例包含一个可直接使用的配置块，你可以添加到[设置文件](#configure-hook-location)。最常见的模式：

* [Claude 需要输入时获取通知](#get-notified-when-claude-needs-input)
* [编辑后自动格式化代码](#auto-format-code-after-edits)
* [阻止对受保护文件的编辑](#block-edits-to-protected-files)
* [压缩后重新注入上下文](#re-inject-context-after-compaction)
* [审计配置更改](#audit-configuration-changes)
* [目录或文件更改时重新加载环境](#reload-environment-when-directory-or-files-change)
* [自动批准特定权限提示](#auto-approve-specific-permission-prompts)

有关运行单独模型审查并将发现反馈到会话中的钩子的生产示例，请参阅 [`security-guidance` 插件如何与 Claude Code 集成](/en/security-guidance#how-the-plugin-integrates-with-claude-code)。

### Claude 需要输入时获取通知

当 Claude 完成工作并需要你的输入时获取桌面通知，这样你可以切换到其他任务而不必检查终端。

此钩子使用 `Notification` 事件，当 Claude 等待输入或权限时触发。下面的每个标签使用平台的原生命令通知命令。将此添加到 `~/.claude/settings.json`：

<Tabs>
  <Tab title="macOS">
    ```json theme={null}
    {
      "hooks": {
        "Notification": [
          {
            "matcher": "",
            "hooks": [
              {
                "type": "command",
                "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
              }
            ]
          }
        ]
      }
    }
    ```

    <Accordion title="如果没有通知出现">
      `osascript` 通过内置的脚本编辑器应用路由通知。如果脚本编辑器没有通知权限，命令会静默失败，macOS 不会提示你授予它。在终端中运行此命令一次使脚本编辑器出现在通知设置中：

      ```bash theme={null}
      osascript -e 'display notification "test"'
      ```

      目前不会显示任何内容。打开**系统设置 > 通知**，在列表中找到**脚本编辑器**，然后打开**允许通知**。再次运行命令确认测试通知出现。
    </Accordion>
  </Tab>

  <Tab title="Linux">
    ```json theme={null}
    {
      "hooks": {
        "Notification": [
          {
            "matcher": "",
            "hooks": [
              {
                "type": "command",
                "command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
              }
            ]
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="Windows (PowerShell)">
    ```json theme={null}
    {
      "hooks": {
        "Notification": [
          {
            "matcher": "",
            "hooks": [
              {
                "type": "command",
                "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')\""
              }
            ]
          }
        ]
      }
    }
    ```
  </Tab>
</Tabs>

空的 `matcher` 在所有通知类型上触发。要只在特定事件上触发，将其设置为以下值之一：

| 匹配器                   | 触发时机                                             |
| :----------------------- | :--------------------------------------------------- |
| `permission_prompt`      | Claude 需要你批准工具使用                             |
| `idle_prompt`            | Claude 完成并等待你的下一个提示词                      |
| `auth_success`           | 认证完成                                             |
| `elicitation_dialog`     | MCP 服务器打开引出表单                                |
| `elicitation_complete`   | MCP 引出表单被提交或关闭                              |
| `elicitation_response`   | MCP 引出响应被发回服务器                              |

输入 `/hooks` 并选择 `Notification` 确认钩子已注册。完整的事件 schema 请参阅 [Notification 参考](/en/hooks#notification)。

### 编辑后自动格式化代码

Claude 编辑的每个文件自动运行 [Prettier](https://prettier.io/)，使格式保持一致，无需手动干预。

此钩子使用带 `Edit|Write` 匹配器的 `PostToolUse` 事件，因此只在文件编辑工具后运行。命令使用 [`jq`](https://jqlang.github.io/jq/) 提取编辑的文件路径并传递给 Prettier。将此添加到项目根目录的 `.claude/settings.json`：

```json theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}
```

<Note>
  本页的 Bash 示例使用 `jq` 进行 JSON 解析。使用 `brew install jq`（macOS）、`apt-get install jq`（Debian/Ubuntu）安装，或参见 [`jq` 下载](https://jqlang.github.io/jq/download/)。
</Note>

### 阻止对受保护文件的编辑

防止 Claude 修改 `.env`、`package-lock.json` 或 `.git/` 中的任何内容等敏感文件。Claude 收到解释编辑被阻止原因的反馈，以便它可以调整方法。

此示例使用钩子调用的单独脚本文件。脚本将目标文件路径与受保护模式列表进行比较，并以退出码 2 阻止编辑。

<Steps>
  <Step title="创建钩子脚本">
    将此保存到 `.claude/hooks/protect-files.sh`：

    ```bash theme={null}
    #!/bin/bash
    # protect-files.sh

    INPUT=$(cat)
    FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

    PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

    for pattern in "${PROTECTED_PATTERNS[@]}"; do
      if [[ "$FILE_PATH" == *"$pattern"* ]]; then
        echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
        exit 2
      fi
    done

    exit 0
    ```
  </Step>

  <Step title="使脚本可执行（macOS/Linux）">
    钩子脚本必须可执行，Claude Code 才能运行它们：

    ```bash theme={null}
    chmod +x .claude/hooks/protect-files.sh
    ```
  </Step>

  <Step title="注册钩子">
    将 `PreToolUse` 钩子添加到 `.claude/settings.json`，在任何 `Edit` 或 `Write` 工具调用之前运行脚本：

    ```json theme={null}
    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "Edit|Write",
            "hooks": [
              {
                "type": "command",
                "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
              }
            ]
          }
        ]
      }
    }
    ```
  </Step>
</Steps>

### 压缩后重新注入上下文

当 Claude 的上下文窗口填满时，压缩会总结对话以释放空间。这可能会丢失重要细节。使用带 `compact` 匹配器的 `SessionStart` 钩子在每次压缩后重新注入关键上下文。

命令写入 stdout 的任何文本都会添加到 Claude 的上下文中。此示例提醒 Claude 项目规范和最近的工作。将此添加到项目根目录的 `.claude/settings.json`：

```json theme={null}
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}
```

你可以将 `echo` 替换为任何产生动态输出的命令，如 `git log --oneline -5` 显示最近的提交。对于每次会话开始时注入上下文，请考虑改用 [CLAUDE.md](/en/memory)。有关环境变量，请参阅参考中的 [`CLAUDE_ENV_FILE`](/en/hooks#persist-environment-variables)。

### 审计配置更改

跟踪会话期间设置或技能文件何时更改。`ConfigChange` 事件在外部进程或编辑器修改配置文件时触发，因此你可以记录更改用于合规性或阻止未授权的修改。

此示例将每次更改追加到审计日志。将此添加到 `~/.claude/settings.json`：

```json theme={null}
{
  "hooks": {
    "ConfigChange": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "jq -c '{timestamp: now | todate, source: .source, file: .file_path}' >> ~/claude-config-audit.log"
          }
        ]
      }
    ]
  }
}
```

匹配器按配置类型过滤：`user_settings`、`project_settings`、`local_settings`、`policy_settings` 或 `skills`。要阻止更改生效，以退出码 2 退出或返回 `{"decision": "block"}`。详见 [ConfigChange 参考](/en/hooks#configchange)了解完整输入 schema。

### 目录或文件更改时重新加载环境

某些项目根据所在目录设置不同的环境变量。[direnv](https://direnv.net/) 等工具在你的 shell 中自动完成此操作，但 Claude 的 Bash 工具不会自行获取这些更改。

将 `SessionStart` 钩子与 `CwdChanged` 钩子配对可以解决此问题。`SessionStart` 为你启动的目录加载变量，`CwdChanged` 在每次 Claude 更改目录时重新加载它们。两者都写入 `CLAUDE_ENV_FILE`，Claude Code 在每次 Bash 命令之前将其作为脚本前导运行。将此添加到 `~/.claude/settings.json`：

```json theme={null}
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ],
    "CwdChanged": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ]
  }
}
```

在每个有 `.envrc` 的目录中运行 `direnv allow` 一次，以便 direnv 被允许加载它。如果你使用 devbox 或 nix 而不是 direnv，相同的模式可以用 `devbox shellenv` 或 `devbox global shellenv` 代替 `direnv export bash`。

要对特定文件而不是每个目录更改做出反应，使用 `FileChanged`，`matcher` 列出要监视的文件名，用 `|` 分隔。要构建监视列表，此值被拆分为字面文件名而不是作为正则表达式评估。详见 [FileChanged](/en/hooks#filechanged) 了解相同的值如何过滤文件更改时运行哪些钩子组。此示例监视工作目录中的 `.envrc` 和 `.env`：

```json theme={null}
{
  "hooks": {
    "FileChanged": [
      {
        "matcher": ".envrc|.env",
        "hooks": [
          {
            "type": "command",
            "command": "direnv export bash > \"$CLAUDE_ENV_FILE\""
          }
        ]
      }
    ]
  }
}
```

详见 [CwdChanged](/en/hooks#cwdchanged) 和 [FileChanged](/en/hooks#filechanged) 参考条目了解输入 schema、`watchPaths` 输出和 `CLAUDE_ENV_FILE` 详情。

### 自动批准特定权限提示

跳过你总是允许的工具调用的批准对话框。此示例自动批准 `ExitPlanMode`，即 Claude 在完成呈现计划并请求继续时调用的工具，这样你就不会在每次计划就绪时被提示。

与上面的退出码示例不同，自动批准需要你的钩子向 stdout 写入 JSON 决策。`PermissionRequest` 钩子在 Claude Code 即将显示权限对话框时触发，返回 `"behavior": "allow"` 代表你回答它。

匹配器将钩子范围限定为仅 `ExitPlanMode`，因此不影响其他提示。将此添加到 `~/.claude/settings.json`：

```json theme={null}
{
  "hooks": {
    "PermissionRequest": [
      {
        "matcher": "ExitPlanMode",
        "hooks": [
          {
            "type": "command",
            "command": "echo '{\"hookSpecificOutput\": {\"hookEventName\": \"PermissionRequest\", \"decision\": {\"behavior\": \"allow\"}}}'"
          }
        ]
      }
    ]
  }
}
```

当钩子批准时，Claude Code 退出计划模式并恢复进入计划模式之前激活的权限模式。记录显示"Allowed by PermissionRequest hook"在对话框原本会出现的位置。钩子路径始终保留当前对话：它不能像对话框那样清除上下文并开始新的实现会话。

要设置特定权限模式，钩子的输出可以包含带有 `setMode` 条目的 `updatedPermissions` 数组。`mode` 值是任何权限模式如 `default`、`acceptEdits` 或 `bypassPermissions`，`destination: "session"` 仅对当前会话应用。

<Note>
  `bypassPermissions` 仅在会话启动时已具有旁路模式的情况下生效：`--dangerously-skip-permissions`、`--permission-mode bypassPermissions`、`--allow-dangerously-skip-permissions` 或设置中的 `permissions.defaultMode: "bypassPermissions"`，且未被 [`permissions.disableBypassPermissionsMode`](/en/permissions#managed-settings) 禁用。它永远不会作为 `defaultMode` 持久化。
</Note>

要将会话切换到 `acceptEdits`，你的钩子将此 JSON 写入 stdout：

```json theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedPermissions": [
        { "type": "setMode", "mode": "acceptEdits", "destination": "session" }
      ]
    }
  }
}
```

保持匹配器尽可能窄。匹配 `.*` 或留空匹配器会自动批准每个权限提示，包括文件写入和 shell 命令。详见 [PermissionRequest 参考](/en/hooks#permissionrequest-decision-control)了解完整的决策字段集。

## 钩子如何工作

钩子事件在 Claude Code 的特定生命周期时间点触发。当事件触发时，所有匹配的钩子并行运行，相同的钩子命令自动去重。下表显示每个事件及其触发时机：

| 事件                   | 触发时机                                                                                                                                         |
| :--------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------- |
| `SessionStart`         | 会话开始或恢复时                                                                                                                                 |
| `Setup`                | 使用 `--init-only` 启动 Claude Code 时，或在 `-p` 模式下使用 `--init` 或 `--maintenance`。用于 CI 或脚本中的一次性准备                               |
| `UserPromptSubmit`     | 你提交提示词时，在 Claude 处理之前                                                                                                                |
| `UserPromptExpansion`  | 用户输入的命令展开为提示词时，在到达 Claude 之前。可以阻止展开                                                                                       |
| `PreToolUse`           | 工具调用执行之前。可以阻止它                                                                                                                      |
| `PermissionRequest`    | 权限对话框出现时                                                                                                                                  |
| `PermissionDenied`     | 工具调用被自动模式分类器拒绝时。返回 `{retry: true}` 告诉模型它可以重试被拒绝的工具调用                                                               |
| `PostToolUse`          | 工具调用成功后                                                                                                                                   |
| `PostToolUseFailure`   | 工具调用失败后                                                                                                                                   |
| `PostToolBatch`        | 并行工具调用的整个批次解析后，在下一次模型调用之前                                                                                                    |
| `Notification`         | Claude Code 发送通知时                                                                                                                            |
| `SubagentStart`        | 子代理启动时                                                                                                                                     |
| `SubagentStop`         | 子代理完成时                                                                                                                                     |
| `TaskCreated`          | 通过 `TaskCreate` 创建任务时                                                                                                                      |
| `TaskCompleted`        | 任务标记为完成时                                                                                                                                  |
| `Stop`                 | Claude 完成响应时                                                                                                                                |
| `StopFailure`          | 由于 API 错误轮次结束时。输出和退出码被忽略                                                                                                        |
| `TeammateIdle`         | [代理团队](/en/agent-teams)队友即将空闲时                                                                                                          |
| `InstructionsLoaded`   | CLAUDE.md 或 `.claude/rules/*.md` 文件加载到上下文时。在会话开始和文件延迟加载时触发                                                                |
| `ConfigChange`         | 会话期间配置文件更改时                                                                                                                            |
| `CwdChanged`           | 工作目录更改时，例如 Claude 执行 `cd` 命令。用于与 direnv 等工具进行响应式环境管理                                                                   |
| `FileChanged`          | 监视的文件在磁盘上更改时。`matcher` 字段指定要监视哪些文件名                                                                                        |
| `WorktreeCreate`       | 通过 `--worktree` 或 `isolation: "worktree"` 创建 worktree 时。替换默认 git 行为                                                                   |
| `WorktreeRemove`       | worktree 被移除时，无论是在会话退出时还是子代理完成时                                                                                                |
| `PreCompact`           | 上下文压缩之前                                                                                                                                   |
| `PostCompact`          | 上下文压缩完成后                                                                                                                                  |
| `Elicitation`          | MCP 服务器在工具调用期间请求用户输入时                                                                                                              |
| `ElicitationResult`    | 用户响应 MCP 引出后，在响应发回服务器之前                                                                                                           |
| `SessionEnd`           | 会话终止时                                                                                                                                       |

每个钩子都有一个 `type` 决定它如何运行。大多数钩子使用 `"type": "command"`，运行 shell 命令。还有四种其他类型可用：

* `"type": "http"`：将事件数据 POST 到 URL。详见 [HTTP 钩子](#http-hooks)。
* `"type": "mcp_tool"`：调用已连接 MCP 服务器上的工具。详见 [MCP 工具钩子](/en/hooks#mcp-tool-hook-fields)。
* `"type": "prompt"`：单轮 LLM 评估。详见[基于提示词的钩子](#prompt-based-hooks)。
* `"type": "agent"`：带工具访问的多轮验证。代理钩子是实验性的，可能会更改。详见[基于代理的钩子](#agent-based-hooks)。

### 合并多个钩子的结果

当多个钩子匹配同一事件时，每个钩子的命令都运行完成，然后 Claude Code 合并结果。一个钩子返回 `deny` 不会阻止兄弟钩子执行。不要依赖一个钩子的 `deny` 来抑制另一个钩子中的副作用。

所有匹配钩子完成后，Claude Code 合并它们的输出。对于 `PreToolUse` 权限决策，最严格的答案胜出：`deny` 覆盖 `ask`，`ask` 覆盖 `allow`。每个钩子的 `additionalContext` 文本都保留并一起传递给 Claude。

以下示例在 `Bash` 上注册了两个 `PreToolUse` 钩子。第一个将每个命令追加到日志文件并退出 0。第二个运行脚本，在命令包含 `rm -rf` 时以退出码 2 拒绝：

```json theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r .tool_input.command >> ~/.claude/bash.log"
          },
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm-rf.sh"
          }
        ]
      }
    ]
  }
}
```

当 Claude 尝试运行 `rm -rf /tmp/build` 时，两个钩子并行执行。日志钩子将命令写入 `~/.claude/bash.log` 并退出 0，报告没有决策。护栏钩子退出 2，拒绝工具调用。拒绝胜出，因此 Claude Code 阻止命令并向 Claude 显示护栏的 stderr。日志条目仍然被写入，因为日志钩子已经运行了。

### 读取输入和返回输出

钩子通过 stdin、stdout、stderr 和退出码与 Claude Code 通信。当事件触发时，Claude Code 将事件特定数据作为 JSON 传递到脚本的 stdin。脚本读取该数据，完成工作，然后通过退出码告诉 Claude Code 下一步做什么。

#### 钩子输入

每个事件包含公共字段如 `session_id` 和 `cwd`，但每个事件类型添加不同的数据。例如，当 Claude 运行 Bash 命令时，`PreToolUse` 钩子在 stdin 上接收类似这样的内容：

```json theme={null}
{
  "session_id": "abc123",          // unique ID for this session
  "cwd": "/Users/sarah/myproject", // working directory when the event fired
  "hook_event_name": "PreToolUse", // which event triggered this hook
  "tool_name": "Bash",             // the tool Claude is about to use
  "tool_input": {                  // the arguments Claude passed to the tool
    "command": "npm test"          // for Bash, this is the shell command
  }
}
```

你的脚本可以解析该 JSON 并对任何这些字段进行操作。`UserPromptSubmit` 钩子获得 `prompt` 文本，`SessionStart` 钩子获得 `source`（startup、resume、clear、compact），依此类推。详见参考中的[公共输入字段](/en/hooks#common-input-fields)了解共享字段，以及每个事件的部分了解事件特定 schema。

#### 钩子输出

你的脚本通过写入 stdout 或 stderr 并以特定代码退出来告诉 Claude Code 下一步做什么。例如，一个想要阻止命令的 `PreToolUse` 钩子：

```bash theme={null}
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q "drop table"; then
  echo "Blocked: dropping tables is not allowed" >&2  # stderr becomes Claude's feedback
  exit 2                                               # exit 2 = block the action
fi

exit 0  # exit 0 = no decision; the normal permission flow applies
```

退出码决定接下来发生什么：

* **退出 0**：钩子报告没有异议，操作正常进行。对于 `PreToolUse` 钩子，这不批准工具调用：正常的[权限流](/en/permissions)仍然适用。对于 `UserPromptSubmit`、`UserPromptExpansion` 和 `SessionStart` 钩子，你写入 stdout 的任何内容都添加到 Claude 的上下文中。
* **退出 2**：操作被阻止。将原因写入 stderr，Claude 将其作为反馈接收以便调整。某些事件不能被阻止：对于 `SessionStart`、`Setup`、`Notification` 等，退出码 2 向用户显示 stderr，执行继续。详见[每个事件的退出码 2 行为](/en/hooks#exit-code-2-behavior-per-event)了解完整列表。
* **任何其他退出码**：操作继续进行。记录显示 `<hook name> hook error` 通知后跟 stderr 的第一行；完整的 stderr 进入[调试日志](/en/hooks#debug-hooks)。

#### 结构化 JSON 输出

退出码只允许你阻止或保持沉默。要获得更多控制，退出 0 并改为向 stdout 打印 JSON 对象。

<Note>
  使用退出码 2 配合 stderr 消息来阻止，或使用退出码 0 配合 JSON 进行结构化控制。不要混合使用：Claude Code 在你退出 2 时忽略 JSON。
</Note>

例如，`PreToolUse` 钩子可以拒绝工具调用并告诉 Claude 原因，或将其升级给用户批准：

```json theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep for better performance"
  }
}
```

使用 `"deny"`，Claude Code 取消工具调用并将 `permissionDecisionReason` 反馈给 Claude。这些 `permissionDecision` 值特定于 `PreToolUse`：

* `"allow"`：跳过交互式权限提示。拒绝和询问规则，包括企业托管拒绝列表，仍然适用
* `"deny"`：取消工具调用并将原因发送给 Claude
* `"ask"`：正常向用户显示权限提示

第四个值 `"defer"` 可在带 `-p` 标志的[非交互模式](/en/headless)中使用。它退出进程并保留工具调用，以便 Agent SDK 包装器可以收集输入并恢复。详见参考中的[延迟工具调用](/en/hooks#defer-a-tool-call-for-later)。

返回 `"allow"` 跳过交互式提示但不覆盖[权限规则](/en/permissions#manage-permissions)。如果拒绝规则匹配工具调用，即使你的钩子返回 `"allow"`，调用也被阻止。如果询问规则匹配，用户仍被提示。这意味着来自任何设置范围的拒绝规则，包括[托管设置](/en/settings#settings-files)，始终优先于钩子批准。

其他事件使用不同的决策模式。例如，`PostToolUse` 和 `Stop` 钩子使用顶级 `decision: "block"` 字段，而 `PermissionRequest` 使用 `hookSpecificOutput.decision.behavior`。详见参考中的[摘要表](/en/hooks#decision-control)了解按事件的完整分解。

对于 `UserPromptSubmit` 钩子，改为使用 `additionalContext` 将文本注入 Claude 的上下文。基于提示词的钩子（`type: "prompt"`）以不同方式处理输出：详见[基于提示词的钩子](#prompt-based-hooks)。

### 使用匹配器过滤钩子

没有匹配器时，钩子在每次事件发生时触发。匹配器让你缩小范围。例如，如果你只想在文件编辑后运行格式化器（而不是每次工具调用后），将匹配器添加到你的 `PostToolUse` 钩子：

```json theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write ..." }
        ]
      }
    ]
  }
}
```

`"Edit|Write"` 匹配器仅在 Claude 使用 `Edit` 或 `Write` 工具时触发，而不是当它使用 `Bash`、`Read` 或任何其他工具时。详见[匹配器模式](/en/hooks#matcher-patterns)了解纯名称和正则表达式如何评估。

<Note>
  Claude 也可以通过 `Bash` 工具运行 shell 命令来创建或修改文件。如果你的钩子必须看到每个文件更改，例如用于合规扫描或审计日志，添加一个 [`Stop`](/en/hooks#stop) 钩子每轮扫描工作树。要改为每次调用覆盖，也匹配 `Bash` 并让脚本用 `git status --porcelain` 列出修改和未跟踪的文件。
</Note>

每个事件类型匹配特定字段：

| 事件                                                                                                                                            | 匹配器过滤的内容                                              | 示例匹配器值                                                                                                                                                    |
| :---------------------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `PreToolUse`、`PostToolUse`、`PostToolUseFailure`、`PermissionRequest`、`PermissionDenied`                                                       | 工具名称                                                      | `Bash`、`Edit\|Write`、`mcp__.*`                                                                                                                                |
| `SessionStart`                                                                                                                                  | 会话启动方式                                                   | `startup`、`resume`、`clear`、`compact`                                                                                                                         |
| `Setup`                                                                                                                                         | 哪个 CLI 标志触发了设置                                        | `init`、`maintenance`                                                                                                                                           |
| `SessionEnd`                                                                                                                                    | 会话结束原因                                                   | `clear`、`resume`、`logout`、`prompt_input_exit`、`bypass_permissions_disabled`、`other`                                                                        |
| `Notification`                                                                                                                                  | 通知类型                                                       | `permission_prompt`、`idle_prompt`、`auth_success`、`elicitation_dialog`、`elicitation_complete`、`elicitation_response`                                        |
| `SubagentStart`                                                                                                                                 | 代理类型                                                       | `general-purpose`、`Explore`、`Plan` 或自定义代理名称                                                                                                            |
| `PreCompact`、`PostCompact`                                                                                                                     | 什么触发了压缩                                                 | `manual`、`auto`                                                                                                                                                |
| `SubagentStop`                                                                                                                                  | 代理类型                                                       | 与 `SubagentStart` 相同的值                                                                                                                                     |
| `ConfigChange`                                                                                                                                  | 配置来源                                                       | `user_settings`、`project_settings`、`local_settings`、`policy_settings`、`skills`                                                                              |
| `StopFailure`                                                                                                                                   | 错误类型                                                       | `rate_limit`、`authentication_failed`、`oauth_org_not_allowed`、`billing_error`、`invalid_request`、`model_not_found`、`server_error`、`max_output_tokens`、`unknown` |
| `InstructionsLoaded`                                                                                                                            | 加载原因                                                       | `session_start`、`nested_traversal`、`path_glob_match`、`include`、`compact`                                                                                    |
| `Elicitation`                                                                                                                                   | MCP 服务器名称                                                 | 你配置的 MCP 服务器名称                                                                                                                                          |
| `ElicitationResult`                                                                                                                             | MCP 服务器名称                                                 | 与 `Elicitation` 相同的值                                                                                                                                       |
| `FileChanged`                                                                                                                                   | 要监视的字面文件名（参见 [FileChanged](/en/hooks#filechanged)）| `.envrc\|.env`                                                                                                                                                  |
| `UserPromptExpansion`                                                                                                                           | 命令名称                                                       | 你的技能或命令名称                                                                                                                                               |
| `UserPromptSubmit`、`PostToolBatch`、`Stop`、`TeammateIdle`、`TaskCreated`、`TaskCompleted`、`WorktreeCreate`、`WorktreeRemove`、`CwdChanged`    | 不支持匹配器                                                    | 总是在每次发生时触发                                                                                                                                              |

更多展示不同事件类型匹配器的示例：

<Tabs>
  <Tab title="记录每个 Bash 命令">
    仅匹配 `Bash` 工具调用并将每个命令记录到文件。`PostToolUse` 事件在命令完成后触发，因此 `tool_input.command` 包含运行的内容。钩子在 stdin 上以 JSON 形式接收事件数据，`jq -r '.tool_input.command'` 仅提取命令字符串，`>>` 将其追加到日志文件：

    ```json theme={null}
    {
      "hooks": {
        "PostToolUse": [
          {
            "matcher": "Bash",
            "hooks": [
              {
                "type": "command",
                "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
              }
            ]
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="匹配 MCP 工具">
    MCP 工具使用与内置工具不同的命名约定：`mcp__<server>__<tool>`，其中 `<server>` 是 MCP 服务器名称，`<tool>` 是它提供的工具。例如，`mcp__github__search_repositories` 或 `mcp__filesystem__read_file`。使用正则表达式匹配器定位特定服务器的所有工具，或使用 `mcp__.*__write.*` 等模式跨服务器匹配。详见参考中的[匹配 MCP 工具](/en/hooks#match-mcp-tools)了解完整示例列表。

    以下命令使用 `jq` 从钩子的 JSON 输入中提取工具名称并写入 stderr。写入 stderr 保持 stdout 干净用于 JSON 输出，并将消息发送到[调试日志](/en/hooks#debug-hooks)：

    ```json theme={null}
    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "mcp__github__.*",
            "hooks": [
              {
                "type": "command",
                "command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
              }
            ]
          }
        ]
      }
    }
    ```
  </Tab>

  <Tab title="会话结束时清理">
    `SessionEnd` 事件支持会话结束原因的匹配器。此钩子仅在 `clear`（当你运行 `/clear`）时触发，不在正常退出时触发：

    ```json theme={null}
    {
      "hooks": {
        "SessionEnd": [
          {
            "matcher": "clear",
            "hooks": [
              {
                "type": "command",
                "command": "rm -f /tmp/claude-scratch-*.txt"
              }
            ]
          }
        ]
      }
    }
    ```
  </Tab>
</Tabs>

完整匹配器语法请参阅[钩子参考](/en/hooks#configuration)。

#### 使用 `if` 字段按工具名称和参数过滤

<Note>
  `if` 字段需要 Claude Code v2.1.85 或更高版本。早期版本忽略它并在每个匹配的调用上运行钩子。
</Note>

`if` 字段使用[权限规则语法](/en/permissions)按工具名称和参数一起过滤钩子，因此钩子进程仅在工具调用匹配时或 Bash 命令过于复杂无法解析时启动。这超出了 `matcher`，后者在组级别仅按工具名称过滤。

例如，要仅在 Claude 使用 `git` 命令而不是所有 Bash 命令时运行钩子：

```json theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(git *)",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-git-policy.sh"
          }
        ]
      }
    ]
  }
}
```

钩子进程仅在 Bash 命令的子命令匹配 `git *` 或命令过于复杂无法解析为子命令时启动。对于 `npm test && git push` 等复合命令，Claude Code 评估每个子命令并触发钩子，因为 `git push` 匹配。`if` 字段接受与权限规则相同的模式：`"Bash(git *)"`、`"Edit(*.ts)"` 等。要匹配多个工具名称，使用单独的处理程序各带自己的 `if` 值，或在支持管道交替的 `matcher` 级别匹配。

`if` 仅在工具事件上有效：`PreToolUse`、`PostToolUse`、`PostToolUseFailure`、`PermissionRequest` 和 `PermissionDenied`。将其添加到任何其他事件会阻止钩子运行。

### 配置钩子位置

添加钩子的位置决定其范围：

| 位置                                                     | 范围                               | 可共享                           |
| :------------------------------------------------------- | :--------------------------------- | :------------------------------- |
| `~/.claude/settings.json`                                | 你的所有项目                        | 否，本地到你的机器                 |
| `.claude/settings.json`                                  | 单个项目                           | 是，可以提交到仓库                 |
| `.claude/settings.local.json`                            | 单个项目                           | 否，被 gitignore                  |
| 托管策略设置                                              | 组织范围                            | 是，管理员控制                    |
| [插件](/en/plugins) `hooks/hooks.json`                    | 插件启用时                          | 是，与插件捆绑                    |
| [技能](/en/skills) 或[代理](/en/sub-agents) 前置数据         | 技能或代理活动时                     | 是，定义在组件文件中               |

在 Claude Code 中运行 [`/hooks`](/en/hooks#the-hooks-menu) 浏览按事件分组的所有已配置钩子。要禁用钩子，在设置文件中设置 `"disableAllHooks": true`。托管设置中配置的钩子仍然运行，除非在那里也设置了 `disableAllHooks`。

如果你在 Claude Code 运行时直接编辑设置文件，文件监视器通常会自动获取钩子更改。

## 基于提示词的钩子

对于需要判断而非确定性规则的决策，使用 `type: "prompt"` 钩子。Claude Code 不运行 shell 命令，而是将你的提示词和钩子的输入数据发送到 Claude 模型（默认 Haiku）来做出决策。如果你需要更多能力，可以用 `model` 字段指定不同的模型。

模型的唯一工作是返回 yes/no 的 JSON 决策：

* `"ok": true`：操作继续
* `"ok": false`：发生什么取决于事件：
  * `Stop` 和 `SubagentStop`：`reason` 反馈给 Claude 以便继续工作
  * `PreToolUse`：工具调用被拒绝，`reason` 作为工具错误返回给 Claude，以便它可以调整并继续
  * `PostToolUse`、`PostToolBatch`、`UserPromptSubmit` 和 `UserPromptExpansion`：轮次结束，`reason` 作为警告行出现在聊天中

此示例使用 `Stop` 钩子询问模型是否所有请求的任务都已完成。如果模型返回 `"ok": false`，Claude 继续工作并将 `reason` 用作下一条指令：

```json theme={null}
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}
```

完整配置选项请参阅参考中的[基于提示词的钩子](/en/hooks#prompt-based-hooks)。

## 基于代理的钩子

<Warning>
  代理钩子是实验性的。行为和配置可能在未来版本中更改。对于生产工作流，首选[命令钩子](/en/hooks#command-hook-fields)。
</Warning>

当验证需要检查文件或运行命令时，使用 `type: "agent"` 钩子。与进行单次 LLM 调用的提示词钩子不同，代理钩子启动一个可以读取文件、搜索代码和使用其他工具来验证条件的子代理。

代理钩子使用与提示词钩子相同的 `"ok"` / `"reason"` 响应格式，但默认超时更长为 60 秒，最多 50 轮工具使用。

此示例在允许 Claude 停止之前验证测试是否通过：

```json theme={null}
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}
```

当钩子输入数据本身足以做出决策时使用提示词钩子。当你需要根据代码库的实际状态验证某些内容时使用代理钩子。

完整配置选项请参阅参考中的[基于代理的钩子](/en/hooks#agent-based-hooks)。

## HTTP 钩子

使用 `type: "http"` 钩子将事件数据 POST 到 HTTP 端点，而不是运行 shell 命令。端点接收与命令钩子在 stdin 上接收的相同 JSON，并通过 HTTP 响应体使用相同的 JSON 格式返回结果。

当你希望 Web 服务器、云函数或外部服务处理钩子逻辑时，HTTP 钩子很有用：例如，跨团队记录工具使用事件的共享审计服务。

此示例将每个工具使用发布到本地日志服务：

```json theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/tool-use",
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}
```

端点应使用与命令钩子相同的[输出格式](/en/hooks#json-output)返回 JSON 响应体。要阻止工具调用，返回带有适当 `hookSpecificOutput` 字段的 2xx 响应。仅 HTTP 状态码无法阻止操作。

头部值支持使用 `$VAR_NAME` 或 `${VAR_NAME}` 语法的环境变量插值。只有 `allowedEnvVars` 数组中列出的变量被解析；所有其他 `$VAR` 引用保持为空。

完整配置选项和响应处理请参阅参考中的 [HTTP 钩子](/en/hooks#http-hook-fields)。

## 限制和故障排除

### 限制

* 命令钩子仅通过 stdout、stderr 和退出码通信。它们不能触发 `/` 命令或工具调用。通过 `additionalContext` 返回的文本作为 Claude 作为纯文本读取的系统提醒注入。HTTP 钩子通过响应体通信。
* 钩子超时因类型而异。使用钩子配置中的 `timeout` 字段以秒为单位覆盖每个钩子。
  * `command`、`http`、`mcp_tool`：10 分钟。`UserPromptSubmit` 将这些降低到 30 秒。
  * `prompt`：30 秒。
  * `agent`：60 秒。
* `PostToolUse` 钩子无法撤消操作，因为工具已经执行。
* `PermissionRequest` 钩子在[非交互模式](/en/headless)（`-p`）中不触发。使用 `PreToolUse` 钩子进行自动权限决策。
* `Stop` 钩子在 Claude 完成响应时触发，不仅在任务完成时。它们在用户中断时不触发。API 错误改为触发 [StopFailure](/en/hooks#stopfailure)。
* 当多个 PreToolUse 钩子返回 [`updatedInput`](/en/hooks#pretooluse) 来重写工具的参数时，最后完成的胜出。由于钩子并行运行，顺序是不确定的。避免有多个钩子修改同一工具的输入。

### 钩子和权限模式

PreToolUse 钩子在任何权限模式检查之前触发。返回 `permissionDecision: "deny"` 的钩子即使在 `bypassPermissions` 模式下或使用 `--dangerously-skip-permissions` 也会阻止工具。这让你可以强制执行用户无法通过更改权限模式绕过的策略。

反之则不然：返回 `"allow"` 的钩子不会绕过设置中的拒绝规则。钩子可以收紧限制但不能将它们放宽到超出权限规则允许的范围。

### 钩子未触发

钩子已配置但从未执行。

* 运行 `/hooks` 确认钩子出现在正确的事件下
* 检查匹配器模式是否精确匹配工具名称（匹配器区分大小写）
* 验证你触发了正确的事件类型（例如，`PreToolUse` 在工具执行前触发，`PostToolUse` 在之后触发）
* 如果在非交互模式（`-p`）中使用 `PermissionRequest` 钩子，改用 `PreToolUse`

### 输出中的钩子错误

你在记录中看到类似"PreToolUse hook error: ..."的消息。

* 你的脚本意外地以非零代码退出。通过管道示例 JSON 手动测试：
  ```bash theme={null}
  echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
  echo $?  # Check the exit code
  ```
* 如果你看到"command not found"，使用绝对路径或 `${CLAUDE_PROJECT_DIR}` 引用脚本。要完全避免 shell 引号，添加 `"args": []` 切换到[执行形式](/en/hooks#exec-form-and-shell-form)，直接启动脚本而无需 shell
* 如果你看到"jq: command not found"，安装 `jq` 或使用 Python/Node.js 进行 JSON 解析
* 如果脚本根本没有运行，使其可执行：`chmod +x ./my-hook.sh`

### `/hooks` 显示没有配置的钩子

你编辑了设置文件但钩子没有出现在菜单中。

* 文件编辑通常会自动获取。如果几秒后仍未出现，文件监视器可能错过了更改：重启会话以强制重新加载。
* 验证你的 JSON 是否有效（不允许尾随逗号和注释）
* 确认设置文件在正确的位置：项目钩子在 `.claude/settings.json`，全局钩子在 `~/.claude/settings.json`

### Stop 钩子达到阻止上限

Claude 继续工作而不是停止，然后以警告结束轮次，说明 Stop 钩子连续阻止了太多次。

Claude Code 在 Stop 钩子连续阻止 8 次无进展后覆盖它。你的钩子脚本需要检查它是否已触发继续。从 JSON 输入解析 `stop_hook_active` 字段，如果为 `true` 则提前退出：

```bash theme={null}
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Allow Claude to stop
fi
# ... rest of your hook logic
```

如果你的钩子合法需要超过八次迭代才能收敛，使用 [`CLAUDE_CODE_STOP_HOOK_BLOCK_CAP`](/en/env-vars) 提高上限。

### JSON 验证失败

Claude Code 显示 JSON 解析错误，即使你的钩子脚本输出有效的 JSON。

当 Claude Code 运行 shell 形式的命令钩子（没有 `args` 的）时，它在 macOS 和 Linux 上启动 `sh -c`，在 Windows 上默认启动 Git Bash。此 shell 是非交互的，但 Git Bash 和某些配置（如 `BASH_ENV` 指向 `~/.bashrc`）仍然加载你的配置文件。如果该配置文件包含无条件的 `echo` 语句，输出会被前置到钩子的 JSON：

```text theme={null}
Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}
```

Claude Code 尝试将其解析为 JSON 并失败。要修复此问题，将 shell 配置文件中的 echo 语句包装为仅在交互式 shell 中运行：

```bash theme={null}
# In ~/.zshrc or ~/.bashrc
if [[ $- == *i* ]]; then
  echo "Shell ready"
fi
```

`$-` 变量包含 shell 标志，`i` 表示交互式。钩子在非交互式 shell 中运行，因此 echo 被跳过。

### 调试技术

记录视图（用 `Ctrl+O` 切换）显示每个触发的钩子的单行摘要：成功是静默的，阻止错误显示 stderr，非阻止错误显示 `<hook name> hook error` 通知后跟 stderr 的第一行。

有关完整的执行细节，包括哪些钩子匹配、它们的退出码、stdout 和 stderr，请阅读调试日志。使用 `claude --debug-file /tmp/claude.log` 启动 Claude Code 以写入已知路径，然后在另一个终端 `tail -f /tmp/claude.log`。如果你在没有该标志的情况下启动，运行 `/debug` 在会话中启用日志记录并找到日志路径。

## 了解更多

* [钩子参考](/en/hooks)：完整的事件 schema、JSON 输出格式、异步钩子和 MCP 工具钩子
* [安全注意事项](/en/hooks#security-considerations)：在共享或生产环境中部署钩子之前审查
* [Bash 命令验证器示例](https://github.com/anthropics/claude-code/blob/main/examples/hooks/bash_command_validator_example.py)：完整的参考实现
