五、Hooks 实战:验证、通知与环境初始化

上一篇介绍了 Hooks 的配置格式。本篇通过实际案例,深入 Hook 的实战用法。

PreToolUse:验证 Hook

PreToolUse 在工具执行前触发,可以阻止操作。这是最重要的安全 Hook。

案例:阻止危险 Bash 命令

创建验证脚本 ~/.claude/hooks/bash-validator.sh

bash 复制代码
#!/bin/bash

# 读取 Hook 输入
input=$(cat)

# 提取命令
command=$(echo "$input" | jq -r '.tool_input.command // empty')

# 危险命令列表
dangerous_patterns=(
  "rm -rf /"
  "rm -rf *"
  ":(){ :|:& };:"
  "mkfs"
  "dd if="
  "> /dev/sda"
  "curl | bash"
  "wget | sh"
)

# 检查危险模式
for pattern in "${dangerous_patterns[@]}"; do
  if [[ "$command" == *"$pattern"* ]]; then
    # 阻止执行
    jq -n \
      --arg reason "Blocked dangerous command matching: $pattern" \
      '{continue: false, decision: "block", reason: $reason}'
    exit 0
  fi
done

# 允许执行
echo '{"continue": true}'

配置 settings.json

json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/bash-validator.sh"
          }
        ]
      }
    ]
  }
}

效果 :当 Claude 尝试执行 rm -rf * 时,Hook 检测到危险模式,返回 block,命令被阻止。

案例:强制 Git 操作审查

只允许安全的 Git 操作:

bash 复制代码
#!/bin/bash
# git-validator.sh

input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command // empty')

# 禁止强制推送到 main/master
if [[ "$command" =~ "git push --force" ]] && [[ "$command" =~ "main|master" ]]; then
  jq -n --arg reason "Force push to main/master is forbidden" \
    '{continue: false, decision: "block", reason: $reason}'
  exit 0
fi

# 禁止删除远程分支
if [[ "$command" =~ "git push --delete" ]]; then
  jq -n --arg reason "Deleting remote branches requires approval" \
    '{continue: false, decision: "block", reason: $reason}'
  exit 0
fi

echo '{"continue": true}'

PreToolUse 输出详解

PreToolUse Hook 可以返回以下字段:

json 复制代码
{
  "continue": false,              // 必需:阻止执行
  "decision": "block",            // 明确拒绝
  "reason": "Dangerous command",  // 原因(显示给用户)
  "stopReason": "Blocked by security hook", // 自定义停止消息
  "permissionDecision": "deny",   // 权限决策
  "additionalContext": "..."      // 添加上下文
}

关键字段

  • continue: false:阻止工具执行
  • decision: block:明确拒绝
  • reason:显示在权限对话框中

PostToolUse:通知 Hook

PostToolUse 在工具执行后触发,用于记录、通知、后续处理

案例:Slack 通知

创建通知脚本:

bash 复制代码
#!/bin/bash
# slack-notify.sh

input=$(cat)
tool_name=$(echo "$input" | jq -r '.tool_name')
tool_input=$(echo "$input" | jq -r '.tool_input')
tool_output=$(echo "$input" | jq -r '.tool_output // "completed"')

# 发送 Slack Webhook
curl -s -X POST "$SLACK_WEBHOOK_URL" \
  -H 'Content-type: application/json' \
  -d "$(jq -n \
    --arg tool "$tool_name" \
    --arg input "$tool_input" \
    --arg output "$tool_output" \
    '{text: "Claude Code executed \($tool)", attachments: [{color: "good", fields: [{title: "Input", value: $input, short: false}, {title: "Output", value: $output, short: false}]}]}')"

# 不影响 Claude 行为
echo '{"continue": true}'

配置:

json 复制代码
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash|Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/slack-notify.sh"
          }
        ]
      }
    ]
  }
}

效果:每次执行 Bash/Write/Edit 后,发送 Slack 通知。

案例:记录操作日志

bash 复制代码
#!/bin/bash
# audit-log.sh

input=$(cat)
timestamp=$(date -Iseconds)
session_id=$(echo "$input" | jq -r '.session_id')
tool_name=$(echo "$input" | jq -r '.tool_name')
tool_input=$(echo "$input" | jq -c '.tool_input')

# 写入审计日志
echo "$timestamp | $session_id | $tool_name | $tool_input" \
  >> ~/.claude/audit.log

echo '{"continue": true}'

SessionStart:初始化 Hook

SessionStart 在会话开始时触发,用于注入上下文、初始化环境

案例:项目约定注入

json 复制代码
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "# Project Conventions\n\n- Use TypeScript strict mode\n- Prefer functional components with hooks\n- Follow Airbnb style guide\n- Test with Jest\n- Document with JSDoc"
          }
        ]
      }
    ]
  }
}

效果:每次会话开始,Claude 自动了解项目约定。

案例:加载环境变量

bash 复制代码
#!/bin/bash
# load-env.sh

# 读取项目环境变量
if [ -f .env.local ]; then
  # 注入环境变量提示
  env_vars=$(grep -v '^#' .env.local | cut -d= -f1)
  jq -n --arg vars "$env_vars" \
    '{continue: true, additionalContext: "Available environment variables: " + $vars}'
