|
|
|
|
@ -18,7 +18,6 @@ const { Installer } = require('../tools/installer/core/installer');
|
|
|
|
|
const { ManifestGenerator } = require('../tools/installer/core/manifest-generator');
|
|
|
|
|
const { OfficialModules } = require('../tools/installer/modules/official-modules');
|
|
|
|
|
const { IdeManager } = require('../tools/installer/ide/manager');
|
|
|
|
|
const { ExternalModuleManager } = require('../tools/installer/modules/external-manager');
|
|
|
|
|
const { clearCache, loadPlatformCodes } = require('../tools/installer/ide/platform-codes');
|
|
|
|
|
|
|
|
|
|
// ANSI colors
|
|
|
|
|
@ -86,28 +85,6 @@ async function createTestBmadFixture() {
|
|
|
|
|
return fixtureDir;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function createAutomatorSourceRootFixture() {
|
|
|
|
|
const repoRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-automator-source-'));
|
|
|
|
|
const sourceRoot = path.join(repoRoot, 'skills');
|
|
|
|
|
|
|
|
|
|
for (const skillName of ['bmad-story-automator', 'bmad-story-automator-review']) {
|
|
|
|
|
const skillDir = path.join(sourceRoot, skillName);
|
|
|
|
|
await fs.ensureDir(skillDir);
|
|
|
|
|
await fs.writeFile(
|
|
|
|
|
path.join(skillDir, 'SKILL.md'),
|
|
|
|
|
['---', `name: ${skillName}`, 'description: Automator skill', '---', '', 'Automator body.'].join('\n'),
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const storySkillDir = path.join(sourceRoot, 'bmad-story-automator');
|
|
|
|
|
await fs.ensureDir(path.join(storySkillDir, 'scripts'));
|
|
|
|
|
await fs.writeFile(path.join(storySkillDir, 'scripts', 'story-automator'), '#!/usr/bin/env bash\n');
|
|
|
|
|
await fs.ensureDir(path.join(storySkillDir, 'src', 'story_automator'));
|
|
|
|
|
await fs.writeFile(path.join(storySkillDir, 'src', 'story_automator', 'cli.py'), 'def main():\n return 0\n');
|
|
|
|
|
|
|
|
|
|
return { repoRoot, sourceRoot };
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async function createSkillCollisionFixture() {
|
|
|
|
|
const fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-skill-collision-'));
|
|
|
|
|
const fixtureDir = path.join(fixtureRoot, '_bmad');
|
|
|
|
|
@ -3543,73 +3520,6 @@ async function runTests() {
|
|
|
|
|
|
|
|
|
|
console.log('');
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Test Suite 45: Automator External Skill-Only Module
|
|
|
|
|
// ============================================================
|
|
|
|
|
console.log(`${colors.yellow}Test Suite 45: Automator External Skill-Only Module${colors.reset}\n`);
|
|
|
|
|
|
|
|
|
|
let automatorSourceFixture42;
|
|
|
|
|
let runtimeTargetRoot42;
|
|
|
|
|
try {
|
|
|
|
|
const yaml42 = require('yaml');
|
|
|
|
|
const fallbackConfig42 = yaml42.parse(
|
|
|
|
|
await fs.readFile(path.join(__dirname, '..', 'tools', 'installer', 'modules', 'registry-fallback.yaml'), 'utf8'),
|
|
|
|
|
);
|
|
|
|
|
const automatorEntry42 = fallbackConfig42.modules['bmad-automator'];
|
|
|
|
|
assert(automatorEntry42?.code === 'baut', 'BMad Automator fallback registry code is baut');
|
|
|
|
|
assert(automatorEntry42?.['source-root'] === 'skills', 'BMad Automator fallback registry points at root skills');
|
|
|
|
|
|
|
|
|
|
const externalManager42 = new ExternalModuleManager();
|
|
|
|
|
const automatorInfo42 = externalManager42._normalizeModule(automatorEntry42);
|
|
|
|
|
assert(automatorInfo42.type === 'experimental', 'BMad Automator is marked experimental');
|
|
|
|
|
assert(automatorInfo42.sourceRoot === 'skills', 'BMad Automator uses root skills source-root for pure skill payload');
|
|
|
|
|
assert(automatorInfo42.defaultChannel === 'next', 'BMad Automator defaults to next for latest payload compatibility fixes');
|
|
|
|
|
|
|
|
|
|
automatorSourceFixture42 = await createAutomatorSourceRootFixture();
|
|
|
|
|
runtimeTargetRoot42 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-automator-runtime-target-'));
|
|
|
|
|
const runtimeBmadDir42 = path.join(runtimeTargetRoot42, '_bmad');
|
|
|
|
|
const officialModules42 = new OfficialModules();
|
|
|
|
|
officialModules42.findModuleSource = async () => automatorSourceFixture42.sourceRoot;
|
|
|
|
|
await officialModules42.install('baut', runtimeBmadDir42, null, { skipModuleInstaller: true, silent: true });
|
|
|
|
|
assert(
|
|
|
|
|
await fs.pathExists(path.join(runtimeBmadDir42, 'baut', 'bmad-story-automator', 'scripts', 'story-automator')),
|
|
|
|
|
'BMad Automator self-contained skill install includes runtime helper',
|
|
|
|
|
);
|
|
|
|
|
assert(
|
|
|
|
|
await fs.pathExists(path.join(runtimeBmadDir42, 'baut', 'bmad-story-automator', 'src', 'story_automator', 'cli.py')),
|
|
|
|
|
'BMad Automator self-contained skill install includes Python runtime source',
|
|
|
|
|
);
|
|
|
|
|
await fs.remove(runtimeTargetRoot42).catch(() => {});
|
|
|
|
|
runtimeTargetRoot42 = null;
|
|
|
|
|
|
|
|
|
|
const escapeRoot42 = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-source-root-'));
|
|
|
|
|
const escapeRepo42 = path.join(escapeRoot42, 'repo');
|
|
|
|
|
await fs.ensureDir(escapeRepo42);
|
|
|
|
|
const escapeManager42 = new ExternalModuleManager();
|
|
|
|
|
escapeManager42.getModuleByCode = async () => ({
|
|
|
|
|
code: 'escape',
|
|
|
|
|
builtIn: false,
|
|
|
|
|
sourceRoot: '../outside',
|
|
|
|
|
});
|
|
|
|
|
escapeManager42.cloneExternalModule = async () => escapeRepo42;
|
|
|
|
|
let rejectedEscapingSourceRoot42 = false;
|
|
|
|
|
try {
|
|
|
|
|
await escapeManager42.findExternalModuleSource('escape');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
rejectedEscapingSourceRoot42 = error.message.includes('source-root escapes repository');
|
|
|
|
|
} finally {
|
|
|
|
|
await fs.remove(escapeRoot42).catch(() => {});
|
|
|
|
|
}
|
|
|
|
|
assert(rejectedEscapingSourceRoot42, 'External module source-root cannot escape cloned repository');
|
|
|
|
|
} catch (error) {
|
|
|
|
|
assert(false, `Automator external skill-only module test succeeds: ${error.message}`);
|
|
|
|
|
} finally {
|
|
|
|
|
if (automatorSourceFixture42) await fs.remove(automatorSourceFixture42.repoRoot).catch(() => {});
|
|
|
|
|
if (runtimeTargetRoot42) await fs.remove(runtimeTargetRoot42).catch(() => {});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
console.log('');
|
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// Summary
|
|
|
|
|
// ============================================================
|
|
|
|
|
|