当你需要将 Claude Code 集成到 CI/CD 流水线中,或者需要一次性审查数十个 Pull Request 时,交互式的 TUI 模式就不再适用了。`--bare` 模式、管道输入和 `stream-json` 流式输出功能让 Claude Code 可以像命令行工具一样被脚本化调用,完美适应自动化场景。

Claude Code 批量 PR Review 与 CI 集成 — --bare 模式、管道输入与 stream-json 流式输出

简介

当你需要将 Claude Code 集成到 CI/CD 流水线中,或者需要一次性审查数十个 Pull Request 时,交互式的 TUI 模式就不再适用了。--bare 模式、管道输入和 stream-json 流式输出功能让 Claude Code 可以像命令行工具一样被脚本化调用,完美适应自动化场景。

想象一下这样的场景:你的团队每天有 50 个 PR 需要审查,手动审查不仅耗时,而且容易遗漏问题。通过 Claude Code 的批量处理能力,你可以在 CI 流水线中自动触发代码审查,生成结构化的审查报告,并将结果推送到 PR 评论中——整个过程无需人工干预。

本文将深入讲解三个核心功能:--bare 模式的无头运行方式、管道输入的处理机制、stream-json 的结构化输出格式,以及如何将它们组合起来构建完整的自动化 PR 审查流水线。

目录

一、bare 模式详解

1.1 什么是 bare 模式?

bare 模式(无头模式)让 Claude Code 在没有交互式终端的情况下运行。它接受输入、处理请求、输出结果,然后自动退出——完全适合脚本调用和 CI 环境。

bash
# 交互式模式(需要终端)
claude
# TUI 界面,需要键盘交互

# bare 模式(无头运行)
claude --bare "审查 src/auth/ 目录下的所有文件"
# 处理完成后自动退出,结果输出到 stdout

1.2 基本用法

bash
# 单个任务
claude --bare "请审查这个 PR 的代码质量"

# 指定工作目录
cd /path/to/project
claude --bare "运行所有测试并修复失败的用例"

# 结合工作树和 PR
claude --bare --from-pr 42 "进行完整的代码审查并生成报告"

1.3 bare 模式 vs 交互式模式

特性 交互式模式 (TUI) bare 模式
终端要求 需要交互式终端 不需要
用户交互 支持键盘输入 不支持
输出格式 彩色文本 + TUI 纯文本 / JSON
退出方式 手动 /exit 任务完成自动退出
适用场景 日常开发 CI/CD、脚本、批处理
超时控制 无限制 可配置超时

1.4 退出码

bash
claude --bare "some task"
echo $?

# 退出码含义:
# 0 - 成功完成
# 1 - 通用错误
# 2 - 超时
# 3 - 权限被拒绝
# 4 - API 错误

1.5 超时控制

bash
# 设置最大运行时间(秒)
claude --bare --timeout 300 "审查代码"

# 设置最大轮次
claude --bare --max-turns 20 "修复所有 lint 错误"

二、管道输入机制

2.1 基本管道

Claude Code 支持从标准输入接收指令:

bash
# 通过管道传递指令
echo "审查 src/ 目录下的所有变更" | claude --bare

# 从文件读取指令
cat review-instructions.txt | claude --bare

# 使用 heredoc
claude --bare << 'EOF'
请对以下文件进行安全审查:
1. src/auth/login.py
2. src/auth/session.py
3. src/auth/middleware.py

重点关注:
- SQL 注入风险
- 会话管理安全
- 密码处理
EOF

2.2 批量管道输入

bash
# 批量审查多个目录
for dir in src/auth src/api src/utils; do
    echo "审查 $dir 目录" | claude --bare
done

# 从 PR 列表批量审查
cat pr-list.txt | while read pr_number; do
    echo "审查 PR #$pr_number" | claude --bare --from-pr "$pr_number"
done

2.3 JSON 管道输入

对于复杂的任务,可以使用 JSON 格式传递结构化指令:

bash
cat << 'EOF' | claude --bare --input-format json
{
    "task": "code_review",
    "target": "src/auth/",
    "focus_areas": ["security", "performance", "testing"],
    "output_format": "structured",
    "severity_threshold": "medium"
}
EOF

三、stream-json 流式输出

3.1 基本用法

--output-format stream-json 让 Claude Code 以 JSON Lines 格式输出结果,每一行是一个完整的 JSON 对象:

bash
claude --bare --output-format stream-json "审查代码"

3.2 输出格式示例

