hook-developer
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseHook Developer
Hook 开发者指南
Complete reference for developing Claude Code hooks. Use this to write hooks with correct input/output schemas.
本文是开发Claude Code Hooks的完整参考手册,可帮助你编写符合正确输入/输出schema的hooks。
When to Use
适用场景
- Creating a new hook
- Debugging hook input/output format
- Understanding what fields are available
- Setting up hook registration in settings.json
- Learning what hooks can block vs inject context
- 创建新的hook
- 调试hook的输入/输出格式
- 了解可用字段
- 在settings.json中配置hook注册
- 了解哪些hook可拦截操作,哪些可注入上下文
Quick Reference
快速参考
| Hook | Fires When | Can Block? | Primary Use |
|---|---|---|---|
| PreToolUse | Before tool executes | YES | Block/modify tool calls |
| PostToolUse | After tool completes | Partial | React to tool results |
| UserPromptSubmit | User sends prompt | YES | Validate/inject context |
| PermissionRequest | Permission dialog shows | YES | Auto-approve/deny |
| SessionStart | Session begins | NO | Load context, set env vars |
| SessionEnd | Session ends | NO | Cleanup/save state |
| Stop | Agent finishes | YES | Force continuation |
| SubagentStart | Subagent spawns | NO | Pattern coordination |
| SubagentStop | Subagent finishes | YES | Force continuation |
| PreCompact | Before compaction | NO | Save state |
| Notification | Notification sent | NO | Custom alerts |
Hook type options: (bash) or (LLM evaluation)
type: "command"type: "prompt"| Hook | 触发时机 | 是否可拦截? | 主要用途 |
|---|---|---|---|
| PreToolUse | 工具执行前 | 是 | 拦截/修改工具调用 |
| PostToolUse | 工具执行完成后 | 部分支持 | 响应工具执行结果 |
| UserPromptSubmit | 用户发送提示词时 | 是 | 验证提示词/注入上下文 |
| PermissionRequest | 权限对话框弹出时 | 是 | 自动批准/拒绝权限请求 |
| SessionStart | 会话开始时 | 否 | 加载上下文、设置环境变量 |
| SessionEnd | 会话结束时 | 否 | 清理/保存状态 |
| Stop | Agent执行完成时 | 是 | 强制Agent继续执行 |
| SubagentStart | 子Agent生成时 | 否 | 模式协调 |
| SubagentStop | 子Agent执行完成时 | 是 | 强制子Agent继续执行 |
| PreCompact | 上下文压缩前 | 否 | 保存状态 |
| Notification | 发送通知时 | 否 | 自定义告警 |
Hook类型选项: (bash命令)或 (大语言模型评估)
type: "command"type: "prompt"Hook Input/Output Schemas
Hook 输入/输出 Schema
PreToolUse
PreToolUse
Purpose: Block or modify tool execution before it happens.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "default|plan|acceptEdits|bypassPermissions",
"hook_event_name": "PreToolUse",
"tool_name": "string",
"tool_input": {
"file_path": "string",
"command": "string"
},
"tool_use_id": "string"
}Output (JSON):
json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "string",
"updatedInput": {}
},
"continue": true,
"stopReason": "string",
"systemMessage": "string",
"suppressOutput": true
}Exit code 2: Blocks tool, stderr shown to Claude.
Common matchers: , , , ,
BashEdit|WriteReadTaskmcp__.*用途: 在工具执行前拦截或修改其执行操作。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "default|plan|acceptEdits|bypassPermissions",
"hook_event_name": "PreToolUse",
"tool_name": "string",
"tool_input": {
"file_path": "string",
"command": "string"
},
"tool_use_id": "string"
}输出(JSON格式):
json
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "string",
"updatedInput": {}
},
"continue": true,
"stopReason": "string",
"systemMessage": "string",
"suppressOutput": true
}退出码2: 拦截工具执行,错误信息会展示给Claude。
常用匹配器: , , , ,
BashEdit|WriteReadTaskmcp__.*PostToolUse
PostToolUse
Purpose: React to tool execution results, provide feedback to Claude.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "PostToolUse",
"tool_name": "string",
"tool_input": {},
"tool_response": {
"filePath": "string",
"success": true,
"output": "string",
"exitCode": 0
},
"tool_use_id": "string"
}CRITICAL: The response field is , NOT .
tool_responsetool_resultOutput (JSON):
json
{
"decision": "block",
"reason": "string",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "string"
},
"continue": true,
"stopReason": "string",
"suppressOutput": true
}Blocking: with prompts Claude to address the issue.
"decision": "block""reason"Common matchers: ,
Edit|WriteBash用途: 响应工具执行结果,向Claude提供反馈。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "PostToolUse",
"tool_name": "string",
"tool_input": {},
"tool_response": {
"filePath": "string",
"success": true,
"output": "string",
"exitCode": 0
},
"tool_use_id": "string"
}重要提示: 响应字段是,而非。
tool_responsetool_result输出(JSON格式):
json
{
"decision": "block",
"reason": "string",
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "string"
},
"continue": true,
"stopReason": "string",
"suppressOutput": true
}拦截方式: 设置并搭配,会提示Claude处理相关问题。
"decision": "block""reason"常用匹配器: ,
Edit|WriteBashUserPromptSubmit
UserPromptSubmit
Purpose: Validate user prompts, inject context before Claude processes.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "UserPromptSubmit",
"prompt": "string"
}Output (Plain text):
Any stdout text is added to context for Claude.Output (JSON):
json
{
"decision": "block",
"reason": "string",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "string"
}
}Blocking: erases prompt, shows to user only (not Claude).
"decision": "block""reason"Exit code 2: Blocks prompt, shows stderr to user only.
用途: 验证用户提示词,在Claude处理前注入上下文。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "UserPromptSubmit",
"prompt": "string"
}输出(纯文本格式):
标准输出的任何文本都会被添加到Claude的上下文中。输出(JSON格式):
json
{
"decision": "block",
"reason": "string",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "string"
}
}拦截方式: 设置会清除提示词,仅向用户展示(不会展示给Claude)。
"decision": "block""reason"退出码2: 拦截提示词,仅向用户展示错误信息。
PermissionRequest
PermissionRequest
Purpose: Automate permission dialog decisions.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "PermissionRequest",
"tool_name": "string",
"tool_input": {}
}Output:
json
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow|deny",
"updatedInput": {},
"message": "string",
"interrupt": false
}
}
}用途: 自动化处理权限对话框的决策。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "PermissionRequest",
"tool_name": "string",
"tool_input": {}
}输出:
json
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow|deny",
"updatedInput": {},
"message": "string",
"interrupt": false
}
}
}SessionStart
SessionStart
Purpose: Initialize session, load context, set environment variables.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "SessionStart",
"source": "startup|resume|clear|compact"
}Environment variable: - write to persist env vars.
CLAUDE_ENV_FILEexport VAR=valueOutput (Plain text or JSON):
json
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "string"
},
"suppressOutput": true
}Plain text stdout is added as context.
用途: 初始化会话、加载上下文、设置环境变量。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "SessionStart",
"source": "startup|resume|clear|compact"
}环境变量: - 写入以持久化环境变量。
CLAUDE_ENV_FILEexport VAR=value输出(纯文本或JSON格式):
json
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "string"
},
"suppressOutput": true
}标准输出的纯文本会被添加为上下文。
SessionEnd
SessionEnd
Purpose: Cleanup, save state, log session.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "SessionEnd",
"reason": "clear|logout|prompt_input_exit|other"
}Output: Cannot affect session (already ending). Use for cleanup only.
用途: 清理资源、保存状态、记录会话。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "SessionEnd",
"reason": "clear|logout|prompt_input_exit|other"
}输出: 无法影响会话(会话已进入结束流程),仅可用于清理操作。
Stop
Stop
Purpose: Control when Claude stops, force continuation.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "Stop",
"stop_hook_active": false
}CRITICAL: Check to prevent infinite loops!
stop_hook_active: trueOutput:
json
{
"decision": "block",
"reason": "string"
}Blocking: forces Claude to continue with as prompt.
"decision": "block""reason"用途: 控制Claude的停止时机,强制其继续执行。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "Stop",
"stop_hook_active": false
}重要提示: 需检查以避免无限循环!
stop_hook_active: true输出:
json
{
"decision": "block",
"reason": "string"
}拦截方式: 设置会强制Claude继续执行,会作为提示词。
"decision": "block""reason"SubagentStart
SubagentStart
Purpose: Run when a subagent (Task tool) is spawned.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "SubagentStart",
"agent_id": "string"
}Output: Context injection only (cannot block).
用途: 当子Agent(Task工具)生成时触发。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "SubagentStart",
"agent_id": "string"
}输出: 仅可注入上下文(无法拦截)。
SubagentStop
SubagentStop
Purpose: Control when subagents (Task tool) stop.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "SubagentStop",
"stop_hook_active": false
}Output: Same as Stop.
用途: 控制子Agent(Task工具)的停止时机。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "SubagentStop",
"stop_hook_active": false
}输出: 与Stop hook的输出格式一致。
PreCompact
PreCompact
Purpose: Save state before context compaction.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "PreCompact",
"trigger": "manual|auto",
"custom_instructions": "string"
}Matchers: ,
manualautoOutput:
json
{
"continue": true,
"systemMessage": "string"
}用途: 在上下文压缩前保存状态。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "PreCompact",
"trigger": "manual|auto",
"custom_instructions": "string"
}匹配器: ,
manualauto输出:
json
{
"continue": true,
"systemMessage": "string"
}Notification
Notification
Purpose: Custom notification handling.
Input:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "Notification",
"message": "string",
"notification_type": "permission_prompt|idle_prompt|auth_success|elicitation_dialog"
}Matchers: , , , ,
permission_promptidle_promptauth_successelicitation_dialog*Output:
json
{
"continue": true,
"suppressOutput": true,
"systemMessage": "string"
}用途: 自定义通知处理逻辑。
输入:
json
{
"session_id": "string",
"transcript_path": "string",
"cwd": "string",
"permission_mode": "string",
"hook_event_name": "Notification",
"message": "string",
"notification_type": "permission_prompt|idle_prompt|auth_success|elicitation_dialog"
}匹配器: , , , ,
permission_promptidle_promptauth_successelicitation_dialog*输出:
json
{
"continue": true,
"suppressOutput": true,
"systemMessage": "string"
}Registration in settings.json
在settings.json中注册Hook
Standard Structure
标准结构
json
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
"timeout": 60
}
]
}
]
}
}json
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
"timeout": 60
}
]
}
]
}
}Matcher Patterns
匹配器模式
| Pattern | Matches |
|---|---|
| Exactly Bash tool |
| Edit OR Write |
| Regex: Read* |
| MCP write tools |
| All tools |
Case-sensitive: ≠
Bashbash| 模式 | 匹配对象 |
|---|---|
| 精确匹配Bash工具 |
| 匹配Edit或Write工具 |
| 正则匹配:以Read开头的工具 |
| 匹配MCP写入类工具 |
| 匹配所有工具 |
大小写敏感: ≠
BashbashEvents Requiring Matchers
需要匹配器的事件
- PreToolUse - YES (required)
- PostToolUse - YES (required)
- PermissionRequest - YES (required)
- Notification - YES (optional)
- SessionStart - YES ()
startup|resume|clear|compact - PreCompact - YES ()
manual|auto
- PreToolUse - 是(必填)
- PostToolUse - 是(必填)
- PermissionRequest - 是(必填)
- Notification - 是(可选)
- SessionStart - 是(需匹配)
startup|resume|clear|compact - PreCompact - 是(需匹配)
manual|auto
Events Without Matchers
无需匹配器的事件
json
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [{ "type": "command", "command": "/path/to/hook.sh" }]
}
]
}
}json
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [{ "type": "command", "command": "/path/to/hook.sh" }]
}
]
}
}Hook Types
Hook类型
Command Hooks (type: "command")
命令型Hook(type: "command")
Default type. Executes bash commands or scripts.
json
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
"timeout": 60
}默认类型,可执行bash命令或脚本。
json
{
"type": "command",
"command": "$CLAUDE_PROJECT_DIR/.claude/hooks/my-hook.sh",
"timeout": 60
}Prompt-Based Hooks (type: "prompt")
提示词型Hook(type: "prompt")
Uses LLM (Haiku) for context-aware decisions. Best for Stop/SubagentStop.
json
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop. Context: $ARGUMENTS. Check if all tasks are complete.",
"timeout": 30
}Response schema:
json
{
"decision": "approve" | "block",
"reason": "Explanation",
"continue": false,
"stopReason": "Message to user",
"systemMessage": "Warning"
}使用大语言模型(Haiku)进行上下文感知决策,最适合用于Stop/SubagentStop场景。
json
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop. Context: $ARGUMENTS. Check if all tasks are complete.",
"timeout": 30
}响应schema:
json
{
"decision": "approve" | "block",
"reason": "Explanation",
"continue": false,
"stopReason": "Message to user",
"systemMessage": "Warning"
}MCP Tool Naming
MCP工具命名规则
MCP tools use pattern :
mcp__<server>__<tool>| Pattern | Matches |
|---|---|
| All memory server tools |
| All MCP write tools |
| All GitHub tools |
MCP工具采用命名模式:
mcp__<server>__<tool>| 模式 | 匹配对象 |
|---|---|
| 所有内存服务器工具 |
| 所有MCP写入类工具 |
| 所有GitHub工具 |
Environment Variables
环境变量
Available to All Hooks
所有Hook均可使用
| Variable | Description |
|---|---|
| Absolute path to project root |
| "true" if remote/web, empty if local CLI |
| 变量名 | 描述 |
|---|---|
| 项目根目录的绝对路径 |
| 远程/网页版为"true",本地CLI版为空 |
SessionStart Only
仅SessionStart可用
| Variable | Description |
|---|---|
| Path to write |
| 变量名 | 描述 |
|---|---|
| 写入 |
Plugin Hooks Only
仅插件Hook可用
| Variable | Description |
|---|---|
| Absolute path to plugin directory |
| 变量名 | 描述 |
|---|---|
| 插件目录的绝对路径 |
Exit Codes
退出码
| Exit Code | Behavior | stdout | stderr |
|---|---|---|---|
| 0 | Success | JSON processed | Ignored |
| 2 | Blocking error | IGNORED | Error message |
| Other | Non-blocking error | Ignored | Verbose mode |
| 退出码 | 行为 | 标准输出 | 标准错误 |
|---|---|---|---|
| 0 | 执行成功 | 处理JSON格式输出 | 忽略 |
| 2 | 拦截型错误 | 忽略 | 展示错误信息 |
| 其他 | 非拦截型错误 | 忽略 | 仅在详细模式下展示 |
Exit Code 2 by Hook
各Hook使用退出码2的效果
| Hook | Effect |
|---|---|
| PreToolUse | Blocks tool, stderr to Claude |
| PostToolUse | stderr to Claude (tool already ran) |
| UserPromptSubmit | Blocks prompt, stderr to user only |
| Stop | Blocks stop, stderr to Claude |
| Hook | 效果 |
|---|---|
| PreToolUse | 拦截工具执行,错误信息展示给Claude |
| PostToolUse | 错误信息展示给Claude(工具已执行完成) |
| UserPromptSubmit | 拦截提示词,错误信息仅展示给用户 |
| Stop | 拦截停止操作,错误信息展示给Claude |
Shell Wrapper Pattern
Shell包装器模式
bash
#!/bin/bash
set -e
cd "$CLAUDE_PROJECT_DIR/.claude/hooks"
cat | npx tsx src/my-hook.tsOr for bundled:
bash
#!/bin/bash
set -e
cd "$HOME/.claude/hooks"
cat | node dist/my-hook.mjsbash
#!/bin/bash
set -e
cd "$CLAUDE_PROJECT_DIR/.claude/hooks"
cat | npx tsx src/my-hook.ts或者针对打包后的文件:
bash
#!/bin/bash
set -e
cd "$HOME/.claude/hooks"
cat | node dist/my-hook.mjsTypeScript Handler Pattern
TypeScript处理器模式
typescript
import { readFileSync } from 'fs';
interface HookInput {
session_id: string;
hook_event_name: string;
tool_name?: string;
tool_input?: Record<string, unknown>;
tool_response?: Record<string, unknown>;
// ... other fields per hook type
}
function readStdin(): string {
return readFileSync(0, 'utf-8');
}
async function main() {
const input: HookInput = JSON.parse(readStdin());
// Process input
const output = {
decision: 'block', // or undefined to allow
reason: 'Why blocking'
};
console.log(JSON.stringify(output));
}
main().catch(console.error);typescript
import { readFileSync } from 'fs';
interface HookInput {
session_id: string;
hook_event_name: string;
tool_name?: string;
tool_input?: Record<string, unknown>;
tool_response?: Record<string, unknown>;
// ... 其他hook类型的字段
}
function readStdin(): string {
return readFileSync(0, 'utf-8');
}
async function main() {
const input: HookInput = JSON.parse(readStdin());
// 处理输入
const output = {
decision: 'block', // 或留空以允许执行
reason: '拦截原因'
};
console.log(JSON.stringify(output));
}
main().catch(console.error);Testing Hooks
测试Hook
Manual Test Commands
手动测试命令
bash
undefinedbash
undefinedPostToolUse (Write)
PostToolUse(Write工具)
echo '{"tool_name":"Write","tool_input":{"file_path":"test.md"},"tool_response":{"success":true},"session_id":"test"}' |
.claude/hooks/my-hook.sh
.claude/hooks/my-hook.sh
echo '{"tool_name":"Write","tool_input":{"file_path":"test.md"},"tool_response":{"success":true},"session_id":"test"}' |
.claude/hooks/my-hook.sh
.claude/hooks/my-hook.sh
PreToolUse (Bash)
PreToolUse(Bash工具)
echo '{"tool_name":"Bash","tool_input":{"command":"ls"},"session_id":"test"}' |
.claude/hooks/my-hook.sh
.claude/hooks/my-hook.sh
echo '{"tool_name":"Bash","tool_input":{"command":"ls"},"session_id":"test"}' |
.claude/hooks/my-hook.sh
.claude/hooks/my-hook.sh
SessionStart
SessionStart
echo '{"hook_event_name":"SessionStart","source":"startup","session_id":"test"}' |
.claude/hooks/session-start.sh
.claude/hooks/session-start.sh
echo '{"hook_event_name":"SessionStart","source":"startup","session_id":"test"}' |
.claude/hooks/session-start.sh
.claude/hooks/session-start.sh
SessionEnd
SessionEnd
echo '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"test"}' |
.claude/hooks/session-end.sh
.claude/hooks/session-end.sh
echo '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"test"}' |
.claude/hooks/session-end.sh
.claude/hooks/session-end.sh
UserPromptSubmit
UserPromptSubmit
echo '{"prompt":"test prompt","session_id":"test"}' |
.claude/hooks/prompt-submit.sh
.claude/hooks/prompt-submit.sh
undefinedecho '{"prompt":"test prompt","session_id":"test"}' |
.claude/hooks/prompt-submit.sh
.claude/hooks/prompt-submit.sh
undefinedRebuild After TypeScript Edits
TypeScript修改后重新打包
bash
cd .claude/hooks
npx esbuild src/my-hook.ts \
--bundle --platform=node --format=esm \
--outfile=dist/my-hook.mjsbash
cd .claude/hooks
npx esbuild src/my-hook.ts \
--bundle --platform=node --format=esm \
--outfile=dist/my-hook.mjsCommon Patterns
常见模式
Block Dangerous Files (PreToolUse)
拦截危险文件操作(PreToolUse)
python
#!/usr/bin/env python3
import json, sys
data = json.load(sys.stdin)
path = data.get('tool_input', {}).get('file_path', '')
BLOCKED = ['.env', 'secrets.json', '.git/']
if any(b in path for b in BLOCKED):
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"Blocked: {path} is protected"
}
}))
else:
print('{}')python
#!/usr/bin/env python3
import json, sys
data = json.load(sys.stdin)
path = data.get('tool_input', {}).get('file_path', '')
BLOCKED = ['.env', 'secrets.json', '.git/']
if any(b in path for b in BLOCKED):
print(json.dumps({
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": f"已拦截:{path}为受保护文件"
}
}))
else:
print('{}')Auto-Format Files (PostToolUse)
自动格式化文件(PostToolUse)
bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
npx prettier --write "$FILE" 2>/dev/null
fi
echo '{}'bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
npx prettier --write "$FILE" 2>/dev/null
fi
echo '{}'Inject Git Context (UserPromptSubmit)
注入Git上下文(UserPromptSubmit)
bash
#!/bin/bash
echo "Git status:"
git status --short 2>/dev/null || echo "(not a git repo)"
echo ""
echo "Recent commits:"
git log --oneline -5 2>/dev/null || echo "(no commits)"bash
#!/bin/bash
echo "Git状态:"
git status --short 2>/dev/null || echo "(当前不是Git仓库)"
echo ""
echo "最近提交:"
git log --oneline -5 2>/dev/null || echo "(无提交记录)"Force Test Verification (Stop)
强制测试验证(Stop)
python
#!/usr/bin/env python3
import json, sys, subprocess
data = json.load(sys.stdin)python
#!/usr/bin/env python3
import json, sys, subprocess
data = json.load(sys.stdin)Prevent infinite loops
防止无限循环
if data.get('stop_hook_active'):
print('{}')
sys.exit(0)
if data.get('stop_hook_active'):
print('{}')
sys.exit(0)
Check if tests pass
检查测试是否通过
result = subprocess.run(['npm', 'test'], capture_output=True)
if result.returncode != 0:
print(json.dumps({
"decision": "block",
"reason": "Tests are failing. Please fix before stopping."
}))
else:
print('{}')
---result = subprocess.run(['npm', 'test'], capture_output=True)
if result.returncode != 0:
print(json.dumps({
"decision": "block",
"reason": "测试未通过,请修复后再停止。"
}))
else:
print('{}')
---Debugging Checklist
调试检查清单
- Hook registered in settings.json?
- Shell script has permission?
+x - Bundle rebuilt after TS changes?
- Using not
tool_response?tool_result - Output is valid JSON (or plain text)?
- Checking in Stop hooks?
stop_hook_active - Using for paths?
$CLAUDE_PROJECT_DIR
- 是否已在settings.json中注册Hook?
- Shell脚本是否有执行权限?
+x - TypeScript修改后是否重新打包?
- 是否使用而非
tool_response?tool_result - 输出是否为合法JSON(或纯文本)?
- Stop hook中是否检查?
stop_hook_active - 是否使用来构建路径?
$CLAUDE_PROJECT_DIR
Key Learnings from Past Sessions
过往会话总结的关键经验
- Field names matter - not
tool_responsetool_result - Output format - +
decision: "block"for blockingreason - Exit code 2 - stderr goes to Claude/user, stdout IGNORED
- Rebuild bundles - TypeScript source edits don't auto-apply
- Test manually - before relying on it
echo '{}' | ./hook.sh - Check outputs first - before editing code
ls .claude/cache/ - Detached spawn hides errors - add logging to debug
- 字段名至关重要 - 使用而非
tool_responsetool_result - 输出格式 - 拦截操作需设置并搭配
decision: "block"reason - 退出码2 - 标准错误会展示给Claude/用户,标准输出会被忽略
- 重新打包 - TypeScript源码修改后不会自动生效,需重新打包
- 手动测试 - 在依赖Hook前先执行测试
echo '{}' | ./hook.sh - 先检查输出 - 修改代码前先查看中的输出
.claude/cache/ - 后台进程会隐藏错误 - 添加日志便于调试
See Also
相关链接
- - Systematic debugging workflow
/debug-hooks - - Hook development rules
.claude/rules/hooks.md
- - 系统化调试流程
/debug-hooks - - Hook开发规则
.claude/rules/hooks.md