English ← MyDocs

文档索引

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

使用钩子自动化工作流

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

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

对于需要判断而非确定性规则的决策,你也可以使用基于提示词的钩子基于代理的钩子,使用 Claude 模型评估条件。

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

Tip

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

设置你的第一个钩子

要创建钩子,将 hooks 块添加到设置文件。本教程创建一个桌面通知钩子,以便 Claude 等待输入时你会收到提醒,而不用盯着终端。

  1. 将钩子添加到设置中

    打开 ~/.claude/settings.json 并添加一个 Notification 钩子。以下示例使用 macOS 的 osascript;Linux 和 Windows 命令请参阅Claude 需要输入时获取通知

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

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

    {
      "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 为你编写钩子。

  2. 验证配置

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

  3. 测试钩子

    Esc 返回 CLI。让 Claude 做一些需要权限的事情,然后切换离开终端。你应该会收到桌面通知。

Tip

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

你可以自动化什么

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

每个示例包含一个可直接使用的配置块,你可以添加到设置文件。最常见的模式:

有关运行单独模型审查并将发现反馈到会话中的钩子的生产示例,请参阅 security-guidance 插件如何与 Claude Code 集成

Claude 需要输入时获取通知

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

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

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}
如果没有通知出现

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

osascript -e 'display notification "test"'

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

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code' 'Claude Code needs your attention'"
          }
        ]
      }
    ]
  }
}
{
  "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')\""
          }
        ]
      }
    ]
  }
}

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

匹配器触发时机
permission_promptClaude 需要你批准工具使用
idle_promptClaude 完成并等待你的下一个提示词
auth_success认证完成
elicitation_dialogMCP 服务器打开引出表单
elicitation_completeMCP 引出表单被提交或关闭
elicitation_responseMCP 引出响应被发回服务器

输入 /hooks 并选择 Notification 确认钩子已注册。完整的事件 schema 请参阅 Notification 参考

编辑后自动格式化代码

Claude 编辑的每个文件自动运行 Prettier,使格式保持一致,无需手动干预。

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

{
  "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 下载

阻止对受保护文件的编辑

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

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

  1. 创建钩子脚本

    将此保存到 .claude/hooks/protect-files.sh

    #!/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
    
  2. 使脚本可执行(macOS/Linux)

    钩子脚本必须可执行,Claude Code 才能运行它们:

    chmod +x .claude/hooks/protect-files.sh
    
  3. 注册钩子

    PreToolUse 钩子添加到 .claude/settings.json,在任何 EditWrite 工具调用之前运行脚本:

    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "Edit|Write",
            "hooks": [
              {
                "type": "command",
                "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
              }
            ]
          }
        ]
      }
    }
    

压缩后重新注入上下文

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

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

