hooks-configuration

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Claude 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

钩子生命周期事件

EventWhen It FiresKey Use Cases
SessionStartSession begins/resumesEnvironment setup, context loading
SessionEndSession terminatesCleanup, state persistence
UserPromptSubmitUser submits promptInput validation, context injection
PreToolUseBefore tool executionPermission control, blocking dangerous ops
PostToolUseAfter tool completesAuto-formatting, logging, validation
PermissionRequestClaude requests permission for a toolAuto approve/deny without user prompt
StopMain agent finishes respondingNotifications, git reminders
SubagentStartSubagent (Task tool) is about to startInput modification, context injection
SubagentStopSubagent finishesPer-task completion evaluation
WorktreeCreateNew git worktree created via EnterWorktreeWorktree setup, dependency install
WorktreeRemoveWorktree removed after session exitsCleanup, uncommitted changes alert
TeammateIdleTeammate in agent team goes idleAssign additional tasks to teammate
TaskCompletedTask in shared task list marked completeValidation gates before task acceptance
PreCompactBefore context compactionTranscript backup
NotificationClaude sends notificationCustom alerts
ConfigChangeClaude Code settings change at runtimeAudit config changes, validation
Stop vs SubagentStop:
Stop
fires at the session level when the main agent finishes a response turn.
SubagentStop
fires when an individual subagent (spawned via the Task tool) completes. Use
Stop
for session-level notifications; use
SubagentStop
for per-task quality gates.
For full schemas, examples, and timeout recommendations for each event, see .claude/rules/hooks-reference.md.
事件名称触发时机核心使用场景
SessionStart会话启动/恢复时环境初始化、上下文加载
SessionEnd会话终止时资源清理、状态持久化
UserPromptSubmit用户提交提示词时输入校验、上下文注入
PreToolUse工具执行前权限控制、拦截危险操作
PostToolUse工具执行完成后自动格式化、日志记录、结果校验
PermissionRequestClaude 申请工具使用权限时无需用户干预自动同意/拒绝权限申请
Stop主 Agent 完成响应时消息通知、Git 操作提醒
SubagentStart子Agent(Task 工具)即将启动时输入修改、上下文注入
SubagentStop子Agent 执行完成时单任务完成质量校验
WorktreeCreate通过 EnterWorktree 创建新 Git 工作树时工作树初始化、依赖安装
WorktreeRemove会话退出后工作树被删除时资源清理、未提交变更提醒
TeammateIdleAgent 团队中的协作节点进入空闲状态时为协作节点分配额外任务
TaskCompleted共享任务列表中的任务被标记为完成时任务验收前的校验关卡
PreCompact上下文压缩前对话记录备份
NotificationClaude 发送通知时自定义告警
ConfigChangeClaude Code 配置在运行时变更时配置变更审计、合法性校验
Stop 与 SubagentStop 的区别
Stop
是会话级别事件,在主 Agent 完成一次响应交互时触发;
SubagentStop
是子Agent级别事件,在单个通过 Task 工具启动的子Agent执行完成时触发。会话级别的通知使用
Stop
,单任务质量校验关卡使用
SubagentStop
如需查看每个事件的完整 schema、示例和超时时间建议,请参考 .claude/rules/hooks-reference.md

Configuration

配置说明

File Locations

文件位置

Hooks are configured in settings files:
  • ~/.claude/settings.json
    - User-level (applies everywhere)
  • .claude/settings.json
    - Project-level (committed to repo)
  • .claude/settings.local.json
    - Local project (not committed)
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
hooks
field:
yaml
---
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 中通过
hooks
字段定义钩子:
yaml
---
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:
    "Bash"
    - matches exactly "Bash" tool
  • Regex patterns:
    "Edit|Write"
    - matches either tool
  • Wildcards:
    "Notebook.*"
    - matches tools starting with "Notebook"
  • All tools:
    "*"
    - matches everything
  • MCP tools:
    "mcp__server__tool"
    - targets MCP server tools
  • 精确匹配
    "Bash"
    - 仅匹配「Bash」工具
  • 正则匹配
    "Edit|Write"
    - 匹配任意一个指定工具
  • 通配符匹配
    "Notebook.*"
    - 匹配所有以「Notebook」开头的工具
  • 全匹配
    "*"
    - 匹配所有工具
  • MCP 工具匹配
    "mcp__server__tool"
    - 匹配指定的 MCP 服务工具

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
undefined
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
undefined

Auto-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 0
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 0

Remind 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 0
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 0

Load 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"
}
EOF
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"
}
EOF

Inject 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
undefined
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
undefined

Desktop Notification on Stop (Stop)

任务完成时发送桌面通知(Stop)