json
{"type": "status", "message": "Starting code review..."}
{"type": "tool_use", "tool": "Read", "target": "src/auth/login.py"}
{"type": "tool_result", "tool": "Read", "status": "success"}
{"type": "tool_use", "tool": "Grep", "target": "src/auth/", "pattern": "password"}
{"type": "tool_result", "tool": "Grep", "status": "success", "matches": 12}
{"type": "finding", "severity": "high", "file": "src/auth/login.py", "line": 45, "message": "Hardcoded password detected"}
{"type": "finding", "severity": "medium", "file": "src/auth/session.py", "line": 78, "message": "Missing CSRF token validation"}
{"type": "summary", "total_findings": 5, "high": 1, "medium": 2, "low": 2, "status": "completed"}

3.3 处理 JSON 输出

bash
# 使用 jq 提取所有发现
claude --bare --output-format stream-json "审查代码" \
  | jq -r 'select(.type == "finding") | "\(.severity): \(.file):\(.line) - \(.message)"'

# 输出:
# high: src/auth/login.py:45 - Hardcoded password detected
# medium: src/auth/session.py:78 - Missing CSRF token validation
bash
# 统计各严重程度数量
claude --bare --output-format stream-json "审查代码" \
  | jq -s '[.[] | select(.type == "finding")] | group_by(.severity) | map({severity: .[0].severity, count: length})'

# 输出:
# [{"severity": "high", "count": 1}, {"severity": "medium", "count": 2}, {"severity": "low", "count": 2}]
bash
# 提取摘要信息
claude --bare --output-format stream-json "审查代码" \
  | jq -r 'select(.type == "summary") | "Total: \(.total_findings) findings (\(.high) high, \(.medium) medium, \(.low) low)"'

# 输出:
# Total: 5 findings (1 high, 2 medium, 2 low)

四、批量 PR 审查脚本

4.1 基础批量审查脚本

bash
#!/bin/bash
# batch-review.sh - 批量审查 PR 列表

set -e

REPO="owner/my-project"
SEVERITY_THRESHOLD="medium"

# 获取所有开放的 PR
pr_numbers=$(gh pr list --repo "$REPO" --state open --json number --jq '.[].number')

echo "📋 找到 $(echo "$pr_numbers" | wc -l) 个待审查的 PR"

results_dir="review-results/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$results_dir"

for pr_num in $pr_numbers; do
    echo "🔍 正在审查 PR #$pr_num..."

    output_file="$results_dir/pr-${pr_num}.json"

    claude --bare \
        --from-pr "$pr_num" \
        --output-format stream-json \
        --timeout 300 \
        "进行全面代码审查,重点关注安全性和代码质量" \
        > "$output_file" 2>&1

    # 提取审查结果
    total=$(jq -s '[.[] | select(.type == "summary")] | .[0].total_findings // 0' "$output_file")
    high=$(jq -s '[.[] | select(.type == "summary")] | .[0].high // 0' "$output_file")

    echo "  ✅ PR #$pr_num 完成: $total 个发现, $high 个高危"

    # 如果有高危问题,发送通知
    if [ "$high" -gt 0 ]; then
        echo "  ⚠️  PR #$pr_num$high 个高危问题,需要关注"
        # 可以添加 webhook 通知等
    fi
done

echo ""
echo "📊 批量审查完成!"
echo "   结果保存在: $results_dir/"

4.2 带并行处理的批量审查

bash
#!/bin/bash
# parallel-review.sh - 并行批量审查

set -e

REPO="owner/my-project"
MAX_PARALLEL=3
results_dir="review-results/$(date +%Y%m%d_%H%M%S)"
mkdir -p "$results_dir"

# 获取 PR 列表
pr_numbers=$(gh pr list --repo "$REPO" --state open --json number --jq '.[].number')

# 并行处理函数
review_pr() {
    local pr_num=$1
    local output_file="$results_dir/pr-${pr_num}.json"

    echo "[PR #$pr_num] 开始审查..."

    claude --bare \
        --from-pr "$pr_num" \
        --output-format stream-json \
        --timeout 300 \
        "进行全面代码审查" \
        > "$output_file" 2>&1

    echo "[PR #$pr_num] 审查完成"
}

# 使用 xargs 并行执行
echo "$pr_numbers" | xargs -P $MAX_PARALLEL -I {} bash -c 'review_pr "$@"' _ {}

echo "📊 所有 PR 审查完成!"

五、CI/CD 集成

5.1 在 CI 中使用 bare 模式

yaml
# .gitlab-ci.yml
stages:
  - review

