create-hook

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Create Hook Command

创建 Hook 命令

Analyze the project, suggest practical hooks, and create them with proper testing.
分析项目,建议实用的 Hook,并通过正确的测试创建它们。

Your Task (/create-hook)

你的任务(/create-hook)

  1. Analyze environment - Detect tooling and existing hooks
  2. Suggest hooks - Based on your project configuration
  3. Configure hook - Ask targeted questions and create the script
  4. Test & validate - Ensure the hook works correctly
  1. 分析环境 - 检测工具链和现有 Hook
  2. 建议 Hook - 根据项目配置提出建议
  3. 配置 Hook - 提出有针对性的问题并创建脚本
  4. 测试与验证 - 确保 Hook 正确工作

Your Workflow

工作流

1. Environment Analysis & Suggestions

1. 环境分析与建议

Automatically detect the project tooling and suggest relevant hooks:
When TypeScript is detected (
tsconfig.json
):
  • PostToolUse hook: "Type-check files after editing"
  • PreToolUse hook: "Block edits with type errors"
When Prettier is detected (
.prettierrc
,
prettier.config.js
):
  • PostToolUse hook: "Auto-format files after editing"
  • PreToolUse hook: "Require formatted code"
When ESLint is detected (
.eslintrc.*
):
  • PostToolUse hook: "Lint and auto-fix after editing"
  • PreToolUse hook: "Block commits with linting errors"
When package.json has scripts:
  • test
    script → "Run tests before commits"
  • build
    script → "Validate build before commits"
When a git repository is detected:
  • PreToolUse/Bash hook: "Prevent commits with secrets"
  • PostToolUse hook: "Security scan on file changes"
Decision Tree:
Project has TypeScript? → Suggest type checking hooks
Project has formatter? → Suggest formatting hooks
Project has tests? → Suggest test validation hooks
Security sensitive? → Suggest security hooks
+ Scan for additional patterns and suggest custom hooks based on:
  - Custom scripts in package.json
  - Unique file patterns or extensions
  - Development workflow indicators
  - Project-specific tooling configurations
自动检测项目工具链,并建议相关 Hook:
检测到 TypeScript 时(
tsconfig.json
):
  • PostToolUse Hook:“编辑后对文件进行类型检查”
  • PreToolUse Hook:“阻止存在类型错误的编辑”
检测到 Prettier 时(
.prettierrc
、
prettier.config.js
):
  • PostToolUse Hook:“编辑后自动格式化文件”
  • PreToolUse Hook:“要求代码已格式化”
检测到 ESLint 时(
.eslintrc.*
):
  • PostToolUse Hook:“编辑后执行 lint 并自动修复”
  • PreToolUse Hook:“阻止存在 lint 错误的提交”
当 package.json 中包含 scripts 时:
  • test
    script → “提交前运行测试”
  • build
    script → “提交前验证构建”
检测到 Git 仓库时:
  • PreToolUse/Bash Hook:“防止提交包含密钥的内容”
  • PostToolUse Hook:“文件变更时执行安全扫描”
决策树:
Project has TypeScript? → Suggest type checking hooks
Project has formatter? → Suggest formatting hooks
Project has tests? → Suggest test validation hooks
Security sensitive? → Suggest security hooks
+ Scan for additional patterns and suggest custom hooks based on:
  - Custom scripts in package.json
  - Unique file patterns or extensions
  - Development workflow indicators
  - Project-specific tooling configurations

2. Hook Configuration

2. Hook 配置

Start by asking: "What should this hook do?" and offer relevant suggestions from your analysis.
Then understand the context from the user's description and only ask about details you're unsure about:
  1. Trigger timing: When should it run?
    • PreToolUse
      : Before file operations (can block)
    • PostToolUse
      : After file operations (feedback/fixes)
    • UserPromptSubmit
      : Before processing requests
    • Other event types as needed
  2. Tool matcher: Which tools should trigger it? (
    Write
    ,
    Edit
    ,
    Bash
    ,
    *
    etc)
  3. Scope:
    global
    ,
    project
    , or
    project-local
  4. Response approach:
    • Exit codes only: Simple (exit 0 = success, exit 2 = block in PreToolUse)
    • JSON response: Advanced control (blocking, context, decisions)
    • Guide based on complexity: simple pass/fail → exit codes, rich feedback → JSON
  5. Blocking behavior (if relevant): "Should this stop operations when issues are found?"
    • PreToolUse: Can block operations (security, validation)
    • PostToolUse: Usually provide feedback only
  6. Claude integration (CRITICAL): "Should Claude Code automatically see and fix issues this hook detects?"
    • If YES: Use
      additionalContext
      for error communication
    • If NO: Use
      suppressOutput: true
      for silent operation
  7. Context pollution: "Should successful operations be silent to avoid noise?"
    • Recommend YES for formatting, routine checks
    • Recommend NO for security alerts, critical errors
  8. File filtering: "What file types should this hook process?"
首先询问:“这个 Hook 应该做什么?”,并提供根据分析结果生成的建议。
然后根据用户的描述了解上下文,只询问尚不确定的细节:
  1. 触发时机:何时运行?
    • PreToolUse
      :文件操作之前(可以阻止操作)
    • PostToolUse
      :文件操作之后(提供反馈或修复)
    • UserPromptSubmit
      :处理请求之前
    • 根据需要使用其他事件类型
  2. Tool matcher:哪些工具应触发它?(
    Write
    、
    Edit
    、
    Bash
    、
    *
    等)
  3. 作用域:
    global
    、
    project
    或
    project-local
  4. 响应方式:
    • 仅使用退出码:简单(退出 0 表示成功;在 PreToolUse 中退出 2 表示阻止)
    • JSON 响应:高级控制(阻止、上下文、决策)
    • 根据复杂度提供建议:简单的通过/失败 → 退出码;丰富的反馈 → JSON
  5. 阻止行为(如相关):“发现问题时是否应停止操作?”
    • PreToolUse:可以阻止操作(安全、验证)
    • PostToolUse:通常只提供反馈
  6. Claude 集成(关键):“Claude Code 是否应自动查看并修复此 Hook 检测到的问题?”
    • 如果是:使用
      additionalContext
      传达错误
    • 如果否:使用
      suppressOutput: true
      静默运行
  7. 上下文污染:“成功的操作是否应保持静默以避免噪声?”
    • 对格式化和常规检查推荐选择“是”
    • 对安全警报和严重错误推荐选择“否”
  8. 文件过滤:“此 Hook 应处理哪些文件类型?”

3. Hook Creation

3. Hook 创建

You should:
  • Create hooks directory:
    ~/.claude/hooks/
    or
    .claude/hooks/
    based on scope
  • Generate script: Create hook script with:
    • Proper shebang and executable permissions
    • Project-specific commands (use detected config paths)
    • Comments explaining the hook's purpose
  • Update settings: Add hook configuration to appropriate settings.json
  • Use absolute paths: Avoid relative paths to scripts and executables. Use
    $CLAUDE_PROJECT_DIR
    to reference project root
  • Offer validation: Ask if the user wants you to test the hook
Key Implementation Standards:
  • Read JSON from stdin (never use argv)
  • Use top-level
    additionalContext
    /
    systemMessage
    for Claude communication
  • Include
    suppressOutput: true
    for successful operations
  • Provide specific error counts and actionable feedback
  • Focus on changed files rather than entire codebase
  • Support common development workflows
⚠️ CRITICAL: Input/Output Format
This is where most hook implementations fail. Pay extra attention to:
  • Input: Reading JSON from stdin correctly (not argv)
  • Output: Using correct top-level JSON structure for Claude communication
  • Documentation: Consulting official docs for exact schemas when in doubt
你应当:
  • 创建 Hooks 目录:根据作用域使用
    ~/.claude/hooks/
    或
    .claude/hooks/
  • 生成脚本:创建包含以下内容的 Hook 脚本:
    • 正确的 shebang 和可执行权限
    • 项目专用命令(使用检测到的配置路径)
    • 解释 Hook 用途的注释
  • 更新设置:将 Hook 配置添加到适当的 settings.json 中
  • 使用绝对路径:避免对脚本和可执行文件使用相对路径。使用
    $CLAUDE_PROJECT_DIR
    引用项目根目录
  • 提供验证选项:询问用户是否要测试该 Hook
关键实现标准:
  • 从 stdin 读取 JSON(绝不使用 argv)
  • 使用顶层
    additionalContext
    /
    systemMessage
    与 Claude 通信
  • 为成功的操作包含
    suppressOutput: true
  • 提供具体的错误数量和可操作的反馈
  • 聚焦于变更的文件,而不是整个代码库
  • 支持常见的开发工作流
⚠️ 关键:输入/输出格式
这是大多数 Hook 实现失败的地方。请特别注意:
  • 输入:正确从 stdin 读取 JSON(而不是 argv)
  • 输出:使用正确的顶层 JSON 结构与 Claude 通信
  • 文档:如有疑问,请查阅官方文档以获取准确的 schema

4. Testing & Validation

4. 测试与验证

CRITICAL: Test both happy and sad paths:
Happy Path Testing:
  1. Test expected success scenario - Create conditions where hook should pass
    • Examples: TypeScript (valid code), Linting (formatted code), Security (safe commands)
Sad Path Testing: 2. Test expected failure scenario - Create conditions where hook should fail/warn
  • Examples: TypeScript (type errors), Linting (unformatted code), Security (dangerous operations)
Verification Steps: 3. Verify expected behavior: Check if it blocks/warns/provides context as intended
Example Testing Process:
  • For a hook preventing file deletion: Create a test file, attempt the protected action, and verify the hook prevents it
If Issues Occur, you should:
  • Check hook registration in settings
  • Verify script permissions (
    chmod +x
    )
  • Test with simplified version first
  • Debug with detailed hook execution analysis
关键:同时测试正常路径和异常路径:
正常路径测试:
  1. 测试预期成功场景 - 创建 Hook 应通过的条件
    • 示例:TypeScript(有效代码)、Linting(格式正确的代码)、Security(安全命令)
异常路径测试: 2. 测试预期失败场景 - 创建 Hook 应失败或发出警告的条件
  • 示例:TypeScript(类型错误)、Linting(格式不正确的代码)、Security(危险操作)
验证步骤: 3. 验证预期行为:检查 Hook 是否按预期阻止操作、发出警告或提供上下文
测试流程示例:
  • 对于阻止文件删除的 Hook:创建测试文件,尝试执行受保护的操作,并验证 Hook 是否阻止了它
如果出现问题,你应当:
  • 检查 settings 中的 Hook 注册情况
  • 验证脚本权限(
    chmod +x
    )
  • 首先使用简化版本进行测试
  • 使用详细的 Hook 执行分析进行调试

Hook Templates

Hook 模板

Type Checking (PostToolUse)

类型检查(PostToolUse)

#!/usr/bin/env node
// Read stdin JSON, check .ts/.tsx files only
// Run: npx tsc --noEmit --pretty
// Output: JSON with additionalContext for errors
#!/usr/bin/env node
// Read stdin JSON, check .ts/.tsx files only
// Run: npx tsc --noEmit --pretty
// Output: JSON with additionalContext for errors

Auto-formatting (PostToolUse)

自动格式化(PostToolUse)

#!/usr/bin/env node
// Read stdin JSON, check supported file types
// Run: npx prettier --write [file]
// Output: JSON with suppressOutput: true
#!/usr/bin/env node
// Read stdin JSON, check supported file types
// Run: npx prettier --write [file]
// Output: JSON with suppressOutput: true

Security Scanning (PreToolUse)

安全扫描(PreToolUse)

bash
#!/bin/bash
bash
#!/bin/bash

Read stdin JSON, check for secrets/keys

Read stdin JSON, check for secrets/keys

Block if dangerous patterns found

Block if dangerous patterns found

Exit 2 to block, 0 to continue

Exit 2 to block, 0 to continue


_Complete templates available at: <https://docs.claude.com/en/docs/claude-code/hooks#examples>_

_完整模板见:<https://docs.claude.com/en/docs/claude-code/hooks#examples>_

Quick Reference

快速参考

Common Patterns:
  • stdin input:
    JSON.parse(process.stdin.read())
  • File filtering: Check extensions before processing
  • Success response:
    {continue: true, suppressOutput: true}
  • Error response:
    {continue: true, additionalContext: "error details"}
  • Block operation:
    exit(2)
    in PreToolUse hooks
Hook Types by Use Case:
  • Code Quality: PostToolUse for feedback and fixes
  • Security: PreToolUse to block dangerous operations
  • CI/CD: PreToolUse to validate before commits
  • Development: PostToolUse for automated improvements
Hook Execution Best Practices:
  • Hooks run in parallel according to official documentation
  • Design for independence since execution order isn't guaranteed
  • Plan hook interactions carefully when multiple hooks affect the same files
常见模式:
  • stdin 输入:
    JSON.parse(process.stdin.read())
  • 文件过滤:处理前检查扩展名
  • 成功响应:
    {continue: true, suppressOutput: true}
  • 错误响应:
    {continue: true, additionalContext: "error details"}
  • 阻止操作:在 PreToolUse Hook 中执行
    exit(2)
按用例划分的 Hook 类型:
  • 代码质量:使用 PostToolUse 提供反馈和修复
  • 安全:使用 PreToolUse 阻止危险操作
  • CI/CD:使用 PreToolUse 在提交前进行验证
  • 开发:使用 PostToolUse 实现自动化改进
Hook 执行最佳实践:
  • 根据官方文档,Hooks 会并行运行
  • 由于执行顺序无法保证,应确保彼此独立
  • 当多个 Hook 影响相同文件时,谨慎规划 Hook 之间的交互

Success Criteria

成功标准

✅ Hook created successfully when:
  • Script has executable permissions
  • Registered in correct settings.json
  • Responds correctly to test scenarios
  • Integrates properly with Claude for automated fixes
  • Follows project conventions and detected tooling
Result: The user gets a working hook that enhances their development workflow with intelligent automation and quality checks.

Documentation Index

Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt Use this file to discover all available pages before exploring further.
✅ 满足以下条件时,Hook 即创建成功:
  • 脚本具有可执行权限
  • 已注册到正确的 settings.json 中
  • 能够正确响应测试场景
  • 与 Claude 正确集成以自动执行修复
  • 遵循项目约定和检测到的工具链
结果:用户获得一个可正常工作的 Hook,通过智能自动化和质量检查提升开发工作流。

文档索引

在 https://code.claude.com/docs/llms.txt 获取完整的文档索引 在进一步探索之前,使用此文件发现所有可用页面。

Automate workflows with hooks

使用 Hooks 自动化工作流

Run shell commands automatically when Claude Code edits files, finishes tasks, or needs input. Format code, send notifications, validate commands, and enforce project rules.
Hooks are user-defined shell commands that execute at specific points in Claude Code's lifecycle. They provide deterministic control over Claude Code's behavior, ensuring certain actions always happen rather than relying on the LLM to choose to run them. Use hooks to enforce project rules, automate repetitive tasks, and integrate Claude Code with your existing tools.
For decisions that require judgment rather than deterministic rules, you can also use prompt-based hooks or agent-based hooks that use a Claude model to evaluate conditions.
For other ways to extend Claude Code, see skills for giving Claude additional instructions and executable commands, subagents for running tasks in isolated contexts, and plugins for packaging extensions to share across projects.
<Tip> This guide covers common use cases and how to get started. For full event schemas, JSON input/output formats, and advanced features like async hooks and MCP tool hooks, see the [Hooks reference](/en/hooks). </Tip>
当 Claude Code 编辑文件、完成任务或需要输入时自动运行 shell 命令。格式化代码、发送通知、验证命令并执行项目规则。
Hooks 是用户定义的 shell 命令,会在 Claude Code 生命周期的特定节点执行。它们为 Claude Code 的行为提供确定性控制,确保特定操作始终发生,而不是依赖 LLM 决定是否运行。使用 Hooks 可以执行项目规则、自动处理重复任务,并将 Claude Code 与现有工具集成。
对于需要判断而非确定性规则的决策,还可以使用 基于 prompt 的 Hooks或基于 Agent 的 Hooks,由 Claude 模型评估条件。
如需通过其他方式扩展 Claude Code,请参阅 skills(为 Claude 提供额外指令和可执行命令)、subagents(在隔离上下文中运行任务)以及 plugins(打包扩展以在项目间共享)。
<Tip> 本指南涵盖常见用例以及入门方法。有关完整事件 schema、JSON 输入/输出格式,以及异步 Hooks 和 MCP Tool Hooks 等高级功能,请参阅 [Hooks reference](/en/hooks)。 </Tip>

Set up your first hook

设置第一个 Hook