{
  "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。有关环境变量,请参阅参考中的 CLAUDE_ENV_FILE

审计配置更改

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

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

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

匹配器按配置类型过滤:user_settingsproject_settingslocal_settingspolicy_settingsskills。要阻止更改生效,以退出码 2 退出或返回 {"decision": "block"}。详见 ConfigChange 参考了解完整输入 schema。

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

某些项目根据所在目录设置不同的环境变量。direnv 等工具在你的 shell 中自动完成此操作,但 Claude 的 Bash 工具不会自行获取这些更改。

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

{
  "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 shellenvdevbox global shellenv 代替 direnv export bash

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

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

详见 CwdChangedFileChanged 参考条目了解输入 schema、watchPaths 输出和 CLAUDE_ENV_FILE 详情。

自动批准特定权限提示

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

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

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

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

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

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

Note

bypassPermissions 仅在会话启动时已具有旁路模式的情况下生效:--dangerously-skip-permissions--permission-mode bypassPermissions--allow-dangerously-skip-permissions 或设置中的 permissions.defaultMode: "bypassPermissions",且未被 permissions.disableBypassPermissionsMode 禁用。它永远不会作为 defaultMode 持久化。

要将会话切换到 acceptEdits,你的钩子将此 JSON 写入 stdout:

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

保持匹配器尽可能窄。匹配 .* 或留空匹配器会自动批准每个权限提示,包括文件写入和 shell 命令。详见 PermissionRequest 参考了解完整的决策字段集。

钩子如何工作

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

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

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

  • "type": "http":将事件数据 POST 到 URL。详见 HTTP 钩子
  • "type": "mcp_tool":调用已连接 MCP 服务器上的工具。详见 MCP 工具钩子
  • "type": "prompt":单轮 LLM 评估。详见基于提示词的钩子
  • "type": "agent":带工具访问的多轮验证。代理钩子是实验性的,可能会更改。详见基于代理的钩子

合并多个钩子的结果

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

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

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

{
  "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_idcwd,但每个事件类型添加不同的数据。例如,当 Claude 运行 Bash 命令时,PreToolUse 钩子在 stdin 上接收类似这样的内容:

{
  "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),依此类推。详见参考中的公共输入字段了解共享字段,以及每个事件的部分了解事件特定 schema。

钩子输出

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

#!/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 钩子,这不批准工具调用:正常的权限流仍然适用。对于 UserPromptSubmitUserPromptExpansionSessionStart 钩子,你写入 stdout 的任何内容都添加到 Claude 的上下文中。
  • 退出 2:操作被阻止。将原因写入 stderr,Claude 将其作为反馈接收以便调整。某些事件不能被阻止:对于 SessionStartSetupNotification 等,退出码 2 向用户显示 stderr,执行继续。详见每个事件的退出码 2 行为了解完整列表。
  • 任何其他退出码:操作继续进行。记录显示 <hook name> hook error 通知后跟 stderr 的第一行;完整的 stderr 进入调试日志

结构化 JSON 输出

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

Note

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

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

{
  "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 标志的非交互模式中使用。它退出进程并保留工具调用,以便 Agent SDK 包装器可以收集输入并恢复。详见参考中的延迟工具调用

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

其他事件使用不同的决策模式。例如,PostToolUseStop 钩子使用顶级 decision: "block" 字段,而 PermissionRequest 使用 hookSpecificOutput.decision.behavior。详见参考中的摘要表了解按事件的完整分解。

对于 UserPromptSubmit 钩子,改为使用 additionalContext 将文本注入 Claude 的上下文。基于提示词的钩子(type: "prompt")以不同方式处理输出:详见基于提示词的钩子

使用匹配器过滤钩子

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

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write ..." }
        ]
      }
    ]
  }
}

"Edit|Write" 匹配器仅在 Claude 使用 EditWrite 工具时触发,而不是当它使用 BashRead 或任何其他工具时。详见匹配器模式了解纯名称和正则表达式如何评估。

Note

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

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

事件匹配器过滤的内容示例匹配器值
PreToolUsePostToolUsePostToolUseFailurePermissionRequestPermissionDenied工具名称BashEdit|Writemcp__.*
SessionStart会话启动方式startupresumeclearcompact
Setup哪个 CLI 标志触发了设置initmaintenance
SessionEnd会话结束原因clearresumelogoutprompt_input_exitbypass_permissions_disabledother
Notification通知类型permission_promptidle_promptauth_successelicitation_dialogelicitation_completeelicitation_response
SubagentStart代理类型general-purposeExplorePlan 或自定义代理名称
PreCompactPostCompact什么触发了压缩manualauto
SubagentStop代理类型SubagentStart 相同的值
ConfigChange配置来源user_settingsproject_settingslocal_settingspolicy_settingsskills
StopFailure错误类型rate_limitauthentication_failedoauth_org_not_allowedbilling_errorinvalid_requestmodel_not_foundserver_errormax_output_tokensunknown
InstructionsLoaded加载原因session_startnested_traversalpath_glob_matchincludecompact
ElicitationMCP 服务器名称你配置的 MCP 服务器名称
ElicitationResultMCP 服务器名称Elicitation 相同的值
FileChanged要监视的字面文件名(参见 FileChanged.envrc|.env
UserPromptExpansion命令名称你的技能或命令名称
UserPromptSubmitPostToolBatchStopTeammateIdleTaskCreatedTaskCompletedWorktreeCreateWorktreeRemoveCwdChanged不支持匹配器总是在每次发生时触发

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

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

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}