claude-code-review:
  stage: review
  image: node:20-alpine
  rules:
    - if: $CI_PIPELINE_SOURCE == "merge_request_event"
  script:
    - npm install -g @anthropic-ai/claude-code
    - export ANTHROPIC_API_KEY=$CLAUDE_API_KEY
    - claude --bare --from-pr $CI_MERGE_REQUEST_IID --output-format stream-json "审查此 MR 的代码质量" > review-result.json
    - cat review-result.json | jq -r 'select(.type == "summary")' > review-summary.json
    - if [ "$(jq '.high // 0' review-summary.json)" -gt 0 ]; then
        echo "发现高危问题,需要人工审查";
        exit 1;
      fi
  artifacts:
    reports:
      - review-summary.json

5.2 Jenkins Pipeline

groovy
pipeline {
    agent any

    stages {
        stage('Claude Code Review') {
            steps {
                sh '''
                    claude --bare \\
                        --from-pr ${CHANGE_ID} \\
                        --output-format stream-json \\
                        --timeout 600 \\
                        "全面代码审查" > review-output.json

                    # 检查高危问题
                    HIGH_COUNT=$(cat review-output.json | jq -s '[.[] | select(.type == "summary")] | .[0].high // 0')
                    if [ "$HIGH_COUNT" -gt 0 ]; then
                        echo "发现 $HIGH_COUNT 个高危问题"
                        # 将结果添加到 PR 评论
                        gh pr comment ${CHANGE_ID} --body-file review-output.json
                        exit 1
                    fi
                '''
            }
        }
    }
}

六、GitHub Actions 实战

6.1 完整的 GitHub Actions 工作流

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

on:
  pull_request:
    types: [opened, synchronize, reopened]
  schedule:
    # 每天早上 9 点批量审查所有开放 PR
    - cron: '0 9 * * *'

permissions:
  contents: read
  pull-requests: write

jobs:
  review:
    runs-on: ubuntu-latest
    timeout-minutes: 30

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

      - name: Setup Claude Code
        run: |
          npm install -g @anthropic-ai/claude-code

      - name: Claude Code Review
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          claude --bare \
            --from-pr ${{ github.event.pull_request.number }} \
            --output-format stream-json \
            --timeout 600 \
            "进行全面代码审查,输出结构化报告" > review-output.json

      - name: Parse Review Results
        id: parse
        run: |
          SUMMARY=$(cat review-output.json | jq -s '[.[] | select(.type == "summary")] | .[0]')
          echo "total=$(echo $SUMMARY | jq '.total_findings // 0')" >> $GITHUB_OUTPUT
          echo "high=$(echo $SUMMARY | jq '.high // 0')" >> $GITHUB_OUTPUT
          echo "medium=$(echo $SUMMARY | jq '.medium // 0')" >> $GITHUB_OUTPUT
          echo "low=$(echo $SUMMARY | jq '.low // 0')" >> $GITHUB_OUTPUT

      - name: Post Review Comment
        if: always()
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          COMMENT="## 🔍 Claude Code 审查报告\n\n"
          COMMENT+="| 严重程度 | 数量 |\n"
          COMMENT+="|---------|------|\n"
          COMMENT+="| 🔴 高危 | ${{ steps.parse.outputs.high }} |\n"
          COMMENT+="| 🟡 中危 | ${{ steps.parse.outputs.medium }} |\n"
          COMMENT+="| 🟢 低危 | ${{ steps.parse.outputs.low }} |\n"
          COMMENT+="| **总计** | **${{ steps.parse.outputs.total }}** |\n\n"

          # 添加详细发现
          COMMENT+="### 详细发现\n\n"
          FINDINGS=$(cat review-output.json | jq -r 'select(.type == "finding") | "- \(.severity): \(.file):\(.line) - \(.message)"')
          COMMENT+="$FINDINGS"

          gh pr comment ${{ github.event.pull_request.number }} --body "$COMMENT"

      - name: Fail on High Severity Issues
        if: steps.parse.outputs.high > '0'
        run: |
          echo "::error::发现 ${{ steps.parse.outputs.high }} 个高危问题,需要人工审查"
          exit 1

6.2 定时批量审查工作流

yaml
# .github/workflows/batch-review.yml
name: Batch PR Review

on:
  schedule:
    - cron: '0 9 * * 1-5'  # 工作日每天 9 点

