Claude Code 钩子

Claude Code 钩子是用户自定义的 Shell 命令,会在 Claude Code 生命周期的特定节点自动执行。

借助钩子,你可以对 Claude Code 的行为实现精准控制,确保某些操作(如代码格式化、日志记录)必定触发,而非依赖大模型自主选择是否执行。

钩子的典型应用场景

钩子能帮你实现很多实用功能,常见场景包括:

  • 消息通知:当 Claude Code 等待输入或需要权限时,自动发送桌面/邮件提醒
  • 自动格式化:编辑 .ts 文件后自动运行 prettier,修改 .go 文件后执行 gofmt
  • 操作日志:记录 Claude 执行的所有命令,用于合规审计或调试排障
  • 代码规范校验:若 Claude 生成的代码不符合项目规范(如命名规则),自动给出反馈
  • 文件权限管控:阻止 Claude 修改生产环境配置文件或敏感目录(如 .env.git

相比于通过提示词约束 Claude 的行为,钩子是应用级的硬规则,只要触发对应事件就会强制执行,稳定性和可靠性更高。

重要安全提醒

钩子运行时会直接使用当前系统环境的凭证(如环境变量、用户权限),存在一定安全风险:

  • 恶意钩子代码可能泄露你的敏感数据(如 API 密钥、项目源码)
  • 错误的钩子命令可能导致文件误删、系统异常

必做安全操作

  1. 注册钩子前,务必逐行审查命令的逻辑和权限
  2. 避免在钩子中执行来源不明的脚本
  3. 详细安全最佳实践,可参考官方文档的 安全注意事项

钩子事件类型说明

Claude Code 内置了多个生命周期事件,你可以为不同事件绑定钩子命令。每个事件会传递不同的上下文数据,且对 Claude 行为的影响方式不同。

事件名称 触发时机 核心作用
PreToolUse 工具调用之前 可拦截工具执行(如阻止修改敏感文件),并向 Claude 反馈调整建议
PermissionRequest 弹出权限请求对话框时 自动批准或拒绝权限申请
PostToolUse 工具调用完成后 执行后置操作(如格式化代码、记录日志)
UserPromptSubmit 用户提交提示词后、Claude 处理前 预处理用户输入(如补充上下文信息)
Notification Claude 发送通知时 自定义通知方式(如桌面弹窗、短信提醒)
Stop Claude 完成响应时 执行收尾工作(如清理临时文件)
SubagentStop 子代理任务完成时 处理子代理的执行结果
PreCompact 即将执行上下文压缩操作时 自定义压缩规则
SessionStart 启动新会话或恢复旧会话时 初始化会话环境(如加载项目配置)
SessionEnd 会话结束时 保存会话数据、清理环境

快速入门:实现命令日志记录

下面以 记录 Claude 执行的所有 Bash 命令 为例,带你一步步完成钩子的配置和使用。

前置准备

安装 jq 工具(用于命令行解析 JSON 数据):

  • macOSbrew install jq
  • Linuxsudo apt install jq / sudo yum install jq
  • Windows:下载 jq 官方安装包,或通过 WSL 安装

步骤 1:打开钩子配置界面

在 Claude Code 的交互界面中,输入斜杠命令 /hooks,回车后选择要绑定的事件 —— 这里我们选 PreToolUse(工具调用前触发,适合记录命令)。

步骤 2:添加事件匹配器

匹配器的作用是 限定钩子的触发条件,只在指定工具被调用时执行钩子命令。

  1. 选择 + Add new matcher…
  2. 输入匹配关键词 Bash,表示仅当 Claude 调用 Bash 工具时触发钩子

    小技巧:输入 * 可以匹配所有工具,实现全局钩子

步骤 3:添加钩子命令

选择 + Add new hook…,输入以下命令(功能:提取命令内容和描述,写入日志文件):

jq -r '"\(.tool_input.command) - \(.tool_input.description // "无描述信息")"' >> ~/.claude/bash-command-log.txt

命令说明:

  • jq -r ...:提取 JSON 中的命令(command)和描述(description),无描述时显示"无描述信息"
  • >> ~/.claude/...:将内容追加写入用户主目录下的日志文件

步骤 4:选择配置存储位置

配置保存位置决定钩子的生效范围:

  • User settings:保存到用户级配置(~/.claude/settings.json),所有项目生效
  • Project settings:保存到当前项目配置(.claude/settings.local.json),仅当前项目生效

这里我们选 User settings,实现全局命令日志记录。选择后按 Esc 键退出配置界面,钩子即注册完成。

步骤 5:验证钩子配置

  1. 再次输入 /hooks 命令,可查看已配置的钩子列表
  2. 或直接打开配置文件 ~/.claude/settings.json,会看到如下配置内容:
    {
      "hooks": {
        "PreToolUse": [
          {
            "matcher": "Bash",
            "hooks": [
              {
                "type": "command",
                "command": "jq -r '\"\\(.tool_input.command) - \\(.tool_input.description // \"无描述信息\")\"' >> ~/.claude/bash-command-log.txt"
              }
            ]
          }
        ]
      }
    }

步骤 6:测试钩子效果

  1. 在 Claude Code 中输入指令:帮我执行 ls 命令
  2. 执行完成后,在终端中查看日志文件:
    cat ~/.claude/bash-command-log.txt
  3. 若日志中出现如下内容,说明钩子配置成功:
    ls - Lists files and directories

实用钩子示例

下面提供几个常用的钩子配置,你可以直接复制使用或按需修改。

📌 完整示例代码可参考官方仓库:bash 命令验证器示例

示例 1:自动格式化 TypeScript 文件

功能:编辑/写入 .ts 文件后,自动用 prettier 格式化代码

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write", // 匹配“编辑”和“写入”工具
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | { read file_path; if echo \"$file_path\" | grep -q '\\.ts$'; then npx prettier --write \"$file_path\"; fi; }"
          }
        ]
      }
    ]
  }
}

