五、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 协同:按顺序执行,任一阻止则停止
相关推荐
Georgeviewer2 小时前
商业落地评测|实体门店GEO优化性价比与服务体系深度复盘
大数据·人工智能
GuWenyue2 小时前
分不清AI Workflow与Agent?3个实战案例彻底讲透,做AI应用不再踩选型坑
人工智能
彩讯股份3006343 小时前
彩讯股份与心洲科技签署战略合作协议,共建企业级模型后训练能力
人工智能·科技
迅易科技3 小时前
从场景验证到Agent上线:迅易 × WorkBuddy如何帮助企业建设AI能力?
人工智能·ai·腾讯云
PNP Robotics3 小时前
多伦多大学机器人峰会|物理AI与具身智能落地新趋势
人工智能·深度学习·机器学习·机器人
GIR1234 小时前
官方出品 | 多通道土壤呼吸测量系统市场现状与十五五规划深度报告:行业分析+趋势预测全收录
大数据·人工智能·机器学习
绿算技术4 小时前
绿算技术亮相第十八届HPC AI中国年会,擘画AI基础设施全栈协同新图景
人工智能
Litluecat4 小时前
2026年7月22日科技热点新闻
人工智能·科技·新闻·每日·速览
To_OC4 小时前
别再傻傻分不清:Workflow 和 Agent 到底不是一回事
人工智能·agent·workflow
触底反弹4 小时前
🔥 2026 大模型选择指南:别再只看 Benchmark 了,这些维度才是关键!
人工智能·面试