bmad-method 6.0.0-Beta.5 → 6.0.0-Beta.7
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/.claude/skills/changelog-social/SKILL.md +178 -0
- package/.claude/skills/changelog-social/examples/discord-example.md +53 -0
- package/.claude/skills/changelog-social/examples/linkedin-example.md +49 -0
- package/.claude/skills/changelog-social/examples/twitter-example.md +55 -0
- package/.claude/skills/changelog-social.skill +0 -0
- package/.claude/skills/draft-changelog/SKILL.md +2 -2
- package/.claude/skills/draft-changelog/prompts/instructions.md +25 -1
- package/.claude/skills/gh-triage/SKILL.md +1 -1
- package/.claude/skills/release-module/SKILL.md +1 -1
- package/.claude/skills/release-module/prompts/instructions.md +0 -4
- package/.github/workflows/quality.yaml +3 -0
- package/.vscode/settings.json +1 -2
- package/CHANGELOG.md +65 -1
- package/docs/bmgd/game-types.md +1 -0
- package/docs/bmgd/index.md +1 -1
- package/docs/bmgd/quick-flow-workflows.md +1 -0
- package/docs/reference/agents.md +22 -0
- package/docs/reference/commands.md +34 -0
- package/docs/reference/testing.md +21 -0
- package/docs/reference/workflow-map.md +1 -1
- package/package.json +5 -2
- package/src/bmm/agents/analyst.agent.yaml +11 -5
- package/src/bmm/agents/dev.agent.yaml +0 -1
- package/src/bmm/agents/pm.agent.yaml +3 -6
- package/src/bmm/agents/{quinn.agent.yaml → qa.agent.yaml} +3 -3
- package/src/bmm/module-help.csv +13 -20
- package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +0 -1
- package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -0
- package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -0
- package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +2 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +63 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -0
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +65 -0
- package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +2 -0
- package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +0 -1
- package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +0 -1
- package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +0 -3
- package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +0 -3
- package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +0 -4
- package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +0 -6
- package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +0 -1
- package/src/bmm/workflows/document-project/workflow.yaml +0 -8
- package/src/bmm/workflows/qa/automate/workflow.yaml +0 -2
- package/src/core/tasks/editorial-review-prose.xml +14 -12
- package/src/core/tasks/editorial-review-structure.xml +28 -28
- package/src/core/tasks/help.md +36 -16
- package/src/core/tasks/index-docs.xml +1 -1
- package/src/core/tasks/review-adversarial-general.xml +2 -2
- package/src/core/tasks/shard-doc.xml +1 -2
- package/src/core/tasks/workflow.xml +1 -1
- package/src/core/workflows/advanced-elicitation/workflow.xml +1 -1
- package/test/test-installation-components.js +1 -1
- package/tools/build-docs.js +21 -1
- package/tools/cli/bmad-cli.js +42 -3
- package/tools/cli/installers/lib/core/config-collector.js +5 -1
- package/tools/cli/installers/lib/core/dependency-resolver.js +1 -1
- package/tools/cli/installers/lib/core/installer.js +1 -6
- package/tools/cli/installers/lib/core/manifest-generator.js +149 -125
- package/tools/cli/installers/lib/custom/handler.js +1 -1
- package/tools/cli/installers/lib/ide/_base-ide.js +25 -15
- package/tools/cli/installers/lib/ide/_config-driven.js +79 -9
- package/tools/cli/installers/lib/ide/codex.js +7 -1
- package/tools/cli/installers/lib/ide/manager.js +8 -2
- package/tools/cli/installers/lib/ide/platform-codes.yaml +0 -3
- package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +14 -3
- package/tools/cli/installers/lib/ide/shared/path-utils.js +9 -2
- package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +126 -29
- package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +2 -2
- package/tools/cli/installers/lib/ide/templates/combined/default-task.md +10 -0
- package/tools/cli/installers/lib/ide/templates/combined/default-tool.md +10 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-task.toml +11 -0
- package/tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml +11 -0
- package/tools/cli/installers/lib/modules/manager.js +3 -6
- package/tools/cli/lib/agent/installer.js +1 -1
- package/tools/cli/lib/prompts.js +192 -6
- package/tools/cli/lib/ui.js +124 -72
- package/tools/schema/agent.js +0 -1
- package/tools/validate-file-refs.js +480 -0
- package/website/astro.config.mjs +1 -35
- package/src/bmm/workflows/1-analysis/research/workflow.md +0 -173
- package/src/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md +0 -433
- package/src/bmm/workflows/2-plan-workflows/create-prd/workflow.md +0 -150
- package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +0 -90
- package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +0 -127
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +0 -39
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +0 -130
- package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +0 -43
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +0 -141
- package/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +0 -49
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +0 -241
- package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +0 -27
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +0 -38
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +0 -133
- package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +0 -27
- package/src/core/resources/excalidraw/README.md +0 -160
- package/src/core/resources/excalidraw/excalidraw-helpers.md +0 -127
- package/src/core/resources/excalidraw/library-loader.md +0 -50
- package/src/core/resources/excalidraw/validate-json-instructions.md +0 -79
- package/tools/flattener/aggregate.js +0 -76
- package/tools/flattener/binary.js +0 -80
- package/tools/flattener/discovery.js +0 -71
- package/tools/flattener/files.js +0 -35
- package/tools/flattener/ignoreRules.js +0 -172
- package/tools/flattener/main.js +0 -483
- package/tools/flattener/projectRoot.js +0 -201
- package/tools/flattener/prompts.js +0 -44
- package/tools/flattener/stats.helpers.js +0 -368
- package/tools/flattener/stats.js +0 -75
- package/tools/flattener/test-matrix.js +0 -409
- package/tools/flattener/xml.js +0 -82
|
@@ -66,6 +66,13 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
66
66
|
*/
|
|
67
67
|
async installToTarget(projectDir, bmadDir, config, options) {
|
|
68
68
|
const { target_dir, template_type, artifact_types } = config;
|
|
69
|
+
|
|
70
|
+
// Skip targets with explicitly empty artifact_types array
|
|
71
|
+
// This prevents creating empty directories when no artifacts will be written
|
|
72
|
+
if (Array.isArray(artifact_types) && artifact_types.length === 0) {
|
|
73
|
+
return { success: true, results: { agents: 0, workflows: 0, tasks: 0, tools: 0 } };
|
|
74
|
+
}
|
|
75
|
+
|
|
69
76
|
const targetPath = path.join(projectDir, target_dir);
|
|
70
77
|
await this.ensureDir(targetPath);
|
|
71
78
|
|
|
@@ -86,10 +93,11 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
86
93
|
results.workflows = await this.writeWorkflowArtifacts(targetPath, artifacts, template_type, config);
|
|
87
94
|
}
|
|
88
95
|
|
|
89
|
-
// Install tasks and tools
|
|
96
|
+
// Install tasks and tools using template system (supports TOML for Gemini, MD for others)
|
|
90
97
|
if (!artifact_types || artifact_types.includes('tasks') || artifact_types.includes('tools')) {
|
|
91
|
-
const taskToolGen = new TaskToolCommandGenerator();
|
|
92
|
-
const
|
|
98
|
+
const taskToolGen = new TaskToolCommandGenerator(this.bmadFolderName);
|
|
99
|
+
const { artifacts } = await taskToolGen.collectTaskToolArtifacts(bmadDir);
|
|
100
|
+
const taskToolResult = await this.writeTaskToolArtifacts(targetPath, artifacts, template_type, config);
|
|
93
101
|
results.tasks = taskToolResult.tasks || 0;
|
|
94
102
|
results.tools = taskToolResult.tools || 0;
|
|
95
103
|
}
|
|
@@ -180,6 +188,53 @@ class ConfigDrivenIdeSetup extends BaseIdeSetup {
|
|
|
180
188
|
return count;
|
|
181
189
|
}
|
|
182
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Write task/tool artifacts to target directory using templates
|
|
193
|
+
* @param {string} targetPath - Target directory path
|
|
194
|
+
* @param {Array} artifacts - Task/tool artifacts
|
|
195
|
+
* @param {string} templateType - Template type to use
|
|
196
|
+
* @param {Object} config - Installation configuration
|
|
197
|
+
* @returns {Promise<Object>} Counts of tasks and tools written
|
|
198
|
+
*/
|
|
199
|
+
async writeTaskToolArtifacts(targetPath, artifacts, templateType, config = {}) {
|
|
200
|
+
let taskCount = 0;
|
|
201
|
+
let toolCount = 0;
|
|
202
|
+
|
|
203
|
+
// Pre-load templates to avoid repeated file I/O in the loop
|
|
204
|
+
const taskTemplate = await this.loadTemplate(templateType, 'task', config, 'default-task');
|
|
205
|
+
const toolTemplate = await this.loadTemplate(templateType, 'tool', config, 'default-tool');
|
|
206
|
+
|
|
207
|
+
const { artifact_types } = config;
|
|
208
|
+
|
|
209
|
+
for (const artifact of artifacts) {
|
|
210
|
+
if (artifact.type !== 'task' && artifact.type !== 'tool') {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// Skip if the specific artifact type is not requested in config
|
|
215
|
+
if (artifact_types) {
|
|
216
|
+
if (artifact.type === 'task' && !artifact_types.includes('tasks')) continue;
|
|
217
|
+
if (artifact.type === 'tool' && !artifact_types.includes('tools')) continue;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Use pre-loaded template based on artifact type
|
|
221
|
+
const { content: template, extension } = artifact.type === 'task' ? taskTemplate : toolTemplate;
|
|
222
|
+
|
|
223
|
+
const content = this.renderTemplate(template, artifact);
|
|
224
|
+
const filename = this.generateFilename(artifact, artifact.type, extension);
|
|
225
|
+
const filePath = path.join(targetPath, filename);
|
|
226
|
+
await this.writeFile(filePath, content);
|
|
227
|
+
|
|
228
|
+
if (artifact.type === 'task') {
|
|
229
|
+
taskCount++;
|
|
230
|
+
} else {
|
|
231
|
+
toolCount++;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
return { tasks: taskCount, tools: toolCount };
|
|
236
|
+
}
|
|
237
|
+
|
|
183
238
|
/**
|
|
184
239
|
* Load template based on type and configuration
|
|
185
240
|
* @param {string} templateType - Template type (claude, windsurf, etc.)
|
|
@@ -316,10 +371,24 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
316
371
|
renderTemplate(template, artifact) {
|
|
317
372
|
// Use the appropriate path property based on artifact type
|
|
318
373
|
let pathToUse = artifact.relativePath || '';
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
374
|
+
switch (artifact.type) {
|
|
375
|
+
case 'agent-launcher': {
|
|
376
|
+
pathToUse = artifact.agentPath || artifact.relativePath || '';
|
|
377
|
+
|
|
378
|
+
break;
|
|
379
|
+
}
|
|
380
|
+
case 'workflow-command': {
|
|
381
|
+
pathToUse = artifact.workflowPath || artifact.relativePath || '';
|
|
382
|
+
|
|
383
|
+
break;
|
|
384
|
+
}
|
|
385
|
+
case 'task':
|
|
386
|
+
case 'tool': {
|
|
387
|
+
pathToUse = artifact.path || artifact.relativePath || '';
|
|
388
|
+
|
|
389
|
+
break;
|
|
390
|
+
}
|
|
391
|
+
// No default
|
|
323
392
|
}
|
|
324
393
|
|
|
325
394
|
let rendered = template
|
|
@@ -351,8 +420,9 @@ LOAD and execute from: {project-root}/{{bmadFolderName}}/{{path}}
|
|
|
351
420
|
// Reuse central logic to ensure consistent naming conventions
|
|
352
421
|
const standardName = toDashPath(artifact.relativePath);
|
|
353
422
|
|
|
354
|
-
// Clean up potential double extensions from source files (e.g. .yaml.md -> .md)
|
|
355
|
-
|
|
423
|
+
// Clean up potential double extensions from source files (e.g. .yaml.md, .xml.md -> .md)
|
|
424
|
+
// This handles any extensions that might slip through toDashPath()
|
|
425
|
+
const baseName = standardName.replace(/\.(md|yaml|yml|json|xml|toml)\.md$/i, '.md');
|
|
356
426
|
|
|
357
427
|
// If using default markdown, preserve the bmad-agent- prefix for agents
|
|
358
428
|
if (extension === '.md') {
|
|
@@ -104,7 +104,10 @@ class CodexSetup extends BaseIdeSetup {
|
|
|
104
104
|
);
|
|
105
105
|
taskArtifacts.push({
|
|
106
106
|
type: 'task',
|
|
107
|
+
name: task.name,
|
|
108
|
+
displayName: task.name,
|
|
107
109
|
module: task.module,
|
|
110
|
+
path: task.path,
|
|
108
111
|
sourcePath: task.path,
|
|
109
112
|
relativePath: path.join(task.module, 'tasks', `${task.name}.md`),
|
|
110
113
|
content,
|
|
@@ -116,7 +119,7 @@ class CodexSetup extends BaseIdeSetup {
|
|
|
116
119
|
const workflowCount = await workflowGenerator.writeDashArtifacts(destDir, workflowArtifacts);
|
|
117
120
|
|
|
118
121
|
// Also write tasks using underscore format
|
|
119
|
-
const ttGen = new TaskToolCommandGenerator();
|
|
122
|
+
const ttGen = new TaskToolCommandGenerator(this.bmadFolderName);
|
|
120
123
|
const tasksWritten = await ttGen.writeDashArtifacts(destDir, taskArtifacts);
|
|
121
124
|
|
|
122
125
|
const written = agentCount + workflowCount + tasksWritten;
|
|
@@ -214,7 +217,10 @@ class CodexSetup extends BaseIdeSetup {
|
|
|
214
217
|
|
|
215
218
|
artifacts.push({
|
|
216
219
|
type: 'task',
|
|
220
|
+
name: task.name,
|
|
221
|
+
displayName: task.name,
|
|
217
222
|
module: task.module,
|
|
223
|
+
path: task.path,
|
|
218
224
|
sourcePath: task.path,
|
|
219
225
|
relativePath: path.join(task.module, 'tasks', `${task.name}.md`),
|
|
220
226
|
content,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const fs = require('fs-extra');
|
|
2
2
|
const path = require('node:path');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
+
const { BMAD_FOLDER_NAME } = require('./shared/path-utils');
|
|
4
5
|
|
|
5
6
|
/**
|
|
6
7
|
* IDE Manager - handles IDE-specific setup
|
|
@@ -14,7 +15,7 @@ class IdeManager {
|
|
|
14
15
|
constructor() {
|
|
15
16
|
this.handlers = new Map();
|
|
16
17
|
this._initialized = false;
|
|
17
|
-
this.bmadFolderName =
|
|
18
|
+
this.bmadFolderName = BMAD_FOLDER_NAME; // Default, can be overridden
|
|
18
19
|
}
|
|
19
20
|
|
|
20
21
|
/**
|
|
@@ -73,6 +74,9 @@ class IdeManager {
|
|
|
73
74
|
if (HandlerClass) {
|
|
74
75
|
const instance = new HandlerClass();
|
|
75
76
|
if (instance.name && typeof instance.name === 'string') {
|
|
77
|
+
if (typeof instance.setBmadFolderName === 'function') {
|
|
78
|
+
instance.setBmadFolderName(this.bmadFolderName);
|
|
79
|
+
}
|
|
76
80
|
this.handlers.set(instance.name, instance);
|
|
77
81
|
}
|
|
78
82
|
}
|
|
@@ -100,7 +104,9 @@ class IdeManager {
|
|
|
100
104
|
if (!platformInfo.installer) continue;
|
|
101
105
|
|
|
102
106
|
const handler = new ConfigDrivenIdeSetup(platformCode, platformInfo);
|
|
103
|
-
handler.setBmadFolderName
|
|
107
|
+
if (typeof handler.setBmadFolderName === 'function') {
|
|
108
|
+
handler.setBmadFolderName(this.bmadFolderName);
|
|
109
|
+
}
|
|
104
110
|
this.handlers.set(platformCode, handler);
|
|
105
111
|
}
|
|
106
112
|
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
const path = require('node:path');
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const chalk = require('chalk');
|
|
4
|
-
const { toColonPath, toDashPath, customAgentColonName, customAgentDashName } = require('./path-utils');
|
|
4
|
+
const { toColonPath, toDashPath, customAgentColonName, customAgentDashName, BMAD_FOLDER_NAME } = require('./path-utils');
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
7
|
* Generates launcher command files for each agent
|
|
8
8
|
* Similar to WorkflowCommandGenerator but for agents
|
|
9
9
|
*/
|
|
10
10
|
class AgentCommandGenerator {
|
|
11
|
-
constructor(bmadFolderName =
|
|
11
|
+
constructor(bmadFolderName = BMAD_FOLDER_NAME) {
|
|
12
12
|
this.templatePath = path.join(__dirname, '../templates/agent-command-template.md');
|
|
13
13
|
this.bmadFolderName = bmadFolderName;
|
|
14
14
|
}
|
|
@@ -141,13 +141,24 @@ async function getTasksFromDir(dirPath, moduleName) {
|
|
|
141
141
|
const files = await fs.readdir(dirPath);
|
|
142
142
|
|
|
143
143
|
for (const file of files) {
|
|
144
|
-
|
|
144
|
+
// Include both .md and .xml task files
|
|
145
|
+
if (!file.endsWith('.md') && !file.endsWith('.xml')) {
|
|
145
146
|
continue;
|
|
146
147
|
}
|
|
147
148
|
|
|
149
|
+
const filePath = path.join(dirPath, file);
|
|
150
|
+
const content = await fs.readFile(filePath, 'utf8');
|
|
151
|
+
|
|
152
|
+
// Skip internal/engine files (not user-facing tasks)
|
|
153
|
+
if (content.includes('internal="true"')) {
|
|
154
|
+
continue;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Remove extension to get task name
|
|
158
|
+
const ext = file.endsWith('.xml') ? '.xml' : '.md';
|
|
148
159
|
tasks.push({
|
|
149
|
-
path:
|
|
150
|
-
name: file.replace(
|
|
160
|
+
path: filePath,
|
|
161
|
+
name: file.replace(ext, ''),
|
|
151
162
|
module: moduleName,
|
|
152
163
|
});
|
|
153
164
|
}
|
|
@@ -18,6 +18,9 @@
|
|
|
18
18
|
const TYPE_SEGMENTS = ['workflows', 'tasks', 'tools'];
|
|
19
19
|
const AGENT_SEGMENT = 'agents';
|
|
20
20
|
|
|
21
|
+
// BMAD installation folder name - centralized constant for all installers
|
|
22
|
+
const BMAD_FOLDER_NAME = '_bmad';
|
|
23
|
+
|
|
21
24
|
/**
|
|
22
25
|
* Convert hierarchical path to flat dash-separated name (NEW STANDARD)
|
|
23
26
|
* Converts: 'bmm', 'agents', 'pm' → 'bmad-agent-bmm-pm.md'
|
|
@@ -59,7 +62,9 @@ function toDashPath(relativePath) {
|
|
|
59
62
|
return 'bmad-unknown.md';
|
|
60
63
|
}
|
|
61
64
|
|
|
62
|
-
|
|
65
|
+
// Strip common file extensions to avoid double extensions in generated filenames
|
|
66
|
+
// e.g., 'create-story.xml' → 'create-story', 'workflow.yaml' → 'workflow'
|
|
67
|
+
const withoutExt = relativePath.replace(/\.(md|yaml|yml|json|xml|toml)$/i, '');
|
|
63
68
|
const parts = withoutExt.split(/[/\\]/);
|
|
64
69
|
|
|
65
70
|
const module = parts[0];
|
|
@@ -183,7 +188,8 @@ function toUnderscoreName(module, type, name) {
|
|
|
183
188
|
* @deprecated Use toDashPath instead
|
|
184
189
|
*/
|
|
185
190
|
function toUnderscorePath(relativePath) {
|
|
186
|
-
|
|
191
|
+
// Strip common file extensions (same as toDashPath for consistency)
|
|
192
|
+
const withoutExt = relativePath.replace(/\.(md|yaml|yml|json|xml|toml)$/i, '');
|
|
187
193
|
const parts = withoutExt.split(/[/\\]/);
|
|
188
194
|
|
|
189
195
|
const module = parts[0];
|
|
@@ -289,4 +295,5 @@ module.exports = {
|
|
|
289
295
|
|
|
290
296
|
TYPE_SEGMENTS,
|
|
291
297
|
AGENT_SEGMENT,
|
|
298
|
+
BMAD_FOLDER_NAME,
|
|
292
299
|
};
|
|
@@ -2,12 +2,95 @@ const path = require('node:path');
|
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const csv = require('csv-parse/sync');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
-
const { toColonName, toColonPath, toDashPath } = require('./path-utils');
|
|
5
|
+
const { toColonName, toColonPath, toDashPath, BMAD_FOLDER_NAME } = require('./path-utils');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Generates command files for standalone tasks and tools
|
|
9
9
|
*/
|
|
10
10
|
class TaskToolCommandGenerator {
|
|
11
|
+
/**
|
|
12
|
+
* @param {string} bmadFolderName - Name of the BMAD folder for template rendering (default: '_bmad')
|
|
13
|
+
* Note: This parameter is accepted for API consistency with AgentCommandGenerator and
|
|
14
|
+
* WorkflowCommandGenerator, but is not used for path stripping. The manifest always stores
|
|
15
|
+
* filesystem paths with '_bmad/' prefix (the actual folder name), while bmadFolderName is
|
|
16
|
+
* used for template placeholder rendering ({{bmadFolderName}}).
|
|
17
|
+
*/
|
|
18
|
+
constructor(bmadFolderName = BMAD_FOLDER_NAME) {
|
|
19
|
+
this.bmadFolderName = bmadFolderName;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Collect task and tool artifacts for IDE installation
|
|
24
|
+
* @param {string} bmadDir - BMAD installation directory
|
|
25
|
+
* @returns {Promise<Object>} Artifacts array with metadata
|
|
26
|
+
*/
|
|
27
|
+
async collectTaskToolArtifacts(bmadDir) {
|
|
28
|
+
const tasks = await this.loadTaskManifest(bmadDir);
|
|
29
|
+
const tools = await this.loadToolManifest(bmadDir);
|
|
30
|
+
|
|
31
|
+
// All tasks/tools in manifest are standalone (internal=true items are filtered during manifest generation)
|
|
32
|
+
const artifacts = [];
|
|
33
|
+
const bmadPrefix = `${BMAD_FOLDER_NAME}/`;
|
|
34
|
+
|
|
35
|
+
// Collect task artifacts
|
|
36
|
+
for (const task of tasks || []) {
|
|
37
|
+
let taskPath = (task.path || '').replaceAll('\\', '/');
|
|
38
|
+
// Convert absolute paths to relative paths
|
|
39
|
+
if (path.isAbsolute(taskPath)) {
|
|
40
|
+
taskPath = path.relative(bmadDir, taskPath).replaceAll('\\', '/');
|
|
41
|
+
}
|
|
42
|
+
// Remove _bmad/ prefix if present to get relative path within bmad folder
|
|
43
|
+
if (taskPath.startsWith(bmadPrefix)) {
|
|
44
|
+
taskPath = taskPath.slice(bmadPrefix.length);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const taskExt = path.extname(taskPath) || '.md';
|
|
48
|
+
artifacts.push({
|
|
49
|
+
type: 'task',
|
|
50
|
+
name: task.name,
|
|
51
|
+
displayName: task.displayName || task.name,
|
|
52
|
+
description: task.description || `Execute ${task.displayName || task.name}`,
|
|
53
|
+
module: task.module,
|
|
54
|
+
// Use forward slashes for cross-platform consistency (not path.join which uses backslashes on Windows)
|
|
55
|
+
relativePath: `${task.module}/tasks/${task.name}${taskExt}`,
|
|
56
|
+
path: taskPath,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Collect tool artifacts
|
|
61
|
+
for (const tool of tools || []) {
|
|
62
|
+
let toolPath = (tool.path || '').replaceAll('\\', '/');
|
|
63
|
+
// Convert absolute paths to relative paths
|
|
64
|
+
if (path.isAbsolute(toolPath)) {
|
|
65
|
+
toolPath = path.relative(bmadDir, toolPath).replaceAll('\\', '/');
|
|
66
|
+
}
|
|
67
|
+
// Remove _bmad/ prefix if present to get relative path within bmad folder
|
|
68
|
+
if (toolPath.startsWith(bmadPrefix)) {
|
|
69
|
+
toolPath = toolPath.slice(bmadPrefix.length);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const toolExt = path.extname(toolPath) || '.md';
|
|
73
|
+
artifacts.push({
|
|
74
|
+
type: 'tool',
|
|
75
|
+
name: tool.name,
|
|
76
|
+
displayName: tool.displayName || tool.name,
|
|
77
|
+
description: tool.description || `Execute ${tool.displayName || tool.name}`,
|
|
78
|
+
module: tool.module,
|
|
79
|
+
// Use forward slashes for cross-platform consistency (not path.join which uses backslashes on Windows)
|
|
80
|
+
relativePath: `${tool.module}/tools/${tool.name}${toolExt}`,
|
|
81
|
+
path: toolPath,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
artifacts,
|
|
87
|
+
counts: {
|
|
88
|
+
tasks: (tasks || []).length,
|
|
89
|
+
tools: (tools || []).length,
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
11
94
|
/**
|
|
12
95
|
* Generate task and tool commands from manifest CSVs
|
|
13
96
|
* @param {string} projectDir - Project directory
|
|
@@ -18,17 +101,13 @@ class TaskToolCommandGenerator {
|
|
|
18
101
|
const tasks = await this.loadTaskManifest(bmadDir);
|
|
19
102
|
const tools = await this.loadToolManifest(bmadDir);
|
|
20
103
|
|
|
21
|
-
// Filter to only standalone items
|
|
22
|
-
const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : [];
|
|
23
|
-
const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : [];
|
|
24
|
-
|
|
25
104
|
// Base commands directory - use provided or default to Claude Code structure
|
|
26
105
|
const commandsDir = baseCommandsDir || path.join(projectDir, '.claude', 'commands', 'bmad');
|
|
27
106
|
|
|
28
107
|
let generatedCount = 0;
|
|
29
108
|
|
|
30
109
|
// Generate command files for tasks
|
|
31
|
-
for (const task of
|
|
110
|
+
for (const task of tasks || []) {
|
|
32
111
|
const moduleTasksDir = path.join(commandsDir, task.module, 'tasks');
|
|
33
112
|
await fs.ensureDir(moduleTasksDir);
|
|
34
113
|
|
|
@@ -40,7 +119,7 @@ class TaskToolCommandGenerator {
|
|
|
40
119
|
}
|
|
41
120
|
|
|
42
121
|
// Generate command files for tools
|
|
43
|
-
for (const tool of
|
|
122
|
+
for (const tool of tools || []) {
|
|
44
123
|
const moduleToolsDir = path.join(commandsDir, tool.module, 'tools');
|
|
45
124
|
await fs.ensureDir(moduleToolsDir);
|
|
46
125
|
|
|
@@ -53,8 +132,8 @@ class TaskToolCommandGenerator {
|
|
|
53
132
|
|
|
54
133
|
return {
|
|
55
134
|
generated: generatedCount,
|
|
56
|
-
tasks:
|
|
57
|
-
tools:
|
|
135
|
+
tasks: (tasks || []).length,
|
|
136
|
+
tools: (tools || []).length,
|
|
58
137
|
};
|
|
59
138
|
}
|
|
60
139
|
|
|
@@ -65,9 +144,35 @@ class TaskToolCommandGenerator {
|
|
|
65
144
|
const description = item.description || `Execute ${item.displayName || item.name}`;
|
|
66
145
|
|
|
67
146
|
// Convert path to use {project-root} placeholder
|
|
147
|
+
// Handle undefined/missing path by constructing from module and name
|
|
68
148
|
let itemPath = item.path;
|
|
69
|
-
if (itemPath
|
|
70
|
-
|
|
149
|
+
if (!itemPath || typeof itemPath !== 'string') {
|
|
150
|
+
// Fallback: construct path from module and name if path is missing
|
|
151
|
+
const typePlural = type === 'task' ? 'tasks' : 'tools';
|
|
152
|
+
itemPath = `{project-root}/${this.bmadFolderName}/${item.module}/${typePlural}/${item.name}.md`;
|
|
153
|
+
} else {
|
|
154
|
+
// Normalize path separators to forward slashes
|
|
155
|
+
itemPath = itemPath.replaceAll('\\', '/');
|
|
156
|
+
|
|
157
|
+
// Extract relative path from absolute paths (Windows or Unix)
|
|
158
|
+
// Look for _bmad/ or bmad/ in the path and extract everything after it
|
|
159
|
+
// Match patterns like: /_bmad/core/tasks/... or /bmad/core/tasks/...
|
|
160
|
+
// Use [/\\] to handle both Unix forward slashes and Windows backslashes,
|
|
161
|
+
// and also paths without a leading separator (e.g., C:/_bmad/...)
|
|
162
|
+
const bmadMatch = itemPath.match(/[/\\]_bmad[/\\](.+)$/) || itemPath.match(/[/\\]bmad[/\\](.+)$/);
|
|
163
|
+
if (bmadMatch) {
|
|
164
|
+
// Found /_bmad/ or /bmad/ - use relative path after it
|
|
165
|
+
itemPath = `{project-root}/${this.bmadFolderName}/${bmadMatch[1]}`;
|
|
166
|
+
} else if (itemPath.startsWith(`${BMAD_FOLDER_NAME}/`)) {
|
|
167
|
+
// Relative path starting with _bmad/
|
|
168
|
+
itemPath = `{project-root}/${this.bmadFolderName}/${itemPath.slice(BMAD_FOLDER_NAME.length + 1)}`;
|
|
169
|
+
} else if (itemPath.startsWith('bmad/')) {
|
|
170
|
+
// Relative path starting with bmad/
|
|
171
|
+
itemPath = `{project-root}/${this.bmadFolderName}/${itemPath.slice(5)}`;
|
|
172
|
+
} else if (!itemPath.startsWith('{project-root}')) {
|
|
173
|
+
// For other relative paths, prefix with project root and bmad folder
|
|
174
|
+
itemPath = `{project-root}/${this.bmadFolderName}/${itemPath}`;
|
|
175
|
+
}
|
|
71
176
|
}
|
|
72
177
|
|
|
73
178
|
return `---
|
|
@@ -130,14 +235,10 @@ Follow all instructions in the ${type} file exactly as written.
|
|
|
130
235
|
const tasks = await this.loadTaskManifest(bmadDir);
|
|
131
236
|
const tools = await this.loadToolManifest(bmadDir);
|
|
132
237
|
|
|
133
|
-
// Filter to only standalone items
|
|
134
|
-
const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : [];
|
|
135
|
-
const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : [];
|
|
136
|
-
|
|
137
238
|
let generatedCount = 0;
|
|
138
239
|
|
|
139
240
|
// Generate command files for tasks
|
|
140
|
-
for (const task of
|
|
241
|
+
for (const task of tasks || []) {
|
|
141
242
|
const commandContent = this.generateCommandContent(task, 'task');
|
|
142
243
|
// Use underscore format: bmad_bmm_name.md
|
|
143
244
|
const flatName = toColonName(task.module, 'tasks', task.name);
|
|
@@ -148,7 +249,7 @@ Follow all instructions in the ${type} file exactly as written.
|
|
|
148
249
|
}
|
|
149
250
|
|
|
150
251
|
// Generate command files for tools
|
|
151
|
-
for (const tool of
|
|
252
|
+
for (const tool of tools || []) {
|
|
152
253
|
const commandContent = this.generateCommandContent(tool, 'tool');
|
|
153
254
|
// Use underscore format: bmad_bmm_name.md
|
|
154
255
|
const flatName = toColonName(tool.module, 'tools', tool.name);
|
|
@@ -160,8 +261,8 @@ Follow all instructions in the ${type} file exactly as written.
|
|
|
160
261
|
|
|
161
262
|
return {
|
|
162
263
|
generated: generatedCount,
|
|
163
|
-
tasks:
|
|
164
|
-
tools:
|
|
264
|
+
tasks: (tasks || []).length,
|
|
265
|
+
tools: (tools || []).length,
|
|
165
266
|
};
|
|
166
267
|
}
|
|
167
268
|
|
|
@@ -178,16 +279,12 @@ Follow all instructions in the ${type} file exactly as written.
|
|
|
178
279
|
const tasks = await this.loadTaskManifest(bmadDir);
|
|
179
280
|
const tools = await this.loadToolManifest(bmadDir);
|
|
180
281
|
|
|
181
|
-
// Filter to only standalone items
|
|
182
|
-
const standaloneTasks = tasks ? tasks.filter((t) => t.standalone === 'true' || t.standalone === true) : [];
|
|
183
|
-
const standaloneTools = tools ? tools.filter((t) => t.standalone === 'true' || t.standalone === true) : [];
|
|
184
|
-
|
|
185
282
|
let generatedCount = 0;
|
|
186
283
|
|
|
187
284
|
// Generate command files for tasks
|
|
188
|
-
for (const task of
|
|
285
|
+
for (const task of tasks || []) {
|
|
189
286
|
const commandContent = this.generateCommandContent(task, 'task');
|
|
190
|
-
// Use
|
|
287
|
+
// Use dash format: bmad-bmm-name.md
|
|
191
288
|
const flatName = toDashPath(`${task.module}/tasks/${task.name}.md`);
|
|
192
289
|
const commandPath = path.join(baseCommandsDir, flatName);
|
|
193
290
|
await fs.ensureDir(path.dirname(commandPath));
|
|
@@ -196,9 +293,9 @@ Follow all instructions in the ${type} file exactly as written.
|
|
|
196
293
|
}
|
|
197
294
|
|
|
198
295
|
// Generate command files for tools
|
|
199
|
-
for (const tool of
|
|
296
|
+
for (const tool of tools || []) {
|
|
200
297
|
const commandContent = this.generateCommandContent(tool, 'tool');
|
|
201
|
-
// Use
|
|
298
|
+
// Use dash format: bmad-bmm-name.md
|
|
202
299
|
const flatName = toDashPath(`${tool.module}/tools/${tool.name}.md`);
|
|
203
300
|
const commandPath = path.join(baseCommandsDir, flatName);
|
|
204
301
|
await fs.ensureDir(path.dirname(commandPath));
|
|
@@ -208,8 +305,8 @@ Follow all instructions in the ${type} file exactly as written.
|
|
|
208
305
|
|
|
209
306
|
return {
|
|
210
307
|
generated: generatedCount,
|
|
211
|
-
tasks:
|
|
212
|
-
tools:
|
|
308
|
+
tasks: (tasks || []).length,
|
|
309
|
+
tools: (tools || []).length,
|
|
213
310
|
};
|
|
214
311
|
}
|
|
215
312
|
|
|
@@ -2,13 +2,13 @@ const path = require('node:path');
|
|
|
2
2
|
const fs = require('fs-extra');
|
|
3
3
|
const csv = require('csv-parse/sync');
|
|
4
4
|
const chalk = require('chalk');
|
|
5
|
-
const { toColonPath, toDashPath, customAgentColonName, customAgentDashName } = require('./path-utils');
|
|
5
|
+
const { toColonPath, toDashPath, customAgentColonName, customAgentDashName, BMAD_FOLDER_NAME } = require('./path-utils');
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Generates command files for each workflow in the manifest
|
|
9
9
|
*/
|
|
10
10
|
class WorkflowCommandGenerator {
|
|
11
|
-
constructor(bmadFolderName =
|
|
11
|
+
constructor(bmadFolderName = BMAD_FOLDER_NAME) {
|
|
12
12
|
this.templatePath = path.join(__dirname, '../templates/workflow-command-template.md');
|
|
13
13
|
this.bmadFolderName = bmadFolderName;
|
|
14
14
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
description = "Executes the {{name}} task from the BMAD Method."
|
|
2
|
+
prompt = """
|
|
3
|
+
Execute the BMAD '{{name}}' task.
|
|
4
|
+
|
|
5
|
+
TASK INSTRUCTIONS:
|
|
6
|
+
1. LOAD the task file from {project-root}/{{bmadFolderName}}/{{path}}
|
|
7
|
+
2. READ its entire contents
|
|
8
|
+
3. FOLLOW every instruction precisely as specified
|
|
9
|
+
|
|
10
|
+
TASK FILE: {project-root}/{{bmadFolderName}}/{{path}}
|
|
11
|
+
"""
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
description = "Executes the {{name}} tool from the BMAD Method."
|
|
2
|
+
prompt = """
|
|
3
|
+
Execute the BMAD '{{name}}' tool.
|
|
4
|
+
|
|
5
|
+
TOOL INSTRUCTIONS:
|
|
6
|
+
1. LOAD the tool file from {project-root}/{{bmadFolderName}}/{{path}}
|
|
7
|
+
2. READ its entire contents
|
|
8
|
+
3. FOLLOW every instruction precisely as specified
|
|
9
|
+
|
|
10
|
+
TOOL FILE: {project-root}/{{bmadFolderName}}/{{path}}
|
|
11
|
+
"""
|
|
@@ -7,6 +7,7 @@ const { XmlHandler } = require('../../../lib/xml-handler');
|
|
|
7
7
|
const { getProjectRoot, getSourcePath, getModulePath } = require('../../../lib/project-root');
|
|
8
8
|
const { filterCustomizationData } = require('../../../lib/agent/compiler');
|
|
9
9
|
const { ExternalModuleManager } = require('./external-manager');
|
|
10
|
+
const { BMAD_FOLDER_NAME } = require('../ide/shared/path-utils');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Manages the installation, updating, and removal of BMAD modules.
|
|
@@ -27,7 +28,7 @@ const { ExternalModuleManager } = require('./external-manager');
|
|
|
27
28
|
class ModuleManager {
|
|
28
29
|
constructor(options = {}) {
|
|
29
30
|
this.xmlHandler = new XmlHandler();
|
|
30
|
-
this.bmadFolderName =
|
|
31
|
+
this.bmadFolderName = BMAD_FOLDER_NAME; // Default, can be overridden
|
|
31
32
|
this.customModulePaths = new Map(); // Initialize custom module paths
|
|
32
33
|
this.externalModuleManager = new ExternalModuleManager(); // For external official modules
|
|
33
34
|
}
|
|
@@ -780,10 +781,6 @@ class ModuleManager {
|
|
|
780
781
|
return;
|
|
781
782
|
}
|
|
782
783
|
|
|
783
|
-
// Remove web_bundle section using regex to preserve formatting
|
|
784
|
-
// Match the web_bundle key and all its content (including nested items)
|
|
785
|
-
// This handles both web_bundle: false and web_bundle: {...}
|
|
786
|
-
|
|
787
784
|
// Find the line that starts web_bundle
|
|
788
785
|
const lines = yamlContent.split('\n');
|
|
789
786
|
let startIdx = -1;
|
|
@@ -870,7 +867,7 @@ class ModuleManager {
|
|
|
870
867
|
for (const agentFile of agentFiles) {
|
|
871
868
|
if (!agentFile.endsWith('.agent.yaml')) continue;
|
|
872
869
|
|
|
873
|
-
const relativePath = path.relative(sourceAgentsPath, agentFile);
|
|
870
|
+
const relativePath = path.relative(sourceAgentsPath, agentFile).split(path.sep).join('/');
|
|
874
871
|
const targetDir = path.join(targetAgentsPath, path.dirname(relativePath));
|
|
875
872
|
|
|
876
873
|
await fs.ensureDir(targetDir);
|
|
@@ -42,7 +42,7 @@ function findBmadConfig(startPath = process.cwd()) {
|
|
|
42
42
|
* @returns {string} Resolved path
|
|
43
43
|
*/
|
|
44
44
|
function resolvePath(pathStr, context) {
|
|
45
|
-
return pathStr.replaceAll('{project-root}', context.projectRoot).replaceAll('{bmad-folder}',
|
|
45
|
+
return pathStr.replaceAll('{project-root}', context.projectRoot).replaceAll('{bmad-folder}', context.bmadFolder);
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
/**
|