Cursor CLI 的无头模式:接进脚本要先把输出格式定死

2026-08-18

把一个 Agent 命令行塞进脚本,最容易翻车的地方不是它会不会写代码,而是你 grep 不到你要的那一行。交互式跑的时候屏幕上花花绿绿一堆进度,你以为脚本里也能拿到同样的东西,结果管道那头收到的是一坨没法解析的文本;等你换成 JSON,又发现失败的那次根本没吐 JSON,jq 直接报错,退出码还被你自己的 | 吃掉了。

所以顺序应该反过来:先把输出格式定死,再去写提示词。下面按 Cursor 官方文档《Using Headless CLI》(cursor.com/docs/cli/headless)和《Output format》(cursor.com/docs/cli/reference/output-format)两页,把无头模式的参数和输出结构过一遍。

一、无头模式到底是哪个开关

官方文档里「headless」不是一个独立命令,就是 print mode。《Parameters》页(cursor.com/docs/cli/reference/parameters)把 -p, --print 列在全局选项里,描述是「Print responses to console (for scripts or non-interactive use). Has access to all tools, including write and shell.」——注意后半句,print mode 下它能用的工具里包含写文件和 shell。

跟它配套的另外两个选项也在同一张表里:

选项官方文档描述(摘录)
-p, --printPrint responses to console (for scripts or non-interactive use)
--output-format <format>Output format (only works with --print):textjsonstream-json,默认 text
--stream-partial-outputStream partial output as individual text deltas(只在 --print 且格式为 stream-json 时生效)

《Output format》页补了一句很重要的话:--output-format 这个选项「is only valid when printing (--print) or when print mode is inferred (non-TTY stdout or piped stdin)」。也就是说,print mode 有可能被自动推断出来——stdout 不是 TTY,或者 stdin 是管道进来的,都算。这条在 CI 里是好事,在本地调试时容易让你分不清当前到底走的哪条路,建议脚本里始终把 -p 显式写上,别指望推断。

二、前置条件

这一段别跳过,缺一样后面全是白忙。

装。 官方文档《Installation》给的是两条命令,macOS、Linux 和 Windows(WSL) 用一条,Windows 原生用另一条:

curl https://cursor.com/install -fsS | bash
irm 'https://cursor.com/install?win32=true' | iex

PATH。 这里有个文档内部对不齐的地方,值得先记一笔:《Installation》页的安装后步骤让你把 ~/.local/bin 加进 PATH,而《GitHub Actions》页(cursor.com/docs/cli/github-actions)的示例写的是 echo "$HOME/.cursor/bin" >> $GITHUB_PATH。两页写的是两个目录,文档里没有解释这个差异。遇到 command not found 时两个都查一下,别只认一个。

鉴权。 《Authentication》页写明两种方式:浏览器登录 agent login,以及 API key。脚本和 CI 场景官方推荐的是环境变量:

export CURSOR_API_KEY=<YOUR_API_KEY>
agent -p "Analyze this code"

Windows 原生 PowerShell 里怎么设这个环境变量,官方文档没有给出对应写法(GitHub Actions 页只说 Windows runner 用那条 PowerShell 安装命令)。用 PowerShell 自身的环境变量语法是通用做法,不是 Cursor 官方文档内容,请按你自己的 CI 规范来,并注意别把 key 落进日志。

工作区信任。 《Parameters》页把 --trust 描述为「Trust the workspace without prompting (headless mode only)」。但 CLI 更新日志(cursor.com/docs/cli/changelog)2026 年 7 月 20 日那条写的是「Previously --trust required headless mode. Passing it in an interactive session now trusts the workspace up front」。两处口径不一致,文档里没有说明哪一处是最新的,遇到时两边都要按自己的版本试。更新日志里另有一条讲无头信任强制:非交互式运行在未受信任的工作区会带指引失败,除非传了 --trust(或 --force)。CI 里第一次跑新仓库卡住,多半就是这里。

团队策略。 更新日志里有一条「Admins can disable headless mode. A team setting blocks non-interactive CLI usage org-wide.」——如果你在公司账号下跑不通,先问管理员,别怀疑脚本。

三、三种格式分别输出什么

text:只要最后那句话

默认值。《Output format》页写明它「provides only the final assistant message without any intermediate progress updates or tool call summaries」,而且只输出最后一次工具调用之后的那条最终消息。脚本只想拿一个答案时用它:

#!/bin/bash
# Simple codebase question - uses text format by default

agent -p "What does this codebase do?"

json:跑完了给你一个对象

「emits a single JSON object (followed by a newline) when the run completes successfully」,delta 和工具事件都不发,文本被聚合进最终结果。成功时的结构文档给全了:

{
  "type": "result",
  "subtype": "success",
  "is_error": false,
  "duration_ms": 1234,
  "duration_api_ms": 1234,
  "result": "<full assistant text>",
  "session_id": "<uuid>",
  "request_id": "<optional request id>"
}

八个字段里有两个要单独提醒:duration_api_ms 文档标注「currently equal to duration_ms」,别拿它当独立指标做统计;request_id 标注为可选、可能被省略,脚本里取它必须给默认值。

失败路径是这套方案里最容易被忽略的一环:文档写明失败时进程以非零码退出,错误信息写到 stderr,「No well-formed JSON object is emitted in failure cases」。所以别把 agent -p --output-format json ... | jq 直接串起来就不管了,先判退出码再解析。

stream-json:一行一个事件

