Skip to content

Commit c575064

Browse files
alexeyvclaude
andauthored
fix(installer): simplify install summary (bmad-code-org#1915)
* fix(installer): simplify install summary * style: fix prettier formatting in test file Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(installer): clean up temp dir leak and conditional IDE footer - Return fixture root from createSkillCollisionFixture so cleanup removes the parent temp directory, not just the _bmad child - Only show bmad-help next-step line when IDEs are configured --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 861716f commit c575064

File tree

4 files changed

+133
-15
lines changed

4 files changed

+133
-15
lines changed

test/test-installation-components.js

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,60 @@ async function createTestBmadFixture() {
8181
return fixtureDir;
8282
}
8383

84+
async function createSkillCollisionFixture() {
85+
const fixtureRoot = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-skill-collision-'));
86+
const fixtureDir = path.join(fixtureRoot, '_bmad');
87+
const configDir = path.join(fixtureDir, '_config');
88+
await fs.ensureDir(configDir);
89+
90+
await fs.writeFile(
91+
path.join(configDir, 'agent-manifest.csv'),
92+
[
93+
'name,displayName,title,icon,capabilities,role,identity,communicationStyle,principles,module,path,canonicalId',
94+
'"bmad-master","BMAD Master","","","","","","","","core","_bmad/core/agents/bmad-master.md","bmad-master"',
95+
'',
96+
].join('\n'),
97+
);
98+
99+
await fs.writeFile(
100+
path.join(configDir, 'workflow-manifest.csv'),
101+
[
102+
'name,description,module,path,canonicalId',
103+
'"help","Workflow help","core","_bmad/core/workflows/help/workflow.md","bmad-help"',
104+
'',
105+
].join('\n'),
106+
);
107+
108+
await fs.writeFile(path.join(configDir, 'task-manifest.csv'), 'name,displayName,description,module,path,standalone,canonicalId\n');
109+
await fs.writeFile(path.join(configDir, 'tool-manifest.csv'), 'name,displayName,description,module,path,standalone,canonicalId\n');
110+
await fs.writeFile(
111+
path.join(configDir, 'skill-manifest.csv'),
112+
[
113+
'canonicalId,name,description,module,path,install_to_bmad',
114+
'"bmad-help","bmad-help","Native help skill","core","_bmad/core/tasks/bmad-help/SKILL.md","true"',
115+
'',
116+
].join('\n'),
117+
);
118+
119+
const skillDir = path.join(fixtureDir, 'core', 'tasks', 'bmad-help');
120+
await fs.ensureDir(skillDir);
121+
await fs.writeFile(
122+
path.join(skillDir, 'SKILL.md'),
123+
['---', 'name: bmad-help', 'description: Native help skill', '---', '', 'Use this skill directly.'].join('\n'),
124+
);
125+
126+
const agentDir = path.join(fixtureDir, 'core', 'agents');
127+
await fs.ensureDir(agentDir);
128+
await fs.writeFile(
129+
path.join(agentDir, 'bmad-master.md'),
130+
['---', 'name: BMAD Master', 'description: Master agent', '---', '', '<agent name="BMAD Master" title="Master">', '</agent>'].join(
131+
'\n',
132+
),
133+
);
134+
135+
return { root: fixtureRoot, bmadDir: fixtureDir };
136+
}
137+
84138
/**
85139
* Test Suite
86140
*/
@@ -1770,6 +1824,50 @@ async function runTests() {
17701824

17711825
console.log('');
17721826

1827+
// ============================================================
1828+
// Test 31: Skill-format installs report unique skill directories
1829+
// ============================================================
1830+
console.log(`${colors.yellow}Test Suite 31: Skill Count Reporting${colors.reset}\n`);
1831+
1832+
let collisionFixtureRoot = null;
1833+
let collisionProjectDir = null;
1834+
1835+
try {
1836+
clearCache();
1837+
const collisionFixture = await createSkillCollisionFixture();
1838+
collisionFixtureRoot = collisionFixture.root;
1839+
collisionProjectDir = await fs.mkdtemp(path.join(os.tmpdir(), 'bmad-antigravity-test-'));
1840+
1841+
const ideManager = new IdeManager();
1842+
await ideManager.ensureInitialized();
1843+
const result = await ideManager.setup('antigravity', collisionProjectDir, collisionFixture.bmadDir, {
1844+
silent: true,
1845+
selectedModules: ['core'],
1846+
});
1847+
1848+
assert(result.success === true, 'Antigravity setup succeeds with overlapping skill names');
1849+
assert(result.detail === '2 skills, 2 agents', 'Installer detail reports total skills and total agents');
1850+
assert(result.handlerResult.results.skillDirectories === 2, 'Result exposes unique skill directory count');
1851+
assert(result.handlerResult.results.agents === 2, 'Result retains generated agent write count');
1852+
assert(result.handlerResult.results.workflows === 1, 'Result retains generated workflow count');
1853+
assert(result.handlerResult.results.skills === 1, 'Result retains verbatim skill count');
1854+
assert(
1855+
await fs.pathExists(path.join(collisionProjectDir, '.agent', 'skills', 'bmad-agent-bmad-master', 'SKILL.md')),
1856+
'Agent skill directory is created',
1857+
);
1858+
assert(
1859+
await fs.pathExists(path.join(collisionProjectDir, '.agent', 'skills', 'bmad-help', 'SKILL.md')),
1860+
'Overlapping skill directory is created once',
1861+
);
1862+
} catch (error) {
1863+
assert(false, 'Skill-format unique count test succeeds', error.message);
1864+
} finally {
1865+
if (collisionProjectDir) await fs.remove(collisionProjectDir).catch(() => {});
1866+
if (collisionFixtureRoot) await fs.remove(collisionFixtureRoot).catch(() => {});
1867+
}
1868+
1869+
console.log('');
1870+
17731871
// ============================================================
17741872
// Summary
17751873
// ============================================================

tools/cli/installers/lib/core/installer.js

Lines changed: 25 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1153,12 +1153,6 @@ class Installer {
11531153
preservedModules: modulesForCsvPreserve,
11541154
});
11551155

1156-
addResult(
1157-
'Manifests',
1158-
'ok',
1159-
`${manifestStats.workflows} workflows, ${manifestStats.agents} agents, ${manifestStats.tasks} tasks, ${manifestStats.tools} tools`,
1160-
);
1161-
11621156
// Merge help catalogs
11631157
message('Generating help catalog...');
11641158
await this.mergeModuleHelpCatalogs(bmadDir);
@@ -1379,10 +1373,27 @@ class Installer {
13791373
*/
13801374
async renderInstallSummary(results, context = {}) {
13811375
const color = await prompts.getColor();
1376+
const selectedIdes = new Set((context.ides || []).map((ide) => String(ide).toLowerCase()));
13821377

13831378
// Build step lines with status indicators
13841379
const lines = [];
13851380
for (const r of results) {
1381+
let stepLabel = null;
1382+
1383+
if (r.status !== 'ok') {
1384+
stepLabel = r.step;
1385+
} else if (r.step === 'Core') {
1386+
stepLabel = 'BMAD';
1387+
} else if (r.step.startsWith('Module: ')) {
1388+
stepLabel = r.step;
1389+
} else if (selectedIdes.has(String(r.step).toLowerCase())) {
1390+
stepLabel = r.step;
1391+
}
1392+
1393+
if (!stepLabel) {
1394+
continue;
1395+
}
1396+
13861397
let icon;
13871398
if (r.status === 'ok') {
13881399
icon = color.green('\u2713');
@@ -1392,7 +1403,11 @@ class Installer {
13921403
icon = color.red('\u2717');
13931404
}
13941405
const detail = r.detail ? color.dim(` (${r.detail})`) : '';
1395-
lines.push(` ${icon} ${r.step}${detail}`);
1406+
lines.push(` ${icon} ${stepLabel}${detail}`);
1407+
}
1408+
1409+
if ((context.ides || []).length === 0) {
1410+
lines.push(` ${color.green('\u2713')} No IDE selected ${color.dim('(installed in _bmad only)')}`);
13961411
}
13971412

13981413
// Context and warnings
@@ -1415,8 +1430,10 @@ class Installer {
14151430
` Join our Discord: ${color.dim('https://discord.gg/gk8jAdXWmj')}`,
14161431
` Star us on GitHub: ${color.dim('https://github.com/bmad-code-org/BMAD-METHOD/')}`,
14171432
` Subscribe on YouTube: ${color.dim('https://www.youtube.com/@BMadCode')}`,
1418-
` Invoke the ${color.cyan('bmad-help')} skill in your IDE Agent to get started`,
14191433
);
1434+
if (context.ides && context.ides.length > 0) {
1435+
lines.push(` Invoke the ${color.cyan('bmad-help')} skill in your IDE Agent to get started`);
1436+
}
14201437

14211438
await prompts.note(lines.join('\n'), 'BMAD is ready to use!');
14221439
}

tools/cli/installers/lib/ide/_config-driven.js

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,7 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
129129

130130
const selectedModules = options.selectedModules || [];
131131
const results = { agents: 0, workflows: 0, tasks: 0, tools: 0, skills: 0 };
132+
this.skillWriteTracker = config.skill_format ? new Set() : null;
132133

133134
// Install standard artifacts (agents, workflows, tasks, tools)
134135
if (!skipStandardArtifacts) {
@@ -159,9 +160,11 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
159160
// Install verbatim skills (type: skill)
160161
if (config.skill_format) {
161162
results.skills = await this.installVerbatimSkills(projectDir, bmadDir, targetPath, config);
163+
results.skillDirectories = this.skillWriteTracker ? this.skillWriteTracker.size : 0;
162164
}
163165

164166
await this.printSummary(results, target_dir, options);
167+
this.skillWriteTracker = null;
165168
return { success: true, results };
166169
}
167170

@@ -495,6 +498,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
495498
// Create skill directory
496499
const skillDir = path.join(targetPath, skillName);
497500
await this.ensureDir(skillDir);
501+
this.skillWriteTracker?.add(skillName);
498502

499503
// Transform content: rewrite frontmatter for skills format
500504
const skillContent = this.transformToSkillFormat(content, skillName);
@@ -667,6 +671,7 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
667671
const skillDir = path.join(targetPath, canonicalId);
668672
await fs.remove(skillDir);
669673
await fs.ensureDir(skillDir);
674+
this.skillWriteTracker?.add(canonicalId);
670675

671676
// Copy all skill files, filtering OS/editor artifacts recursively
672677
const skipPatterns = new Set(['.DS_Store', 'Thumbs.db', 'desktop.ini']);
@@ -707,11 +712,10 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
707712
async printSummary(results, targetDir, options = {}) {
708713
if (options.silent) return;
709714
const parts = [];
715+
const totalSkills =
716+
results.skillDirectories || (results.workflows || 0) + (results.tasks || 0) + (results.tools || 0) + (results.skills || 0);
717+
if (totalSkills > 0) parts.push(`${totalSkills} skills`);
710718
if (results.agents > 0) parts.push(`${results.agents} agents`);
711-
if (results.workflows > 0) parts.push(`${results.workflows} workflows`);
712-
if (results.tasks > 0) parts.push(`${results.tasks} tasks`);
713-
if (results.tools > 0) parts.push(`${results.tools} tools`);
714-
if (results.skills > 0) parts.push(`${results.skills} skills`);
715719
await prompts.log.success(`${this.name} configured: ${parts.join(', ')}${targetDir}`);
716720
}
717721

tools/cli/installers/lib/ide/manager.js

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -162,10 +162,9 @@ class IdeManager {
162162
// Config-driven handlers return { success, results: { agents, workflows, tasks, tools } }
163163
const r = handlerResult.results;
164164
const parts = [];
165+
const totalSkills = r.skillDirectories || (r.workflows || 0) + (r.tasks || 0) + (r.tools || 0) + (r.skills || 0);
166+
if (totalSkills > 0) parts.push(`${totalSkills} skills`);
165167
if (r.agents > 0) parts.push(`${r.agents} agents`);
166-
if (r.workflows > 0) parts.push(`${r.workflows} workflows`);
167-
if (r.tasks > 0) parts.push(`${r.tasks} tasks`);
168-
if (r.tools > 0) parts.push(`${r.tools} tools`);
169168
detail = parts.join(', ');
170169
}
171170
// Propagate handler's success status (default true for backward compat)

0 commit comments

Comments
 (0)