<!-- title: Claude Code无人值守管道：Hook到CI门禁实战 -->

# Claude Code无人值守管道：Hook事件到CI门禁全实战

> **学习目标**：掌握Claude Code Hooks事件系统的完整架构，学会用七类生命周期事件构建自动化管道，将 `-p` 非交互模式与Hooks结合打造无人值守的CI门禁系统。
>
> **适用人群**：已熟悉Claude Code基本操作、希望将AI编程能力集成到团队自动化流水线的开发者。

## 一、前置条件

| 条件 | 说明 |
|------|------|
| Claude Code ≥ 1.0 | `claude --version` 确认 |
| Node.js ≥ 18 | Hooks脚本运行环境 |
| jq | JSON处理（`brew install jq` 或 `apt install jq`）|
| 一个Git仓库 | 本文示例基于真实项目结构 |
| 基础Shell知识 | `bash`、管道、退出码概念 |

核心概念速查：Claude Code有两种运行模式——**交互模式**（Interactive）和**非交互/Headless模式**（通过 `-p` / `--print` 触发）。Hooks是贯穿两种模式的事件拦截层。

## 二、Hooks事件系统：七类生命周期全景

### 2.1 事件分类与触发时机

Claude Code的Hooks系统是一个**面向切面编程（AOP）** 的事件总线。每个Hook在Agent循环的特定阶段触发，接收JSON输入，通过退出码和stdout返回决策。

```mermaid
sequenceDiagram
    participant U as 用户/脚本
    participant CC as Claude Code Agent Loop
    participant H as Hooks事件总线
    participant T as 工具(Tool)

    Note over CC: SessionStart → 触发一次
    U->>CC: 发送Prompt
    CC->>H: UserPromptSubmit事件
    H-->>CC: 放行(exit 0) 或 注入上下文

    loop Agent Loop (每轮迭代)
        CC->>H: PreToolUse事件
        H-->>CC: 允许(exit 0) / 阻止(exit 2) / 询问用户(exit 3)
        alt 工具被允许
            CC->>T: 执行工具(Bash/Edit/Write...)
            T-->>CC: 工具结果
            CC->>H: PostToolUse事件
            H-->>CC: 日志/后处理
        end
    end

    Note over CC: Stop事件 → Agent完成
    CC->>H: Notification事件(可选)
    H-->>U: 通知(Slack/邮件/桌面)
    CC-->>U: 最终回复
```

七类核心生命周期事件（截至2026年5月）：

| 事件类型 | 触发时机 | 核心用途 | 退出码行为 |
|---------|---------|---------|-----------|
| **SessionStart** | 会话初始化时触发一次 | 加载上下文、设置环境变量 | 0=继续，2=终止 |
| **UserPromptSubmit** | 用户提交Prompt后、Agent处理前 | 注入系统提示、预处理指令 | 0=继续，2=阻止提交 |
| **PreToolUse** | 工具执行前 | 安全审查、权限控制、输入校验 | 0=允许，2=阻止，3=询问用户 |
| **PostToolUse** | 工具执行后 | 自动格式化、审计日志、触发后处理 | 0=继续（可修改结果） |
| **Stop** | Agent完成任务后 | 质量检查、结果验证、汇总报告 | 0=完成，2=继续工作 |
| **SubagentStop** | 子Agent完成时 | 收集子Agent结果、协调多Agent | 0=完成，2=继续 |
| **Notification** | 需要通知用户时 | 桌面通知、Slack/邮件推送 | 0=已处理 |

### 2.2 Hook配置架构

Hooks在 `settings.json` 中声明式配置，支持三层优先级：

```
~/.claude/settings.json        ← 全局（所有项目）
.claude/settings.json          ← 项目级（推荐）
.claude/settings.local.json    ← 本地覆盖（不入Git）
```

基本配置结构：

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/pre-bash.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write $CLAUDE_FILE_PATH 2>/dev/null"
          }
        ]
      }
    ]
  }
}
```

**Matcher语法**：使用正则表达式匹配工具名称。`Bash` 匹配Bash工具调用，`Write|Edit` 匹配文件写入和编辑操作，`.*` 匹配所有工具。

## 三、-p模式：非交互式管道的基石

### 3.1 基本用法与输出控制

`-p`（`--print`）标志将Claude Code转换为标准命令行工具——接收输入，输出结果，然后退出。

```bash
# 最基本用法
claude -p "解释这段代码的作用：$(cat main.go)"

# 管道输入
echo "为这个函数写单元测试" | claude -p

# 指定输出格式
claude -p "列出所有TODO注释" --output-format json

