fix(installer): suspend Kilo Code and add verified Gemini/Crush results
Kilo Code does not support the Agent Skills standard — the migration from modes+workflows to skills was based on a false fork assumption. - Add suspended field to platform-codes.yaml, hiding Kilo from the IDE picker and blocking setup with a clear message - Fail the installer early (before writing _bmad/) if all selected IDEs are suspended, protecting existing installations from being corrupted - Still clean up legacy Kilo artifacts (.kilocodemodes, .kilocode/workflows) when users switch to a different IDE - Mark Crush and Gemini CLI as manually verified (both work end-to-end) - Replace Suite 22 install tests with suspended-behavior tests (7 assertions)
This commit is contained in:
parent
837ccca0e4
commit
b5d894999a
|
|
@ -1135,82 +1135,59 @@ async function runTests() {
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Suite 22: KiloCoder Native Skills
|
// Suite 22: KiloCoder Suspended
|
||||||
// ============================================================
|
// ============================================================
|
||||||
console.log(`${colors.yellow}Test Suite 22: KiloCoder Native Skills${colors.reset}\n`);
|
console.log(`${colors.yellow}Test Suite 22: KiloCoder Suspended${colors.reset}\n`);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
clearCache();
|
clearCache();
|
||||||
const platformCodes22 = await loadPlatformCodes();
|
const platformCodes22 = await loadPlatformCodes();
|
||||||
const kiloInstaller = platformCodes22.platforms.kilo?.installer;
|
const kiloConfig22 = platformCodes22.platforms.kilo;
|
||||||
|
|
||||||
assert(kiloInstaller?.target_dir === '.kilocode/skills', 'KiloCoder target_dir uses native skills path');
|
assert(typeof kiloConfig22?.suspended === 'string', 'KiloCoder has a suspended message in platform config');
|
||||||
|
|
||||||
assert(kiloInstaller?.skill_format === true, 'KiloCoder installer enables native skill output');
|
assert(kiloConfig22?.installer?.target_dir === '.kilocode/skills', 'KiloCoder retains target_dir config for future use');
|
||||||
|
|
||||||
assert(
|
const ideManager22 = new IdeManager();
|
||||||
Array.isArray(kiloInstaller?.legacy_targets) && kiloInstaller.legacy_targets.includes('.kilocode/workflows'),
|
await ideManager22.ensureInitialized();
|
||||||
'KiloCoder installer cleans legacy workflows output',
|
|
||||||
);
|
|
||||||
|
|
||||||
// Fresh install test
|
// Should not appear in available IDEs
|
||||||
|
const availableIdes22 = ideManager22.getAvailableIdes();
|
||||||
|
assert(!availableIdes22.some((ide) => ide.value === 'kilo'), 'KiloCoder is hidden from IDE selection');
|
||||||
|
|
||||||
|
// Setup should be blocked but legacy files should be cleaned up
|
||||||
const tempProjectDir22 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-kilo-test-'));
|
const tempProjectDir22 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-kilo-test-'));
|
||||||
const installedBmadDir22 = await createTestBmadFixture();
|
const installedBmadDir22 = await createTestBmadFixture();
|
||||||
|
|
||||||
|
// Pre-populate legacy Kilo artifacts that should be cleaned up
|
||||||
const legacyDir22 = path.join(tempProjectDir22, '.kilocode', 'workflows');
|
const legacyDir22 = path.join(tempProjectDir22, '.kilocode', 'workflows');
|
||||||
await fs.ensureDir(legacyDir22);
|
await fs.ensureDir(legacyDir22);
|
||||||
await fs.writeFile(path.join(legacyDir22, 'bmad-legacy.md'), 'legacy\n');
|
await fs.writeFile(path.join(legacyDir22, 'bmad-legacy.md'), 'legacy\n');
|
||||||
|
|
||||||
// Create a .kilocodemodes file with BMAD modes and a user mode
|
|
||||||
const kiloModesPath22 = path.join(tempProjectDir22, '.kilocodemodes');
|
|
||||||
const yaml22 = require('yaml');
|
|
||||||
const kiloModesContent = yaml22.stringify({
|
|
||||||
customModes: [
|
|
||||||
{ slug: 'bmad-bmm-architect', name: 'BMAD Architect', roleDefinition: 'test' },
|
|
||||||
{ slug: 'bmad-core-master', name: 'BMAD Master', roleDefinition: 'test' },
|
|
||||||
{ slug: 'user-custom-mode', name: 'My Custom Mode', roleDefinition: 'user mode' },
|
|
||||||
],
|
|
||||||
});
|
|
||||||
await fs.writeFile(kiloModesPath22, kiloModesContent);
|
|
||||||
|
|
||||||
const ideManager22 = new IdeManager();
|
|
||||||
await ideManager22.ensureInitialized();
|
|
||||||
const result22 = await ideManager22.setup('kilo', tempProjectDir22, installedBmadDir22, {
|
const result22 = await ideManager22.setup('kilo', tempProjectDir22, installedBmadDir22, {
|
||||||
silent: true,
|
silent: true,
|
||||||
selectedModules: ['bmm'],
|
selectedModules: ['bmm'],
|
||||||
});
|
});
|
||||||
|
|
||||||
assert(result22.success === true, 'KiloCoder setup succeeds against temp project');
|
assert(result22.success === false, 'KiloCoder setup is blocked when suspended');
|
||||||
|
assert(result22.error === 'suspended', 'KiloCoder setup returns suspended error');
|
||||||
|
|
||||||
const skillFile22 = path.join(tempProjectDir22, '.kilocode', 'skills', 'bmad-master', 'SKILL.md');
|
// Should not write new skill files
|
||||||
assert(await fs.pathExists(skillFile22), 'KiloCoder install writes SKILL.md directory output');
|
|
||||||
|
|
||||||
const skillContent22 = await fs.readFile(skillFile22, 'utf8');
|
|
||||||
const nameMatch22 = skillContent22.match(/^name:\s*(.+)$/m);
|
|
||||||
assert(nameMatch22 && nameMatch22[1].trim() === 'bmad-master', 'KiloCoder skill name frontmatter matches directory name exactly');
|
|
||||||
|
|
||||||
assert(!(await fs.pathExists(path.join(tempProjectDir22, '.kilocode', 'workflows'))), 'KiloCoder setup removes legacy workflows dir');
|
|
||||||
|
|
||||||
// Verify .kilocodemodes cleanup: BMAD modes removed, user mode preserved
|
|
||||||
const cleanedModes22 = yaml22.parse(await fs.readFile(kiloModesPath22, 'utf8'));
|
|
||||||
assert(
|
assert(
|
||||||
Array.isArray(cleanedModes22.customModes) && cleanedModes22.customModes.length === 1,
|
!(await fs.pathExists(path.join(tempProjectDir22, '.kilocode', 'skills'))),
|
||||||
'KiloCoder cleanup removes BMAD modes from .kilocodemodes',
|
'KiloCoder does not create skills directory when suspended',
|
||||||
);
|
);
|
||||||
assert(cleanedModes22.customModes[0].slug === 'user-custom-mode', 'KiloCoder cleanup preserves non-BMAD modes in .kilocodemodes');
|
|
||||||
|
|
||||||
// Reinstall test
|
// Legacy files should be cleaned up
|
||||||
const result22b = await ideManager22.setup('kilo', tempProjectDir22, installedBmadDir22, {
|
assert(
|
||||||
silent: true,
|
!(await fs.pathExists(path.join(tempProjectDir22, '.kilocode', 'workflows'))),
|
||||||
selectedModules: ['bmm'],
|
'KiloCoder legacy workflows are cleaned up even when suspended',
|
||||||
});
|
);
|
||||||
|
|
||||||
assert(result22b.success === true, 'KiloCoder reinstall/upgrade succeeds over existing skills');
|
|
||||||
assert(await fs.pathExists(skillFile22), 'KiloCoder reinstall preserves SKILL.md output');
|
|
||||||
|
|
||||||
await fs.remove(tempProjectDir22);
|
await fs.remove(tempProjectDir22);
|
||||||
await fs.remove(installedBmadDir22);
|
await fs.remove(installedBmadDir22);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
assert(false, 'KiloCoder native skills migration test succeeds', error.message);
|
assert(false, 'KiloCoder suspended test succeeds', error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
|
||||||
|
|
@ -717,6 +717,25 @@ class Installer {
|
||||||
config.skipIde = toolSelection.skipIde;
|
config.skipIde = toolSelection.skipIde;
|
||||||
const ideConfigurations = toolSelection.configurations;
|
const ideConfigurations = toolSelection.configurations;
|
||||||
|
|
||||||
|
// Early check: fail fast if ALL selected IDEs are suspended
|
||||||
|
if (config.ides && config.ides.length > 0) {
|
||||||
|
await this.ideManager.ensureInitialized();
|
||||||
|
const suspendedIdes = config.ides.filter((ide) => {
|
||||||
|
const handler = this.ideManager.handlers.get(ide);
|
||||||
|
return handler?.platformConfig?.suspended;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (suspendedIdes.length > 0 && suspendedIdes.length === config.ides.length) {
|
||||||
|
for (const ide of suspendedIdes) {
|
||||||
|
const handler = this.ideManager.handlers.get(ide);
|
||||||
|
await prompts.log.error(`${handler.displayName || ide}: ${handler.platformConfig.suspended}`);
|
||||||
|
}
|
||||||
|
throw new Error(
|
||||||
|
`All selected tool(s) are suspended: ${suspendedIdes.join(', ')}. Installation aborted to prevent upgrading _bmad/ without a working IDE configuration.`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Detect IDEs that were previously installed but are NOT in the new selection (to be removed)
|
// Detect IDEs that were previously installed but are NOT in the new selection (to be removed)
|
||||||
if (config._isUpdate && config._existingInstall) {
|
if (config._isUpdate && config._existingInstall) {
|
||||||
const previouslyInstalledIdes = new Set(config._existingInstall.ides || []);
|
const previouslyInstalledIdes = new Set(config._existingInstall.ides || []);
|
||||||
|
|
|
||||||
|
|
@ -128,6 +128,11 @@ class IdeManager {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Skip suspended platforms (e.g., IDE doesn't support skills yet)
|
||||||
|
if (handler.platformConfig?.suspended) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
ides.push({
|
ides.push({
|
||||||
value: key,
|
value: key,
|
||||||
name: name,
|
name: name,
|
||||||
|
|
@ -177,6 +182,18 @@ class IdeManager {
|
||||||
return { success: false, ide: ideName, error: 'unsupported IDE' };
|
return { success: false, ide: ideName, error: 'unsupported IDE' };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Block suspended platforms — clean up legacy files but don't install
|
||||||
|
if (handler.platformConfig?.suspended) {
|
||||||
|
if (!options.silent) {
|
||||||
|
await prompts.log.warn(`${handler.displayName || ideName}: ${handler.platformConfig.suspended}`);
|
||||||
|
}
|
||||||
|
// Still clean up legacy artifacts so old broken configs don't linger
|
||||||
|
if (typeof handler.cleanup === 'function') {
|
||||||
|
await handler.cleanup(projectDir, { silent: true });
|
||||||
|
}
|
||||||
|
return { success: false, ide: ideName, error: 'suspended' };
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const handlerResult = await handler.setup(projectDir, bmadDir, options);
|
const handlerResult = await handler.setup(projectDir, bmadDir, options);
|
||||||
// Build detail string from handler-returned data
|
// Build detail string from handler-returned data
|
||||||
|
|
|
||||||
|
|
@ -156,6 +156,7 @@ platforms:
|
||||||
preferred: false
|
preferred: false
|
||||||
category: ide
|
category: ide
|
||||||
description: "AI coding platform"
|
description: "AI coding platform"
|
||||||
|
suspended: "Kilo Code does not yet support the Agent Skills standard. Support is paused until they implement it. See https://github.com/kilocode/kilo-code/issues for updates."
|
||||||
installer:
|
installer:
|
||||||
legacy_targets:
|
legacy_targets:
|
||||||
- .kilocode/workflows
|
- .kilocode/workflows
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,7 @@ Support assumption: full Agent Skills support. Crush scans project-local `.crush
|
||||||
- [x] Test fresh install — 43 skills installed to `.crush/skills/`
|
- [x] Test fresh install — 43 skills installed to `.crush/skills/`
|
||||||
- [x] Test reinstall/upgrade from legacy command output
|
- [x] Test reinstall/upgrade from legacy command output
|
||||||
- [x] Confirm no ancestor conflict protection is needed because Crush only scans project-local `.crush/skills/`, no ancestor inheritance
|
- [x] Confirm no ancestor conflict protection is needed because Crush only scans project-local `.crush/skills/`, no ancestor inheritance
|
||||||
- [ ] **NEEDS MANUAL IDE VERIFICATION** — install Crush via brew and confirm skills appear in UI
|
- [x] Manual CLI verification — `crush run` lists all 10 skills and successfully triggers bmad-help
|
||||||
- [x] Implement/extend automated tests — 9 assertions in test suite 20
|
- [x] Implement/extend automated tests — 9 assertions in test suite 20
|
||||||
- [x] Commit
|
- [x] Commit
|
||||||
|
|
||||||
|
|
@ -235,7 +235,7 @@ Support assumption: full Agent Skills support. Gemini CLI docs confirm workspace
|
||||||
- [x] Test reinstall/upgrade from legacy TOML command output — legacy dir removed, skills installed
|
- [x] Test reinstall/upgrade from legacy TOML command output — legacy dir removed, skills installed
|
||||||
- [x] Confirm no ancestor conflict protection is needed — Gemini CLI uses workspace > user > extension precedence, no ancestor directory inheritance
|
- [x] Confirm no ancestor conflict protection is needed — Gemini CLI uses workspace > user > extension precedence, no ancestor directory inheritance
|
||||||
- [x] Implement/extend automated tests — 9 assertions in test suite 23 (config, fresh install, legacy cleanup, reinstall)
|
- [x] Implement/extend automated tests — 9 assertions in test suite 23 (config, fresh install, legacy cleanup, reinstall)
|
||||||
- [ ] **NEEDS MANUAL IDE VERIFICATION** — run `gemini` CLI in a project with installed skills and confirm they appear and can be triggered
|
- [x] Manual CLI verification — `gemini` lists all 10 skills and successfully triggers bmad-help
|
||||||
- [ ] Commit
|
- [ ] Commit
|
||||||
|
|
||||||
## iFlow
|
## iFlow
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue