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:
@@ -378,7 +378,7 @@ describe('applySurface', () => {
|
||||
// user-created command files that install would preserve.
|
||||
//
|
||||
// Affected runtimes have a FLAT command dir (opencode `commands/` — #2329,
|
||||
// cursor `commands/`, augment `commands/`, kilo `command/`) with kind.prefix='msd-'.
|
||||
// cursor `commands/`) with kind.prefix='msd-'.
|
||||
// _copyStaged names files `msd-<stem>.md` but the buggy _syncMsdDir copies
|
||||
// them as `<stem>.md` (unprefixed) and also deletes ALL .md files not in the
|
||||
// staged set, including user files.
|
||||
@@ -446,8 +446,8 @@ describe('applySurface', () => {
|
||||
// against future drift between _syncMsdDir (surface) and _copyStaged (install)
|
||||
// command-naming logic.
|
||||
//
|
||||
// Matrix: opencode/cursor/augment = flat commands/ + prefix msd- (#2329: opencode
|
||||
// moved from singular command/ to commands/); kilo = flat command/ + prefix msd-.
|
||||
// Matrix: opencode/cursor = flat commands/ + prefix msd- (#2329: opencode
|
||||
// moved from singular command/ to commands/).
|
||||
// For each runtime we: run install into installDir, run applySurface into
|
||||
// surfaceDir (same 'standard' profile both sides), then compare sorted .md
|
||||
// filename sets in the commands dest dir. On a fresh dir (no superseded files)
|
||||
@@ -461,7 +461,7 @@ describe('applySurface', () => {
|
||||
// the same skill set so any filename difference is purely a naming bug.
|
||||
const resolvedProfile = resolveProfile({ modes: ['standard'], manifest });
|
||||
|
||||
const PARITY_RUNTIMES = ['opencode', 'cursor', 'augment', 'kilo'];
|
||||
const PARITY_RUNTIMES = ['opencode', 'cursor'];
|
||||
|
||||
for (const runtime of PARITY_RUNTIMES) {
|
||||
// Create two independent temp dirs — one for install, one for surface.
|
||||
@@ -1231,42 +1231,37 @@ describe('listSurface', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// ─── #1615: applySurface must rewrite commands kind (Windsurf workflows) ─────
|
||||
// ─── #1615: applySurface must rewrite commands kind ─────────────────────────
|
||||
// Adversarial review of PR #1622 found that applySurface only rewrites 'skills'
|
||||
// kinds, skipping 'commands'. Windsurf's capability now stages workflow files
|
||||
// kinds, skipping 'commands'. A commands kind stages command files
|
||||
// as kind='commands'; without the rewrite, /msd-surface would write workflow
|
||||
// bodies containing raw @~/.claude/... references that don't exist on a
|
||||
// Windsurf install. The same gap affected any runtime with commands kinds.
|
||||
// runtime install. The same gap affected any runtime with commands kinds.
|
||||
describe('applySurface — commands kind path rewrite (#1615 adversarial review)', () => {
|
||||
test('windsurf workflow bodies are rewritten to install target (no raw ~/.claude/)', (t) => {
|
||||
const base = createTempDir('msd-surface-cmds-windsurf-');
|
||||
t.after(() => cleanup(base));
|
||||
const runtimeConfigDir = base;
|
||||
|
||||
// Stage the canonical command body the workflow delegates to.
|
||||
const canonicalDir = path.join(runtimeConfigDir, 'msd-core', 'commands', 'msd');
|
||||
fs.mkdirSync(canonicalDir, { recursive: true });
|
||||
fs.writeFileSync(path.join(canonicalDir, 'help.md'),
|
||||
'---\nname: help\ndescription: Show help\n---\n\nHelp body\n');
|
||||
test('zcode command bodies are rewritten to install target (no raw ~/.claude/)', (t) => {
|
||||
// A real install provides the complete package source the surface needs.
|
||||
const installed = runMinimalInstall({ runtime: 'zcode', scope: 'global' });
|
||||
const runtimeConfigDir = installed.configDir;
|
||||
t.after(() => { try { cleanup(installed.root); } catch { /* best-effort */ } });
|
||||
|
||||
const manifest = loadSkillsManifest(REAL_COMMANDS_DIR);
|
||||
const layout = resolveRuntimeArtifactLayout('windsurf', runtimeConfigDir, 'local');
|
||||
const layout = resolveRuntimeArtifactLayout('zcode', runtimeConfigDir, 'global');
|
||||
|
||||
// Sanity: layout must have a commands kind (workflows) — pre-condition
|
||||
// Sanity: layout must have a commands kind — pre-condition
|
||||
// introduced by PR #1622; if a future refactor removes it, this test
|
||||
// would silently pass without exercising the rewrite path.
|
||||
const commandsKind = layout.kinds.find((k) => k.kind === 'commands');
|
||||
assert.ok(commandsKind, 'pre-condition: windsurf layout has a commands kind');
|
||||
assert.ok(commandsKind, 'pre-condition: zcode layout has a commands kind');
|
||||
|
||||
applySurface(runtimeConfigDir, layout, manifest, CLUSTERS);
|
||||
|
||||
// Workflow files should be written to <configDir>/workflows/msd-*.md
|
||||
const workflowsDir = path.join(runtimeConfigDir, 'workflows');
|
||||
// Command files should be written to <configDir>/<commands destSubpath>/msd-*.md
|
||||
const workflowsDir = path.join(runtimeConfigDir, commandsKind.destSubpath);
|
||||
const workflowFiles = fs.existsSync(workflowsDir)
|
||||
? fs.readdirSync(workflowsDir).filter((f) => f.startsWith('msd-') && f.endsWith('.md'))
|
||||
: [];
|
||||
assert.ok(workflowFiles.length > 0,
|
||||
`expected at least one msd-*.md workflow under ${workflowsDir}; got [${workflowFiles.join(', ')}]`);
|
||||
`expected at least one msd-*.md command under ${workflowsDir}; got [${workflowFiles.join(', ')}]`);
|
||||
|
||||
// Every workflow body must reference the install target, NOT the raw
|
||||
// ~/.claude/ path. This is the regression: pre-fix, the commands kind
|
||||
@@ -1559,7 +1554,7 @@ describe('installOpencodeFamilySkills destination parity (#2911 sibling coverage
|
||||
return commandsKind.stage(resolveProfile({ modes: ['core'], manifest: loadSkillsManifest(REAL_COMMANDS_DIR) }));
|
||||
}
|
||||
|
||||
for (const runtime of ['opencode', 'kilo']) {
|
||||
for (const runtime of ['opencode']) {
|
||||
test(`${runtime}: installOpencodeFamilySkills honors a skills-kind home override instead of always resolving against configDir (#2911 sibling)`, (t) => {
|
||||
const configDir = fs.mkdtempSync(path.join(os.tmpdir(), `msd-2911-ocfs-${runtime}-`));
|
||||
const fakeHomeOverride = fs.mkdtempSync(path.join(os.tmpdir(), `msd-2911-ocfs-home-${runtime}-`));
|
||||
@@ -1635,8 +1630,8 @@ describe('installOpencodeFamilySkills destination parity (#2911 sibling coverage
|
||||
// #924: Claude was reverted to FLAT, so the claude case below asserts the
|
||||
// flat layout is preserved (not re-nested) rather than a nested one.
|
||||
describe('issue-69: applySurface preserves nested skill layout (no re-flatten)', () => {
|
||||
test('cline global full: applySurface keeps 6 router dirs and nested msd-ns-manage/skills/help/SKILL.md', (t) => {
|
||||
const installed = runMinimalInstall({ runtime: 'cline', scope: 'global' });
|
||||
test('zcode global full: applySurface keeps 6 router dirs and nested msd-ns-manage/skills/help/SKILL.md', (t) => {
|
||||
const installed = runMinimalInstall({ runtime: 'zcode', scope: 'global' });
|
||||
const dir = installed.configDir;
|
||||
t.after(() => { try { cleanup(installed.root); } catch { /* best-effort */ } });
|
||||
|
||||
@@ -1658,7 +1653,7 @@ describe('issue-69: applySurface preserves nested skill layout (no re-flatten)',
|
||||
);
|
||||
|
||||
// Step 2: applySurface (full surface, no surface state file → resolves to full)
|
||||
const layout = resolveRuntimeArtifactLayout('cline', dir, 'global');
|
||||
const layout = resolveRuntimeArtifactLayout('zcode', dir, 'global');
|
||||
applySurface(dir, layout, manifest);
|
||||
|
||||
// Step 3: assert nested layout is preserved after applySurface
|
||||
@@ -1730,90 +1725,3 @@ describe('issue-69: applySurface preserves nested skill layout (no re-flatten)',
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
// ─── #4211: kimi-agents materialization ─────────────────────────────────────
|
||||
//
|
||||
// Kimi's managed tree is `agents/msd.yaml` + `agents/msd.md` +
|
||||
// `agents/subagents/msd-*.{yaml,md}` (runtime-artifact-layout.cts
|
||||
// kimiAgentsKind), and install copies it recursively (_copyStaged in
|
||||
// src/install-engine.cts). Surface apply fell through to the flat
|
||||
// command/agent branch of _syncMsdDir, which reads only top-level `*.md`: it
|
||||
// ignored the YAML half and the subagents/ subtree, and rewrote `msd.md` as
|
||||
// `msdmsd.md` (the flat branch re-applies kind.prefix to a name that already
|
||||
// carries it) — corrupting Kimi's installed artifacts while still exiting 0.
|
||||
|
||||
describe('#4211: applySurface materializes the kimi-agents kind like a fresh install', () => {
|
||||
function kimiTree(dir, base = dir) {
|
||||
if (!fs.existsSync(dir)) return [];
|
||||
let out = [];
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) out = out.concat(kimiTree(full, base));
|
||||
else out.push(path.relative(base, full).split(path.sep).join('/'));
|
||||
}
|
||||
return out.sort();
|
||||
}
|
||||
|
||||
function installKimi(t) {
|
||||
const installed = runMinimalInstall({ runtime: 'kimi', scope: 'global' });
|
||||
t.after(() => { try { cleanup(installed.root); } catch { /* best-effort */ } });
|
||||
return { configDir: installed.configDir, agentsDir: path.join(installed.configDir, 'agents') };
|
||||
}
|
||||
|
||||
test('the materialized tree is identical to the installed one — no msdmsd.md, no dropped YAML', (t) => {
|
||||
const { configDir, agentsDir } = installKimi(t);
|
||||
|
||||
const before = kimiTree(agentsDir);
|
||||
assert.ok(before.includes('msd.yaml'), 'precondition: install writes agents/msd.yaml');
|
||||
assert.ok(before.includes('msd.md'), 'precondition: install writes agents/msd.md');
|
||||
assert.ok(before.some((f) => f.startsWith('subagents/msd-') && f.endsWith('.yaml')),
|
||||
'precondition: install writes agents/subagents/msd-*.yaml');
|
||||
const rootPromptBefore = fs.readFileSync(path.join(agentsDir, 'msd.md'), 'utf8');
|
||||
|
||||
const layout = resolveRuntimeArtifactLayout('kimi', configDir, 'global');
|
||||
applySurface(configDir, layout, realManifest(), CLUSTERS);
|
||||
|
||||
const after = kimiTree(agentsDir);
|
||||
assert.deepEqual(after, before,
|
||||
'surface apply must produce the same managed artifact tree as the install it re-stages');
|
||||
assert.ok(!after.includes('msdmsd.md'), 'the root prompt must not be re-prefixed into msdmsd.md');
|
||||
assert.equal(fs.readFileSync(path.join(agentsDir, 'msd.md'), 'utf8'), rootPromptBefore,
|
||||
'the root prompt content must survive re-materialization');
|
||||
});
|
||||
|
||||
test('user-owned files under agents/ are preserved', (t) => {
|
||||
const { configDir, agentsDir } = installKimi(t);
|
||||
|
||||
const userRoot = path.join(agentsDir, 'my-own-agent.yaml');
|
||||
const userSub = path.join(agentsDir, 'subagents', 'my-own-subagent.yaml');
|
||||
const userNote = path.join(agentsDir, 'subagents', 'notes.txt');
|
||||
fs.writeFileSync(userRoot, 'name: mine\n');
|
||||
fs.writeFileSync(userSub, 'name: mine-sub\n');
|
||||
fs.writeFileSync(userNote, 'scratch\n');
|
||||
|
||||
const layout = resolveRuntimeArtifactLayout('kimi', configDir, 'global');
|
||||
applySurface(configDir, layout, realManifest(), CLUSTERS);
|
||||
|
||||
for (const file of [userRoot, userSub, userNote]) {
|
||||
assert.ok(fs.existsSync(file), `${path.basename(file)} is user-owned and must survive surface apply`);
|
||||
}
|
||||
});
|
||||
|
||||
test('a MSD subagent the surface no longer stages is pruned', (t) => {
|
||||
const { configDir, agentsDir } = installKimi(t);
|
||||
|
||||
// Shaped exactly like a subagent an earlier version staged and this one
|
||||
// does not — the case install's _removeMsdEntries prunes for this kind.
|
||||
const retiredYaml = path.join(agentsDir, 'subagents', 'msd-retired-agent.yaml');
|
||||
const retiredPrompt = path.join(agentsDir, 'subagents', 'msd-retired-agent.md');
|
||||
fs.writeFileSync(retiredYaml, 'name: msd-retired-agent\n');
|
||||
fs.writeFileSync(retiredPrompt, '# retired\n');
|
||||
|
||||
const layout = resolveRuntimeArtifactLayout('kimi', configDir, 'global');
|
||||
applySurface(configDir, layout, realManifest(), CLUSTERS);
|
||||
|
||||
assert.equal(fs.existsSync(retiredYaml), false, 'a stale MSD subagent must be pruned');
|
||||
assert.equal(fs.existsSync(retiredPrompt), false, 'a stale MSD subagent prompt must be pruned');
|
||||
assert.ok(fs.existsSync(path.join(agentsDir, 'msd.yaml')), 'the live root agent must remain');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user