MCP 工具使用与内置工具不同的命名约定:mcp__<server>__<tool>,其中 <server> 是 MCP 服务器名称,<tool> 是它提供的工具。例如,mcp__github__search_repositoriesmcp__filesystem__read_file。使用正则表达式匹配器定位特定服务器的所有工具,或使用 mcp__.*__write.* 等模式跨服务器匹配。详见参考中的匹配 MCP 工具了解完整示例列表。

以下命令使用 jq 从钩子的 JSON 输入中提取工具名称并写入 stderr。写入 stderr 保持 stdout 干净用于 JSON 输出,并将消息发送到调试日志

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
          }
        ]
      }
    ]
  }
}

SessionEnd 事件支持会话结束原因的匹配器。此钩子仅在 clear(当你运行 /clear)时触发,不在正常退出时触发:

{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "rm -f /tmp/claude-scratch-*.txt"
          }
        ]
      }
    ]
  }
}

完整匹配器语法请参阅钩子参考

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

Note

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

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

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

{
  "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 仅在工具事件上有效:PreToolUsePostToolUsePostToolUseFailurePermissionRequestPermissionDenied。将其添加到任何其他事件会阻止钩子运行。

配置钩子位置

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

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

在 Claude Code 中运行 /hooks 浏览按事件分组的所有已配置钩子。要禁用钩子,在设置文件中设置 "disableAllHooks": true。托管设置中配置的钩子仍然运行,除非在那里也设置了 disableAllHooks

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

基于提示词的钩子

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

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

  • "ok": true:操作继续
  • "ok": false:发生什么取决于事件:
    • StopSubagentStopreason 反馈给 Claude 以便继续工作
    • PreToolUse:工具调用被拒绝,reason 作为工具错误返回给 Claude,以便它可以调整并继续
    • PostToolUsePostToolBatchUserPromptSubmitUserPromptExpansion:轮次结束,reason 作为警告行出现在聊天中

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

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

完整配置选项请参阅参考中的基于提示词的钩子

基于代理的钩子

Warning

代理钩子是实验性的。行为和配置可能在未来版本中更改。对于生产工作流,首选命令钩子

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

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

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

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

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

完整配置选项请参阅参考中的基于代理的钩子

HTTP 钩子

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

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

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

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

端点应使用与命令钩子相同的输出格式返回 JSON 响应体。要阻止工具调用,返回带有适当 hookSpecificOutput 字段的 2xx 响应。仅 HTTP 状态码无法阻止操作。

头部值支持使用 $VAR_NAME{{CONTENT}}amp;#123;VAR_NAME&#125; 语法的环境变量插值。只有 allowedEnvVars 数组中列出的变量被解析;所有其他 $VAR 引用保持为空。

完整配置选项和响应处理请参阅参考中的 HTTP 钩子

限制和故障排除

限制

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

钩子和权限模式

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

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

钩子未触发

钩子已配置但从未执行。

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

输出中的钩子错误

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

  • 你的脚本意外地以非零代码退出。通过管道示例 JSON 手动测试:
    echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
    echo $?  # Check the exit code
    
  • 如果你看到"command not found",使用绝对路径或 {{CONTENT}}amp;#123;CLAUDE_PROJECT_DIR&#125; 引用脚本。要完全避免 shell 引号,添加 "args": [] 切换到执行形式,直接启动脚本而无需 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 则提前退出:

#!/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 提高上限。

JSON 验证失败

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

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

Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}

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

# 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 在会话中启用日志记录并找到日志路径。

了解更多