feat(skills): migrate Roo Code installer to native skills format

Move Roo Code from legacy `.roo/commands/` flat files to native
`.roo/skills/{skill-name}/SKILL.md` directory output. Verified
skill discovery in Roo Code v3.51 with 43 skills installed.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Alex Verkhovsky 2026-03-06 23:00:07 -07:00
parent 434e7efab6
commit 202f0ca037
3 changed files with 273 additions and 28 deletions

View File

@ -457,9 +457,238 @@ async function runTests() {
console.log('');
// ============================================================
// Test 9: OpenCode Ancestor Conflict
// Test 9: Claude Code Native Skills Install
// ============================================================
console.log(`${colors.yellow}Test Suite 9: OpenCode Ancestor Conflict${colors.reset}\n`);
console.log(`${colors.yellow}Test Suite 9: Claude Code Native Skills${colors.reset}\n`);
try {
clearCache();
const platformCodes9 = await loadPlatformCodes();
const claudeInstaller = platformCodes9.platforms['claude-code']?.installer;
assert(claudeInstaller?.target_dir === '.claude/skills', 'Claude Code target_dir uses native skills path');
assert(claudeInstaller?.skill_format === true, 'Claude Code installer enables native skill output');
assert(claudeInstaller?.ancestor_conflict_check === true, 'Claude Code installer enables ancestor conflict checks');
assert(
Array.isArray(claudeInstaller?.legacy_targets) && claudeInstaller.legacy_targets.includes('.claude/commands'),
'Claude Code installer cleans legacy command output',
);
const tempProjectDir9 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-claude-code-test-'));
const installedBmadDir9 = await createTestBmadFixture();
const legacyDir9 = path.join(tempProjectDir9, '.claude', 'commands');
await fs.ensureDir(legacyDir9);
await fs.writeFile(path.join(legacyDir9, 'bmad-legacy.md'), 'legacy\n');
const ideManager9 = new IdeManager();
await ideManager9.ensureInitialized();
const result9 = await ideManager9.setup('claude-code', tempProjectDir9, installedBmadDir9, {
silent: true,
selectedModules: ['bmm'],
});
assert(result9.success === true, 'Claude Code setup succeeds against temp project');
const skillFile9 = path.join(tempProjectDir9, '.claude', 'skills', 'bmad-master', 'SKILL.md');
assert(await fs.pathExists(skillFile9), 'Claude Code install writes SKILL.md directory output');
assert(!(await fs.pathExists(legacyDir9)), 'Claude Code setup removes legacy commands dir');
await fs.remove(tempProjectDir9);
await fs.remove(installedBmadDir9);
} catch (error) {
assert(false, 'Claude Code native skills migration test succeeds', error.message);
}
console.log('');
// ============================================================
// Test 10: Claude Code Ancestor Conflict
// ============================================================
console.log(`${colors.yellow}Test Suite 10: Claude Code Ancestor Conflict${colors.reset}\n`);
try {
const tempRoot10 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-claude-code-ancestor-test-'));
const parentProjectDir10 = path.join(tempRoot10, 'parent');
const childProjectDir10 = path.join(parentProjectDir10, 'child');
const installedBmadDir10 = await createTestBmadFixture();
await fs.ensureDir(path.join(parentProjectDir10, '.git'));
await fs.ensureDir(path.join(parentProjectDir10, '.claude', 'skills', 'bmad-existing'));
await fs.ensureDir(childProjectDir10);
await fs.writeFile(path.join(parentProjectDir10, '.claude', 'skills', 'bmad-existing', 'SKILL.md'), 'legacy\n');
const ideManager10 = new IdeManager();
await ideManager10.ensureInitialized();
const result10 = await ideManager10.setup('claude-code', childProjectDir10, installedBmadDir10, {
silent: true,
selectedModules: ['bmm'],
});
const expectedConflictDir10 = await fs.realpath(path.join(parentProjectDir10, '.claude', 'skills'));
assert(result10.success === false, 'Claude Code setup refuses install when ancestor skills already exist');
assert(result10.handlerResult?.reason === 'ancestor-conflict', 'Claude Code ancestor rejection reports ancestor-conflict reason');
assert(
result10.handlerResult?.conflictDir === expectedConflictDir10,
'Claude Code ancestor rejection points at ancestor .claude/skills dir',
);
await fs.remove(tempRoot10);
await fs.remove(installedBmadDir10);
} catch (error) {
assert(false, 'Claude Code ancestor conflict protection test succeeds', error.message);
}
console.log('');
// ============================================================
// Test 11: Codex Native Skills Install
// ============================================================
console.log(`${colors.yellow}Test Suite 11: Codex Native Skills${colors.reset}\n`);
try {
clearCache();
const platformCodes11 = await loadPlatformCodes();
const codexInstaller = platformCodes11.platforms.codex?.installer;
assert(codexInstaller?.target_dir === '.agents/skills', 'Codex target_dir uses native skills path');
assert(codexInstaller?.skill_format === true, 'Codex installer enables native skill output');
assert(codexInstaller?.ancestor_conflict_check === true, 'Codex installer enables ancestor conflict checks');
assert(
Array.isArray(codexInstaller?.legacy_targets) && codexInstaller.legacy_targets.includes('.codex/prompts'),
'Codex installer cleans legacy prompt output',
);
const tempProjectDir11 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-codex-test-'));
const installedBmadDir11 = await createTestBmadFixture();
const legacyDir11 = path.join(tempProjectDir11, '.codex', 'prompts');
await fs.ensureDir(legacyDir11);
await fs.writeFile(path.join(legacyDir11, 'bmad-legacy.md'), 'legacy\n');
const ideManager11 = new IdeManager();
await ideManager11.ensureInitialized();
const result11 = await ideManager11.setup('codex', tempProjectDir11, installedBmadDir11, {
silent: true,
selectedModules: ['bmm'],
});
assert(result11.success === true, 'Codex setup succeeds against temp project');
const skillFile11 = path.join(tempProjectDir11, '.agents', 'skills', 'bmad-master', 'SKILL.md');
assert(await fs.pathExists(skillFile11), 'Codex install writes SKILL.md directory output');
assert(!(await fs.pathExists(legacyDir11)), 'Codex setup removes legacy prompts dir');
await fs.remove(tempProjectDir11);
await fs.remove(installedBmadDir11);
} catch (error) {
assert(false, 'Codex native skills migration test succeeds', error.message);
}
console.log('');
// ============================================================
// Test 12: Codex Ancestor Conflict
// ============================================================
console.log(`${colors.yellow}Test Suite 12: Codex Ancestor Conflict${colors.reset}\n`);
try {
const tempRoot12 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-codex-ancestor-test-'));
const parentProjectDir12 = path.join(tempRoot12, 'parent');
const childProjectDir12 = path.join(parentProjectDir12, 'child');
const installedBmadDir12 = await createTestBmadFixture();
await fs.ensureDir(path.join(parentProjectDir12, '.git'));
await fs.ensureDir(path.join(parentProjectDir12, '.agents', 'skills', 'bmad-existing'));
await fs.ensureDir(childProjectDir12);
await fs.writeFile(path.join(parentProjectDir12, '.agents', 'skills', 'bmad-existing', 'SKILL.md'), 'legacy\n');
const ideManager12 = new IdeManager();
await ideManager12.ensureInitialized();
const result12 = await ideManager12.setup('codex', childProjectDir12, installedBmadDir12, {
silent: true,
selectedModules: ['bmm'],
});
const expectedConflictDir12 = await fs.realpath(path.join(parentProjectDir12, '.agents', 'skills'));
assert(result12.success === false, 'Codex setup refuses install when ancestor skills already exist');
assert(result12.handlerResult?.reason === 'ancestor-conflict', 'Codex ancestor rejection reports ancestor-conflict reason');
assert(result12.handlerResult?.conflictDir === expectedConflictDir12, 'Codex ancestor rejection points at ancestor .agents/skills dir');
await fs.remove(tempRoot12);
await fs.remove(installedBmadDir12);
} catch (error) {
assert(false, 'Codex ancestor conflict protection test succeeds', error.message);
}
console.log('');
// ============================================================
// Test 13: Roo Code Native Skills Install
// ============================================================
console.log(`${colors.yellow}Test Suite 13: Roo Code Native Skills${colors.reset}\n`);
try {
clearCache();
const platformCodes13 = await loadPlatformCodes();
const rooInstaller = platformCodes13.platforms.roo?.installer;
assert(rooInstaller?.target_dir === '.roo/skills', 'Roo target_dir uses native skills path');
assert(rooInstaller?.skill_format === true, 'Roo installer enables native skill output');
assert(
Array.isArray(rooInstaller?.legacy_targets) && rooInstaller.legacy_targets.includes('.roo/commands'),
'Roo installer cleans legacy command output',
);
const tempProjectDir13 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-roo-test-'));
const installedBmadDir13 = await createTestBmadFixture();
const legacyDir13 = path.join(tempProjectDir13, '.roo', 'commands', 'bmad-legacy-dir');
await fs.ensureDir(legacyDir13);
await fs.writeFile(path.join(tempProjectDir13, '.roo', 'commands', 'bmad-legacy.md'), 'legacy\n');
await fs.writeFile(path.join(legacyDir13, 'SKILL.md'), 'legacy\n');
const ideManager13 = new IdeManager();
await ideManager13.ensureInitialized();
const result13 = await ideManager13.setup('roo', tempProjectDir13, installedBmadDir13, {
silent: true,
selectedModules: ['bmm'],
});
assert(result13.success === true, 'Roo setup succeeds against temp project');
const skillFile13 = path.join(tempProjectDir13, '.roo', 'skills', 'bmad-master', 'SKILL.md');
assert(await fs.pathExists(skillFile13), 'Roo install writes SKILL.md directory output');
// Verify name frontmatter matches directory name (Roo constraint: lowercase alphanumeric + hyphens)
const skillContent13 = await fs.readFile(skillFile13, 'utf8');
const nameMatch13 = skillContent13.match(/^name:\s*(.+)$/m);
assert(
nameMatch13 && nameMatch13[1].trim() === 'bmad-master',
'Roo skill name frontmatter matches directory name exactly (lowercase alphanumeric + hyphens)',
);
assert(!(await fs.pathExists(path.join(tempProjectDir13, '.roo', 'commands'))), 'Roo setup removes legacy commands dir');
await fs.remove(tempProjectDir13);
await fs.remove(installedBmadDir13);
} catch (error) {
assert(false, 'Roo native skills migration test succeeds', error.message);
}
console.log('');
// ============================================================
// Test 14: OpenCode Ancestor Conflict
// ============================================================
console.log(`${colors.yellow}Test Suite 14: OpenCode Ancestor Conflict${colors.reset}\n`);
try {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-opencode-ancestor-test-'));
@ -496,9 +725,9 @@ async function runTests() {
console.log('');
// ============================================================
// Test 10: QA Agent Compilation
// Test 15: QA Agent Compilation
// ============================================================
console.log(`${colors.yellow}Test Suite 10: QA Agent Compilation${colors.reset}\n`);
console.log(`${colors.yellow}Test Suite 15: QA Agent Compilation${colors.reset}\n`);
try {
const builder = new YamlXmlBuilder();

View File

@ -180,8 +180,11 @@ platforms:
category: ide
description: "Enhanced Cline fork"
installer:
target_dir: .roo/commands
legacy_targets:
- .roo/commands
target_dir: .roo/skills
template_type: default
skill_format: true
rovo-dev:
name: "Rovo Dev"

View File

@ -15,25 +15,27 @@ This checklist now includes those completed platforms plus the remaining full-su
Support assumption: full Agent Skills support. BMAD has already migrated from `.claude/commands` to `.claude/skills`.
- [ ] Confirm current implementation still matches Claude Code skills expectations
- [ ] Confirm legacy cleanup for `.claude/commands`
- [ ] Test fresh install
- [ ] Test reinstall/upgrade from legacy command output
- [ ] Confirm ancestor conflict protection
- [ ] Implement/extend automated tests as needed
- [ ] Commit any follow-up fixes if required
**Install:** `npm install -g @anthropic-ai/claude-code` or `brew install claude-code`
- [x] Confirm current implementation still matches Claude Code skills expectations
- [x] Confirm legacy cleanup for `.claude/commands`
- [x] Test fresh install
- [x] Test reinstall/upgrade from legacy command output
- [x] Confirm ancestor conflict protection because Claude Code inherits skills from parent directories and `ancestor_conflict_check: true` is set in platform-codes.yaml
- [x] Implement/extend automated tests as needed
## Codex CLI
Support assumption: full Agent Skills support. BMAD has already migrated from `.codex/prompts` to `.agents/skills`.
- [ ] Confirm current implementation still matches Codex CLI skills expectations
- [ ] Confirm legacy cleanup for project and global `.codex/prompts`
- [ ] Test fresh install
- [ ] Test reinstall/upgrade from legacy prompt output
**Install:** `npm install -g @openai/codex`
- [x] Confirm current implementation still matches Codex CLI skills expectations
- [x] Confirm legacy cleanup for project and global `.codex/prompts`
- [x] Test fresh install
- [x] Test reinstall/upgrade from legacy prompt output
- [x] Confirm ancestor conflict protection because Codex inherits parent-directory `.agents/skills`
- [ ] Implement/extend automated tests as needed
- [ ] Commit any follow-up fixes if required
- [x] Implement/extend automated tests as needed
## Cursor
@ -46,7 +48,7 @@ Support assumption: full Agent Skills support. BMAD currently installs legacy co
- [x] Test reinstall/upgrade from legacy command output
- [x] Confirm no ancestor conflict protection is needed because a child workspace surfaced child `.cursor/skills` entries but not a parent-only skill during manual verification
- [ ] Implement/extend automated tests
- [x] Commit
- [ ] Commit
## Windsurf
@ -59,12 +61,13 @@ Support assumption: full Agent Skills support. Windsurf docs confirm workspace s
- [x] Test reinstall/upgrade from legacy workflow output
- [x] Confirm no ancestor conflict protection is needed because manual Windsurf verification showed child-local `@` skills loaded while a parent-only skill was not inherited
- [x] Implement/extend automated tests
- [x] Commit
## Cline
Support assumption: full Agent Skills support. BMAD currently installs workflow files to `.clinerules/workflows`; target should move to the platform's native skills directory.
**Install:** VS Code extension `saoudrizwan.claude-dev` — search "Cline" in Extensions or `code --install-extension saoudrizwan.claude-dev`
- [ ] Confirm current Cline skills path and whether `.cline/skills` is the correct BMAD target
- [ ] Implement installer migration to native skills output
- [ ] Add legacy cleanup for `.clinerules/workflows`
@ -85,7 +88,6 @@ Support assumption: full Agent Skills support. Antigravity docs confirm workspac
- [x] Test reinstall/upgrade from legacy workflow output
- [x] Confirm no ancestor conflict protection is needed because manual Antigravity verification in `/tmp/antigravity-ancestor-repro/parent/child` showed only the child-local `child-only` skill, with no inherited parent `.agent/skills` entry
- [x] Implement/extend automated tests
- [x] Commit
## Auggie
@ -104,6 +106,8 @@ Support assumption: full Agent Skills support. BMAD currently installs commands
Support assumption: full Agent Skills support. BMAD currently installs commands to `.codebuddy/commands`; target should move to `.codebuddy/skills`.
**Install:** Download [Tencent CodeBuddy IDE](https://codebuddyide.net/) or install as VS Code extension `CodebuddyAI.codebuddy-ai`
- [ ] Confirm CodeBuddy native skills path and any naming/frontmatter requirements
- [ ] Implement installer migration to native skills output
- [ ] Add legacy cleanup for `.codebuddy/commands`
@ -117,6 +121,8 @@ Support assumption: full Agent Skills support. BMAD currently installs commands
Support assumption: full Agent Skills support. BMAD currently installs commands to `.crush/commands`; target should move to the platform's native skills location.
**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`
@ -137,7 +143,6 @@ Support assumption: full Agent Skills support. Kiro docs confirm project skills
- [x] Test reinstall/upgrade from legacy steering output
- [x] Confirm no ancestor conflict protection is needed because manual Kiro verification showed Slash-visible skills from the current workspace only, with no ancestor `.kiro/skills` inheritance
- [x] Implement/extend automated tests
- [x] Commit
## OpenCode
@ -156,19 +161,23 @@ Support assumption: full Agent Skills support. BMAD currently splits output betw
Support assumption: full Agent Skills support. BMAD currently installs commands to `.roo/commands`; target should move to `.roo/skills` or the correct mode-aware skill directories.
- [ ] Confirm Roo native skills path and whether BMAD should use generic or mode-specific skill directories
- [ ] Implement installer migration to native skills output
- [ ] Add legacy cleanup for `.roo/commands`
- [ ] Test fresh install
**Install:** VS Code extension `RooVeterinaryInc.roo-cline` — search "Roo Code" in Extensions or `code --install-extension RooVeterinaryInc.roo-cline`
- [x] Confirm Roo native skills path is `.roo/skills/{skill-name}/SKILL.md` with `name` frontmatter matching directory exactly (lowercase, alphanumeric + hyphens only)
- [x] Implement installer migration to native skills output
- [x] Add legacy cleanup for `.roo/commands`
- [x] Test fresh install — 43 skills installed, verified in Roo Code v3.51
- [ ] Test reinstall/upgrade from legacy command output
- [ ] Confirm ancestor conflict protection where applicable
- [ ] Implement/extend automated tests
- [x] Confirm no ancestor conflict protection is needed because manual Roo Code v3.51 verification showed child-local `child-only` skill loaded while parent-only skill was not inherited
- [x] Implement/extend automated tests — 7 assertions in test suite 13
- [ ] Commit
## Trae
Support assumption: full Agent Skills support. BMAD currently installs rule files to `.trae/rules`; target should move to the platform's native skills directory.
**Install:** Download [standalone IDE](https://www.trae.ai/download) (macOS/Windows/Linux) or `winget install -e --id ByteDance.Trae`
- [ ] Confirm Trae native skills path and whether the current `.trae/rules` path is still required for compatibility
- [ ] Implement installer migration to native skills output
- [ ] Add legacy cleanup for `.trae/rules`
@ -182,6 +191,8 @@ Support assumption: full Agent Skills support. BMAD currently installs rule file
Support assumption: full Agent Skills support. BMAD currently uses a custom installer that generates `.github/agents`, `.github/prompts`, and `.github/copilot-instructions.md`; target should move to `.github/skills`.
**Install:** VS Code extension `GitHub.copilot` — search "GitHub Copilot" in Extensions or `code --install-extension GitHub.copilot`
- [ ] Confirm GitHub Copilot native skills path and whether `.github/agents` remains necessary as a compatibility layer
- [ ] Design the migration away from the custom prompt/agent installer model
- [ ] Implement native skills output, ideally with shared config-driven code where practical
@ -196,6 +207,8 @@ Support assumption: full Agent Skills support. BMAD currently uses a custom inst
Support assumption: full Agent Skills support. BMAD currently uses a custom installer that writes `.kilocodemodes` and `.kilocode/workflows`; target should move to native skills output.
**Install:** VS Code extension `kilocode.kilo-code` — search "Kilo Code" in Extensions or `code --install-extension kilocode.kilo-code`
- [ ] Confirm KiloCoder native skills path and whether `.kilocodemodes` should be removed entirely or retained temporarily for compatibility
- [ ] Design the migration away from modes plus workflow markdown
- [ ] Implement native skills output