示例 2:自动修复 Markdown 文件格式

功能:为无语言标签的代码块自动补全标签、清理多余空行

第一步:添加钩子配置

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/markdown_formatter.py"
          }
        ]
      }
    ]
  }
}

第二步:创建格式化脚本

在项目目录下新建文件 .claude/hooks/markdown_formatter.py,粘贴以下代码:

实例

#!/usr/bin/env python3
"""
Markdown 格式化工具:自动补全代码块语言标签、清理多余空行
"""

import json
import sys
import re
import os

def detect_language(code):
    """根据代码内容自动检测编程语言"""
    code = code.strip()
    # 检测 JSON
    if re.search(r'^\s*[{\[]', code):
        try:
            json.loads(code)
            return 'json'
        except:
            pass
    # 检测 Python
    if re.search(r'^\s*def\s+\w+\s*\(', code, re.M) or re.search(r'^\s*(import|from)\s+\w+', code, re.M):
        return 'python'
    # 检测 JavaScript/TypeScript
    if re.search(r'\b(function\s+\w+\s*\(|const\s+\w+\s*=)', code) or re.search(r'=>|console\.(log|error)', code):
        return 'javascript'
    # 检测 Bash
    if re.search(r'^#!.*\b(bash|sh)\b', code, re.M) or re.search(r'\b(if|then|fi|for|in|do|done)\b', code):
        return 'bash'
    # 默认文本格式
    return 'text'

def format_markdown(content):
    """格式化 Markdown 内容"""
    # 为无标签代码块补全语言
    fence_pattern = r'(?ms)^([ \t]{0,3})```([^\n]*)\n(.*?)(\n\1```)\s*$'
    def add_lang(match):
        indent, info, body, closing = match.groups()
        if not info.strip():
            lang = detect_language(body)
            return f"{indent}```{lang}\n{body}{closing}\n"
        return match.group(0)
    content = re.sub(fence_pattern, add_lang, content)
    # 清理多余空行(仅清理代码块外的内容)
    content = re.sub(r'\n{3,}', '\n\n', content)
    return content.rstrip() + '\n'