NDJSON,每行一个 JSON 对象代表执行中的一个事件,默认按「一条完整 assistant 消息一行」聚合文本 delta。文档列出的事件类型是:systemsubtypeinit,一次会话开头发一次,携带 apiKeySourcecwdsession_idmodelpermissionMode)、userassistanttool_callsubtypestarted / completed,带 call_id),最后以一个 result 事件收尾。

工具事件的形状按工具类型分开写:读文件走 tool_call.readToolCallargs 里是 path,完成时 result.success 带文件元数据;写文件走 tool_call.writeToolCall,完成时 result.success 里是 pathlinesCreatedfileSize。文档还提到其它工具「May use tool_call.function structure」,即 { "name": ..., "arguments": ... } 这种形状——注意这里的措辞是「可能」,写解析器时别假设所有工具都长成 readToolCall 那样。

同样地,失败时「the stream may end early without a terminal event」,流可能没有终结事件就断了。所以判断「跑完了」的依据应该是拿到 typeresult 的那一行,而不是「管道关了」。

加不加 --stream-partial-output

要字符级实时输出才加,且只对 stream-json 有效。加了之后 assistant 事件会变成三种,文档给了一张判别表,这是本篇最该抄走的一张:

timestamp_msmodel_call_id这是什么该怎么办
带新文本的流式 delta,追加 message.content[].text
工具调用前的缓冲 flush(重复内容)跳过
回合结束时的最终 flush(重复内容)跳过

不按这张表过滤,你的累加文本就会多出两遍重复。官方示例脚本里对应的判定是这么写的:

has_ts=$(echo "$line" | jq 'has("timestamp_ms")')
has_mc=$(echo "$line" | jq 'has("model_call_id")')
if [ "$has_ts" = "true" ] && [ "$has_mc" = "false" ]; then
  content=$(echo "$line" | jq -r '.message.content[0].text // empty')

文档同时给了偷懒的办法:如果你不需要实时流,「skip all assistant events and read the result field from the terminal result event」——所有 assistant 事件全扔,只读最后那个 result 事件里的 result 字段。

四、会不会改文件:--force

这是无头模式的另一半。《Using Headless CLI》页写得很直白:--print 要配 --force(或别名 --yolo)才会真正落盘。

# 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

需要说清楚的是:这个开关的作用是「allows the agent to make direct file changes without confirmation」,也就是把确认环节去掉。《Using Agent in CLI》页还有一句「Cursor has full write access in non-interactive mode.」加了 --force 就没有人再看一眼命令再放行,风险控制要靠别处——《GitHub Actions》页给的思路是用 permissions 的 allow / deny 配置在 CLI 层面限制,以及把 git 提交、发 PR 评论这类关键动作拆成独立的、确定性的 CI 步骤,不交给 Agent。这两条是文档明确推荐的做法,具体怎么划边界要结合你自己的仓库评估。

五、边界:文档明说的和没说的

  • thinking 事件在 print mode 下被抑制,任何输出格式里都不会出现。想从无头输出里捞思考过程,捞不到。
  • --stream-partial-output 只对 stream-json 生效,配 jsontext 是无效组合。
  • json 格式失败时不产出合法 JSON;stream-json 失败时可能没有终结事件。两种失败都靠非零退出码 + stderr 判断。
  • 字段是会加的。文档明说「Field additions may occur over time in a backward-compatible way (consumers should ignore unknown fields)」,并点名 system init 事件未来可能加上 toolsmcp_servers。解析器写成严格模式、遇到未知字段就报错,迟早会被一次升级打断。
  • Windows 原生侧有明显缺口:官方文档给了 Windows 的安装命令,但《Using Headless CLI》页的全部示例脚本都是 bash 加 jq,没有给出 PowerShell 的等价写法;print mode 被自动推断的那条规则(non-TTY stdout 或 piped stdin)文档也没有区分平台说明。在 Windows 上落地,要么走 WSL 用文档原样的脚本,要么自己把解析逻辑重写一遍——后者已经不在官方文档的覆盖范围内了。
  • 无头运行的并发上限与超时该怎么设,《Using Headless CLI》和《Output format》两页都没有给出说明。别照着别处的经验往上套。

六、怎么确认你配对了

按从轻到重四步走,每步都能单独定位问题:

agent --version
agent status --format json
agent -p --output-format json "What does this codebase do?"
agent -p --force --output-format stream-json --stream-partial-output "Summarize the README"

第一条确认装上了、PATH 对了(agent update 是文档给的手动升级命令,CLI 默认会尝试自动更新)。第二条确认鉴权状态——statusabout 都支持 --format <format>,取值 textjson,默认 text,这是脚本里判断「登没登上」最省事的入口。第三条只读不写,看两件事:退出码是不是 0,以及 jq -r '.result' 能不能取到文本;能取到就说明 JSON 那条路通了。第四条才加 --force,建议先在一个空的临时仓库里跑,看事件流里 tool_callstarted / completed 是否成对出现、最后有没有 result 事件。

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

最后提醒一句:上面所有字段名、事件类型和参数取值,都来自落盘当天的官方文档。官方更新日志里与无头模式相关的条目一直在出现,字段与行为随版本变动是常态,解析脚本最好把「遇到不认识的字段就忽略」当成默认策略写进去,而不是等它挂了再改。具体以官方文档与更新日志的最新内容为准。


本文依据 Cursor 官方文档(cursor.com/docscursor.com/help)于 2026-08-18 的公开内容整理。 该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。 该产品迭代频繁,文中涉及的设置项与命令随版本变动,请以官方文档最新内容为准。 本文不涉及订阅价格、额度与模型清单,相关信息请以官方定价与模型说明页为准。

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

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