# 流式输出（逐token输出，适合实时展示）
claude -p "重构这个模块" --output-format stream-json

# 纯文本输出（默认）
claude -p "生成README" --output-format text
```

### 3.2 结构化输出：JSON Schema约束

这是自动化管道的关键——让Claude返回可预测的JSON结构：

```bash
# 定义期望的输出Schema
cat > /tmp/review-schema.json << 'EOF'
{
  "type": "object",
  "properties": {
    "issues": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "file": { "type": "string" },
          "line": { "type": "number" },
          "severity": { "enum": ["error", "warning", "info"] },
          "message": { "type": "string" }
        },
        "required": ["file", "severity", "message"]
      }
    },
    "summary": { "type": "string" }
  },
  "required": ["issues", "summary"]
}
EOF

# 使用Schema约束输出
claude -p "审查当前目录的代码质量" \
  --output-format json \
  --json-schema "$(cat /tmp/review-schema.json)"
```

预期输出：
```json
{
  "issues": [
    {
      "file": "src/auth.ts",
      "line": 42,
      "severity": "error",
      "message": "SQL注入风险：直接拼接用户输入"
    },
    {
      "file": "src/utils.ts",
      "line": 15,
      "severity": "warning",
      "message": "未处理的Promise rejection"
    }
  ],
  "summary": "发现1个严重问题和1个警告，建议优先修复auth.ts中的SQL注入"
}
```

### 3.3 会话恢复与连续对话

`-p` 模式支持通过 `--resume` 恢复之前的会话，实现多轮自动化：

```bash
# 第一轮：分析代码
SESSION_ID=$(claude -p "分析src/目录的架构问题" --output-format json | jq -r '.session_id')

# 第二轮：基于分析结果生成修复方案（同一会话上下文）
claude -p "根据刚才的分析，生成修复计划" --resume "$SESSION_ID"

# 第三轮：执行修复
claude -p "按照修复计划，开始修改代码" --resume "$SESSION_ID" \
  --permission-mode acceptEdits
```

### 3.4 权限模式与安全边界

非交互模式下的权限控制是自动化的安全基石：

| 模式 | 标志 | 行为 | 适用场景 |
|------|------|------|---------|
| `default` | （默认） | 每次操作需要确认 | ❌ 不适合自动化 |
| `plan` | `--permission-mode plan` | 只规划不执行 | 代码审查、分析 |
| `acceptEdits` | `--permission-mode acceptEdits` | 自动接受文件编辑 | 代码生成、重构 |
| `bypassPermissions` | `--dangerously-skip-permissions` | 跳过所有权限检查 | ⚠️ 仅CI沙箱环境 |

```bash
# 安全的自动化模式：只允许特定工具
claude -p "运行测试并修复失败用例" \
  --permission-mode acceptEdits \
  --allowedTools "Bash(npm test:*),Edit,Read,Write"
```

## 四、Hook深入：构建自动化守护层

### 4.1 Hook的JSON输入输出协议

每个Hook通过 `stdin` 接收JSON，通过退出码和 `stdout` 返回决策。这是理解Hooks的核心：

**PreToolUse 输入示例**：

```json
{
  "event": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules && npm install",
    "description": "清理并重新安装依赖"
  },
  "session_id": "abc123",
  "conversation_id": "conv-456"
}
```

**Hook脚本模板**：

```bash
#!/bin/bash
# .claude/hooks/pre-bash-guard.sh

# 从stdin读取JSON输入
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# 规则1：阻止危险命令
DANGEROUS_PATTERNS=('rm -rf /' 'drop table' 'dd if=' ':(){ :|:& };:' 'mkfs.')

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
  if echo "$COMMAND" | grep -qi "$pattern"; then
    # 退出码2 = 阻止操作，stdout作为拒绝理由
    echo "BLOCKED: 检测到危险命令模式: $pattern"
    exit 2
  fi
done

# 规则2：阻止访问敏感文件
if echo "$COMMAND" | grep -qE '\.(env|pem|key|p12)\b'; then
  echo "BLOCKED: 禁止访问密钥/凭证文件"
  exit 2
fi

# 退出码0 = 允许操作
exit 0
```

### 4.2 PostToolUse：自动格式化与审计

PostToolUse是自动化的主力——在工具执行后自动触发后处理：

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/post-format.sh"
          }
        ]
      },
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/post-bash-audit.sh"
          }
        ]
      }
    ]
  }
}
```

**自动格式化脚本**：

