refactor: drop 12 runtimes, keep Claude, Codex, OpenCode, Cursor, ZCode, Antigravity

Removes kilo, kimi, kimi-code, copilot, windsurf, augment, trae, qwen, hermes,
cline, codebuddy and pi end to end: capability descriptors, installer branches
and converters (bin/install.js 14.9k -> 11.2k lines), TypeScript converters,
hook surfaces and runtime homes, review lanes qwen/kimi-code, the two pi
migrations, Kimi payload normalization in the hook guards, dead hostBehaviors
vocabulary, launcher home probes, fixtures, runtime-specific tests and the
prose that presented them as supported.

Installer output for the six kept runtimes is byte-identical to before the
prune. The Kimi tool-vocabulary tests in workflow-guard, read-guard and
read-injection-scanner are left in place pending a decision.
This commit is contained in:
Jakub Zych
2026-10-06 20:02:40 +02:00
parent 12ee75a509
commit 6cfa0c55d2
509 changed files with 1869 additions and 47737 deletions

View File

@@ -24,10 +24,7 @@ const { cleanup } = require('./helpers.cjs');
const ROOT = path.join(__dirname, '..');
const {
getGlobalConfigDir,
getGlobalSkillsBase,
resolveAntigravityGlobalDir,
resolveKimiGlobalDir,
resolveKimiHooksTomlDir,
resolveConfigHomeFromDescriptor,
resolveSkillsBaseFromDescriptor,
detectAntigravityDirAmbiguity,
@@ -57,11 +54,9 @@ function withEnv(overrides, fn) {
// All env vars for all runtimes — cleared in each test that calls getGlobalConfigDir directly
const ALL_ENV_KEYS = [
'CLAUDE_CONFIG_DIR', 'CURSOR_CONFIG_DIR', 'CODEX_HOME',
'GROK_AGENTS_HOME', 'COPILOT_CONFIG_DIR', 'COPILOT_HOME', 'ANTIGRAVITY_CONFIG_DIR',
'WINDSURF_CONFIG_DIR', 'AUGMENT_CONFIG_DIR', 'TRAE_CONFIG_DIR', 'QWEN_CONFIG_DIR',
'HERMES_HOME', 'CODEBUDDY_CONFIG_DIR', 'CLINE_CONFIG_DIR', 'KIMI_CONFIG_DIR',
'OPENCODE_CONFIG_DIR', 'OPENCODE_CONFIG', 'KILO_CONFIG_DIR', 'KILO_CONFIG',
'XDG_CONFIG_HOME', 'PI_CODING_AGENT_DIR',
'GROK_AGENTS_HOME', 'ANTIGRAVITY_CONFIG_DIR',
'OPENCODE_CONFIG_DIR', 'OPENCODE_CONFIG',
'XDG_CONFIG_HOME',
];
function clearAllEnvKeys() {
@@ -91,27 +86,14 @@ const GOLDEN_DEFAULTS = {
cursor: path.join(HOME, '.cursor'),
codex: path.join(HOME, '.codex'),
grok: path.join(HOME, '.agents'),
copilot: path.join(HOME, '.copilot'),
antigravity: path.join(HOME, '.gemini', 'antigravity'), // probe-miss → first candidate
windsurf: path.join(HOME, '.codeium', 'windsurf'),
augment: path.join(HOME, '.augment'),
trae: path.join(HOME, '.trae'),
qwen: path.join(HOME, '.qwen'),
hermes: path.join(HOME, '.hermes'),
codebuddy: path.join(HOME, '.codebuddy'),
cline: path.join(HOME, '.cline'),
opencode: path.join(HOME, '.config', 'opencode'),
kilo: path.join(HOME, '.config', 'kilo'),
zcode: path.join(HOME, '.zcode'),
pi: path.join(HOME, '.pi', 'agent'), // dot-home-nested, no probe (like windsurf)
};
// ── GOLDEN DEFAULTS ────────────────────────────────────────────────────────────
describe('descriptor-driven equivalence: defaults (no env vars, no probe hits)', () => {
// kimi is excluded: its default depends on real filesystem probing (probe-hit/miss
// vary by machine). kimi probe scenarios are covered in the generic-agents-root suite
// with injected existsSync.
// antigravity is excluded: it also depends on real fs probing (probe candidates
// ~/.gemini/antigravity, ~/.gemini/antigravity-ide, ~/.gemini/antigravity-cli);
// a machine that has antigravity-ide or antigravity-cli but not antigravity gets a
@@ -148,18 +130,8 @@ describe('descriptor-driven equivalence: env-var overrides', () => {
{ runtime: 'cursor', envKey: 'CURSOR_CONFIG_DIR', value: '/custom/cursor' },
{ runtime: 'codex', envKey: 'CODEX_HOME', value: '/custom/codex' },
{ runtime: 'grok', envKey: 'GROK_AGENTS_HOME', value: '/custom/grok' },
{ runtime: 'augment', envKey: 'AUGMENT_CONFIG_DIR', value: '/custom/augment' },
{ runtime: 'trae', envKey: 'TRAE_CONFIG_DIR', value: '/custom/trae' },
{ runtime: 'qwen', envKey: 'QWEN_CONFIG_DIR', value: '/custom/qwen' },
{ runtime: 'hermes', envKey: 'HERMES_HOME', value: '/custom/hermes' },
{ runtime: 'codebuddy', envKey: 'CODEBUDDY_CONFIG_DIR', value: '/custom/codebuddy' },
{ runtime: 'cline', envKey: 'CLINE_CONFIG_DIR', value: '/custom/cline' },
{ runtime: 'windsurf', envKey: 'WINDSURF_CONFIG_DIR', value: '/custom/windsurf' },
{ runtime: 'antigravity', envKey: 'ANTIGRAVITY_CONFIG_DIR', value: '/custom/antigravity' },
{ runtime: 'kimi', envKey: 'KIMI_CONFIG_DIR', value: '/custom/kimi' },
{ runtime: 'opencode', envKey: 'OPENCODE_CONFIG_DIR', value: '/custom/opencode' },
{ runtime: 'kilo', envKey: 'KILO_CONFIG_DIR', value: '/custom/kilo' },
{ runtime: 'pi', envKey: 'PI_CODING_AGENT_DIR', value: '/custom/pi-agent' },
];
for (const { runtime, envKey, value } of cases) {
@@ -173,28 +145,6 @@ describe('descriptor-driven equivalence: env-var overrides', () => {
}
});
}
// copilot: COPILOT_CONFIG_DIR takes precedence over COPILOT_HOME
test('copilot: COPILOT_CONFIG_DIR override (first env wins)', () => {
const saved = clearAllEnvKeys();
process.env['COPILOT_CONFIG_DIR'] = '/custom/copilot-dir';
process.env['COPILOT_HOME'] = '/should/not/win';
try {
assert.strictEqual(String(getGlobalConfigDir('copilot')).replace(/\\/g, '/'), '/custom/copilot-dir');
} finally {
restoreEnvKeys(saved);
}
});
test('copilot: COPILOT_HOME fallback when COPILOT_CONFIG_DIR absent', () => {
const saved = clearAllEnvKeys();
process.env['COPILOT_HOME'] = '/custom/copilot-home';
try {
assert.strictEqual(String(getGlobalConfigDir('copilot')).replace(/\\/g, '/'), '/custom/copilot-home');
} finally {
restoreEnvKeys(saved);
}
});
});
// ── GOLDEN TILDE EXPANSION ─────────────────────────────────────────────────────
@@ -205,60 +155,6 @@ describe('descriptor-driven equivalence: tilde expansion in env overrides', () =
assert.strictEqual(getGlobalConfigDir('claude'), path.join(HOME, 'foo'));
});
});
test('kimi: KIMI_CONFIG_DIR=~/kimi expands to homedir/kimi', () => {
withEnv({ KIMI_CONFIG_DIR: '~/kimi' }, () => {
assert.strictEqual(getGlobalConfigDir('kimi'), path.join(HOME, 'kimi'));
});
});
test('pi: PI_CODING_AGENT_DIR=~/pi-agent expands to homedir/pi-agent', () => {
withEnv({ PI_CODING_AGENT_DIR: '~/pi-agent' }, () => {
assert.strictEqual(getGlobalConfigDir('pi'), path.join(HOME, 'pi-agent'));
});
});
});
// ── #3023: PI_CODING_AGENT_DIR override — pi's own upstream config-dir env var ──
//
// pi's real source (`packages/coding-agent/src/config.ts`) reads
// `PI_CODING_AGENT_DIR` to override its GLOBAL agent dir outright — the whole
// `~/.pi/agent` path, not just the `.pi` segment. That is exactly the
// dot-home-nested env-override shape `resolveConfigHomeFromDescriptor` already
// implements for antigravity/windsurf: `env[0]` set -> `expandTilde(value)`
// returned directly, no join with parent/name. Adding the var to pi's
// `capabilities/pi/capability.json` `configHome.env` was the whole fix; no new
// resolver branch was needed.
describe('#3023: pi PI_CODING_AGENT_DIR — dot-home-nested override semantics', () => {
test('unset -> default ~/.pi/agent', () => {
const result = resolveConfigHomeFromDescriptor(
{ kind: 'dot-home-nested', name: 'agent', parent: '.pi', env: ['PI_CODING_AGENT_DIR'] },
{ env: {}, home: '/home/u', existsSync: () => false },
);
assert.strictEqual(result, path.join('/home/u', '.pi', 'agent'));
});
test('set -> full override wins outright (whole path, not joined with parent/name)', () => {
const result = resolveConfigHomeFromDescriptor(
{ kind: 'dot-home-nested', name: 'agent', parent: '.pi', env: ['PI_CODING_AGENT_DIR'] },
{ env: { PI_CODING_AGENT_DIR: '/custom/pi-agent' }, home: '/home/u', existsSync: () => false },
);
assert.strictEqual(result, '/custom/pi-agent');
});
// Tilde expansion against an INJECTED home (not the real os.homedir()) is
// covered below in "expandTilde honors an injected opts.home" — the fix for
// the bug where `expandTilde` ignored `resolveConfigHomeFromDescriptor`'s
// `opts.home` and always resolved `~` against the real os.homedir(), shared
// by every dot-home/dot-home-nested/xdg/generic-agents-root env override.
test('empty-string env value falls back to the default, never redirects to a bogus path', () => {
const result = resolveConfigHomeFromDescriptor(
{ kind: 'dot-home-nested', name: 'agent', parent: '.pi', env: ['PI_CODING_AGENT_DIR'] },
{ env: { PI_CODING_AGENT_DIR: '' }, home: '/home/u', existsSync: () => false },
);
assert.strictEqual(result, path.join('/home/u', '.pi', 'agent'));
});
});
// ── expandTilde honors an injected opts.home (not just the real os.homedir()) ─
@@ -283,15 +179,6 @@ describe('expandTilde honors an injected opts.home (regression)', () => {
assert.notStrictEqual(result, path.join(HOME, 'custom'));
});
test('pi (dot-home-nested): PI_CODING_AGENT_DIR=~/custom + injected home → resolves under injected home, not os.homedir()', () => {
const result = resolveConfigHomeFromDescriptor(
{ kind: 'dot-home-nested', name: 'agent', parent: '.pi', env: ['PI_CODING_AGENT_DIR'] },
{ env: { PI_CODING_AGENT_DIR: '~/custom' }, home: INJECTED_HOME },
);
assert.strictEqual(result, path.join(INJECTED_HOME, 'custom'));
assert.notStrictEqual(result, path.join(HOME, 'custom'));
});
test('claude: absolute env override + injected home → unchanged (tilde expansion not triggered)', () => {
const result = resolveConfigHomeFromDescriptor(
{ kind: 'dot-home', name: '.claude', env: ['CLAUDE_CONFIG_DIR'] },
@@ -300,14 +187,6 @@ describe('expandTilde honors an injected opts.home (regression)', () => {
assert.strictEqual(result, '/absolute/custom');
});
test('pi: absolute env override + injected home → unchanged (tilde expansion not triggered)', () => {
const result = resolveConfigHomeFromDescriptor(
{ kind: 'dot-home-nested', name: 'agent', parent: '.pi', env: ['PI_CODING_AGENT_DIR'] },
{ env: { PI_CODING_AGENT_DIR: '/absolute/custom' }, home: INJECTED_HOME },
);
assert.strictEqual(result, '/absolute/custom');
});
test('claude: no injected home → still resolves under the real os.homedir() (no behavior change for production callers)', () => {
assert.strictEqual(
resolveConfigHomeFromDescriptor(
@@ -318,27 +197,12 @@ describe('expandTilde honors an injected opts.home (regression)', () => {
);
});
test('pi: no injected home → still resolves under the real os.homedir() (no behavior change for production callers)', () => {
assert.strictEqual(
resolveConfigHomeFromDescriptor(
{ kind: 'dot-home-nested', name: 'agent', parent: '.pi', env: ['PI_CODING_AGENT_DIR'] },
{ env: { PI_CODING_AGENT_DIR: '~/custom' } },
),
path.join(HOME, 'custom'),
);
});
test('claude: no injected home, via getGlobalConfigDir (real end-to-end seam) → real os.homedir()', () => {
withEnv({ CLAUDE_CONFIG_DIR: '~/custom' }, () => {
assert.strictEqual(getGlobalConfigDir('claude'), path.join(HOME, 'custom'));
});
});
test('pi: no injected home, via getGlobalConfigDir (real end-to-end seam) → real os.homedir()', () => {
withEnv({ PI_CODING_AGENT_DIR: '~/custom' }, () => {
assert.strictEqual(getGlobalConfigDir('pi'), path.join(HOME, 'custom'));
});
});
});
// ── #3023 review finding 1: whitespace-only env override must fall back ──────
@@ -362,7 +226,6 @@ describe('expandTilde honors an injected opts.home (regression)', () => {
// byte-identical". Only entirely-blank values are rejected.
describe('#3023 review finding 1: whitespace-only env override falls back to default (regression)', () => {
const CLAUDE_DESCRIPTOR = { kind: 'dot-home', name: '.claude', env: ['CLAUDE_CONFIG_DIR'] };
const PI_DESCRIPTOR = { kind: 'dot-home-nested', name: 'agent', parent: '.pi', env: ['PI_CODING_AGENT_DIR'] };
describe('claude (dot-home)', () => {
test('whitespace-only env value falls back to the descriptor default, never the literal whitespace string', () => {
@@ -407,54 +270,6 @@ describe('#3023 review finding 1: whitespace-only env override falls back to def
});
});
describe('pi (dot-home-nested)', () => {
test('whitespace-only env value falls back to the descriptor default, never the literal whitespace string', () => {
const result = resolveConfigHomeFromDescriptor(PI_DESCRIPTOR, {
env: { PI_CODING_AGENT_DIR: ' ' },
home: '/home/u',
existsSync: () => false,
});
assert.strictEqual(result, path.join('/home/u', '.pi', 'agent'));
assert.notStrictEqual(result, ' ');
});
test('empty-string env value falls back to the default (existing behavior preserved)', () => {
const result = resolveConfigHomeFromDescriptor(PI_DESCRIPTOR, {
env: { PI_CODING_AGENT_DIR: '' },
home: '/home/u',
existsSync: () => false,
});
assert.strictEqual(result, path.join('/home/u', '.pi', 'agent'));
});
test('unset env value falls back to the default', () => {
const result = resolveConfigHomeFromDescriptor(PI_DESCRIPTOR, {
env: {},
home: '/home/u',
existsSync: () => false,
});
assert.strictEqual(result, path.join('/home/u', '.pi', 'agent'));
});
test('env value with interior spaces resolves under the injected home, spaces intact (guard is not over-broad)', () => {
const result = resolveConfigHomeFromDescriptor(PI_DESCRIPTOR, {
env: { PI_CODING_AGENT_DIR: '~/My Agent Dir' },
home: '/home/u',
existsSync: () => false,
});
assert.strictEqual(result, path.join('/home/u', 'My Agent Dir'));
});
test('normal absolute path env value is unchanged', () => {
const result = resolveConfigHomeFromDescriptor(PI_DESCRIPTOR, {
env: { PI_CODING_AGENT_DIR: '/custom/pi-agent' },
home: '/home/u',
existsSync: () => false,
});
assert.strictEqual(result, '/custom/pi-agent');
});
});
// Full branch coverage: the same whitespace-only guard applies to every
// env-override consumption site in resolveConfigHomeFromDescriptor, not
// just dot-home/dot-home-nested. Each of these fails before the fix and
@@ -556,48 +371,6 @@ describe('descriptor-driven equivalence: xdg runtimes (opencode, kilo)', () => {
}
});
// kilo
test('kilo: KILO_CONFIG (file-path) → dirname', () => {
const saved = clearAllEnvKeys();
process.env['KILO_CONFIG'] = '/home/u/cfg/kilo.json';
try {
assert.strictEqual(String(getGlobalConfigDir('kilo')).replace(/\\/g, '/'), '/home/u/cfg');
} finally {
restoreEnvKeys(saved);
}
});
test('kilo: KILO_CONFIG_DIR takes precedence over KILO_CONFIG', () => {
const saved = clearAllEnvKeys();
process.env['KILO_CONFIG_DIR'] = '/dir/wins';
process.env['KILO_CONFIG'] = '/file/loses.json';
try {
assert.strictEqual(String(getGlobalConfigDir('kilo')).replace(/\\/g, '/'), '/dir/wins');
} finally {
restoreEnvKeys(saved);
}
});
test('kilo: KILO_CONFIG takes precedence over XDG_CONFIG_HOME', () => {
const saved = clearAllEnvKeys();
process.env['KILO_CONFIG'] = '/cfg/kilo.json';
process.env['XDG_CONFIG_HOME'] = '/xdg/should/lose';
try {
assert.strictEqual(String(getGlobalConfigDir('kilo')).replace(/\\/g, '/'), '/cfg');
} finally {
restoreEnvKeys(saved);
}
});
test('kilo: XDG_CONFIG_HOME → ~/.config/kilo subdir', () => {
const saved = clearAllEnvKeys();
process.env['XDG_CONFIG_HOME'] = '/xdg';
try {
assert.strictEqual(getGlobalConfigDir('kilo'), path.join('/xdg', 'kilo'));
} finally {
restoreEnvKeys(saved);
}
});
});
// ── GOLDEN DOT-HOME-NESTED (antigravity probe) ────────────────────────────────
@@ -800,18 +573,6 @@ describe('descriptor-driven equivalence: dot-home-nested antigravity probe hit/m
assert.strictEqual(result, '/custom/ag');
});
test('windsurf (no probe) → ~/.codeium/windsurf regardless of existsSync', () => {
const result = resolveConfigHomeFromDescriptor(
{
kind: 'dot-home-nested',
name: 'windsurf',
parent: '.codeium',
env: ['WINDSURF_CONFIG_DIR'],
},
{ env: {}, home: '/home/u', existsSync: () => true },
);
assert.strictEqual(result, path.join('/home/u', '.codeium', 'windsurf'));
});
});
// ── #213/#217 thread-4: existing-install ambiguity detector ───────────────────
@@ -875,120 +636,9 @@ describe('detectAntigravityDirAmbiguity (migration/operator-guidance signal)', (
});
});
// ── GOLDEN GENERIC-AGENTS-ROOT (kimi probe) ───────────────────────────────────
describe('descriptor-driven equivalence: generic-agents-root kimi probe hit/miss', () => {
test('kimi probe-miss → recommended root ~/.config/agents', () => {
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-equiv-kimi-miss-'));
try {
const result = resolveConfigHomeFromDescriptor(
{
kind: 'generic-agents-root',
name: 'agents',
env: ['KIMI_CONFIG_DIR'],
probe: ['~/.config/agents', '~/.agents'],
probeExists: 'skills',
},
{ env: {}, home: tmpHome, existsSync: () => false },
);
assert.strictEqual(result, path.join(tmpHome, '.config', 'agents'));
} finally {
cleanup(tmpHome);
}
});
test('kimi probe-hit on recommended root ~/.config/agents/skills', () => {
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-equiv-kimi-recommended-'));
try {
const recommended = path.join(tmpHome, '.config', 'agents');
const result = resolveConfigHomeFromDescriptor(
{
kind: 'generic-agents-root',
name: 'agents',
env: ['KIMI_CONFIG_DIR'],
probe: ['~/.config/agents', '~/.agents'],
probeExists: 'skills',
},
{
env: {},
home: tmpHome,
existsSync: (p) => p === path.join(recommended, 'skills'),
},
);
assert.strictEqual(result, recommended);
} finally {
cleanup(tmpHome);
}
});
test('kimi probe-hit on fallback ~/.agents/skills (recommended does not exist)', () => {
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-equiv-kimi-fallback-'));
try {
const fallback = path.join(tmpHome, '.agents');
const result = resolveConfigHomeFromDescriptor(
{
kind: 'generic-agents-root',
name: 'agents',
env: ['KIMI_CONFIG_DIR'],
probe: ['~/.config/agents', '~/.agents'],
probeExists: 'skills',
},
{
env: {},
home: tmpHome,
existsSync: (p) => p === path.join(fallback, 'skills'),
},
);
assert.strictEqual(result, fallback);
} finally {
cleanup(tmpHome);
}
});
test('kimi: KIMI_CONFIG_DIR env override wins over any probe', () => {
const result = resolveConfigHomeFromDescriptor(
{
kind: 'generic-agents-root',
name: 'agents',
env: ['KIMI_CONFIG_DIR'],
probe: ['~/.config/agents', '~/.agents'],
probeExists: 'skills',
},
{ env: { KIMI_CONFIG_DIR: '/custom/kimi' }, home: '/home/u', existsSync: () => true },
);
assert.strictEqual(result, '/custom/kimi');
});
// Verify resolveKimiGlobalDir wrapper delegates correctly
test('resolveKimiGlobalDir wrapper: probe-miss → recommended root', () => {
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-equiv-rkgd-miss-'));
try {
assert.strictEqual(
resolveKimiGlobalDir({ env: {}, home: tmpHome, existsSync: () => false }),
path.join(tmpHome, '.config', 'agents'),
);
} finally {
cleanup(tmpHome);
}
});
test('resolveKimiGlobalDir wrapper: fallback probe-hit', () => {
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-equiv-rkgd-hit-'));
try {
const fallback = path.join(tmpHome, '.agents');
assert.strictEqual(
resolveKimiGlobalDir({
env: {},
home: tmpHome,
existsSync: (p) => p === path.join(fallback, 'skills'),
}),
fallback,
);
} finally {
cleanup(tmpHome);
}
});
// ── resolveAntigravityGlobalDir wrapper ───────────────────────────────────────
describe('descriptor-driven equivalence: resolveAntigravityGlobalDir wrapper', () => {
// Verify resolveAntigravityGlobalDir wrapper delegates correctly
test('resolveAntigravityGlobalDir wrapper: probe-miss → ~/.gemini/antigravity', () => {
const tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'msd-equiv-ragd-miss-'));
@@ -1089,23 +739,6 @@ describe('descriptor-driven equivalence: unknown runtime fallback', () => {
});
describe('descriptor-driven global skills base', () => {
test('hermes skills base is derived from descriptor artifact layout', () => {
const saved = clearAllEnvKeys();
try {
assert.strictEqual(getGlobalSkillsBase('hermes'), path.join(HOME, '.hermes', 'skills', 'msd'));
} finally {
restoreEnvKeys(saved);
}
});
test('kilo skills base is derived from configHome.skillsHome descriptor', () => {
const saved = clearAllEnvKeys();
try {
assert.strictEqual(getGlobalSkillsBase('kilo'), path.join(HOME, '.kilo', 'skills'));
} finally {
restoreEnvKeys(saved);
}
});
test('synthetic runtime skillsHome descriptor resolves without a runtime-name branch', () => {
const base = resolveSkillsBaseFromDescriptor(
@@ -1215,17 +848,8 @@ describe('bug #3126: runtime-homes getGlobalConfigDir — defaults', () => {
['claude', path.join(os.homedir(), '.claude')],
['cursor', path.join(os.homedir(), '.cursor')],
['codex', path.join(os.homedir(), '.codex')],
['copilot', path.join(os.homedir(), '.copilot')],
['antigravity', path.join(os.homedir(), '.gemini', 'antigravity')],
['windsurf', path.join(os.homedir(), '.codeium', 'windsurf')],
['augment', path.join(os.homedir(), '.augment')],
['trae', path.join(os.homedir(), '.trae')],
['qwen', path.join(os.homedir(), '.qwen')],
['hermes', path.join(os.homedir(), '.hermes')],
['codebuddy', path.join(os.homedir(), '.codebuddy')],
['cline', path.join(os.homedir(), '.cline')],
['opencode', path.join(os.homedir(), '.config', 'opencode')],
['kilo', path.join(os.homedir(), '.config', 'kilo')],
];
for (const [runtime, expected] of defaults) {
test(`${runtime} default configDir`, () => {
@@ -1285,15 +909,6 @@ describe('bug #3126: runtime-homes env-var overrides', () => {
});
});
});
test('kilo uses XDG_CONFIG_HOME when KILO_CONFIG_DIR absent', () => {
withEnv('KILO_CONFIG_DIR', undefined, () => {
withEnv('KILO_CONFIG', undefined, () => {
withEnv('XDG_CONFIG_HOME', '/xdg', () => {
assert.strictEqual(getGlobalConfigDir('kilo'), path.join('/xdg', 'kilo'));
});
});
});
});
test('antigravity detects 2.x IDE dir when legacy dir is absent', () => {
const home = require('node:fs').mkdtempSync(path.join(os.tmpdir(), 'msd-antigravity-home-'));
@@ -1328,22 +943,6 @@ describe('bug #3126: runtime-homes getGlobalSkillsBase', () => {
);
});
});
test('hermes: skills at <configDir>/skills/msd (nested layout #2841)', () => {
withEnv('HERMES_HOME', undefined, () => {
assert.strictEqual(
getGlobalSkillsBase('hermes'),
path.join(os.homedir(), '.hermes', 'skills', 'msd'),
);
});
});
test('cline: returns ~/.cline/skills (skills-capable since v3.48.0 — #782)', () => {
withEnv('CLINE_CONFIG_DIR', undefined, () => {
assert.strictEqual(
getGlobalSkillsBase('cline'),
path.join(os.homedir(), '.cline', 'skills'),
);
});
});
});
describe('bug #3126: runtime-homes getGlobalSkillDir', () => {
@@ -1355,22 +954,6 @@ describe('bug #3126: runtime-homes getGlobalSkillDir', () => {
);
});
});
test('hermes: <configDir>/skills/msd/<skillName>', () => {
withEnv('HERMES_HOME', undefined, () => {
assert.strictEqual(
getGlobalSkillDir('hermes', 'msd-executor'),
path.join(os.homedir(), '.hermes', 'skills', 'msd', 'msd-executor'),
);
});
});
test('cline: returns ~/.cline/skills/msd-executor (skills-capable since v3.48.0 — #782)', () => {
withEnv('CLINE_CONFIG_DIR', undefined, () => {
assert.strictEqual(
getGlobalSkillDir('cline', 'msd-executor'),
path.join(os.homedir(), '.cline', 'skills', 'msd-executor'),
);
});
});
});
describe('getGlobalConfigDir — explicitDir override and opencode/kilo file-path precedence', () => {
@@ -1434,47 +1017,6 @@ describe('getGlobalConfigDir — explicitDir override and opencode/kilo file-pat
});
});
// ── kilo: KILO_CONFIG file-path step ─────────────────────────────────────
test('kilo: KILO_CONFIG → path.dirname(expandTilde(value))', () => {
withEnv('KILO_CONFIG_DIR', undefined, () => {
withEnv('XDG_CONFIG_HOME', undefined, () => {
withEnv('KILO_CONFIG', '/home/u/cfg/kilo.json', () => {
assert.strictEqual(String(getGlobalConfigDir('kilo')).replace(/\\/g, '/'), '/home/u/cfg');
});
});
});
});
test('kilo: KILO_CONFIG_DIR takes precedence over KILO_CONFIG', () => {
withEnv('KILO_CONFIG_DIR', '/dir/wins', () => {
withEnv('KILO_CONFIG', '/file/loses.json', () => {
assert.strictEqual(String(getGlobalConfigDir('kilo')).replace(/\\/g, '/'), '/dir/wins');
});
});
});
test('kilo: KILO_CONFIG takes precedence over XDG_CONFIG_HOME', () => {
withEnv('KILO_CONFIG_DIR', undefined, () => {
withEnv('KILO_CONFIG', '/cfg/kilo.json', () => {
withEnv('XDG_CONFIG_HOME', '/xdg/should/lose', () => {
assert.strictEqual(String(getGlobalConfigDir('kilo')).replace(/\\/g, '/'), '/cfg');
});
});
});
});
test('kilo: default ~/.config/kilo when no env vars set', () => {
withEnv('KILO_CONFIG_DIR', undefined, () => {
withEnv('KILO_CONFIG', undefined, () => {
withEnv('XDG_CONFIG_HOME', undefined, () => {
assert.strictEqual(
getGlobalConfigDir('kilo'),
path.join(os.homedir(), '.config', 'kilo'),
);
});
});
});
});
});
describe('bug #3126: buildAgentSkillsBlock resolves the agent-skills path per runtime (not hardcoded .claude)', () => {
@@ -1570,93 +1112,3 @@ describe('bug #3126: buildAgentSkillsBlock resolves the agent-skills path per ru
}
// ── resolveKimiHooksTomlDir: per-runtime hooks root (#2755) ──────────────────
describe('resolveKimiHooksTomlDir — per-runtime hooks root (#2755)', () => {
// Pure path computation; the fixture home never needs to exist on disk.
const FIXTURE_HOME = path.join(os.tmpdir(), 'msd-2755-home-fixture');
const at = (...seg) => path.join(FIXTURE_HOME, ...seg);
test('a bare call does not throw', () => {
assert.doesNotThrow(() => resolveKimiHooksTomlDir());
});
test('an omitted runtime still resolves Kimi CLI\'s ~/.kimi (back-compat)', () => {
// The function is exported; callers and tests outside this diff pass no
// runtime, and their destination must not move.
assert.equal(resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {} }), at('.kimi'));
});
test('runtime "kimi" resolves ~/.kimi', () => {
assert.equal(
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {}, runtime: 'kimi' }),
at('.kimi'),
);
});
test('runtime "kimi-code" resolves ~/.kimi-code', () => {
assert.equal(
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {}, runtime: 'kimi-code' }),
at('.kimi-code'),
);
});
test('KIMI_SHARE_DIR overrides the kimi root', () => {
assert.equal(
String(resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: { KIMI_SHARE_DIR: '/share' }, runtime: 'kimi' })).replace(/\\/g, '/'),
'/share',
);
});
test('KIMI_CODE_HOME overrides the kimi-code root', () => {
assert.equal(
String(resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: { KIMI_CODE_HOME: '/kcode' }, runtime: 'kimi-code' })).replace(/\\/g, '/'),
'/kcode',
);
});
test('KIMI_SHARE_DIR does not hijack the kimi-code root', () => {
// KIMI_SHARE_DIR is Kimi CLI's own upstream var. Before #2755 it silently
// redirected kimi-code too — that accident was the issue's suggested
// workaround, and the fix must make it inert.
assert.equal(
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: { KIMI_SHARE_DIR: '/share' }, runtime: 'kimi-code' }),
at('.kimi-code'),
);
});
test('KIMI_CODE_HOME does not hijack the kimi root', () => {
assert.equal(
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: { KIMI_CODE_HOME: '/kcode' }, runtime: 'kimi' }),
at('.kimi'),
);
});
test('an unknown runtime falls back to ~/.kimi', () => {
assert.equal(
resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {}, runtime: 'not-a-kimi' }),
at('.kimi'),
);
});
test('every kimi-hooks-toml runtime resolves a distinct hooks root', () => {
// Divergence guard (CLAUDE.md → Generative Fix Divergence). The resolver
// hardcodes the two Kimi roots while the capability registry independently
// decides which runtimes use the kimi-hooks-toml surface. If a third one is
// ever added without teaching the resolver its root, it silently inherits
// ~/.kimi — which IS the #2755 defect, re-created. This fails the moment
// those two surfaces drift apart.
const capsDir = path.join(ROOT, 'capabilities');
const ids = fs.readdirSync(capsDir).filter((id) => {
const file = path.join(capsDir, id, 'capability.json');
if (!fs.existsSync(file)) return false;
return JSON.parse(fs.readFileSync(file, 'utf8'))?.runtime?.hooksSurface === 'kimi-hooks-toml';
});
assert.deepEqual(ids.sort(), ['kimi', 'kimi-code'],
'the kimi-hooks-toml runtime set changed — teach resolveKimiHooksTomlDir the new root, then update this list');
const roots = ids.map((id) => resolveKimiHooksTomlDir({ home: FIXTURE_HOME, env: {}, runtime: id }));
assert.equal(new Set(roots).size, roots.length,
`each kimi-hooks-toml runtime must resolve its own root; got ${JSON.stringify(roots)}`);
});
});