hooks-configuration
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseClaude Code Hooks Configuration
Claude Code 钩子配置
Expert knowledge for configuring and developing Claude Code hooks to automate workflows and enforce best practices.
用于配置和开发 Claude Code 钩子以实现工作流自动化、强制落地最佳实践的专业知识。
Core Concepts
核心概念
What Are Hooks?
Hooks are user-defined shell commands that execute at specific points in Claude Code's lifecycle. Unlike relying on Claude to "decide" to run something, hooks provide deterministic, guaranteed execution.
Why Use Hooks?
- Enforce code formatting automatically
- Block dangerous commands before execution
- Inject context at session start
- Log commands for audit trails
- Send notifications when tasks complete
什么是钩子?
钩子是用户自定义的 shell 命令,会在 Claude Code 生命周期的特定节点执行。和依赖 Claude「主动决定」运行某些内容不同,钩子提供确定性的、有保障的执行能力。
为什么使用钩子?
- 自动强制代码格式化规范
- 执行前拦截危险命令
- 会话启动时自动注入上下文
- 记录命令操作用于审计追踪
- 任务完成时自动发送通知
Hook Lifecycle Events
钩子生命周期事件
| Event | When It Fires | Key Use Cases |
|---|---|---|
| SessionStart | Session begins/resumes | Environment setup, context loading |
| SessionEnd | Session terminates | Cleanup, state persistence |
| UserPromptSubmit | User submits prompt | Input validation, context injection |
| PreToolUse | Before tool execution | Permission control, blocking dangerous ops |
| PostToolUse | After tool completes | Auto-formatting, logging, validation |
| PermissionRequest | Claude requests permission for a tool | Auto approve/deny without user prompt |
| Stop | Main agent finishes responding | Notifications, git reminders |
| SubagentStart | Subagent (Task tool) is about to start | Input modification, context injection |
| SubagentStop | Subagent finishes | Per-task completion evaluation |
| WorktreeCreate | New git worktree created via EnterWorktree | Worktree setup, dependency install |
| WorktreeRemove | Worktree removed after session exits | Cleanup, uncommitted changes alert |
| TeammateIdle | Teammate in agent team goes idle | Assign additional tasks to teammate |
| TaskCompleted | Task in shared task list marked complete | Validation gates before task acceptance |
| PreCompact | Before context compaction | Transcript backup |
| Notification | Claude sends notification | Custom alerts |
| ConfigChange | Claude Code settings change at runtime | Audit config changes, validation |
Stop vs SubagentStop:fires at the session level when the main agent finishes a response turn.Stopfires when an individual subagent (spawned via the Task tool) completes. UseSubagentStopfor session-level notifications; useStopfor per-task quality gates.SubagentStop
For full schemas, examples, and timeout recommendations for each event, see .claude/rules/hooks-reference.md.
| 事件名称 | 触发时机 | 核心使用场景 |
|---|---|---|
| SessionStart | 会话启动/恢复时 | 环境初始化、上下文加载 |
| SessionEnd | 会话终止时 | 资源清理、状态持久化 |
| UserPromptSubmit | 用户提交提示词时 | 输入校验、上下文注入 |
| PreToolUse | 工具执行前 | 权限控制、拦截危险操作 |
| PostToolUse | 工具执行完成后 | 自动格式化、日志记录、结果校验 |
| PermissionRequest | Claude 申请工具使用权限时 | 无需用户干预自动同意/拒绝权限申请 |
| Stop | 主 Agent 完成响应时 | 消息通知、Git 操作提醒 |
| SubagentStart | 子Agent(Task 工具)即将启动时 | 输入修改、上下文注入 |
| SubagentStop | 子Agent 执行完成时 | 单任务完成质量校验 |
| WorktreeCreate | 通过 EnterWorktree 创建新 Git 工作树时 | 工作树初始化、依赖安装 |
| WorktreeRemove | 会话退出后工作树被删除时 | 资源清理、未提交变更提醒 |
| TeammateIdle | Agent 团队中的协作节点进入空闲状态时 | 为协作节点分配额外任务 |
| TaskCompleted | 共享任务列表中的任务被标记为完成时 | 任务验收前的校验关卡 |
| PreCompact | 上下文压缩前 | 对话记录备份 |
| Notification | Claude 发送通知时 | 自定义告警 |
| ConfigChange | Claude Code 配置在运行时变更时 | 配置变更审计、合法性校验 |
Stop 与 SubagentStop 的区别:是会话级别事件,在主 Agent 完成一次响应交互时触发;Stop是子Agent级别事件,在单个通过 Task 工具启动的子Agent执行完成时触发。会话级别的通知使用SubagentStop,单任务质量校验关卡使用Stop。SubagentStop
如需查看每个事件的完整 schema、示例和超时时间建议,请参考 .claude/rules/hooks-reference.md。
Configuration
配置说明
File Locations
文件位置
Hooks are configured in settings files:
- - User-level (applies everywhere)
~/.claude/settings.json - - Project-level (committed to repo)
.claude/settings.json - - Local project (not committed)
.claude/settings.local.json
Claude Code merges all matching hooks from all files.
钩子在配置文件中进行定义:
- - 用户级别配置(全局生效)
~/.claude/settings.json - - 项目级别配置(可提交到代码仓库)
.claude/settings.json - - 本地项目配置(不提交到代码仓库)
.claude/settings.local.json
Claude Code 会合并所有配置文件中定义的钩子规则。
Frontmatter Hooks (Skills and Commands)
Frontmatter 钩子(Skill 和命令场景)
Hooks can also be defined directly in skill and command frontmatter using the field:
hooksyaml
---
name: my-skill
description: A skill with hooks
allowed-tools: Bash, Read
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "echo 'Pre-tool hook from skill'"
timeout: 10
---This allows skills and commands to define their own hooks that are active only when that skill/command is in use.
你也可以直接在 Skill 和命令的 frontmatter 中通过 字段定义钩子:
hooksyaml
---
name: my-skill
description: A skill with hooks
allowed-tools: Bash, Read
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "echo 'Pre-tool hook from skill'"
timeout: 10
---这种方式可以让 Skill 和命令定义仅在自身启用时才生效的专属钩子。
Basic Structure
基础结构
json
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here",
"timeout": 30
}
]
}
]
}
}json
{
"hooks": {
"EventName": [
{
"matcher": "ToolPattern",
"hooks": [
{
"type": "command",
"command": "your-command-here",
"timeout": 30
}
]
}
]
}
}Matcher Patterns
匹配器规则
- Exact match: - matches exactly "Bash" tool
"Bash" - Regex patterns: - matches either tool
"Edit|Write" - Wildcards: - matches tools starting with "Notebook"
"Notebook.*" - All tools: - matches everything
"*" - MCP tools: - targets MCP server tools
"mcp__server__tool"
- 精确匹配:- 仅匹配「Bash」工具
"Bash" - 正则匹配:- 匹配任意一个指定工具
"Edit|Write" - 通配符匹配:- 匹配所有以「Notebook」开头的工具
"Notebook.*" - 全匹配:- 匹配所有工具
"*" - MCP 工具匹配:- 匹配指定的 MCP 服务工具
"mcp__server__tool"
Input Schema
输入 Schema
Hooks receive JSON via stdin with these common fields:
json
{
"session_id": "unique-session-id",
"transcript_path": "/path/to/conversation.json",
"cwd": "/current/working/directory",
"permission_mode": "mode",
"hook_event_name": "PreToolUse"
}PreToolUse additional fields:
json
{
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}PostToolUse additional fields:
json
{
"tool_name": "Bash",
"tool_input": { ... },
"tool_response": { ... }
}SubagentStart additional fields:
json
{
"subagent_type": "Explore",
"subagent_prompt": "original prompt text",
"subagent_model": "claude-opus"
}钩子通过标准输入接收 JSON 格式数据,通用字段如下:
json
{
"session_id": "unique-session-id",
"transcript_path": "/path/to/conversation.json",
"cwd": "/current/working/directory",
"permission_mode": "mode",
"hook_event_name": "PreToolUse"
}PreToolUse 额外字段:
json
{
"tool_name": "Bash",
"tool_input": {
"command": "npm test"
}
}PostToolUse 额外字段:
json
{
"tool_name": "Bash",
"tool_input": { ... },
"tool_response": { ... }
}SubagentStart 额外字段:
json
{
"subagent_type": "Explore",
"subagent_prompt": "original prompt text",
"subagent_model": "claude-opus"
}Output Schema
输出 Schema
Exit Codes
退出码定义
- 0: Success (command allowed)
- 2: Blocking error (stderr shown to Claude, operation blocked)
- Other: Non-blocking error (logged in verbose mode)
- 0:执行成功(允许操作继续)
- 2:拦截类错误(标准错误输出会展示给 Claude,操作被阻断)
- 其他值:非阻断类错误(仅在 verbose 模式下记录日志)
JSON Response (optional)
JSON 响应(可选)
PreToolUse:
json
{
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "explanation",
"updatedInput": { "modified": "input" }
}Stop/SubagentStop:
json
{
"decision": "block",
"reason": "required explanation for continuing"
}SubagentStart (input modification):
json
{
"updatedPrompt": "modified prompt text to inject context or modify behavior"
}SessionStart:
json
{
"additionalContext": "Information to inject into session"
}PreToolUse 场景:
json
{
"permissionDecision": "allow|deny|ask",
"permissionDecisionReason": "explanation",
"updatedInput": { "modified": "input" }
}Stop/SubagentStop 场景:
json
{
"decision": "block",
"reason": "required explanation for continuing"
}SubagentStart(输入修改场景):
json
{
"updatedPrompt": "modified prompt text to inject context or modify behavior"
}SessionStart 场景:
json
{
"additionalContext": "Information to inject into session"
}Common Hook Patterns
常用钩子实现模式
Block Dangerous Commands (PreToolUse)
拦截危险命令(PreToolUse)
bash
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')bash
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')Block rm -rf /
Block rm -rf /
if echo "$COMMAND" | grep -Eq 'rm\s+(-rf|-fr)\s+/'; then
echo "BLOCKED: Refusing to run destructive command on root" >&2
exit 2
fi
exit 0
undefinedif echo "$COMMAND" | grep -Eq 'rm\s+(-rf|-fr)\s+/'; then
echo "BLOCKED: Refusing to run destructive command on root" >&2
exit 2
fi
exit 0
undefinedAuto-Format After Edits (PostToolUse)
编辑完成后自动格式化(PostToolUse)
bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.py ]]; then
ruff format "$FILE" 2>/dev/null
ruff check --fix "$FILE" 2>/dev/null
elif [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
prettier --write "$FILE" 2>/dev/null
fi
exit 0bash
#!/bin/bash
INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.py ]]; then
ruff format "$FILE" 2>/dev/null
ruff check --fix "$FILE" 2>/dev/null
elif [[ "$FILE" == *.ts ]] || [[ "$FILE" == *.tsx ]]; then
prettier --write "$FILE" 2>/dev/null
fi
exit 0Remind About Built-in Tools (PreToolUse)
提示使用内置工具(PreToolUse)
bash
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -Eq '^\s*cat\s+[^|><]'; then
echo "REMINDER: Use the Read tool instead of 'cat'" >&2
exit 2
fi
exit 0bash
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
if echo "$COMMAND" | grep -Eq '^\s*cat\s+[^|><]'; then
echo "REMINDER: Use the Read tool instead of 'cat'" >&2
exit 2
fi
exit 0Load Context at Session Start (SessionStart)
会话启动时加载上下文(SessionStart)
bash
#!/bin/bash
GIT_STATUS=$(git status --short 2>/dev/null | head -5)
BRANCH=$(git branch --show-current 2>/dev/null)
cat << EOF
{
"additionalContext": "Current branch: $BRANCH\nPending changes:\n$GIT_STATUS"
}
EOFbash
#!/bin/bash
GIT_STATUS=$(git status --short 2>/dev/null | head -5)
BRANCH=$(git branch --show-current 2>/dev/null)
cat << EOF
{
"additionalContext": "Current branch: $BRANCH\nPending changes:\n$GIT_STATUS"
}
EOFInject Context for Subagents (SubagentStart)
为子Agent注入上下文(SubagentStart)
bash
#!/bin/bash
INPUT=$(cat)
SUBAGENT_TYPE=$(echo "$INPUT" | jq -r '.subagent_type // empty')
ORIGINAL_PROMPT=$(echo "$INPUT" | jq -r '.subagent_prompt // empty')bash
#!/bin/bash
INPUT=$(cat)
SUBAGENT_TYPE=$(echo "$INPUT" | jq -r '.subagent_type // empty')
ORIGINAL_PROMPT=$(echo "$INPUT" | jq -r '.subagent_prompt // empty')Add project context to Explore agents
Add project context to Explore agents
if [ "$SUBAGENT_TYPE" = "Explore" ]; then
PROJECT_INFO="Project uses TypeScript with Bun. Main source in src/."
cat << EOF
{
"updatedPrompt": "$PROJECT_INFO\n\n$ORIGINAL_PROMPT"
}
EOF
fi
exit 0
undefinedif [ "$SUBAGENT_TYPE" = "Explore" ]; then
PROJECT_INFO="Project uses TypeScript with Bun. Main source in src/."
cat << EOF
{
"updatedPrompt": "$PROJECT_INFO\n\n$ORIGINAL_PROMPT"
}
EOF
fi
exit 0
undefinedDesktop Notification on Stop (Stop)
任务完成时发送桌面通知(Stop)
bash
#!/bin/bashbash
#!/bin/bashLinux
Linux
notify-send "Claude Code" "Task completed" 2>/dev/null
notify-send "Claude Code" "Task completed" 2>/dev/null
macOS
macOS
osascript -e 'display notification "Task completed" with title "Claude Code"' 2>/dev/null
exit 0
undefinedosascript -e 'display notification "Task completed" with title "Claude Code"' 2>/dev/null
exit 0
undefinedAudit Logging (PostToolUse)
审计日志记录(PostToolUse)
bash
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // "N/A"')
echo "$(date -Iseconds) | $TOOL | $COMMAND" >> ~/.claude/audit.log
exit 0bash
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // "N/A"')
echo "$(date -Iseconds) | $TOOL | $COMMAND" >> ~/.claude/audit.log
exit 0Auto-Approve Safe Operations (PermissionRequest)
自动批准安全操作(PermissionRequest)
bash
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')bash
#!/bin/bash
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool_name')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')Auto-approve read-only git operations
Auto-approve read-only git operations
if [ "$TOOL" = "Bash" ] && echo "$COMMAND" | grep -Eq '^git (status|log|diff|branch|remote)'; then
echo '{"decision": "approve", "reason": "Read-only git operation"}'
exit 0
fi
if [ "$TOOL" = "Bash" ] && echo "$COMMAND" | grep -Eq '^git (status|log|diff|branch|remote)'; then
echo '{"decision": "approve", "reason": "Read-only git operation"}'
exit 0
fi
Auto-deny destructive operations on root
Auto-deny destructive operations on root
if [ "$TOOL" = "Bash" ] && echo "$COMMAND" | grep -Eq 'rm\s+(-rf|-fr)\s+/'; then
echo '{"decision": "deny", "reason": "Destructive root operation blocked"}'
exit 0
fi
exit 0
undefinedif [ "$TOOL" = "Bash" ] && echo "$COMMAND" | grep -Eq 'rm\s+(-rf|-fr)\s+/'; then
echo '{"decision": "deny", "reason": "Destructive root operation blocked"}'
exit 0
fi
exit 0
undefinedSet Up Worktree Environment (WorktreeCreate)
工作树环境初始化(WorktreeCreate)
bash
#!/bin/bash
INPUT=$(cat)
WORKTREE_PATH=$(echo "$INPUT" | jq -r '.worktree_path')
if [ -f "$WORKTREE_PATH/package.json" ]; then
(cd "$WORKTREE_PATH" && bun install --frozen-lockfile) 2>/dev/null
fi
exit 0bash
#!/bin/bash
INPUT=$(cat)
WORKTREE_PATH=$(echo "$INPUT" | jq -r '.worktree_path')
if [ -f "$WORKTREE_PATH/package.json" ]; then
(cd "$WORKTREE_PATH" && bun install --frozen-lockfile) 2>/dev/null
fi
exit 0Gate Task Completion (TaskCompleted)
任务完成验收校验(TaskCompleted)
bash
#!/bin/bash
INPUT=$(cat)
TASK_TITLE=$(echo "$INPUT" | jq -r '.task_title')
if echo "$TASK_TITLE" | grep -qi 'implement\|add\|fix\|refactor'; then
if ! npm test --bail 2>/dev/null; then
echo '{"decision": "block", "reason": "Tests must pass before task is accepted."}'
exit 0
fi
fi
exit 0bash
#!/bin/bash
INPUT=$(cat)
TASK_TITLE=$(echo "$INPUT" | jq -r '.task_title')
if echo "$TASK_TITLE" | grep -qi 'implement\|add\|fix\|refactor'; then
if ! npm test --bail 2>/dev/null; then
echo '{"decision": "block", "reason": "Tests must pass before task is accepted."}'
exit 0
fi
fi
exit 0Configuration Examples
配置示例
Anti-Pattern Detection
反模式检测
json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash $CLAUDE_PROJECT_DIR/hooks-plugin/hooks/bash-antipatterns.sh",
"timeout": 5
}
]
}
]
}
}json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash $CLAUDE_PROJECT_DIR/hooks-plugin/hooks/bash-antipatterns.sh",
"timeout": 5
}
]
}
]
}
}Auto-Format Python Files
Python 文件自动格式化
json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash -c 'FILE=$(cat | jq -r \".tool_input.file_path\"); [[ \"$FILE\" == *.py ]] && ruff format \"$FILE\"'"
}
]
}
]
}
}json
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "bash -c 'FILE=$(cat | jq -r \".tool_input.file_path\"); [[ \"$FILE\" == *.py ]] && ruff format \"$FILE\"'"
}
]
}
]
}
}Git Reminder on Stop
停止时Git提交提醒
json
{
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash -c 'changes=$(git status --porcelain | wc -l); [ $changes -gt 0 ] && echo \"Reminder: $changes uncommitted changes\"'"
}
]
}
]
}
}json
{
"hooks": {
"Stop": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "bash -c 'changes=$(git status --porcelain | wc -l); [ $changes -gt 0 ] && echo \"Reminder: $changes uncommitted changes\"'"
}
]
}
]
}
}Prompt-Based and Agent-Based Hooks
基于提示词和Agent的钩子
In addition to command hooks, Claude Code supports LLM-powered hooks for decisions that require judgment.
除了命令钩子外,Claude Code 还支持 LLM 驱动的钩子,适用于需要主观判断的决策场景。
Hook Types
钩子类型对比
| Type | How It Works | Default Timeout | Use When |
|---|---|---|---|
| Runs a shell command, reads stdin, returns exit code | 600s | Deterministic rules (regex, field checks) |
| Single-turn LLM call (Haiku), returns | 30s | Judgment on hook input data alone |
| Multi-turn subagent with tool access, returns | 60s | Verification needing file/tool access |
| 类型 | 实现逻辑 | 默认超时 | 适用场景 |
|---|---|---|---|
| 运行 shell 命令,读取标准输入,返回退出码 | 600秒 | 确定性规则场景(正则匹配、字段校验等) |
| 单轮 LLM 调用(Haiku 模型),返回 | 30秒 | 仅需基于钩子输入数据做判断的场景 |
| 具备工具访问能力的多轮子Agent,返回 | 60秒 | 需要访问文件/工具做验证的场景 |
Supported Events
支持的事件
Prompt and agent hooks work on: , , , , , , , .
PreToolUsePostToolUsePostToolUseFailurePermissionRequestStopSubagentStopTaskCompletedUserPromptSubmitAll other events (, , , etc.) support only hooks.
SessionStartSessionEndPreCompactcommand提示词和Agent钩子仅支持以下事件:、、、、、、、。
PreToolUsePostToolUsePostToolUseFailurePermissionRequestStopSubagentStopTaskCompletedUserPromptSubmit其他所有事件(、、等)仅支持 类型钩子。
SessionStartSessionEndPreCompactcommandPrompt Hook Configuration
提示词钩子配置
json
{
"type": "prompt",
"prompt": "Evaluate whether all tasks are complete. $ARGUMENTS",
"model": "haiku",
"timeout": 30,
"statusMessage": "Checking completeness..."
}json
{
"type": "prompt",
"prompt": "Evaluate whether all tasks are complete. $ARGUMENTS",
"model": "haiku",
"timeout": 30,
"statusMessage": "Checking completeness..."
}Agent Hook Configuration
Agent钩子配置
json
{
"type": "agent",
"prompt": "Run the test suite and verify all tests pass. $ARGUMENTS",
"model": "haiku",
"timeout": 120,
"statusMessage": "Running test verification..."
}json
{
"type": "agent",
"prompt": "Run the test suite and verify all tests pass. $ARGUMENTS",
"model": "haiku",
"timeout": 120,
"statusMessage": "Running test verification..."
}Response Schema (both types)
响应Schema(两种类型通用)
json
{"ok": true}
{"ok": false, "reason": "Explanation of what's wrong"}json
{"ok": true}
{"ok": false, "reason": "Explanation of what's wrong"}Stop Hook Loop Prevention
Stop 钩子循环预防
Stop hooks fire every time Claude finishes responding, including after acting on stop hook feedback. Include this check in Stop hook prompts:
First: if stop_hook_active is true in the input, respond with {"ok": true} immediately.For the full decision guide on when to use each hook type, see .claude/rules/prompt-agent-hooks.md.
Stop 钩子会在每次 Claude 完成响应时触发,包括响应 Stop 钩子反馈后的场景。请在 Stop 钩子的提示词中添加如下检查:
First: if stop_hook_active is true in the input, respond with {"ok": true} immediately.如需查看不同钩子类型的选型决策指南,请参考 .claude/rules/prompt-agent-hooks.md。
Handling Blocked Commands
被拦截命令的处理规则
When a PreToolUse hook blocks a command:
| Situation | Action |
|---|---|
| Hook suggests alternative | Use the suggested tool/approach |
| Alternative won't work | Ask user to run command manually |
| User says "proceed" | Still blocked - explain and provide command for manual execution |
Critical: User permission does NOT bypass hooks. Retrying a blocked command will fail again.
When command is legitimately needed:
- Explain why the command is required
- Describe alternatives considered and why they won't work
- Provide exact command for user to run manually
- Let user decide
Example response:
The hook blocked `git reset --hard abc123` because it's usually unnecessary.
I considered:
- `git pull`: Won't work because we need to discard local commits, not merge
- `git restore`: Only handles file changes, not commit history
If you need to proceed, please run manually:
git reset --hard abc123
This is needed because [specific justification].当 PreToolUse 钩子拦截了命令时:
| 场景 | 处理方式 |
|---|---|
| 钩子给出了替代方案 | 使用推荐的工具/方案 |
| 替代方案不可行 | 告知用户需要手动运行命令 |
| 用户要求「继续执行」 | 依然会被拦截 - 解释原因并提供手动执行的命令 |
重要提示:用户授权无法绕过钩子规则,重试被拦截的命令依然会失败。
当确实需要执行被拦截的命令时:
- 说明该命令的必要性
- 描述已尝试的替代方案及不可行的原因
- 提供完整的手动执行命令
- 交由用户决策
响应示例:
The hook blocked `git reset --hard abc123` because it's usually unnecessary.
I considered:
- `git pull`: Won't work because we need to discard local commits, not merge
- `git restore`: Only handles file changes, not commit history
If you need to proceed, please run manually:
git reset --hard abc123
This is needed because [specific justification].Best Practices
最佳实践
Script Development:
- Always read input from stdin with
cat - Use for JSON parsing
jq - Quote all variables to prevent injection
- Exit with code 2 to block, 0 to allow
- Write blocking messages to stderr
- Keep hooks fast (< 5 seconds)
Configuration:
- Use for portable paths
$CLAUDE_PROJECT_DIR - Set explicit timeouts (default: 10 minutes / 600s as of 2.1.50)
- Use specific matchers over wildcards
- Test hooks manually before enabling
Security:
- Validate all inputs
- Use absolute paths
- Avoid touching or
.envdirectly.git/ - Review hook code before deployment
脚本开发规范:
- 始终通过 从标准输入读取数据
cat - 使用 做 JSON 解析
jq - 为所有变量添加引号避免注入风险
- 退出码为2表示拦截操作,0表示允许操作
- 拦截信息输出到标准错误流
- 保持钩子执行速度快(< 5秒)
配置规范:
- 使用 实现路径可移植
$CLAUDE_PROJECT_DIR - 设置明确的超时时间(2.1.50版本默认超时为10分钟/600秒)
- 优先使用精准匹配器而非通配符
- 启用前手动测试钩子逻辑
安全规范:
- 校验所有输入数据
- 使用绝对路径
- 避免直接操作 或
.env目录.git/ - 部署前审核钩子代码
Debugging
调试方法
Verify hook registration:
/hooksEnable debug logging:
bash
claude --debugTest hooks manually:
bash
echo '{"tool_input": {"command": "cat file.txt"}}' | bash your-hook.sh
echo $? # Check exit code验证钩子注册状态:
/hooks启用调试日志:
bash
claude --debug手动测试钩子:
bash
echo '{"tool_input": {"command": "cat file.txt"}}' | bash your-hook.sh
echo $? # Check exit codeAvailable Hooks in This Plugin
本插件提供的内置钩子
- bash-antipatterns.sh: Detects when Claude uses shell commands instead of built-in tools (cat, grep, sed, timeout, etc.)
See for full documentation.
hooks/README.md- bash-antipatterns.sh:检测 Claude 是否使用 shell 命令替代了内置工具(cat、grep、sed、timeout等)
完整文档请查看 。
hooks/README.md