bmad-method 4.6.0 → 4.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/bmad-core/templates/architecture-tmpl.md +2 -0
- package/bmad-core/templates/brownfield-architecture-tmpl.md +2 -0
- package/bmad-core/templates/brownfield-prd-tmpl.md +2 -0
- package/bmad-core/templates/competitor-analysis-tmpl.md +2 -0
- package/bmad-core/templates/front-end-architecture-tmpl.md +2 -0
- package/bmad-core/templates/front-end-spec-tmpl.md +2 -0
- package/bmad-core/templates/fullstack-architecture-tmpl.md +2 -0
- package/bmad-core/templates/market-research-tmpl.md +2 -0
- package/bmad-core/templates/prd-tmpl.md +2 -0
- package/bmad-core/templates/project-brief-tmpl.md +2 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-designer.txt +481 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-developer.txt +100 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/agents/game-sm.txt +100 -0
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +4703 -2876
- package/dist/expansion-packs/bmad-infrastructure-devops/agents/infra-devops-platform.txt +35 -0
- package/dist/expansion-packs/expansion-creator/agents/bmad-the-creator.txt +399 -0
- package/expansion-packs/bmad-2d-phaser-game-dev/agent-teams/phaser-2d-nodejs-game-team.yml +1 -0
- package/package.json +1 -1
- package/tools/builders/web-builder.js +189 -20
- package/tools/installer/lib/installer.js +4 -1
- package/tools/installer/package.json +1 -1
- package/tools/upgraders/v3-to-v4-upgrader.js +31 -41
|
@@ -12,6 +12,11 @@ class WebBuilder {
|
|
|
12
12
|
this.templatePath = path.join(this.rootDir, 'bmad-core', 'templates', 'web-agent-startup-instructions-template.md');
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
parseYaml(content) {
|
|
16
|
+
const yaml = require('js-yaml');
|
|
17
|
+
return yaml.load(content);
|
|
18
|
+
}
|
|
19
|
+
|
|
15
20
|
async cleanOutputDirs() {
|
|
16
21
|
for (const dir of this.outputDirs) {
|
|
17
22
|
try {
|
|
@@ -232,7 +237,7 @@ class WebBuilder {
|
|
|
232
237
|
const agentContent = await fs.readFile(agentPath, 'utf8');
|
|
233
238
|
sections.push(this.formatSection(`agents#${agentName}`, agentContent));
|
|
234
239
|
|
|
235
|
-
// Resolve and add agent dependencies
|
|
240
|
+
// Resolve and add agent dependencies
|
|
236
241
|
const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/);
|
|
237
242
|
if (agentYaml) {
|
|
238
243
|
try {
|
|
@@ -240,16 +245,43 @@ class WebBuilder {
|
|
|
240
245
|
const agentConfig = yaml.load(agentYaml[1]);
|
|
241
246
|
|
|
242
247
|
if (agentConfig.dependencies) {
|
|
243
|
-
// Add resources
|
|
248
|
+
// Add resources, first try expansion pack, then core
|
|
244
249
|
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
|
|
245
250
|
if (Array.isArray(resources)) {
|
|
246
251
|
for (const resourceName of resources) {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
252
|
+
let found = false;
|
|
253
|
+
const extensions = ['.md', '.yml', '.yaml'];
|
|
254
|
+
|
|
255
|
+
// Try expansion pack first
|
|
256
|
+
for (const ext of extensions) {
|
|
257
|
+
const resourcePath = path.join(packDir, resourceType, `${resourceName}${ext}`);
|
|
258
|
+
try {
|
|
259
|
+
const resourceContent = await fs.readFile(resourcePath, 'utf8');
|
|
260
|
+
sections.push(this.formatSection(`${resourceType}#${resourceName}`, resourceContent));
|
|
261
|
+
found = true;
|
|
262
|
+
break;
|
|
263
|
+
} catch (error) {
|
|
264
|
+
// Not in expansion pack, continue
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
// If not found in expansion pack, try core
|
|
269
|
+
if (!found) {
|
|
270
|
+
for (const ext of extensions) {
|
|
271
|
+
const corePath = path.join(this.rootDir, 'bmad-core', resourceType, `${resourceName}${ext}`);
|
|
272
|
+
try {
|
|
273
|
+
const coreContent = await fs.readFile(corePath, 'utf8');
|
|
274
|
+
sections.push(this.formatSection(`${resourceType}#${resourceName}`, coreContent));
|
|
275
|
+
found = true;
|
|
276
|
+
break;
|
|
277
|
+
} catch (error) {
|
|
278
|
+
// Not in core either, continue
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (!found) {
|
|
284
|
+
console.warn(` ⚠ Dependency ${resourceType}#${resourceName} not found in expansion pack or core`);
|
|
253
285
|
}
|
|
254
286
|
}
|
|
255
287
|
}
|
|
@@ -268,32 +300,164 @@ class WebBuilder {
|
|
|
268
300
|
|
|
269
301
|
const sections = [template];
|
|
270
302
|
|
|
271
|
-
// Add team configuration
|
|
303
|
+
// Add team configuration and parse to get agent list
|
|
272
304
|
const teamContent = await fs.readFile(teamConfigPath, 'utf8');
|
|
273
305
|
const teamFileName = path.basename(teamConfigPath, '.yml');
|
|
306
|
+
const teamConfig = this.parseYaml(teamContent);
|
|
274
307
|
sections.push(this.formatSection(`agent-teams#${teamFileName}`, teamContent));
|
|
275
308
|
|
|
276
|
-
//
|
|
277
|
-
const
|
|
278
|
-
const orchestratorContent = await fs.readFile(orchestratorPath, 'utf8');
|
|
279
|
-
sections.push(this.formatSection('agents#bmad-orchestrator', orchestratorContent));
|
|
280
|
-
|
|
281
|
-
// Add expansion pack agents
|
|
309
|
+
// Get list of expansion pack agents
|
|
310
|
+
const expansionAgents = new Set();
|
|
282
311
|
const agentsDir = path.join(packDir, 'agents');
|
|
283
312
|
try {
|
|
284
313
|
const agentFiles = await fs.readdir(agentsDir);
|
|
285
314
|
for (const agentFile of agentFiles.filter(f => f.endsWith('.md'))) {
|
|
286
|
-
const agentPath = path.join(agentsDir, agentFile);
|
|
287
|
-
const agentContent = await fs.readFile(agentPath, 'utf8');
|
|
288
315
|
const agentName = agentFile.replace('.md', '');
|
|
289
|
-
|
|
316
|
+
expansionAgents.add(agentName);
|
|
290
317
|
}
|
|
291
318
|
} catch (error) {
|
|
292
319
|
console.warn(` ⚠ No agents directory found in ${packName}`);
|
|
293
320
|
}
|
|
294
321
|
|
|
295
|
-
//
|
|
322
|
+
// Build a map of all available expansion pack resources for override checking
|
|
323
|
+
const expansionResources = new Map();
|
|
296
324
|
const resourceDirs = ['templates', 'tasks', 'checklists', 'workflows', 'data'];
|
|
325
|
+
for (const resourceDir of resourceDirs) {
|
|
326
|
+
const resourcePath = path.join(packDir, resourceDir);
|
|
327
|
+
try {
|
|
328
|
+
const resourceFiles = await fs.readdir(resourcePath);
|
|
329
|
+
for (const resourceFile of resourceFiles.filter(f => f.endsWith('.md') || f.endsWith('.yml'))) {
|
|
330
|
+
const fileName = resourceFile.replace(/\.(md|yml)$/, '');
|
|
331
|
+
expansionResources.set(`${resourceDir}#${fileName}`, true);
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
// Directory might not exist, that's fine
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Process all agents listed in team configuration
|
|
339
|
+
const agentsToProcess = teamConfig.agents || [];
|
|
340
|
+
|
|
341
|
+
// Ensure bmad-orchestrator is always included for teams
|
|
342
|
+
if (!agentsToProcess.includes('bmad-orchestrator')) {
|
|
343
|
+
console.warn(` ⚠ Team ${teamFileName} missing bmad-orchestrator, adding automatically`);
|
|
344
|
+
agentsToProcess.unshift('bmad-orchestrator');
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
// Track all dependencies from all agents (deduplicated)
|
|
348
|
+
const allDependencies = new Map();
|
|
349
|
+
|
|
350
|
+
for (const agentId of agentsToProcess) {
|
|
351
|
+
|
|
352
|
+
if (expansionAgents.has(agentId)) {
|
|
353
|
+
// Use expansion pack version (override)
|
|
354
|
+
const agentPath = path.join(agentsDir, `${agentId}.md`);
|
|
355
|
+
const agentContent = await fs.readFile(agentPath, 'utf8');
|
|
356
|
+
sections.push(this.formatSection(`agents#${agentId}`, agentContent));
|
|
357
|
+
|
|
358
|
+
// Parse and collect dependencies from expansion agent
|
|
359
|
+
const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/);
|
|
360
|
+
if (agentYaml) {
|
|
361
|
+
try {
|
|
362
|
+
const agentConfig = this.parseYaml(agentYaml[1]);
|
|
363
|
+
if (agentConfig.dependencies) {
|
|
364
|
+
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
|
|
365
|
+
if (Array.isArray(resources)) {
|
|
366
|
+
for (const resourceName of resources) {
|
|
367
|
+
const key = `${resourceType}#${resourceName}`;
|
|
368
|
+
if (!allDependencies.has(key)) {
|
|
369
|
+
allDependencies.set(key, { type: resourceType, name: resourceName });
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
} catch (error) {
|
|
376
|
+
console.debug(`Failed to parse agent YAML for ${agentId}:`, error.message);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
} else {
|
|
380
|
+
// Use core BMAD version
|
|
381
|
+
try {
|
|
382
|
+
const coreAgentPath = path.join(this.rootDir, 'bmad-core', 'agents', `${agentId}.md`);
|
|
383
|
+
const coreAgentContent = await fs.readFile(coreAgentPath, 'utf8');
|
|
384
|
+
sections.push(this.formatSection(`agents#${agentId}`, coreAgentContent));
|
|
385
|
+
|
|
386
|
+
// Parse and collect dependencies from core agent
|
|
387
|
+
const agentYaml = coreAgentContent.match(/```yaml\n([\s\S]*?)\n```/);
|
|
388
|
+
if (agentYaml) {
|
|
389
|
+
try {
|
|
390
|
+
// Clean up the YAML to handle command descriptions after dashes
|
|
391
|
+
let yamlContent = agentYaml[1];
|
|
392
|
+
yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, '$1$2');
|
|
393
|
+
|
|
394
|
+
const agentConfig = this.parseYaml(yamlContent);
|
|
395
|
+
if (agentConfig.dependencies) {
|
|
396
|
+
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
|
|
397
|
+
if (Array.isArray(resources)) {
|
|
398
|
+
for (const resourceName of resources) {
|
|
399
|
+
const key = `${resourceType}#${resourceName}`;
|
|
400
|
+
if (!allDependencies.has(key)) {
|
|
401
|
+
allDependencies.set(key, { type: resourceType, name: resourceName });
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
} catch (error) {
|
|
408
|
+
console.debug(`Failed to parse agent YAML for ${agentId}:`, error.message);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
} catch (error) {
|
|
412
|
+
console.warn(` ⚠ Agent ${agentId} not found in core or expansion pack`);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Add all collected dependencies from agents
|
|
418
|
+
// Always prefer expansion pack versions if they exist
|
|
419
|
+
for (const [key, dep] of allDependencies) {
|
|
420
|
+
let found = false;
|
|
421
|
+
const extensions = ['.md', '.yml', '.yaml'];
|
|
422
|
+
|
|
423
|
+
// Always check expansion pack first, even if the dependency came from a core agent
|
|
424
|
+
if (expansionResources.has(key)) {
|
|
425
|
+
// We know it exists in expansion pack, find and load it
|
|
426
|
+
for (const ext of extensions) {
|
|
427
|
+
const expansionPath = path.join(packDir, dep.type, `${dep.name}${ext}`);
|
|
428
|
+
try {
|
|
429
|
+
const content = await fs.readFile(expansionPath, 'utf8');
|
|
430
|
+
sections.push(this.formatSection(key, content));
|
|
431
|
+
console.log(` ✓ Using expansion override for ${key}`);
|
|
432
|
+
found = true;
|
|
433
|
+
break;
|
|
434
|
+
} catch (error) {
|
|
435
|
+
// Try next extension
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// If not found in expansion pack (or doesn't exist there), try core
|
|
441
|
+
if (!found) {
|
|
442
|
+
for (const ext of extensions) {
|
|
443
|
+
const corePath = path.join(this.rootDir, 'bmad-core', dep.type, `${dep.name}${ext}`);
|
|
444
|
+
try {
|
|
445
|
+
const content = await fs.readFile(corePath, 'utf8');
|
|
446
|
+
sections.push(this.formatSection(key, content));
|
|
447
|
+
found = true;
|
|
448
|
+
break;
|
|
449
|
+
} catch (error) {
|
|
450
|
+
// Not in core either, continue
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (!found) {
|
|
456
|
+
console.warn(` ⚠ Dependency ${key} not found in expansion pack or core`);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
// Add remaining expansion pack resources not already included as dependencies
|
|
297
461
|
for (const resourceDir of resourceDirs) {
|
|
298
462
|
const resourcePath = path.join(packDir, resourceDir);
|
|
299
463
|
try {
|
|
@@ -302,7 +466,12 @@ class WebBuilder {
|
|
|
302
466
|
const filePath = path.join(resourcePath, resourceFile);
|
|
303
467
|
const fileContent = await fs.readFile(filePath, 'utf8');
|
|
304
468
|
const fileName = resourceFile.replace(/\.(md|yml)$/, '');
|
|
305
|
-
|
|
469
|
+
|
|
470
|
+
// Only add if not already included as a dependency
|
|
471
|
+
const resourceKey = `${resourceDir}#${fileName}`;
|
|
472
|
+
if (!allDependencies.has(resourceKey)) {
|
|
473
|
+
sections.push(this.formatSection(resourceKey, fileContent));
|
|
474
|
+
}
|
|
306
475
|
}
|
|
307
476
|
} catch (error) {
|
|
308
477
|
// Directory might not exist, that's fine
|
|
@@ -425,7 +425,10 @@ class Installer {
|
|
|
425
425
|
console.log(chalk.cyan("\n📦 Starting v3 to v4 upgrade process..."));
|
|
426
426
|
const V3ToV4Upgrader = require("../../upgraders/v3-to-v4-upgrader");
|
|
427
427
|
const upgrader = new V3ToV4Upgrader();
|
|
428
|
-
return await upgrader.upgrade({
|
|
428
|
+
return await upgrader.upgrade({
|
|
429
|
+
projectPath: installDir,
|
|
430
|
+
ides: config.ides || [] // Pass IDE selections from initial config
|
|
431
|
+
});
|
|
429
432
|
}
|
|
430
433
|
case "alongside":
|
|
431
434
|
return await this.performFreshInstall(config, installDir, spinner);
|
|
@@ -98,7 +98,7 @@ class V3ToV4Upgrader {
|
|
|
98
98
|
|
|
99
99
|
// 8. Setup IDE
|
|
100
100
|
if (!options.dryRun) {
|
|
101
|
-
await this.setupIDE(projectPath);
|
|
101
|
+
await this.setupIDE(projectPath, options.ides);
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
// 9. Show completion report
|
|
@@ -379,8 +379,8 @@ class V3ToV4Upgrader {
|
|
|
379
379
|
const spinner = ora("Installing V4 structure...").start();
|
|
380
380
|
|
|
381
381
|
try {
|
|
382
|
-
// Get the source
|
|
383
|
-
const sourcePath = path.join(__dirname, "..", "..", "
|
|
382
|
+
// Get the source bmad-core directory (without dot prefix)
|
|
383
|
+
const sourcePath = path.join(__dirname, "..", "..", "bmad-core");
|
|
384
384
|
const destPath = path.join(projectPath, ".bmad-core");
|
|
385
385
|
|
|
386
386
|
// Copy .bmad-core
|
|
@@ -545,47 +545,37 @@ class V3ToV4Upgrader {
|
|
|
545
545
|
}
|
|
546
546
|
}
|
|
547
547
|
|
|
548
|
-
async setupIDE(projectPath) {
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
choices: [
|
|
555
|
-
{ name: "Cursor", value: "cursor" },
|
|
556
|
-
{ name: "Claude Code", value: "claude-code" },
|
|
557
|
-
{ name: "Windsurf", value: "windsurf" },
|
|
558
|
-
{ name: "Roo Code", value: "roo" },
|
|
559
|
-
{ name: "VS Code", value: "skip" },
|
|
560
|
-
{ name: "Other/Skip", value: "skip" },
|
|
561
|
-
],
|
|
562
|
-
},
|
|
563
|
-
]);
|
|
564
|
-
|
|
565
|
-
const selectedIde = ide === "skip" ? null : ide;
|
|
566
|
-
|
|
567
|
-
if (selectedIde) {
|
|
568
|
-
const ideSetup = require("../installer/lib/ide-setup");
|
|
569
|
-
const spinner = ora("Setting up IDE rules for all agents...").start();
|
|
570
|
-
|
|
571
|
-
try {
|
|
572
|
-
await ideSetup.setup(selectedIde, projectPath);
|
|
573
|
-
spinner.succeed("IDE setup complete!");
|
|
548
|
+
async setupIDE(projectPath, selectedIdes) {
|
|
549
|
+
// Use the IDE selections passed from the installer
|
|
550
|
+
if (!selectedIdes || selectedIdes.length === 0) {
|
|
551
|
+
console.log(chalk.dim("No IDE setup requested - skipping"));
|
|
552
|
+
return;
|
|
553
|
+
}
|
|
574
554
|
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
"claude-code": "Commands created in .claude/commands/",
|
|
578
|
-
windsurf: "Rules created in .windsurf/rules/",
|
|
579
|
-
roo: "Custom modes created in .roomodes",
|
|
580
|
-
};
|
|
555
|
+
const ideSetup = require("../installer/lib/ide-setup");
|
|
556
|
+
const spinner = ora("Setting up IDE rules for all agents...").start();
|
|
581
557
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
558
|
+
try {
|
|
559
|
+
const ideMessages = {
|
|
560
|
+
cursor: "Rules created in .cursor/rules/",
|
|
561
|
+
"claude-code": "Commands created in .claude/commands/",
|
|
562
|
+
windsurf: "Rules created in .windsurf/rules/",
|
|
563
|
+
roo: "Custom modes created in .roomodes",
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
// Setup each selected IDE
|
|
567
|
+
for (const ide of selectedIdes) {
|
|
568
|
+
spinner.text = `Setting up ${ide}...`;
|
|
569
|
+
await ideSetup.setup(ide, projectPath);
|
|
570
|
+
console.log(chalk.green(`\n✓ ${ideMessages[ide]}`));
|
|
588
571
|
}
|
|
572
|
+
|
|
573
|
+
spinner.succeed(`IDE setup complete for ${selectedIdes.length} IDE(s)!`);
|
|
574
|
+
} catch (error) {
|
|
575
|
+
spinner.fail("IDE setup failed");
|
|
576
|
+
console.error(
|
|
577
|
+
chalk.yellow("IDE setup failed, but upgrade is complete.")
|
|
578
|
+
);
|
|
589
579
|
}
|
|
590
580
|
}
|
|
591
581
|
|