Claude Code 钩子
Claude Code 钩子是用户自定义的 Shell 命令,会在 Claude Code 生命周期的特定节点自动执行。
借助钩子,你可以对 Claude Code 的行为实现精准控制,确保某些操作(如代码格式化、日志记录)必定触发,而非依赖大模型自主选择是否执行。
钩子的典型应用场景
钩子能帮你实现很多实用功能,常见场景包括:
- 消息通知:当 Claude Code 等待输入或需要权限时,自动发送桌面/邮件提醒
- 自动格式化:编辑
.ts文件后自动运行prettier,修改.go文件后执行gofmt - 操作日志:记录 Claude 执行的所有命令,用于合规审计或调试排障
- 代码规范校验:若 Claude 生成的代码不符合项目规范(如命名规则),自动给出反馈
- 文件权限管控:阻止 Claude 修改生产环境配置文件或敏感目录(如
.env、.git)
相比于通过提示词约束 Claude 的行为,钩子是应用级的硬规则,只要触发对应事件就会强制执行,稳定性和可靠性更高。
重要安全提醒
钩子运行时会直接使用当前系统环境的凭证(如环境变量、用户权限),存在一定安全风险:
- 恶意钩子代码可能泄露你的敏感数据(如 API 密钥、项目源码)
- 错误的钩子命令可能导致文件误删、系统异常
必做安全操作:
- 注册钩子前,务必逐行审查命令的逻辑和权限
- 避免在钩子中执行来源不明的脚本
- 详细安全最佳实践,可参考官方文档的 安全注意事项
钩子事件类型说明
Claude Code 内置了多个生命周期事件,你可以为不同事件绑定钩子命令。每个事件会传递不同的上下文数据,且对 Claude 行为的影响方式不同。
| 事件名称 | 触发时机 | 核心作用 |
|---|---|---|
PreToolUse |
工具调用之前 | 可拦截工具执行(如阻止修改敏感文件),并向 Claude 反馈调整建议 |
PermissionRequest |
弹出权限请求对话框时 | 自动批准或拒绝权限申请 |
PostToolUse |
工具调用完成后 | 执行后置操作(如格式化代码、记录日志) |
UserPromptSubmit |
用户提交提示词后、Claude 处理前 | 预处理用户输入(如补充上下文信息) |
Notification |
Claude 发送通知时 | 自定义通知方式(如桌面弹窗、短信提醒) |
Stop |
Claude 完成响应时 | 执行收尾工作(如清理临时文件) |
SubagentStop |
子代理任务完成时 | 处理子代理的执行结果 |
PreCompact |
即将执行上下文压缩操作时 | 自定义压缩规则 |
SessionStart |
启动新会话或恢复旧会话时 | 初始化会话环境(如加载项目配置) |
SessionEnd |
会话结束时 | 保存会话数据、清理环境 |
快速入门:实现命令日志记录
下面以 记录 Claude 执行的所有 Bash 命令 为例,带你一步步完成钩子的配置和使用。
前置准备
安装 jq 工具(用于命令行解析 JSON 数据):
- macOS:
brew install jq - Linux:
sudo apt install jq/sudo yum install jq - Windows:下载 jq 官方安装包,或通过 WSL 安装
步骤 1:打开钩子配置界面
在 Claude Code 的交互界面中,输入斜杠命令 /hooks,回车后选择要绑定的事件 —— 这里我们选 PreToolUse(工具调用前触发,适合记录命令)。
步骤 2:添加事件匹配器
匹配器的作用是 限定钩子的触发条件,只在指定工具被调用时执行钩子命令。
- 选择
+ Add new matcher… - 输入匹配关键词
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:验证钩子配置
- 再次输入
/hooks命令,可查看已配置的钩子列表 - 或直接打开配置文件
~/.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:测试钩子效果
- 在 Claude Code 中输入指令:
帮我执行 ls 命令 - 执行完成后,在终端中查看日志文件:
cat ~/.claude/bash-command-log.txt
- 若日志中出现如下内容,说明钩子配置成功:
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,粘贴以下代码:
实例
"""
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 编辑 .env、package-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 | 匹配 Edit 或 Write 工具 |
| 前缀匹配 | 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 仅在对应组件被激活并运行时生效,组件执行完成后会自动清理,不会影响全局会话。
支持的钩子事件
仅支持 PreToolUse、PostToolUse、Stop 三类事件,与全局 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 配置
支持的钩子事件
同样仅支持 PreToolUse、PostToolUse、Stop 三类事件,作用域仅限该子 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
---
注意事项:
- 组件内 Hooks 的匹配器规则与全局 Hooks 完全一致:支持精确匹配(如
"Write")、多工具匹配(如"Edit|Write")、通配匹配("*"),且区分大小写; - 组件内 Hooks 与全局 Hooks 会并行执行:若全局和组件内同时配置了针对同一事件的 Hooks,触发时两类 Hooks 会一起运行,互不冲突;
- 配置格式要求:组件内 Hooks 需写在 frontmatter(
---包裹的区域)中,遵循 YAML 语法,缩进错误会导致配置失效; - 脚本路径建议:优先使用相对路径(如
./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_repositories、mcp__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 参数,查看钩子执行全过程 |
参考文档:如需查看钩子的完整功能说明,可查阅官方 钩子参考文档。
点我分享笔记