The fastest way to create a hook is through the
/hooks
interactive menu in Claude Code. This walkthrough creates a desktop notification hook, so you get alerted whenever Claude is waiting for your input instead of watching the terminal.
<Steps> <Step title="Open the hooks menu"> Type `/hooks` in the Claude Code CLI. You'll see a list of all available hook events, plus an option to disable all hooks. Each event corresponds to a point in Claude's lifecycle where you can run custom code. Select `Notification` to create a hook that fires when Claude needs your attention. </Step> <Step title="Configure the matcher"> The menu shows a list of matchers, which filter when the hook fires. Set the matcher to `*` to fire on all notification types. You can narrow it later by changing the matcher to a specific value like `permission_prompt` or `idle_prompt`. </Step> <Step title="Add your command"> Select `+ Add new hook…`. The menu prompts you for a shell command to run when the event fires. Hooks run any shell command you provide, so you can use your platform's built-in notification tool. Copy the command for your OS:
<Tabs>
  <Tab title="macOS">
    Uses [`osascript`](https://ss64.com/mac/osascript.html) to trigger a native macOS notification through AppleScript:

    ```
    osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'
    ```
  </Tab>

  <Tab title="Linux">
    Uses `notify-send`, which is pre-installed on most Linux desktops with a notification daemon:

    ```
    notify-send 'Claude Code' 'Claude Code needs your attention'
    ```
  </Tab>

  <Tab title="Windows (PowerShell)">
    Uses PowerShell to show a native message box through .NET's Windows Forms:

    ```
    powershell.exe -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')"
    ```
  </Tab>
</Tabs>
</Step> <Step title="Choose a storage location"> The menu asks where to save the hook configuration. Select `User settings` to store it in `~/.claude/settings.json`, which applies the hook to all your projects. You could also choose `Project settings` to scope it to the current project. See [Configure hook location](#configure-hook-location) for all available scopes. </Step> <Step title="Test the hook"> Press `Esc` to return to the CLI. Ask Claude to do something that requires permission, then switch away from the terminal. You should receive a desktop notification. </Step> </Steps>
创建 Hook 的最快方式是通过 Claude Code 中的
/hooks
交互式菜单。本演练将创建一个桌面通知 Hook,这样每当 Claude 等待你输入时都能收到提醒,而不必一直盯着终端。
<Steps> <Step title="打开 Hooks 菜单"> 在 Claude Code CLI 中输入 `/hooks`。你会看到所有可用的 Hook 事件列表,以及禁用所有 Hooks 的选项。每个事件对应 Claude 生命周期中的一个节点,你可以在该节点运行自定义代码。选择 `Notification` 来创建一个 Hook,在 Claude 需要你关注时触发。 </Step> <Step title="配置 matcher"> 菜单会显示 matcher 列表,用于筛选 Hook 的触发条件。将 matcher 设置为 `*` 可触发所有通知类型。稍后可以将 matcher 改为 `permission_prompt` 或 `idle_prompt` 等具体值以缩小范围。 </Step> <Step title="添加命令"> 选择 `+ Add new hook…`。菜单会提示输入事件触发时要运行的 shell 命令。Hooks 可以运行你提供的任何 shell 命令,因此可以使用操作系统内置的通知工具。复制适用于你的操作系统的命令:
<Tabs>
  <Tab title="macOS">
    使用 [`osascript`](https://ss64.com/mac/osascript.html) 通过 AppleScript 触发原生 macOS 通知:

    ```
    osascript -e 'display notification "Claude Code needs your attention" with title "Claude Code"'
    ```
  </Tab>

  <Tab title="Linux">
    使用 `notify-send`,它预安装在大多数运行通知守护进程的 Linux 桌面系统上:

    ```
    notify-send 'Claude Code' 'Claude Code needs your attention'
    ```
  </Tab>

  <Tab title="Windows(PowerShell)">
    使用 PowerShell 通过 .NET 的 Windows Forms 显示原生消息框:

    ```
    powershell.exe -Command "[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code needs your attention', 'Claude Code')"
    ```
  </Tab>
</Tabs>
</Step> <Step title="选择存储位置"> 菜单会询问 Hook 配置的保存位置。选择 `User settings` 可将 Hook 存储在 `~/.claude/settings.json` 中,并应用于所有项目。也可以选择 `Project settings`,将范围限定为当前项目。有关所有可用范围,请参阅 [Configure hook location](#configure-hook-location)。 </Step> <Step title="测试 Hook"> 按 `Esc` 返回 CLI。让 Claude 执行一个需要权限的操作,然后切换到其他窗口。你应该会收到桌面通知。 </Step> </Steps>

What you can automate

可以自动化的内容

Hooks let you run code at key points in Claude Code's lifecycle: format files after edits, block commands before they execute, send notifications when Claude needs input, inject context at session start, and more. For the full list of hook events, see the Hooks reference.
Each example includes a ready-to-use configuration block that you add to a settings file. The most common patterns:
Hooks 可以在 Claude Code 生命周期的关键节点运行代码,例如在编辑后格式化文件、在执行命令前阻止命令、在 Claude 需要输入时发送通知、在会话开始时注入上下文等。有关完整 Hook 事件列表,请参阅 Hooks reference。
每个示例都包含一个可直接使用的配置块,你需要将其添加到 settings 文件中。最常见的模式包括:

Get notified when Claude needs input

在 Claude 需要输入时收到通知

Get a desktop notification whenever Claude finishes working and needs your input, so you can switch to other tasks without checking the terminal.
This hook uses the
Notification
event, which fires when Claude is waiting for input or permission. Each tab below uses the platform's native notification command. Add this to
~/.claude/settings.json
, or use the interactive walkthrough above to configure it with
/hooks
:
<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\"'" } ] } ] } } ``` </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>
每当 Claude 完成工作并需要你输入时都显示桌面通知,这样你就可以切换到其他任务,而无需检查终端。
此 Hook 使用
Notification
事件,该事件会在 Claude 等待输入或权限时触发。下方每个选项卡都使用对应平台的原生通知命令。将此配置添加到
~/.claude/settings.json
,或使用上方的交互式演练通过
/hooks
进行配置:
<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\"'" } ] } ] } } ``` </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>

Auto-format code after edits

编辑后自动格式化代码