bash
#!/bin/bash
bash
#!/bin/bash

Linux

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
undefined
osascript -e 'display notification "Task completed" with title "Claude Code"' 2>/dev/null
exit 0
undefined

Audit 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 0
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 0

Auto-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
undefined
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
undefined

Set 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 0
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 0

Gate 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 0
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 0

Configuration 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

钩子类型对比

TypeHow It WorksDefault TimeoutUse When
command
Runs a shell command, reads stdin, returns exit code600sDeterministic rules (regex, field checks)
prompt
Single-turn LLM call (Haiku), returns
{ok: true/false}
30sJudgment on hook input data alone
agent
Multi-turn subagent with tool access, returns
{ok: true/false}
60sVerification needing file/tool access
类型实现逻辑默认超时适用场景
command
运行 shell 命令,读取标准输入,返回退出码600秒确定性规则场景(正则匹配、字段校验等)
prompt
单轮 LLM 调用(Haiku 模型),返回
{ok: true/false}
30秒仅需基于钩子输入数据做判断的场景
agent
具备工具访问能力的多轮子Agent,返回
{ok: true/false}
60秒需要访问文件/工具做验证的场景

Supported Events

支持的事件

Prompt and agent hooks work on:
PreToolUse
,
PostToolUse
,
PostToolUseFailure
,
PermissionRequest
,
Stop
,
SubagentStop
,
TaskCompleted
,
UserPromptSubmit
.
All other events (
SessionStart
,
SessionEnd
,
PreCompact
, etc.) support only
command
hooks.
提示词和Agent钩子仅支持以下事件:
PreToolUse
PostToolUse
PostToolUseFailure
PermissionRequest
Stop
SubagentStop
TaskCompleted
UserPromptSubmit
其他所有事件(
SessionStart
SessionEnd
PreCompact
等)仅支持
command
类型钩子。

Prompt 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:
SituationAction
Hook suggests alternativeUse the suggested tool/approach
Alternative won't workAsk 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:
  1. Explain why the command is required
  2. Describe alternatives considered and why they won't work
  3. Provide exact command for user to run manually
  4. 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 钩子拦截了命令时:
场景处理方式
钩子给出了替代方案使用推荐的工具/方案
替代方案不可行告知用户需要手动运行命令
用户要求「继续执行」依然会被拦截 - 解释原因并提供手动执行的命令
重要提示:用户授权无法绕过钩子规则,重试被拦截的命令依然会失败。
当确实需要执行被拦截的命令时:
  1. 说明该命令的必要性
  2. 描述已尝试的替代方案及不可行的原因
  3. 提供完整的手动执行命令
  4. 交由用户决策
响应示例:
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:
  1. Always read input from stdin with
    cat
  2. Use
    jq
    for JSON parsing
  3. Quote all variables to prevent injection
  4. Exit with code 2 to block, 0 to allow
  5. Write blocking messages to stderr
  6. Keep hooks fast (< 5 seconds)
Configuration:
  1. Use
    $CLAUDE_PROJECT_DIR
    for portable paths
  2. Set explicit timeouts (default: 10 minutes / 600s as of 2.1.50)
  3. Use specific matchers over wildcards
  4. Test hooks manually before enabling
Security:
  1. Validate all inputs
  2. Use absolute paths
  3. Avoid touching
    .env
    or
    .git/
    directly
  4. Review hook code before deployment
脚本开发规范:
  1. 始终通过
    cat
    从标准输入读取数据
  2. 使用
    jq
    做 JSON 解析
  3. 为所有变量添加引号避免注入风险
  4. 退出码为2表示拦截操作,0表示允许操作
  5. 拦截信息输出到标准错误流
  6. 保持钩子执行速度快(< 5秒)
配置规范:
  1. 使用
    $CLAUDE_PROJECT_DIR
    实现路径可移植
  2. 设置明确的超时时间(2.1.50版本默认超时为10分钟/600秒)
  3. 优先使用精准匹配器而非通配符
  4. 启用前手动测试钩子逻辑
安全规范:
  1. 校验所有输入数据
  2. 使用绝对路径
  3. 避免直接操作
    .env
    .git/
    目录
  4. 部署前审核钩子代码

Debugging

调试方法

Verify hook registration:
/hooks
Enable debug logging:
bash
claude --debug
Test 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 code

Available Hooks in This Plugin

本插件提供的内置钩子

  • bash-antipatterns.sh: Detects when Claude uses shell commands instead of built-in tools (cat, grep, sed, timeout, etc.)
See
hooks/README.md
for full documentation.
  • bash-antipatterns.sh:检测 Claude 是否使用 shell 命令替代了内置工具(cat、grep、sed、timeout等)
完整文档请查看
hooks/README.md