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.
- package/.prettierignore +0 -1
- package/CHANGELOG.md +24 -0
- package/README.md +18 -1
- package/bmad-core/agents/analyst.md +11 -8
- package/bmad-core/agents/architect.md +10 -7
- package/bmad-core/agents/bmad-master.md +13 -11
- package/bmad-core/agents/bmad-orchestrator.md +3 -0
- package/bmad-core/agents/dev.md +20 -27
- package/bmad-core/agents/pm.md +8 -5
- package/bmad-core/agents/po.md +13 -10
- package/bmad-core/agents/qa.md +8 -5
- package/bmad-core/agents/sm.md +15 -25
- package/bmad-core/agents/ux-expert.md +11 -8
- package/bmad-core/core-config.yml +26 -0
- package/bmad-core/data/bmad-kb.md +6 -3
- package/bmad-core/tasks/create-next-story.md +63 -45
- package/bmad-core/utils/file-resolution-context.md +10 -0
- package/dist/agents/analyst.txt +404 -17
- package/dist/agents/architect.txt +14 -6
- package/dist/agents/bmad-master.txt +485 -109
- package/dist/agents/bmad-orchestrator.txt +402 -22
- package/dist/agents/dev.txt +26 -34
- package/dist/agents/pm.txt +9 -5
- package/dist/agents/po.txt +10 -10
- package/dist/agents/qa.txt +4 -4
- package/dist/agents/sm.txt +74 -63
- package/dist/agents/ux-expert.txt +9 -7
- package/dist/expansion-packs/bmad-2d-phaser-game-dev/teams/phaser-2d-nodejs-game-team.txt +49 -20
- package/dist/expansion-packs/expansion-creator/agents/bmad-the-creator.txt +6 -3
- package/dist/teams/team-all.txt +561 -158
- package/dist/teams/team-fullstack.txt +457 -57
- package/dist/teams/team-ide-minimal.txt +516 -133
- package/dist/teams/team-no-ui.txt +448 -50
- package/docs/bmad-workflow-guide.md +1 -1
- package/docs/claude-code-guide.md +1 -1
- package/docs/core-architecture.md +4 -4
- package/docs/cursor-guide.md +1 -1
- package/docs/roo-code-guide.md +1 -1
- package/docs/user-guide.md +1 -1
- package/docs/windsurf-guide.md +1 -1
- package/expansion-packs/expansion-creator/templates/agent-tmpl.md +4 -1
- package/package.json +1 -1
- package/tools/builders/web-builder.js +158 -94
- package/tools/installer/bin/bmad.js +34 -2
- package/tools/installer/lib/file-manager.js +3 -2
- package/tools/installer/lib/ide-setup.js +39 -123
- package/tools/installer/lib/installer.js +13 -1
- package/tools/installer/package.json +1 -1
- /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
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
269
|
-
fileRegex:
|
|
270
|
-
description:
|
|
233
|
+
analyst: {
|
|
234
|
+
fileRegex: "\\.(md|txt)$",
|
|
235
|
+
description: "Documentation and text files",
|
|
271
236
|
},
|
|
272
|
-
|
|
273
|
-
fileRegex:
|
|
274
|
-
description:
|
|
237
|
+
pm: {
|
|
238
|
+
fileRegex: "\\.(md|txt)$",
|
|
239
|
+
description: "Product documentation",
|
|
275
240
|
},
|
|
276
|
-
|
|
277
|
-
fileRegex:
|
|
278
|
-
description:
|
|
241
|
+
architect: {
|
|
242
|
+
fileRegex: "\\.(md|txt|yml|yaml|json)$",
|
|
243
|
+
description: "Architecture docs and configs",
|
|
279
244
|
},
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
fileRegex:
|
|
283
|
-
description:
|
|
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
|
-
|
|
286
|
-
fileRegex:
|
|
287
|
-
description:
|
|
250
|
+
"ux-expert": {
|
|
251
|
+
fileRegex: "\\.(md|css|scss|html|jsx|tsx)$",
|
|
252
|
+
description: "Design-related files",
|
|
288
253
|
},
|
|
289
|
-
|
|
290
|
-
fileRegex:
|
|
291
|
-
description:
|
|
254
|
+
po: {
|
|
255
|
+
fileRegex: "\\.(md|txt)$",
|
|
256
|
+
description: "Story and requirement docs",
|
|
292
257
|
},
|
|
293
|
-
|
|
294
|
-
fileRegex:
|
|
295
|
-
description:
|
|
258
|
+
sm: {
|
|
259
|
+
fileRegex: "\\.(md|txt)$",
|
|
260
|
+
description: "Process and planning docs",
|
|
296
261
|
},
|
|
297
|
-
|
|
298
|
-
|
|
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
|
|
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);
|