bmad-method 4.7.0 → 4.9.0

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.
Files changed (49) hide show
  1. package/.prettierignore +0 -1
  2. package/CHANGELOG.md +24 -0
  3. package/README.md +18 -1
  4. package/bmad-core/agents/analyst.md +11 -8
  5. package/bmad-core/agents/architect.md +10 -7
  6. package/bmad-core/agents/bmad-master.md +13 -11
  7. package/bmad-core/agents/bmad-orchestrator.md +3 -0
  8. package/bmad-core/agents/dev.md +20 -27
  9. package/bmad-core/agents/pm.md +8 -5
  10. package/bmad-core/agents/po.md +13 -10
  11. package/bmad-core/agents/qa.md +8 -5
  12. package/bmad-core/agents/sm.md +15 -25
  13. package/bmad-core/agents/ux-expert.md +11 -8
  14. package/bmad-core/core-config.yml +26 -0
  15. package/bmad-core/data/bmad-kb.md +6 -3
  16. package/bmad-core/tasks/create-next-story.md +63 -45
  17. package/bmad-core/utils/file-resolution-context.md +10 -0
  18. package/dist/agents/analyst.txt +404 -17
  19. package/dist/agents/architect.txt +14 -6
  20. package/dist/agents/bmad-master.txt +485 -109
  21. package/dist/agents/bmad-orchestrator.txt +402 -22
  22. package/dist/agents/dev.txt +26 -34
  23. package/dist/agents/pm.txt +9 -5
  24. package/dist/agents/po.txt +10 -10
  25. package/dist/agents/qa.txt +4 -4
  26. package/dist/agents/sm.txt +74 -63
  27. package/dist/agents/ux-expert.txt +9 -7
  28. package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +49 -20
  29. package/dist/expansion-packs/expansion-creator/agents/bmad-the-creator.txt +6 -3
  30. package/dist/teams/team-all.txt +561 -158
  31. package/dist/teams/team-fullstack.txt +457 -57
  32. package/dist/teams/team-ide-minimal.txt +516 -133
  33. package/dist/teams/team-no-ui.txt +448 -50
  34. package/docs/bmad-workflow-guide.md +1 -1
  35. package/docs/claude-code-guide.md +1 -1
  36. package/docs/core-architecture.md +4 -4
  37. package/docs/cursor-guide.md +1 -1
  38. package/docs/roo-code-guide.md +1 -1
  39. package/docs/user-guide.md +1 -1
  40. package/docs/windsurf-guide.md +1 -1
  41. package/expansion-packs/expansion-creator/templates/agent-tmpl.md +4 -1
  42. package/package.json +1 -1
  43. package/tools/builders/web-builder.js +158 -94
  44. package/tools/installer/bin/bmad.js +34 -2
  45. package/tools/installer/lib/file-manager.js +3 -2
  46. package/tools/installer/lib/ide-setup.js +39 -123
  47. package/tools/installer/lib/installer.js +13 -1
  48. package/tools/installer/package.json +1 -1
  49. /package/bmad-core/{templates/web-agent-startup-instructions-template.md → utils/web-agent-startup-instructions.md} +0 -0
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  const { program } = require('commander');
4
+ const path = require('path');
4
5
 
5
6
  // Dynamic imports for ES modules
6
7
  let chalk, inquirer;
@@ -57,7 +58,9 @@ program
57
58
  if (!options.full && !options.agent && !options.team && !options.expansionOnly) {
58
59
  // Interactive mode
59
60
  const answers = await promptInstallation();
60
- await installer.install(answers);
61
+ if (!answers._alreadyInstalled) {
62
+ await installer.install(answers);
63
+ }
61
64
  } else {
62
65
  // Direct mode
63
66
  let installType = 'full';
@@ -158,6 +161,35 @@ async function promptInstallation() {
158
161
  ]);
159
162
  answers.directory = directory;
160
163
 
