fix(#4705): emit Antigravity-native tool names as a YAML sequence (#4822)

* test(#4705): add failing-first coverage for native Antigravity tool sequences

* fix(#4705): emit Antigravity-native tool names as a YAML sequence

convertClaudeAgentToAntigravityAgent and the installer's twin emitted
Gemini CLI tool names as a comma-separated scalar. Antigravity's
documented subagent contract (antigravity.google/docs/subagents) wants a
YAML sequence of native names — view_file, grep_search, run_command,
replace_file_content are the documented examples, and wrong or malformed
grants can hang the subagent per Antigravity's own warning.

Map values move to the native vocabulary where documented (Read ->
view_file, Edit -> replace_file_content, Bash -> run_command, Grep ->
grep_search); undocumented entries keep their best-known grant rather
than being dropped (dropping would silently remove a restriction). The
emitter writes one '- name' item per line; an agent whose every tool was
filtered emits an explicit tools: [] instead of an empty scalar.
Pre-existing pins updated to the native vocabulary.

* test(#4705): update the #4727 map-value pin to the Antigravity-native vocabulary

The #4727-era pin held the map VALUES at the Gemini CLI dialect on the
belief that Antigravity speaks it; the confirmed bug #4705 (with
Antigravity's own documented subagent contract) supersedes that for the
four documented names. Key/shape pinning is preserved; only the values
move.

* docs(#4705): backfill changeset PR number

---------

Co-authored-by: sim <sim@local>
This commit is contained in:
Tom Boucher
2026-09-17 08:12:18 -04:00
committed by GitHub
parent be1b76dddd
commit 7d0c6339d0
5 changed files with 96 additions and 30 deletions

View File

@@ -2431,13 +2431,20 @@ const claudeToCopilotTools = {
// Tool name mapping from Claude Code to Antigravity
// Antigravity uses Gemini's snake_case built-in tool names
// #4705: values are Antigravity-NATIVE tool names per the documented subagent
// contract (antigravity.google/docs/subagents: view_file, replace_file_content,
// grep_search, run_command are the documented examples; the catalog is
// non-exhaustive, so unmapped entries keep their best-known grant rather than
// being dropped — dropping would silently remove a restriction). The old
// values were Gemini CLI dialect names, which Antigravity's tool validation
// does not document and may hang the subagent on.
const claudeToAntigravityTools = {
Read: 'read_file',
Read: 'view_file',
Write: 'write_file',
Edit: 'replace',
Bash: 'run_shell_command',
Edit: 'replace_file_content',
Bash: 'run_command',
Glob: 'glob',
Grep: 'search_file_content',
Grep: 'grep_search',
WebSearch: 'google_web_search',
WebFetch: 'web_fetch',
TodoWrite: 'write_todos',
@@ -2552,7 +2559,13 @@ function convertClaudeAgentToAntigravityAgent(content, isGlobal = false) {
const mappedTools = claudeTools.map(t => convertAntigravityToolName(t)).filter(Boolean);
// #2876: quote description for the same reason as the skill variant.
let fm = `---\nname: ${name}\ndescription: ${yamlQuote(description)}\ntools: ${mappedTools.join(', ')}\n`;
// #4705: tools is a YAML SEQUENCE of native names (one `- name` item per
// line), not a comma-separated scalar. Empty mapped set -> `tools: []` so an
// agent whose every tool was filtered stays explicitly restricted.
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 += '---';