hook-authoring
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHook Authoring — Official Best Practices
Hook 开发——官方最佳实践
What Hooks Do
钩子的作用
Hooks are deterministic shell commands (or LLM prompts) that execute at specific lifecycle points. They provide guaranteed behavior — not relying on the LLM to choose to run them.
Hooks是在特定生命周期节点执行的确定性shell命令(或LLM提示词)。它们提供可预期的行为——无需依赖LLM自主选择是否运行。
Configuration Locations
配置位置
| Location | Scope | Shareable |
|---|---|---|
| All projects | No |
| Single project | Yes (commit) |
| Single project | No (gitignored) |
| Agent/skill frontmatter | While component active | Yes |
Plugin | When plugin enabled | Yes |
| 位置 | 作用范围 | 可共享性 |
|---|---|---|
| 所有项目 | 否 |
| 单个项目 | 是(可提交到版本库) |
| 单个项目 | 否(已加入git忽略) |
| Agent/skill 前置信息 | 组件激活期间 | 是 |
插件 | 插件启用时 | 是 |
Hook Types
钩子类型
| Type | How it works | Use when |
|---|---|---|
| Runs shell command, reads stdin JSON, uses exit codes | Deterministic validation, formatting, logging |
| Single-turn LLM call, returns | Judgment-based decisions without tool access |
| Multi-turn subagent with tool access | Verification requiring file reads or commands |
| POSTs event data to URL endpoint | External service integration, audit logging |
| 类型 | 工作方式 | 适用场景 |
|---|---|---|
| 运行shell命令,读取标准输入JSON,使用退出码 | 确定性验证、格式化、日志记录 |
| 单轮LLM调用,返回 | 无需工具访问的基于判断的决策 |
| 具备工具访问权限的多轮子智能体 | 需要读取文件或执行命令的验证操作 |
| 将事件数据POST到URL端点 | 外部服务集成、审计日志 |
Hook Events
钩子事件
See quick-ref/events-reference.md for full input/output schemas.
| Event | Matcher input | Can block? | Common use |
|---|---|---|---|
| startup/resume/clear/compact | No | Re-inject context after compaction |
| (none) | Yes | Validate/transform user input |
| Tool name | Yes | Block commands, validate operations |
| Tool name | Yes | Auto-allow/deny permissions |
| Tool name | No* | Auto-format files, logging |
| Tool name | No | Error handling |
| Notification type | No | Desktop alerts |
| Agent type | No | Setup before agent runs |
| Agent type | No | Cleanup after agent |
| (none) | Yes | Verify completeness |
| Config source | Yes | Audit, block unauthorized changes |
| manual/auto | No | Save context before compaction |
| Exit reason | No | Cleanup |
*PostToolUse hooks can return to keep Claude working.
Stop{"decision": "block"}查看quick-ref/events-reference.md获取完整的输入/输出 schema。
| 事件 | 匹配器输入 | 可阻止操作? | 常见用途 |
|---|---|---|---|
| startup/resume/clear/compact | 否 | 压缩后重新注入上下文 |
| 无 | 是 | 验证/转换用户输入 |
| 工具名称 | 是 | 阻止命令、验证操作 |
| 工具名称 | 是 | 自动允许/拒绝权限 |
| 工具名称 | 否* | 自动格式化文件、日志记录 |
| 工具名称 | 否 | 错误处理 |
| 通知类型 | 否 | 桌面提醒 |
| 智能体类型 | 否 | 智能体运行前的初始化 |
| 智能体类型 | 否 | 智能体运行后的清理 |
| 无 | 是 | 验证完成状态 |
| 配置来源 | 是 | 审计、阻止未授权更改 |
| manual/auto | 否 | 压缩前保存上下文 |
| 退出原因 | 否 | 清理操作 |
*PostToolUse的钩子可返回以维持Claude的运行状态。
Stop{"decision": "block"}Configuration Format
配置格式
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write",
"timeout": 30
}
]
}
]
}
}json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write",
"timeout": 30
}
]
}
]
}
}Input/Output Protocol
输入/输出协议
Input (stdin JSON)
输入(标准输入JSON)
Every hook receives JSON on stdin with common fields + event-specific data:
json
{
"session_id": "abc123",
"cwd": "/path/to/project",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "npm test" }
}每个钩子都会通过标准输入接收包含通用字段+事件特定数据的JSON:
json
{
"session_id": "abc123",
"cwd": "/path/to/project",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "npm test" }
}Output (exit codes)
输出(退出码)
| Exit code | Effect |
|---|---|
| Allow — action proceeds. Stdout added to context (SessionStart, UserPromptSubmit) |
| Block — action cancelled. Stderr sent to Claude as feedback |
| Other | Allow — stderr logged (visible in verbose mode Ctrl+O) |
| 退出码 | 效果 |
|---|---|
| 允许——操作继续执行。标准输出内容会添加到上下文(SessionStart、UserPromptSubmit事件) |
| 阻止——操作被取消。标准错误内容会作为反馈发送给Claude |
| 其他 | 允许——标准错误内容会被记录(可在详细模式Ctrl+O中查看) |
Structured JSON output (exit 0 + JSON on stdout)
结构化JSON输出(退出码0 + 标准输出JSON)
json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rg instead of grep"
}
}PreToolUse decisions: , , .
"allow""deny""ask"json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Use rg instead of grep"
}
}PreToolUse的决策值:、、。
"allow""deny""ask"Common Patterns
常见模式
Auto-format after edits
编辑后自动格式化
json
{
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
}]
}json
{
"PostToolUse": [{
"matcher": "Edit|Write",
"hooks": [{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }]
}]
}Block protected files
阻止修改受保护文件
bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
for pattern in ".env" "package-lock.json" ".git/"; do
if [[ "$FILE" == *"$pattern"* ]]; then
echo "Blocked: matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
for pattern in ".env" "package-lock.json" ".git/"; do
if [[ "$FILE" == *"$pattern"* ]]; then
echo "Blocked: matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0Re-inject context after compaction
压缩后重新注入上下文
json
{
"SessionStart": [{
"matcher": "compact",
"hooks": [{ "type": "command", "command": "echo 'Reminder: use Bun, not npm. Run tests before commits.'" }]
}]
}json
{
"SessionStart": [{
"matcher": "compact",
"hooks": [{ "type": "command", "command": "echo 'Reminder: use Bun, not npm. Run tests before commits.'" }]
}]
}Notification on idle
闲置时发送通知
json
{
"Notification": [{
"matcher": "",
"hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude needs attention\" with title \"Claude Code\"'" }]
}]
}json
{
"Notification": [{
"matcher": "",
"hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude needs attention\" with title \"Claude Code\"'" }]
}]
}Stop Hook Infinite Loop Prevention
阻止Hook无限循环
Always check to avoid loops:
stop_hook_activebash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # Let Claude stop
fi请始终检查以避免循环:
stop_hook_activebash
INPUT=$(cat)
if [ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # 允许Claude停止
fi... your logic
... 你的业务逻辑
undefinedundefinedAnti-Patterns
反模式
| Anti-Pattern | Fix |
|---|---|
Shell profile | Wrap in |
| Stop hook without loop guard | Check |
| Using PostToolUse to undo actions | Too late — use PreToolUse to block instead |
| Relying on PermissionRequest in headless mode | Doesn't fire in |
| 反模式 | 修复方案 |
|---|---|
Shell配置文件中的 | 使用 |
| Stop Hook未添加循环防护 | 检查 |
| 使用PostToolUse撤销操作 | 为时已晚——请改用PreToolUse阻止操作 |
| 在无头模式下依赖PermissionRequest | 该模式下不会触发(-p参数)。请改用PreToolUse |
Checklist
检查清单
- Correct event chosen for the use case
- Matcher pattern tested (case-sensitive, regex)
- Script is executable ()
chmod +x - Uses for JSON parsing (or Python/Node)
jq - Exit code 2 for blocking, 0 for allowing
- Stop hooks check
stop_hook_active - Tested with sample JSON piped to stdin
- Hook script uses absolute paths or
$CLAUDE_PROJECT_DIR
- 为使用场景选择了正确的事件
- 已测试匹配器模式(区分大小写,支持正则)
- 脚本具备可执行权限()
chmod +x - 使用解析JSON(或Python/Node)
jq - 使用退出码2表示阻止,0表示允许
- Stop Hook检查了
stop_hook_active - 通过管道传入示例JSON进行了测试
- Hook脚本使用绝对路径或
$CLAUDE_PROJECT_DIR
Reference
参考资料
- All events with input/output schemas
- Hook types comparison
- 包含输入/输出schema的所有事件
- 钩子类型对比