You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
// All platforms: try direct `xlings install ... -y` first.
// The direct command is more reliable for large packages (e.g. LLVM ~800MB) because:
// - it doesn't pipe through NDJSON interface (simpler subprocess chain)
// - xlings manages its own stdin/stdout/stderr
// - extraction subprocess coordination works normally
// The NDJSON interface path is kept as a fallback for progress reporting.
THE ONLY FILE that knows pre-0.0.93 spellings. Core code sees canonical forms exclusively; the public parse entry points call normalize_ first.* Deleting this module would break exactly one thing: old inputs — never a canonical path.
Implemented, and shipped in 2026.8.8.4. The contract lives at docs/50-machine-output.md (with the Chinese mirror), and the design record is .agents/docs/2026-08-08-machine-readable-output-protocol-design.md.
What the RFC asked for, and where it landed:
The envelope.--format json emits mcpp.wire with schemaVersion / kind / kindVersion / effects / data / diagnostics. Envelope and kind versions are separate, so adding a field to mcpp.env does not push the version a client reads for mcpp.xpkg.
--format unified, and --json kept for ever. §5 of the document states the split the RFC's own discussion converged on: --json keeps the payload it shipped (cache list stays {root, entries}, xpkg parse stays top-level), --format json is the enveloped one. The two published flags are therefore not a compatibility hazard.
destructive replaced by an effect set. §4. Measured rather than declared: on a fresh MCPP_HOME, xpkg parse and cache list create nothing and self env creates six entries. A boolean cannot separate "mcpp initialises itself" from "mcpp executes code in the workspace", and an editor's untrusted-workspace gate only cares about the second.
mcpp self env --format json — the item design: mcpp.toml 补全重做方案 —— 语义模块 + index 动态数据(#4 重做) mcpp-vscode#8 §2.4 was re-deriving by hand — takes an independent read-only path that does not call load_or_init. Asking where something is must not be the reason it gets created.
Positive detection as the primary rule. §1. --protocol-version exists but is documented as an optimisation, not the foundation, because on every mcpp released before it, it is itself an unknown option.
733_envelope_reports_observed_network_access.sh and the rest of the e2e family keep it pinned. Closing; anything further is a new kind or a new field, not this contract.
背景
mcpp 的机器可读输出目前是分散演化的:
xpkg parse --json、cache list --json各自决定输出结构,pack --format是产物形态不是输出格式,self env只有人类文本,#372 又引入了第三套(ide snapshot --format json/ide configure --format ndjson+ 独立的 envelope 和 ID 体系)。同时已经有两个真实客户端在等接口:
xpkg parse --json加 schema 版本字段、请求把"无写副作用"固定为契约本 RFC 提议先把契约层统一下来,再谈新增命令。
一、现状盘点(均为实测/源码核对)
1.1 输出格式选项已经分裂,且
--format有语义冲突mcpp pack --format tar|dir--formatmcpp xpkg parse --json--jsonmcpp cache list --json--jsonmcpp ide snapshot --format json--formatmcpp ide configure --format ndjson--format已发布的 mcpp 里,"输出格式"的事实拼写是
--json;--format被pack占用为另一个语义。1.2 未知格式的响应,#372 内部就不一致
同一个 PR、同一天写的两个命令:
ide snapshot --format yamlMCPP_IDE_UNSUPPORTED_FORMAT诊断ide configure --format json三个维度全不同。在把
--format推广到更多命令之前,必须先定死这一条,否则是把已知缺陷标准化。1.3 客户端拿不到的东西,mcpp 其实已经在算
mcpp self env已经打印MCPP_HOME/xlings home/ index repos / default toolchain,但没有--format json。结果 mcpp-community/mcpp-vscode#8 §2.4 被迫自己重实现整条定位逻辑:这段逻辑 100% 依赖 mcpp 内部实现、跨平台、易碎,而且是 mcpp-community/mcpp-vscode#8 全篇里唯一没有契约测试兜底的部分。给
self env加 JSON 输出约 15 行,可以直接删掉它。1.4
xpkg parse --json确实没有 schema 版本字段实测确认,mcpp-community/mcpp-vscode#8 §7.1 的请求成立且未被满足。成本是一个字段。
1.5 我们自己就是"汇合式协议"的消费者,而且绕开了它
xlings interface是完整的汇合式设计:interface <capability> --args '<JSON>'+--list(20 个 capability,含destructive标记和inputSchema)+--version。而 mcpp 对它的实际用法(
src/xlings.cppm:1064-1076,main 分支):主路径走裸
xlings install -y,把输出全部重定向到 null,然后 mcpp 自己在 stderr 画一个只有秒数的 spinner。也就是说:我们宁愿丢掉 xlings 的全部结构化进度事件,也不走那条 NDJSON 管道。事件流的唯一独占价值就是进度事件,它在真实压力下不可靠 → 消费者放弃它。这是"汇合传输层"最直接的反证。
另一个观察:
xlings interface --list里 20 个 capability 的outputSchema全部是{"properties":{"exitCode":{"type":"integer"}}}。声明了 schema 但没填 —— 这比不声明更危险,因为客户端会以为有契约。(我们踩过:#238 的 multi-repo 失败模式发出{"exitCode":1}且没有 error 事件,所以InstallProgressHandler才要用capturedDiagnostics_手工兜住 error/warn 文本。)1.6 CDB 的 bootstrap 问题比想象中小
实测(mcpp 2026.8.6.3):
源码上的原因:
src/build/ninja_backend.cppm:1546,write_compile_commands()在 spawn ninja 之前执行。真正的门槛不是"编译成功",而是prepare_build()成功。因此 IDE 侧真正缺的不是"能在编不过时拿到 CDB"(已经有了),而是:
tests/**及其 dev-dependency 上下文二、判据
沿用三档划分:
第 3 档要注意一个反直觉的事实:有些能力 mcpp 做反而更贵,因为 mcpp 是多进程、公开契约、要向后兼容的,必须额外承担只读性保证、路径 containment、选择器语义、诊断降级 —— 而插件在自己进程里读文件,这些负担都不存在。
三、提案
阶段 0 — 先定死"未知 format 怎么办"(前置条件)
客户端在拿到输出之前无法知道 mcpp 支不支持它请求的格式,所以拒绝也必须用客户端能解析的形式说出来。
这一条不定,后面所有
--format推广都是在复制缺陷。阶段 1 — 通用输出信封(新模块
mcpp.wire){ "schemaVersion": 1, "kind": "mcpp.metadata", // mcpp.env / mcpp.xpkg / mcpp.cdb / ... "destructive": false, // ← 借鉴 xlings interface 的设计 "mcpp": { "version": "2026.8.8.1", "protocol": { "min": 1, "max": 1 } }, "data": { /* 命令特有 */ }, "diagnostics": [ { "code": "...", "severity": "error|warning", "source": "mcpp", "message": "...", "path": "...", "range": {"start":{"line":1,"column":1},"end":{...}} } ] }destructive字段的价值:mcpp-community/mcpp-vscode#8 §7.2 花了一整段请求"请确认xpkg parse --json无写副作用并可作为契约依赖",本机实测过还要请上游固定。有了这个字段,请求当场变成机器可判定的契约。它同时替代 #372 文档里那段"configure 不是 read-only,IDE 必须先拿 workspace trust"的散文 —— VSCode 的 untrusted workspace 门可以直接读字段决定跑不跑。代码来源:
Diagnostic/Position/Range/Severity模型和 envelope 构造 + 内容寻址 ID 在 #372 里已经写好且质量很高(src/ide/model.cppm、src/ide/snapshot.cppm,约 150 行)。提议把它们从mcpp.ide.*提升为mcpp.wire,服务所有命令而不只是 IDE。新增协商入口(唯一"汇合"的地方):
一次调用、不需要项目、不触网。客户端启动时判断该走哪条路,不用先 spawn 一个可能失败的命令再解析错误消息(mcpp-community/mcpp-vscode#5 验收标准第 2 条)。
阶段 2 —
--format归一 + 老命令永久兼容规范拼写:
--format json(ndjson保留给未来真正需要流式的场景)。选
--format而非--json的理由不是"跟 #372 一致",而是:布尔开关无法表达第二种格式,也无法承载"未知值"这个错误分支——而阶段 0 恰恰需要它。兼容策略 —— 直接套用本仓库已有的范式(
src/toolchain/compat.cppm):三条硬约束:
--json永久保留,不删pack --format tar|dir的语义冲突:它是产物形态不是 stdout 格式。两条路,倾向前者:pack为例外(它没有机器可读输出,不参与本协议)--layout tar|dir为规范拼写,--format降为别名,走同一套兼容机制阶段 3 — 补齐机器接口(按判据筛过)
destructivemcpp self env --format jsonmcpp xpkg parse --format jsonmcpp cache list --format jsonmcpp metadata --format jsonmcpp metadata --resolved --format jsonmcpp build --configure-only --format json两个设计约束:
mcpp metadata默认必须不触网、不解析依赖(对标cargo metadata --no-deps)。否则 design: mcpp.toml 补全重做方案 —— 语义模块 + index 动态数据(#4 重做) mcpp-vscode#8 的场景(编辑 mcpp.toml 时补全)每次敲键都可能触发网络。需要依赖图和失效清单的,显式加--resolved。BuildContext::fp和BuildPlan里:有了它,客户端的 watcher 就有了机器可读依据,而不是照着文档里一段会漂移的散文硬编码。
阶段 4 — 明确不做
mcpp interface <cap> --args/--list/ capability 自描述--help作为发现机制;xlings 的 interface 面向 AI agent 编排,mcpp 的消费者是 IDE 插件和 CI 脚本,形态不同ide snapshot)[workspace].members约 60 行;mcpp 做要 400+ 行(额外承担只读保证、路径 containment、选择器语义、诊断降级)。成员发现并入mcpp metadata即可stat()+ 客户端自己的状态机四、必须守住的纪律
xlings interface的outputSchema全空(§1.5)教了一课:envelope 的价值全在data被真正版本化和文档化上。只做外层信封、内层随手改,客户端会因为"看到 schemaVersion 就以为有契约"而被坑得更惨。具体到 mcpp:
kind的data形状进docs/spec/schemaVersion且protocol.min/max同时给出重叠窗口kind至少一个 golden fixture 测试(feat(ide): add versioned project snapshots and pre-build CDB #372 的tests/fixtures/ide/snapshot-v1.json是好范式,值得保留并推广)同一条纪律也适用于错误码:
MCPP_IDE_CONFIGURE_FAILED这种「一个码覆盖全部失败 + 文档明令不许解析人类消息」的形状,是名义结构化、实际空洞。新增的每个失败分支要么有自己的 code,要么显式声明为「不可细分」。五、与 #372 的关系
#372 里有三类内容,建议分开处理:
A. 直接可用、建议尽快单独合入
src/build/test_targets.cppm(81 行)—— 把tests/**发现从run_tests提取出来,消除了 IDE CDB 与mcpp test在 member 选择 / 命名 / per-glob flags 上漂移的可能。是真正的收敛。write_fresh_compile_commands+platform::fs::replace_file+FileLock(ec)—— 顺带修掉一个既有缺陷:write_compile_commands()目前是ofstream截断写,非原子、不检查失败,clangd 可能读到半截 JSON。prepare.cppm的 stdout 纪律修复。test_compile_commands/test_platform_fs/test_test_options)。B. 建议捞进本 RFC 复用
src/ide/model.cppm的Diagnostic/Position/Range/Severity+wire_name(~90 行)src/ide/snapshot.cppm的 envelope 构造 + 内容寻址 ID(~60 行)这两块设计质量高,只是被绑死在一个命令上。提升为
mcpp.wire后可服务全部 JSON 出口。C. 建议不合入
inspect.cppm(433) /publish.cppm(333) /events.cppm(106) /cmd_ide.cppm(119) /ide snapshot/ide configure/IdePhase·SnapshotState·ArtifactState(生产代码零使用者)/describe_std_module(仅单测调用)/.xlings.jsonpin bump(与本 PR 无关)/docs/superpowers/目录树(本仓库设计文档惯例是.agents/docs/)。另外,若 #372 按现状推进,两个问题需要先修:
src/pm/package_fetcher.cppm:1036硬编码/*quiet=*/false调用ensure_official_package_index_fresh,其内部xlings::print_status(xlings.cppm:1583)和update_index_unguarded的run_streaming回调直接std::println到 stdout,不看mcpp::ui::is_quiet()。任一xim:包(含工具链)在本地索引缺失且未 debounce 时触发,xlings update的全量输出会进 NDJSON 流。tests/e2e/198用_inherit_toolchain.sh预置本机工具链,索引永远命中,结构性覆盖不到。根因是 stdout 归属分散在多个 bool 参数里;建议收敛为单一开关(让
xlings::print_status走mcpp::ui::status,或加ui::stdout_is_protocol()一票否决)。inspect.cppm对 rooted workspace 产出workspacePath == "."(单测RootedWorkspaceSelectsRoot明确断言),而文档 §7.2 要求客户端configure --package <workspacePath>per member;-p .经resolve_member_dir在[workspace].members里找不到.→ 报错退出 3。根因是 member selector 语法在三处独立推导:
project.cppm::resolve_member_dir、prepare.cppm:951、ide/inspect.cppm::matches_selector,前两处一致、第三处多了.别名。现有测试恰好绕开(e2e 用 virtual workspace;单测里 name/dir/workspacePath 三者相同)。六、实施顺序
前 5 步合计约 400 行,即可满足 mcpp-community/mcpp-vscode#5 的全部验收标准和 mcpp-community/mcpp-vscode#8 §7 的两条请求。
七、待确认
@Sunrisepeak
--format作为规范拼写、--json永久别名且不打 deprecation 警告 —— 是否接受?pack --format tar|dir走「声明为例外」还是「加--layout别名迁移」?mcpp --protocol-version作为唯一的汇合式协商入口 —— 是否接受?(明确不做mcpp interface)destructive字段是否接受作为公开契约(等于把 design: mcpp.toml 补全重做方案 —— 语义模块 + index 动态数据(#4 重做) mcpp-vscode#8 §7.2 的人工承诺固化)?@wellwei
mcpp build --configure-only(spawn + 退出码 + 读 CDB)而非 NDJSON 事件流,扩展侧代码会更少 —— 但已有实现分支需返工,是否值得?-p .)若 feat(ide): add versioned project snapshots and pre-build CDB #372 继续推进,需要先处理。@Ximiaw
mcpp self env --format json是否能覆盖 design: mcpp.toml 补全重做方案 —— 语义模块 + index 动态数据(#4 重做) mcpp-vscode#8 §2.4 的全部需求(home / registry / index 路径 + shim 情形)?还缺什么字段请列出。mcpp metadata --format json(不触网,含 manifest 诊断 code/path/line/column)是否满足 design: mcpp.toml 补全重做方案 —— 语义模块 + index 动态数据(#4 重做) mcpp-vscode#8 §1「不做」栏里那条"等上游版本化 schema"的一部分?还是说静态字段键/枚举值仍需要独立的 schema 出口?