Claude Code 与 Cursor 的无头形态对照:接进 CI 时两边的输入输出约定

2026-08-18

把一个 coding agent 接进 CI,难的不是”能不能非交互跑”——两边都给了 -p。难的是脚本外面那一圈约定:进程起来时带不带你本机那套配置、输出是什么形状、失败怎么让流水线知道、跑完会不会有个后台进程把 job 挂住。这四件事只要有一件对不上,你会得到一个绿灯但什么都没做的 job,或者一个永远不结束的 job。

依据只有两家的官方文档:Claude Code 侧是 code.claude.com/docsheadlesscli-referencegithub-actions 三页,Cursor 侧是 cursor.com/docs/cli/headlesscursor.com/docs/cli/reference/output-formatcursor.com/docs/cli/github-actions 三页。两个产品都闭源,凡是一方文档里查不到的维度,直接写「不比」。

一、入口:同样是 -p,分岔在”带不带你的本地上下文”

Claude Code 的非交互入口,官方文档给的是这条:

claude -p "Find and fix the bug in auth.py" --allowedTools "Read,Edit,Bash"

真正影响 CI 可复现性的是 --bare。官方文档写明它会跳过 hooksskillspluginMCP 服务器、auto memory 和 CLAUDE.md 的自动发现,并自述这是脚本与 SDK 调用的推荐模式、未来版本会成为 -p 的默认(这一句是文档自述的计划,不是既成事实)。

反过来说,不加 --bare 的后果文档也写得很直白:-p 会话不会弹 workspace trust 对话框,所以项目 .claude/settings.json 里的 hooks 照跑、.mcp.json 里的服务器照连,哪怕这个目录你从没信任过。在 CI 上拉一个外部仓库跑 agent 时,这条值得先看一眼。

--bare 也有代价:文档写明该模式下不读 OAuth 凭据与系统 keychain,走 Claude API 要在环境里设 ANTHROPIC_API_KEY,而 Amazon Bedrock、Google Cloud’s Agent Platform、Microsoft Foundry 照旧读各自 provider 的凭据。要往回补上下文,文档列了对应的 flag:--append-system-prompt 补系统提示、--settings 给设置、--mcp-config 给 MCP 服务器、--agents 给自定义 agent、--plugin-dir--plugin-urlplugin

claude --bare -p "Summarize README.md" --allowedTools "Read"

Cursor 侧的入口是 agent -p,认证走 CURSOR_API_KEY 环境变量。参数表里有一个 --trust,说明是「Trust the workspace without prompting」,并标注 headless mode only;不过同一套文档的 changelog 里又写明 --trust 现在在交互会话里也能用(此前只能在 headless 下传),两处口径对不上,以官方文档最新内容为准。但「跳过自动发现、让每台机器结果一致」这种等价于 --bare 的开关,我们在 Cursor 文档里没有找到对应说明,这一维不比。

倒是有一条反过来:Cursor 的输出格式页写明 --output-format 只在 --print 或 print 模式被推断时有效,而推断的条件是 stdout 非 TTY 或 stdin 是管道——CI 里即使忘了写 -p,也可能已经进了 print 模式。Claude Code 侧没有找到”自动进入 print 模式”的说明,它的 -p 得显式给。

二、输出:三档同名,字段落点不同

两边的 --output-format 都是 textjsonstream-json 三档,默认都是 text。同名不同义的地方在字段。

Cursor 的 json 是一个 JSON 对象加一个换行,deltas 与 tool 事件都不发,文本聚合进最终结果。文档给出的成功响应字段是:

字段含义
type终结结果恒为 "result"
subtype成功恒为 "success"
is_error成功恒为 false
duration_ms总执行耗时(毫秒)
duration_api_msAPI 请求耗时,文档注明当前与 duration_ms 相等
result完整助手响应文本
session_id会话标识
request_id可选,可能缺省

引这张表是因为它决定了 jq 表达式怎么写——Cursor 的官方脚本示例取的就是 jq -r '.result'

Claude Code 的 json 同样把文本结果放在 result 字段,另带 session ID 与 metadata。它多一层:要让输出符合指定结构,用 --output-format json--json-schema,结构化结果落在 structured_output 字段(注意不是 result):

claude -p "Extract the main function names from auth.py" \
  --output-format json \
  --json-schema '{"type":"object","properties":{"functions":{"type":"array","items":{"type":"string"}}},"required":["functions"]}'

文档还写明了 schema 非法时的行为:报 Error: --json-schema is not a valid JSON Schema 并附校验器诊断;format 关键字被接受但只当注解、不做强制校验。而在 v2.1.205 之前,非法 schema 会被静默忽略并退回非结构化文本——CI 镜像里锁着更老版本时,“schema 到底生效没有”会很难查。

流式那一档两边都要额外开关。Claude Code 的 stream-json 需要配 --verbose,要 token 级增量还得再加 --include-partial-messages,流的最后一行是 result 消息。Cursor 的 stream-json 是每条完整 assistant 消息一行(工具调用之间的那一整段),要字符级增量得加 --stream-partial-output