```bash
#!/bin/bash
# .claude/hooks/post-format.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

[ -z "$FILE_PATH" ] && exit 0
[ ! -f "$FILE_PATH" ] && exit 0

# 根据文件类型选择格式化工具
case "$FILE_PATH" in
  *.ts|*.tsx|*.js|*.jsx)
    npx prettier --write "$FILE_PATH" 2>/dev/null
    npx eslint --fix "$FILE_PATH" 2>/dev/null
    ;;
  *.py)
    python -m black "$FILE_PATH" 2>/dev/null
    python -m isort "$FILE_PATH" 2>/dev/null
    ;;
  *.go)
    gofmt -w "$FILE_PATH" 2>/dev/null
    ;;
  *.rs)
    rustfmt "$FILE_PATH" 2>/dev/null
    ;;
esac

exit 0
```

### 4.3 SessionStart：动态注入上下文

SessionStart事件可以返回额外上下文注入到Agent的Prompt中，实现动态项目配置：

```bash
#!/bin/bash
# .claude/hooks/session-context.sh

# 读取Git分支名
BRANCH=$(git branch --show-current 2>/dev/null || echo "unknown")

# 读取最近的变更文件
CHANGED_FILES=$(git diff --name-only HEAD~1 2>/dev/null | head -20 | tr '\n' ',')

# 读取package.json中的项目名
PROJECT=$(jq -r '.name // "unknown"' package.json 2>/dev/null)

# 输出JSON格式的额外上下文（stdout会被注入到Prompt中）
cat << EOF
{"extra_context": "当前项目: $PROJECT | 分支: $BRANCH | 最近变更: $CHANGED_FILES | 请在回复中考虑这些上下文信息。"}
EOF

exit 0
```

### 4.4 Stop事件：质量门禁

Stop事件在Agent完成任务时触发，可以作为最终的质量检查点：

```bash
#!/bin/bash
# .claude/hooks/stop-quality-gate.sh

INPUT=$(cat)

# 检查是否有未提交的lint错误
LINT_ERRORS=$(npx eslint src/ --format json 2>/dev/null | jq '[.[] | select(.errorCount > 0)] | length')

if [ "$LINT_ERRORS" -gt 0 ]; then
  # 退出码2 = 让Agent继续工作（修复lint错误）
  echo "检测到 $LINT_ERRORS 个Lint错误，请修复后重新检查。"
  exit 2
fi

# 检查测试是否通过
TEST_RESULT=$(npm test 2>&1)
if echo "$TEST_RESULT" | grep -q "FAIL"; then
  echo "测试未通过，请修复失败的测试用例。"
  exit 2
fi

echo "质量检查通过"
exit 0
```

### 4.5 Notification：多通道告警

```bash
#!/bin/bash
# .claude/hooks/notify.sh

INPUT=$(cat)
MESSAGE=$(echo "$INPUT" | jq -r '.message // "任务完成"')
TITLE=$(echo "$INPUT" | jq -r '.title // "Claude Code"')

# macOS桌面通知
if [[ "$OSTYPE" == "darwin"* ]]; then
  osascript -e "display notification \"$MESSAGE\" with title \"$TITLE\""
fi

# Linux桌面通知
if [[ "$OSTYPE" == "linux"* ]]; then
  notify-send "$TITLE" "$MESSAGE" 2>/dev/null
fi

# Slack通知（如果配置了Webhook）
if [ -n "$SLACK_WEBHOOK_URL" ]; then
  curl -s -X POST "$SLACK_WEBHOOK_URL" \
    -H 'Content-Type: application/json' \
    -d "{\"text\": \"[$TITLE] $MESSAGE\"}" > /dev/null 2>&1
fi

exit 0
```

## 五、实战：构建无人值守CI门禁系统

### 5.1 系统架构

下面我们将构建一个完整的CI门禁系统，结合 `-p` 模式和Hooks，实现：代码提交 → 自动审查 → 自动修复 → 质量门禁 → 通知。

```mermaid
flowchart TB
    subgraph 触发层
        A[Git Push / PR创建] --> B[GitHub Actions触发]
        C[Cron定时任务] --> D[Shell脚本调度]
        E[Webhook事件] --> F[事件处理器]
    end

    subgraph 管道层
        B --> G[claude -p 分析]
        D --> G
        F --> G
        G --> H{JSON输出解析}
        H -->|有严重问题| I[claude -p 修复 --resume]
        H -->|无问题| J[质量检查通过]
        I --> K[Hooks Stop门禁]
        K -->|exit 2| I
        K -->|exit 0| J
    end

    subgraph 通知层
        J --> L[Notification Hook]
        L --> M[Slack/邮件/PR评论]
    end

    style G fill:#4A90D9,color:#fff
    style K fill:#E67E22,color:#fff
    style J fill:#27AE60,color:#fff
```

