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
|
@@ -23,7 +23,7 @@ For ideation and planning, use Google's Gemini Custom Gem with the team-fullstac
|
|
|
23
23
|
|
|
24
24
|
1. Open [Google gems](https://gemini.google.com/gems/view)
|
|
25
25
|
2. Create a new Gem - give it a title and description
|
|
26
|
-
3. Copy the contents of `.<install location
|
|
26
|
+
3. Copy the contents of `.<install location>/<web-bundles>/teams/team-fullstack.txt` (location can vary if you chose a non default installation location for the bundles) - or just use the bundle premade from the repo dist folder.
|
|
27
27
|
4. Paste this content into Gemini to set up the team
|
|
28
28
|
|
|
29
29
|
### Gemini Planning Phase
|
|
@@ -35,7 +35,7 @@ graph TD
|
|
|
35
35
|
end
|
|
36
36
|
|
|
37
37
|
subgraph Outputs
|
|
38
|
-
J["
|
|
38
|
+
J["dist"]
|
|
39
39
|
end
|
|
40
40
|
|
|
41
41
|
B -- defines dependencies for --> E
|
|
@@ -133,17 +133,17 @@ The framework is designed for two primary environments: local IDEs and web-based
|
|
|
133
133
|
|
|
134
134
|
### 4.1. Web Builder (`tools/builders/web-builder.js`)
|
|
135
135
|
|
|
136
|
-
- **Purpose**: This Node.js script is responsible for creating the `.txt` bundles found in `.
|
|
136
|
+
- **Purpose**: This Node.js script is responsible for creating the `.txt` bundles found in `dist`.
|
|
137
137
|
- **Process**:
|
|
138
138
|
1. **Resolves Dependencies**: For a given agent or team, the script reads its definition file.
|
|
139
139
|
2. It recursively finds all dependent resources (tasks, templates, etc.) that the agent/team needs.
|
|
140
140
|
3. **Bundles Content**: It reads the content of all these files and concatenates them into a single, large text file, with clear separators indicating the original file path of each section.
|
|
141
|
-
4. **Outputs Bundle**: The final `.txt` file is saved in the `
|
|
141
|
+
4. **Outputs Bundle**: The final `.txt` file is saved in the `dist` directory, ready to be uploaded to a web UI.
|
|
142
142
|
|
|
143
143
|
### 4.2. Environment-Specific Usage
|
|
144
144
|
|
|
145
145
|
- **For IDEs**: Users interact with the agents directly via their markdown files in `.bmad-core/agents/`. The IDE integration (for Cursor, Claude Code, etc.) knows how to call these agents.
|
|
146
|
-
- **For Web UIs**: Users upload a pre-built bundle from `.
|
|
146
|
+
- **For Web UIs**: Users upload a pre-built bundle from `dist`. This single file provides the AI with the context of the entire team and all their required tools and knowledge.
|
|
147
147
|
|
|
148
148
|
## 5. BMAD Workflows
|
|
149
149
|
|
package/docs/cursor-guide.md
CHANGED
|
@@ -23,7 +23,7 @@ For ideation and planning, use Google's Gemini Custom Gem with the team-fullstac
|
|
|
23
23
|
|
|
24
24
|
1. Open [Google gems](https://gemini.google.com/gems/view)
|
|
25
25
|
2. Create a new Gem - give it a title and description
|
|
26
|
-
3. Copy the contents of `.<install location
|
|
26
|
+
3. Copy the contents of `.<install location>/<web-bundles>/teams/team-fullstack.txt` (location can vary if you chose a non default installation location for the bundles) - or just use the bundle premade from the repo dist folder.
|
|
27
27
|
4. Paste this content into Gemini to set up the team
|
|
28
28
|
|
|
29
29
|
### Gemini Planning Phase
|
package/docs/roo-code-guide.md
CHANGED
|
@@ -23,7 +23,7 @@ For ideation and planning, use Google's Gemini Custom Gem with the team-fullstac
|
|
|
23
23
|
|
|
24
24
|
1. Open [Google gems](https://gemini.google.com/gems/view)
|
|
25
25
|
2. Create a new Gem - give it a title and description
|
|
26
|
-
3. Copy the contents of `.<install location
|
|
26
|
+
3. Copy the contents of `.<install location>/<web-bundles>/teams/team-fullstack.txt` (location can vary if you chose a non default installation location for the bundles) - or just use the bundle premade from the repo dist folder.
|
|
27
27
|
4. Paste this content into Gemini to set up the team
|
|
28
28
|
|
|
29
29
|
### Gemini Planning Phase
|
package/docs/user-guide.md
CHANGED
|
@@ -46,7 +46,7 @@ BMAD-METHOD (Breakthrough Method of Agile AI-Driven Development) is an AI agent
|
|
|
46
46
|
|
|
47
47
|
Best for: ChatGPT, Claude, Gemini users
|
|
48
48
|
|
|
49
|
-
1. Navigate to
|
|
49
|
+
1. Navigate to `dist/teams/`
|
|
50
50
|
2. Copy `team-fullstack.txt` content
|
|
51
51
|
3. Create new Gemini Gem or CustomGPT
|
|
52
52
|
4. Upload file with instructions: "Your critical operating instructions are attached, do not break character as directed"
|
package/docs/windsurf-guide.md
CHANGED
|
@@ -23,7 +23,7 @@ For ideation and planning, use Google's Gemini Custom Gem with the team-fullstac
|
|
|
23
23
|
|
|
24
24
|
1. Open [Google gems](https://gemini.google.com/gems/view)
|
|
25
25
|
2. Create a new Gem - give it a title and description
|
|
26
|
-
3. Copy the contents of `.<install location
|
|
26
|
+
3. Copy the contents of `.<install location>/<web-bundles>/teams/team-fullstack.txt` (location can vary if you chose a non default installation location for the bundles) - or just use the bundle premade from the repo dist folder.
|
|
27
27
|
4. Paste this content into Gemini to set up the team
|
|
28
28
|
|
|
29
29
|
### Gemini Planning Phase
|
|
@@ -16,6 +16,7 @@ activation-instructions:
|
|
|
16
16
|
- Only read the files/tasks listed here when user selects them for execution to minimize context usage
|
|
17
17
|
- The customization field ALWAYS takes precedence over any conflicting instructions
|
|
18
18
|
- When listing tasks/templates or presenting options during conversations, always show as numbered options list, allowing the user to type a number to select or execute
|
|
19
|
+
- Command
|
|
19
20
|
|
|
20
21
|
agent:
|
|
21
22
|
name: [AGENT_NAME]
|
|
@@ -36,7 +37,9 @@ persona:
|
|
|
36
37
|
# Add more principles as needed
|
|
37
38
|
|
|
38
39
|
startup:
|
|
39
|
-
-
|
|
40
|
+
- Greet the user with your name and role, and inform of the *help command.
|
|
41
|
+
- [STARTUP_INSTRUCTION]
|
|
42
|
+
- [STARTUP_INSTRUCTION]...
|
|
40
43
|
|
|
41
44
|
commands:
|
|
42
45
|
- "*help" - Show: numbered list of the following commands to allow selection
|
package/package.json
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
-
const fs = require(
|
|
2
|
-
const path = require(
|
|
3
|
-
const DependencyResolver = require(
|
|
1
|
+
const fs = require("node:fs").promises;
|
|
2
|
+
const path = require("node:path");
|
|
3
|
+
const DependencyResolver = require("../lib/dependency-resolver");
|
|
4
4
|
|
|
5
5
|
class WebBuilder {
|
|
6
6
|
constructor(options = {}) {
|
|
7
7
|
this.rootDir = options.rootDir || process.cwd();
|
|
8
|
-
this.outputDirs = options.outputDirs || [
|
|
9
|
-
path.join(this.rootDir, 'dist')
|
|
10
|
-
];
|
|
8
|
+
this.outputDirs = options.outputDirs || [path.join(this.rootDir, "dist")];
|
|
11
9
|
this.resolver = new DependencyResolver(this.rootDir);
|
|
12
|
-
this.templatePath = path.join(
|
|
10
|
+
this.templatePath = path.join(
|
|
11
|
+
this.rootDir,
|
|
12
|
+
"bmad-core",
|
|
13
|
+
"utils",
|
|
14
|
+
"web-agent-startup-instructions.md"
|
|
15
|
+
);
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
parseYaml(content) {
|
|
16
|
-
const yaml = require(
|
|
19
|
+
const yaml = require("js-yaml");
|
|
17
20
|
return yaml.load(content);
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -38,10 +41,10 @@ class WebBuilder {
|
|
|
38
41
|
|
|
39
42
|
// Write to all output directories
|
|
40
43
|
for (const outputDir of this.outputDirs) {
|
|
41
|
-
const outputPath = path.join(outputDir,
|
|
44
|
+
const outputPath = path.join(outputDir, "agents");
|
|
42
45
|
await fs.mkdir(outputPath, { recursive: true });
|
|
43
46
|
const outputFile = path.join(outputPath, `${agentId}.txt`);
|
|
44
|
-
await fs.writeFile(outputFile, bundle,
|
|
47
|
+
await fs.writeFile(outputFile, bundle, "utf8");
|
|
45
48
|
}
|
|
46
49
|
}
|
|
47
50
|
|
|
@@ -57,10 +60,10 @@ class WebBuilder {
|
|
|
57
60
|
|
|
58
61
|
// Write to all output directories
|
|
59
62
|
for (const outputDir of this.outputDirs) {
|
|
60
|
-
const outputPath = path.join(outputDir,
|
|
63
|
+
const outputPath = path.join(outputDir, "teams");
|
|
61
64
|
await fs.mkdir(outputPath, { recursive: true });
|
|
62
65
|
const outputFile = path.join(outputPath, `${teamId}.txt`);
|
|
63
|
-
await fs.writeFile(outputFile, bundle,
|
|
66
|
+
await fs.writeFile(outputFile, bundle, "utf8");
|
|
64
67
|
}
|
|
65
68
|
}
|
|
66
69
|
|
|
@@ -69,7 +72,7 @@ class WebBuilder {
|
|
|
69
72
|
|
|
70
73
|
async buildAgentBundle(agentId) {
|
|
71
74
|
const dependencies = await this.resolver.resolveAgentDependencies(agentId);
|
|
72
|
-
const template = await fs.readFile(this.templatePath,
|
|
75
|
+
const template = await fs.readFile(this.templatePath, "utf8");
|
|
73
76
|
|
|
74
77
|
const sections = [template];
|
|
75
78
|
|
|
@@ -81,12 +84,12 @@ class WebBuilder {
|
|
|
81
84
|
sections.push(this.formatSection(resource.path, resource.content));
|
|
82
85
|
}
|
|
83
86
|
|
|
84
|
-
return sections.join(
|
|
87
|
+
return sections.join("\n");
|
|
85
88
|
}
|
|
86
89
|
|
|
87
90
|
async buildTeamBundle(teamId) {
|
|
88
91
|
const dependencies = await this.resolver.resolveTeamDependencies(teamId);
|
|
89
|
-
const template = await fs.readFile(this.templatePath,
|
|
92
|
+
const template = await fs.readFile(this.templatePath, "utf8");
|
|
90
93
|
|
|
91
94
|
const sections = [template];
|
|
92
95
|
|
|
@@ -103,21 +106,72 @@ class WebBuilder {
|
|
|
103
106
|
sections.push(this.formatSection(resource.path, resource.content));
|
|
104
107
|
}
|
|
105
108
|
|
|
106
|
-
return sections.join(
|
|
109
|
+
return sections.join("\n");
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
processAgentContent(content) {
|
|
113
|
+
// First, replace content before YAML with the template
|
|
114
|
+
const yamlMatch = content.match(/```ya?ml\n([\s\S]*?)\n```/);
|
|
115
|
+
if (!yamlMatch) return content;
|
|
116
|
+
|
|
117
|
+
const yamlContent = yamlMatch[1];
|
|
118
|
+
const yamlStartIndex = content.indexOf(yamlMatch[0]);
|
|
119
|
+
const yamlEndIndex = yamlStartIndex + yamlMatch[0].length;
|
|
120
|
+
|
|
121
|
+
// Parse YAML and remove root and IDE-FILE-RESOLUTION properties
|
|
122
|
+
try {
|
|
123
|
+
const yaml = require("js-yaml");
|
|
124
|
+
const parsed = yaml.load(yamlContent);
|
|
125
|
+
|
|
126
|
+
// Remove the properties if they exist at root level
|
|
127
|
+
delete parsed.root;
|
|
128
|
+
delete parsed['IDE-FILE-RESOLUTION'];
|
|
129
|
+
delete parsed['REQUEST-RESOLUTION'];
|
|
130
|
+
|
|
131
|
+
// Also remove from activation-instructions if they exist
|
|
132
|
+
if (parsed['activation-instructions'] && Array.isArray(parsed['activation-instructions'])) {
|
|
133
|
+
parsed['activation-instructions'] = parsed['activation-instructions'].filter(instruction => {
|
|
134
|
+
return !instruction.startsWith('IDE-FILE-RESOLUTION:') &&
|
|
135
|
+
!instruction.startsWith('REQUEST-RESOLUTION:');
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Reconstruct the YAML
|
|
140
|
+
const cleanedYaml = yaml.dump(parsed, { lineWidth: -1 });
|
|
141
|
+
|
|
142
|
+
// Get the agent name from the YAML for the header
|
|
143
|
+
const agentName = parsed.agent?.id || 'agent';
|
|
144
|
+
|
|
145
|
+
// Build the new content with just the agent header and YAML
|
|
146
|
+
const newHeader = `# ${agentName}\n\nCRITICAL: Read the full YML, start activation to alter your state of being, follow startup section instructions, stay in this being until told to exit this mode:\n\n`;
|
|
147
|
+
const afterYaml = content.substring(yamlEndIndex);
|
|
148
|
+
|
|
149
|
+
return newHeader + "```yaml\n" + cleanedYaml.trim() + "\n```" + afterYaml;
|
|
150
|
+
} catch (error) {
|
|
151
|
+
console.warn("Failed to process agent YAML:", error.message);
|
|
152
|
+
// If parsing fails, return original content
|
|
153
|
+
return content;
|
|
154
|
+
}
|
|
107
155
|
}
|
|
108
156
|
|
|
109
157
|
formatSection(path, content) {
|
|
110
|
-
const separator =
|
|
158
|
+
const separator = "====================";
|
|
159
|
+
|
|
160
|
+
// Process agent content if this is an agent file
|
|
161
|
+
if (path.startsWith("agents#")) {
|
|
162
|
+
content = this.processAgentContent(content);
|
|
163
|
+
}
|
|
164
|
+
|
|
111
165
|
return [
|
|
112
166
|
`${separator} START: ${path} ${separator}`,
|
|
113
167
|
content.trim(),
|
|
114
168
|
`${separator} END: ${path} ${separator}`,
|
|
115
|
-
|
|
116
|
-
].join(
|
|
169
|
+
"",
|
|
170
|
+
].join("\n");
|
|
117
171
|
}
|
|
118
172
|
|
|
119
173
|
async validate() {
|
|
120
|
-
console.log(
|
|
174
|
+
console.log("Validating agent configurations...");
|
|
121
175
|
const agents = await this.resolver.listAgents();
|
|
122
176
|
for (const agentId of agents) {
|
|
123
177
|
try {
|
|
@@ -129,7 +183,7 @@ class WebBuilder {
|
|
|
129
183
|
}
|
|
130
184
|
}
|
|
131
185
|
|
|
132
|
-
console.log(
|
|
186
|
+
console.log("\nValidating team configurations...");
|
|
133
187
|
const teams = await this.resolver.listTeams();
|
|
134
188
|
for (const teamId of teams) {
|
|
135
189
|
try {
|
|
@@ -154,10 +208,8 @@ class WebBuilder {
|
|
|
154
208
|
}
|
|
155
209
|
|
|
156
210
|
async buildExpansionPack(packName, options = {}) {
|
|
157
|
-
const packDir = path.join(this.rootDir,
|
|
158
|
-
const outputDirs = [
|
|
159
|
-
path.join(this.rootDir, 'dist', 'expansion-packs', packName)
|
|
160
|
-
];
|
|
211
|
+
const packDir = path.join(this.rootDir, "expansion-packs", packName);
|
|
212
|
+
const outputDirs = [path.join(this.rootDir, "dist", "expansion-packs", packName)];
|
|
161
213
|
|
|
162
214
|
// Clean output directories if requested
|
|
163
215
|
if (options.clean !== false) {
|
|
@@ -171,27 +223,27 @@ class WebBuilder {
|
|
|
171
223
|
}
|
|
172
224
|
|
|
173
225
|
// Build individual agents first
|
|
174
|
-
const agentsDir = path.join(packDir,
|
|
226
|
+
const agentsDir = path.join(packDir, "agents");
|
|
175
227
|
try {
|
|
176
228
|
const agentFiles = await fs.readdir(agentsDir);
|
|
177
|
-
const agentMarkdownFiles = agentFiles.filter(f => f.endsWith(
|
|
178
|
-
|
|
229
|
+
const agentMarkdownFiles = agentFiles.filter((f) => f.endsWith(".md"));
|
|
230
|
+
|
|
179
231
|
if (agentMarkdownFiles.length > 0) {
|
|
180
232
|
console.log(` Building individual agents for ${packName}:`);
|
|
181
|
-
|
|
233
|
+
|
|
182
234
|
for (const agentFile of agentMarkdownFiles) {
|
|
183
|
-
const agentName = agentFile.replace(
|
|
235
|
+
const agentName = agentFile.replace(".md", "");
|
|
184
236
|
console.log(` - ${agentName}`);
|
|
185
|
-
|
|
237
|
+
|
|
186
238
|
// Build individual agent bundle
|
|
187
239
|
const bundle = await this.buildExpansionAgentBundle(packName, packDir, agentName);
|
|
188
|
-
|
|
240
|
+
|
|
189
241
|
// Write to all output directories
|
|
190
242
|
for (const outputDir of outputDirs) {
|
|
191
|
-
const agentsOutputDir = path.join(outputDir,
|
|
243
|
+
const agentsOutputDir = path.join(outputDir, "agents");
|
|
192
244
|
await fs.mkdir(agentsOutputDir, { recursive: true });
|
|
193
245
|
const outputFile = path.join(agentsOutputDir, `${agentName}.txt`);
|
|
194
|
-
await fs.writeFile(outputFile, bundle,
|
|
246
|
+
await fs.writeFile(outputFile, bundle, "utf8");
|
|
195
247
|
}
|
|
196
248
|
}
|
|
197
249
|
}
|
|
@@ -200,24 +252,24 @@ class WebBuilder {
|
|
|
200
252
|
}
|
|
201
253
|
|
|
202
254
|
// Build team bundle
|
|
203
|
-
const agentTeamsDir = path.join(packDir,
|
|
255
|
+
const agentTeamsDir = path.join(packDir, "agent-teams");
|
|
204
256
|
try {
|
|
205
257
|
const teamFiles = await fs.readdir(agentTeamsDir);
|
|
206
|
-
const teamFile = teamFiles.find(f => f.endsWith(
|
|
207
|
-
|
|
258
|
+
const teamFile = teamFiles.find((f) => f.endsWith(".yml"));
|
|
259
|
+
|
|
208
260
|
if (teamFile) {
|
|
209
261
|
console.log(` Building team bundle for ${packName}`);
|
|
210
262
|
const teamConfigPath = path.join(agentTeamsDir, teamFile);
|
|
211
|
-
|
|
263
|
+
|
|
212
264
|
// Build expansion pack as a team bundle
|
|
213
265
|
const bundle = await this.buildExpansionTeamBundle(packName, packDir, teamConfigPath);
|
|
214
|
-
|
|
266
|
+
|
|
215
267
|
// Write to all output directories
|
|
216
268
|
for (const outputDir of outputDirs) {
|
|
217
|
-
const teamsOutputDir = path.join(outputDir,
|
|
269
|
+
const teamsOutputDir = path.join(outputDir, "teams");
|
|
218
270
|
await fs.mkdir(teamsOutputDir, { recursive: true });
|
|
219
|
-
const outputFile = path.join(teamsOutputDir, teamFile.replace(
|
|
220
|
-
await fs.writeFile(outputFile, bundle,
|
|
271
|
+
const outputFile = path.join(teamsOutputDir, teamFile.replace(".yml", ".txt"));
|
|
272
|
+
await fs.writeFile(outputFile, bundle, "utf8");
|
|
221
273
|
console.log(` ✓ Created bundle: ${path.relative(this.rootDir, outputFile)}`);
|
|
222
274
|
}
|
|
223
275
|
} else {
|
|
@@ -229,49 +281,58 @@ class WebBuilder {
|
|
|
229
281
|
}
|
|
230
282
|
|
|
231
283
|
async buildExpansionAgentBundle(packName, packDir, agentName) {
|
|
232
|
-
const template = await fs.readFile(this.templatePath,
|
|
284
|
+
const template = await fs.readFile(this.templatePath, "utf8");
|
|
233
285
|
const sections = [template];
|
|
234
286
|
|
|
235
287
|
// Add agent configuration
|
|
236
|
-
const agentPath = path.join(packDir,
|
|
237
|
-
const agentContent = await fs.readFile(agentPath,
|
|
288
|
+
const agentPath = path.join(packDir, "agents", `${agentName}.md`);
|
|
289
|
+
const agentContent = await fs.readFile(agentPath, "utf8");
|
|
238
290
|
sections.push(this.formatSection(`agents#${agentName}`, agentContent));
|
|
239
291
|
|
|
240
292
|
// Resolve and add agent dependencies
|
|
241
293
|
const agentYaml = agentContent.match(/```yaml\n([\s\S]*?)\n```/);
|
|
242
294
|
if (agentYaml) {
|
|
243
295
|
try {
|
|
244
|
-
const yaml = require(
|
|
296
|
+
const yaml = require("js-yaml");
|
|
245
297
|
const agentConfig = yaml.load(agentYaml[1]);
|
|
246
|
-
|
|
298
|
+
|
|
247
299
|
if (agentConfig.dependencies) {
|
|
248
300
|
// Add resources, first try expansion pack, then core
|
|
249
301
|
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
|
|
250
302
|
if (Array.isArray(resources)) {
|
|
251
303
|
for (const resourceName of resources) {
|
|
252
304
|
let found = false;
|
|
253
|
-
const extensions = [
|
|
254
|
-
|
|
305
|
+
const extensions = [".md", ".yml", ".yaml"];
|
|
306
|
+
|
|
255
307
|
// Try expansion pack first
|
|
256
308
|
for (const ext of extensions) {
|
|
257
309
|
const resourcePath = path.join(packDir, resourceType, `${resourceName}${ext}`);
|
|
258
310
|
try {
|
|
259
|
-
const resourceContent = await fs.readFile(resourcePath,
|
|
260
|
-
sections.push(
|
|
311
|
+
const resourceContent = await fs.readFile(resourcePath, "utf8");
|
|
312
|
+
sections.push(
|
|
313
|
+
this.formatSection(`${resourceType}#${resourceName}`, resourceContent)
|
|
314
|
+
);
|
|
261
315
|
found = true;
|
|
262
316
|
break;
|
|
263
317
|
} catch (error) {
|
|
264
318
|
// Not in expansion pack, continue
|
|
265
319
|
}
|
|
266
320
|
}
|
|
267
|
-
|
|
321
|
+
|
|
268
322
|
// If not found in expansion pack, try core
|
|
269
323
|
if (!found) {
|
|
270
324
|
for (const ext of extensions) {
|
|
271
|
-
const corePath = path.join(
|
|
325
|
+
const corePath = path.join(
|
|
326
|
+
this.rootDir,
|
|
327
|
+
"bmad-core",
|
|
328
|
+
resourceType,
|
|
329
|
+
`${resourceName}${ext}`
|
|
330
|
+
);
|
|
272
331
|
try {
|
|
273
|
-
const coreContent = await fs.readFile(corePath,
|
|
274
|
-
sections.push(
|
|
332
|
+
const coreContent = await fs.readFile(corePath, "utf8");
|
|
333
|
+
sections.push(
|
|
334
|
+
this.formatSection(`${resourceType}#${resourceName}`, coreContent)
|
|
335
|
+
);
|
|
275
336
|
found = true;
|
|
276
337
|
break;
|
|
277
338
|
} catch (error) {
|
|
@@ -279,9 +340,11 @@ class WebBuilder {
|
|
|
279
340
|
}
|
|
280
341
|
}
|
|
281
342
|
}
|
|
282
|
-
|
|
343
|
+
|
|
283
344
|
if (!found) {
|
|
284
|
-
console.warn(
|
|
345
|
+
console.warn(
|
|
346
|
+
` ⚠ Dependency ${resourceType}#${resourceName} not found in expansion pack or core`
|
|
347
|
+
);
|
|
285
348
|
}
|
|
286
349
|
}
|
|
287
350
|
}
|
|
@@ -292,27 +355,27 @@ class WebBuilder {
|
|
|
292
355
|
}
|
|
293
356
|
}
|
|
294
357
|
|
|
295
|
-
return sections.join(
|
|
358
|
+
return sections.join("\n");
|
|
296
359
|
}
|
|
297
360
|
|
|
298
361
|
async buildExpansionTeamBundle(packName, packDir, teamConfigPath) {
|
|
299
|
-
const template = await fs.readFile(this.templatePath,
|
|
362
|
+
const template = await fs.readFile(this.templatePath, "utf8");
|
|
300
363
|
|
|
301
364
|
const sections = [template];
|
|
302
365
|
|
|
303
366
|
// Add team configuration and parse to get agent list
|
|
304
|
-
const teamContent = await fs.readFile(teamConfigPath,
|
|
305
|
-
const teamFileName = path.basename(teamConfigPath,
|
|
367
|
+
const teamContent = await fs.readFile(teamConfigPath, "utf8");
|
|
368
|
+
const teamFileName = path.basename(teamConfigPath, ".yml");
|
|
306
369
|
const teamConfig = this.parseYaml(teamContent);
|
|
307
370
|
sections.push(this.formatSection(`agent-teams#${teamFileName}`, teamContent));
|
|
308
371
|
|
|
309
372
|
// Get list of expansion pack agents
|
|
310
373
|
const expansionAgents = new Set();
|
|
311
|
-
const agentsDir = path.join(packDir,
|
|
374
|
+
const agentsDir = path.join(packDir, "agents");
|
|
312
375
|
try {
|
|
313
376
|
const agentFiles = await fs.readdir(agentsDir);
|
|
314
|
-
for (const agentFile of agentFiles.filter(f => f.endsWith(
|
|
315
|
-
const agentName = agentFile.replace(
|
|
377
|
+
for (const agentFile of agentFiles.filter((f) => f.endsWith(".md"))) {
|
|
378
|
+
const agentName = agentFile.replace(".md", "");
|
|
316
379
|
expansionAgents.add(agentName);
|
|
317
380
|
}
|
|
318
381
|
} catch (error) {
|
|
@@ -321,13 +384,15 @@ class WebBuilder {
|
|
|
321
384
|
|
|
322
385
|
// Build a map of all available expansion pack resources for override checking
|
|
323
386
|
const expansionResources = new Map();
|
|
324
|
-
const resourceDirs = [
|
|
387
|
+
const resourceDirs = ["templates", "tasks", "checklists", "workflows", "data"];
|
|
325
388
|
for (const resourceDir of resourceDirs) {
|
|
326
389
|
const resourcePath = path.join(packDir, resourceDir);
|
|
327
390
|
try {
|
|
328
391
|
const resourceFiles = await fs.readdir(resourcePath);
|
|
329
|
-
for (const resourceFile of resourceFiles.filter(
|
|
330
|
-
|
|
392
|
+
for (const resourceFile of resourceFiles.filter(
|
|
393
|
+
(f) => f.endsWith(".md") || f.endsWith(".yml")
|
|
394
|
+
)) {
|
|
395
|
+
const fileName = resourceFile.replace(/\.(md|yml)$/, "");
|
|
331
396
|
expansionResources.set(`${resourceDir}#${fileName}`, true);
|
|
332
397
|
}
|
|
333
398
|
} catch (error) {
|
|
@@ -337,22 +402,21 @@ class WebBuilder {
|
|
|
337
402
|
|
|
338
403
|
// Process all agents listed in team configuration
|
|
339
404
|
const agentsToProcess = teamConfig.agents || [];
|
|
340
|
-
|
|
405
|
+
|
|
341
406
|
// Ensure bmad-orchestrator is always included for teams
|
|
342
|
-
if (!agentsToProcess.includes(
|
|
407
|
+
if (!agentsToProcess.includes("bmad-orchestrator")) {
|
|
343
408
|
console.warn(` ⚠ Team ${teamFileName} missing bmad-orchestrator, adding automatically`);
|
|
344
|
-
agentsToProcess.unshift(
|
|
409
|
+
agentsToProcess.unshift("bmad-orchestrator");
|
|
345
410
|
}
|
|
346
411
|
|
|
347
412
|
// Track all dependencies from all agents (deduplicated)
|
|
348
413
|
const allDependencies = new Map();
|
|
349
414
|
|
|
350
415
|
for (const agentId of agentsToProcess) {
|
|
351
|
-
|
|
352
416
|
if (expansionAgents.has(agentId)) {
|
|
353
417
|
// Use expansion pack version (override)
|
|
354
418
|
const agentPath = path.join(agentsDir, `${agentId}.md`);
|
|
355
|
-
const agentContent = await fs.readFile(agentPath,
|
|
419
|
+
const agentContent = await fs.readFile(agentPath, "utf8");
|
|
356
420
|
sections.push(this.formatSection(`agents#${agentId}`, agentContent));
|
|
357
421
|
|
|
358
422
|
// Parse and collect dependencies from expansion agent
|
|
@@ -379,8 +443,8 @@ class WebBuilder {
|
|
|
379
443
|
} else {
|
|
380
444
|
// Use core BMAD version
|
|
381
445
|
try {
|
|
382
|
-
const coreAgentPath = path.join(this.rootDir,
|
|
383
|
-
const coreAgentContent = await fs.readFile(coreAgentPath,
|
|
446
|
+
const coreAgentPath = path.join(this.rootDir, "bmad-core", "agents", `${agentId}.md`);
|
|
447
|
+
const coreAgentContent = await fs.readFile(coreAgentPath, "utf8");
|
|
384
448
|
sections.push(this.formatSection(`agents#${agentId}`, coreAgentContent));
|
|
385
449
|
|
|
386
450
|
// Parse and collect dependencies from core agent
|
|
@@ -389,8 +453,8 @@ class WebBuilder {
|
|
|
389
453
|
try {
|
|
390
454
|
// Clean up the YAML to handle command descriptions after dashes
|
|
391
455
|
let yamlContent = agentYaml[1];
|
|
392
|
-
yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm,
|
|
393
|
-
|
|
456
|
+
yamlContent = yamlContent.replace(/^(\s*-)(\s*"[^"]+")(\s*-\s*.*)$/gm, "$1$2");
|
|
457
|
+
|
|
394
458
|
const agentConfig = this.parseYaml(yamlContent);
|
|
395
459
|
if (agentConfig.dependencies) {
|
|
396
460
|
for (const [resourceType, resources] of Object.entries(agentConfig.dependencies)) {
|
|
@@ -418,15 +482,15 @@ class WebBuilder {
|
|
|
418
482
|
// Always prefer expansion pack versions if they exist
|
|
419
483
|
for (const [key, dep] of allDependencies) {
|
|
420
484
|
let found = false;
|
|
421
|
-
const extensions = [
|
|
422
|
-
|
|
485
|
+
const extensions = [".md", ".yml", ".yaml"];
|
|
486
|
+
|
|
423
487
|
// Always check expansion pack first, even if the dependency came from a core agent
|
|
424
488
|
if (expansionResources.has(key)) {
|
|
425
489
|
// We know it exists in expansion pack, find and load it
|
|
426
490
|
for (const ext of extensions) {
|
|
427
491
|
const expansionPath = path.join(packDir, dep.type, `${dep.name}${ext}`);
|
|
428
492
|
try {
|
|
429
|
-
const content = await fs.readFile(expansionPath,
|
|
493
|
+
const content = await fs.readFile(expansionPath, "utf8");
|
|
430
494
|
sections.push(this.formatSection(key, content));
|
|
431
495
|
console.log(` ✓ Using expansion override for ${key}`);
|
|
432
496
|
found = true;
|
|
@@ -436,13 +500,13 @@ class WebBuilder {
|
|
|
436
500
|
}
|
|
437
501
|
}
|
|
438
502
|
}
|
|
439
|
-
|
|
503
|
+
|
|
440
504
|
// If not found in expansion pack (or doesn't exist there), try core
|
|
441
505
|
if (!found) {
|
|
442
506
|
for (const ext of extensions) {
|
|
443
|
-
const corePath = path.join(this.rootDir,
|
|
507
|
+
const corePath = path.join(this.rootDir, "bmad-core", dep.type, `${dep.name}${ext}`);
|
|
444
508
|
try {
|
|
445
|
-
const content = await fs.readFile(corePath,
|
|
509
|
+
const content = await fs.readFile(corePath, "utf8");
|
|
446
510
|
sections.push(this.formatSection(key, content));
|
|
447
511
|
found = true;
|
|
448
512
|
break;
|
|
@@ -451,7 +515,7 @@ class WebBuilder {
|
|
|
451
515
|
}
|
|
452
516
|
}
|
|
453
517
|
}
|
|
454
|
-
|
|
518
|
+
|
|
455
519
|
if (!found) {
|
|
456
520
|
console.warn(` ⚠ Dependency ${key} not found in expansion pack or core`);
|
|
457
521
|
}
|
|
@@ -462,11 +526,13 @@ class WebBuilder {
|
|
|
462
526
|
const resourcePath = path.join(packDir, resourceDir);
|
|
463
527
|
try {
|
|
464
528
|
const resourceFiles = await fs.readdir(resourcePath);
|
|
465
|
-
for (const resourceFile of resourceFiles.filter(
|
|
529
|
+
for (const resourceFile of resourceFiles.filter(
|
|
530
|
+
(f) => f.endsWith(".md") || f.endsWith(".yml")
|
|
531
|
+
)) {
|
|
466
532
|
const filePath = path.join(resourcePath, resourceFile);
|
|
467
|
-
const fileContent = await fs.readFile(filePath,
|
|
468
|
-
const fileName = resourceFile.replace(/\.(md|yml)$/,
|
|
469
|
-
|
|
533
|
+
const fileContent = await fs.readFile(filePath, "utf8");
|
|
534
|
+
const fileName = resourceFile.replace(/\.(md|yml)$/, "");
|
|
535
|
+
|
|
470
536
|
// Only add if not already included as a dependency
|
|
471
537
|
const resourceKey = `${resourceDir}#${fileName}`;
|
|
472
538
|
if (!allDependencies.has(resourceKey)) {
|
|
@@ -478,18 +544,16 @@ class WebBuilder {
|
|
|
478
544
|
}
|
|
479
545
|
}
|
|
480
546
|
|
|
481
|
-
return sections.join(
|
|
547
|
+
return sections.join("\n");
|
|
482
548
|
}
|
|
483
549
|
|
|
484
550
|
async listExpansionPacks() {
|
|
485
|
-
const expansionPacksDir = path.join(this.rootDir,
|
|
551
|
+
const expansionPacksDir = path.join(this.rootDir, "expansion-packs");
|
|
486
552
|
try {
|
|
487
553
|
const entries = await fs.readdir(expansionPacksDir, { withFileTypes: true });
|
|
488
|
-
return entries
|
|
489
|
-
.filter(entry => entry.isDirectory())
|
|
490
|
-
.map(entry => entry.name);
|
|
554
|
+
return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
491
555
|
} catch (error) {
|
|
492
|
-
console.warn(
|
|
556
|
+
console.warn("No expansion-packs directory found");
|
|
493
557
|
return [];
|
|
494
558
|
}
|
|
495
559
|
}
|
|
@@ -499,4 +563,4 @@ class WebBuilder {
|
|
|
499
563
|
}
|
|
500
564
|
}
|
|
501
565
|
|
|
502
|
-
module.exports = WebBuilder;
|
|
566
|
+
module.exports = WebBuilder;
|