jobs:
  batch-review:
    runs-on: ubuntu-latest
    timeout-minutes: 120

    steps:
      - uses: actions/checkout@v4

      - name: Setup Claude Code
        run: npm install -g @anthropic-ai/claude-code

      - name: Get Open PRs
        id: prs
        run: |
          PRS=$(gh pr list --state open --json number,title --jq '.[].number')
          echo "numbers=$PRS" >> $GITHUB_OUTPUT

      - name: Review Each PR
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          for pr in ${{ steps.prs.outputs.numbers }}; do
            echo "Reviewing PR #$pr"
            claude --bare \
              --from-pr "$pr" \
              --output-format stream-json \
              --timeout 300 \
              "全面代码审查" > "review-pr-$pr.json" 2>&1 || true
          done

      - name: Aggregate Results
        run: |
          echo "## 📊 批量审查报告" > summary.md
          echo "" >> summary.md
          for f in review-pr-*.json; do
            pr=$(echo $f | sed 's/review-pr-//;s/.json//')
            summary=$(cat "$f" | jq -s '[.[] | select(.type == "summary")] | .[0]')
            total=$(echo $summary | jq '.total_findings // 0')
            high=$(echo $summary | jq '.high // 0')
            echo "- PR #$pr: $total 个发现 ($high 高危)" >> summary.md
          done

      - name: Create Summary Issue
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        run: |
          gh issue create \
            --title "Claude Code 批量审查报告 - $(date +%Y-%m-%d)" \
            --body-file summary.md \
            --label "automated-review"

七、性能优化

7.1 并发控制

bash
#!/bin/bash
# 控制并发审查数量,避免 API 限流

MAX_CONCURRENT=5
SEMAPHORE_FILE="/tmp/claude-review-semaphore"

# 初始化信号量
mkdir -p "$SEMAPHORE_FILE"

acquire_semaphore() {
    while [ "$(ls "$SEMAPHORE_FILE" | wc -l)" -ge "$MAX_CONCURRENT" ]; do
        sleep 5
    done
    touch "$SEMAPHORE_FILE/$$"
}

release_semaphore() {
    rm -f "$SEMAPHORE_FILE/$$"
}

# 审查函数
review_with_semaphore() {
    local pr_num=$1
    acquire_semaphore
    claude --bare --from-pr "$pr_num" --output-format stream-json "审查" > "result-$pr_num.json"
    release_semaphore
}

# 启动并行审查
for pr in $PR_LIST; do
    review_with_semaphore "$pr" &
done

wait
rm -rf "$SEMAPHORE_FILE"

7.2 缓存策略

bash
#!/bin/bash
# 缓存已审查的 PR,避免重复审查

CACHE_DIR=".claude/review-cache"
mkdir -p "$CACHE_DIR"

should_review() {
    local pr_num=$1
    local cache_file="$CACHE_DIR/pr-${pr_num}.json"

    if [ -f "$cache_file" ]; then
        # 检查 PR 是否有新提交
        last_reviewed=$(jq -r '.reviewed_at' "$cache_file")
        last_commit=$(git log -1 --format=%ct "origin/pr/$pr_num")

        if [ "$last_commit" -le "$last_reviewed" ]; then
            echo "PR #$pr_num 自上次审查后无变更,跳过"
            return 1
        fi
    fi
    return 0
}

for pr in $PR_LIST; do
    if should_review "$pr"; then
        claude --bare --from-pr "$pr" --output-format stream-json "审查" > "result-$pr.json"
        # 缓存结果
        jq --arg ts "$(date +%s)" '.reviewed_at = $ts' "result-$pr.json" > "$CACHE_DIR/pr-${pr_num}.json"
    fi
done

7.3 成本预估

bash
#!/bin/bash
# 批量审查成本预估

estimate_cost() {
    local pr_count=$1
    local avg_lines_per_pr=200
    local tokens_per_line=50
    local total_tokens=$((pr_count * avg_lines_per_pr * tokens_per_line))
    local cost_per_million=3.0  # Sonnet 4 价格

    local cost=$(echo "scale=2; $total_tokens / 1000000 * $cost_per_million" | bc)
    echo "预估 $pr_count 个 PR 审查成本: \$$cost (约 $total_tokens tokens)"
}

estimate_cost 50
# 输出: 预估 50 个 PR 审查成本: $15.00 (约 5000000 tokens)

八、总结

--bare 模式、管道输入和 stream-json 输出让 Claude Code 从交互式编程助手转变为可编程的自动化工具。通过合理组合这三个功能,你可以构建出高效的批量 PR 审查流水线,将代码质量检查无缝集成到 CI/CD 流程中。

关键要点:

  • --bare 模式让 Claude Code 无头运行,适合脚本和 CI 调用
  • 管道输入支持从文件、stdin 批量传递审查指令
  • stream-json 提供结构化输出,可用 jq 等工具解析
  • 批量审查脚本可结合并行处理提高效率
  • GitHub Actions 工作流可实现自动 PR 审查
  • 缓存和信号量机制优化成本和并发
  • 合理的超时和退出码处理保证流水线稳定性

九、下篇预告

Claude Code 成本优化深度指南 — 掌握 max-turnsmax-budget-usdfallback-model 等成本控制策略,学习 Token 监控、模型降级、提示优化等实战技巧,让你的 AI 编程助手既强大又经济。