if __name__ == "__main__":
    try:
        # 读取 Claude 传递的 JSON 数据
        input_data = json.load(sys.stdin)
        file_path = input_data.get('tool_input', {}).get('file_path', '')
        # 仅处理 .md/.mdx 文件
        if not file_path.endswith(('.md', '.mdx')):
            sys.exit(0)
        # 读取并格式化文件
        if os.path.exists(file_path):
            with open(file_path, 'r', encoding='utf-8') as f:
                content = f.read()
            formatted_content = format_markdown(content)
            # 仅在内容变化时写入
            if formatted_content != content:
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(formatted_content)
                print(f"已格式化 Markdown 文件:{file_path}")
    except Exception as e:
        print(f"格式化失败:{e}", file=sys.stderr)
        sys.exit(1)

第三步:赋予脚本执行权限

chmod +x .claude/hooks/markdown_formatter.py

示例 3:Claude 等待输入时发送桌面通知

功能:当 Claude 需要用户输入时,自动弹出桌面提醒(适用于 Linux/macOS)

{
  "hooks": {
    "Notification": [
      {
        "matcher": "", // 匹配所有通知事件
        "hooks": [
          {
            "type": "command",
            "command": "notify-send 'Claude Code 提示' '请你输入指令或确认权限'"
          }
        ]
      }
    ]
  }
}

示例 4:禁止修改敏感文件

功能:阻止 Claude 编辑 .envpackage-lock.json 等敏感文件

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "python3 -c \"import json, sys; data=json.load(sys.stdin); path=data.get('tool_input',{}).get('file_path',''); sys.exit(2 if any(p in path for p in ['.env', 'package-lock.json', '.git/']) else 0)\""
          }
        ]
      }
    ]
  }
}

说明:脚本返回状态码 2 时,Claude Code 会拦截此次工具调用,从而阻止文件修改。


Claude Code 钩子参考手册

配置文件路径

配置级别 文件路径 生效范围
用户级 ~/.claude/settings.json 所有项目
项目级 .claude/settings.json 当前项目
本地项目级(不提交) .claude/settings.local.json 当前项目,不纳入版本控制
托管策略级 管理员指定路径 企业/团队统一管控

核心配置结构

Hooks 按事件+匹配器组织,支持 command(执行 Shell 命令)和 prompt(调用 LLM 决策)两种类型。

{
  "hooks": {
    "【钩子事件名】": [
      {
        "matcher": "【工具匹配规则】", // 部分事件可省略
        "hooks": [
          {
            "type": "command/prompt",
            "command": "【Shell 命令】", // type=command 时必填
            "prompt": "【LLM 提示词】",  // type=prompt 时必填
            "timeout": 30 // 可选,超时时间(秒)
          }
        ]
      }
    ]
  }
}

匹配器规则(仅适用于工具类事件)

匹配规则 示例 说明
精确匹配 Write 仅匹配 Write 工具
多工具匹配 Edit | Write 匹配 EditWrite 工具
前缀匹配 Notebook.* 匹配所有以 Notebook 开头的工具
全匹配 * / 空字符串 匹配所有工具

特殊配置技巧

场景 配置方法 示例
引用项目内脚本 使用环境变量 $CLAUDE_PROJECT_DIR "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"
插件 Hooks 插件内配置 hooks/hooks.json,用 ${CLAUDE_PLUGIN_ROOT} 引用插件文件 "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh"
组件级 Hooks(Skill/Agent) 在组件 frontmatter 中定义,作用域仅限组件生命周期 见下方扩展配置表格

扩展配置(Skill/Agent/斜杠命令)

扩展配置允许直接在 Skill、Agent 或自定义斜杠命令的定义中内嵌 Hooks 配置,这类 Hooks 仅在对应组件被激活并运行时生效,组件执行完成后会自动清理,不会影响全局会话。

支持的钩子事件

仅支持 PreToolUsePostToolUseStop 三类事件,与全局 Hooks 功能一致,但作用域仅限当前 Skill/斜杠命令的生命周期。

特有配置项

  • once: true(可选):设置为 true 时,该 Hooks 在整个会话中仅运行一次,首次成功执行后会自动移除,避免重复触发。

完整配置示例