这里有个细读文档才会知道的坑,两边形态不同但性质一样:流里有重复文本,你得学会跳过。Cursor 文档写明开了 --stream-partial-output 之后 assistant 事件有三种,只有一种带新文本,靠 timestamp_msmodel_call_id 在不在来区分——timestamp_ms 在而 model_call_id 不在的是真增量,model_call_id 也在的是工具调用前的缓冲刷新(重复),两个都不在的是回合末尾的最终刷新(也是重复)。Claude Code 侧对应的字段是 parent_tool_use_idsubagent 的消息带上派生它的那次工具调用 ID,主对话的消息该字段为 null;默认只发 subagenttool_usetool_result 块,要拿到文本与思考块得加 --forward-subagent-text,文档写明需要 v2.1.211 或更新版本。

还有一条只在 Cursor 文档里查到的:print 模式下 thinking 事件被抑制,任何输出格式里都不会出现。Claude Code 侧没有找到同样口径的表述,不比。

三、失败:先决定用什么判成败

Cursor 文档写得很干脆:失败时进程以非零码退出、错误写 stderr,不产出良构 JSON 对象stream-json 失败时流可能提前结束、没有终结事件。所以在 Cursor 侧,“stdout 里没解析出 JSON”和”运行失败”基本是一回事。

Claude Code 的口径不一样,而且分两种。文档写明成功退出码为 0、失败为非零;但传了非法 flag 时,错误在这次运行开始前就报到 stderr;而运行过程中发生的失败(文档举的例子是缺认证),会把这个失败作为结果打到 stdout。也就是说你可能拿到一个结构完好的输出,内容却是一条失败信息。

两处放一起,CI 脚本的结论是同一个:别用”stdout 有没有 JSON”判成败,用退出码。Cursor 官方的代码审查脚本示例就是这么写的:

if [ $? -eq 0 ]; then
  echo "✅ Code review completed successfully"
else
  echo "❌ Code review failed"
  exit 1
fi

退出码的语义上,Claude Code 文档多给了一个具体值:用 SIGTERM 停止一次 claude -p 运行时(kill、进程守护、或 SDK 宿主关闭会话),它会中止进行中的回合、终止正在跑的 Bash 命令的整棵进程树、执行 SessionEnd hooks,然后以退出码 143 退出。这让”CI 超时被杀”和”业务失败”在脚本里可以分开处理。Cursor 侧只写了”非零”,没有给具体值,这一维不比。

四、跑完会不会挂住

这是 CI 里最烦人的一类故障,而两边文档的覆盖程度差别很大。

Claude Code 文档专门有一节讲退出时的后台任务:如果 Claude 在 claude -p 期间起了后台 Bash 任务(文档举例是 dev server 或 watch 构建),该 shell 会在最终结果返回、stdin 关闭之后的一小段宽限期内被终止;而后台 subagent 与 workflow 不受这个宽限约束,因为它们的结果属于最终输出的一部分,claude -p 会等它们完成。从 v2.1.182 起这个等待默认带上限,可以用 CLAUDE_CODE_PRINT_BG_WAIT_CEILING_MS 调整,设成 0 表示不设上限。文档还提到在 v2.1.163 之前,一个永不退出的后台进程会把 claude -p 无限期挂在那里。

Cursor 侧关于”非交互运行结束时后台进程怎么处理”,我们在文档里没有找到对应说明,这一维不比。实践上就是:提示词有可能让它起长活进程时,兜底得写在 CI 步骤里(这是通用做法,非该产品官方文档内容)。

五、写文件的默认姿态,以及一处读起来不一致的口径

Cursor 的 headless 页写明:--print 要配 --force(或其别名 --yolo)才能在脚本里改文件,并在示例注释里写了不加 --force 时改动只是被提议、不会应用:

# Enable file modifications in print mode
agent -p --force "Refactor this code to use modern ES6+ syntax"

# Without --force, changes are only proposed, not applied
agent -p "Add JSDoc comments to this file"  # Won't modify files

但同一套文档另外两处措辞不同:cursor.com/docs/cli/using 那页在”Non-interactive mode”一节里写「Cursor has full write access in non-interactive mode」,参数表里 -p, --print 的说明也写着可以访问包括写入与 shell 在内的全部工具;权限页则写 print 模式可以用写入与 shell 工具,由 permissions.allowpermissions.deny--force 共同控制哪些不提示直接跑。这三处放在一起,“不加 --force 到底会不会落盘”读起来是不一致的。指出来就停——不替官方解释原因,也不猜实现。实践上的处理很简单:别把”我没加 --force”当成护栏,要拦就用 permissions.deny 写死。