### 5.2 项目结构

```
project/
├── .claude/
│   ├── settings.json          # Hooks配置
│   ├── CLAUDE.md              # 项目指令
│   └── hooks/
│       ├── pre-bash-guard.sh  # Bash命令守卫
│       ├── post-format.sh     # 自动格式化
│       ├── post-bash-audit.sh # 命令审计日志
│       ├── stop-gate.sh       # 完成质量门禁
│       ├── session-context.sh # 动态上下文注入
│       └── notify.sh          # 多通道通知
├── .github/
│   └── workflows/
│       └── claude-review.yml  # CI管道配置
├── scripts/
│   ├── batch-review.sh        # 批量审查脚本
│   └── auto-fix.sh            # 自动修复脚本
└── src/
    └── ...
```

### 5.3 Hooks完整配置

```json
// .claude/settings.json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/session-context.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/pre-bash-guard.sh"
          }
        ]
      },
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/pre-write-protect.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/post-format.sh"
          }
        ]
      },
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/post-bash-audit.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/stop-gate.sh"
          }
        ]
      }
    ],
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/notify.sh"
          }
        ]
      }
    ]
  }
}
```

### 5.4 写保护Hook

```bash
#!/bin/bash
# .claude/hooks/pre-write-protect.sh

INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# 保护关键配置文件
PROTECTED_FILES=(
  "package-lock.json"
  "yarn.lock"
  "pnpm-lock.yaml"
  ".env.production"
  "terraform.tfstate"
  "docker-compose.prod.yml"
)

for protected in "${PROTECTED_FILES[@]}"; do
  if [[ "$FILE_PATH" == *"$protected"* ]]; then
    echo "BLOCKED: 文件 $FILE_PATH 受保护，禁止自动修改"
    exit 2
  fi
done

# 保护生产分支上的敏感目录
BRANCH=$(git branch --show-current 2>/dev/null)
if [[ "$BRANCH" == "main" || "$BRANCH" == "master" || "$BRANCH" == "release"* ]]; then
  if [[ "$FILE_PATH" == src/core/* || "$FILE_PATH" == src/migrations/* ]]; then
    echo "BLOCKED: 分支 $BRANCH 上禁止修改 $FILE_PATH，请通过PR流程"
    exit 2
  fi
fi

exit 0
```

### 5.5 审计日志Hook

```bash
#!/bin/bash
# .claude/hooks/post-bash-audit.sh

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
RESULT=$(echo "$INPUT" | jq -r '.tool_result // empty')
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
SESSION=$(echo "$INPUT" | jq -r '.session_id // "unknown"')

LOG_DIR=".claude/audit"
mkdir -p "$LOG_DIR"

# 写入审计日志
echo "$INPUT" | jq --arg ts "$TIMESTAMP" --arg sid "$SESSION" \
  '. + {audit_timestamp: $ts, audit_session: $sid}' \
  >> "$LOG_DIR/commands-$(date +%Y%m%d).jsonl"

# 高风险操作实时告警
if echo "$COMMAND" | grep -qE '(curl|wget|ssh|scp|rsync|nc )'; then
  echo "[AUDIT] 网络操作: $COMMAND" >> "$LOG_DIR/security-alerts.log"
fi

exit 0
```

### 5.6 GitHub Actions CI管道