else
  echo '{"continue": true}'
fi

配置:

json 复制代码
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "~/.claude/hooks/load-env.sh"
          }
        ]
      }
    ]
  }
}

SessionStart 输出

SessionStart Hook 可以返回:

json 复制代码
{
  "continue": true,
  "additionalContext": "...",    // 添加上下文到系统提示
  "initialUserMessage": "...",   // 替代用户的初始消息
  "watchPaths": ["..."]          // 监听的文件路径(FileChanged)
}

watchPaths :设置后,当这些文件变化时触发 FileChanged Hook。

stdin/stdout 交互详解

Hook 通过 stdin 接收 JSON 输入:

输入结构

json 复制代码
{
  "hook_event_name": "PreToolUse",
  "session_id": "abc-123",
  "tool_name": "Bash",
  "tool_use_id": "tool-456",
  "tool_input": {
    "command": "git status"
  },
  "cwd": "/path/to/project"
}

字段说明

字段 说明
hook_event_name Hook 事件类型
session_id 会话 ID
tool_name 工具名(PreToolUse/PostToolUse)
tool_use_id 工具调用 ID
tool_input 工具输入参数
cwd 当前工作目录

输出结构

json 复制代码
{
  "continue": true,
  "suppressOutput": false,
  "additionalContext": "...",
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "updatedInput": { ... }
  }
}

hookSpecificOutput:事件特定的输出字段。

PreToolUse 特定字段

json 复制代码
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",     // 或 deny
    "permissionDecisionReason": "...",
    "updatedInput": {                  // 修改工具输入
      "command": "safe-command"
    },
    "additionalContext": "..."
  }
}

updatedInput:可以修改工具的输入!例如,把危险命令替换为安全版本。

PostToolUse 特定字段

json 复制代码
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "updatedMCPToolOutput": { ... },  // 修改 MCP 工具输出
    "additionalContext": "..."
  }
}

实战:修改工具输入

高级用法:PreToolUse Hook 可以修改工具输入

bash 复制代码
#!/bin/bash
# sanitize-path.sh

input=$(cat)
command=$(echo "$input" | jq -r '.tool_input.command')

# 如果包含未转义的路径,添加引号
if [[ "$command" =~ "path with spaces" ]]; then
  safe_command=$(echo "$command" | sed 's/path with spaces/"path with spaces"/')

  jq -n \
    --arg cmd "$safe_command" \
    '{continue: true, hookSpecificOutput: {hookEventName: "PreToolUse", updatedInput: {command: $cmd}}}'
else
  echo '{"continue": true}'
fi

效果:自动修复路径参数,避免 Bash 命令失败。

多 Hook 协同

同一事件可以有多个 Hook,按顺序执行:

json 复制代码
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          { "type": "command", "command": "audit-logger.sh" },
          { "type": "command", "command": "safety-check.sh" }
        ]
      }
    ]
  }
}

执行顺序

  1. audit-logger.sh 执行,返回 continue: true
  2. safety-check.sh 执行,可能返回 block
  3. 任一 Hook 返回 continue: false,后续 Hook 不执行

下一步

下一篇将介绍 MCP 协议基础:

  • MCP 是什么?
  • 7 种传输类型
  • 配置格式

本篇要点

  1. PreToolUse 验证 :返回 block 阻止危险操作
  2. PostToolUse 通知:发送 Slack、记录审计日志
  3. SessionStart 初始化:注入项目约定、加载环境
  4. stdin/stdout JSON:Hook 通过 JSON 交互
  5. updatedInput:PreToolUse 可修改工具输入
  6. 多 Hook 协同:按顺序执行,任一阻止则停止
相关推荐
To_OC15 小时前
别死磕 Prompt 了!我用 Harness 流水线,让大模型自动产出高质量代码
人工智能·llm·agent
恒拓高科WorkPlus15 小时前
企业级即时通讯与数字协同平台:BeeWorks如何连接沟通、文档、业务与AI
人工智能
XGeFei15 小时前
【AI应用/Agent智能体搭建平台2】Dify——自部署
人工智能·笔记·学习
hans汉斯15 小时前
《软件工程与应用》期刊推荐&10月版面征稿中
图像处理·人工智能·深度学习·算法·音视频·软件工程
前端开发江鸟15 小时前
为了实现“小说自由”,我搭了一套 AI 小说工作流,结果却不如回家种田
人工智能
叠层归一研究院15 小时前
AGI 系统(八):符号范畴嵌入函子 — SymCat ↪ C_M107 严格化
c语言·开发语言·人工智能·算法·transformer·agi
2601_9648402715 小时前
4G云门禁普惠化技术实践:基于边缘计算破解成本、隐私、部署三大行业瓶颈
大数据·人工智能·边缘计算
明朝百晓生16 小时前
Deep RL learning[2026/8]
开发语言·javascript·人工智能
具身智能进化论16 小时前
国产协作机器人品牌如何选择,企业长期使用更看重什么
大数据·人工智能·机器人·工厂方法模式
AKAMAI16 小时前
2026年Gartner Peer Insights 边缘分布平台客户之声将Akamai评为客户之选
人工智能·云计算