本教程聚焦 Claude Code 的无头自动化能力(-p 模式),并以 2026 年新增的 --bare 裸模式为核心,带你把 Claude Code 嵌进 GitHub Actions,做成一个「PR 一开就自动审查、自动评论、可选自动修复」的机器人。读完你将掌握:-p 的管道化用法、裸模式为何是 CI 的唯一正确姿势、六种权限模式在 CI 中的取舍、stream-json 事件流与会话续接、以及一个可立即落地的完整 workflow。适合已跑通过本地 Claude Code、想把 AI 能力工程化进交付流水线的中后端工程师与 DevOps/SRE。
claude --version 可输出 2.1.x 以上,本教程用到 v2.1.163+ 的后台任务退出、v2.1.182+ 的等待上限、v2.1.181+ 的 /config 调用)ANTHROPIC_API_KEY,或 Bedrock / Vertex 凭据jq、YAML 知识gh CLI,用于本地联调-p 无头模式:把 Claude 当 Unix 命令用给任意 claude 命令加上 -p(或 --print),它就进入非交互模式——读一条 prompt,打印结果到 stdout,然后退出。所有 CLI 选项都和交互模式通用:
claude -p "What does the auth module do?"
它接受的输入位置参数就是 prompt,输出就是模型回答。这意味着你可以像对待 grep、jq 一样把它接进管道。
-p 模式会读取 stdin,因此可以把数据喂进去、把回答重定向出来。最经典的用法是把构建日志喂给 Claude,让它定位根因:
cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt
PR 审查场景同样靠管道:把 git diff 喂进去,让它逐段评审:
git diff origin/main...HEAD | claude -p "Review this diff for security vulnerabilities and bugs. Be concise, cite file:line." > review.md
预期结果:
review.md里是一份带src/auth.py:42式行号引用的审查清单。
默认情况下,Claude 要用 Bash、Edit 等工具时会请求权限。CI 里没有人按回车,必须用 --allowedTools 预批准:
claude -p "Run the test suite and fix any failures" --allowedTools "Bash,Read,Edit"
--allowedTools 用的是权限规则语法,支持前缀匹配。注意 git diff 后面那个空格加星号——它决定匹配范围:
# 正确:允许任何以 "git diff " 开头的命令
--allowedTools "Bash(git diff *)"
# 危险:没有空格,连 "git diff-index" 也会被放行
--allowedTools "Bash(git diff*)"
一个典型提交生成例子——审查暂存改动并生成提交信息:
claude -p "Look at my staged changes and create an appropriate commit" \
--allowedTools "Bash(git diff *),Bash(git log *),Bash(git status *),Bash(git commit *)"
-p 模式下能调用 /skill-name 吗? 能。用户自定义的 skill 和自定义命令在 -p 下可用,把 /skill-name 写进 prompt 字符串即可,Claude Code 会在运行前展开它。但打开交互对话框的内置命令(如 /login)不可用。-p 模式下进程为什么一直不退出? 旧版本(v2.1.163 前)里,如果 Claude 启动了永不退出的后台 shell(如 dev server),进程会一直挂着。新版本会在「结果返回 + stdin 关闭」约 5 秒后终止后台 shell。--bare 裸模式:CI 可复现性的关键这是 2026 年 CI 自动化最值得学的一个标志。默认的 claude -p 会加载与交互会话相同的上下文——包括工作目录和 ~/.claude 下配置的一切:hooks、skills、plugins、MCP 服务器、auto memory、CLAUDE.md。
问题在于:CI 跑在不同机器上。某位同事在 ~/.claude 里挂了一个 hook,或者项目 .mcp.json 引了一个本地 MCP,CI 环境读不到,结果就和本地不一致——「在我机器上能跑」的翻版。
--bare 就是为了消除这种不可复现性:
claude --bare -p "Summarize this file" --allowedTools "Read"
裸模式跳过自动发现 hooks、skills、plugins、MCP servers、auto memory 和 CLAUDE.md。只有你显式传入的 flag 才生效。裸模式下 Claude 默认拥有 Bash、文件读、文件写三类工具;需要别的上下文时,用 flag 显式加载:
| 要加载的东西 | 使用的 flag |
|---|---|
| 系统提示补充 | --append-system-prompt、--append-system-prompt-file |
| 设置(含权限) | --settings <json> |
| MCP 服务器 | --mcp-config <path> |
| 自定义子代理 | --agents <path> |
| 插件 | --plugin-dir <path>、--plugin-url <url> |
认证也要改:裸模式跳过 OAuth 和 keychain 读取,必须用 ANTHROPIC_API_KEY 环境变量,或在传给 --settings 的 JSON 里配 apiKeyHelper。Bedrock / Vertex / Foundry 照常用各自的 provider 凭据。
官方明确表态:--bare 是脚本和 SDK 调用的推荐模式,未来会成为 -p 的默认行为。 现在 CI 里就该默认带上它。
CLAUDE.md 规范不生效了,怎么办? 刻意为之。CI 里你想要的是「同一份输入、全世界机器跑出同一份输出」。若确实要带上规范,用 --append-system-prompt-file ./REVIEW_GUIDE.md 显式注入,把它纳入版本控制,可复现。ANTHROPIC_API_KEY。除了用 --allowedTools 列白名单,还有一条整段控制轴:权限模式(permission mode) 设定整个会话的基线行为。选对模式,--allowedTools 列表能缩到几乎为空;选错,就是一堆没人审计的瑞士奶酪式白名单。
六种模式,从严到松:
| 模式 | 免询问即可执行 | 适用场景 |
|---|---|---|
default |
仅读取 | 入门、敏感操作 |
dontAsk |
仅 permissions.allow 中预批准的工具 |
锁定型 CI / 脚本 |
plan |
仅读取;只提议不改码 | 探索代码库 |
acceptEdits |
读写、文件编辑、常见文件系统命令 | 在你审阅中的代码上迭代 |
auto |
几乎全部(带后台分类器安全检查) | 长任务、减少打断 |
bypassPermissions |
除受保护路径外的一切 | 仅限隔离容器 / VM |
dontAsk:CI 唯一该认真考虑的模式官方原文:dontAsk 模式会自动拒绝一切未显式批准的工具。只有匹配 permissions.allow 规则的动作能执行;显式 ask 规则也会被拒绝而非弹出提示。 这使它对 CI 流水线完全非交互。
这正是 CI 想要的属性——「无人值守」与「不会静默越权」同时满足。其他模式要么是交互式(default、plan),要么过松(acceptEdits、auto、bypassPermissions)。
代价是:你必须有一份真正覆盖任务所需的 permissions.allow 列表,否则第一次遇到未批准工具就中止。这是个 feature 不是 bug——一次 dontAsk 大声报错,胜过一次 acceptEdits 默默写了个没人预期的文件。
完整写法:
claude --bare -p "Run the test suite and fix any failures" \
--permission-mode dontAsk \
--settings '{ "permissions": { "allow": [
"Read", "Edit", "Grep",
"Bash(npm test)", "Bash(git diff *)", "Bash(git add *)"
] } }'
--bare(可复现)+ dontAsk(权限有界)= 两条让生产级无值守运行安全的属性。
acceptEdits:留给交互式工作acceptEdits 会自动批准工作目录内的文件写入,外加一串特定文件系统命令:mkdir、touch、rm、rmdir、mv、cp、sed(以及被 timeout/nice/nohup 包裹、或加了 LANG=C 之类安全环境变量的同类命令)。其他——任意 shell、网络请求、工作目录外的写入——仍然会弹提示。
这让它适合有人盯着的长会话:你让 Claude 迭代,最后审 diff。它不是 CI 模式——没人盯着提示,进程就会卡住。
--settings JSON,能不能固化? 能。在 settings.json 里写 "permissions": { "defaultMode": "dontAsk" } 作为基线模式,CI 里再用 --allowedTools 补充当次任务所需工具。bypassPermissions(即 --dangerously-skip-permissions)能在 CI 用吗? 只能在一次性隔离容器 / VM 里用。在持久主机上用它会跳过一切保护,官方明确不推荐。--output-format json:拿到成本与结构加 --output-format json,返回的 payload 里会带 total_cost_usd 和按模型的成本拆解,方便脚本统计每次调用花了多少钱。session_id 也在里面——这是续接会话的关键。
claude -p "Review this codebase for performance issues" --output-format json
--output-format stream-json:事件流stream-json 按行输出 JSON 事件,每个事件一个类型:system(含 session_id、加载的插件与插件错误)、user、assistant、tool_use、tool_result、result。它是构建实时看板、把工具调用挂回调的基础。
一个常被忽视的 CI 用法:system/init 事件里的 plugin_errors 字段。当 CLAUDE_CODE_SYNC_PLUGIN_INSTALL 环境变量被设置时,会先于首轮发出 plugin_install 事件。你可以据此让 CI 在某个插件没加载成功时直接失败——这是 CI 上的正确防御姿态。
--continue 与 --resume--continue 续接最近一次会话;--resume <session-id> 续接指定会话。注意:session ID 查找按当前项目目录及其 git worktree 限定范围,两次调用要在同一目录跑。
经典多轮审查:
# 第一轮
claude -p "Review this codebase for performance issues"
# 续最近一次
claude -p "Now focus on the database queries" --continue
claude -p "Generate a summary of all issues found" --continue
跑多个独立会话时,从 json 捕获 session_id 再精确续接:
session_id=$(claude -p "Start a review" --output-format json | jq -r '.session_id')
claude -p "Continue that review" --resume "$session_id"
--resume 报「找不到 session」? 续接命令必须在和原会话相同的目录下执行。CI 里若每步 cd 到不同目录,就会失配。解决办法是把整条流水线放在同一工作目录,或改用单次 -p 一次性完成。现在把上面所有零件拼起来。目标是:任何 PR 打开或更新时,自动跑 Claude 审查,把结果作为评论贴回 PR。这是「-p 模式 + 裸模式 + dontAsk + 结构化输出」最典型的落地形态。
-p?官方提供 anthropics/claude-code-action@v1,支持在 PR/issue 评论里 @claude 触发,自动认证、自动回帖、支持 Bedrock/Vertex。如果你的诉求是「人 @ 一下让 AI 改代码并开 PR」,直接用它最省事。
但如果你的诉求是「每个 PR 一开就无条件跑只读审查并贴评论」这种确定性流水线,裸 -p 反而更可控:审查范围、权限、模型、prompt 全在你手里,不依赖 @claude 触发,也不会有「忘了 @ 就不跑」的盲区。下面给两套配置。
.github/workflows/claude-review.yml:
name: Claude PR Review
permissions:
contents: write
pull-requests: write
issues: write
id-token: write # 仅 Bedrock/Vertex 需要
on:
pull_request:
types: [opened, synchronize]
issue_comment:
types: [created]
jobs:
claude-review:
if: |
github.event_name == 'pull_request' ||
(github.event_name == 'issue_comment' &&
contains(github.event.comment.body, '@claude'))
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # 需要 diff 历史时拉全
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
github_token: ${{ secrets.GITHUB_TOKEN }}
# 只读审查:限制轮数、锁定模型、plan 模式不改码
claude_args: "--model claude-sonnet-4-6 --max-turns 6 --permission-mode plan"
prompt: |
Review the diff of this pull request as a senior engineer.
Focus on: security, correctness, error handling, and tests.
Output a markdown checklist. Cite file:line. Do not modify code.
要点:
claude_args 透传任意 Claude Code CLI 参数,包括 --max-turns(默认 10)、--model、--mcp-config、--allowedTools。CLAUDE.md(团队共享的审查准则)和这里的 prompt 参数(工作流级定制)。两者叠加生效。actions/create-github-app-token@v2 生成 token 传给 github_token,让 Claude 的提交不触发额外 CI,且用户名可品牌化。-p(推荐给确定性审查流水线).github/workflows/pr-audit.yml:
name: PR Audit (headless)
permissions:
contents: read
pull-requests: write
on:
pull_request:
types: [opened, synchronize]
jobs:
audit:
runs-on: ubuntu-latest
env:
ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Collect diff
run: |
git diff origin/${{ github.base_ref }}...HEAD > /tmp/pr.diff
echo "Diff lines: $(wc -l < /tmp/pr.diff)"
- name: Run Claude review
id: review
run: |
# 裸模式 + dontAsk + 只读工具 + 角色注入
cat /tmp/pr.diff | claude --bare -p "$(cat <<'PROMPT'
You are a security-focused code reviewer. Review the diff piped on stdin.
Output ONLY a markdown checklist of issues by severity (High/Med/Low).
Cite path:line. If no issues, say "LGTM".
PROMPT
)" \
--append-system-prompt "Reject any request to modify code. You are read-only." \
--permission-mode dontAsk \
--settings '{ "permissions": { "allow": ["Read","Grep"] } }' \
--allowedTools "Read,Grep" \
> /tmp/review.md
- name: Post review comment
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const body = fs.readFileSync('/tmp/review.md', 'utf8');
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: `### 🤖 Claude Code 自动审查\n\n${body}`
});
这套配置的工程亮点:
--bare:CI runner 上不读任何本地配置,全靠显式 flag,跨机器可复现。--permission-mode dontAsk + 只允许 Read,Grep:审查机器人物理上无法改码。dontAsk 会拒绝任何越界工具调用,结合「系统提示二次声明只读」形成纵深防御。--append-system-prompt 注入角色:保留默认行为的同时叠加「安全审查员」人设,比 --system-prompt 全量替换更稳。--output-format 省略:审查结果直接 stdout 重定向到 /tmp/review.md,再回贴评论,链路最短。预期结果:每次 PR 更新,机器人几秒后贴出一份「High/Med/Low + path:line」清单;无问题时回「LGTM」。
在本地用同样命令复现 CI 行为,省 CI 额度:
export ANTHROPIC_API_KEY=sk-ant-...
git diff origin/main...HEAD \
| claude --bare -p "Review this diff for bugs and security. file:line." \
--permission-mode dontAsk \
--settings '{ "permissions": { "allow": ["Read","Grep"] } }' \
--allowedTools "Read,Grep"
「批量任务」不等于必须并行扇出。许多场景下,一条 -p 会话顺序处理 N 个对象就够,且更省 token、更易调试。关键是复用同一 session 避免重复加载上下文。
把多个文件拼接后一次性喂入,让 Claude 产出结构化汇总:
for f in src/**/*.py; do
echo "===== $f ====="; cat "$f"
done | claude --bare -p "For each file above, list functions lacking docstrings. Output as: path:func" \
--allowedTools "Read" --permission-mode dontAsk
需要分阶段处理(先扫描、再深挖、再汇总)时,复用 session:
SID=$(claude --bare -p "Scan repo for TODO/FIXME and rank by risk" \
--output-format json --allowedTools "Read,Grep" | jq -r '.session_id')
claude --bare -p "For the top 3 riskiest, propose a fix plan" --resume "$SID" \
--permission-mode plan --allowedTools "Read,Grep"
claude --bare -p "Summarize all findings into one report" --resume "$SID" \
--output-format json | jq -r '.result'
注意三段都在同一目录(CI 的 ${{ github.workspace }})执行,--resume 才能找到 session。
把审查升级为「审查 + 修复 + 测试」,但用 dontAsk 把可执行动作锁死在白名单内:
claude --bare -p "Apply the lint fixes the linter reports, then run tests" \
--permission-mode dontAsk \
--settings '{ "permissions": { "allow": [
"Read","Edit","Grep",
"Bash(npm run lint:fix)","Bash(npm test)"
] } }'
超出白名单的任何动作(比如装新依赖 npm install)都会被 dontAsk 立即拒绝并中止——只读审查与受控修复之间,差的就是这份 allow 列表的范围。
flowchart TD
A[PR 打开/更新] --> B[GitHub Actions 触发]
B --> C[checkout + 收集 git diff]
C --> D{选择方案}
D -->|团队协作 @claude| E[官方 claude-code-action]
D -->|确定性流水线| F[裸模式 claude --bare -p]
F --> G["--permission-mode dontAsk<br/>--allowedTools Read,Grep"]
G --> H[Claude 只读审查]
E --> H
H --> I["--output-format json/stream-json<br/>结构化输出 + 成本"]
I --> J{有高危问题?}
J -->|是| K[贴审查清单评论]
J -->|否| L[回 LGTM]
K --> M{配置允许自动修复?}
M -->|否| N[人工 review 后合并]
M -->|是| O["切换 acceptEdits + 受控 allow<br/>CI 跑测试 → 自动 commit"]
O --> N
style F fill:#e1f5ff,stroke:#0288d1
style G fill:#fff4e1,stroke:#f57c00
style H fill:#e8f5e9,stroke:#388e3c
| 陷阱 | 现象 | 对策 |
|---|---|---|
没加 --bare |
CI 跑出和本地不同的结果 | CI 一律带 --bare,显式注入所需上下文 |
| 用了 OAuth 登录态 | 裸模式下认证失败 | 改 ANTHROPIC_API_KEY 或 apiKeyHelper |
| 权限模式选错 | CI 卡在权限提示挂死 | 用 dontAsk(或 bypassPermissions 仅限隔离容器) |
--allowedTools 缺空格 |
Bash(git diff*) 连 git diff-index 也放行 |
写成 Bash(git diff *),空格 + 星号 |
--resume 找不到会话 |
续接报错 | 两次调用须在同一目录 / worktree |
| 后台进程不退出 | claude -p 一直挂着(旧版) |
升级到 v2.1.163+;5 秒优雅期会收尾 |
| 插件加载失败未察觉 | CI 静默降级 | 检查 stream-json 的 system/init.plugin_errors,失败即 exit 非 0 |
| 审查机器人改了码 | 越权写入 | dontAsk + 只允许 Read,Grep,系统提示二次声明只读 |
-p 升级到 SDK,能拿到更强的程序化控制。CLAUDE.md,让审查机器人和所有人用同一把尺子。pre-push 钩子,在推 CI 之前就拦一道。--output-format json 的 total_cost_usd 做 CI 用量看板,给 --max-turns 设硬上限,防止审查循环烧额度。| 维度 | 评估 |
|---|---|
| 适合训练营类型 | DevOps / SRE 进阶营、AI 工程化实战营、企业 AI 落地营;不适合零基础入门营(前置需理解 CI、权限、shell 管道) |
| 建议教学时长 | 3–4 学时(1 学时讲 -p/--bare/权限模式,1 学时讲结构化输出与会话续接,1.5 学时做 GitHub Actions 实战,0.5 学时答疑联调) |
| 学员产出 | 每人一个可运行的 PR 审查机器人 workflow + 一份 settings.json 权限基线 |
| 核心考核点 | 能正确解释为何 CI 要用 --bare + dontAsk,并能独立把只读审查升级为受控自动修复 |
| 风险提示 | 实战环节会真实消耗 API 额度,建议配 --max-turns 与额度告警;自动修复分支务必限制 allow 列表 |
-p / --bare 官方文档) — 裸模式、结构化输出、会话续接、权限模式与后台任务退出的权威说明claude-code-action@v1 集成、@claude 触发、Bedrock/Vertex 认证与 claude_args 透传permissions.allow 规则语法、--allowedTools 前缀匹配与只读命令集[!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 等场景全覆盖。