```yaml
# .github/workflows/claude-review.yml
name: Claude Code Review

on:
  pull_request:
    types: [opened, synchronize]
  push:
    branches: [main, develop]

jobs:
  ai-review:
    runs-on: ubuntu-latest
    timeout-minutes: 15
    permissions:
      contents: read
      pull-requests: write

    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Claude Code
        run: |
          npm install -g @anthropic-ai/claude-code
          echo "Installed claude $(claude --version)"

      - name: Analyze Changes
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
        id: analyze
        run: |
          # 获取变更文件列表
          if [ "${{ github.event_name }}" = "pull_request" ]; then
            DIFF_CMD="git diff origin/main...HEAD"
          else
            DIFF_CMD="git diff HEAD~1 HEAD"
          fi

          # 运行Claude分析
          RESULT=$(claude -p "你是一个代码审查专家。分析以下Git Diff，输出JSON格式的审查结果：
          {
            \"issues\": [{\"file\": \"\", \"line\": 0, \"severity\": \"error|warning|info\", \"message\": \"\"}],
            \"score\": 0-100,
            \"summary\": \"一句话总结\"
          }
          
          Diff内容：
          $($DIFF_CMD)" \
            --output-format json \
            --permission-mode plan \
            --bare \
            --max-turns 3)

          echo "result<<EOF" >> $GITHUB_OUTPUT
          echo "$RESULT" >> $GITHUB_OUTPUT
          echo "EOF" >> $GITHUB_OUTPUT

      - name: Post Review Comment
        if: github.event_name == 'pull_request'
        env:
          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          RESULT='${{ steps.analyze.outputs.result }}'
          SCORE=$(echo "$RESULT" | jq -r '.score // "N/A"')
          SUMMARY=$(echo "$RESULT" | jq -r '.summary // "No summary"')
          ISSUES=$(echo "$RESULT" | jq -r '.issues | length')

          # 格式化评论
          cat > /tmp/comment.md << COMMENT
          ## Claude Code Review

          **评分**: ${SCORE}/100
          **问题数**: ${ISSUES}

          ${SUMMARY}

          $(echo "$RESULT" | jq -r '.issues[] | "### \(.severity | ascii_upcase): \(.file):\(.line // \"N/A\")\n\(.message)\n"' 2>/dev/null)
          COMMENT

          gh pr comment ${{ github.event.pull_request.number }} \
            --body-file /tmp/comment.md

      - name: Quality Gate
        run: |
          RESULT='${{ steps.analyze.outputs.result }}'
          ERRORS=$(echo "$RESULT" | jq '[.issues[] | select(.severity == "error")] | length')

          if [ "$ERRORS" -gt 0 ]; then
            echo "::error::发现 $ERRORS 个严重问题"
            exit 1
          fi

          echo "✅ 代码质量检查通过"
```

### 5.7 批量审查脚本

对多个目录或多个仓库进行批量自动化审查：

```bash
#!/bin/bash
# scripts/batch-review.sh
# 用法: ./batch-review.sh [目录或文件列表]

set -euo pipefail

TARGETS=("${@:-src/}")
REPORT_DIR=".claude/reports/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$REPORT_DIR"

echo "=== Claude Code 批量审查 ==="
echo "目标: ${TARGETS[*]}"
echo "报告目录: $REPORT_DIR"
echo ""

# 定义审查Schema
REVIEW_SCHEMA='{
  "type": "object",
  "properties": {
    "issues": {
      "type": "array",
      "items": {
        "type": "object",
        "properties": {
          "file": {"type": "string"},
          "line": {"type": "number"},
          "severity": {"enum": ["error","warning","info"]},
          "category": {"enum": ["security","performance","maintainability","style"]},
          "message": {"type": "string"}
        }
      }
    },
    "score": {"type": "number"},
    "summary": {"type": "string"}
  }
}'

TOTAL_ISSUES=0
TOTAL_FILES=0

# 函数：审查单个目标
review_target() {
  local target="$1"
  local output_file="$REPORT_DIR/$(echo "$target" | tr '/' '_').json"

  echo "审查: $target"

  # 使用-p模式进行非交互审查
  claude -p "审查 $target 中的代码质量问题，关注安全性、性能和可维护性。" \
    --output-format json \
    --json-schema "$REVIEW_SCHEMA" \
    --permission-mode plan \
    --bare \
    --max-turns 2 \
    > "$output_file" 2>/dev/null

  # 解析结果
  local issues=$(jq '[.issues // []] | flatten | length' "$output_file" 2>/dev/null || echo 0)
  local score=$(jq '.score // 0' "$output_file" 2>/dev/null || echo 0)

  TOTAL_ISSUES=$((TOTAL_ISSUES + issues))
  TOTAL_FILES=$((TOTAL_FILES + 1))

  echo "  发现 $issues 个问题，评分: $score/100"
}

# 并行审查（最多4个并发）
export -f review_target
export REPORT_DIR REVIEW_SCHEMA TOTAL_ISSUES TOTAL_FILES

for target in "${TARGETS[@]}"; do
  # 如果目标是目录，按子目录拆分并行处理
  if [ -d "$target" ]; then
    find "$target" -maxdepth 1 -type d | tail -n +2 | \
      xargs -P 4 -I {} bash -c 'review_target "$@"' _ {}
  else
    review_target "$target"
  fi
done

# 生成汇总报告
echo ""
echo "=== 审查汇总 ==="
echo "审查目标数: ${#TARGETS[@]}"
echo "发现问题: $TOTAL_ISSUES"

# 合并所有报告
jq -s '{
  total_issues: [.[].issues // [] | flatten] | add | length,
  avg_score: ([.[].score // 0] | add / length),
  by_severity: {
    errors: [.[].issues // [] | flatten | .[] | select(.severity == "error")] | length,
    warnings: [.[].issues // [] | flatten | .[] | select(.severity == "warning")] | length,
    info: [.[].issues // [] | flatten | .[] | select(.severity == "info")] | length
  },
  reports: .
}' "$REPORT_DIR"/*.json > "$REPORT_DIR/summary.json" 2>/dev/null

echo "详细报告: $REPORT_DIR/summary.json"

# 如果有严重错误，退出码非0
ERRORS=$(jq '.by_severity.errors // 0' "$REPORT_DIR/summary.json" 2>/dev/null || echo 0)
if [ "$ERRORS" -gt 0 ]; then
  echo "⚠️ 发现 $ERRORS 个严重问题，请修复后重新审查"
  exit 1
fi

echo "✅ 所有检查通过"
```

