Compare commits
No commits in common. "75ec4aa504bce8078c36276ce969946f530cbeb0" and "861716fbe3dd17618fa4e56eac5eca8c4ba39b61" have entirely different histories.
75ec4aa504
...
861716fbe3
|
|
@ -37,7 +37,7 @@ permissions:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
publish:
|
publish:
|
||||||
if: github.repository == 'bmad-code-org/BMAD-METHOD' && (github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main')
|
if: github.event_name != 'workflow_dispatch' || github.ref == 'refs/heads/main'
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
|
|
|
||||||
|
|
@ -81,60 +81,6 @@ async function createTestBmadFixture() {
|
||||||
return fixtureDir;
|
return fixtureDir;
|
||||||
}
|
}
|
||||||
|
|
||||||
async function createSkillCollisionFixture() {
|
|
||||||
const fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-skill-collision-'));
|
|
||||||
const fixtureDir = path.join(fixtureRoot, '_bmad');
|
|
||||||
const configDir = path.join(fixtureDir, '_config');
|
|
||||||
await fs.ensureDir(configDir);
|
|
||||||
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(configDir, 'agent-manifest.csv'),
|
|
||||||
[
|
|
||||||
'name,displayName,title,icon,capabilities,role,identity,communicationStyle,principles,module,path,canonicalId',
|
|
||||||
'"bmad-master","BMAD Master","","","","","","","","core","_bmad/core/agents/bmad-master.md","bmad-master"',
|
|
||||||
'',
|
|
||||||
].join('\n'),
|
|
||||||
);
|
|
||||||
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(configDir, 'workflow-manifest.csv'),
|
|
||||||
[
|
|
||||||
'name,description,module,path,canonicalId',
|
|
||||||
'"help","Workflow help","core","_bmad/core/workflows/help/workflow.md","bmad-help"',
|
|
||||||
'',
|
|
||||||
].join('\n'),
|
|
||||||
);
|
|
||||||
|
|
||||||
await fs.writeFile(path.join(configDir, 'task-manifest.csv'), 'name,displayName,description,module,path,standalone,canonicalId\n');
|
|
||||||
await fs.writeFile(path.join(configDir, 'tool-manifest.csv'), 'name,displayName,description,module,path,standalone,canonicalId\n');
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(configDir, 'skill-manifest.csv'),
|
|
||||||
[
|
|
||||||
'canonicalId,name,description,module,path,install_to_bmad',
|
|
||||||
'"bmad-help","bmad-help","Native help skill","core","_bmad/core/tasks/bmad-help/SKILL.md","true"',
|
|
||||||
'',
|
|
||||||
].join('\n'),
|
|
||||||
);
|
|
||||||
|
|
||||||
const skillDir = path.join(fixtureDir, 'core', 'tasks', 'bmad-help');
|
|
||||||
await fs.ensureDir(skillDir);
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(skillDir, 'SKILL.md'),
|
|
||||||
['---', 'name: bmad-help', 'description: Native help skill', '---', '', 'Use this skill directly.'].join('\n'),
|
|
||||||
);
|
|
||||||
|
|
||||||
const agentDir = path.join(fixtureDir, 'core', 'agents');
|
|
||||||
await fs.ensureDir(agentDir);
|
|
||||||
await fs.writeFile(
|
|
||||||
path.join(agentDir, 'bmad-master.md'),
|
|
||||||
['---', 'name: BMAD Master', 'description: Master agent', '---', '', '<agent name="BMAD Master" title="Master">', '</agent>'].join(
|
|
||||||
'\n',
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
return { root: fixtureRoot, bmadDir: fixtureDir };
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test Suite
|
* Test Suite
|
||||||
*/
|
*/
|
||||||
|
|
@ -1824,50 +1770,6 @@ async function runTests() {
|
||||||
|
|
||||||
console.log('');
|
console.log('');
|
||||||
|
|
||||||
// ============================================================
|
|
||||||
// Test 31: Skill-format installs report unique skill directories
|
|
||||||
// ============================================================
|
|
||||||
console.log(`${colors.yellow}Test Suite 31: Skill Count Reporting${colors.reset}\n`);
|
|
||||||
|
|
||||||
let collisionFixtureRoot = null;
|
|
||||||
let collisionProjectDir = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
clearCache();
|
|
||||||
const collisionFixture = await createSkillCollisionFixture();
|
|
||||||
collisionFixtureRoot = collisionFixture.root;
|
|
||||||
collisionProjectDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-antigravity-test-'));
|
|
||||||
|
|
||||||
const ideManager = new IdeManager();
|
|
||||||
await ideManager.ensureInitialized();
|
|
||||||
const result = await ideManager.setup('antigravity', collisionProjectDir, collisionFixture.bmadDir, {
|
|
||||||
silent: true,
|
|
||||||
selectedModules: ['core'],
|
|
||||||
});
|
|
||||||
|
|
||||||
assert(result.success === true, 'Antigravity setup succeeds with overlapping skill names');
|
|
||||||
assert(result.detail === '2 agents', 'Installer detail reports agents separately from skills');
|
|
||||||
assert(result.handlerResult.results.skillDirectories === 2, 'Result exposes unique skill directory count');
|
|
||||||
assert(result.handlerResult.results.agents === 2, 'Result retains generated agent write count');
|
|
||||||
assert(result.handlerResult.results.workflows === 1, 'Result retains generated workflow count');
|
|
||||||
assert(result.handlerResult.results.skills === 1, 'Result retains verbatim skill count');
|
|
||||||
assert(
|
|
||||||
await fs.pathExists(path.join(collisionProjectDir, '.agent', 'skills', 'bmad-agent-bmad-master', 'SKILL.md')),
|
|
||||||
'Agent skill directory is created',
|
|
||||||
);
|
|
||||||
assert(
|
|
||||||
await fs.pathExists(path.join(collisionProjectDir, '.agent', 'skills', 'bmad-help', 'SKILL.md')),
|
|
||||||
'Overlapping skill directory is created once',
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
assert(false, 'Skill-format unique count test succeeds', error.message);
|
|
||||||
} finally {
|
|
||||||
if (collisionProjectDir) await fs.remove(collisionProjectDir).catch(() => {});
|
|
||||||
if (collisionFixtureRoot) await fs.remove(collisionFixtureRoot).catch(() => {});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('');
|
|
||||||
|
|
||||||
// ============================================================
|
// ============================================================
|
||||||
// Summary
|
// Summary
|
||||||
// ============================================================
|
// ============================================================
|
||||||
|
|
|
||||||
|
|
@ -1153,6 +1153,12 @@ class Installer {
|
||||||
preservedModules: modulesForCsvPreserve,
|
preservedModules: modulesForCsvPreserve,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addResult(
|
||||||
|
'Manifests',
|
||||||
|
'ok',
|
||||||
|
`${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools`,
|
||||||
|
);
|
||||||
|
|
||||||
// Merge help catalogs
|
// Merge help catalogs
|
||||||
message('Generating help catalog...');
|
message('Generating help catalog...');
|
||||||
await this.mergeModuleHelpCatalogs(bmadDir);
|
await this.mergeModuleHelpCatalogs(bmadDir);
|
||||||
|
|
@ -1373,27 +1379,10 @@ class Installer {
|
||||||
*/
|
*/
|
||||||
async renderInstallSummary(results, context = {}) {
|
async renderInstallSummary(results, context = {}) {
|
||||||
const color = await prompts.getColor();
|
const color = await prompts.getColor();
|
||||||
const selectedIdes = new Set((context.ides || []).map((ide) => String(ide).toLowerCase()));
|
|
||||||
|
|
||||||
// Build step lines with status indicators
|
// Build step lines with status indicators
|
||||||
const lines = [];
|
const lines = [];
|
||||||
for (const r of results) {
|
for (const r of results) {
|
||||||
let stepLabel = null;
|
|
||||||
|
|
||||||
if (r.status !== 'ok') {
|
|
||||||
stepLabel = r.step;
|
|
||||||
} else if (r.step === 'Core') {
|
|
||||||
stepLabel = 'BMAD';
|
|
||||||
} else if (r.step.startsWith('Module: ')) {
|
|
||||||
stepLabel = r.step;
|
|
||||||
} else if (selectedIdes.has(String(r.step).toLowerCase())) {
|
|
||||||
stepLabel = r.step;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!stepLabel) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let icon;
|
let icon;
|
||||||
if (r.status === 'ok') {
|
if (r.status === 'ok') {
|
||||||
icon = color.green('\u2713');
|
icon = color.green('\u2713');
|
||||||
|
|
@ -1403,11 +1392,7 @@ class Installer {
|
||||||
icon = color.red('\u2717');
|
icon = color.red('\u2717');
|
||||||
}
|
}
|
||||||
const detail = r.detail ? color.dim(` (${r.detail})`) : '';
|
const detail = r.detail ? color.dim(` (${r.detail})`) : '';
|
||||||
lines.push(` ${icon} ${stepLabel}${detail}`);
|
lines.push(` ${icon} ${r.step}${detail}`);
|
||||||
}
|
|
||||||
|
|
||||||
if ((context.ides || []).length === 0) {
|
|
||||||
lines.push(` ${color.green('\u2713')} No IDE selected ${color.dim('(installed in _bmad only)')}`);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Context and warnings
|
// Context and warnings
|
||||||
|
|
@ -1430,10 +1415,8 @@ class Installer {
|
||||||
` Join our Discord: ${color.dim('https://discord.gg/gk8jAdXWmj')}`,
|
` Join our Discord: ${color.dim('https://discord.gg/gk8jAdXWmj')}`,
|
||||||
` Star us on GitHub: ${color.dim('https://github.com/bmad-code-org/BMAD-METHOD/')}`,
|
` Star us on GitHub: ${color.dim('https://github.com/bmad-code-org/BMAD-METHOD/')}`,
|
||||||
` Subscribe on YouTube: ${color.dim('https://www.youtube.com/@BMadCode')}`,
|
` Subscribe on YouTube: ${color.dim('https://www.youtube.com/@BMadCode')}`,
|
||||||
|
` Invoke the ${color.cyan('bmad-help')} skill in your IDE Agent to get started`,
|
||||||
);
|
);
|
||||||
if (context.ides && context.ides.length > 0) {
|
|
||||||
lines.push(` Invoke the ${color.cyan('bmad-help')} skill in your IDE Agent to get started`);
|
|
||||||
}
|
|
||||||
|
|
||||||
await prompts.note(lines.join('\n'), 'BMAD is ready to use!');
|
await prompts.note(lines.join('\n'), 'BMAD is ready to use!');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -129,7 +129,6 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
||||||
|
|
||||||
const selectedModules = options.selectedModules || [];
|
const selectedModules = options.selectedModules || [];
|
||||||
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
|
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
|
||||||
this.skillWriteTracker = config.skill_format ? new Set() : null;
|
|
||||||
|
|
||||||
// Install standard artifacts (agents, workflows, tasks, tools)
|
// Install standard artifacts (agents, workflows, tasks, tools)
|
||||||
if (!skipStandardArtifacts) {
|
if (!skipStandardArtifacts) {
|
||||||
|
|
@ -160,11 +159,9 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
||||||
// Install verbatim skills (type: skill)
|
// Install verbatim skills (type: skill)
|
||||||
if (config.skill_format) {
|
if (config.skill_format) {
|
||||||
results.skills = await this.installVerbatimSkills(projectDir, bmadDir, targetPath, config);
|
results.skills = await this.installVerbatimSkills(projectDir, bmadDir, targetPath, config);
|
||||||
results.skillDirectories = this.skillWriteTracker ? this.skillWriteTracker.size : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.printSummary(results, target_dir, options);
|
await this.printSummary(results, target_dir, options);
|
||||||
this.skillWriteTracker = null;
|
|
||||||
return { success: true, results };
|
return { success: true, results };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -498,7 +495,6 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
||||||
// Create skill directory
|
// Create skill directory
|
||||||
const skillDir = path.join(targetPath, skillName);
|
const skillDir = path.join(targetPath, skillName);
|
||||||
await this.ensureDir(skillDir);
|
await this.ensureDir(skillDir);
|
||||||
this.skillWriteTracker?.add(skillName);
|
|
||||||
|
|
||||||
// Transform content: rewrite frontmatter for skills format
|
// Transform content: rewrite frontmatter for skills format
|
||||||
const skillContent = this.transformToSkillFormat(content, skillName);
|
const skillContent = this.transformToSkillFormat(content, skillName);
|
||||||
|
|
@ -671,7 +667,6 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
||||||
const skillDir = path.join(targetPath, canonicalId);
|
const skillDir = path.join(targetPath, canonicalId);
|
||||||
await fs.remove(skillDir);
|
await fs.remove(skillDir);
|
||||||
await fs.ensureDir(skillDir);
|
await fs.ensureDir(skillDir);
|
||||||
this.skillWriteTracker?.add(canonicalId);
|
|
||||||
|
|
||||||
// Copy all skill files, filtering OS/editor artifacts recursively
|
// Copy all skill files, filtering OS/editor artifacts recursively
|
||||||
const skipPatterns = new Set(['.DS_Store', 'Thumbs.db', 'desktop.ini']);
|
const skipPatterns = new Set(['.DS_Store', 'Thumbs.db', 'desktop.ini']);
|
||||||
|
|
@ -712,11 +707,11 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
||||||
async printSummary(results, targetDir, options = {}) {
|
async printSummary(results, targetDir, options = {}) {
|
||||||
if (options.silent) return;
|
if (options.silent) return;
|
||||||
const parts = [];
|
const parts = [];
|
||||||
const totalDirs =
|
|
||||||
results.skillDirectories || (results.workflows || 0) + (results.tasks || 0) + (results.tools || 0) + (results.skills || 0);
|
|
||||||
const skillCount = totalDirs - (results.agents || 0);
|
|
||||||
if (skillCount > 0) parts.push(`${skillCount} skills`);
|
|
||||||
if (results.agents > 0) parts.push(`${results.agents} agents`);
|
if (results.agents > 0) parts.push(`${results.agents} agents`);
|
||||||
|
if (results.workflows > 0) parts.push(`${results.workflows} workflows`);
|
||||||
|
if (results.tasks > 0) parts.push(`${results.tasks} tasks`);
|
||||||
|
if (results.tools > 0) parts.push(`${results.tools} tools`);
|
||||||
|
if (results.skills > 0) parts.push(`${results.skills} skills`);
|
||||||
await prompts.log.success(`${this.name} configured: ${parts.join(', ')} → ${targetDir}`);
|
await prompts.log.success(`${this.name} configured: ${parts.join(', ')} → ${targetDir}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -162,10 +162,10 @@ class IdeManager {
|
||||||
// Config-driven handlers return { success, results: { agents, workflows, tasks, tools } }
|
// Config-driven handlers return { success, results: { agents, workflows, tasks, tools } }
|
||||||
const r = handlerResult.results;
|
const r = handlerResult.results;
|
||||||
const parts = [];
|
const parts = [];
|
||||||
const totalDirs = r.skillDirectories || (r.workflows || 0) + (r.tasks || 0) + (r.tools || 0) + (r.skills || 0);
|
|
||||||
const skillCount = totalDirs - (r.agents || 0);
|
|
||||||
if (skillCount > 0) parts.push(`${skillCount} skills`);
|
|
||||||
if (r.agents > 0) parts.push(`${r.agents} agents`);
|
if (r.agents > 0) parts.push(`${r.agents} agents`);
|
||||||
|
if (r.workflows > 0) parts.push(`${r.workflows} workflows`);
|
||||||
|
if (r.tasks > 0) parts.push(`${r.tasks} tasks`);
|
||||||
|
if (r.tools > 0) parts.push(`${r.tools} tools`);
|
||||||
detail = parts.join(', ');
|
detail = parts.join(', ');
|
||||||
}
|
}
|
||||||
// Propagate handler's success status (default true for backward compat)
|
// Propagate handler's success status (default true for backward compat)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue