fix: universal agent name replacement for non-Claude runtimes (#766) (#1195)

* fix: universal agent name replacement for non-Claude runtimes (#766)

Adds neutralizeAgentReferences() shared function that all non-Claude
runtime converters call to replace Claude-specific references:

- 'Claude' (standalone agent name) → 'the agent'
- 'CLAUDE.md' → runtime-specific file (AGENTS.md, GEMINI.md, COPILOT.md)
- Removes 'Do NOT load full AGENTS.md' (harmful for AGENTS.md runtimes)

Preserves: 'Claude Code' (product), 'Claude Opus/Sonnet/Haiku' (models),
'claude-' prefixes (packages, CSS classes).

Integrated into: OpenCode, Gemini, Copilot, Antigravity, and Codex
converters. Claude Code converter unchanged (references are correct).

Includes 7 new tests covering all replacement rules.

Closes #766

* fix: use copilot-instructions.md instead of COPILOT.md for Copilot runtime

Addresses review from Solvely-Colin: Copilot's actual instruction file
is copilot-instructions.md, not COPILOT.md. The neutralizer was mapping
CLAUDE.md -> COPILOT.md which would reference a non-existent file.

- install.js: pass 'copilot-instructions.md' to neutralizeAgentReferences
- runtime-converters.test.cjs: update test to validate correct filename
This commit is contained in:
Tom Boucher
2026-03-18 19:20:58 -04:00
committed by GitHub
parent 973c6b267d
commit 5fd384f336
2 changed files with 96 additions and 1 deletions

View File

@@ -17,6 +17,7 @@ process.env.GSD_TEST_MODE = '1';
const {
convertClaudeToOpencodeFrontmatter,
convertClaudeToGeminiAgent,
neutralizeAgentReferences,
} = require('../bin/install.js');
// Sample Claude agent frontmatter (matches actual GSD agent format)
@@ -183,3 +184,56 @@ Use \${PHASE} in shell examples.
assert.ok(!result.includes('${PHASE}'), 'removes Gemini template-string pattern');
});
});
// ─── neutralizeAgentReferences (#766) ─────────────────────────────────────────
describe('neutralizeAgentReferences', () => {
test('replaces standalone Claude with "the agent"', () => {
const input = 'Claude handles these decisions. Claude should read the file.';
const result = neutralizeAgentReferences(input, 'AGENTS.md');
assert.ok(!result.includes('Claude handles'), 'standalone Claude replaced');
assert.ok(result.includes('the agent handles'), 'replaced with "the agent"');
});
test('preserves Claude Code (product name)', () => {
const input = 'This is a Claude Code bug. Use Claude Code settings.';
const result = neutralizeAgentReferences(input, 'AGENTS.md');
assert.ok(result.includes('Claude Code bug'), 'Claude Code preserved');
assert.ok(result.includes('Claude Code settings'), 'Claude Code preserved');
});
test('preserves Claude model names', () => {
const input = 'Use Claude Opus for planning. Claude Sonnet for execution. Claude Haiku for research.';
const result = neutralizeAgentReferences(input, 'AGENTS.md');
assert.ok(result.includes('Claude Opus'), 'Opus preserved');
assert.ok(result.includes('Claude Sonnet'), 'Sonnet preserved');
assert.ok(result.includes('Claude Haiku'), 'Haiku preserved');
});
test('replaces CLAUDE.md with runtime instruction file', () => {
const input = 'Read CLAUDE.md for project instructions. Check ./CLAUDE.md if exists.';
const result = neutralizeAgentReferences(input, 'AGENTS.md');
assert.ok(result.includes('AGENTS.md'), 'CLAUDE.md -> AGENTS.md');
assert.ok(!result.includes('CLAUDE.md'), 'no CLAUDE.md remains');
});
test('uses different instruction file per runtime', () => {
const input = 'Read CLAUDE.md for instructions.';
assert.ok(neutralizeAgentReferences(input, 'GEMINI.md').includes('GEMINI.md'));
assert.ok(neutralizeAgentReferences(input, 'copilot-instructions.md').includes('copilot-instructions.md'));
assert.ok(neutralizeAgentReferences(input, 'AGENTS.md').includes('AGENTS.md'));
});
test('removes AGENTS.md load-blocking instruction', () => {
const input = 'Do NOT load full `AGENTS.md` files — they contain agent definitions.';
const result = neutralizeAgentReferences(input, 'AGENTS.md');
assert.ok(!result.includes('Do NOT load full'), 'blocking instruction removed');
});
test('preserves claude- prefixes (CSS classes, package names)', () => {
const input = 'The claude-ctx session and claude-code package.';
const result = neutralizeAgentReferences(input, 'AGENTS.md');
assert.ok(result.includes('claude-ctx'), 'claude- prefix preserved');
assert.ok(result.includes('claude-code'), 'claude-code preserved');
});
});