### 5.8 自动修复管道

审查发现问题后，自动触发修复流程：

```bash
#!/bin/bash
# scripts/auto-fix.sh
# 用法: ./auto-fix.sh <审查报告JSON>

set -euo pipefail

REPORT_FILE="${1:?用法: auto-fix.sh <report.json>}"
MAX_FIX_ROUNDS=3

[ ! -f "$REPORT_FILE" ] && { echo "报告文件不存在: $REPORT_FILE"; exit 1; }

# 提取所有error级别的问题
ERRORS=$(jq -c '[.issues // [] | flatten | .[] | select(.severity == "error")]' "$REPORT_FILE")

if [ "$ERRORS" == "[]" ]; then
  echo "没有严重问题需要修复"
  exit 0
fi

echo "发现需要自动修复的问题："
echo "$ERRORS" | jq -r '.[] | "  [\(.severity)] \(.file):\(.line // "N/A") - \(.message)"'

# 构造修复Prompt
FIX_PROMPT="请修复以下代码问题，每个问题修复后运行相关测试确认修复有效：
$(echo "$ERRORS" | jq -r '.[] | "- \(.file):\(.line // "N/A"): \(.message)"')"

# 第一轮：初始会话
SESSION=$(claude -p "$FIX_PROMPT" \
  --permission-mode acceptEdits \
  --output-format json \
  --bare \
  --max-turns 10 | jq -r '.session_id // empty')

if [ -z "$SESSION" ]; then
  echo "修复失败：无法创建会话"
  exit 1
fi

# 循环检查修复结果
for round in $(seq 2 $MAX_FIX_ROUNDS); do
  echo "--- 修复轮次 $round/$MAX_FIX_ROUNDS ---"

  # 重新运行审查
  NEW_REPORT=".claude/reports/fix-round-$round.json"
  claude -p "重新检查之前的问题是否已修复，是否引入了新问题" \
    --resume "$SESSION" \
    --output-format json \
    --json-schema "$(jq -c '.' /tmp/review-schema.json 2>/dev/null || echo '{}')" \
    --permission-mode plan \
    --bare \
    --max-turns 3 \
    > "$NEW_REPORT"

  # 检查剩余错误
  REMAINING=$(jq '[.issues // [] | flatten | .[] | select(.severity == "error")] | length' "$NEW_REPORT")

  if [ "$REMAINING" -eq 0 ]; then
    echo "✅ 所有问题已修复"
    exit 0
  fi

  echo "仍有 $REMAINING 个问题，继续修复..."
done

echo "⚠️ 达到最大修复轮次 ($MAX_FIX_ROUNDS)，请手动检查"
exit 1
```

## 六、高级模式：事件链与状态机

### 6.1 Hook事件链

通过将多个Hook串联，构建复杂的事件处理链：

```mermaid
stateDiagram-v2
    [*] --> SessionStart: 会话开始
    SessionStart --> Idle: 加载项目上下文

    Idle --> UserPromptSubmit: 用户输入
    UserPromptSubmit --> PreToolUse: Agent决定使用工具

    state PreToolUse {
        [*] --> 检查命令安全性
        检查命令安全性 --> 检查文件保护: 通过
        检查命令安全性 --> [*]: 阻止(exit 2)
        检查文件保护 --> [*]: 通过(exit 0)
        检查文件保护 --> [*]: 阻止(exit 2)
    }

    PreToolUse --> ToolExecution: 允许
    PreToolUse --> Idle: 阻止
    ToolExecution --> PostToolUse: 执行完成

    state PostToolUse {
        [*] --> 自动格式化
        自动格式化 --> 审计日志: 完成
        审计日志 --> [*]: 记录完成
    }

    PostToolUse --> Idle: 继续Agent Loop
    PostToolUse --> Stop: 任务完成

    state Stop {
        [*] --> 运行Lint
        运行Lint --> 运行测试: 通过
        运行Lint --> [*]: 失败→继续修复
        运行测试 --> [*]: 通过(exit 0)
        运行测试 --> [*]: 失败→继续修复
    }

    Stop --> Notification: 全部通过
    Stop --> Idle: 有问题，继续
    Notification --> [*]: 通知用户
```

