【免费下载链接】gsd-core
Git. Ship. Done - Core
导读
本文围绕 gsd-core 的一次运行时转换修复展开:当项目通过安装器把 Claude Code 形态的agents/*.md子代理文件转换给 Gemini 系运行时(Antigravity)使用,同时又为 Gemini CLI 的移除做收尾时,转换逻辑必须把 Claude 独有的Task/Agent/SlashCommand等“调度器(dispatcher)”工具从tools:授权列表中剔除,而不是把它们小写化成无效的agent等工具名写进目标 frontmatter。读完本文,你将掌握 gsd-core 运行时工件转换的完整链路、convertAntigravityToolName的排除语义、Gemini 运行时退役后的迁移路径,以及如何用测试验证转换产物不会携带非法工具授权。
一、变更背景:一条已归档 changeset 背后的完整修复
关联文档.changeset/archived/fix-3344-gemini-agent-tool.md是一份已归档的 changeset 片段,原文只有一句话:
Gemini and Antigravity agent conversion now drops Claude-only agent dispatcher tools instead of emitting invalid
agentpermissions.
它属于 gsd-core 的.changeset/archived/目录。根据 .changeset/archived/README.md,该目录存放的是gsd-core ≤ 1.3.1 时代已发布变更的存档片段,仅用于溯源(provenance),不会被render(scripts/changeset/cli.cjs)消费,也不应移回上层目录。虽然句子很短,但它在当前仓库中对应着一整条可验证的实现链路:
- 源码侧:
src/runtime-artifact-conversion.cts中 Antigravity 代理转换器convertClaudeAgentToAntigravityAgent与工具名映射convertAntigravityToolName; - 双实现侧:
bin/install.js中保留了一份同构的镜像实现(DEFECT.GENERATIVE-FIX注释明确要求两处行为必须同步); - 测试侧:tests/gemini-runtime-removed.test.cjs 与 tests/install-runtime-artifacts.test.cjs 对排除语义、重命名完整性和产物形状做了结构化断言。
二、跨运行时 Agent 转换的整体架构
gsd-core 以 Claude Code 的原生 Markdown 形态(frontmatter + 正文)作为所有agents/子代理的源格式,安装时按目标运行时把每个文件转换成对方能解析的形态。整套转换逻辑集中在src/runtime-artifact-conversion.cts(4226 行,从bin/install.js机械抽取而来),并遵循 ADR-1239(Phase B,issue #1679)的“描述符驱动”原则:
- 运行时的唯一事实来源是 capability registry:
capabilities/<runtime>/capability.json声明runtime.hostBehaviors、artifactLayout的converter名称等字段,源码通过_hostBehaviors(runtime)(见 runtime-artifact-conversion.cts)读取; - 非 Claude 运行时集合是动态推导的:
NON_CLAUDE_RUNTIMES直接取 registryruntimes的键集减去'claude'(runtime-artifact-conversion.cts),避免手工维护列表与 registry 漂移; - 每个运行时拥有独立的工具名映射表:例如 Claude→OpenCode(
claudeToOpencodeTools)、Claude→Kimi(claudeToKimiTools,要求完全限定的模块路径)、Claude→Kilo(claudeToKiloAgentPermissions,映射成permission:块)、Claude→Copilot(claudeToCopilotTools)等。
与本文主题最相关的是 Claude→Antigravity 的映射表claudeToAntigravityTools(runtime-artifact-conversion.cts):
| Claude Code 工具 | Antigravity 原生工具名 |
|---|---|
Read | view_file |
Write | write_file |
Edit | replace_file_content |
Bash | run_command |
Glob | glob |
Grep | grep_search |
WebSearch | google_web_search |
WebFetch | web_fetch |
TodoWrite | write_todos |
代码注释(issue #4705)特别说明:这些值是Antigravity 原生工具名(来自其官方 subagent 文档示例view_file/replace_file_content/grep_search/run_command),而非 Gemini CLI 方言;目录是非穷尽的,因此未映射的工具名保留“最佳已知授权”而非直接丢弃——丢弃会静默移除一项限制。
三、核心修复语义:为什么agent权限是无效的
这条 changeset 的措辞里有三个关键概念,需要逐一拆解。
3.1 什么是“Claude-only agent dispatcher tools”
在 Claude Code 形态的 agent frontmatter 中,tools:列表里出现Task与Agent表示该子代理可以再调度子代理(subagent dispatch);SlashCommand(即Skill)表示可以调用技能。它们是 Claude Code 生态独有的“调度器工具”,在其他运行时的工具方言里没有对等的内建工具:
- Antigravity(Gemini 工具方言)没有
skill内建工具,也没有ask_user工具; - Antigravity 的 agent 会被自动注册为可调用工具,无需在 frontmatter 里授权
Task/Agent; - MCP 工具(
mcp__*)由mcpServers配置在运行时自动发现,同样不需要在 frontmatter 里列出。
3.2 “instead of emitting invalidagentpermissions”指什么缺陷
修复前的错误行为是:对未显式映射的工具名做默认小写化回退(return claudeTool.toLowerCase())。于是Task会变成task,而Agent会变成agent——这个agent在 Antigravity/Gemini 方言的tools:校验中不是合法工具名,会触发 frontmatter 校验失败(Antigravity 报tools.N: Invalid tool name),甚至可能让整个 agent 加载中止。
换句话说:“保守保留”反而是 bug。把未知工具名小写化塞进目标 frontmatter,等于向一个不存在该工具名的运行时声明了无效权限;正确的做法是对 Claude 独有的调度器工具显式排除(返回null)。
3.3 排除清单与源码证据
convertAntigravityToolName(runtime-artifact-conversion.cts)的完整排除语义如下:
// MCP 工具:排除——运行时从 mcpServers 配置自动发现 if (claudeTool.startsWith('mcp__')) return null; // Task/Agent:排除——agent 会被自动注册为可调用工具 // AskUserQuestion:排除——Antigravity(Gemini 工具方言)没有 ask_user 工具 // Skill/SlashCommand:排除——没有 skill 内建工具,小写回退会产出无效名 if ( claudeTool === 'Task' || claudeTool === 'Agent' || claudeTool === 'AskUserQuestion' || claudeTool === 'ask_user' || claudeTool === 'Skill' || claudeTool === 'SlashCommand' ) { return null; } // 显式映射命中则返回原生名 if (claudeToAntigravityTools[claudeTool]) return claudeToAntigravityTools[claudeTool]; // 默认:小写化 return claudeTool.toLowerCase();tests/gemini-runtime-removed.test.cjs(#4727 的“rename 完整性”用例)对排除边界做了逐一断言(gemini-runtime-removed.test.cjs):
for (const excluded of ['mcp__anything', 'Task', 'Agent', 'AskUserQuestion', 'ask_user', 'Skill', 'SlashCommand']) { assert.strictEqual(mod.convertAntigravityToolName(excluded), null, `${excluded} must still be excluded ...`); } // 未映射名称仍走小写回退 assert.strictEqual(mod.convertAntigravityToolName('SomeOtherTool'), 'someothertool', ...);同一测试的 #4705 用例还验证了转换产物的形状(gemini-runtime-removed.test.cjs):
const input = ['---', 'name: gsd-x', 'description: d', 'tools: Read, Write, WebFetch, Skill', '---', '', 'body'].join('\n'); const result = convertClaudeAgentToAntigravityAgent(input); const toolsBlock = result.slice(result.indexOf('tools:')).split('\n').filter((l) => l.startsWith('- ')); assert.ok(toolsBlock.includes('- view_file'), 'Read → view_file (native name, #4705)'); assert.ok(toolsBlock.includes('- write_file'), 'Write → write_file'); assert.ok(toolsBlock.includes('- web_fetch'), 'WebFetch → web_fetch'); assert.ok(!toolsBlock.some((l) => /\bskill\b/.test(l)), 'Skill is still excluded (would be an invalid backend tool name)');四、转换器的完整行为:convertClaudeAgentToAntigravityAgent
从 Claude agent 到 Antigravity agent 的完整转换由 runtime-artifact-conversion.cts 中的convertClaudeAgentToAntigravityAgent(content, isGlobal)承担(bin/install.js第 2516 行起有一份同构镜像):
- 先做内容级路径/品牌改写:调用
convertClaudeToAntigravityContent,把~/.claude/、$HOME/.claude/按安装作用域改写为 Antigravity 路径。全局安装时(#3738):~/.claude/skills/→~/.gemini/config/skills/(Antigravity 实际扫描的目录),其余~/.claude/→~/.gemini/antigravity/;本地安装时./.claude/→./.agents/(runtime-artifact-conversion.cts)。 - 解析 frontmatter:提取
name、description、color与tools原始字段。 - 逐项映射工具:按逗号切分
tools:后逐个调用convertAntigravityToolName,再用.filter(Boolean)丢弃返回null的调度器/MCP/无方言工具。 - 重建 frontmatter(#4705 起):
tools输出为YAML 序列(每行一个- 名称),而不是 Claude 的单行逗号字符串;当映射结果为空时输出tools: [],让“所有工具都被过滤”的 agent 保持显式受限,而不是继承全量工具集。 - 保留
color、引用 description(#2876:description 统一用yamlQuote加引号,防止[BETA]等 YAML 流指示符破坏解析器)。
const toolsBlock = mappedTools.length > 0 ? `tools:\n${mappedTools.map((t) => `- ${t}`).join('\n')}\n` : 'tools: []\n'; let fm = `---\nname: ${name}\ndescription: ${yamlQuote(description)}\n${toolsBlock}`; if (color) fm += `color: ${color}\n`; fm += '---';对比其他运行时可见该转换的“最小 frontmatter”倾向:Cursor/Windsurf/Augment/Trae/Codebuddy 的 agent 转换器只保留name+description(如convertClaudeAgentToCodebuddyAgent,runtime-artifact-conversion.cts),而 Qwen 因文档声明兼容 Claude 字段会保留tools与color(convertClaudeAgentToQwenAgent);ZCode 则是“Claude 形状”逐字节保留,仅剥离mcp__*授权(convertClaudeAgentToZcodeAgent,runtime-artifact-conversion.cts)。Antigravity 介于两者之间:保留tools序列与color,但所有工具名必须是目标方言。
五、双子星背景:Gemini 运行时退役与 Antigravity 重定向
这条 changeset 之所以把 Gemini 与 Antigravity 并列,是因为两条运行时主线在 2026 年发生了合并:
- Gemini CLI 被 Google 于 2026-06-18 日落(sunset),Antigravity CLI 是官方继任者(issue #1928,gsd-core 1.8.0 起移除
gemini运行时)。 --gemini被改造成显式弃用重定向而非静默别名:单独使用--gemini时打印日落公告、以退出码 1 结束且不安装任何运行时;--gemini与其他合法运行时并存时打印公告但仍安装另一运行时;--gemini --help仍正常输出 usage。全部行为由 tests/gemini-runtime-removed.test.cjs 以隔离 HOME 的 spawn 测试钉死。gemini从canonicalizeRuntimeName、getRuntimeLabel、getGlobalConfigHomeFragment、getProjectInstructionFile等运行时命名策略表面全部移除,对标签/配置片段表面直接抛RetiredRuntimeError(gemini-runtime-removed.test.cjs)。- 工具映射经历两次演进:#4727 把
claudeToGeminiTools/convertGeminiToolName原地改名为claudeToAntigravityTools/convertAntigravityToolName(纯标识符重命名,键与形状不变,测试断言旧名不再导出);#4705 再把值从 Gemini CLI 方言换成 Antigravity 原生工具名——因为 Antigravity 的工具校验不认 Gemini CLI 方言名,错误的名字可能挂起子代理(gemini-runtime-removed.test.cjs)。
六、Gemini 家族契约的保留(负空间保护)
移除gemini运行时不等于删除一切含 “gemini” 的字符串。测试中的“负空间(negative space)”断言明确保护 Antigravity 的 Gemini 家族契约(gemini-runtime-removed.test.cjs):
- Antigravity 的配置目录仍嵌套在
~/.gemini之下:configHome.parent为.gemini、configHome.name为antigravity; - 其 hook 事件方言仍是
hookEvents: 'gemini'(BeforeTool/AfterTool等),项目指令文件仍是GEMINI.md; - 全局 skills/agents 安装到
~/.gemini/config(#3738 起),这是 Antigravity 实际扫描的目录; - Google 的模型 ID(如
gemini-3.1-pro-preview、gemini-3-flash、gemini-2.5-flash-lite)属于模型轴(provider axis),保留在model-catalog.json的 google provider preset 中,与运行时轴(runtime axis)严格区分。
由此,一个“把 gemini 全部字符串替换成 antigravity”的粗暴扫描会被测试立刻拦下——它会把 Antigravity 的真实磁盘契约改坏。
七、同族修复模式:其他运行时如何处置 Claude 专属工具
这条 changeset 的“排除而非错误映射”思路并非孤例,src/runtime-artifact-conversion.cts中每个非 Claude 运行时转换器都面对“Claude 独有的工具怎么处置”的问题,处置策略各不相同:
- Kimi:
convertKimiToolName对mcp__*返回null(Kimi agent YAML 不承载 MCP 配置),未映射工具同样返回null并产出kimi_unsupported_tool诊断;Task/Agent则映射为kimi_cli.tools.agent:Agent(Kimi 的模块路径形式,runtime-artifact-conversion.cts)。 - Kilo:通过
claudeToKiloAgentPermissions把Task映射为task权限,再按kiloAgentPermissionOrder输出permission:块(允许/拒绝矩阵,runtime-artifact-conversion.cts)。 - Copilot:
claudeToCopilotTools把Task映射为agent权限(runtime-artifact-conversion.cts),并在 agent 转换时输出 JSON 数组形式的tools:(CONV-04/CONV-05)。 - ZCode:Claude 形状逐字节保留,只把
mcp__*授权剥离——因为 ZCode 的 dispatcher 把每个mcp__<server>__*条目当作必需 MCP 服务器,未连接时直接以CONFIGURATION_ERROR: Required MCP server is not connected硬失败子代理生成(runtime-artifact-conversion.cts);全部工具被剥离时干脆删除tools:键,让 agent 继承全量工具集(degrade-gracefully)。
可见“dispatcher 工具”在转换层是常态化的难点:Claude 的Task/Agent/SlashCommand每个目标运行时都需要显式决策——映射成等价能力、显式排除、还是按目标方言重命名。
八、如何验证与自测(对当前仓库可复现)
读者可以直接用仓库内的测试与源码做验证(仓库只读,仅运行验证命令,不做任何修改):
- 单元验证转换排除语义:运行
node --test tests/gemini-runtime-removed.test.cjs,重点看 “#4705 the Antigravity-native tool vocabulary powers Antigravity agent conversion” 与 “#4727 the rename is complete” 两个用例——前者断言Skill不进入产物、Read→view_file;后者断言Task/Agent/AskUserQuestion/Skill/SlashCommand/mcp__*全部返回null。 - 全量转换面验证:运行
node --test tests/install-runtime-artifacts.test.cjs,该文件覆盖各运行时的命令/技能/agent 转换器(含 antigravity 的~/.claude→~/.gemini/antigravity路径改写,install-runtime-artifacts.test.cjs),以及#2095要求的输出奇偶校验(tests/runtime-converters.test.cjs)。 - 黑盒安装验证:
tests/gemini-runtime-removed.test.cjs的#1928系列以隔离 HOME 的spawnSync运行真实安装器,断言--gemini的日落重定向、退出码、无堆栈泄漏、以及 Claude 安装不被误卸载。
九、从这条 changeset 可提炼的工程结论
- 转换器必须对“目标方言不存在的能力”做显式排除,而不是用小写化等启发式“保守保留”——在 frontmatter 校验严格的主机(Antigravity)上,无效工具名会让整个 agent 无法加载。
- 同一转换逻辑存在双实现时必须有输出奇偶测试:
src/runtime-artifact-conversion.cts与bin/install.js的镜像副本靠#2095的输出奇偶测试兜底(源码注释DEFECT.GENERATIVE-FIX明确警告“镜像任何行为变更到两处”)。 - 运行时身份来自 registry,而不是硬编码:
NON_CLAUDE_RUNTIMES与_hostBehaviors都从capabilities/*/capability.json推导,新增/移除运行时不需要同步维护工具函数里的字面量。 - 退役一个运行时要用“负空间测试”保护继任者:
gemini移除的每条断言都是结构化的(registry 键、描述符字段、表行),并显式断言 Antigravity 的~/.gemini/*契约、GEMINI.md、hookEvents: 'gemini'必须原样保留。
参考路径速查
- 变更记录:.changeset/archived/fix-3344-gemini-agent-tool.md
- 归档机制说明:.changeset/archived/README.md
- 核心转换源码:src/runtime-artifact-conversion.cts(
convertAntigravityToolName见 L2479-L2507,convertClaudeAgentToAntigravityAgent见 L2566-L2592,工具映射表见 L2460-L2470) - 安装器镜像实现:bin/install.js(L1728、L2516-L2540)
- 回归测试:tests/gemini-runtime-removed.test.cjs、tests/install-runtime-artifacts.test.cjs
- 运行时描述符:capabilities/antigravity/capability.json
【免费下载链接】gsd-core
Git. Ship. Done - Core
相关推荐
Zed Agent 工具权限完全指南:用 agent.tool_permissions 精细控制 Agent 工具执行
Zed Agent 工具权限完全指南:用 agent.tool_permissions 精细控制 Agent 工具执行 本文基于 Zed 官方文档 tool p
开发工具代码编辑器桌面应用gsd-core 运行时感知模型解析修复:`model_profile_overrides` 与 `dynamic_routing` 如何真正作用于实际派生的 Agent
gsd core 运行时感知模型解析修复: model_profile_overrides 与 dynamic_routing 如何真正作用于实际派生的 Age
gsd-core 运行时感知的指令文件输出:从 3163 看 generate-claude-md 如何让 Codex 项目正确写入 AGENTS.md
gsd core 运行时感知的指令文件输出:从 3163 看 generate claude md 如何让 Codex 项目正确写入 AGENTS.md 本文围
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考