164
+ // Check if this is an existing v4 installation
165
+ const installDir = path.resolve(answers.directory);
166
+ const state = await installer.detectInstallationState(installDir);
167
+
168
+ if (state.type === 'v4_existing') {
169
+ console.log(chalk.yellow('\nšŸ” Found existing BMAD v4 installation'));
170
+ console.log(` Directory: ${installDir}`);
171
+ console.log(` Version: ${state.manifest?.version || 'Unknown'}`);
172
+ console.log(` Installed: ${state.manifest?.installed_at ? new Date(state.manifest.installed_at).toLocaleDateString() : 'Unknown'}`);
173
+
174
+ const { shouldUpdate } = await inquirer.prompt([
175
+ {
176
+ type: 'confirm',
177
+ name: 'shouldUpdate',
178
+ message: 'Would you like to update your existing BMAD v4 installation?',
179
+ default: true
180
+ }
181
+ ]);
182
+
183
+ if (shouldUpdate) {
184
+ // Skip other prompts and go directly to update
185
+ answers.installType = 'update';
186
+ answers._alreadyInstalled = true; // Flag to prevent double installation
187
+ await installer.install(answers);
188
+ return answers; // Return the answers object
189
+ }
190
+ // If user doesn't want to update, continue with normal flow
191
+ }
192
+
161
193
  // Ask for installation type
