From b5d894999aed11ed62b2c5be44902a1840e4b4f6 Mon Sep 17 00:00:00 2001 From: Alex Verkhovsky Date: Sat, 7 Mar 2026 05:56:38 -0700 Subject: [PATCH] fix(installer): suspend Kilo Code and add verified Gemini/Crush results MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- test/test-installation-components.js | 73 +++++++------------ tools/cli/installers/lib/core/installer.js | 19 +++++ tools/cli/installers/lib/ide/manager.js | 17 +++++ .../installers/lib/ide/platform-codes.yaml | 1 + .../docs/native-skills-migration-checklist.md | 4 +- 5 files changed, 64 insertions(+), 50 deletions(-) diff --git a/test/test-installation-components.js b/test/test-installation-components.js index d04d5d33f..4315a2b29 100644 --- a/test/test-installation-components.js +++ b/test/test-installation-components.js @@ -1135,82 +1135,59 @@ async function runTests() { 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 { clearCache(); 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( - Array.isArray(kiloInstaller?.legacy_targets) && kiloInstaller.legacy_targets.includes('.kilocode/workflows'), - 'KiloCoder installer cleans legacy workflows output', - ); + const ideManager22 = new IdeManager(); + await ideManager22.ensureInitialized(); - // 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 installedBmadDir22 = await createTestBmadFixture(); + + // Pre-populate legacy Kilo artifacts that should be cleaned up const legacyDir22 = path.join(tempProjectDir22, '.kilocode', 'workflows'); await fs.ensureDir(legacyDir22); 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, { silent: true, 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'); - 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')); + // Should not write new skill files assert( - Array.isArray(cleanedModes22.customModes) && cleanedModes22.customModes.length === 1, - 'KiloCoder cleanup removes BMAD modes from .kilocodemodes', + !(await fs.pathExists(path.join(tempProjectDir22, '.kilocode', 'skills'))), + '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 - const result22b = await ideManager22.setup('kilo', tempProjectDir22, installedBmadDir22, { - silent: true, - selectedModules: ['bmm'], - }); - - assert(result22b.success === true, 'KiloCoder reinstall/upgrade succeeds over existing skills'); - assert(await fs.pathExists(skillFile22), 'KiloCoder reinstall preserves SKILL.md output'); + // Legacy files should be cleaned up + assert( + !(await fs.pathExists(path.join(tempProjectDir22, '.kilocode', 'workflows'))), + 'KiloCoder legacy workflows are cleaned up even when suspended', + ); await fs.remove(tempProjectDir22); await fs.remove(installedBmadDir22); } catch (error) { - assert(false, 'KiloCoder native skills migration test succeeds', error.message); + assert(false, 'KiloCoder suspended test succeeds', error.message); } console.log(''); diff --git a/tools/cli/installers/lib/core/installer.js b/tools/cli/installers/lib/core/installer.js index fe8b88d7c..97caedc60 100644 --- a/tools/cli/installers/lib/core/installer.js +++ b/tools/cli/installers/lib/core/installer.js @@ -717,6 +717,25 @@ class Installer { config.skipIde = toolSelection.skipIde; 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) if (config._isUpdate && config._existingInstall) { const previouslyInstalledIdes = new Set(config._existingInstall.ides || []); diff --git a/tools/cli/installers/lib/ide/manager.js b/tools/cli/installers/lib/ide/manager.js index 8ae376710..7d2330c14 100644 --- a/tools/cli/installers/lib/ide/manager.js +++ b/tools/cli/installers/lib/ide/manager.js @@ -128,6 +128,11 @@ class IdeManager { continue; } + // Skip suspended platforms (e.g., IDE doesn't support skills yet) + if (handler.platformConfig?.suspended) { + continue; + } + ides.push({ value: key, name: name, @@ -177,6 +182,18 @@ class IdeManager { 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 { const handlerResult = await handler.setup(projectDir, bmadDir, options); // Build detail string from handler-returned data diff --git a/tools/cli/installers/lib/ide/platform-codes.yaml b/tools/cli/installers/lib/ide/platform-codes.yaml index 8d82badeb..efef85579 100644 --- a/tools/cli/installers/lib/ide/platform-codes.yaml +++ b/tools/cli/installers/lib/ide/platform-codes.yaml @@ -156,6 +156,7 @@ platforms: preferred: false category: ide 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: legacy_targets: - .kilocode/workflows diff --git a/tools/docs/native-skills-migration-checklist.md b/tools/docs/native-skills-migration-checklist.md index 83b0f23c6..84284c7f9 100644 --- a/tools/docs/native-skills-migration-checklist.md +++ b/tools/docs/native-skills-migration-checklist.md @@ -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 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 -- [ ] **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] 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] 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) -- [ ] **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 ## iFlow