Automatically run Prettier on every file Claude edits, so formatting stays consistent without manual intervention.
This hook uses the
PostToolUse
event with an
Edit|Write
matcher, so it runs only after file-editing tools. The command extracts the edited file path with
jq
and passes it to Prettier. Add this to
.claude/settings.json
in your project root:
json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write"
          }
        ]
      }
    ]
  }
}
<Note> The Bash examples on this page use `jq` for JSON parsing. Install it with `brew install jq` (macOS), `apt-get install jq` (Debian/Ubuntu), or see [`jq` downloads](https://jqlang.github.io/jq/download/). </Note>
在 Claude 编辑每个文件后自动运行 Prettier,确保格式保持一致而无需手动干预。
此 Hook 使用
PostToolUse
事件和
Edit|Write
matcher,因此只会在文件编辑工具运行后触发。命令通过
jq
提取已编辑文件的路径,并将其传递给 Prettier。将此配置添加到项目根目录的
.claude/settings.json
:
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` 下载页面](https://jqlang.github.io/jq/download/)。 </Note>

Block edits to protected files

阻止编辑受保护的文件

Prevent Claude from modifying sensitive files like
.env
,
package-lock.json
, or anything in
.git/
. Claude receives feedback explaining why the edit was blocked, so it can adjust its approach.
This example uses a separate script file that the hook calls. The script checks the target file path against a list of protected patterns and exits with code 2 to block the edit.
<Steps> <Step title="Create the hook script"> Save this to `.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="Make the script executable (macOS/Linux)"> Hook scripts must be executable for Claude Code to run them:
```bash  theme={null}
chmod +x .claude/hooks/protect-files.sh
```
</Step> <Step title="Register the hook"> Add a `PreToolUse` hook to `.claude/settings.json` that runs the script before any `Edit` or `Write` tool call:
```json  theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}
```
</Step> </Steps>
阻止 Claude 修改
.env
、
package-lock.json
或
.git/
中的内容等敏感文件。Claude 会收到说明阻止原因的反馈,从而能够调整处理方式。
此示例使用 Hook 调用的独立脚本文件。脚本会将目标文件路径与受保护模式列表进行比较,并以退出码 2 退出以阻止编辑。
<Steps> <Step title="创建 Hook 脚本"> 将以下内容保存为 `.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 要运行 Hook 脚本,脚本必须具有可执行权限:
```bash  theme={null}
chmod +x .claude/hooks/protect-files.sh
```
</Step> <Step title="注册 Hook"> 在 `.claude/settings.json` 中添加 `PreToolUse` Hook,以便在调用任何 `Edit` 或 `Write` 工具前运行脚本:
```json  theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}
```
</Step> </Steps>

Re-inject context after compaction

压缩后重新注入上下文

When Claude's context window fills up, compaction summarizes the conversation to free space. This can lose important details. Use a
SessionStart
hook with a
compact
matcher to re-inject critical context after every compaction.
Any text your command writes to stdout is added to Claude's context. This example reminds Claude of project conventions and recent work. Add this to
.claude/settings.json
in your project root:
json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Reminder: use Bun, not npm. Run bun test before committing. Current sprint: auth refactor.'"
          }
        ]
      }
    ]
  }
}
You can replace the
echo
with any command that produces dynamic output, like
git log --oneline -5
to show recent commits. For injecting context on every session start, consider using CLAUDE.md instead. For environment variables, see
CLAUDE_ENV_FILE
in the reference.
当 Claude 的上下文窗口填满时,压缩功能会总结对话以释放空间。这可能导致重要细节丢失。使用带有
compact
matcher 的
SessionStart
Hook,可以在每次压缩后重新注入关键上下文。
命令写入 stdout 的任何文本都会添加到 Claude 的上下文中。此示例用于提醒 Claude 了解项目约定和最近的工作。将此配置添加到项目根目录的
.claude/settings.json
:
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
。

How hooks work

Hooks 的工作方式

Hook events fire at specific lifecycle points in Claude Code. When an event fires, all matching hooks run in parallel, and identical hook commands are automatically deduplicated. The table below shows each event and when it triggers:
EventWhen it fires
SessionStart
When a session begins or resumes
UserPromptSubmit
When you submit a prompt, before Claude processes it
PreToolUse
Before a tool call executes. Can block it
PermissionRequest
When a permission dialog appears
PostToolUse
After a tool call succeeds
PostToolUseFailure
After a tool call fails
Notification
When Claude Code sends a notification
SubagentStart
When a subagent is spawned
SubagentStop
When a subagent finishes
Stop
When Claude finishes responding
PreCompact
Before context compaction
SessionEnd
When a session terminates
Each hook has a
type
that determines how it runs. Most hooks use
"type": "command"
, which runs a shell command. Two other options use a Claude model to make decisions:
"type": "prompt"
for single-turn evaluation and
"type": "agent"
for multi-turn verification with tool access. See Prompt-based hooks and Agent-based hooks for details.
Hook 事件会在 Claude Code 生命周期的特定节点触发。事件触发时,所有匹配的 Hooks 都会并行运行,完全相同的 Hook 命令会被自动去重。下表列出了每个事件及其触发时机:
事件触发时机
SessionStart
会话开始或恢复时
UserPromptSubmit
提交 prompt 后、Claude 处理之前
PreToolUse
工具调用执行之前,可以阻止调用
PermissionRequest
权限对话框出现时
PostToolUse
工具调用成功后
PostToolUseFailure
工具调用失败后
Notification
Claude Code 发送通知时
SubagentStart
创建 subagent 时
SubagentStop
subagent 完成时
Stop
Claude 完成响应时
PreCompact
上下文压缩之前
SessionEnd
Claude Code 会话终止时
每个 Hook 都有一个决定其运行方式的
type
。大多数 Hooks 使用
"type": "command"
运行 shell 命令。另有两个选项使用 Claude 模型作出决策:用于单轮评估的
"type": "prompt"
,以及用于支持工具访问的多轮验证的
"type": "agent"
。有关详细信息,请参阅 Prompt-based hooks和 Agent-based hooks。

Read input and return output

读取输入并返回输出

Hooks communicate with Claude Code through stdin, stdout, stderr, and exit codes. When an event fires, Claude Code passes event-specific data as JSON to your script's stdin. Your script reads that data, does its work, and tells Claude Code what to do next via the exit code.
Hooks 通过 stdin、stdout、stderr 和退出码与 Claude Code 通信。事件触发时,Claude Code 会将特定于事件的数据作为 JSON 传递给脚本的 stdin。脚本读取数据、执行工作,并通过退出码告知 Claude Code 下一步应该执行什么。

Hook input

Hook 输入

Every event includes common fields like
session_id
and
cwd
, but each event type adds different data. For example, when Claude runs a Bash command, a
PreToolUse
hook receives something like this on stdin:
json
{
  "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
  }
}
Your script can parse that JSON and act on any of those fields.
UserPromptSubmit
hooks get the
prompt
text instead,
SessionStart
hooks get the
source
(startup, resume, compact), and so on. See Common input fields in the reference for shared fields, and each event's section for event-specific schemas.
每个事件都包含
session_id
、
cwd
等通用字段,但不同事件类型会添加不同的数据。例如,当 Claude 运行 Bash 命令时,
PreToolUse
Hook 可能会在 stdin 中收到以下内容:
json
{
  "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
Hooks 获得
prompt
文本,
SessionStart
Hooks 获得
source
(startup、resume、compact),依此类推。有关共享字段,请参阅参考文档中的 Common input fields;有关事件专用 schema,请参阅每个事件的对应章节。

Hook output

Hook 输出

Your script tells Claude Code what to do next by writing to stdout or stderr and exiting with a specific code. For example, a
PreToolUse
hook that wants to block a command:
bash
#!/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 = let it proceed
The exit code determines what happens next:
  • Exit 0: the action proceeds. For
    UserPromptSubmit
    and
    SessionStart
    hooks, anything you write to stdout is added to Claude's context.
  • Exit 2: the action is blocked. Write a reason to stderr, and Claude receives it as feedback so it can adjust.
  • Any other exit code: the action proceeds. Stderr is logged but not shown to Claude. Toggle verbose mode with
    Ctrl+O
    to see these messages in the transcript.
脚本通过写入 stdout 或 stderr 并以特定代码退出来告知 Claude Code 下一步应该执行什么。例如,以下
PreToolUse
Hook 会阻止某条命令:
bash
#!/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 = let it proceed
退出码决定接下来发生什么:
  • 退出 0:操作继续。对于
    UserPromptSubmit
    和
    SessionStart
    Hooks,写入 stdout 的任何内容都会添加到 Claude 的上下文中。
  • 退出 2:操作被阻止。将原因写入 stderr,Claude 会将其作为反馈并据此调整。
  • 其他退出码:操作继续。stderr 会被记录,但不会展示给 Claude。按
    Ctrl+O
    可切换详细模式以查看这些消息。

Structured JSON output

结构化 JSON 输出

Exit codes give you two options: allow or block. For more control, exit 0 and print a JSON object to stdout instead.
<Note> Use exit 2 to block with a stderr message, or exit 0 with JSON for structured control. Don't mix them: Claude Code ignores JSON when you exit 2. </Note>
For example, a
PreToolUse
hook can deny a tool call and tell Claude why, or escalate it to the user for approval:
json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep for better performance"
  }
}
Claude Code reads
permissionDecision
and cancels the tool call, then feeds
permissionDecisionReason
back to Claude as feedback. These three options are specific to
PreToolUse
:
  • "allow"
    : proceed without showing a permission prompt
  • "deny"
    : cancel the tool call and send the reason to Claude
  • "ask"
    : show the permission prompt to the user as normal
Other events use different decision patterns. For example,
PostToolUse
and
Stop
hooks use a top-level
decision: "block"
field, while
PermissionRequest
uses
hookSpecificOutput.decision.behavior
. See the summary table in the reference for a full breakdown by event.
For
UserPromptSubmit
hooks, use
additionalContext
instead to inject text into Claude's context. Prompt-based hooks (
type: "prompt"
) handle output differently: see Prompt-based hooks.
退出码只有允许或阻止两种选择。若需要更精细的控制,可以退出 0 并将 JSON 对象输出到 stdout。
<Note> 使用退出码 2 和 stderr 消息阻止操作,或使用退出 0 和 JSON 进行结构化控制。不要混用:退出 2 时 Claude Code 会忽略 JSON。 </Note>
例如,
PreToolUse
Hook 可以拒绝工具调用并告知 Claude 原因,也可以将请求升级给用户审批:
json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep for better performance"
  }
}
Claude Code 读取
permissionDecision
并取消工具调用,然后将
permissionDecisionReason
作为反馈传回 Claude。以下三个选项专用于
PreToolUse
:
  • "allow"
    :继续执行且不显示权限提示
  • "deny"
    :取消工具调用并向 Claude 发送原因
  • "ask"
    :像平常一样向用户显示权限提示
其他事件使用不同的决策模式。例如,
PostToolUse
和
Stop
Hooks 使用顶层
decision: "block"
字段,而
PermissionRequest
使用
hookSpecificOutput.decision.behavior
。完整概览请参阅参考文档中的汇总表。
对于
UserPromptSubmit
Hooks,请改用
additionalContext
将文本注入 Claude 的上下文。基于 prompt 的 Hooks(
type: "prompt"
)使用不同的输出方式,详见 Prompt-based hooks。

Filter hooks with matchers

使用 matcher 过滤 Hooks

Without a matcher, a hook fires on every occurrence of its event. Matchers let you narrow that down. For example, if you want to run a formatter only after file edits (not after every tool call), add a matcher to your
PostToolUse
hook:
json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write ..." }
        ]
      }
    ]
  }
}
The
"Edit|Write"
matcher is a regex pattern that matches the tool name. The hook only fires when Claude uses the
Edit
or
Write
tool, not when it uses
Bash
,
Read
, or any other tool.
Each event type matches on a specific field. Matchers support exact strings and regex patterns:
EventWhat the matcher filtersExample matcher values
PreToolUse
,
PostToolUse
,
PostToolUseFailure
,
PermissionRequest
tool name
Bash
,
Edit|Write
,
mcp__.*
SessionStart
how the session started
startup
,
resume
,
clear
,
compact
SessionEnd
why the session ended
clear
,
logout
,
prompt_input_exit
,
other
Notification
notification type
permission_prompt
,
idle_prompt
,
auth_success
,
elicitation_dialog
SubagentStart
agent type
Bash
,
Explore
,
Plan
, or custom agent names
PreCompact
what triggered compaction
manual
,
auto
UserPromptSubmit
,
Stop
no matcher supportalways fires on every occurrence
SubagentStop
agent typesame values as
SubagentStart
A few more examples showing matchers on different event types:
<Tabs> <Tab title="Log every Bash command"> Match only `Bash` tool calls and log each command to a file. The `PostToolUse` event fires after the command completes, so `tool_input.command` contains what ran. The hook receives the event data as JSON on stdin, and `jq -r '.tool_input.command'` extracts just the command string, which `>>` appends to the log file:
```json  theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt"
          }
        ]
      }
    ]
  }
}
```
</Tab> <Tab title="Match MCP tools"> MCP tools use a different naming convention than built-in tools: `mcp__<server>__<tool>`, where `<server>` is the MCP server name and `<tool>` is the tool it provides. For example, `mcp__github__search_repositories` or `mcp__filesystem__read_file`. Use a regex matcher to target all tools from a specific server, or match across servers with a pattern like `mcp__.*__write.*`. See [Match MCP tools](/en/hooks#match-mcp-tools) in the reference for the full list of examples.
The command below extracts the tool name from the hook's JSON input with `jq` and writes it to stderr, where it shows up in verbose mode (`Ctrl+O`):

```json  theme={null}
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"GitHub tool called: $(jq -r '.tool_name')\" >&2"
          }
        ]
      }
    ]
  }
}
```
</Tab> <Tab title="Clean up on session end"> The `SessionEnd` event supports matchers on the reason the session ended. This hook only fires on `clear` (when you run `/clear`), not on normal exits:
```json  theme={null}
{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "rm -f /tmp/claude-scratch-*.txt"
          }
        ]
      }
    ]
  }
}
```
</Tab> </Tabs>
For full matcher syntax, see the Hooks reference.
不使用 matcher 时,每次出现对应事件都会触发 Hook。Matchers 可以缩小触发范围。例如,如果希望只在编辑文件后运行格式化程序,可以为
PostToolUse
Hook 添加 matcher:
json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          { "type": "command", "command": "prettier --write ..." }
        ]
      }
    ]
  }
}
"Edit|Write"
matcher 是匹配工具名称的正则表达式。只有 Claude 使用
Edit
或
Write
工具时才会触发 Hook;使用
Bash
、
Read
或其他工具时不会触发。
每种事件类型匹配特定字段。Matchers 支持精确字符串和正则表达式模式:
事件matcher 筛选的内容matcher 示例值
PreToolUse
、
PostToolUse
、
PostToolUseFailure
、
PermissionRequest
工具名称
Bash
、
Edit|Write
、
mcp__.*
SessionStart
会话启动方式
startup
、
resume
、
clear
、
compact
SessionEnd
会话结束原因
clear
、
logout
、
prompt_input_exit
、
other
Notification
通知类型
permission_prompt
、
idle_prompt
、
auth_success
、
elicitation_dialog
SubagentStart
Agent 类型
Bash
、
Explore
、
Plan
或自定义 Agent 名称
PreCompact
触发压缩的原因
manual
、
auto
UserPromptSubmit
、
Stop
不支持 matcher每次出现时始终触发
SubagentStop
Agent 类型与
SubagentStart
相同
下面给出了不同事件类型 matcher 的更多示例:
<Tabs> <Tab title="记录每条 Bash 命令"> 仅匹配 `Bash` 工具调用,并将每条命令记录到文件。`PostToolUse` 事件在命令完成后触发,因此 `tool_input.command` 中包含已执行的命令。Hook 通过 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 server 名称,`<tool>` 是其提供的工具。例如 `mcp__github__search_repositories` 或 `mcp__filesystem__read_file`。使用正则表达式 matcher 可以定位特定 server 的所有工具,或使用 `mcp__.*__write.*` 等模式跨 server 匹配。有关完整示例列表,请参阅参考文档中的 [Match MCP tools](/en/hooks#match-mcp-tools)。
以下命令使用 `jq` 从 Hook 的 JSON 输入中提取工具名称,并将其写入 stderr;该内容会显示在详细模式(`Ctrl+O`)中:

```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` 事件支持按会话结束原因匹配。此 Hook 只会在执行 `/clear` 时触发,不会在正常退出时触发:
```json  theme={null}
{
  "hooks": {
    "SessionEnd": [
      {
        "matcher": "clear",
        "hooks": [
          {
            "type": "command",
            "command": "rm -f /tmp/claude-scratch-*.txt"
          }
        ]
      }
    ]
  }
}
```
</Tab> </Tabs>
有关完整的 matcher 语法,请参阅 Hooks reference。

Configure hook location

配置 Hook 位置

Where you add a hook determines its scope:
LocationScopeShareable
~/.claude/settings.json
All your projectsNo, local to your machine
.claude/settings.json
Single projectYes, can be committed to the repo
.claude/settings.local.json
Single projectNo, gitignored
Managed policy settingsOrganization-wideYes, admin-controlled
Plugin
hooks/hooks.json
When plugin is enabledYes, bundled with the plugin
Skill or agent frontmatterWhile the skill or agent is activeYes, defined in the component file
You can also use the
/hooks
menu
in Claude Code to add, delete, and view hooks interactively. To disable all hooks at once, use the toggle at the bottom of the
/hooks
menu or set
"disableAllHooks": true
in your settings file.
Hooks added through the
/hooks
menu take effect immediately. If you edit settings files directly while Claude Code is running, the changes won't take effect until you review them in the
/hooks
menu or restart your session.
添加 Hook 的位置决定其作用域:
位置作用域可共享
~/.claude/settings.json
所有项目否,仅限本机
.claude/settings.json
单个项目是,可以提交到仓库
.claude/settings.local.json
单个项目否,已被 Git 忽略
托管策略设置组织范围是,由管理员控制
Plugin
hooks/hooks.json
启用 Plugin 时是,随 Plugin 一起分发
Skill 或 agent frontmatterSkill 或 Agent 处于活动状态时是,在组件文件中定义
还可以使用 Claude Code 中的
/hooks
菜单
交互式添加、删除和查看 Hooks。要一次性禁用所有 Hooks,请使用菜单底部的开关,或在 settings 文件中设置
"disableAllHooks": true
。
通过
/hooks
菜单添加的 Hooks 会立即生效。如果在 Claude Code 运行时直接编辑 settings 文件,则需要在
/hooks
菜单中查看更改或重启会话后才会生效。

Prompt-based hooks

基于 prompt 的 Hooks

For decisions that require judgment rather than deterministic rules, use
type: "prompt"
hooks. Instead of running a shell command, Claude Code sends your prompt and the hook's input data to a Claude model (Haiku by default) to make the decision. You can specify a different model with the
model
field if you need more capability.
The model's only job is to return a yes/no decision as JSON:
  • "ok": true
    : the action proceeds
  • "ok": false
    : the action is blocked. The model's
    "reason"
    is fed back to Claude so it can adjust.
This example uses a
Stop
hook to ask the model whether all requested tasks are complete. If the model returns
"ok": false
, Claude keeps working and uses the
reason
as its next instruction:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}
For full configuration options, see Prompt-based hooks in the reference.
对于需要判断而非确定性规则的决策,请使用
type: "prompt"
Hooks。Claude Code 不会运行 shell 命令,而是将你的 prompt 和 Hook 输入数据发送给 Claude 模型(默认使用 Haiku)作出决策。如果需要更强的能力,可以通过
model
字段指定其他模型。
模型唯一的任务是返回 yes/no 决策 JSON:
  • "ok": true
    :操作继续
  • "ok": false
    :操作被阻止。模型的
    "reason"
    会反馈给 Claude,以便其调整。
此示例使用
Stop
Hook 让模型判断所有请求的任务是否已经完成。如果模型返回
"ok": false"
,Claude 会继续工作,并将
reason
作为下一条指令:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Check if all tasks are complete. If not, respond with {\"ok\": false, \"reason\": \"what remains to be done\"}."
          }
        ]
      }
    ]
  }
}
有关完整的配置选项,请参阅参考文档中的 Prompt-based hooks。

Agent-based hooks

基于 Agent 的 Hooks

When verification requires inspecting files or running commands, use
type: "agent"
hooks. Unlike prompt hooks which make a single LLM call, agent hooks spawn a subagent that can read files, search code, and use other tools to verify conditions before returning a decision.
Agent hooks use the same
"ok"
/
"reason"
response format as prompt hooks, but with a longer default timeout of 60 seconds and up to 50 tool-use turns.
This example verifies that tests pass before allowing Claude to stop:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}
Use prompt hooks when the hook input data alone is enough to make a decision. Use agent hooks when you need to verify something against the actual state of the codebase.
For full configuration options, see Agent-based hooks in the reference.
当验证需要检查文件或运行命令时,请使用
type: "agent"
Hooks。与只发起单次 LLM 调用的 prompt Hooks 不同,Agent Hooks 会创建 subagent,使其能够读取文件、搜索代码并使用其他工具验证条件,然后返回决策。
Agent Hooks 使用与 prompt Hooks 相同的
"ok"
/
"reason"
响应格式,但默认超时时间为 60 秒,最多可使用 50 轮工具。
此示例验证所有测试是否通过,然后才允许 Claude 停止:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}
当仅依靠 Hook 输入数据就足以作出决策时,使用 prompt Hooks;当需要根据代码库的实际状态进行验证时,使用 Agent Hooks。
有关完整的配置选项,请参阅参考文档中的 Agent-based hooks。

Limitations and troubleshooting

限制与故障排除

Limitations

限制

  • Hooks communicate through stdout, stderr, and exit codes only. They cannot trigger slash commands or tool calls directly.
  • Hook timeout is 10 minutes by default, configurable per hook with the
    timeout
    field (in seconds).
  • PostToolUse
    hooks cannot undo actions since the tool has already executed.
  • PermissionRequest
    hooks do not fire in non-interactive mode (
    -p
    ). Use
    PreToolUse
    hooks for automated permission decisions.
  • Stop
    hooks fire whenever Claude finishes responding, not only at task completion. They do not fire on user interrupts.
  • Hooks 仅通过 stdout、stderr 和退出码通信,不能直接触发 slash command 或工具调用。
  • Hook 默认超时时间为 10 分钟,可以通过每个 Hook 的
    timeout
    字段以秒为单位配置。
  • PostToolUse
    Hooks 无法撤销操作,因为工具已经执行。
  • PermissionRequest
    Hooks 在非交互模式(
    -p
    )下不会触发。请使用
    PreToolUse
    Hooks 进行自动化权限决策。
  • Stop
    Hooks 会在 Claude 每次完成响应时触发,而不仅是在任务完成时触发。用户中断时不会触发。

Hook not firing

Hook 未触发

The hook is configured but never executes.
  • Run
    /hooks
    and confirm the hook appears under the correct event
  • Check that the matcher pattern matches the tool name exactly (matchers are case-sensitive)
  • Verify you're triggering the right event type (e.g.,
    PreToolUse
    fires before tool execution,
    PostToolUse
    fires after)
  • If using
    PermissionRequest
    hooks in non-interactive mode (
    -p
    ), switch to
    PreToolUse
    instead
Hook 已配置,但从未执行。
  • 运行
    /hooks
    ,确认 Hook 出现在正确的事件下
  • 检查 matcher 模式是否与工具名称完全匹配(matchers 区分大小写)
  • 验证是否触发了正确的事件类型(例如,
    PreToolUse
    在工具执行前触发,
    PostToolUse
    在工具执行后触发)
  • 如果在非交互模式(
    -p
    )下使用
    PermissionRequest
    Hooks,请改用
    PreToolUse

Hook error in output

输出中出现 Hook 错误

You see a message like "PreToolUse hook error: ..." in the transcript.
  • Your script exited with a non-zero code unexpectedly. Test it manually by piping sample JSON:
    bash
    echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
    echo $?  # Check the exit code
  • If you see "command not found", use absolute paths or
    $CLAUDE_PROJECT_DIR
    to reference scripts
  • If you see "jq: command not found", install
    jq
    or use Python/Node.js for JSON parsing
  • If the script isn't running at all, make it executable:
    chmod +x ./my-hook.sh
在记录中出现类似“PreToolUse hook error: ...”的消息。
  • 脚本意外地以非零代码退出。通过传入示例 JSON 手动测试:
    bash
    echo '{"tool_name":"Bash","tool_input":{"command":"ls"}}' | ./my-hook.sh
    echo $?  # Check the exit code
  • 如果出现“command not found”,请使用绝对路径,或使用
    $CLAUDE_PROJECT_DIR
    引用脚本
  • 如果出现“jq: command not found”,请安装
    jq
    ,或使用 Python/Node.js 解析 JSON
  • 如果脚本完全没有运行,请使其可执行:
    chmod +x ./my-hook.sh

/hooks
shows no hooks configured

/hooks
未显示已配置的 Hooks

You edited a settings file but the hooks don't appear in the menu.
  • Restart your session or open
    /hooks
    to reload. Hooks added through the
    /hooks
    menu take effect immediately, but manual file edits require a reload.
  • Verify your JSON is valid (trailing commas and comments are not allowed)
  • Confirm the settings file is in the correct location:
    .claude/settings.json
    for project hooks,
    ~/.claude/settings.json
    for global hooks
你编辑了 settings 文件,但 Hooks 没有出现在菜单中。
  • 重启会话或打开
    /hooks
    重新加载。通过
    /hooks
    菜单添加的 Hooks 会立即生效,但手动编辑文件需要重新加载。
  • 验证 JSON 是否有效(不允许尾随逗号和注释)
  • 确认 settings 文件位置正确:项目 Hooks 使用
    .claude/settings.json
    ,全局 Hooks 使用
    ~/.claude/settings.json

Stop hook runs forever

Stop Hook 无限运行

Claude keeps working in an infinite loop instead of stopping.
Your Stop hook script needs to check whether it already triggered a continuation. Parse the
stop_hook_active
field from the JSON input and exit early if it's
true
:
bash
#!/bin/bash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0  # Allow Claude to stop
fi
Claude 持续工作并陷入无限循环,而不是停止。
你的 Stop Hook 脚本需要检查它是否已经触发过一次继续操作。解析 JSON 输入中的
stop_hook_active
字段,如果值为
true
,则提前退出:
bash
#!/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

... rest of your hook logic

undefined
undefined

JSON validation failed

JSON 验证失败

Claude Code shows a JSON parsing error even though your hook script outputs valid JSON.
When Claude Code runs a hook, it spawns a shell that sources your profile (
~/.zshrc
or
~/.bashrc
). If your profile contains unconditional
echo
statements, that output gets prepended to your hook's JSON:
Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}
Claude Code tries to parse this as JSON and fails. To fix this, wrap echo statements in your shell profile so they only run in interactive shells:
bash
undefined
即使 Hook 脚本输出的是有效 JSON,Claude Code 仍显示 JSON 解析错误。
Claude Code 运行 Hook 时会启动一个加载你的配置文件(
~/.zshrc
或
~/.bashrc
)的 shell。如果配置文件中包含无条件执行的
echo
语句,其输出会被添加到 Hook JSON 前面:
Shell ready on arm64
{"decision": "block", "reason": "Not allowed"}
Claude Code 会尝试将此内容解析为 JSON 并失败。要解决此问题,请将 echo 语句放入 shell 配置文件的交互模式条件中:
bash
undefined

In ~/.zshrc or ~/.bashrc

In ~/.zshrc or ~/.bashrc

if [[ $- == i ]]; then echo "Shell ready" fi

The `$-` variable contains shell flags, and `i` means interactive. Hooks run in non-interactive shells, so the echo is skipped.
if [[ $- == i ]]; then echo "Shell ready" fi

`$-` 变量包含 shell 标志,其中 `i` 表示交互模式。Hooks 在非交互 shell 中运行,因此会跳过 echo。

Debug techniques

调试技巧

Toggle verbose mode with
Ctrl+O
to see hook output in the transcript, or run
claude --debug
for full execution details including which hooks matched and their exit codes.
按
Ctrl+O
切换详细模式以在记录中查看 Hook 输出,或运行
claude --debug
查看完整的执行详情,包括哪些 Hooks 匹配及其退出码。

Learn more

了解更多


Documentation Index

Fetch the complete documentation index at: https://code.claude.com/docs/llms.txt Use this file to discover all available pages before exploring further.

文档索引

在 https://code.claude.com/docs/llms.txt 获取完整的文档索引 在进一步探索之前,使用此文件发现所有可用页面。

Hooks reference

Hooks 参考

Reference for Claude Code hook events, configuration schema, JSON input/output formats, exit codes, async hooks, prompt hooks, and MCP tool hooks.
<Tip> For a quickstart guide with examples, see [Automate workflows with hooks](/en/hooks-guide). </Tip>
Hooks are user-defined shell commands or LLM prompts that execute automatically at specific points in Claude Code's lifecycle. Use this reference to look up event schemas, configuration options, JSON input/output formats, and advanced features like async hooks and MCP tool hooks. If you're setting up hooks for the first time, start with the guide instead.
Claude Code Hook 事件、配置 schema、JSON 输入/输出格式、退出码、异步 Hooks、prompt Hooks 和 MCP Tool Hooks 的参考文档。
<Tip> 有关包含示例的快速入门指南,请参阅 [Automate workflows with hooks](/en/hooks-guide)。 </Tip>
Hooks 是用户定义的 shell 命令或 LLM prompts,会在 Claude Code 生命周期的特定节点自动执行。使用本参考文档查询事件 schema、配置选项、JSON 输入/输出格式,以及异步 Hooks 和 MCP Tool Hooks 等高级功能。如果首次设置 Hooks,请先阅读指南。

Hook lifecycle

Hook 生命周期

Hooks fire at specific points during a Claude Code session. When an event fires and a matcher matches, Claude Code passes JSON context about the event to your hook handler. For command hooks, this arrives on stdin. Your handler can then inspect the input, take action, and optionally return a decision. Some events fire once per session, while others fire repeatedly inside the agentic loop:
<div style={{maxWidth: "500px", margin: "0 auto"}}> <Frame> <img src="https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=5c25fedbc3db6f8882af50c3cc478c32" alt="Hook lifecycle diagram showing the sequence of hooks from SessionStart through the agentic loop to SessionEnd" data-og-width="8876" width="8876" data-og-height="12492" height="12492" data-path="images/hooks-lifecycle.png" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=280&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=62406fcd5d4a189cc8842ee1bd946b84 280w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=560&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=fa3049022a6973c5f974e0f95b28169d 560w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=840&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=bd2890897db61a03160b93d4f972ff8e 840w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=1100&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=7ae8e098340479347135e39df4a13454 1100w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=1650&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=848a8606aab22c2ccaa16b6a18431e32 1650w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=2500&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=f3a9ef7feb61fa8fe362005aa185efbc 2500w" /> </Frame> </div>
The table below summarizes when each event fires. The Hook events section documents the full input schema and decision control options for each one.
EventWhen it fires
SessionStart
When a session begins or resumes
UserPromptSubmit
When you submit a prompt, before Claude processes it
PreToolUse
Before a tool call executes. Can block it
PermissionRequest
When a permission dialog appears
PostToolUse
After a tool call succeeds
PostToolUseFailure
After a tool call fails
Notification
When Claude Code sends a notification
SubagentStart
When a subagent is spawned
SubagentStop
When a subagent finishes
Stop
When Claude finishes responding
PreCompact
Before context compaction
SessionEnd
When a session terminates
Hooks 会在 Claude Code 会话生命周期的特定节点触发。事件触发且 matcher 匹配后,Claude Code 会将事件相关 JSON 上下文传递给 Hook 处理器。对于 command Hooks,这些数据通过 stdin 传入。处理器随后可以检查输入、执行操作,并选择返回决策。有些事件每个会话只触发一次,而其他事件会在 Agent 循环中反复触发:
<div style={{maxWidth: "500px", margin: "0 auto"}}> <Frame> <img src="https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=c5c25fedbc3db6f8882af50c3cc478c32" alt="展示从 SessionStart 到 Agent 循环再到 SessionEnd 的 Hook 生命周期顺序图" data-og-width="8876" width="8876" height="12492" data-path="images/hooks-lifecycle.png" data-optimize="true" data-og-height="12492" srcset="https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=280&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=24a9ee15b19b0c4ba05b6d62ec6ec545 280w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=560&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=cbce02757f1ea14d2a49e8b460e8c1d5 560w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=880&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=adb6d6f90c1a16720c9be1073b114b3a 880w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=1200&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=e7022e313b3c1663f4f1f3bd0c43a3d 1200w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=1600&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=4c1bf2dd3f23e8c67dfd2fc1b4a67da8 1600w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=2000&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=7686a37a293ce5f68a9946a64af8f7c7c 2000w, https://mintcdn.com/claude-code/z2YM37Ycg6eMbID3/images/hooks-lifecycle.png?w=2800&fit=max&auto=format&n=z2YM37Ycg6eMbID3&q=85&s=1d1f7c89d2b52c4f0769e7ce4eab4f6 2800w" /> </Frame> </div>
下表汇总了每个事件的触发时机。Hook events章节记录了每个事件的完整输入 schema 和决策控制选项。
事件触发时机
SessionStart
会话开始或恢复时
UserPromptSubmit
提交 prompt 后、Claude 处理之前
PreToolUse
工具调用执行之前,可以阻止调用
PermissionRequest
权限对话框出现时
PostToolUse
工具调用成功后
PostToolUseFailure
工具调用失败后
Notification
Claude Code 发送通知时
SubagentStart
创建 subagent 时
SubagentStop
subagent 完成时
Stop
Claude 完成响应时
PreCompact
上下文压缩之前
SessionEnd
Claude Code 会话终止时

How a hook resolves

Hook 的解析方式

To see how these pieces fit together, consider this
PreToolUse
hook that blocks destructive shell commands. The hook runs
block-rm.sh
before every Bash tool call:
json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}
The script reads the JSON input from stdin, extracts the command, and returns a
permissionDecision
of
"deny"
if it contains
rm -rf
:
bash
#!/bin/bash
为了了解这些部分如何协同工作,请考虑以下阻止破坏性 shell 命令的
PreToolUse
Hook。该 Hook 会在每次 Bash 工具调用前运行
block-rm.sh
:
json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}
脚本从 stdin 读取 JSON 输入、提取命令,并在命令包含
rm -rf
时返回
"deny"
形式的
permissionDecision
:
bash
#!/bin/bash

.claude/hooks/block-rm.sh

.claude/hooks/block-rm.sh

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

Now suppose Claude Code decides to run `Bash "rm -rf /tmp/build"`. Here's what happens:

<Frame>
  <img src="https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=7c13f51ffcbc37d22a593b27e2f2de72" alt="Hook resolution flow: PreToolUse event fires, matcher checks for Bash match, hook handler runs, result returns to Claude Code" data-og-width="780" width="780" data-og-height="290" height="290" data-path="images/hook-resolution.svg" data-optimize="true" data-opv="3" srcset="https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=280&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=36a39a07e8bc1995dcb4639e09846905 280w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=560&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=6568d90c596c7605bbac2c325b0a0c86 560w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=840&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=255a6f68b9475a0e41dbde7b88002dad 840w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=1100&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=dcecf8d5edc88cd2bc49deb006d5760d 1100w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=1650&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=04fe51bf69ae375e9fd517f18674e35f 1650w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=2500&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=b1b76e0b77fddb5c7fa7bf302dacd80b 2500w" />
</Frame>

<Steps>
  <Step title="Event fires">
    The `PreToolUse` event fires. Claude Code sends the tool input as JSON on stdin to the hook:

    ```json  theme={null}
    { "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
    ```
  </Step>

  <Step title="Matcher checks">
    The matcher `"Bash"` matches the tool name, so `block-rm.sh` runs. If you omit the matcher or use `"*"`, the hook runs on every occurrence of the event. Hooks only skip when a matcher is defined and doesn't match.
  </Step>

  <Step title="Hook handler runs">
    The script extracts `"rm -rf /tmp/build"` from the input and finds `rm -rf`, so it prints a decision to stdout:

    ```json  theme={null}
    {
      "hookSpecificOutput": {
        "hookEventName": "PreToolUse",
        "permissionDecision": "deny",
        "permissionDecisionReason": "Destructive command blocked by hook"
      }
    }
    ```

    If the command had been safe (like `npm test`), the script would hit `exit 0` instead, which tells Claude Code to allow the tool call with no further action.
  </Step>

  <Step title="Claude Code acts on the result">
    Claude Code reads the JSON decision, blocks the tool call, and shows Claude the reason.
  </Step>
</Steps>

The [Configuration](#configuration) section below documents the full schema, and each [hook event](#hook-events) section documents what input your command receives and what output it can return.
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "Destructive command blocked by hook" } }' else exit 0 # allow the command fi

现在假设 Claude Code 决定运行 `Bash` 工具并执行 `rm -rf /tmp/build`,将会发生以下情况:

<Frame>
  <img src="https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=3b30c34a8e4b04fe31a6e40ecf3f4987" alt="Hook 解析流程:PreToolUse 事件触发、matcher 检查 Bash 匹配、Hook 处理器运行,并将结果返回给 Claude Code" data-og-width="780" width="780" height="290" data-path="images/hook-resolution.svg" data-optimize="true" data-og-height="290" srcset="https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=280&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=9e78b9dba7fd59d87a0e6b87b465ae3 280w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=560&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=baf15d481250252aedbe6ce5b28767d 560w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=840&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=3d12ee7fd1822cd57164a912674b804b 840w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=1120&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=e5ba1a66fce44653b6351f7710a3bb29 1120w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=1400&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=9c72fc4fd5c9d5200c35a7b3f3d31d3 1400w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=1680&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=3cc9d1d9c2204489502588319c7af3d 1680w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=1960&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=3d3d4a20c1b6e19d9d17e36e4025a284 1960w, https://mintcdn.com/claude-code/s7NM0vfd_wres2nf/images/hook-resolution.svg?w=2240&fit=max&auto=format&n=s7NM0vfd_wres2nf&q=85&s=9bfeafda81b3750f328a98d6ed9823b 2240w" />
</Frame>

<Steps>
  <Step title="事件触发">
    `PreToolUse` 事件触发。Claude Code 通过 stdin 将工具输入作为 JSON 发送给 Hook:

    ```json  theme={null}
    { "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
    ```
  </Step>

  <Step title="Matcher 检查">
    matcher `"Bash"` 与工具名称匹配,因此会运行 `block-rm.sh`。如果省略 matcher 或使用 `"*"`,Hook 会在每次出现该事件时运行。只有在已定义 matcher 且其不匹配时,Hook 才会跳过。
  </Step>

  <Step title="Hook 处理器运行">
    脚本从输入中提取 `"rm -rf /tmp/build"` 并找到 `rm -rf`,随后将决策输出到 stdout:

    ```json  theme={null}
    {
      "hookSpecificOutput": {
        "hookEventName": "PreToolUse",
        "permissionDecision": "deny",
        "permissionDecisionReason": "Destructive command blocked by hook"
      }
    }
    ```

    如果命令是安全的(例如 `npm test`),脚本会改为执行 `exit 0`,这会告诉 Claude Code 允许工具调用且不执行其他操作。
  </Step>

  <Step title="Claude Code 处理结果">
    Claude Code 读取 JSON 决策,阻止工具调用,并向 Claude 展示原因。
  </Step>
</Steps>

下方的 [Configuration](#configuration)章节记录了完整 schema,每个 [hook event](#hook-events)章节则记录 command 接收的输入以及可以返回的输出。

Configuration

配置

Hooks are defined in JSON settings files. The configuration has three levels of nesting:
  1. Choose a hook event to respond to, like
    PreToolUse
    or
    Stop
  2. Add a matcher group to filter when it fires, like "only for the Bash tool"
  3. Define one or more hook handlers to run when matched
See How a hook resolves above for a complete walkthrough with an annotated example.
<Note> This page uses specific terms for each level: **hook event** for the lifecycle point, **matcher group** for the filter, and **hook handler** for the shell command, prompt, or agent that runs. "Hook" on its own refers to the general feature. </Note>
Hooks 在 JSON settings 文件中定义。配置包含三个嵌套层级:
  1. 选择要响应的 hook event,例如
    PreToolUse
    或
    Stop
  2. 添加 matcher group来筛选触发时机,例如“仅用于 Bash 工具”
  3. 定义匹配后运行的一个或多个 hook handler
有关完整的标注示例,请参阅上方的 How a hook resolves。
<Note> 本页使用特定术语表示各层级:**hook event** 表示生命周期节点,**matcher group** 表示筛选器,**hook handler** 表示运行的 shell 命令、prompt 或 Agent。单独使用 **Hook** 时表示整个功能。 </Note>

Hook locations

Hook 位置

Where you define a hook determines its scope:
LocationScopeShareable
~/.claude/settings.json
All your projectsNo, local to your machine
.claude/settings.json
Single projectYes, can be committed to the repo
.claude/settings.local.json
Single projectNo, gitignored
Managed policy settingsOrganization-wideYes, admin-controlled
Plugin
hooks/hooks.json
When plugin is enabledYes, bundled with the plugin
Skill or agent frontmatterWhile the component is activeYes, defined in the component file
For details on settings file resolution, see settings. Enterprise administrators can use
allowManagedHooksOnly
to block user, project, and plugin hooks. See Hook configuration.
定义 Hook 的位置决定其作用域:
位置作用域可共享
~/.claude/settings.json
所有项目否,仅限本机
.claude/settings.json
单个项目是,可以提交到仓库
.claude/settings.local.json
单个项目否,已被 Git 忽略
托管策略设置组织范围是,由管理员控制
Plugin
hooks/hooks.json
启用 Plugin 时是,随 Plugin 一起分发
Skill 或 agent frontmatter组件处于活动状态时是,在组件文件中定义
有关 settings 文件解析方式的详细信息,请参阅 settings。企业管理员可以使用
allowManagedHooksOnly
阻止用户、项目和 Plugin Hooks。有关详细信息,请参阅 Hook configuration。

Matcher patterns

Matcher 模式

The
matcher
field is a regex string that filters when hooks fire. Use
"*"
,
""
, or omit
matcher
entirely to match all occurrences. Each event type matches on a different field:
EventWhat the matcher filtersExample matcher values
PreToolUse
,
PostToolUse
,
PostToolUseFailure
,
PermissionRequest
tool name
Bash
,
Edit|Write
,
mcp__.*
SessionStart
how the session started
startup
,
resume
,
clear
,
compact
SessionEnd
why the session ended
clear
,
logout
,
prompt_input_exit
,
bypass_permissions_disabled
,
other
Notification
notification type
permission_prompt
,
idle_prompt
,
auth_success
,
elicitation_dialog
SubagentStart
agent type
Bash
,
Explore
,
Plan
, or custom agent names
PreCompact
what triggered compaction
manual
,
auto
SubagentStop
agent typesame values as
SubagentStart
UserPromptSubmit
,
Stop
no matcher supportalways fires on every occurrence
The matcher is a regex, so
Edit|Write
matches either tool and
Notebook.*
matches any tool starting with Notebook. The matcher runs against a field from the JSON input that Claude Code sends to your hook on stdin. For tool events, that field is
tool_name
. Each hook event section lists the full set of matcher values and the input schema for that event.
This example runs a linting script only when Claude writes or edits a file:
json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/lint-check.sh"
          }
        ]
      }
    ]
  }
}
UserPromptSubmit
and
Stop
don't support matchers and always fire on every occurrence. If you add a
matcher
field to these events, it is silently ignored.
matcher
字段是一个用于筛选 Hook 触发条件的正则表达式字符串。使用
"*"
、
""
或完全省略
matcher
均可匹配所有情况。每种事件类型匹配不同字段:
事件matcher 筛选的内容matcher 示例值
PreToolUse
、
PostToolUse
、
PostToolUseFailure
、
PermissionRequest
工具名称
Bash
、
Edit|Write
、
mcp__.*
SessionStart
会话启动方式
startup
、
resume
、
clear
、
compact
SessionEnd
会话结束原因
clear
、
logout
、
prompt_input_exit
、
bypass_permissions_disabled
、
other
Notification
通知类型
permission_prompt
、
idle_prompt
、
auth_success
、
elicitation_dialog
SubagentStart
Agent 类型
Bash
、
Explore
、
Plan
或自定义 Agent 名称
PreCompact
触发压缩的原因
manual
、
auto
SubagentStop
Agent 类型与
SubagentStart
相同
UserPromptSubmit
、
Stop
不支持 matcher每次出现时始终触发
Matcher 是正则表达式,因此
Edit|Write
可以匹配任一工具,
Notebook.*
可以匹配所有以 Notebook 开头的工具。Matcher 会针对 Claude Code 通过 stdin 发送给 Hook 的 JSON 输入中的某个字段运行。对于工具事件,该字段为
tool_name
。每个 hook event章节都列出了完整的 matcher 值和输入 schema。
此示例仅在 Claude 写入或编辑文件时运行 lint 脚本:
json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/lint-check.sh"
          }
        ]
      }
    ]
  }
}
UserPromptSubmit
和
Stop
不支持 matcher,每次出现时始终触发。如果为这些事件添加
matcher
字段,该字段会被静默忽略。

Match MCP tools

匹配 MCP 工具

MCP server tools appear as regular tools in tool events (
PreToolUse
,
PostToolUse
,
PostToolUseFailure
,
PermissionRequest
), so you can match them the same way you match any other tool name.
MCP tools follow the naming pattern
mcp__<server>__<tool>
, for example:
  • mcp__memory__create_entities
    : Memory server's create entities tool
  • mcp__filesystem__read_file
    : Filesystem server's read file tool
  • mcp__github__search_repositories
    : GitHub server's search tool
Use regex patterns to target specific MCP tools or groups of tools:
  • mcp__memory__.*
    matches all tools from the
    memory
    server
  • mcp__.*__write.*
    matches any tool containing "write" from any server
This example logs all memory server operations and validates write operations from any MCP server:
json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}
MCP server 工具会像常规工具一样出现在工具事件(
PreToolUse
、
PostToolUse
、
PostToolUseFailure
、
PermissionRequest
)中,因此可以使用与常规工具名称相同的方式进行匹配。
MCP 工具遵循
mcp__<server>__<tool>
命名模式,例如:
  • mcp__memory__create_entities
    :Memory server 的创建实体工具
  • mcp__filesystem__read_file
    :Filesystem server 的读取文件工具
  • mcp__github__search_repositories
    :GitHub server 的搜索仓库工具
使用正则表达式模式可以定位特定 MCP 工具或工具组:
  • mcp__memory__.*
    匹配
    memory
    server 的所有工具
  • mcp__.*__write.*
    匹配任意 server 中名称包含“write”的工具
此示例记录所有 memory server 操作,并验证任意 MCP server 的写操作:
json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

Hook handler fields

Hook handler 字段

Each object in the inner
hooks
array is a hook handler: the shell command, LLM prompt, or agent that runs when the matcher matches. There are three types:
  • Command hooks (
    type: "command"
    ): run a shell command. Your script receives the event's JSON input on stdin and communicates results back through exit codes and stdout.
  • Prompt hooks (
    type: "prompt"
    ): send a prompt to a Claude model for single-turn evaluation. The model returns a yes/no decision as JSON. See Prompt-based hooks.
  • Agent hooks (
    type: "agent"
    ): spawn a subagent that can use tools like Read, Grep, and Glob to verify conditions before returning a decision. See Agent-based hooks.
内部
hooks
数组中的每个对象都是一个 Hook handler,即 matcher 匹配后运行的 shell 命令、LLM prompt 或 Agent。共有三种类型:
  • Command hooks(
    type: "command"
    ):运行 shell 命令。脚本通过 stdin 接收事件的 JSON 输入,并通过退出码和 stdout 传达结果。
  • Prompt hooks(
    type: "prompt"
    ):将 prompt 发送给 Claude 模型进行单轮评估。模型返回 yes/no 决策 JSON。有关详细信息,请参阅 Prompt-based hooks。
  • Agent hooks(
    type: "agent"
    ):创建 subagent,使其可以使用 Read、Grep 和 Glob 等工具验证条件,然后返回决策。有关详细信息,请参阅 Agent-based hooks。

Common fields

通用字段

These fields apply to all hook types:
FieldRequiredDescription
type
yes
"command"
,
"prompt"
, or
"agent"
timeout
noSeconds before canceling. Defaults: 600 for command, 30 for prompt, 60 for agent
statusMessage
noCustom spinner message displayed while the hook runs
once
noIf
true
, runs only once per session then is removed. Skills only, not agents. See Hooks in skills and agents
以下字段适用于所有 Hook 类型:
字段必需说明
type
是
"command"
、
"prompt"
或
"agent"
timeout
否取消前的秒数。默认值:command 为 600,prompt 为 30,agent 为 60
statusMessage
否Hook 运行期间显示的自定义加载提示信息
once
否如果为
true
,则每个会话仅运行一次,随后被移除。仅支持 Skills,不支持 Agents。参见 Hooks in skills and agents

Command hook fields

Command hook 字段

In addition to the common fields, command hooks accept these fields:
FieldRequiredDescription
command
yesShell command to execute
async
noIf
true
, runs in the background without blocking. See Run hooks in the background
除通用字段外,command Hooks 还接受以下字段:
字段必需说明
command
是要执行的 shell 命令
async
否如果为
true
,则在后台运行而不阻塞。请参阅 Run hooks in the background

Prompt and agent hook fields

Prompt 和 Agent hook 字段

In addition to the common fields, prompt and agent hooks accept these fields:
FieldRequiredDescription
prompt
yesPrompt text to send to the model. Use
$ARGUMENTS
as a placeholder for the hook input JSON
model
noModel to use for evaluation. Defaults to a fast model
All matching hooks run in parallel, and identical handlers are deduplicated automatically. Handlers run in the current directory with Claude Code's environment. The
$CLAUDE_CODE_REMOTE
environment variable is set to
"true"
in remote web environments and not set in the local CLI.
除通用字段外,prompt 和 Agent Hooks 还接受以下字段:
字段必需说明
prompt
是发送给模型的 prompt 文本。使用
$ARGUMENTS
作为 Hook 输入 JSON 的占位符
model
否用于评估的模型,默认为快速模型
所有匹配的 Hooks 都会并行运行,完全相同的处理器会被自动去重。处理器在当前目录中运行,并使用 Claude Code 的环境。远程 Web 环境中会设置
$CLAUDE_CODE_REMOTE
环境变量并将其设为
"true"
,本地 CLI 中不会设置该变量。

Reference scripts by path

通过路径引用脚本

Use environment variables to reference hook scripts relative to the project or plugin root, regardless of the working directory when the hook runs:
  • $CLAUDE_PROJECT_DIR
    : the project root. Wrap in quotes to handle paths with spaces.
  • ${CLAUDE_PLUGIN_ROOT}
    : the plugin's root directory, for scripts bundled with a plugin.
<Tabs> <Tab title="Project scripts"> This example uses `$CLAUDE_PROJECT_DIR` to run a style checker from the project's `.claude/hooks/` directory after any `Write` or `Edit` tool call:
```json  theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"
          }
        ]
      }
    ]
  }
}
```
</Tab> <Tab title="Plugin scripts"> Define plugin hooks in `hooks/hooks.json` with an optional top-level `description` field. When a plugin is enabled, its hooks merge with your user and project hooks.
This example runs a formatting script bundled with the plugin:

```json  theme={null}
{
  "description": "Automatic code formatting",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
```

See the [plugin components reference](/en/plugins-reference#hooks) for details on creating plugin hooks.
</Tab> </Tabs>
使用环境变量引用相对于项目或 Plugin 根目录的 Hook 脚本,无论 Hook 运行时的工作目录是什么:
  • $CLAUDE_PROJECT_DIR
    :项目根目录。添加引号以处理包含空格的路径。
  • ${CLAUDE_PLUGIN_ROOT}
    :Plugin 的根目录,用于存放随 plugin 一起分发的脚本。
<Tabs> <Tab title="项目脚本"> 此示例使用 `$CLAUDE_PROJECT_DIR`,在任何 `Write` 或 `Edit` 工具调用后,从项目的 `.claude/hooks/` 目录运行样式检查器:
```json  theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"
          }
        ]
      }
    ]
  }
}
```
</Tab> <Tab title="Plugin 脚本"> 在 `hooks/hooks.json` 中定义 Plugin Hooks,可以选择添加顶层 `description` 字段。启用 Plugin 后,其 Hooks 会与用户和项目 Hooks 合并。
此示例运行随 Plugin 一起分发的格式化脚本:

```json  theme={null}
{
  "description": "Automatic code formatting",
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
```

有关创建 Plugin Hooks 的详细信息,请参阅 [plugin components reference](/en/plugins-reference#hooks)。
</Tab> </Tabs>

Hooks in skills and agents

Skills 和 Agents 中的 Hooks

In addition to settings files and plugins, hooks can be defined directly in skills and subagents using frontmatter. These hooks are scoped to the component's lifecycle and only run when that component is active.
All hook events are supported. For subagents,
Stop
hooks are automatically converted to
SubagentStop
since that is the event that fires when a subagent completes.
Hooks use the same configuration format as settings-based hooks but are scoped to the component's lifetime and cleaned up when it finishes.
This skill defines a
PreToolUse
hook that runs a security validation script before each
Bash
command:
yaml
---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---
Agents use the same format in their YAML frontmatter.
除 settings 文件和 Plugins 外,还可以直接使用 frontmatter 在 skills 和 subagents 中定义 Hooks。这些 Hooks 的作用域限定为组件生命周期,只在组件处于活动状态时运行。
支持所有 Hook 事件。对于 subagents,
Stop
Hooks 会自动转换为
SubagentStop
,因为 subagent 完成时触发的是该事件。
Hooks 使用与基于 settings 的 Hooks 相同的配置格式,但作用域限定为组件生命周期,并在组件完成时清理。
此 Skill 定义了一个
PreToolUse
Hook,用于在每条
Bash
命令前运行安全验证脚本:
yaml
---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---
Agents 在 YAML frontmatter 中使用相同格式。

The
/hooks
menu

/hooks
菜单

Type
/hooks
in Claude Code to open the interactive hooks manager, where you can view, add, and delete hooks without editing settings files directly. For a step-by-step walkthrough, see Set up your first hook in the guide.
Each hook in the menu is labeled with a bracket prefix indicating its source:
  • [User]
    : from
    ~/.claude/settings.json
  • [Project]
    : from
    .claude/settings.json
  • [Local]
    : from
    .claude/settings.local.json
  • [Plugin]
    : from a plugin's
    hooks/hooks.json
    , read-only
在 Claude Code 中输入
/hooks
打开交互式 Hooks 管理器,无需直接编辑 settings 文件即可查看、添加和删除 Hooks。有关分步演练,请参阅指南中的 Set up your first hook。
菜单中的每个 Hook 都使用方括号前缀标示来源:
  • [User]
    :来自
    ~/.claude/settings.json
  • [Project]
    :来自
    .claude/settings.json
  • [Local]
    :来自
    .claude/settings.local.json
  • [Plugin]
    :来自 Plugin 的
    hooks/hooks.json
    ,只读

Disable or remove hooks

禁用或移除 Hooks

To remove a hook, delete its entry from the settings JSON file, or use the
/hooks
menu and select the hook to delete it.
To temporarily disable all hooks without removing them, set
"disableAllHooks": true
in your settings file or use the toggle in the
/hooks
menu. There is no way to disable an individual hook while keeping it in the configuration.
Direct edits to hooks in settings files don't take effect immediately. Claude Code captures a snapshot of hooks at startup and uses it throughout the session. This prevents malicious or accidental hook modifications from taking effect mid-session without your review. If hooks are modified externally, Claude Code warns you and requires review in the
/hooks
menu before changes apply.
要移除 Hook,请删除 settings JSON 文件中的对应条目,或使用
/hooks
菜单并选择要删除的 Hook。
如果只想临时禁用所有 Hooks 而不将其移除,可以在 settings 文件中设置
"disableAllHooks": true
,或使用
/hooks
菜单中的开关。无法在保留配置的情况下单独禁用某个 Hook。
在 settings 文件中直接编辑 Hooks 不会立即生效。Claude Code 会在启动时捕获 Hooks 快照,并在整个会话中使用该快照。这样可以防止恶意或意外的 Hook 修改在未经审查的情况下于会话中途生效。如果 Hooks 被外部修改,Claude Code 会发出警告,并要求先在
/hooks
菜单中审查更改,然后才会应用。

Hook input and output

Hook 输入和输出

Hooks receive JSON data via stdin and communicate results through exit codes, stdout, and stderr. This section covers fields and behavior common to all events. Each event's section under Hook events includes its specific input schema and decision control options.
Hooks 通过 stdin 接收 JSON 数据,并通过退出码、stdout 和 stderr 传达结果。本节介绍所有事件共有的字段和行为。每个事件在 Hook events下的对应章节都会介绍其专用输入 schema 和决策控制选项。

Common input fields

通用输入字段

All hook events receive these fields via stdin as JSON, in addition to event-specific fields documented in each hook event section:
FieldDescription
session_id
Current session identifier
transcript_path
Path to conversation JSON
cwd
Current working directory when the hook is invoked
permission_mode
Current permission mode:
"default"
,
"plan"
,
"acceptEdits"
,
"dontAsk"
, or
"bypassPermissions"
hook_event_name
Name of the event that fired
For example, a
PreToolUse
hook for a Bash command receives this on stdin:
json
{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}
The
tool_name
and
tool_input
fields are event-specific. Each hook event section documents the additional fields for that event.
除各事件在 Hook events下记录的专用字段外,所有 Hook 事件都会通过 stdin 接收以下 JSON 字段:
字段说明
session_id
当前会话标识符
transcript_path
对话 JSON 的路径
cwd
调用 Hook 时的工作目录
permission_mode
当前权限模式:
"default"
、
"plan"
、
"acceptEdits"
、
"dontAsk"
或
"bypassPermissions"
hook_event_name
触发的事件名称
例如,用于 Bash 命令的
PreToolUse
Hook 会在 stdin 中收到以下内容:
json
{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}
tool_name
和
tool_input
字段因事件而异。每个 hook event章节都记录了额外的字段。

Exit code output

退出码输出

The exit code from your hook command tells Claude Code whether the action should proceed, be blocked, or be ignored.
Exit 0 means success. Claude Code parses stdout for JSON output fields. JSON output is only processed on exit 0. For most events, stdout is only shown in verbose mode (
Ctrl+O
). The exceptions are
UserPromptSubmit
and
SessionStart
, where stdout is added as context that Claude can see and act on.
Exit 2 means a blocking error. Claude Code ignores stdout and any JSON in it. Instead, stderr text is fed back to Claude as an error message. The effect depends on the event:
PreToolUse
blocks the tool call,
UserPromptSubmit
rejects the prompt, and so on. See exit code 2 behavior for the full list.
Any other exit code is a non-blocking error. stderr is shown in verbose mode (
Ctrl+O
) and execution continues.
For example, a hook command script that blocks dangerous Bash commands:
bash
#!/bin/bash
Hook 命令的退出码会告知 Claude Code 应继续、阻止还是忽略操作。
退出 0 表示成功。Claude Code 会解析 stdout 中的 JSON 输出字段。只有退出 0 时才会处理 JSON 输出。对于大多数事件,stdout 只会在详细模式(
Ctrl+O
)中显示。例外情况是
UserPromptSubmit
和
SessionStart
,它们的 stdout 会作为 Claude 可以看到并采取行动的上下文添加进去。
退出 2 表示阻止错误。Claude Code 会忽略 stdout 及其中的任何 JSON,而是将 stderr 文本作为错误消息反馈给 Claude。具体效果取决于事件:
PreToolUse
会阻止工具调用,
UserPromptSubmit
会拒绝 prompt,依此类推。有关完整列表,请参阅 exit code 2 behavior。
其他退出码 表示非阻止错误。stderr 会在详细模式(
Ctrl+O
)中显示,执行会继续。
例如,以下 Hook 命令脚本会阻止危险的 Bash 命令:
bash
#!/bin/bash

Reads JSON input from stdin, checks the command

Reads JSON input from stdin, checks the command

command=$(jq -r '.tool_input.command' < /dev/stdin)
if [[ "$command" == rm* ]]; then echo "Blocked: rm commands are not allowed" >&2 exit 2 # Blocking error: tool call is prevented fi
exit 0 # Success: tool call proceeds
undefined
command=$(jq -r '.tool_input.command' < /dev/stdin)
if [[ "$command" == rm* ]]; then echo "Blocked: rm commands are not allowed" >&2 exit 2 # Blocking error: tool call is prevented fi
exit 0 # Success: tool call proceeds
undefined

Exit code 2 behavior per event

各事件的退出码 2 行为

Exit code 2 is the way a hook signals "stop, don't do this." The effect depends on the event, because some events represent actions that can be blocked (like a tool call that hasn't happened yet) and others represent things that already happened or can't be prevented.
Hook eventCan block?What happens on exit 2
PreToolUse
YesBlocks the tool call
PermissionRequest
YesDenies the permission
UserPromptSubmit
YesBlocks prompt processing and erases the prompt
Stop
YesPrevents Claude from stopping, continues the conversation
SubagentStop
YesPrevents the subagent from stopping
PostToolUse
NoShows stderr to Claude (tool already ran)
PostToolUseFailure
NoShows stderr to Claude (tool already failed)
Notification
NoShows stderr to user only
SubagentStart
NoShows stderr to user only
SessionStart
NoShows stderr to user only
SessionEnd
NoShows stderr to user only
PreCompact
NoShows stderr to user only
退出码 2 是 Hook 表示“停止,不要这样做”的方式。其效果取决于事件,因为有些事件代表尚未发生且可以阻止的操作,而其他事件代表已经发生或无法阻止的事情。
Hook 事件可以阻止?退出 2 时的行为
PreToolUse
是阻止工具调用
PermissionRequest
是拒绝权限请求
UserPromptSubmit
是阻止处理 prompt 并将其从上下文中删除
Stop
是阻止 Claude 停止并继续对话
SubagentStop
是阻止 subagent 停止
PostToolUse
否向 Claude 显示 stderr(工具已运行)
PostToolUseFailure
否向 Claude 显示 stderr(工具已失败)
Notification
否仅向用户显示 stderr
SubagentStart
否仅向用户显示 stderr
SessionStart
否仅向用户显示 stderr
SessionEnd
否仅向用户显示 stderr
PreCompact
否仅向用户显示 stderr

JSON output

JSON 输出

Exit codes let you allow or block, but JSON output gives you finer-grained control. Instead of exiting with code 2 to block, exit 0 and print a JSON object to stdout. Claude Code reads specific fields from that JSON to control behavior, including decision control for blocking, allowing, or escalating to the user.
<Note> You must choose one approach per hook, not both: either use exit codes alone for signaling, or exit 0 and print JSON for structured control. Claude Code only processes JSON on exit 0. If you exit 2, any JSON is ignored. </Note>
Your hook's stdout must contain only the JSON object. If your shell profile prints text on startup, it can interfere with JSON parsing. See JSON validation failed in the troubleshooting guide.
The JSON object supports three kinds of fields:
  • Universal fields like
    continue
    work across all events. These are listed in the table below.
  • Top-level
    decision
    and
    reason
    are used by some events to block or provide feedback.
  • hookSpecificOutput
    is a nested object for events that need richer control. It requires a
    hookEventName
    field set to the event name.
FieldDefaultDescription
continue
true
If
false
, Claude stops processing entirely after the hook runs. Takes precedence over any event-specific decision fields
stopReason
noneMessage shown to the user when
continue
is
false
. Not shown to Claude
suppressOutput
false
If
true
, hides stdout from verbose mode output
systemMessage
noneWarning message shown to the user
To stop Claude entirely regardless of event type:
json
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }
退出码只允许允许或阻止两种结果,而 JSON 输出可以提供更精细的控制。可以退出 0 并将 JSON 对象输出到 stdout,而不是使用退出码 2 阻止操作。Claude Code 会读取该 JSON 中的特定字段来控制行为,包括用于阻止、允许或升级给用户的决策控制。
<Note> 每个 Hook 必须选择一种方式,不能同时使用:根据信号使用退出码,或者退出 0 并输出 JSON 进行结构化控制。Claude Code 只在退出 0 时处理 JSON。如果退出 2,JSON 会被忽略。 </Note>
Hook 的 stdout 必须只包含 JSON 对象。如果 shell 配置文件在启动时输出文本,可能会干扰 JSON 解析。请参阅故障排除指南中的 JSON validation failed。
JSON 对象支持三类字段:
  • 通用字段,例如适用于所有事件的
    continue
  • 顶层
    decision
    和
    reason
    ,用于某些事件阻止操作或提供反馈
  • hookSpecificOutput
    ,用于需要更丰富控制的事件。它要求包含设置为事件名称的
    hookEventName
    字段
字段默认值说明
continue
true
如果为
false
,Claude 在 Hook 运行后会完全停止处理。优先于所有事件专用决策字段
stopReason
无当
continue
为
false
时展示给用户的消息,不会展示给 Claude
suppressOutput
false
如果为
true
,则从详细模式输出中隐藏 stdout
systemMessage
无展示给用户的警告消息
无论事件类型如何,要完全停止 Claude:
json
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }

Decision control

决策控制

Not every event supports blocking or controlling behavior through JSON. The events that do each use a different set of fields to express that decision. Use this table as a quick reference before writing a hook:
EventsDecision patternKey fields
UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStopTop-level
decision
decision: "block"
,
reason
PreToolUse
hookSpecificOutput
permissionDecision
(allow/deny/ask),
permissionDecisionReason
PermissionRequest
hookSpecificOutput
decision.behavior
(allow/deny)
Here are examples of each pattern in action:
<Tabs> <Tab title="Top-level decision"> Used by `UserPromptSubmit`, `PostToolUse`, `PostToolUseFailure`, `Stop`, and `SubagentStop`. The only value is `"block"` — to allow the action to proceed, omit `decision` from your JSON, or exit 0 without any JSON at all:
```json  theme={null}
{
  "decision": "block",
  "reason": "Test suite must pass before proceeding"
}
```
</Tab> <Tab title="PreToolUse"> Uses `hookSpecificOutput` for richer control: allow, deny, or escalate to the user. You can also modify tool input before it runs or inject additional context for Claude. See [PreToolUse decision control](#pretooluse-decision-control) for the full set of options.
```json  theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Database writes are not allowed"
  }
}
```
</Tab> <Tab title="PermissionRequest"> Uses `hookSpecificOutput` to allow or deny a permission request on behalf of the user. When allowing, you can also modify the tool's input or apply permission rules so the user isn't prompted again. See [PermissionRequest decision control](#permissionrequest-decision-control) for the full set of options.
```json  theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}
```
</Tab> </Tabs>
For extended examples including Bash command validation, prompt filtering, and auto-approval scripts, see What you can automate in the guide and the Bash command validator reference implementation.
并非所有事件都支持通过 JSON 阻止或控制行为。支持的事件分别使用不同字段表达决策。编写 Hook 前,可使用此表快速查阅:
事件决策模式关键字段
UserPromptSubmit、PostToolUse、PostToolUseFailure、Stop、SubagentStop顶层
decision
decision: "block"
、
reason
PreToolUse
hookSpecificOutput
permissionDecision
(allow/deny/ask)、
permissionDecisionReason
PermissionRequest
hookSpecificOutput
decision.behavior
(allow/deny)
以下是每种模式的应用示例:
<Tabs> <Tab title="顶层 decision"> 用于 `UserPromptSubmit`、`PostToolUse`、`PostToolUseFailure`、`Stop` 和 `SubagentStop`。唯一值为 `"block"`。若要允许操作继续,请省略 `decision` 字段,或退出 0 且不输出任何 JSON:
```json  theme={null}
{
  "decision": "block",
  "reason": "Test suite must pass before proceeding"
}
```
</Tab> <Tab title="PreToolUse"> 使用 `hookSpecificOutput` 进行更丰富的控制:允许、拒绝或升级给用户。还可以修改工具运行前的输入,或向 Claude 注入额外上下文。完整选项请参阅 [PreToolUse decision control](#pretooluse-decision-control)。
```json  theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Database writes are not allowed"
  }
}
```
</Tab> <Tab title="PermissionRequest"> 使用 `hookSpecificOutput` 代表用户允许或拒绝权限请求。允许时,还可以修改工具输入或应用权限规则,避免再次提示用户。
```json  theme={null}
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}
```
</Tab> </Tabs>
有关 Bash 命令验证、prompt 过滤和自动批准脚本的更多示例,请参阅指南中的 What you can automate以及 Bash command validator reference implementation。

Hook events

Hook 事件

Each event corresponds to a point in Claude Code's lifecycle where hooks can run. The sections below are ordered to match the lifecycle: from session setup through the agentic loop to session end. Each section describes when the event fires, what matchers it supports, the JSON input it receives, and how to control behavior through output.
每个事件都对应 Claude Code 生命周期中的一个节点,Hooks 可以在该节点运行。以下章节按生命周期顺序排列:从会话设置、Agent 循环到会话结束。每个章节都会说明事件的触发时机、支持的 matcher、接收的 JSON 输入以及可以控制行为的输出方式。

SessionStart

SessionStart

Runs when Claude Code starts a new session or resumes an existing session. Useful for loading development context like existing issues or recent changes to your codebase, or setting up environment variables. For static context that does not require a script, use CLAUDE.md instead.
SessionStart runs on every session, so keep these hooks fast.
The matcher value corresponds to how the session was initiated:
MatcherWhen it fires
startup
New session
resume
--resume
,
--continue
, or
/resume
clear
/clear
compact
Auto or manual compaction
在 Claude Code 启动新会话或恢复现有会话时运行。适用于加载开发上下文,例如现有 issue 或代码库的近期变更,也可用于设置环境变量。对于不需要脚本的静态上下文,请改用 CLAUDE.md。
SessionStart 会在每个会话中运行,因此这些 Hooks 应保持快速执行。
Matcher 值对应会话的启动方式:
Matcher触发时机
startup
新会话
resume
--resume
、
--continue
或
/resume
clear
/clear
compact
自动或手动压缩

SessionStart input

SessionStart 输入

In addition to the common input fields, SessionStart hooks receive
source
,
model
, and optionally
agent_type
. The
source
field indicates how the session started:
"startup"
for new sessions,
"resume"
for resumed sessions,
"clear"
after
/clear
, or
"compact"
after compaction. The
model
field contains the model identifier. If you start Claude Code with
claude --agent <name>
, an
agent_type
field contains the agent name.
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SessionStart",
  "source": "startup",
  "model": "claude-sonnet-4-5-20250929"
}
除通用输入字段外,SessionStart Hooks 还会接收
source
、
model
和可选的
agent_type
。
source
字段表示会话的启动方式:新会话为
"startup"
,恢复会话为
"resume"
,执行
/clear
后为
"clear"
,压缩后为
"compact"
。
model
字段包含模型标识符。如果使用
claude --agent <name>
启动 Claude Code,
agent_type
字段会包含 Agent 名称。
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SessionStart",
  "source": "startup",
  "model": "claude-sonnet-4-5-20250929"
}

SessionStart decision control

SessionStart 决策控制

Any text your hook script prints to stdout is added as context for Claude. In addition to the JSON output fields available to all hooks, you can return these event-specific fields:
FieldDescription
additionalContext
String added to Claude's context. Multiple hooks' values are concatenated
json
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "My additional context here"
  }
}
Hook 脚本输出到 stdout 的任何文本都会作为上下文添加到 Claude 中。除所有 Hook 均可使用的 JSON 输出字段外,还可以返回以下事件专用字段:
字段说明
additionalContext
添加到 Claude 上下文的字符串。多个 Hook 的值会连接在一起
json
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "My additional context here"
  }
}

Persist environment variables

持久化环境变量

SessionStart hooks have access to the
CLAUDE_ENV_FILE
environment variable, which provides a file path where you can persist environment variables for subsequent Bash commands.
To set individual environment variables, write
export
statements to
CLAUDE_ENV_FILE
. Use append (
>>
) to preserve variables set by other hooks:
bash
#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0
To capture all environment changes from setup commands, compare the exported variables before and after:
bash
#!/bin/bash

ENV_BEFORE=$(export -p | sort)
SessionStart Hooks 可以访问
CLAUDE_ENV_FILE
环境变量,该变量提供一个文件路径,用于持久化后续 Bash 命令使用的环境变量。
要设置单个环境变量,请将
export
语句写入
CLAUDE_ENV_FILE
。使用
>>
追加,以保留其他 Hook 设置的变量:
bash
#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0
若要捕获设置命令所做的所有环境变更,请比较执行前后的导出变量:
bash
#!/bin/bash

ENV_BEFORE=$(export -p | sort)

Run your setup commands that modify the environment

Run your setup commands that modify the environment

source ~/.nvm/nvm.sh nvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then ENV_AFTER=$(export -p | sort) comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE" fi
exit 0

Any variables written to this file will be available in all subsequent Bash commands that Claude Code executes during the session.

<Note>
  `CLAUDE_ENV_FILE` is available for SessionStart hooks. Other hook types do not have access to this variable.
</Note>
source ~/.nvm/nvm.sh nvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then ENV_AFTER=$(export -p | sort) comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE" fi
exit 0

写入此文件的所有变量都将在会话期间 Claude Code 后续执行的所有 Bash 命令中可用。

<Note>
  `CLAUDE_ENV_FILE` 仅适用于 SessionStart Hooks,其他 Hook 类型无法访问此变量。
</Note>

UserPromptSubmit

UserPromptSubmit

Runs when the user submits a prompt, before Claude processes it. This allows you to add additional context based on the prompt/conversation, validate prompts, or block certain types of prompts.
在用户提交 prompt 后、Claude 处理之前运行。允许你根据 prompt 或对话添加额外上下文、验证 prompt,或阻止特定类型的 prompt。

UserPromptSubmit input

UserPromptSubmit 输入

In addition to the common input fields, UserPromptSubmit hooks receive the
prompt
field containing the text the user submitted.
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}
除通用输入字段外,UserPromptSubmit Hooks 还会接收
prompt
字段,其中包含用户提交的文本。
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

UserPromptSubmit decision control

UserPromptSubmit 决策控制

UserPromptSubmit
hooks can control whether a user prompt is processed and add context. All JSON output fields are available.
There are two ways to add context to the conversation on exit code 0:
  • Plain text stdout: any non-JSON text written to stdout is added as context
  • JSON with
    additionalContext
    : use the JSON format below for more control. The
    additionalContext
    field is added as context
Plain stdout is shown as hook output in the transcript. The
additionalContext
field is added more discretely.
To block a prompt, return a JSON object with
decision
set to
"block"
:
FieldDescription
decision
"block"
prevents the prompt from being processed and erases it from context. Omit to allow the prompt to proceed
reason
Shown to the user when
decision
is
"block"
. Not added to context
additionalContext
String added to Claude's context
json
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}
<Note> The JSON format isn't required for simple use cases. To add context, you can print plain text to stdout with exit code 0. Use JSON when you need to block prompts or want more structured control. </Note>
UserPromptSubmit Hooks 可以控制用户 prompt 是否被处理,并添加上下文。所有 JSON 输出字段均可使用。
退出码为 0 时,可以通过两种方式将上下文添加到对话:
  • 纯文本 stdout:写入 stdout 的任何非 JSON 文本都会作为上下文添加
  • 包含
    additionalContext
    的 JSON
    :使用以下 JSON 格式进行更精确的控制。
    additionalContext
    字段会作为上下文添加
纯 stdout 会作为 Hook 输出显示在记录中,而
additionalContext
字段的添加方式更隐蔽。
要阻止 prompt,请返回将
decision
设置为
"block"
的 JSON 对象:
字段说明
decision
"block"
会阻止处理 prompt,并将其从上下文中删除。省略则允许 prompt 继续处理
reason
当
decision
为
"block"
时展示给用户,不会添加到上下文
additionalContext
添加到 Claude 上下文的字符串
json
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}
<Note> 简单用例并不要求 JSON 格式。要添加上下文,可以在退出码为 0 时输出纯文本。需要阻止 prompt 或进行更结构化的控制时,请使用 JSON。 </Note>

PreToolUse

PreToolUse

Runs after Claude creates tool parameters and before processing the tool call. Matches on tool name:
Bash
,
Edit
,
Write
,
Read
,
Glob
,
Grep
,
Task
,
WebFetch
,
WebSearch
, and any MCP tool names.
Use PreToolUse decision control to allow, deny, or ask for permission to use the tool.
在 Claude 创建工具参数后、处理工具调用之前运行。按工具名称匹配:
Bash
、
Edit
、
Write
、
Read
、
Glob
、
Grep
、
Task
、
WebFetch
、
WebSearch
以及任意 MCP 工具名称。
使用 PreToolUse 决策控制允许、拒绝或请求用户授权使用工具。

PreToolUse input

PreToolUse 输入

In addition to the common input fields, PreToolUse hooks receive
tool_name
,
tool_input
, and
tool_use_id
. The
tool_input
fields depend on the tool:
除通用输入字段外,PreToolUse Hooks 还会接收
tool_name
、
tool_input
和
tool_use_id
。
tool_input
字段取决于工具。
Bash
Bash
Executes shell commands.
FieldTypeExampleDescription
command
string
"npm test"
The shell command to execute
description
string
"Run test suite"
Optional description of what the command does
timeout
number
120000
Optional timeout in milliseconds
run_in_background
boolean
false
Whether to run the command in background
执行 shell 命令。
字段类型示例说明
command
string
"npm test"
要执行的 shell 命令
description
string
"Run test suite"
命令用途的可选说明
timeout
number
120000
可选超时时间,单位为毫秒
run_in_background
boolean
false
是否在后台运行命令
Write
Write
Creates or overwrites a file.
FieldTypeExampleDescription
file_path
string
"/path/to/file.txt"
Absolute path to the file to write
content
string
"file content"
Content to write to the file
创建或覆盖文件。
字段类型示例说明
file_path
string
"/path/to/file.txt"
要写入文件的绝对路径
content
string
"file content"
要写入文件的内容
Edit
Edit
Replaces a string in an existing file.
FieldTypeExampleDescription
file_path
string
"/path/to/file.txt"
Absolute path to the file to edit
old_string
string
"original text"
Text to find and replace
new_string
string
"replacement text"
Replacement text
replace_all
boolean
false
Whether to replace all occurrences
替换现有文件中的字符串。
字段类型示例说明
file_path
string
"/path/to/file.txt"
要编辑文件的绝对路径
old_string
string
"original text"
要查找并替换的文本
new_string
string
"replacement text"
替换文本
replace_all
boolean
false
是否替换所有匹配项
Read
Read
Reads file contents.
FieldTypeExampleDescription
file_path
string
"/path/to/file.txt"
Absolute path to the file to read
offset
number
10
Optional line number to start reading from
limit
number
50
Optional number of lines to read
读取文件内容。
字段类型示例说明
file_path
string
"/path/to/file.txt"
要读取文件的绝对路径
offset
number
10
可选起始行号
limit
number
50
可选读取行数
Glob
Glob
Finds files matching a glob pattern.
FieldTypeExampleDescription
pattern
string
"**/*.ts"
Glob pattern to match files against
path
string
"/path/to/dir"
Optional directory to search in. Defaults to current working directory
查找与 glob 模式匹配的文件。
字段类型示例说明
pattern
string
"**/*.ts"
用于匹配文件的 glob 模式
path
string
"/path/to/dir"
可选搜索目录,默认为当前工作目录
Grep
Grep
Searches file contents with regular expressions.
FieldTypeExampleDescription
pattern
string
"TODO.*fix"
Regular expression pattern to search for
path
string
"/path/to/dir"
Optional file or directory to search in
glob
string
"*.ts"
Optional glob pattern to filter files
output_mode
string
"content"
"content"
,
"files_with_matches"
, or
"count"
. Defaults to
"files_with_matches"
-i
boolean
true
Case insensitive search
multiline
boolean
false
Enable multiline matching
使用正则表达式搜索文件内容。
字段类型示例说明
pattern
string
"TODO.*fix"
要搜索的正则表达式模式
path
string
"/path/to/dir"
可选文件或目录搜索路径
glob
string
"*.ts"
可选文件 glob 过滤模式
output_mode
string
"content"
"content"
、
"files_with_matches"
或
"count"
,默认为
"files_with_matches"
-i
boolean
true
区分大小写
multiline
boolean
false
启用多行匹配
WebFetch
WebFetch
Fetches and processes web content.
FieldTypeExampleDescription
url
string
"https://example.com/api"
URL to fetch content from
prompt
string
"Extract the API endpoints"
Prompt to run on the fetched content
获取并处理 Web 内容。
字段类型示例说明
url
string
"https://example.com/api"
要获取内容的 URL
prompt
string
"Extract the API endpoints"
用于处理已获取内容的 prompt
WebSearch
WebSearch
Searches the web.
FieldTypeExampleDescription
query
string
"react hooks best practices"
Search query
allowed_domains
array
["docs.example.com"]
Optional: only include results from these domains
blocked_domains
array
["spam.example.com"]
Optional: exclude results from these domains
搜索 Web。
字段类型示例说明
query
string
"react hooks best practices"
搜索查询
allowed_domains
array
["docs.example.com"]
可选,仅包含这些域名的结果
blocked_domains
array
["spam.example.com"]
可选,排除这些域名的结果
Task
Task
Spawns a subagent.
FieldTypeExampleDescription
prompt
string
"Find all API endpoints"
The task for the agent to perform
description
string
"Find API endpoints"
Short description of the task
subagent_type
string
"Explore"
Type of specialized agent to use
model
string
"sonnet"
Optional model alias to override the default
创建 subagent。
字段类型示例说明
prompt
string
"Find all API endpoints"
Agent 要执行的任务
description
string
"Find API endpoints"
任务的简短说明
subagent_type
string
"Explore"
要使用的专用 Agent 类型
model
string
"sonnet"
可选模型别名,用于覆盖默认模型

PreToolUse decision control

PreToolUse 决策控制

PreToolUse
hooks can control whether a tool call proceeds. Unlike other hooks that use a top-level
decision
field, PreToolUse returns its decision inside a
hookSpecificOutput
object. This gives it richer control: three outcomes (allow, deny, or ask) plus the ability to modify tool input before execution.
FieldDescription
permissionDecision
"allow"
bypasses the permission system,
"deny"
prevents the tool call,
"ask"
prompts the user to confirm
permissionDecisionReason
For
"allow"
and
"ask"
, shown to the user but not Claude. For
"deny"
, shown to Claude
updatedInput
Modifies the tool's input parameters before execution. Combine with
"allow"
to auto-approve, or
"ask"
to show the modified input to the user
additionalContext
String added to Claude's context before the tool executes
json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}
<Note> PreToolUse previously used top-level `decision` and `reason` fields, but these are deprecated for this event. Use `hookSpecificOutput.permissionDecision` and `hookSpecificOutput.permissionDecisionReason` instead. The deprecated values `"approve"` and `"block"` map to `"allow"` and `"deny"` respectively. Other events like PostToolUse and Stop continue to use top-level `decision` and `reason` as their current format. </Note>
PreToolUse Hooks 可以控制工具调用是否继续。与使用顶层
decision
字段的其他 Hooks 不同,PreToolUse 在
hookSpecificOutput
对象中返回决策。因此它提供更丰富的控制:允许、拒绝或请求确认三种结果,并且可以在执行前修改工具输入。
字段说明
permissionDecision
"allow"
绕过权限系统,
"deny"
阻止工具调用,
"ask"
提示用户确认
permissionDecisionReason
对于
"allow"
和
"ask"
,展示给用户但不展示给 Claude;对于
"deny"
,展示给 Claude
updatedInput
在执行前修改工具输入参数。与
"allow"
结合使用可自动批准,与
"ask"
结合使用可向用户展示修改后的输入
additionalContext
在工具执行前添加到 Claude 上下文的字符串
json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}
<Note> PreToolUse 过去使用顶层 `decision` 和 `reason` 字段,但这些字段现已弃用。请改用 `hookSpecificOutput.permissionDecision` 和 `hookSpecificOutput.permissionDecisionReason`。弃用值 `"approve"` 和 `"block"` 分别映射到 `"allow"` 和 `"deny"`。PostToolUse 和 Stop 等其他事件仍使用顶层 `decision` 和 `reason` 作为当前格式。 </Note>

PermissionRequest

PermissionRequest

Runs when the user is shown a permission dialog. Use PermissionRequest decision control to allow or deny on behalf of the user.
Matches on tool name, same values as PreToolUse.
在向用户显示权限对话框时运行。 使用 PermissionRequest 决策控制代表用户允许或拒绝。
按工具名称匹配,取值与 PreToolUse 相同。

PermissionRequest input

PermissionRequest 输入

PermissionRequest hooks receive
tool_name
and
tool_input
fields like PreToolUse hooks, but without
tool_use_id
. An optional
permission_suggestions
array contains the "always allow" options the user would normally see in the permission dialog. The difference is when the hook fires: PermissionRequest hooks run when a permission dialog is about to be shown to the user, while PreToolUse hooks run before tool execution regardless of permission status.
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PermissionRequest",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules",
    "description": "Remove node_modules directory"
  },
  "permission_suggestions": [
    { "type": "toolAlwaysAllow", "tool": "Bash" }
  ]
}
PermissionRequest Hooks 接收与 PreToolUse Hooks 类似的
tool_name
和
tool_input
字段,但不包含
tool_use_id
。可选的
permission_suggestions
数组包含用户通常会在权限对话框中看到的“始终允许”选项。两者区别在于触发时机:PermissionRequest Hooks 在权限对话框即将向用户显示时运行,而 PreToolUse Hooks 无论权限状态如何,都会在工具执行前运行。
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PermissionRequest",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules",
    "description": "Remove node_modules directory"
  },
  "permission_suggestions": [
    { "type": "toolAlwaysAllow", "tool": "Bash" }
  ]
}

PermissionRequest decision control

PermissionRequest 决策控制

PermissionRequest
hooks can allow or deny permission requests. In addition to the JSON output fields available to all hooks, your hook script can return a
decision
object with these event-specific fields:
FieldDescription
behavior
"allow"
grants the permission,
"deny"
denies it
updatedInput
For
"allow"
only: modifies the tool's input parameters before execution
updatedPermissions
For
"allow"
only: applies permission rule updates, equivalent to the user selecting an "always allow" option
message
For
"deny"
only: tells Claude why the permission was denied
interrupt
For
"deny"
only: if
true
, stops Claude
json
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}
PermissionRequest Hooks 可以允许或拒绝权限请求。除所有 Hook 均可使用的 JSON 输出字段外,Hook 脚本还可以返回包含以下事件专用字段的
decision
对象:
字段说明
behavior
"allow"
授予权限,
"deny"
拒绝权限
updatedInput
仅用于
"allow"
:在执行前修改工具输入参数
updatedPermissions
仅用于
"allow"
:应用权限规则更新,等效于用户选择“始终允许”选项
message
仅用于
"deny"
:告知 Claude 拒绝权限的原因
interrupt
仅用于
"deny"
:如果为
true
,则停止 Claude
json
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}

PostToolUse

PostToolUse

Runs immediately after a tool completes successfully.
Matches on tool name, same values as PreToolUse.
在工具成功完成后立即运行。
按工具名称匹配,取值与 PreToolUse 相同。

PostToolUse input

PostToolUse 输入

PostToolUse
hooks fire after a tool has already executed successfully. The input includes both
tool_input
, the arguments sent to the tool, and
tool_response
, the result it returned. The exact schema for both depends on the tool.
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  },
  "tool_use_id": "toolu_01ABC123..."
}
PostToolUse Hooks 在工具成功执行后触发。输入同时包括
tool_input
(发送给工具的参数)和
tool_response
(工具返回的结果)。两者的确切 schema 取决于工具。
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  },
  "tool_use_id": "toolu_01ABC123..."
}

PostToolUse decision control

PostToolUse 决策控制

PostToolUse
hooks can provide feedback to Claude after tool execution. In addition to the JSON output fields available to all hooks, your hook script can return these event-specific fields:
FieldDescription
decision
"block"
prompts Claude with the
reason
. Omit to allow the action to proceed
reason
Explanation shown to Claude when
decision
is
"block"
additionalContext
Additional context for Claude to consider
updatedMCPToolOutput
For MCP tools only: replaces the tool's output with the provided value
json
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude"
  }
}
PostToolUse Hooks 可以在工具执行后向 Claude 提供反馈。除所有 Hook 均可使用的 JSON 输出字段外,还可以返回以下事件专用字段:
字段说明
decision
"block"
会使用
reason
提示 Claude。省略则允许操作继续
reason
当
decision
为
"block"
时展示给 Claude 的说明
additionalContext
Claude 需要考虑的额外上下文
updatedMCPToolOutput
仅用于 MCP 工具:使用提供的值替换工具输出
json
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude"
  }
}

PostToolUseFailure

PostToolUseFailure

Runs when a tool execution fails. This event fires for tool calls that throw errors or return failure results. Use this to log failures, send alerts, or provide corrective feedback to Claude.
Matches on tool name, same values as PreToolUse.
在工具执行失败时运行。对于抛出错误或返回失败结果的工具调用,会触发此事件。可用于记录失败、发送警报或向 Claude 提供纠正反馈。
按工具名称匹配,取值与 PreToolUse 相同。

PostToolUseFailure input

PostToolUseFailure 输入

PostToolUseFailure hooks receive the same
tool_name
and
tool_input
fields as PostToolUse, along with error information as top-level fields:
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  },
  "tool_use_id": "toolu_01ABC123...",
  "error": "Command exited with non-zero status code 1",
  "is_interrupt": false
}
FieldDescription
error
String describing what went wrong
is_interrupt
Optional boolean indicating whether the failure was caused by user interruption
PostToolUseFailure Hooks 接收与 PostToolUse 相同的
tool_name
和
tool_input
字段,并将错误信息作为顶层字段提供:
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  },
  "tool_use_id": "toolu_01ABC123...",
  "error": "Command exited with non-zero status code 1",
  "is_interrupt": false
}
字段说明
error
描述问题的字符串
is_interrupt
可选布尔值,指示失败是否由用户中断引起

PostToolUseFailure decision control

PostToolUseFailure 决策控制

PostToolUseFailure
hooks can provide context to Claude after a tool failure. In addition to the JSON output fields available to all hooks, your hook script can return these event-specific fields:
FieldDescription
additionalContext
Additional context for Claude to consider alongside the error
json
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUseFailure",
    "additionalContext": "Additional information about the failure for Claude"
  }
}
PostToolUseFailure Hooks 可以在工具失败后向 Claude 提供上下文。除所有 Hook 均可使用的 JSON 输出字段外,还可以返回以下事件专用字段:
字段说明
additionalContext
Claude 需要考虑的额外上下文,与错误一同提供
json
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUseFailure",
    "additionalContext": "Additional information about the failure for Claude"
  }
}

Notification

Notification

Runs when Claude Code sends notifications. Matches on notification type:
permission_prompt
,
idle_prompt
,
auth_success
,
elicitation_dialog
. Omit the matcher to run hooks for all notification types.
Use separate matchers to run different handlers depending on the notification type. This configuration triggers a permission-specific alert script when Claude needs permission approval and a different notification when Claude has been idle:
json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-alert.sh"
          }
        ]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/idle-notification.sh"
          }
        ]
      }
    ]
  }
}
在 Claude Code 发送通知时运行。按通知类型匹配:
permission_prompt
、
idle_prompt
、
auth_success
、
elicitation_dialog
。省略 matcher 可运行所有通知类型的 Hooks。
使用不同的 matcher,可以根据通知类型运行不同的处理器。以下配置会在 Claude 需要权限批准时触发权限专用警报脚本,并在 Claude 空闲时触发另一条通知:
json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-alert.sh"
          }
        ]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/idle-notification.sh"
          }
        ]
      }
    ]
  }
}

Notification input

Notification 输入

In addition to the common input fields, Notification hooks receive
message
with the notification text, an optional
title
, and
notification_type
indicating which type fired.
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission to use Bash",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}
Notification hooks cannot block or modify notifications. In addition to the JSON output fields available to all hooks, you can return
additionalContext
to add context to the conversation:
FieldDescription
additionalContext
String added to Claude's context
除通用输入字段外,Notification Hooks 还会接收包含通知文本的
message
、可选的
title
,以及指明触发类型的
notification_type
。
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission to use Bash",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}
Notification Hooks 无法阻止或修改通知。除所有 Hook 均可使用的 JSON 输出字段外,还可以返回
additionalContext
,将上下文添加到对话:
字段说明
additionalContext
添加到 Claude 上下文的字符串

SubagentStart

SubagentStart

Runs when a Claude Code subagent is spawned via the Task tool. Supports matchers to filter by agent type name (built-in agents like
Bash
,
Explore
,
Plan
, or custom agent names from
.claude/agents/
).
在通过 Task 工具创建 Claude Code subagent 时运行。支持使用 matcher 按 Agent 类型名称筛选,例如内置 Agent
Bash
、
Explore
、
Plan
,或
.claude/agents/
中的自定义 Agent 名称。

SubagentStart input

SubagentStart 输入

In addition to the common input fields, SubagentStart hooks receive
agent_id
with the unique identifier for the subagent and
agent_type
with the agent name (built-in agents like
"Bash"
,
"Explore"
,
"Plan"
, or custom agent names).
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}
SubagentStart hooks cannot block subagent creation, but they can inject context into the subagent. In addition to the JSON output fields available to all hooks, you can return:
FieldDescription
additionalContext
String added to the subagent's context
json
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Follow security guidelines for this task"
  }
}
除通用输入字段外,SubagentStart Hooks 还会接收
agent_id
(subagent 的唯一标识符)和
agent_type
(Agent 名称,如
"Bash"
、
"Explore"
、
"Plan"
或自定义 Agent 名称)。
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}
SubagentStart Hooks 无法阻止 subagent 创建,但可以向 subagent 注入上下文。除所有 Hook 均可使用的 JSON 输出字段外,还可以返回:
字段说明
additionalContext
添加到 subagent 上下文的字符串
json
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Follow security guidelines for this task"
  }
}

SubagentStop

SubagentStop

Runs when a Claude Code subagent has finished responding. Matches on agent type, same values as SubagentStart.
在 Claude Code subagent 完成响应时运行。按 Agent 类型匹配,取值与 SubagentStart 相同。

SubagentStop input

SubagentStop 输入

In addition to the common input fields, SubagentStop hooks receive
stop_hook_active
,
agent_id
,
agent_type
, and
agent_transcript_path
. The
agent_type
field is the value used for matcher filtering. The
transcript_path
is the main session's transcript, while
agent_transcript_path
is the subagent's own transcript stored in a nested
subagents/
folder.
json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl"
}
SubagentStop hooks use the same decision control format as Stop hooks.
SubagentStop Hooks 接收
stop_hook_active
、
agent_id
、
agent_type
和
agent_transcript_path
。
agent_type
字段是用于 matcher 筛选的值。
transcript_path
是主会话的记录,而
agent_transcript_path
是 subagent 自己的记录,存储在嵌套的
subagents/
文件夹中。
json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl"
}
SubagentStop Hooks 使用与 Stop Hooks相同的决策控制格式。

Stop

Stop

Runs when the main Claude Code agent has finished responding. Does not run if the stoppage occurred due to a user interrupt.
在主 Claude Code Agent 完成响应时运行。如果停止是由用户中断导致的,则不会运行。

Stop input

Stop 输入

In addition to the common input fields, Stop hooks receive
stop_hook_active
. This field is
true
when Claude Code is already continuing as a result of a stop hook. Check this value or process the transcript to prevent Claude Code from running indefinitely.
json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}
除通用输入字段外,Stop Hooks 还会接收
stop_hook_active
。当 Claude Code 已经因 Stop Hook 而继续工作时,该字段为
true
。应检查此值或处理记录,以防止 Claude Code 无限运行。
json
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true
}

Stop decision control

Stop 决策控制

Stop
and
SubagentStop
hooks can control whether Claude continues. In addition to the JSON output fields available to all hooks, your hook script can return these event-specific fields:
FieldDescription
decision
"block"
prevents Claude from stopping. Omit to allow Claude to stop
reason
Required when
decision
is
"block"
. Tells Claude why it should continue
json
{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}
Stop 和 SubagentStop Hooks 可以控制 Claude 是否继续。除所有 Hook 均可使用的 JSON 输出字段外,还可以返回以下事件专用字段:
字段说明
decision
"block"
阻止 Claude 停止。省略则允许 Claude 停止
reason
当
decision
为
"block"
时必需。告知 Claude 为什么应继续工作
json
{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}

PreCompact

PreCompact

Runs before Claude Code is about to run a compact operation.
The matcher value indicates whether compaction was triggered manually or automatically:
MatcherWhen it fires
manual
/compact
auto
Auto-compact when the context window is full
在 Claude Code 即将执行压缩操作之前运行。
Matcher 值表示压缩是由手动还是自动触发:
Matcher触发时机
manual
/compact
auto
上下文窗口填满时自动压缩

PreCompact input

PreCompact 输入

In addition to the common input fields, PreCompact hooks receive
trigger
and
custom_instructions
. For
manual
,
custom_instructions
contains what the user passes into
/compact
. For
auto
,
custom_instructions
is empty.
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}
除通用输入字段外,PreCompact Hooks 还会接收
trigger
和
custom_instructions
。对于
manual
,
custom_instructions
包含用户传入
/compact
的内容;对于
auto
,
custom_instructions
为空。
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}

SessionEnd

SessionEnd

Runs when a Claude Code session ends. Useful for cleanup tasks, logging session statistics, or saving session state. Supports matchers to filter by exit reason.
The
reason
field in the hook input indicates why the session ended:
ReasonDescription
clear
Session cleared with
/clear
command
logout
User logged out
prompt_input_exit
User exited while prompt input was visible
bypass_permissions_disabled
Bypass permissions mode was disabled
other
Other exit reasons
在 Claude Code 会话结束时运行。适用于清理任务、记录会话统计信息或保存会话状态。支持按退出原因匹配。
Hook 输入中的
reason
字段表示会话结束原因:
原因说明
clear
使用
/clear
命令清除会话
logout
用户退出登录
prompt_input_exit
显示 prompt 输入时用户退出
bypass_permissions_disabled
bypass permissions 模式被禁用
other
其他退出原因

SessionEnd input

SessionEnd 输入

In addition to the common input fields, SessionEnd hooks receive a
reason
field indicating why the session ended. See the reason table above for all values.
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}
SessionEnd hooks have no decision control. They cannot block session termination but can perform cleanup tasks.
除通用输入字段外,SessionEnd Hooks 还会接收
reason
字段,用于指明会话结束原因。有关全部值,请参阅上方的 reason table。
json
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}
SessionEnd Hooks 没有决策控制。它们无法阻止会话终止,但可以执行清理任务。

Prompt-based hooks

基于 prompt 的 Hooks

In addition to Bash command hooks (
type: "command"
), Claude Code supports prompt-based hooks (
type: "prompt"
) that use an LLM to evaluate whether to allow or block an action. Prompt-based hooks work with the following events:
PreToolUse
,
PostToolUse
,
PostToolUseFailure
,
PermissionRequest
,
UserPromptSubmit
,
Stop
, and
SubagentStop
.
除 Bash command Hooks(
type: "command"
)外,Claude Code 还支持基于 prompt 的 Hooks(
type: "prompt"
),使用 LLM 判断是否允许或阻止操作。基于 prompt 的 Hooks 支持以下事件:
PreToolUse
、
PostToolUse
、
PostToolUseFailure
、
PermissionRequest
、
UserPromptSubmit
、
Stop
和
SubagentStop
。

How prompt-based hooks work

基于 prompt 的 Hooks 如何工作

Instead of executing a Bash command, prompt-based hooks:
  1. Send the hook input and your prompt to a Claude model, Haiku by default
  2. The LLM responds with structured JSON containing a decision
  3. Claude Code processes the decision automatically
基于 prompt 的 Hooks 不会执行 Bash 命令,而是:
  1. 将 Hook 输入和你的 prompt 发送给 Claude 模型,默认使用 Haiku
  2. LLM 返回包含决策的结构化 JSON
  3. Claude Code 自动处理该决策

Prompt hook configuration

Prompt hook 配置

Set
type
to
"prompt"
and provide a
prompt
string instead of a
command
. Use the
$ARGUMENTS
placeholder to inject the hook's JSON input data into your prompt text. Claude Code sends the combined prompt and input to a fast Claude model, which returns a JSON decision.
This
Stop
hook asks the LLM to evaluate whether all tasks are complete before allowing Claude to finish:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
FieldRequiredDescription
type
yesMust be
"prompt"
prompt
yesThe prompt text to send to the LLM. Use
$ARGUMENTS
as a placeholder for the hook input JSON. If
$ARGUMENTS
is not present, input JSON is appended to the prompt
model
noModel to use for evaluation. Defaults to a fast model
timeout
noTimeout in seconds. Default: 30
将
type
设置为
"prompt"
,并提供
prompt
字符串而不是
command
。使用
$ARGUMENTS
占位符将 Hook 的 JSON 输入数据注入 prompt 文本。Claude Code 会将组合后的 prompt 和输入发送给快速 Claude 模型,该模型返回 JSON 决策。
此
Stop
Hook 让 LLM 在允许 Claude 完成前评估所有任务是否已完成:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
字段必需说明
type
是必须为
"prompt"
prompt
是发送给 LLM 的 prompt 文本。使用
$ARGUMENTS
作为 Hook 输入 JSON 的占位符。如果不存在
$ARGUMENTS
,输入 JSON 会追加到 prompt 末尾
model
否用于评估的模型,默认为快速模型
timeout
否超时时间,单位为秒,默认值为 30

Response schema

响应 schema

The LLM must respond with JSON containing:
json
{
  "ok": true | false,
  "reason": "Explanation for the decision"
}
FieldDescription
ok
true
allows the action,
false
prevents it
reason
Required when
ok
is
false
. Explanation shown to Claude
LLM 必须返回包含以下内容的 JSON:
json
{
  "ok": true | false,
  "reason": "Explanation for the decision"
}
字段说明
ok
true
允许操作,
false
阻止操作
reason
当
ok
为
false
时必需。展示给 Claude 的说明

Example: Multi-criteria Stop hook

示例:多条件 Stop hook

This
Stop
hook uses a detailed prompt to check three conditions before allowing Claude to stop. If
"ok"
is
false
, Claude continues working with the provided reason as its next instruction.
SubagentStop
hooks use the same format to evaluate whether a subagent should stop:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}
此
Stop
Hook 使用详细 prompt,在允许 Claude 停止前检查三个条件。如果
"ok"
为
false
,Claude 会继续工作,并将提供的
reason
作为下一条指令。
SubagentStop
Hooks 使用相同格式来评估 subagent 是否应停止:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Agent-based hooks

基于 Agent 的 Hooks

Agent-based hooks (
type: "agent"
) are like prompt-based hooks but with multi-turn tool access. Instead of a single LLM call, an agent hook spawns a subagent that can read files, search code, and inspect the codebase to verify conditions. Agent hooks support the same events as prompt-based hooks.
基于 Agent 的 Hooks(
type: "agent"
)与基于 prompt 的 Hooks 类似,但支持多轮工具访问。Agent Hook 不会只发起单次 LLM 调用,而是创建 subagent,使其能够读取文件、搜索代码并检查代码库来验证条件。Agent Hooks 支持与基于 prompt 的 Hooks 相同的事件。

How agent hooks work

Agent hooks 如何工作

When an agent hook fires:
  1. Claude Code spawns a subagent with your prompt and the hook's JSON input
  2. The subagent can use tools like Read, Grep, and Glob to investigate
  3. After up to 50 turns, the subagent returns a structured
    { "ok": true/false }
    decision
  4. Claude Code processes the decision the same way as a prompt hook
Agent hooks are useful when verification requires inspecting actual files or test output, not just evaluating the hook input data alone.
Agent Hook 触发时:
  1. Claude Code 使用你的 prompt 和 Hook 的 JSON 输入创建 subagent
  2. Subagent 可以使用 Read、Grep 和 Glob 等工具进行调查
  3. 最多经过 50 轮后,subagent 返回结构化的
    { "ok": true/false }
    决策
  4. Claude Code 以与 prompt Hook 相同的方式处理该决策
当验证需要检查实际文件或测试输出,而不仅是评估 Hook 输入数据时,Agent Hooks 非常有用。

Agent hook configuration

Agent hook 配置

Set
type
to
"agent"
and provide a
prompt
string. The configuration fields are the same as prompt hooks, with a longer default timeout:
FieldRequiredDescription
type
yesMust be
"agent"
prompt
yesPrompt describing what to verify. Use
$ARGUMENTS
as a placeholder for the hook input JSON
model
noModel to use. Defaults to a fast model
timeout
noTimeout in seconds. Default: 60
The response schema is the same as prompt hooks:
{ "ok": true }
to allow or
{ "ok": false, "reason": "..." }
to block.
This
Stop
hook verifies that all unit tests pass before allowing Claude to finish:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}
将
type
设置为
"agent"
,并提供
prompt
字符串。配置字段与 prompt Hooks相同,但默认超时时间更长:
字段必需说明
type
是必须为
"agent"
prompt
是描述验证内容的 prompt。使用
$ARGUMENTS
作为 Hook 输入 JSON 的占位符
model
否要使用的模型,默认为快速模型
timeout
否超时时间,单位为秒,默认值为 60
响应 schema 与 prompt Hooks 相同:
{ "ok": true }
允许操作,
{ "ok": false, "reason": "..." }
阻止操作。
此
Stop
Hook 在允许 Claude 完成前验证所有单元测试是否通过:
json
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

Run hooks in the background

在后台运行 Hooks

By default, hooks block Claude's execution until they complete. For long-running tasks like deployments, test suites, or external API calls, set
"async": true
to run the hook in the background while Claude continues working. Async hooks cannot block or control Claude's behavior: response fields like
decision
,
permissionDecision
, and
continue
have no effect, because the action they would have controlled has already completed.
默认情况下,Hooks 会阻塞 Claude 的执行,直到完成。对于部署、测试套件或外部 API 调用等耗时任务,可设置
"async": true
,让 Hook 在后台运行,同时 Claude 继续工作。异步 Hooks 无法阻止或控制 Claude 的行为:
decision
、
permissionDecision
和
continue
等响应字段均不起作用,因为其原本要控制的操作已经完成。

Configure an async hook

配置异步 Hook

Add
"async": true
to a command hook's configuration to run it in the background without blocking Claude. This field is only available on
type: "command"
hooks.
This hook runs a test script after every
Write
tool call. Claude continues working immediately while
run-tests.sh
executes for up to 120 seconds. When the script finishes, its output is delivered on the next conversation turn:
json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}
The
timeout
field sets the maximum time in seconds for the background process. If not specified, async hooks use the same 10-minute default as sync hooks.
在 command Hook 的配置中添加
"async": true
,即可在后台运行而不阻塞 Claude。此字段仅适用于
type: "command"
Hooks。
此 Hook 会在每次
Write
工具调用后运行测试脚本。Claude 会立即继续工作,而
run-tests.sh
最多运行 120 秒。脚本完成后,其输出会在下一轮对话中送达:
json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}
timeout
字段设置后台进程的最长运行时间,单位为秒。如果未指定,异步 Hooks 与同步 Hooks 一样使用默认的 10 分钟。

How async hooks execute

异步 Hooks 的执行方式

When an async hook fires, Claude Code starts the hook process and immediately continues without waiting for it to finish. The hook receives the same JSON input via stdin as a synchronous hook.
After the background process exits, if the hook produced a JSON response with a
systemMessage
or
additionalContext
field, that content is delivered to Claude as context on the next conversation turn.
异步 Hook 触发时,Claude Code 会启动 Hook 进程并立即继续,不等待其完成。Hook 通过 stdin 接收与同步 Hook 相同的 JSON 输入。
后台进程退出后,如果 Hook 产生了包含
systemMessage
或
additionalContext
字段的 JSON 响应,该内容会在下一轮对话中作为上下文送达 Claude。

Example: run tests after file changes

示例:文件变更后运行测试

This hook starts a test suite in the background whenever Claude writes a file, then reports the results back to Claude when the tests finish. Save this script to
.claude/hooks/run-tests-async.sh
in your project and make it executable with
chmod +x
:
bash
#!/bin/bash
此 Hook 会在 Claude 写入文件时在后台启动测试套件,并在测试完成后将结果报告给 Claude。将此脚本保存到项目中的
.claude/hooks/run-tests-async.sh
,并通过
chmod +x
使其可执行:
bash
#!/bin/bash

run-tests-async.sh

run-tests-async.sh

Read hook input from stdin

Read hook input from stdin

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

Only run tests for source files

Only run tests for source files

if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then exit 0 fi
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then exit 0 fi

Run tests and report results via systemMessage

Run tests and report results via systemMessage

RESULT=$(npm test 2>&1) EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then echo "{"systemMessage": "Tests passed after editing $FILE_PATH"}" else echo "{"systemMessage": "Tests failed after editing $FILE_PATH: $RESULT"}" fi

Then add this configuration to `.claude/settings.json` in your project root. The `async: true` flag lets Claude keep working while tests run:

```json  theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests-async.sh",
            "async": true,
            "timeout": 300
          }
        ]
      }
    ]
  }
}
RESULT=$(npm test 2>&1) EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then echo "{"systemMessage": "Tests passed after editing $FILE_PATH"}" else echo "{"systemMessage": "Tests failed after editing $FILE_PATH: $RESULT"}" fi