162
194
  const { installType } = await inquirer.prompt([
163
195
  {
@@ -373,7 +405,7 @@ async function promptInstallation() {
373
405
  type: 'input',
374
406
  name: 'webBundlesDirectory',
375
407
  message: 'Enter directory for web bundles:',
376
- default: `${directory}/web-bundles`,
408
+ default: `${answers.directory}/web-bundles`,
377
409
  validate: (input) => {
378
410
  if (!input.trim()) {
379
411
  return 'Please enter a valid directory path';
@@ -2,6 +2,7 @@ const fs = require("fs-extra");
2
2
  const path = require("path");
3
3
  const crypto = require("crypto");
4
4
  const glob = require("glob");
5
+ const yaml = require("js-yaml");
5
6
 
6
7
  // Dynamic import for ES module
7
8
  let chalk;
@@ -106,7 +107,7 @@ class FileManager {
106
107
 
107
108
  // Write manifest
108
109
  await fs.ensureDir(path.dirname(manifestPath));
109
- await fs.writeFile(manifestPath, JSON.stringify(manifest, null, 2));
110
+ await fs.writeFile(manifestPath, yaml.dump(manifest, { indent: 2 }));
110
111
 
111
112
  return manifest;
112
113
  }
@@ -120,7 +121,7 @@ class FileManager {
120
121
 
121
122
  try {
122
123
  const content = await fs.readFile(manifestPath, "utf8");
123
- return JSON.parse(content);
124
+ return yaml.load(content);
124
125
  } catch (error) {
125
126
  return null;
126
127
  }
@@ -39,20 +39,13 @@ class IdeSetup {
39
39
 
40
40
  async setupCursor(installDir, selectedAgent) {
41
41
  const cursorRulesDir = path.join(installDir, ".cursor", "rules");
42
- const agents = selectedAgent
43
- ? [selectedAgent]
44
- : await this.getAllAgentIds(installDir);
42
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
45
43
 
46
44
  await fileManager.ensureDirectory(cursorRulesDir);
47
45
 
48
46
  for (const agentId of agents) {
49
47
  // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
50
- let agentPath = path.join(
51
- installDir,
52
- ".bmad-core",
53
- "agents",
54
- `${agentId}.md`
55
- );
48
+ let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
56
49
  if (!(await fileManager.pathExists(agentPath))) {
57
50
  agentPath = path.join(installDir, "agents", `${agentId}.md`);
58
51
  }
@@ -103,20 +96,13 @@ class IdeSetup {
103
96
 
104
97
  async setupClaudeCode(installDir, selectedAgent) {
105
98
  const commandsDir = path.join(installDir, ".claude", "commands");
106
- const agents = selectedAgent
107
- ? [selectedAgent]
108
- : await this.getAllAgentIds(installDir);
99
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
109
100
 
110
101
  await fileManager.ensureDirectory(commandsDir);
111
102
 
112
103
  for (const agentId of agents) {
113
104
  // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
114
- let agentPath = path.join(
115
- installDir,
116
- ".bmad-core",
117
- "agents",
118
- `${agentId}.md`
119
- );
105
+ let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
120
106
  if (!(await fileManager.pathExists(agentPath))) {
121
107
  agentPath = path.join(installDir, "agents", `${agentId}.md`);
122
108
  }
@@ -136,29 +122,20 @@ class IdeSetup {
136
122
  }
137
123
  }
138
124
 
139
- console.log(
140
- chalk.green(`\nāœ“ Created Claude Code commands in ${commandsDir}`)
141
- );
125
+ console.log(chalk.green(`\nāœ“ Created Claude Code commands in ${commandsDir}`));
142
126
 
143
127
  return true;
144
128
  }
145
129
 
146
130
  async setupWindsurf(installDir, selectedAgent) {
147
131
  const windsurfRulesDir = path.join(installDir, ".windsurf", "rules");
148
- const agents = selectedAgent
149
- ? [selectedAgent]
150
- : await this.getAllAgentIds(installDir);
132
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
151
133
 
152
134
  await fileManager.ensureDirectory(windsurfRulesDir);
153
135
 
154
136
  for (const agentId of agents) {
155
137
  // Check if .bmad-core is a subdirectory (full install) or if agents are in root (single agent install)
156
- let agentPath = path.join(
157
- installDir,
158
- ".bmad-core",
159
- "agents",
160
- `${agentId}.md`
161
- );
138
+ let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
162
139
  if (!(await fileManager.pathExists(agentPath))) {
163
140
  agentPath = path.join(installDir, "agents", `${agentId}.md`);
164
141
  }
@@ -197,9 +174,7 @@ class IdeSetup {
197
174
  }
198
175
  }
199
176
 
200
- console.log(
201
- chalk.green(`\nāœ“ Created Windsurf rules in ${windsurfRulesDir}`)
202
- );
177
+ console.log(chalk.green(`\nāœ“ Created Windsurf rules in ${windsurfRulesDir}`));
203
178
 
204
179
  return true;
205
180
  }
@@ -233,13 +208,7 @@ class IdeSetup {
233
208
  }
234
209
 
235
210
  async setupRoo(installDir, selectedAgent) {
236
- const agents = selectedAgent
237
- ? [selectedAgent]
238
- : await this.getAllAgentIds(installDir);
239
-
240
- // Create .roo directory first
241
- const rooDir = path.join(installDir, ".roo");
242
- await fileManager.ensureDirectory(rooDir);
211
+ const agents = selectedAgent ? [selectedAgent] : await this.getAllAgentIds(installDir);
243
212
 
244
213
  // Check for existing .roomodes file in project root
245
214
  const roomodesPath = path.join(installDir, ".roomodes");
@@ -253,11 +222,7 @@ class IdeSetup {
253
222
  for (const match of modeMatches) {
254
223
  existingModes.push(match[1]);
255
224
  }
256
- console.log(
257
- chalk.yellow(
258
- `Found existing .roomodes file with ${existingModes.length} modes`
259
- )
260
- );
225
+ console.log(chalk.yellow(`Found existing .roomodes file with ${existingModes.length} modes`));
261
226
  }
262
227
 
263
228
  // Create new modes content
@@ -265,55 +230,48 @@ class IdeSetup {
265
230
 
266
231
  // Define file permissions for each agent type
267
232
  const agentPermissions = {
268
- 'analyst': {
269
- fileRegex: '\\.(md|txt)$',
270
- description: 'Documentation and text files'
233
+ analyst: {
234
+ fileRegex: "\\.(md|txt)$",
235
+ description: "Documentation and text files",
271
236
  },
272
- 'pm': {
273
- fileRegex: '\\.(md|txt)$',
274
- description: 'Product documentation'
237
+ pm: {
238
+ fileRegex: "\\.(md|txt)$",
239
+ description: "Product documentation",
275
240
  },
276
- 'architect': {
277
- fileRegex: '\\.(md|txt|yml|yaml|json)$',
278
- description: 'Architecture docs and configs'
241
+ architect: {
242
+ fileRegex: "\\.(md|txt|yml|yaml|json)$",
243
+ description: "Architecture docs and configs",
279
244
  },
280
- 'dev': null, // Full edit access
281
- 'qa': {
282
- fileRegex: '\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$',
283
- description: 'Test files and documentation'
245
+ dev: null, // Full edit access
246
+ qa: {
247
+ fileRegex: "\\.(test|spec)\\.(js|ts|jsx|tsx)$|\\.md$",
248
+ description: "Test files and documentation",
284
249
  },
285
- 'ux-expert': {
286
- fileRegex: '\\.(md|css|scss|html|jsx|tsx)$',
287
- description: 'Design-related files'
250
+ "ux-expert": {
251
+ fileRegex: "\\.(md|css|scss|html|jsx|tsx)$",
252
+ description: "Design-related files",
288
253
  },
289
- 'po': {
290
- fileRegex: '\\.(md|txt)$',
291
- description: 'Story and requirement docs'
254
+ po: {
255
+ fileRegex: "\\.(md|txt)$",
256
+ description: "Story and requirement docs",
292
257
  },
293
- 'sm': {
294
- fileRegex: '\\.(md|txt)$',
295
- description: 'Process and planning docs'
258
+ sm: {
259
+ fileRegex: "\\.(md|txt)$",
260
+ description: "Process and planning docs",
296
261
  },
297
- 'bmad-orchestrator': null, // Full edit access
298
- 'bmad-master': null // Full edit access
262
+ "bmad-orchestrator": null, // Full edit access
263
+ "bmad-master": null, // Full edit access
299
264
  };
300
265
 
301
266
  for (const agentId of agents) {
302
267
  // Skip if already exists
303
268
  if (existingModes.includes(`bmad-${agentId}`)) {
304
- console.log(
305
- chalk.dim(`Skipping ${agentId} - already exists in .roomodes`)
306
- );
269
+ console.log(chalk.dim(`Skipping ${agentId} - already exists in .roomodes`));
307
270
  continue;
308
271
  }
309
272
 
310
273
  // Read agent file to extract all information
311
- let agentPath = path.join(
312
- installDir,
313
- ".bmad-core",
314
- "agents",
315
- `${agentId}.md`
316
- );
274
+ let agentPath = path.join(installDir, ".bmad-core", "agents", `${agentId}.md`);
317
275
  if (!(await fileManager.pathExists(agentPath))) {
318
276
  agentPath = path.join(installDir, "agents", `${agentId}.md`);
319
277
  }
@@ -334,9 +292,7 @@ class IdeSetup {
334
292
 
335
293
  const title = titleMatch ? titleMatch[1].trim() : this.getAgentTitle(agentId);
336
294
  const icon = iconMatch ? iconMatch[1].trim() : "šŸ¤–";
337
- const whenToUse = whenToUseMatch
338
- ? whenToUseMatch[1].trim()
339
- : `Use for ${title} tasks`;
295
+ const whenToUse = whenToUseMatch ? whenToUseMatch[1].trim() : `Use for ${title} tasks`;
340
296
  const roleDefinition = roleDefinitionMatch
341
297
  ? roleDefinitionMatch[1].trim()
342
298
  : `You are a ${title} specializing in ${title.toLowerCase()} tasks and responsibilities.`;
@@ -360,9 +316,7 @@ class IdeSetup {
360
316
  newModesContent += ` - edit\n`;
361
317
  }
362
318
 
363
- console.log(
364
- chalk.green(`āœ“ Added mode: bmad-${agentId} (${icon} ${title})`)
365
- );
319
+ console.log(chalk.green(`āœ“ Added mode: bmad-${agentId} (${icon} ${title})`));
366
320
  }
367
321
  }
368
322
  }
@@ -381,46 +335,8 @@ class IdeSetup {
381
335
  await fileManager.writeFile(roomodesPath, roomodesContent);
382
336
  console.log(chalk.green("āœ“ Created .roomodes file in project root"));
383
337
 
384
- // Create README in .roo directory
385
- const rooReadme = `# Roo Code Custom Modes for BMAD-METHOD
386
-
387
- This directory contains custom mode configurations for Roo Code to enable BMAD agent personalities.
388
-
389
- ## Setup
390
-
391
- The \`.roomodes\` file defines all BMAD agents as custom modes using the proper \`customModes:\` structure. Modes are automatically available in Roo Code when you open this project.
392
-
393
- ## Available Modes
394
-
395
- ${agents.map((id) => `- **bmad-${id}** - ${this.getAgentTitle(id)}`).join("\n")}
396
-
397
- ## Usage
398
-
399
- In Roo Code:
400
- 1. Open the mode selector (usually in the status bar)
401
- 2. Select any BMAD agent mode
402
- 3. The AI will adopt that agent's personality and expertise
403
-
404
- ## File Permissions
405
-
406
- Each agent has specific file access permissions:
407
- - **Analysts, PM, PO, SM**: Limited to documentation files (.md, .txt)
408
- - **Architect**: Architecture docs and configs (.md, .txt, .yml, .yaml, .json)
409
- - **QA**: Test files and documentation
410
- - **UX Expert**: Design-related files (.md, .css, .scss, .html, .jsx, .tsx)
411
- - **Developer, Orchestrator, Master**: Full edit access to all files
412
- `;
413
-
414
- const readmePath = path.join(rooDir, "README.md");
415
- await fileManager.writeFile(readmePath, rooReadme);
416
- console.log(chalk.green("āœ“ Created .roo/README.md"));
417
-
418
338
  console.log(chalk.green(`\nāœ“ Roo Code setup complete!`));
419
- console.log(
420
- chalk.dim(
421
- "Custom modes will be available when you open this project in Roo Code"
422
- )
423
- );
339
+ console.log(chalk.dim("Custom modes will be available when you open this project in Roo Code"));
424
340
 
425
341
  return true;
426
342
  }
@@ -102,6 +102,17 @@ class Installer {
102
102
  spinner.start("Analyzing installation directory...");
103
103
  }
104
104
 
105
+ // If this is an update request from early detection, handle it directly
106
+ if (config.installType === 'update') {
107
+ const state = await this.detectInstallationState(installDir);
108
+ if (state.type === 'v4_existing') {
109
+ return await this.performUpdate(config, installDir, state.manifest, spinner);
110
+ } else {
111
+ spinner.fail('No existing v4 installation found to update');
112
+ throw new Error('No existing v4 installation found');
113
+ }
114
+ }
115
+
105
116
  // Detect current state
106
117
  const state = await this.detectInstallationState(installDir);
107
118
 
@@ -541,7 +552,8 @@ class Installer {
541
552
  installType: manifest.install_type,
542
553
  agent: manifest.agent,
543
554
  directory: installDir,
544
- ide: newConfig.ide || manifest.ide_setup, // Use new IDE choice if provided
555
+ ide: newConfig?.ide || manifest.ide_setup, // Use new IDE choice if provided
556
+ ides: newConfig?.ides || manifest.ides_setup || [],
545
557
  };
546
558
 
547
559
  await this.performFreshInstall(config, installDir, spinner);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bmad-method",
3
- "version": "4.7.0",
3
+ "version": "4.9.0",
4
4
  "description": "BMAD Method installer - AI-powered Agile development framework",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {