Migrate OpenCode installer to native skills

This commit is contained in:
Alex Verkhovsky 2026-03-06 05:17:00 -07:00
parent 44ae403db6
commit 2f8443be80
3 changed files with 118 additions and 16 deletions

View File

@ -374,9 +374,112 @@ async function runTests() {
console.log('');
// ============================================================
// Test 8: QA Agent Compilation
// Test 8: OpenCode Native Skills Install
// ============================================================
console.log(`${colors.yellow}Test Suite 8: QA Agent Compilation${colors.reset}\n`);
console.log(`${colors.yellow}Test Suite 8: OpenCode Native Skills${colors.reset}\n`);
try {
clearCache();
const platformCodes = await loadPlatformCodes();
const opencodeInstaller = platformCodes.platforms.opencode?.installer;
assert(opencodeInstaller?.target_dir === '.opencode/skills', 'OpenCode target_dir uses native skills path');
assert(opencodeInstaller?.skill_format === true, 'OpenCode installer enables native skill output');
assert(opencodeInstaller?.ancestor_conflict_check === true, 'OpenCode installer enables ancestor conflict checks');
assert(
Array.isArray(opencodeInstaller?.legacy_targets) &&
['.opencode/agents', '.opencode/commands', '.opencode/agent', '.opencode/command'].every((legacyTarget) =>
opencodeInstaller.legacy_targets.includes(legacyTarget),
),
'OpenCode installer cleans split legacy agent and command output',
);
const tempProjectDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-opencode-test-'));
const installedBmadDir = await findInstalledBmadDir(projectRoot);
const legacyDirs = [
path.join(tempProjectDir, '.opencode', 'agents', 'bmad-legacy-agent'),
path.join(tempProjectDir, '.opencode', 'commands', 'bmad-legacy-command'),
path.join(tempProjectDir, '.opencode', 'agent', 'bmad-legacy-agent-singular'),
path.join(tempProjectDir, '.opencode', 'command', 'bmad-legacy-command-singular'),
];
for (const legacyDir of legacyDirs) {
await fs.ensureDir(legacyDir);
await fs.writeFile(path.join(legacyDir, 'SKILL.md'), 'legacy\n');
await fs.writeFile(path.join(path.dirname(legacyDir), `${path.basename(legacyDir)}.md`), 'legacy\n');
}
const ideManager = new IdeManager();
await ideManager.ensureInitialized();
const result = await ideManager.setup('opencode', tempProjectDir, installedBmadDir, {
silent: true,
selectedModules: ['bmm'],
});
assert(result.success === true, 'OpenCode setup succeeds against temp project');
const skillFile = path.join(tempProjectDir, '.opencode', 'skills', 'bmad-master', 'SKILL.md');
assert(await fs.pathExists(skillFile), 'OpenCode install writes SKILL.md directory output');
for (const legacyDir of ['agents', 'commands', 'agent', 'command']) {
assert(
!(await fs.pathExists(path.join(tempProjectDir, '.opencode', legacyDir))),
`OpenCode setup removes legacy .opencode/${legacyDir} dir`,
);
}
await fs.remove(tempProjectDir);
} catch (error) {
assert(false, 'OpenCode native skills migration test succeeds', error.message);
}
console.log('');
// ============================================================
// Test 9: OpenCode Ancestor Conflict
// ============================================================
console.log(`${colors.yellow}Test Suite 9: OpenCode Ancestor Conflict${colors.reset}\n`);
try {
const tempRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-opencode-ancestor-test-'));
const parentProjectDir = path.join(tempRoot, 'parent');
const childProjectDir = path.join(parentProjectDir, 'child');
const installedBmadDir = await findInstalledBmadDir(projectRoot);
await fs.ensureDir(path.join(parentProjectDir, '.git'));
await fs.ensureDir(path.join(parentProjectDir, '.opencode', 'skills', 'bmad-existing'));
await fs.ensureDir(childProjectDir);
await fs.writeFile(path.join(parentProjectDir, '.opencode', 'skills', 'bmad-existing', 'SKILL.md'), 'legacy\n');
const ideManager = new IdeManager();
await ideManager.ensureInitialized();
const result = await ideManager.setup('opencode', childProjectDir, installedBmadDir, {
silent: true,
selectedModules: ['bmm'],
});
const expectedConflictDir = await fs.realpath(path.join(parentProjectDir, '.opencode', 'skills'));
assert(result.success === false, 'OpenCode setup refuses install when ancestor skills already exist');
assert(result.handlerResult?.reason === 'ancestor-conflict', 'OpenCode ancestor rejection reports ancestor-conflict reason');
assert(
result.handlerResult?.conflictDir === expectedConflictDir,
'OpenCode ancestor rejection points at ancestor .opencode/skills dir',
);
await fs.remove(tempRoot);
} catch (error) {
assert(false, 'OpenCode ancestor conflict protection test succeeds', error.message);
}
console.log('');
// ============================================================
// Test 10: QA Agent Compilation
// ============================================================
console.log(`${colors.yellow}Test Suite 10: QA Agent Compilation${colors.reset}\n`);
try {
const builder = new YamlXmlBuilder();

View File

@ -156,15 +156,14 @@ platforms:
description: "OpenCode terminal coding assistant"
installer:
legacy_targets:
- .opencode/agents
- .opencode/commands
- .opencode/agent
- .opencode/command
targets:
- target_dir: .opencode/agents
template_type: opencode
artifact_types: [agents]
- target_dir: .opencode/commands
template_type: opencode
artifact_types: [workflows, tasks, tools]
target_dir: .opencode/skills
template_type: opencode
skill_format: true
ancestor_conflict_check: true
qwen:
name: "QwenCoder"

View File

@ -143,13 +143,13 @@ Support assumption: full Agent Skills support. Kiro docs confirm project skills
Support assumption: full Agent Skills support. BMAD currently splits output between `.opencode/agents` and `.opencode/commands`; target should consolidate to `.opencode/skills`.
- [ ] Confirm OpenCode native skills path and whether `.claude/skills` or `.agents/skills` compatibility matters
- [ ] Implement installer migration from multi-target legacy output to single native skills target
- [ ] Add legacy cleanup for `.opencode/agents`, `.opencode/commands`, `.opencode/agent`, and `.opencode/command`
- [ ] Test fresh install
- [ ] Test reinstall/upgrade from split legacy output
- [ ] Confirm ancestor conflict protection if OpenCode inherits parent-directory skills
- [ ] Implement/extend automated tests
- [x] Confirm OpenCode native skills path and compatibility loading from `.claude/skills` and `.agents/skills` in OpenCode docs
- [x] Implement installer migration from multi-target legacy output to single native skills target
- [x] Add legacy cleanup for `.opencode/agents`, `.opencode/commands`, `.opencode/agent`, and `.opencode/command`
- [x] Test fresh install
- [x] Test reinstall/upgrade from split legacy output
- [x] Confirm ancestor conflict protection is required because OpenCode docs state project-local skill discovery walks upward to the git worktree
- [x] Implement/extend automated tests
- [ ] Commit
## Roo Code