Files
Ra3ModXmlExt/docs/ai-agent-integration-plan.md
2026-09-10 21:39:32 +02:00

51 KiB
Raw Permalink Blame History

RA3 Mod XML AI Agent 接入计划与进度追踪

状态:实施中
创建时间:2026-09-09
目标:让 AI Agent / 其他工具能够方便、可靠地使用 RA3 Mod XML 扩展构建的语义索引。


进度追踪

阶段 内容 状态
Phase 0 计划与进度文件 完成
Phase 1 外部快照格式与导出命令 完成
Phase 2 查询核心与 CLI 完成
Phase 3 MCP Server 完成(基础 stdio MCP
Phase 4 MCP 配置助手 / 稳定 launcher 完成(launcher + 启用命令 + Claude/Cursor 配置写入 + 复制配置)
Phase 5 Agent Skill 安装器 完成(默认安装 + 多位置选择 + 安装记录 + 卸载/同步函数)
Phase 6 Live 查询与快照合并策略 完成(本地只读 HTTP live server + endpoint 文件 + MCP 在线优先/离线兜底 + final 索引静默 5 秒自动快照)
Phase 7 测试、文档、发布 🟡 部分完成(README + 计划文档已更新;全部 31 个 test/*.test.mjs 逐个直接运行通过;已用直接 esbuild CLI 验证 dist 构建与 MCP smoke testnode --test 受沙箱限制,尚未 VSIX 打包/发布)

二期进度(2026-09-10

阶段 内容 状态
二期 A1 live 响应携带 projectDirMCP 侧校验 完成
二期 A2 endpoint 按项目分文件(保留全局兜底) 完成
二期 A3 live server 支持 ?project= 路由(不再只看活动编辑器) 完成
二期 A4 关闭窗口只清理自己写的 endpoint 完成
二期 A5 processId 存活校验 完成
二期 A6 live 失败负缓存 完成
二期 A7 snapshotBaseName 改为 sha1 前缀 + 可读 slug 完成
二期 B1 Skill 适用范围段落(正/负信号 + get_status 探针) 完成
二期 C1 get_asset_references:有界、带 provenance 的正向引用查询 完成
二期 C2 本地 HTTP /get_asset_references 与 MCP 工具接入 完成
二期 C3 二期测试与文档 完成(264/264 测试通过)

三期进度(2026-09-10

阶段 内容 状态
三期 0 讨论结论与进度落档 完成
三期 1 运行时解析:无 Node 时用 VS Code ElectronELECTRON_RUN_AS_NODE 完成
三期 2 抽出 src/agent/liveClient.ts,MCP 与 CLI 共用转发/校验/负缓存 完成
三期 3 instances/ 注册表 + 跨实例剪枝 + 只读 index.json 发现面 完成
三期 4 CLI 瘦身:live 优先、快照兜底、cwd 项目识别、补 projects/get_asset_references 完成
三期 5 三期测试与文档 完成(302 用例,299 通过 / 3 因沙箱跳过)
三期 6 (可选)TCP → 命名管道 / UDS 传输 未开始

1. 背景与目标

当前 RA3 Mod XML 扩展已经能构建大型项目的语义索引,包括:

  • asset 定义:type / id / file / line / origin / stream
  • $DEFINE
  • <Include> / xi:include 关系
  • 语义引用和反向引用
  • manifest 资产
  • .w3x art 资产

这些能力目前主要服务于 VS Code 编辑器内部。
本计划的目标是:

  1. 让 AI Agent、脚本和其他工具能够方便、可靠地使用该索引;
  2. 不需要用户注册全局 CLI / 修改 PATH;
  3. 安装/更新扩展后能通过一次性设置完成接入;
  4. 在用户持续编辑代码时,索引查询仍然足够实时且不会造成磁盘写放大;
  5. 为 AI Agent 提供工具 + 引导 Skill,让它知道何时、如何调用索引。

2. 核心设计决策

2.1 不采用“全局 CLI + PATH”作为主入口

CLI 可以作为高级/调试工具保留,但不是默认路径。

2.2 以 MCP Server 作为 AI Agent 主接口

MCP Server 使用本地 stdio 启动,通过绝对路径配置即可,不需要 PATH。

2.3 内存索引为主,磁盘快照为辅

  • 实时查询尽量走 VS Code 扩展内存中的 ModIndex
  • 磁盘只保存低频、合并后的“当前稳定快照”作为离线兜底;
  • 避免用户连续编辑保存时产生大量磁盘 IO。

2.4 Agent Skill 是可选增强层

MCP 注册解决“工具可用”,Skill 解决“Agent 知道怎么用好工具”。
Skill 不作为启用 MCP 的强制条件。

2.5 所有查询结果都携带索引状态

外部接口必须返回:索引是否存在、是否正在构建、是否完整、是否 stale、数据版本/时间。


3. 目标架构

VS Code 扩展
   │
   ├── 内存 ModIndex
   │       │
   │       ├── 本地只读查询服务(可选,热数据)
   │       └── 低频快照导出(冷数据)
   │
   ├── MCP 配置助手
   │       └── 写入 AI 客户端配置 + 稳定 launcher
   │
   └── Agent Skill 安装器
           └── 安装到 ~/.agents/skills 等位置

AI Agent / 外部工具
   │
   ├── MCP Server(推荐)
   │       ├── 扩展在线 → 查询内存/本地服务
   │       └── 扩展离线 → 读取最近稳定快照
   │
   ├── CLI(可选)
   └── HTTP/JSON-RPC(可选)

4. 组件设计

4.1 外部快照格式

定义一份稳定、公开的快照 schema,不直接暴露内部 workspaceStorage 缓存。

{
  "schemaVersion": 1,
  "project": "D:/Mods/CoronaMod/mods/mods/corona",
  "generatedAt": "...",
  "buildId": 128,
  "phase": "art",
  "complete": true,
  "stale": false,
  "stats": {
    "assetCount": 38304,
    "referenceCount": 97862,
    "defineCount": 240,
    "streamCount": 12
  },
  "assets": [
    {
      "type": "GameObject",
      "id": "...",
      "file": "Data/...",
      "line": 10,
      "origin": "project",
      "stream": "static",
      "viaInstance": false
    }
  ],
  "defines": [],
  "references": [],
  "includeGraph": [],
  "sourceCandidates": []
}

生成方式:

  • ModIndex 导出;
  • 使用数组而不是 JS Map
  • 原子写入:temp + rename
  • 同一项目只维护 current 快照,不保留每次历史版本;
  • 可选维护 previous 用于构建期间回退。

4.2 MCP Server

MCP Server 不直接持有完整索引,而是作为查询代理:

  1. 扩展在线时,转发到 VS Code 扩展的本地只读查询服务;
  2. 扩展离线时,读取最近一次稳定快照;
  3. 所有工具返回结果时附带索引状态。

建议 MCP 工具集:

get_status()
list_projects()
find_asset(id, type?)
find_references(id, type?)
list_assets_by_type(type, prefix?, limit?)
get_definition(type, id)
resolve_include(source)
is_file_active(path)
find_define(name)
get_usage_guide()

4.3 稳定 launcher 与 MCP 配置助手

扩展安装路径会随版本变化,所以不能直接写死在 MCP 配置里。

稳定入口:

~/.ra3modxml/
  ra3-mod-xml-mcp.cmd
  projects.json
  snapshots/
    current.json.gz
    meta.json
  skill-install.json

MCP 配置示例:

{
  "mcpServers": {
    "ra3-mod-xml": {
      "command": "C:\\Users\\lanyi\\.ra3modxml\\ra3-mod-xml-mcp.cmd",
      "args": ["--project", "D:\\Mods\\CoronaMod\\mods\\mods\\corona"]
    }
  }
}

扩展每次激活/升级时刷新 launcher 内容,指向当前扩展安装目录。
这样 MCP 配置只需要写一次,升级不断。

4.4 Agent Skill

默认安装:

~/.agents/skills/ra3-mod-xml/
├── SKILL.md
└── references/
    └── query-guide.md

Skill 内容原则:

  • 只描述功能本身;
  • 说明“什么情况下该用这个 Skill”以及“怎么用”;
  • 不引用各个工作区里的具体文档,例如 docs/codebase-navigation-guide.md,避免注意力噪音;
  • 保持通用,不绑定某个 mod。

Skill 内容建议包括:

  • 索引覆盖范围和状态含义;
  • MCP 工具用途;
  • 推荐查询工作流:
    • 从 ID 找定义 → find_asset
    • 找所有引用 → find_references
    • 确认文件是否 active → is_file_active
    • 追继承链 → find_references + find_asset
    • 找未引用资产 → unreferenced
  • 使用规则:
    • 优先查询索引,而不是全项目 rg
    • 查询尽量带 type,避免同名不同类型混淆;
    • 不要把整个索引快照读进上下文;
    • 如果索引状态不是 ready/stale,应查询状态或重试。

4.5 索引状态与一致性

定义索引状态:

状态 含义
no_index 从未构建
building 正在构建/重建
ready_xml XML phase 已发布,art 未完成
ready 完整 final 索引
stale 构建期间有文件变化
error 构建失败但保留 last good

查询接口支持两种模式:

  • 默认 / eventual:有可用快照就返回,同时附带 stale/incomplete 标记;
  • 严格 / consistent:调用方传 wait_for_index=true / allow_stale=false,等待新 final 索引或超时。

4.6 持久化策略

  • 热数据:内存 ModIndex
  • 实时查询:本地查询服务 / MCP 转发;
  • 冷数据:低频快照,例如:
    • 用户手动导出;
    • 索引稳定一段时间后自动写一次;
    • 扩展退出前写一次(尽力而为);
  • 不每次 rebuild 都写全量快照。

4.7 用户交互流程

安装/更新扩展后:

  1. 首次索引完成;
  2. 如果尚未启用且未被用户忽略,弹出提示:
RA3 Mod XML 索引已就绪。
是否让 AI Agent 使用索引查询能力?
  1. 用户点击“启用”;
  2. 扩展默认注册 MCP Server
  3. 同时询问/默认勾选:
[✓] 同时安装 RA3 Mod XML Agent Skill(推荐)
  1. 写入 MCP 配置;
  2. 安装 Skill 到 ~/.agents/skills/ra3-mod-xml/
  3. 提供“安装到其他位置”或“手动操作指南”。

4.8 安全设计

  • 本地查询服务只监听 127.0.0.1
  • 使用随机 token
  • MCP Server 默认只读,不提供文件修改/命令执行工具;
  • 写 MCP 配置前必须用户同意;
  • Skill 安装目录记录 managed marker,避免覆盖用户自定义内容。

5. 实施阶段

Phase 1:外部快照格式与导出命令

  • 新增 src/agent/snapshot.ts
  • 新增命令:ra3modxml.exportIndexSnapshot
  • 定义 schemaVersion
  • ModIndex 生成可序列化快照
  • 原子写入 ~/.ra3modxml/snapshots/current.json.gz
  • 输出状态元数据

Phase 2:查询核心与 CLI

  • 复用纯 TS indexer 模块;
  • 新增 src/agent/query.ts
    • findAsset
    • findReferences
    • isFileActive
    • listAssetsByType
    • getStatus
  • 新增 CLI 入口(可选):
    • 不注册 PATH
    • 通过绝对路径调用;
    • 供脚本/调试使用。

Phase 3MCP Server

  • 新增 src/agent/mcpServer.ts
  • 支持 stdio 协议;
  • 启动时读取连接信息/快照;
  • 提供前述 MCP 工具;
  • 每个响应带索引状态。

Phase 4MCP 配置助手

  • 新增 src/agent/setup.ts
  • 创建稳定 launcher
  • 检测/写入常见客户端配置:
    • Claude Desktop
    • Cursor
    • Codex
    • 通用 JSON
  • 提供“复制 MCP 配置”;
  • 提供手动操作指南。

Phase 5Agent Skill 安装器

  • 新增 src/agent/skill.ts
  • 生成 SKILL.md + references/query-guide.md
  • 默认安装到 ~/.agents/skills/ra3-mod-xml/
  • 可选安装到:
    • ~/.claude/skills/ra3-mod-xml/
    • 项目 .agents/skills/
    • 项目 .claude/skills/
    • 自定义位置
  • 记录已安装位置,扩展升级时自动同步;
  • 不覆盖用户自定义内容。

Phase 6Live 查询与快照合并

  • 在扩展内启动本地只读查询服务;
  • MCP Server 在线时优先转发到扩展;
  • 离线时读取 current.json.gz
  • 实现快照 quiet-period 合并写入,避免频繁全量落盘;
  • 实现状态机:no_index / building / ready_xml / ready / stale / error

Phase 7:测试、文档、发布

  • 测试快照导出/导入一致性;
  • 测试 MCP 工具查询;
  • 测试 Skill 安装/更新/卸载;
  • 测试持续编辑场景下的状态与 stale 行为;
  • 更新 README / docs
  • 发布新版本。

6. 预估文件改动

新增文件

src/agent/
  types.ts
  snapshot.ts
  query.ts
  mcpServer.ts
  setup.ts
  skill.ts
  localServer.ts
  launcher.ts

docs/
  ai-agent-integration-plan.md

修改文件

package.json                 # commands / configuration
src/extension.ts             # 注册命令、设置、初始化
src/workspace.ts             # 暴露 index status / snapshot 发布钩子
src/features/agentSetup.ts   # 用户引导 UI(或并入 agent/setup.ts

7. 验收标准

  1. 用户安装/更新扩展并完成一次索引后,可以看到“启用 AI Agent 访问”的引导。
  2. 用户点击启用后,不需要手动配置 PATH,不需要手动查找 workspaceStorage。
  3. 常见 AI 客户端能通过 MCP 调用索引工具。
  4. Agent 能正确回答:
    • “某 asset 定义在哪个文件?”
    • “谁引用了这个 asset?”
    • “这个 XML 文件是否真的被 Include?”
  5. 查询接口在无索引/索引更新中不会返回误导性空结果。
  6. 用户连续编辑保存时,不会因为频繁快照写入而产生明显卡顿或磁盘膨胀。
  7. Skill 安装后,Agent 知道优先使用 MCP 查询,而不是全文 grep。
  8. 扩展升级后,MCP 配置仍然有效,Skill 能自动同步到已安装位置。


第二部分:二期计划(讨论结论 + 实施)

讨论时间:2026-09-10 主题:Skill 的项目识别边界、正向/间接引用查询的取舍、MCP 与 localhost 架构现状


8. 讨论结论一:谁来判定"这是不是一个红警 3 模组项目"

问题

Skill 需要说明"何时应该被使用"和"适用范围"。这要求 Agent 能理解什么是 "红警 3 模组项目里的 XML",同时避免把无关项目误判为 RA3 模组。

问题在于:这个判断应该由扩展/Skill 自己解释(甚至给出推断规则),还是应该 由项目开发者在自己的项目文档 / AGENTS.md 里声明?

结论:分层,且 Skill 不做复杂推断

职责 理由
Skill 说明能力覆盖范围 + 少量正/负信号 + 便宜的探测方式 Skill 是用户全局的,会在所有对话里加载,必须简短且能自我限制
项目开发者AGENTS.md / README / 项目标记) 声明"这个仓库是什么项目、要不要用这些工具" 只有项目自己知道自己是什么,这是每个仓库的局部事实
MCP 工具自身 用响应证明自己是否可用 get_status 是最便宜的判定,不应靠读文档猜

为什么不把判断规则写进 Skill

  • Skill 在 ~/.agents/skills/ 下全局生效;如果写一堆判定规则,会在与 RA3 无关的项目里制造"要不要用这个工具"的注意力噪音。
  • 规则永远不完备。真实反例:
    • 目录里有 Schemas/xsd/CnC3Types.xsd → 可能只是 SDK 本身,不是 mod;
    • 只有一堆从 SDK 抄来的 .xsd → 是"RA3 相关",但没有 mod 数据;
    • CnC3Types.xsd 里的 CnC3C&C3(命令与征服 3 / 凯恩之怒), 所以"存在 CnC3Types.xsd" 不等于 "是 RA3 模组"
    • Data/Mod.xml / *.babproj / Data/additionalmaps/mapmetadata_*.xml → 基本可确认是 SAGE / BinaryAssetBuilder 系项目(RA3 只是其中之一)。
  • 一旦误判,Agent 会拿 RA3 的索引数据去回答另一个项目的问题,比不使用更糟。

落地方案

Skill 侧src/agent/skill.tsSKILL_MD)新增 applicability 段落:

  • 正向信号(任一命中即可尝试): Data/Mod.xmlData/additionalmaps/mapmetadata_*.xml*.babproj XML 根为 <AssetDeclaration>;使用 <Includes><Include source="DATA:…"/></Includes>
  • 明确否定:无关仓库、仅含拷贝 .xsd 的项目、通用 XML 配置、构建脚本、 非 SAGE 游戏项目。
  • 便宜的探针:不确定时先调 get_status;它返回索引归属的 projectDir。 如果与当前工作区根不一致,或状态是 no_index,就停止使用索引工具 改为直接读文件。

这样即使误触发,成本也只是一次 get_status,不会污染后续推理。

项目开发者侧:在项目根 AGENTS.md 写一句即可,例如:

## RA3 Mod tooling
This repository is a Red Alert 3 mod (SAGE XML). When locating asset
definitions or references, prefer the `ra3-mod-xml` MCP tools
(`find_asset`, `find_references`, `is_file_active`) over full-text search.

扩展不自动往用户仓库写标记文件(如 .agents/ra3-mod-xml-project.json) 作为二期内容;若将来提供,必须显式确认。


9. 讨论结论二:要不要提供"某个 Asset 直接/间接引用的所有 Asset"

场景

用户要求 Agent"查阅或修改雅典娜炮的武器"。Agent 需要:

  1. 找到雅典娜炮的 GameObject
  2. 读源码,发现 <FireWeaponUpdate /> / <WeaponSetUpdate /> 等子元素, 在这些子元素里找到武器引用;
  3. <CreateObjectDie /> 引用了临时物体,还要去读临时物体的 GameObject, 在它的 FireWeaponUpdate 里找武器;
  4. 若雅典娜炮只有 FireWeaponUpdateWeaponSetUpdateinheritFromBaseCannon 上,还要读 BaseCannon 并综合判断。

评估问题:

  • 是否有必要提供"某个 Asset 直接或间接引用的所有 Asset"?
  • 只给精炼 asset 列表,Agent 不知道是哪个子元素导致的引用,有用吗?
  • 若同时提供上下文,磁盘/内存/性能允许吗?内容长度会不会反而比直接读源码更贵?
  • 相对直接读源码未必有显著提升,反而增加注意力噪音?
  • 间接引用会不会多项式/指数爆炸?

先指出当前实现的真实缺口

现在的索引只有反向引用(谁引用了我),没有正向查询(我引用了谁)。 find_references(AthenaCannon) 返回的是"谁引用了雅典娜炮",而不是"雅典娜炮 用了哪些武器"。所以上述 Agent 工作流目前完全无法用工具完成,只能读源码。

因此问题不是"要不要锦上添花",而是这里确实缺一个基础能力。

结论:不提供"所有可达 Asset 的扁平闭包",改提供"有界 + 带 provenance 的边列表"

不采用扁平静态闭包的理由:

  1. 丢失 provenanceAgent 仍然要读源码。 到底是 FireWeaponUpdate 的武器、 WeaponSetUpdate 的武器、CreateObjectDie 临时物体的武器,还是 inheritFrom 继承来的武器?这决定了 Agent 改哪个文件、加不加 xai:joinAction。丢掉上下文,列表基本没用。
  2. 尺寸会失控。 一个 GameObject 的可达闭包可能是 Weapon → Projectile → Warhead → DamageNugget → FX → Particle → TextureModel → Mesh → Material → TextureAttributeModifierUpgradeCommandButton……Corona 规模下单个单位几百到上千节点完全可能。200 条边就是几万 token比直接读 3 个小 XML 文件贵得多
  3. 注意力噪音。 列表里 80% 是与当前问题无关的 Texture / FX 时,推理质量下降。
  4. "间接"的诱惑会持续膨胀。 一旦提供无界闭包,Agent 会习惯性调用它, 然后被淹没。

但以下特性确实值得工具化:

  • 多跳遍历是纯机械的,正是工具该做的事;
  • inheritFrom 链 + xai:joinAction 语义很微妙,Agent 手推容易错;
  • 每跳都 read 一个文件,Corona 下成本和 token 都很高;
  • 工具可以做到确定性、不遗漏(按 XSD 判定"哪些属性是引用", 而不是靠 Agent 逐个注意)。

设计要点(二期实现遵循):

  • 返回边列表而不是节点集合。每条边携带:
    • fromeffective asset)、to(解析出的定义位置)、
    • via.kindattribute / content / inheritFrom)、
    • via.elementvia.parentvia.attribute
    • source.file / source.lineXML 实际所在位置),
    • definedIn(当该边来自 inheritFrom 祖先的 XML 时,标明真正写它的资产)。
  • 默认 depth: 1,硬上限 3。 Agent 拿到直接引用后自己决定是否对某个具体 节点递归,比一次性 dump 闭包更省 token、更有针对性。
  • targetTypes 过滤是抑制噪音最有效的手段。雅典娜炮场景就是 targetTypes: ["WeaponTemplate"](可选再带 GameObjectCreateObjectDie), 返回可能只有 310 条边。
  • inheritFrom 不消耗 depth:继承是资产自身定义的一部分,不是运行时引用。 因此在同一 depth 内递归遍历继承链(链长上限 8),并把 definedIn 标出来。
  • 截断时返回 omittedByTargetType 摘要,让 Agent 知道被砍掉了什么形状的数据, 可以精确下钻,而不是无脑重试或放弃。
  • 不提供"合并后的有效值"joinAction 的 Replace/Remove/merge 语义算错的风险 太高,一期不做。

关于爆炸和成本:

  • 算法上不是指数:带 visited-set 的 BFS 是 O(V+E)。真正的风险是语义宽度 不是复杂度,因此用 depth + targetTypes + maxEdges 三重限制。
  • 只做 live 查询(VS Code 打开时可用)。要拿元素上下文,最干净的做法是按需 解析定义所在文件的 DOM,而不是往 records 缓存里塞元素名/属性名 —— Corona 有 ~98k 条引用记录,再塞 5 个字符串字段会让常驻内存明显膨胀,为一个默认 depth=1 的功能付出这个成本不划算。
  • 离线快照路径明确返回"需要 live index",而不是静默返回空结果。

10. 讨论结论三:MCP / localhost 的实际架构与多窗口缺陷

当前架构(事实)

AI 客户端
   │  stdio (JSON-RPC, MCP)
   ▼
dist/agent/mcpServer.js          ← 由 AI 客户端启动,参数 --project <dir>
   │  HTTP (127.0.0.1:临时端口, Bearer token)
   ▼
VS Code 扩展进程内的 localServer  ← 仅在"启用 AI Agent 访问"后启动
   │
   ├─ 在线 → ws.activeIndex() 实时内存索引
   └─ 离线 → ~/.ra3modxml/snapshots/project-<hash>.json.gz
  • AI 客户端不需要知道端口。 MCP Server 自己读 ~/.ra3modxml/endpoint.jsonurl + tokenfetch。localhost 层是 纯实现细节。
  • 端口server.listen(port ?? 0, "127.0.0.1"),即由操作系统分配临时端口, 不会冲突;当前未传 port,所以永远是临时端口。
  • 鉴权Authorization: Bearer <token>token 为启动时随机生成;绑定 127.0.0.1,局域网访问不到。
  • 降级live 查询失败(endpoint 不存在 / 连接被拒 / 超时 1.5s)时回退到快照。

多项目 / 多 VSCode 窗口的真实缺陷

endpoint.json全局唯一的一个文件,所有 VSCode 窗口都往它写入:

  1. 最后启用的窗口覆盖前面所有窗口。 两个窗口都启用了 Agent 访问时, 只有后启动的那个能被 MCP Server 找到。
  2. 可能串项目(严重)。 假设窗口 B 覆盖了 endpoint,而某个 MCP Server 是用 --project 窗口A 启动的,它的 tryLiveQuery 会打到窗口 B 的 server, 拿到窗口 B 的索引数据,而返回值里没有任何东西能暴露这个不一致。 这比 no_index 危险得多 —— 会静默给出错误答案。
  3. 关闭任意窗口都会 clearEndpoint(),把还开着的其他窗口的 live 通道一起断掉。
  4. 同一窗口内多项目也不稳。 live server 用 getIndex: () => ws.activeIndex(),而 activeIndex() 跟着当前活动编辑器走。 用户切到另一个项目的文件,live 查询就会静默换项目;而 endpoint.json 里的 projectDir 只在启动时写一次,之后不更新。
  5. processId 虽然写进了 endpoint,但从未被校验VSCode 崩溃后残留的 endpoint 只能靠连接失败兜底。
  6. 离线快照路径是正确的 —— snapshotPathForProject() 按项目路径做 hash, 多项目互不干扰。问题只在 live 层。

修复方案(二期 A,按性价比排序)

  1. live 响应必须带 projectDirMCP 侧必须校验它和 --project 一致, 不一致就直接走快照。这是最小的正确性补丁,最先做。
  2. endpoint 按项目存~/.ra3modxml/endpoints/<project-hash>.json MCP Server 按 --project 找对应的那个;同时保留 endpoint.json 作为 "最近"的兼容入口。
  3. live server 支持 ?project= 路由,而不是只认 activeIndex()。 扩展侧加 indexForProject(projectDir),一个窗口的一个 server 就能服务它 所有已索引项目,也就不怕用户切编辑器。
  4. 关闭时只清自己的那一条,不无条件删全局 endpoint。
  5. 校验 processId 是否存活,不存活就当 endpoint 失效。
  6. tryLiveQuery 失败后做几秒的"live 不可用"负缓存,避免每次调用都试一次。
  7. snapshotBaseName 换成 sha1 前缀 + 可读 slug(与 diskCacheKey 一致的做法), 降低碰撞概率。

11. 二期实施清单

二期 A:多窗口 / 多项目正确性

  • src/agent/snapshot.ts:新增 projectHash()snapshotBaseName() 改为 <slug>-<sha1-12>
  • src/agent/endpoint.ts:新增 endpointPathForProject() / writeEndpointForProject() / readEndpointForProject() / clearEndpointForProject() / isProcessAlive()
  • src/workspace.ts:新增 indexForProject(projectDir)
  • src/agent/liveQuery.tsliveStatus() 在没有索引时回落到请求的 projectDir
  • src/agent/localServer.tsgetIndex(projectDir?) 路由、异步 handler、 /projects 端点、所有响应携带 index.projectDir
  • src/agent/mcpServer.ts:按项目读 endpoint、校验 projectDirprocessId?project= 透传、live 失败负缓存、响应 projectDir 二次校验。
  • src/extension.ts:启动时按项目写 endpoint、关闭时只清自己写过的那些。

二期 BSkill 适用范围

  • src/agent/skill.tsSKILL_MD 增加 applicability 段落(正向信号、明确否定、 get_status 探针);保持"不引用工作区文档"的原则。

二期 Cget_asset_references

  • src/agent/forwardRefs.ts(新增):DOM-based、有界、带 provenance 的正向引用遍历。
  • src/agent/localServer.ts:新增 GET /get_asset_references
  • src/agent/mcpServer.ts:新增 get_asset_references 工具(live-only 离线时返回明确的"需要 live index"说明)。
  • src/extension.ts:为 live server 提供 loadFile()(读文本 + 解析 + LineMap)。

二期 D:测试

  • test/agentEndpoint.test.mjs:按项目 endpoint、进程存活、清理隔离。
  • test/agentLocalServer.test.mjs?project= 路由、projectDir 校验、 /get_asset_references
  • test/agentForwardRefs.test.mjs:直接引用、继承链、CreateObjectDie 内容引用、 targetTypes 过滤、maxEdges 截断、depth 上限。
  • test/agentSkill.test.mjsSkill 文本包含 applicability 段落且不含工作区文档引用。
  • test/agentMcpRouting.test.mjslive URL 固定 project、跨项目响应拒绝。

12. 二期实施结果

全部完成,npx tsc --noEmit 通过,test/*.test.mjs264/264 通过。

新增 / 修改文件

新增:
  src/agent/forwardRefs.ts          有界、带 provenance 的正向引用遍历
  test/agentForwardRefs.test.mjs
  test/agentMcpRouting.test.mjs

修改:
  src/agent/endpoint.ts             按项目 endpoint + isProcessAlive()
  src/agent/snapshot.ts             projectHash() / projectSlug() / snapshotBaseName()
  src/agent/liveQuery.ts            liveStatus() 回落请求的 projectDir
  src/agent/localServer.ts          异步 handler、?project= 路由、/projects、/get_asset_references
  src/agent/mcpServer.ts            按项目 endpoint、projectDir 校验、负缓存、get_asset_references
  src/agent/skill.ts                applicability 段落 + get_asset_references 用法
  src/workspace.ts                  新增 indexForProject()
  src/extension.ts                  按项目写/清 endpoint、loadFile、endpoint 刷新
  test/agentEndpoint.test.mjs
  test/agentLocalServer.test.mjs
  test/agentSkill.test.mjs

关键行为

多窗口 / 多项目

  • ~/.ra3modxml/endpoints/<slug>-<sha1-12>.json 一项目一文件;MCP Server 先读自己项目的 文件,读不到才退回全局 endpoint.json,且仅在 projectDir 匹配时使用。
  • 每个 live 请求带 ?project=server 用 ModWorkspace.indexForProject() 路由, 不再依赖"当前活动编辑器"。
  • MCP 侧对响应里的 index.projectDir 做二次校验,不匹配就拒答并提示重新启用, 而不是给一个看起来合理但属于别的项目的答案。
  • 关闭窗口只删除本窗口写过的 endpoint 文件。
  • processId 不存活时 endpoint 视为失效。
  • live 连接失败后有 5 秒负缓存,避免每次工具调用都探一次。

Skill

  • 新增 When this skill applies 段落:正向信号(Data/Mod.xmlmapmetadata_*.xml*.babproj<AssetDeclaration>uri:ea.com:eala:asset)、明确否定规则(无关仓库、仅含拷贝 .xsd 的项目)、 以及 CnC3Types.xsd 属于 C&C3 而非 RA3 的反例。
  • 规定用 get_status 做便宜探针:状态为 no_indexprojectDir 不匹配时 停止使用索引工具,改为直接读文件。
  • 仍然不引用任何工作区文档(有测试断言)。

get_asset_references(正向引用)

  • 返回from / to / via{kind,element,parent,attribute} / source{file,line,character} / definedIn / value
  • depth 默认 1、上限 3inheritFrom 不消耗 depth,祖先 XML 在同层遍历, 用 definedIn 标出真正写这条边的资产(解决"雅典娜炮自己没有 WeaponSetUpdate 但 BaseCannon 有"的场景)。
  • targetTypes 按 XSD 可赋值性过滤,但 inheritFrom 边始终保留,因为 它解释了其余边写在哪里。
  • maxEdges 截断时返回 truncated + omittedByTargetType(只统计因上限被丢弃的边, 已被 targetTypes 过滤的不计入)。
  • 只走 live 路径;不可用时返回明确的错误说明,而不是空结果。

已知限制

  • get_asset_references 不计算 xai:joinActionReplace/Remove)合并后的 有效值,需要调用方自行确认。
  • 只有 live 路径支持正向引用;离线快照路径明确返回"需要 live index"。
  • snapshotBaseName 改为 <slug>-<sha1-12>,旧的 project-<hash>.json.gz 快照文件不会被自动清理(该功能尚未发布,无兼容负担)。
  • 本沙箱禁止 spawn 子进程,因此 node --testnpm run build 无法直接运行; 验证方式为:逐个 node test/*.test.mjs、直接调用 esbuild CLI 构建 dist、 用管道驱动 dist/agent/mcpServer.js 做端到端 smoke test。


第三部分:三期计划(讨论结论 + 实施)

讨论时间:2026-09-10 主题:Agent 自发现 MCP 的现实边界、崩溃残留清理、CLI 定位、无 Node 环境


13. 讨论结论:自操作 Agent 能否自行发现并配置 MCP

现状(核实过代码)

SKILL_MD 全文不含 ~/.ra3modxml、launcher 路径、CLI 或任何"工具不存在时怎么办" 的说明。它的第一条指令是"调用 get_status"。因此当 skill 已加载但 MCP 工具 不在工具列表里时,agent 只能得到 unknown tool,然后没有下一步。

三个独立缺口:

  1. 没有发现面。 磁盘上的文件名带 sha1-12 哈希与 slug endpoints/<slug>-<sha1-12>.jsonsnapshots/<slug>-<sha1-12>.json.gz), 只有读过本仓库源码的人才知道这套命名。agent 没有"读一个文件就知道全貌"的入口。
  2. skill 与 MCP 是独立安装的。 installAgentSkill 装的是 ~/.agents/skills/ra3-mod-xml/,与 MCP 配置无关,所以"skill 在、MCP 不在" 是常见状态,而这恰恰是最需要 fallback 的场景。
  3. 多数 harness 不允许 agent 给自己挂 MCP。 Claude Code 需要重启才能加载新的 MCP server(相关 issue 与 SIGHUP workaround 见调研),Reddit 上也有"每次装 MCP 都要手动重启"的反馈;反例是 LibreChat 的设置面板明确"take effect without a restart"。所以这是 harness 相关agent 可以配置,但通常不能让自己 当前会话用上,只能为下一次会话准备好。

结论

  • skill 不应假设 MCP 已就绪,而要给出"如何触达索引"的阶梯:
    1. MCP 工具已在工具列表 → 直接用;
    2. 否则读发现面清单;不存在 → 告诉用户去 VS Code 跑一次 Enable不要瞎猜 存在 → 用 shell 走 stdio,或用 CLI
    3. 若能写自己的 MCP 配置,可以提议代写,但必须告知下个会话才生效
  • "需要重启才生效"这类细节不必写进 skill。 新一代自操作 agent 本来就该自己 探测能力边界;skill 只需提示它"检查自己的能力范围并告知用户"。
  • 不要把各 harness 的配置路径写进 skill 文本。 那些路径会变,写进 skill 等于 把易变知识固化进 agent 上下文。逻辑留在 setup.ts (已有 addMcpServerToConfigFile / commonMcpConfigTargets),通过 CLI 暴露成 configure --harness <name>skill 只说"在用户同意后可以运行这条命令"。

14. 讨论结论:崩溃残留与 instances/ 注册表

问题确认

核实结果:全仓只有两处 rm,都在 stopAgentLocalServer(即优雅 dispose)中。 endpoints/ 没有任何剪枝逻辑。因此:

  • 崩溃后文件永久残留,只有下次打开同一个工作区才会覆盖同名文件; 删掉项目、移动目录、改 SDK 路径后,旧文件永远躺着。
  • PID 复用是真实风险。 Windows PID 会被回收;若残留 PID 恰好被无关进程占用, isProcessAlive 返回 true → 连接失败 → 5 秒负缓存 → 反复重试。不会给出错误 答案(responseProjectMismatch 仍在),但会持续做无用功。

结论:不要"共享一个 JSON + 加锁合并"

共享单文件需要锁文件(Windows 上 wx + 退避 + 陈旧锁窃取)、需要 read-modify-write 合并(否则并发写互相丢条目),而锁本身也会被崩溃留下 ——为了修 A 的崩溃问题引入了 B 的崩溃问题

改用写入方互不重叠的结构:

~/.ra3modxml/
  instances/
    vscode-<pid>-<rand>.json    ← 每个 VS Code 窗口一个,只有属主会写
  index.json                    ← 只读合并视图,供 agent/人发现
  snapshots/<slug>-<sha1-12>.json.gz
  skill-install.json
  • 无锁、无合并:每实例只写自己那一个文件,天然无竞争。
  • 崩溃自愈:任何实例激活时扫一遍 instances/isProcessAlive(pid) === false 的直接 unlink,不需要等"下次打开同一个工作区"。
  • 读方仍要校验liveness = PID 存活 能连上;PID 复用场景下连接失败即 视为死,并顺带触发剪枝。
  • index.json 是纯只读派生物(由 instances/ 生成),机器协调文件与人类/agent 发现面分离。

endpoints/ 保留为兼容读取路径,不再主动写入。


15. 讨论结论:CLI 定位与"稳定 IPC"

澄清:端口从未写进 harness 永久设置

写进配置的是 launcher

{ "mcpServers": { "ra3-mod-xml": { "command": "~/.ra3modxml/ra3-mod-xml-mcp.cmd", "args": ["--project", "..."] } } }

端口由 instances/*.json 桥接,每次开新会话时由 MCP server 现读。所以 "临时端口会变"对 harness 完全透明。换命名管道/UDS 的收益不是"稳定性",而是:

  • Windows 防火墙对监听 TCP socket 可能弹窗,命名管道不会;
  • 不占端口;
  • 权限模型更自然(管道可设 ACL;TCP loopback 任何本机进程都能连,现在靠 token)。

代价:Node 的 fetch 不支持 socketPath,客户端要从 fetch 换成 http.request({ socketPath })(服务端 listen(pipePath) 即可,路由不用改)。

CLI 定位:薄

CLI 只做两件事:读磁盘快照 + 转发给扩展(live。不要在 CLI 里重新实现 "高级功能",那等于写两遍。

但要注意:转发逻辑现在锁死在 mcpServer.tstryLiveQuery / liveUrlForTool / responseProjectMismatch / 负缓存)。 若 CLI 再写一遍就是重复。因此必须先抽 src/agent/liveClient.ts,让 mcpServer.tscli.ts 同时依赖它:

  • 换传输层时只改一个文件;
  • projectDir 校验、负缓存等安全逻辑不会在两个入口之间漂移。

16. 讨论结论:无 Node 环境(已验证)

问题

  • 不能假定用户装了 Node。
  • writeLauncher 生成的是裸 node "<server>" ...,所以没装 Node 的用户 点完 Enable 之后 MCP 起不来,而且不会报"缺 node"。这是已存在的缺口 不是未来风险。
  • 打包自包含或原生二进制是否必要?

结论:不需要打包,VS Code 自带运行时,只是还没用它

VS Code 的 Electron 二进制可以当 Node 用:

@echo off
set ELECTRON_RUN_AS_NODE=1
"C:\...\Microsoft VS Code\Code.exe" "<ext>\dist\agent\mcpServer.js" --project "D:\..."

本机实测(2026-09-10VS Code 1.135.0 / Electron 42.8.1

项目 结果
ELECTRON_RUN_AS_NODE=1 生效 process.versions.electron = 42.8.1
Node 版本 24.18.1(系统 Node 为 24.16.0
必需模块(fs/fs/promises/path/os/http/readline/zlib/crypto/util/net/child_process 全部可载入
node:test 可载入
forwardRefs.parseLoadedXml elements=2
localServer.startLocalServer + HTTP 请求 正常返回
zlib gzip/gunzip 往返
打包后的 dist/agent/mcpServer.js 完整 stdio 会话 initialize / tools/list(9 个工具) / tools/call get_status 全部正确

因此:

  • 零外部依赖,只装 VS Code 就够;
  • 路径来自 process.execPath,自动适配 Code / Insiders / VSCodium / 各平台;
  • VS Code 升级后路径变了也没关系——扩展每次激活重写 launcher,而 launcher 本身 在 ~/.ra3modxml/ 下路径稳定;
  • 需要 fallbackElectron 二进制被移动/卸载时回退到 PATH 上的 node

已知代价(实测未测,需留意的边界):

  • Electron 以 Node 模式启动比真 Node 慢(握手延迟增加,对 MCP 应无碍);
  • 远程/WSL/容器场景下 process.execPath 是 server 端二进制,MCP 必须跑在远端 —— 独立边界问题,本期不解决。

如何在"本机有 Node"的前提下测试"无 Node"

不需要真的卸载 Node。要做的是证明 Electron 路径可以独立工作,即整条链路 不依赖"PATH 上能解析到 node"

  1. 直接只用 Electron 二进制驱动 dist/agent/mcpServer.js(已做,见上表);
  2. 让解析出的 launcher 只用 Electron,不回落;
  3. 断言 launcher 文本中不出现裸 node(当 Electron 可用时)。

这三点都在本环境可做,无需移除 Node。


17. 三期实施清单

优先级 内容 理由
1 运行时解析(src/agent/runtime.ts+ launcher 优先 Electron 现在是"配好了但可能起不来",最伤
2 src/agent/liveClient.ts 后续所有传输层改动的前提;避免转发逻辑写两遍
3 instances/ + 跨实例剪枝 + 只读 index.json 修崩溃残留,兼做 agent 发现面
4 CLI 瘦身(live 优先 / 快照兜底 / cwd 识别 / projects 让"没有 MCP 的 agent"也能用
5 传输层 TCP → 命名管道 / UDS 收益中等,需 2 完成,可缓

18. 三期实施结果

全部完成。

新增 / 修改文件

新增:
  src/agent/runtime.ts         运行时解析 + launcher 脚本生成(纯函数,可测)
  src/agent/liveClient.ts      共享 live 转发层(MCP 与 CLI 共用)
  src/agent/instances.ts       instances/ 注册表、剪枝、只读 index.json
  test/agentRuntime.test.mjs
  test/agentLiveClient.test.mjs
  test/agentInstances.test.mjs
  test/agentCli.test.mjs
  test/agentLiveE2E.test.mjs
  tools/probe-electron-node.cjs    Electron-as-Node 能力探针(保留为证据)
  tools/serve-fake-index.cjs       假 live 索引服务器(手工 smoke 用)

修改:
  src/agent/setup.ts           writeLauncher 改用 runtime + launcherScript
                               返回 { path, runtime, nodeFree }
  src/agent/mcpServer.ts       改用 LiveClient;新增 list_projects
                               启动时剪枝崩溃实例;require.main 守卫
  src/agent/cli.ts             重写为薄客户端:live 优先 / 快照兜底 /
                               cwd 项目识别 / projects / outgoing
  src/extension.ts             instances/ 写入与剪枝、index.json 刷新、
                               launcher 激活时刷新、nodeFree 警告

关键行为

无 Node 运行(三期 1

  • launcher 优先用 VS Code 的 Electron 二进制(ELECTRON_RUN_AS_NODE=1), Node 只作为"Electron 二进制不存在"时的兜底: if not exist "%RA3_RUNTIME%" goto :ra3_node
  • goto 而不是 if (...) 块:批处理里块内的 %errorlevel%解析时展开, 会导致退出码错误。
  • 扩展激活时会重写 launcher(不只是 Enable 时),所以 VS Code 升级换了路径 也能自动跟上。
  • 解析不到 Electron 时会警告用户"launcher 将依赖 PATH 上的 Node"。

instances/ 注册表(三期 3

~/.ra3modxml/
  instances/vscode-<pid>-<rand>.json   每窗口一个,只有属主写
  index.json                           只读合并视图(不含 token
  endpoints/<slug>-<sha1-12>.json      兼容读取,不再主动写
  snapshots/…
  skill-install.json
  • 无锁无合并:写入方互不重叠,没有 read-modify-write 竞争。
  • 崩溃自愈:任何实例启动时剪掉 PID 已死的条目,不需要等同一工作区重开。
  • 只剪确定的死 PIDisProcessAlive 对未知/缺失 PID 返回 true, 所以老格式文件不会被误删。
  • index.json 是派生物,只用于发现;断言过其中不含 token。

共享 live 层(三期 2

liveClient.ts 现在同时被 MCP 与 CLI 使用,集中了:

  • 请求永远带 ?project=(否则会被活动编辑器的项目回答);
  • index.projectDir 不匹配就拒答;
  • 死 PID 视为失效;
  • 失败后 5 秒负缓存(可注入时钟,测试可确定性验证);
  • 端点发现顺序:按项目文件 → 任何 projects 含该项目的实例 → 全局兜底指针。

薄 CLI(三期 4

  • outgoing / projects 是 live-only:不可用时返回明确原因, 而不是空结果(空结果会被 agent 读成"没有引用")。
  • 项目解析顺序:--project--snapshot → 从 cwd 向上找 mod 项目根。
  • 退出码:0 成功 / 2 用法错误 / 3 不可用。

已知限制

  • get_asset_references 不计算 xai:joinActionReplace/Remove)合并后的 有效值,需要调用方自行确认。
  • 传输层仍是 loopback HTTP(临时端口 + token)。换命名管道/UDS 的收益是 免防火墙弹窗与更自然的权限模型,代价是客户端要从 fetch 改为 http.request({ socketPath });因为转发逻辑已集中,改动面只剩 liveClient.ts
  • 远程 / WSL / 容器场景下 process.execPath 是服务端二进制,MCP 必须跑在 远端;本期未处理。
  • CLI 在"无 Node 且无 VS Code"的环境下无法运行(这是逻辑必然:它需要一个 运行时)。有 VS Code 时用 Electron 二进制即可。

19. 三期测试与验证

npx tsc --noEmit               通过
test/*.test.mjs                37 个文件 / 302 个用例,299 通过,3 跳过

跳过的 3 个都是同一原因:本沙箱禁止从 Node 进程 spawn shellEPERM), 所以"真正拉起 launcher / CLI 子进程"的集成用例会优雅跳过并给出原因。 它们在本机正常运行时会执行。

Electron-as-Node 实测证据

tools/probe-electron-node.cjsVS Code 1.135.0 / Electron 42.8.1Windows):

项目 结果
ELECTRON_RUN_AS_NODE=1 生效 process.versions.electron = 42.8.1
Node 版本 24.18.1(系统 Node 24.16.0
必需模块 fs/fs/promises/path/os/http/readline/zlib/crypto/util/net/child_process
node:test 可载入
parseLoadedXml
startLocalServer + HTTP 请求
zlib gzip/gunzip 往返
打包后 dist/agent/mcpServer.js 完整 stdio 会话 initialize / tools/list(10 个) / tools/call

无 Node 运行的证明方式

不是卸载 Node,而是证明整条链路不依赖"PATH 上能解析到 node"

  1. cmd /c + ELECTRON_RUN_AS_NODE=1 直接驱动 dist/agent/mcpServer.js → 完整 MCP 会话成功;
  2. 生成的 launcher 里把 Node 兜底指向一个不存在的路径,会话仍然成功 → 证明走的是 Electron 分支;
  3. 断言 launcher 文本:Windows 下 Node 分支只能通过 if not exist "%RA3_RUNTIME%" goto :ra3_node 到达。

其他已验证行为

  • CLI 会自然退出:实测一次 live 查询后进程 59ms 内自然退出, fetch 的 keep-alive 不会吊住 CLI(否则对 agent 是致命的可用性问题)。
  • live 端到端test/agentLiveE2E.test.mjs 启动真实 startLocalServer 按扩展的方式写 instance/endpoint/manifest,然后用同一个 LiveClient 跑通全部工具,包括 get_asset_referencesdefinedIn 继承溯源。
  • 跨项目隔离:为第三个项目创建的客户端在只有 A/B 实例时返回 null, 不会回落到 A 的数据。
  • 发现降级:删掉 endpoints/ 后仍能通过 instances/ 找到实例。
  • 崩溃剪枝:插入一个 PID 必死的实例,剪枝后另一个实例仍正常工作; 清理本窗口实例不影响其他窗口。

20. P0 修复(2026-09-10

三期测试暴露 / 评审发现的问题,以下四项已修复:

# 问题 修复
1 test/agentSkill.test.mjsSKILL.md 措辞不一致(new session / Never fabricate index results 在源码中丢失),干净重建后测试失败 恢复两条护栏;测试同步断言
2 Skill 里的 CnC3Types.xsd 说法错误:它是 RA3 Mod SDK 也携带的 SAGE 基础 schemasdk.ts 的 SDK 根标记就是它),原句会让 agent 误判真实 RA3 项目 删除该句,改为通用否定规则(SAGE-looking XML / 复制的 .xsd / Data 目录本身都不构成证据)
3 关闭一个 VS Code 窗口会无条件 clearEndpoint() + writeManifest([]),抹掉其他仍在服务窗口的 endpoint.json / index.json 新增 refreshDiscovery():从幸存 instance 文件重新推导两者;stopAgentLocalServer 改用它;新增回归测试
4 npm run build 不清理 dist/0.1.26 VSIX 同时带着旧布局的 dist/mcpServer.js / dist/cli.js esbuild.mjs 构建前 rm -rf dist

同时:

  • SKILL.md 修正被合并的 find_define 列表项与 the your own harness' 笔误; 从 SKILL.md 链接 references/query-guide.md(skills 客户端只按需加载被引用的 资源,之前该文件永远不会被读取);CLI 段落补充“它是 Node 脚本、没有 Node 时用 launcher 里的 Electron 运行时”的说明。
  • CHANGELOG.md 补上 0.1.26 条目。
  • 全量测试:310 通过 / 0 失败 / 3 跳过(跳过项仍是沙箱禁止 spawn 的三条)。

21. P1 第一批:安装 / 卸载 / 禁用(2026-09-10

针对“安装与卸载要简单”“升级后要能自动同步”两项:

内容 实现
安装不再依赖索引 ra3modxml.installAgentSkill 改为只读取 ws.projectRoot(可为空),无项目时仍可装到用户级目录
安装位置更灵活 默认 ~/.agents/skills、Claude Code、项目级 .agents/skills / .claude/skills、以及自定义文件夹(<folder>/ra3-mod-xml
安全卸载 新增 readSkillMarker / installedSkillStatus / uninstallRecordedSkills / forgetSkillInstallRecords:只删除带 marker 且路径匹配的目录,用户替换过的目录跳过并报告
禁用实时访问 新增 ra3modxml.disableAgentAccess:停服务器、清本窗口 instance/endpoint、从幸存实例重建发现文件,保留 skill 与 MCP 配置
完整卸载向导 新增 ra3modxml.uninstallAgentIntegration:可勾选停止实时访问 / 移除 Skill / 移除 MCP 配置 / 删除 launcher,确认后才执行
MCP 配置可回滚 新增 mcp-install.json 记录(installMcpServerConfigToFile)、removeMcpServerFromConfigFile(同时兼容 mcpServers 与 VS Code 的 servers,空容器自动删除)、uninstallMcpServerConfigs(记录 + 常规客户端路径)、removeLauncher
升级自动同步 激活时对比 skill-install.json 中的版本,有差异才调用 syncInstalledSkills 重写;无 marker 的目录不改写
本地化 新增 55 条 AI Agent 相关字符串到 l10n/bundle.l10n.jsonzh-cn(共 210 条/语言),命令标题加入 package.nls.*

测试:agentSetup.test.mjs 6 项、agentSkill.test.mjs 7 项(含 marker 防误删、 MCP 配置保留其他 server、空容器清理、记录回滚)。

22. P1 第二批:首装 / 升级提示(2026-09-10

  • 新增纯逻辑模块 src/agent/onboarding.ts(可单测):
    • AGENT_FEATURE_VERSION = "0.1.26"
    • shouldOfferAgentOnboarding(state, currentVersion): 首次安装提示一次;从早于 0.1.26 的版本升级提示一次; 已在带此功能的版本提示过则保持静默;dismissed 永久抑制;
    • compareVersions 处理 "0.1" < "0.1.26""0.1.26-beta" == "0.1.26"
  • extension.ts第一次索引完成后ws.onIndexUpdate)弹一次信息提示, 按钮为 Enable AI Agent access… / Learn more / Don't show again 无论用户选择还是忽略,都会把 informedVersion 写入 globalState,不会重复打扰。
  • 新增 4 条本地化字符串(两种语言各 214 条)。
  • test/onboarding.test.mjs5 个用例覆盖上述规则。

仍未处理(下一批)

  • VS Code 原生 chatSkills / mcpServerDefinitionProviders 接入(免 launcher / 免手写配置)。
  • Claude/Cursor 路径收敛为数据驱动预设 + configure --harness 稳定 CLI launcher(免 Node 路径发现)。
  • mcpServer.tsserverInfo.version 仍硬编码 0.1.0 startAgentLocalServer 仍有并发启动竞态。