`codegraph install` now detects and configures Kiro alongside the existing seven agents. Writes `mcpServers.codegraph` to `~/.kiro/settings/mcp.json` (global) or `./.kiro/settings/mcp.json` (local), plus a dedicated `~/.kiro/steering/codegraph.md` / `./.kiro/steering/codegraph.md` instruction file — Kiro's steering system loads every `*.md` file in `steering/` as agent context, so a dedicated file is the natural surface (no marker-based merging needed). Sibling MCP servers in `mcp.json` and unrelated steering files (`product.md`, `tech.md`, etc.) are preserved across install and uninstall. Validated end-to-end on macOS, Linux (Docker node:22-bookworm arm64), and Windows 11 (Parallels VM, Node 24): full installer-targets suite passes (132 tests) on all three platforms, and live install / idempotent re-run / uninstall round-trip works as expected. Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
8c69001289
commit
6558b585ed
@@ -390,6 +390,84 @@ describe('Installer targets — partial-state idempotency', () => {
|
||||
expect(body).not.toContain('codegraph_callers');
|
||||
});
|
||||
|
||||
it('kiro: install writes settings/mcp.json (mcpServers.codegraph) and steering/codegraph.md', () => {
|
||||
const kiro = getTarget('kiro')!;
|
||||
const result = kiro.install('global', { autoAllow: true });
|
||||
const mcp = path.join(tmpHome, '.kiro', 'settings', 'mcp.json');
|
||||
const steering = path.join(tmpHome, '.kiro', 'steering', 'codegraph.md');
|
||||
expect(result.files.some((f) => f.path === mcp)).toBe(true);
|
||||
expect(result.files.some((f) => f.path === steering)).toBe(true);
|
||||
|
||||
const cfg = JSON.parse(fs.readFileSync(mcp, 'utf-8'));
|
||||
expect(cfg.mcpServers.codegraph).toEqual({ type: 'stdio', command: 'codegraph', args: ['serve', '--mcp'] });
|
||||
|
||||
const md = fs.readFileSync(steering, 'utf-8');
|
||||
expect(md).toContain('codegraph_callers');
|
||||
expect(md).toContain('CodeGraph MCP server');
|
||||
});
|
||||
|
||||
it('kiro: install preserves a pre-existing sibling MCP server in mcp.json', () => {
|
||||
const kiro = getTarget('kiro')!;
|
||||
const mcp = path.join(tmpHome, '.kiro', 'settings', 'mcp.json');
|
||||
fs.mkdirSync(path.dirname(mcp), { recursive: true });
|
||||
fs.writeFileSync(mcp, JSON.stringify({
|
||||
mcpServers: { other: { command: 'uvx', args: ['other-server'] } },
|
||||
}, null, 2) + '\n');
|
||||
|
||||
kiro.install('global', { autoAllow: true });
|
||||
|
||||
const after = JSON.parse(fs.readFileSync(mcp, 'utf-8'));
|
||||
expect(after.mcpServers.other).toBeDefined();
|
||||
expect(after.mcpServers.codegraph).toBeDefined();
|
||||
});
|
||||
|
||||
it('kiro: uninstall strips codegraph but leaves sibling MCP servers intact', () => {
|
||||
const kiro = getTarget('kiro')!;
|
||||
const mcp = path.join(tmpHome, '.kiro', 'settings', 'mcp.json');
|
||||
fs.mkdirSync(path.dirname(mcp), { recursive: true });
|
||||
fs.writeFileSync(mcp, JSON.stringify({
|
||||
mcpServers: { other: { command: 'uvx', args: ['other-server'] } },
|
||||
}, null, 2) + '\n');
|
||||
|
||||
kiro.install('global', { autoAllow: true });
|
||||
kiro.uninstall('global');
|
||||
|
||||
const after = JSON.parse(fs.readFileSync(mcp, 'utf-8'));
|
||||
expect(after.mcpServers.other).toBeDefined();
|
||||
expect(after.mcpServers.codegraph).toBeUndefined();
|
||||
});
|
||||
|
||||
it('kiro: uninstall removes the steering codegraph.md file outright', () => {
|
||||
const kiro = getTarget('kiro')!;
|
||||
kiro.install('global', { autoAllow: true });
|
||||
const steering = path.join(tmpHome, '.kiro', 'steering', 'codegraph.md');
|
||||
expect(fs.existsSync(steering)).toBe(true);
|
||||
|
||||
kiro.uninstall('global');
|
||||
expect(fs.existsSync(steering)).toBe(false);
|
||||
});
|
||||
|
||||
it('kiro: uninstall leaves a sibling steering file (product.md) untouched', () => {
|
||||
const kiro = getTarget('kiro')!;
|
||||
const sibling = path.join(tmpHome, '.kiro', 'steering', 'product.md');
|
||||
fs.mkdirSync(path.dirname(sibling), { recursive: true });
|
||||
fs.writeFileSync(sibling, '# Product\n\nMy team practices.\n');
|
||||
|
||||
kiro.install('global', { autoAllow: true });
|
||||
kiro.uninstall('global');
|
||||
|
||||
expect(fs.existsSync(sibling)).toBe(true);
|
||||
expect(fs.readFileSync(sibling, 'utf-8')).toContain('My team practices.');
|
||||
});
|
||||
|
||||
it('kiro: local install writes ./.kiro/settings/mcp.json and ./.kiro/steering/codegraph.md', () => {
|
||||
const kiro = getTarget('kiro')!;
|
||||
const result = kiro.install('local', { autoAllow: true });
|
||||
const paths = result.files.map((f) => f.path.replace(/\\/g, '/'));
|
||||
expect(paths.some((p) => p.endsWith('/.kiro/settings/mcp.json'))).toBe(true);
|
||||
expect(paths.some((p) => p.endsWith('/.kiro/steering/codegraph.md'))).toBe(true);
|
||||
});
|
||||
|
||||
it('antigravity: install writes to LEGACY ~/.gemini/antigravity/mcp_config.json when no migration marker', () => {
|
||||
const antigravity = getTarget('antigravity')!;
|
||||
antigravity.install('global', { autoAllow: true });
|
||||
@@ -970,6 +1048,7 @@ describe('Installer targets — registry', () => {
|
||||
expect(getTarget('hermes')?.id).toBe('hermes');
|
||||
expect(getTarget('gemini')?.id).toBe('gemini');
|
||||
expect(getTarget('antigravity')?.id).toBe('antigravity');
|
||||
expect(getTarget('kiro')?.id).toBe('kiro');
|
||||
expect(getTarget('not-a-real-target')).toBeUndefined();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user