### 6.2 条件化Hook

通过脚本内的逻辑判断实现条件化行为：

```bash
#!/bin/bash
# .claude/hooks/conditional-hook.sh
# 演示：只在特定条件下触发的Hook逻辑

INPUT=$(cat)
EVENT=$(echo "$INPUT" | jq -r '.event // empty')
TOOL=$(echo "$INPUT" | jq -r '.tool_name // empty')
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

# 条件1：CI环境下更严格
if [ "${CI:-}" = "true" ]; then
  # CI中禁止所有网络操作（除了npm test）
  if echo "$COMMAND" | grep -qE '(curl|wget|ssh)' && \
     ! echo "$COMMAND" | grep -q 'npm'; then
    echo "BLOCKED: CI环境中禁止网络操作: $COMMAND"
    exit 2
  fi
fi

# 条件2：根据分支名调整策略
BRANCH=$(git branch --show-current 2>/dev/null || echo "HEADless")
if [[ "$BRANCH" == feature/* ]]; then
  # Feature分支：宽松模式，允许实验性操作
  exit 0
elif [[ "$BRANCH" == main || "$BRANCH" == release/* ]]; then
  # 主干分支：严格模式
  if echo "$COMMAND" | grep -qE '(npm publish|docker push|git push)'; then
    echo "BLOCKED: 主干分支禁止直接发布操作"
    exit 2
  fi
fi

# 条件3：根据时间限制（工作时间内才允许某些操作）
HOUR=$(date +%H)
if [ "$HOUR" -lt 9 ] || [ "$HOUR" -gt 18 ]; then
  if echo "$COMMAND" | grep -qE '(deploy|publish|release)'; then
    echo "BLOCKED: 非工作时间禁止部署操作 (当前: ${HOUR}:00)"
    exit 2
  fi
fi

exit 0
```

### 6.3 Hook之间共享状态

通过临时文件在Hook之间传递状态：

```bash
#!/bin/bash
# .claude/hooks/state-manager.sh
# 用法: state-manager.sh [get|set] [key] [value]

STATE_DIR="/tmp/claude-hooks-state"
mkdir -p "$STATE_DIR"

ACTION="${1:-get}"
KEY="${2:-default}"
VALUE="${3:-}"

SESSION_ID="${CLAUDE_SESSION_ID:-unknown}"
STATE_FILE="$STATE_DIR/$SESSION_ID.json"

case "$ACTION" in
  set)
    # 设置状态
    if [ ! -f "$STATE_FILE" ]; then
      echo '{}' > "$STATE_FILE"
    fi
    jq --arg k "$KEY" --arg v "$VALUE" '.[$k] = $v' "$STATE_FILE" > "${STATE_FILE}.tmp"
    mv "${STATE_FILE}.tmp" "$STATE_FILE"
    ;;
  get)
    # 获取状态
    if [ -f "$STATE_FILE" ]; then
      jq -r --arg k "$KEY" '.[$k] // ""' "$STATE_FILE"
    fi
    ;;
  incr)
    # 计数器递增
    CURRENT=$(bash "$0" get "$KEY")
    NEW_VAL=$((CURRENT + 1))
    bash "$0" set "$KEY" "$NEW_VAL"
    echo "$NEW_VAL"
    ;;
esac
```

在Hooks中使用状态管理器：

```bash
#!/bin/bash
# 在PreToolUse中计数Bash调用
COUNT=$(bash .claude/hooks/state-manager.sh incr bash_calls)
if [ "$COUNT" -gt 50 ]; then
  echo "WARNING: 本次会话已执行 $COUNT 次Bash命令，可能存在效率问题"
fi
exit 0
```

## 七、性能优化与调试

### 7.1 --bare模式加速启动

`--bare` 标志跳过Hooks自动发现和Skills加载，大幅减少启动时间：

```bash
# 正常启动（加载所有Hooks和Skills）
time claude -p "hello" --output-format text
# 约 3-5 秒

# bare模式（跳过发现阶段）
time claude -p "hello" --output-format text --bare
# 约 1-2 秒

# CI中推荐：bare + 指定必要工具
claude -p "运行测试" \
  --bare \
  --allowedTools "Bash,Read" \
  --permission-mode acceptEdits
```