然后将以下配置添加到项目根目录的 `.claude/settings.json`。`async: true` 标志可让 Claude 在测试运行时继续工作:

```json  theme={null}
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests-async.sh",
            "async": true,
            "timeout": 300
          }
        ]
      }
    ]
  }
}

Limitations

限制

Async hooks have several constraints compared to synchronous hooks:
  • Only
    type: "command"
    hooks support
    async
    . Prompt-based hooks cannot run asynchronously.
  • Async hooks cannot block tool calls or return decisions. By the time the hook completes, the triggering action has already proceeded.
  • Hook output is delivered on the next conversation turn. If the session is idle, the response waits until the next user interaction.
  • Each execution creates a separate background process. There is no deduplication across multiple firings of the same async hook.
异步 Hooks 与同步 Hooks 相比存在以下限制:
  • 只有
    type: "command"
    Hooks 支持
    async
    。基于 prompt 的 Hooks 无法异步运行。
  • 异步 Hooks 无法阻止工具调用或返回决策。Hook 完成时,触发它的操作已经继续执行。
  • Hook 输出会在下一轮对话中送达。如果会话处于空闲状态,响应会等到下一次用户交互。
  • 每次执行都会创建独立的后台进程。同一个异步 Hook 多次触发时不会跨执行去重。

Security considerations

安全注意事项

Disclaimer

免责声明

Hooks run with your system user's full permissions.
<Warning> Hooks execute shell commands with your full user permissions. They can modify, delete, or access any files your user account can access. Review and test all hook commands before adding them to your configuration. </Warning>
Hooks 以系统用户的完整权限运行。
<Warning> Hooks 会使用你的完整用户权限执行 shell 命令。它们可以修改、删除或访问用户账户有权访问的任何文件。将 Hook 命令添加到配置前,请先审查并测试所有命令。 </Warning>

Security best practices

安全最佳实践

Keep these practices in mind when writing hooks:
  • Validate and sanitize inputs: never trust input data blindly
  • Always quote shell variables: use
    "$VAR"
    not
    $VAR
  • Block path traversal: check for
    ..
    in file paths
  • Use absolute paths: specify full paths for scripts, using
    "$CLAUDE_PROJECT_DIR"
    for the project root
  • Skip sensitive files: avoid
    .env
    ,
    .git/
    , keys, etc.
编写 Hooks 时请注意以下实践:
  • 验证并清理输入:绝不要盲目信任输入数据
  • 始终为 shell 变量添加引号:使用
    "$VAR"
    ,而不是
    $VAR
  • 阻止路径遍历:检查文件路径中是否存在
    ..
  • 使用绝对路径:为脚本指定完整路径,并使用
    "$CLAUDE_PROJECT_DIR"
    表示项目根目录
  • 跳过敏感文件:避免处理
    .env
    、
    .git/
    、密钥等内容

Debug hooks

调试 Hooks

Run
claude --debug
to see hook execution details, including which hooks matched, their exit codes, and output. Toggle verbose mode with
Ctrl+O
to see hook progress in the transcript.
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
For troubleshooting common issues like hooks not firing, infinite Stop hook loops, or configuration errors, see Limitations and troubleshooting in the guide.
运行
claude --debug
可查看 Hook 执行详情,包括哪些 Hooks 匹配、退出码和输出。按
Ctrl+O
切换详细模式,即可在记录中查看 Hook 进度。
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Getting matching hook commands for PostToolUse with query: Write
[DEBUG] Found 1 hook matchers in settings
[DEBUG] Matched 1 hooks for query "Write"
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
有关 Hook 未触发、Stop Hook 无限循环或配置错误等常见问题的故障排除,请参阅指南中的 Limitations and troubleshooting。