---
# Skill/斜杠命令的基础信息
name: secure-operations
description: 执行Shell命令前先做安全校验的工具
# Hooks 配置段
hooks:
  PreToolUse:
    # 匹配器:仅拦截Bash工具调用
    - matcher: "Bash"
      hooks:
        - type: "command"
          # 要执行的安全校验脚本
          command: "./scripts/security-check.sh"
          # 会话内仅执行一次
          once: true
          # 超时时间(秒),避免脚本卡死
          timeout: 15
---

Agent 中的 Hooks 配置

支持的钩子事件

同样仅支持 PreToolUsePostToolUseStop 三类事件,作用域仅限该子 Agent 的任务执行周期。

特有配置项

无额外专属配置项,不支持 once: true(Agent 每次执行任务时,Hooks 都会触发)。

完整配置示例

---
# Agent 的基础信息
name: code-reviewer
description: 自动审查代码修改并运行代码检查的子代理
# Hooks 配置段
hooks:
  PostToolUse:
    # 匹配器:拦截Edit(编辑)或Write(写入)工具
    - matcher: "Edit|Write"
      hooks:
        - type: "command"
          # 代码检查脚本,执行lint校验
          command: "./scripts/run-linter.sh"
          # 超时时间(秒)
          timeout: 30
---

注意事项:

  1. 组件内 Hooks 的匹配器规则与全局 Hooks 完全一致:支持精确匹配(如 "Write")、多工具匹配(如 "Edit|Write")、通配匹配("*"),且区分大小写;
  2. 组件内 Hooks 与全局 Hooks 会并行执行:若全局和组件内同时配置了针对同一事件的 Hooks,触发时两类 Hooks 会一起运行,互不冲突;
  3. 配置格式要求:组件内 Hooks 需写在 frontmatter(--- 包裹的区域)中,遵循 YAML 语法,缩进错误会导致配置失效;
  4. 脚本路径建议:优先使用相对路径(如 ./scripts/xxx.sh),或借助 $CLAUDE_PROJECT_DIR 环境变量指定绝对路径,确保组件在任意目录下都能找到脚本。

钩子事件--工具类事件(支持匹配器)

事件名 触发时机 常见匹配器 核心作用
PreToolUse 工具调用 Bash/Edit/Write/Read 拦截工具执行、修改入参、自动批准/拒绝权限
PermissionRequest 弹出权限请求对话框时 PreToolUse 自动处理权限申请,无需用户手动确认
PostToolUse 工具调用成功后 PreToolUse 执行后置操作(如代码格式化、日志记录)
Notification Claude 发送通知时 permission_prompt/idle_prompt/auth_success 自定义通知方式(如桌面弹窗、邮件提醒)
PreCompact 执行上下文压缩操作前 manual(手动触发)/auto(自动触发) 自定义压缩规则、备份重要上下文

钩子事件--会话/任务类事件(无匹配器)

事件名 触发时机 核心作用
UserPromptSubmit 用户提交提示后、Claude 处理前 验证提示合法性、补充上下文信息
Stop 主 Agent 完成响应时(用户中断不触发) 智能判断是否需要继续执行任务
SubagentStop 子 Agent 任务完成时 评估子任务结果,决定是否终止
SessionStart 启动/恢复会话时 初始化环境、加载项目配置、设置持久化环境变量
SessionEnd 会话结束时 清理临时文件、记录会话日志、保存工作状态

钩子类型对比

Claude Code 支持两种钩子类型,满足不同场景需求。

特性 command 类型(命令钩子) prompt 类型(提示词钩子)
执行方式 运行 Shell 命令/脚本 调用 LLM(默认 Haiku 模型)做智能决策
决策逻辑 基于代码逻辑的确定性判断 基于上下文的灵活语义判断
配置难度 需要编写脚本,门槛较高 只需写提示词,简单易上手
响应速度 快(本地执行) 较慢(需 API 调用)
适用场景 代码格式化、日志记录、权限拦截等固定规则 任务完成度评估、复杂意图判断等灵活场景
核心配置 command + timeout prompt + timeout(可引用 $ARGUMENTS 占位符)

输入与输出 -- 钩子输入(stdin 传入 JSON)