Claude Code 侧对应的旋钮是两组。一组是 --allowedTools,用权限规则语法,文档特别强调了尾部空格加星号的前缀匹配:Bash(git diff *) 允许任何以 git diff 开头的命令,而 * 前那个空格很重要——没有它,Bash(git diff*) 会连 git diff-index 一起放行。另一组是 --permission-modeacceptEdits 让 Claude 写文件不提示,并自动批准 mkdirtouchmvcp 这类常见文件系统命令,但除只读命令集外的其它 shell 命令与网络请求仍需要 allow 规则;dontAsk 拒绝任何不在 permissions.allow 规则或只读命令集内的东西,文档写明这适合锁死的 CI 运行。另有一条例外:AskUserQuestion、被组织设为 ask 的 connector 工具、标了 requiresUserInteraction 的 MCP 工具,即使 allow 规则命中也会被拒。

以上命令均按官方文档中的参数语义组合,未经实测,以官方文档与 --help 的实际输出为准。

六、Windows 侧

Claude Code 文档在把 agent 包成 npm script 的例子里明确说了转义双引号是为了让脚本对 Windows 保持可移植:

{
  "scripts": {
    "lint:claude": "git diff main | claude -p \"you are a typo linter. for each typo in this diff, report filename:line on one line and the issue on the next. return nothing else.\""
  }
}

同一页还有一条 Windows 专属的历史问题:文档写明在 v2.1.211 之前,Windows 上不可读的 stdin 会让会话崩溃、或者静默退出且没有任何输出;该版本之后的行为是打一条警告到 stderr,然后继续用命令行上的提示词跑。Windows runner 上锁着更老版本时,管道输入这条路先别走。管道本身还有体积上限,超过会明确报错并以非零码退出,文档建议改成把内容写进文件、在提示词里引用文件路径。

Cursor 的安装脚本在文档里就是分开的:macOS、Linux、WSL 用 curl https://cursor.com/install -fsS | bash,Windows 用 PowerShell 的 irm 'https://cursor.com/install?win32=true' | iex;GitHub Actions 页也重复写明 Windows runner 用 PowerShell 那条。

七、落到流水线里的形态

Claude Code 有官方的 GitHub Action,anthropics/claude-code-action@v1。运行模式由 workflow 里有没有 prompt 输入决定:没有就是 interactive,等 @claude 触发短语;有就是 automation,直接跑,结果默认落在 workflow 运行日志里。CLI 参数通过 claude_args 透传,文档点名的常用项是 --max-turns--model--mcp-config--allowedTools--debug。认证用 ANTHROPIC_API_KEYCLAUDE_CODE_OAUTH_TOKEN 两个 secret 之一。触发侧还有两道对触发者的检查:写权限检查(schedule 这类无人发起的事件跳过),以及拒绝 bot actor 除非列进 allowed_bots。如果 workflow 还引用着 anthropics/claude-code-action@beta,文档给了迁移路径:@beta@v1、移除 mode 输入、direct_prompt 改名 promptmax_turnsmodel 这类选项挪进 claude_args

Cursor 文档给的是在 workflow 里装 CLI 之后直接调命令:

- name: Install Cursor CLI
  run: |
    curl https://cursor.com/install -fsS | bash
    echo "$HOME/.cursor/bin" >> $GITHUB_PATH

- name: Run Cursor Agent
  env:
    CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
  run: |
    agent -p "Your prompt here" --model gpt-5

(上面这段里的模型名是官方文档当时写的示例值,平台上可用的模型随时在变,别当清单用。)同一页还给了 CLI 级限制的 permissions 配置示例:用 Shell(...)Read(...)Write(...) 这类 token 分别写进 allowdeny,把 git 与 gh 这类产生外部副作用的操作放进 deny,交由后续确定性的 workflow 步骤完成。文档自述推荐生产 CI 用这种”受限自治”的写法。

决策路径

倒着从处境推回来:

  • 要每台机器结果一致:Claude Code 有 --bare,且文档说清了不加它会带进哪些东西;Cursor 侧没找到等价说明,只能自己控制工作目录里的配置。
  • 要结构化结果:两边都用 --output-format jsonresult;只有 Claude Code 文档写了用 --json-schema 约束结构,结果落在 structured_output
  • 要实时进度:两边都用 stream-json 加各自的额外开关,且都要写重复文本的过滤规则。
  • 要让 CI 判对红绿:一律看退出码。Claude Code 额外给了 SIGTERM 的 143,可以区分被杀与失败。
  • 怕 job 挂住:只有 Claude Code 文档给了后台任务在退出时的处理约定和可调上限。
  • 怕它乱改文件:Claude Code 用 --allowedTools 规则或 --permission-mode dontAsk;Cursor 用 permissions.deny 写死,而不是指望”没加 --force”。

两边都在快速迭代,上面每一条都以各自官方文档的最新内容为准。


本文依据 Claude Code 官方文档(code.claude.com/docs)于 2026-08-17 的公开内容整理。 该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。 该产品迭代频繁,文中涉及的命令、配置项与默认值随版本变动,请以官方文档最新内容为准。 本文不涉及价格、额度与限流的具体数值,相关信息请以官方定价与用量说明页为准。

本文涉及的另一方内容依据其官方文档整理(Cursor:cursor.com/docs)。 双方均为闭源商业产品,本文只对照各方公开写明的机制,不推断实现,也不对产品做优劣排名

安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。

想系统学会用 AI?报名体系课或加入会员,照着学、照着用。