前十二课里 Claude Code 都有一个人坐在终端前拍板;本课把人撤掉。你会学到 headless 模式(claude -p)的输入输出契约——prompt 进、文本或 JSON 出、退出码定成败——以及没人按「允许」时权限怎么办,最后用官方 GitHub Action 把它接进 PR 流程。
为什么这天学这个
本课是前面几课的「无人值守版」总装:D3 权限模式里你学过交互式的允许/拒绝,而 CI 里没有人回答提示,答案只能预先写进 --allowedTools 或权限模式;D8 Hooks 里的「机器裁判」思路在 CI 中同样成立——无头运行时,hooks 是少数还能强制拦截动作的机制。本站观点
往后看,D14 Plugins 讲打包分发,而 headless 正是分发后的主要消费场景之一:官方 action 支持在 CI 里装插件、跑插件里的 skill。学完本课再去读实战 Tip F3 · GitHub Actions 集成和 F4 · 定时任务,就是从「懂机制」到「照着抄」的一步。
概念讲清楚
headless:一条命令的输入输出契约
官方给任何 claude 命令加上 -p(即 --print)就进入非交互模式:执行完 prompt、打印结果、退出,所有 CLI flag 都可以和 -p 组合。它读 stdin,所以能像普通 Unix 工具一样接管道(piped stdin 自 v2.1.128 起上限 10MB):
# 问答式:结果打到 stdout
claude -p "What does the auth module do?"
# 管道式:构建日志进,解释出
cat build-error.txt | claude -p 'concisely explain the root cause of this build error' > output.txt
官方成败用退出码表达:成功退出码 0,运行失败非 0,脚本可以直接对退出状态做分支;被 SIGTERM 终止时中止当前轮次、跑完 SessionEnd hooks 后以 143 退出。官方CI 里推荐加 --bare:跳过 hooks、skills、插件、MCP、auto memory 和 CLAUDE.md 的自动发现,保证每台机器结果一致、启动更快;官方说明它将来会成为 -p 的默认行为。注意 bare 模式不读 OAuth 凭据和系统钥匙串,必须通过环境变量 ANTHROPIC_API_KEY(或 settings 里的 apiKeyHelper)鉴权。
三种输出格式:给人看 vs 给程序看
| --output-format | 输出 | 适用 |
|---|---|---|
text(默认) | 纯文本回答 | 人看、简单管道 |
json | 单个 JSON:result、session_id、total_cost_usd 及分模型成本 | 脚本解析、逐次记账 |
stream-json | 逐行 JSON 事件流(配 --verbose --include-partial-messages 可拿到 token 级增量) | 实时进度、自建 UI |
官方要输出符合固定 schema 的结构化结果,用 --output-format json 加 --json-schema,结果落在 structured_output 字段里,配 jq 就能直接取值;schema 非法时报错退出(v2.1.205 起)。官方stream-json 流的第一个事件 system/init 带有 mcp_server_errors、plugin_errors 等字段——CI 可以对「非空错误数组」直接判失败,抓住那种服务器没加载成功但进程仍正常退出的静默故障。
无人值守时,权限怎么办
这是 D3 问题的 CI 解法。交互式会话里权限提示由人回答;headless 里没人在,官方给了三层收口:官方
- 逐条放行:
--allowedTools "Bash(git diff *),Read,Edit",用权限规则语法精确到命令前缀。注意*前那个空格:Bash(git diff *)只放行 git diff 开头的命令,写成git diff*会连git diff-index一起放行。 - 整体基线:
--permission-mode dontAsk拒绝一切不在 allow 规则或只读命令集内的动作,适合锁死的 CI;--permission-mode acceptEdits免提示写文件并自动放行 mkdir、touch、mv、cp 等常见文件系统命令,其余 shell 命令仍需 allow 规则,否则运行中止。 - 止损上限:
--max-turns限制轮次(CLI 默认无上限,达到即报错退出)、--max-budget-usd限制单次美元花费(subagent 的花费也计入,上限强制行为需 v2.1.217+)。
--dangerously-skip-permissions 全放开」——省事但等于把仓库和 runner 的全部权限交给模型输出,一旦 prompt 被 PR 内容注入就没有任何闸门。CI 里更稳的组合是:精确的 allowedTools + dontAsk/acceptEdits 基线 + max-turns/预算上限,再用 D8 的 hooks 做硬拦截。本站观点接进 GitHub Actions
官方官方提供 GitHub Action anthropics/claude-code-action@v1(底层就是 Agent SDK / headless 能力)。它会自动检测运行模式:配置了事件触发但没写 prompt,就响应评论里的 @claude 提及(交互模式);写了 prompt 输入,就直接执行(自动化模式)。任何 CLI 参数都能通过 claude_args 透传,比如 --max-turns(action 场景默认 10)、--model、--allowedTools。
官方鉴权:最快路径是在 Claude Code 终端里跑 /install-github-app(需仓库 admin,GitHub App 要求 Contents/Issues/Pull requests 的读写权限,快捷方式仅限直连 Claude API 的用户);手动路径是装 App、把 API key 存进仓库 secret ANTHROPIC_API_KEY、从官方示例拷 workflow。订阅用户可用 claude setup-token 生成长效 OAuth token 给 CI 使用。企业环境可走 Amazon Bedrock / Google Cloud,用 OIDC 免存静态密钥,action 侧开 use_bedrock 或 use_vertex。永远不要把 key 写进 workflow 文件。
官方成本:CI 里花的是两笔钱——GitHub Actions 分钟数 + API token。官方给的控法:用具体明确的 @claude 指令减少无效调用、--max-turns 防止无限迭代、workflow 级 timeout 防失控任务、GitHub concurrency 控制并发数。配合 --output-format json 里的 total_cost_usd,可以逐次记账。
当天能做完的实操
前三步只需要本机装好 Claude Code,约 15 分钟;第四步需要一个你有 admin 权限的 GitHub 仓库。
- 跑一次最小 headless 调用并看退出码。在任意项目目录执行下面第一条命令,预期看到一个 JSON,里面有
result(回答文本)、session_id、total_cost_usd;紧接着echo $?应输出0。 - 把它当 Unix 工具用。跑第二条管道命令,预期 Claude 只输出 diff 里的拼写问题(没有多余寒暄)——这就是「Claude 作为 linter」的雏形,不给 Bash 权限也能读到 diff,因为内容是从管道进来的。
- 体验权限收口。第三条命令用
acceptEdits基线跑一个会改文件的任务,预期不弹任何确认;然后把--permission-mode acceptEdits去掉重跑,预期任务因为没人批准写文件而无法完成——这就是 CI 里必须预先声明权限的原因。
# 1. 最小 headless 调用 + 退出码
claude -p "用一句话总结这个项目是做什么的" --output-format json
echo $?
# 2. 管道:diff 进,拼写检查出
git diff main | claude -p "你是拼写检查器。对 diff 中每个拼写错误,输出一行 文件:行号,下一行写问题。没有其它输出。"
# 3. 权限收口:先带基线跑,再去掉基线对比
claude -p "把 README.md 里的错别字修掉" --permission-mode acceptEdits --max-turns 5
- 接进 GitHub Actions。在有 admin 权限的仓库目录里进入交互式 Claude Code,运行
/install-github-app,按引导装 App、加 secret、生成 workflow;完成后在任一 issue 或 PR 评论里发@claude 这个仓库的测试覆盖薄弱点在哪?,预期几分钟内 Claude 以评论回复。没有合适仓库的话,今天先把下面这个最小 workflow 读懂,明天再装:
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
claude_args: "--max-turns 10"
验收标准
- 能向别人解释
claude -p的输入输出契约:prompt/stdin 进,text/json/stream-json 出,退出码 0 或非 0 定成败。 - 能说出 json 与 stream-json 各适合什么场景,以及
--json-schema的结果落在哪个字段。 - 实操 1–3 全部跑通,并能解释第 3 步去掉权限基线后行为为什么变了。
- 能说出 CI 鉴权的三条路:
ANTHROPIC_API_KEYsecret、订阅用户的claude setup-token、Bedrock/Vertex 的 OIDC。 - 自测题:CI 里要无人值守地让 Claude 修 lint 并提交,既不想弹任何提示、又不想全放开权限,该怎么配?(要点:
--allowedTools精确放行 git/lint 命令 +acceptEdits或dontAsk基线 +--max-turns/--max-budget-usd止损;而不是--dangerously-skip-permissions。)