所有钩子都会收到通用字段,部分事件附带特定字段。

字段类型 通用字段 说明
基础信息 session_id 会话唯一标识
transcript_path 对话记录文件路径
cwd 钩子执行时的当前工作目录
permission_mode 当前权限模式(default/plan/acceptEdits 等)
hook_event_name 当前触发的钩子事件名

各事件特有字段示例

事件名 特有字段 示例
PreToolUse tool_name/tool_input {"tool_name":"Write","tool_input":{"file_path":"/test.txt"}}
UserPromptSubmit prompt {"prompt":"帮我写一个排序函数"}
SessionEnd reason {"reason":"clear/logout/other"}

钩子输出(两种方式)

方式1:退出代码(简单场景)

通过退出代码传递执行状态,stdout/stderr 用于反馈信息。

退出代码 含义 行为说明
0 执行成功 stdout 可返回 JSON 做高级控制;部分事件(如 UserPromptSubmit)会将 stdout 加入上下文
2 阻止操作 仅使用 stderr 作为错误消息反馈给 Claude,阻止当前事件继续执行
其他非零值 非阻塞错误 stderr 仅在详细模式(ctrl+o)显示,不影响事件执行

退出代码 2 在各事件中的行为

事件名 触发行为
PreToolUse 阻止工具调用,向 Claude 展示 stderr
UserPromptSubmit 阻止提示处理,擦除用户输入
Stop 阻止 Claude 停止,强制继续工作
PostToolUse/Notification 仅展示 stderr,不影响已完成操作

方式2:JSON 输出(高级场景)

退出代码为 0 时,可通过 stdout 返回 JSON 实现精细化控制,核心字段如下:

通用 JSON 字段 作用
continue: true/false 是否允许事件继续执行(false 优先于其他规则)
stopReason continue=false 时,展示给用户的原因
systemMessage 向用户显示的警告信息

事件特有 JSON 字段示例

事件名 特有字段 示例
PreToolUse permissionDecision(allow/deny/ask) {"hookSpecificOutput":{"permissionDecision":"allow","updatedInput":{"file_path":"/new.txt"}}}
UserPromptSubmit decision: block/undefined {"decision":"block","reason":"提示包含敏感信息"}
PostToolUse additionalContext {"hookSpecificOutput":{"additionalContext":"文件已格式化完成"}}

MCP 工具的 Hooks 配置

MCP 工具可与 Hooks 无缝集成,通过特定命名规则匹配。

MCP 工具命名规则

mcp__<服务器名>__<工具名>

示例:mcp__github__search_repositoriesmcp__filesystem__read_file

匹配示例

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*", // 匹配 memory 服务器的所有工具
        "hooks": [{"type": "command", "command": "echo '内存操作日志' >> log.txt"}]
      },
      {
        "matcher": "mcp__.*__write.*", // 匹配所有服务器的写操作工具
        "hooks": [{"type": "command", "command": "./validate-write.py"}]
      }
    ]
  }
}

安全与调试

安全最佳实践

安全要点 具体操作
输入校验 严格校验 tool_input 中的文件路径、命令参数,防止路径遍历(如 ../
变量引用 Shell 命令中使用 "$VAR" 而非 $VAR,避免参数注入
权限最小化 钩子脚本仅赋予必要权限,避免使用 sudo 等高危命令
敏感文件排除 拦截对 .env.git、密钥文件的操作
命令审查 注册钩子前,手动执行命令验证逻辑,确认无恶意行为

调试技巧

问题类型 排查步骤
钩子不生效 1. 执行 /hooks 命令检查配置是否注册<br>2. 验证 JSON 语法是否正确<br>3. 检查匹配器规则是否与工具名一致(区分大小写)
命令执行失败 1. 手动运行钩子命令,确认是否可正常执行<br>2. 检查脚本是否有可执行权限(chmod +x script.sh)<br>3. 使用绝对路径调用脚本,避免环境变量问题
查看详细日志 启动 Claude Code 时添加 --debug 参数,查看钩子执行全过程

参考文档:如需查看钩子的完整功能说明,可查阅官方 钩子参考文档