From 41c9b6bcfd662506a449cff0e4b214fcb3de2442 Mon Sep 17 00:00:00 2001 From: Alex Verkhovsky Date: Sat, 7 Mar 2026 00:17:53 -0700 Subject: [PATCH] feat(skills): migrate Crush to config-driven native skills Move Crush installer from .crush/commands to .crush/skills with SKILL.md directory output. Add legacy cleanup and 9 test assertions. --- test/test-installation-components.js | 60 +++++++++++++++++++ .../installers/lib/ide/platform-codes.yaml | 5 +- .../docs/native-skills-migration-checklist.md | 19 +++--- 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/test/test-installation-components.js b/test/test-installation-components.js index 95cd31603..fed98fe5b 100644 --- a/test/test-installation-components.js +++ b/test/test-installation-components.js @@ -1015,6 +1015,66 @@ async function runTests() { console.log(''); + // ============================================================ + // Test 20: Crush Native Skills Install + // ============================================================ + console.log(`${colors.yellow}Test Suite 20: Crush Native Skills${colors.reset}\n`); + + try { + clearCache(); + const platformCodes20 = await loadPlatformCodes(); + const crushInstaller = platformCodes20.platforms.crush?.installer; + + assert(crushInstaller?.target_dir === '.crush/skills', 'Crush target_dir uses native skills path'); + + assert(crushInstaller?.skill_format === true, 'Crush installer enables native skill output'); + + assert( + Array.isArray(crushInstaller?.legacy_targets) && crushInstaller.legacy_targets.includes('.crush/commands'), + 'Crush installer cleans legacy command output', + ); + + const tempProjectDir20 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-crush-test-')); + const installedBmadDir20 = await createTestBmadFixture(); + const legacyDir20 = path.join(tempProjectDir20, '.crush', 'commands', 'bmad-legacy-dir'); + await fs.ensureDir(legacyDir20); + await fs.writeFile(path.join(tempProjectDir20, '.crush', 'commands', 'bmad-legacy.md'), 'legacy\n'); + await fs.writeFile(path.join(legacyDir20, 'SKILL.md'), 'legacy\n'); + + const ideManager20 = new IdeManager(); + await ideManager20.ensureInitialized(); + const result20 = await ideManager20.setup('crush', tempProjectDir20, installedBmadDir20, { + silent: true, + selectedModules: ['bmm'], + }); + + assert(result20.success === true, 'Crush setup succeeds against temp project'); + + const skillFile20 = path.join(tempProjectDir20, '.crush', 'skills', 'bmad-master', 'SKILL.md'); + assert(await fs.pathExists(skillFile20), 'Crush install writes SKILL.md directory output'); + + const skillContent20 = await fs.readFile(skillFile20, 'utf8'); + const nameMatch20 = skillContent20.match(/^name:\s*(.+)$/m); + assert(nameMatch20 && nameMatch20[1].trim() === 'bmad-master', 'Crush skill name frontmatter matches directory name exactly'); + + assert(!(await fs.pathExists(path.join(tempProjectDir20, '.crush', 'commands'))), 'Crush setup removes legacy commands dir'); + + const result20b = await ideManager20.setup('crush', tempProjectDir20, installedBmadDir20, { + silent: true, + selectedModules: ['bmm'], + }); + + assert(result20b.success === true, 'Crush reinstall/upgrade succeeds over existing skills'); + assert(await fs.pathExists(skillFile20), 'Crush reinstall preserves SKILL.md output'); + + await fs.remove(tempProjectDir20); + await fs.remove(installedBmadDir20); + } catch (error) { + assert(false, 'Crush native skills migration test succeeds', error.message); + } + + console.log(''); + // ============================================================ // Summary // ============================================================ diff --git a/tools/cli/installers/lib/ide/platform-codes.yaml b/tools/cli/installers/lib/ide/platform-codes.yaml index fa948ab97..4e9312588 100644 --- a/tools/cli/installers/lib/ide/platform-codes.yaml +++ b/tools/cli/installers/lib/ide/platform-codes.yaml @@ -96,8 +96,11 @@ platforms: category: ide description: "AI development assistant" installer: - target_dir: .crush/commands + legacy_targets: + - .crush/commands + target_dir: .crush/skills template_type: default + skill_format: true cursor: name: "Cursor" diff --git a/tools/docs/native-skills-migration-checklist.md b/tools/docs/native-skills-migration-checklist.md index 51d545167..b28f7e621 100644 --- a/tools/docs/native-skills-migration-checklist.md +++ b/tools/docs/native-skills-migration-checklist.md @@ -119,18 +119,19 @@ Support assumption: full Agent Skills support. CodeBuddy docs confirm workspace ## Crush -Support assumption: full Agent Skills support. BMAD currently installs commands to `.crush/commands`; target should move to the platform's native skills location. +Support assumption: full Agent Skills support. Crush scans project-local `.crush/skills/` exclusively ([GitHub issue #2072](https://github.com/charmbracelet/crush/issues/2072) confirms this and requests adding `~/.agents/skills/`). BMAD has now migrated from `.crush/commands` to `.crush/skills`. **Install:** `brew install charmbracelet/tap/crush` (macOS/Linux) or `winget install charmbracelet.crush` (Windows) -- [ ] Confirm Crush project-local versus global skills path and BMAD's preferred install target -- [ ] Implement installer migration to native skills output -- [ ] Add legacy cleanup for `.crush/commands` -- [ ] Test fresh install -- [ ] Test reinstall/upgrade from legacy command output -- [ ] Confirm ancestor conflict protection where applicable -- [ ] Implement/extend automated tests -- [ ] Commit +- [x] Confirm Crush project-local skills path is `.crush/skills/{skill-name}/SKILL.md` — per GitHub issue #2072 confirming `.crush/skills/` is the only scan path +- [x] Implement installer migration to native skills output +- [x] Add legacy cleanup for `.crush/commands` +- [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] Implement/extend automated tests — 9 assertions in test suite 20 +- [x] Commit ## Kiro