### 7.2 Hook执行超时控制

```bash
#!/bin/bash
# 在Hook中设置超时，防止Hook脚本挂起

TIMEOUT_SECONDS=10

# 用timeout命令包装实际逻辑
run_with_timeout() {
  timeout "$TIMEOUT_SECONDS" bash -c '
    INPUT=$(cat)
    # ... Hook逻辑 ...
    exit 0
  '
  EXIT_CODE=$?

  if [ $EXIT_CODE -eq 124 ]; then
    # 超时，默认允许（避免阻塞Agent）
    echo "Hook执行超时，默认放行"
    exit 0
  fi

  exit $EXIT_CODE
}

cat | run_with_timeout
```

### 7.3 调试Hook

```bash
# 启用Hook调试日志
export CLAUDE_HOOK_DEBUG=1

# 或在Hook脚本中添加调试输出到stderr
echo "[DEBUG] Hook输入: $(cat)" >&2

# 查看Hook执行历史
ls -la .claude/audit/

# 手动测试Hook脚本
echo '{"event":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls -la"}}' | \
  bash .claude/hooks/pre-bash-guard.sh
echo "退出码: $?"
```

## 八、常见问题与解决方案

| 问题 | 原因 | 解决方案 |
|------|------|---------|
| Hook不触发 | Matcher拼写错误 | 检查 `settings.json` 中matcher是否匹配工具名（区分大小写） |
| Hook超时 | 脚本内有交互式命令 | 使用 `timeout` 包装，避免 `read` 等阻塞操作 |
| `-p` 模式卡住 | 权限等待确认 | 添加 `--permission-mode` 或 `--allowedTools` |
| JSON输出无效 | Schema约束不匹配 | 先用 `--output-format text` 确认输出，再切JSON |
| Hook状态丢失 | 使用 `/tmp` 重启后清空 | 改用项目目录 `.claude/state/` 存储状态 |
| `--resume` 失败 | Session ID过期 | Session有TTL，确保在有效期内恢复 |
| CI中API限流 | 并发请求过多 | 使用 `xargs -P N` 控制并发数，建议 N ≤ 4 |

## 九、训练营适用性评估

| 维度 | 评估 |
|------|------|
| **适合训练营类型** | DevOps训练营、AI工程化训练营、团队效率提升工作坊 |
| **教学时长** | 4-6小时（含实操） |
| **难度等级** | 中高级 |
| **前置要求** | Shell脚本基础、Git操作、CI/CD概念 |
| **实操占比** | 70%（大量脚本编写和管道调试） |
| **设备要求** | 每人一台Linux/macOS终端，Claude Code API访问权限 |
| **分组建议** | 2-3人一组，每组一个真实项目仓库 |
| **考核方式** | 构建完整的CI审查管道 + Hooks质量门禁通过率 |

```mermaid
flowchart LR
    subgraph 训练营4小时安排
        A[0:00-0:30<br/>Hooks概念与七类事件] -->
        B[0:30-1:30<br/>编写PreToolUse守卫<br/>PostToolUse格式化] -->
        C[1:30-2:00<br/>-p模式与JSON输出] -->
        D[2:00-3:00<br/>构建CI管道<br/>GitHub Actions集成] -->
        E[3:00-3:30<br/>批量审查与自动修复] -->
        F[3:30-4:00<br/>综合演练<br/>+ 质量门禁验收]
    end

    style A fill:#3498DB,color:#fff
    style D fill:#E67E22,color:#fff
    style F fill:#27AE60,color:#fff
```

## 参考来源

1. [Hooks reference - Claude Code Docs](https://code.claude.com/docs/en/hooks) — 官方Hooks事件参考文档，包含JSON Schema、退出码定义和配置语法
2. [Run Claude Code programmatically - Claude Code Docs](https://code.claude.com/docs/en/headless) — 官方Headless/Print模式文档，-p标志、JSON Schema输出和管道集成
3. [Automate workflows with hooks - Claude Code Docs](https://code.claude.com/docs/en/hooks-guide) — 官方Hooks使用指南，包含实际工作流自动化示例
4. [Claude Code Hooks: Complete Guide with 20+ Examples](https://aiorg.dev/blog/claude-code-hooks) — 20+个即用型Hook配置，覆盖格式化、安全审查、通知等场景
5. [Claude Code Batch Processing Complete Guide](https://smartscope.blog/en/generative-ai/claude/claude-code-batch-processing/) — /batch命令和-p模式批量处理的系统化指南

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
