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.
Files changed (119) hide show
  1. package/.claude/skills/changelog-social/SKILL.md +178 -0
  2. package/.claude/skills/changelog-social/examples/discord-example.md +53 -0
  3. package/.claude/skills/changelog-social/examples/linkedin-example.md +49 -0
  4. package/.claude/skills/changelog-social/examples/twitter-example.md +55 -0
  5. package/.claude/skills/changelog-social.skill +0 -0
  6. package/.claude/skills/draft-changelog/SKILL.md +2 -2
  7. package/.claude/skills/draft-changelog/prompts/instructions.md +25 -1
  8. package/.claude/skills/gh-triage/SKILL.md +1 -1
  9. package/.claude/skills/release-module/SKILL.md +1 -1
  10. package/.claude/skills/release-module/prompts/instructions.md +0 -4
  11. package/.github/workflows/quality.yaml +3 -0
  12. package/.vscode/settings.json +1 -2
  13. package/CHANGELOG.md +65 -1
  14. package/docs/bmgd/game-types.md +1 -0
  15. package/docs/bmgd/index.md +1 -1
  16. package/docs/bmgd/quick-flow-workflows.md +1 -0
  17. package/docs/reference/agents.md +22 -0
  18. package/docs/reference/commands.md +34 -0
  19. package/docs/reference/testing.md +21 -0
  20. package/docs/reference/workflow-map.md +1 -1
  21. package/package.json +5 -2
  22. package/src/bmm/agents/analyst.agent.yaml +11 -5
  23. package/src/bmm/agents/dev.agent.yaml +0 -1
  24. package/src/bmm/agents/pm.agent.yaml +3 -6
  25. package/src/bmm/agents/{quinn.agent.yaml → qa.agent.yaml} +3 -3
  26. package/src/bmm/module-help.csv +13 -20
  27. package/src/bmm/workflows/1-analysis/create-product-brief/workflow.md +0 -1
  28. package/src/bmm/workflows/1-analysis/research/workflow-domain-research.md +54 -0
  29. package/src/bmm/workflows/1-analysis/research/workflow-market-research.md +54 -0
  30. package/src/bmm/workflows/1-analysis/research/workflow-technical-research.md +54 -0
  31. package/src/bmm/workflows/2-plan-workflows/create-prd/data/domain-complexity.csv +2 -0
  32. package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-create-prd.md +63 -0
  33. package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-edit-prd.md +65 -0
  34. package/src/bmm/workflows/2-plan-workflows/create-prd/workflow-validate-prd.md +65 -0
  35. package/src/bmm/workflows/2-plan-workflows/create-ux-design/workflow.md +0 -1
  36. package/src/bmm/workflows/3-solutioning/check-implementation-readiness/workflow.md +0 -1
  37. package/src/bmm/workflows/3-solutioning/create-architecture/data/domain-complexity.csv +2 -0
  38. package/src/bmm/workflows/3-solutioning/create-architecture/workflow.md +0 -1
  39. package/src/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md +0 -1
  40. package/src/bmm/workflows/4-implementation/code-review/workflow.yaml +0 -3
  41. package/src/bmm/workflows/4-implementation/correct-course/workflow.yaml +0 -4
  42. package/src/bmm/workflows/4-implementation/create-story/workflow.yaml +0 -4
  43. package/src/bmm/workflows/4-implementation/dev-story/workflow.yaml +0 -4
  44. package/src/bmm/workflows/4-implementation/retrospective/workflow.yaml +0 -3
  45. package/src/bmm/workflows/4-implementation/sprint-planning/workflow.yaml +0 -4
  46. package/src/bmm/workflows/4-implementation/sprint-status/workflow.yaml +0 -6
  47. package/src/bmm/workflows/bmad-quick-flow/quick-spec/workflow.md +0 -1
  48. package/src/bmm/workflows/document-project/workflow.yaml +0 -8
  49. package/src/bmm/workflows/qa/automate/workflow.yaml +0 -2
  50. package/src/core/tasks/editorial-review-prose.xml +14 -12
  51. package/src/core/tasks/editorial-review-structure.xml +28 -28
  52. package/src/core/tasks/help.md +36 -16
  53. package/src/core/tasks/index-docs.xml +1 -1
  54. package/src/core/tasks/review-adversarial-general.xml +2 -2
  55. package/src/core/tasks/shard-doc.xml +1 -2
  56. package/src/core/tasks/workflow.xml +1 -1
  57. package/src/core/workflows/advanced-elicitation/workflow.xml +1 -1
  58. package/test/test-installation-components.js +1 -1
  59. package/tools/build-docs.js +21 -1
  60. package/tools/cli/bmad-cli.js +42 -3
  61. package/tools/cli/installers/lib/core/config-collector.js +5 -1
  62. package/tools/cli/installers/lib/core/dependency-resolver.js +1 -1
  63. package/tools/cli/installers/lib/core/installer.js +1 -6
  64. package/tools/cli/installers/lib/core/manifest-generator.js +149 -125
  65. package/tools/cli/installers/lib/custom/handler.js +1 -1
  66. package/tools/cli/installers/lib/ide/_base-ide.js +25 -15
  67. package/tools/cli/installers/lib/ide/_config-driven.js +79 -9
  68. package/tools/cli/installers/lib/ide/codex.js +7 -1
  69. package/tools/cli/installers/lib/ide/manager.js +8 -2
  70. package/tools/cli/installers/lib/ide/platform-codes.yaml +0 -3
  71. package/tools/cli/installers/lib/ide/shared/agent-command-generator.js +2 -2
  72. package/tools/cli/installers/lib/ide/shared/bmad-artifacts.js +14 -3
  73. package/tools/cli/installers/lib/ide/shared/path-utils.js +9 -2
  74. package/tools/cli/installers/lib/ide/shared/task-tool-command-generator.js +126 -29
  75. package/tools/cli/installers/lib/ide/shared/workflow-command-generator.js +2 -2
  76. package/tools/cli/installers/lib/ide/templates/combined/default-task.md +10 -0
  77. package/tools/cli/installers/lib/ide/templates/combined/default-tool.md +10 -0
  78. package/tools/cli/installers/lib/ide/templates/combined/gemini-task.toml +11 -0
  79. package/tools/cli/installers/lib/ide/templates/combined/gemini-tool.toml +11 -0
  80. package/tools/cli/installers/lib/modules/manager.js +3 -6
  81. package/tools/cli/lib/agent/installer.js +1 -1
  82. package/tools/cli/lib/prompts.js +192 -6
  83. package/tools/cli/lib/ui.js +124 -72
  84. package/tools/schema/agent.js +0 -1
  85. package/tools/validate-file-refs.js +480 -0
  86. package/website/astro.config.mjs +1 -35
  87. package/src/bmm/workflows/1-analysis/research/workflow.md +0 -173
  88. package/src/bmm/workflows/2-plan-workflows/create-prd/validation-report-prd-workflow.md +0 -433
  89. package/src/bmm/workflows/2-plan-workflows/create-prd/workflow.md +0 -150
  90. package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-library.json +0 -90
  91. package/src/bmm/workflows/excalidraw-diagrams/_shared/excalidraw-templates.yaml +0 -127
  92. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/checklist.md +0 -39
  93. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/instructions.md +0 -130
  94. package/src/bmm/workflows/excalidraw-diagrams/create-dataflow/workflow.yaml +0 -27
  95. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/checklist.md +0 -43
  96. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/instructions.md +0 -141
  97. package/src/bmm/workflows/excalidraw-diagrams/create-diagram/workflow.yaml +0 -27
  98. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/checklist.md +0 -49
  99. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/instructions.md +0 -241
  100. package/src/bmm/workflows/excalidraw-diagrams/create-flowchart/workflow.yaml +0 -27
  101. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/checklist.md +0 -38
  102. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/instructions.md +0 -133
  103. package/src/bmm/workflows/excalidraw-diagrams/create-wireframe/workflow.yaml +0 -27
  104. package/src/core/resources/excalidraw/README.md +0 -160
  105. package/src/core/resources/excalidraw/excalidraw-helpers.md +0 -127
  106. package/src/core/resources/excalidraw/library-loader.md +0 -50
  107. package/src/core/resources/excalidraw/validate-json-instructions.md +0 -79
  108. package/tools/flattener/aggregate.js +0 -76
  109. package/tools/flattener/binary.js +0 -80
  110. package/tools/flattener/discovery.js +0 -71
  111. package/tools/flattener/files.js +0 -35
  112. package/tools/flattener/ignoreRules.js +0 -172
  113. package/tools/flattener/main.js +0 -483
  114. package/tools/flattener/projectRoot.js +0 -201
  115. package/tools/flattener/prompts.js +0 -44
  116. package/tools/flattener/stats.helpers.js +0 -368
  117. package/tools/flattener/stats.js +0 -75
  118. package/tools/flattener/test-matrix.js +0 -409
  119. 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 taskToolResult = await taskToolGen.generateDashTaskToolCommands(projectDir, bmadDir, targetPath);
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
- if (artifact.type === 'agent-launcher') {
320
- pathToUse = artifact.agentPath || artifact.relativePath || '';
321
- } else if (artifact.type === 'workflow-command') {
322
- pathToUse = artifact.workflowPath || artifact.relativePath || '';
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
- const baseName = standardName.replace(/\.(yaml|yml)\.md$/, '.md');
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 = 'bmad'; // Default, can be overridden
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(this.bmadFolderName);
107
+ if (typeof handler.setBmadFolderName === 'function') {
108
+ handler.setBmadFolderName(this.bmadFolderName);
109
+ }
104
110
  this.handlers.set(platformCode, handler);
105
111
  }
106
112
  }
@@ -94,9 +94,6 @@ platforms:
94
94
  - target_dir: .github/agents
95
95
  template_type: copilot_agents
96
96
  artifact_types: [agents]
97
- - target_dir: .vscode
98
- template_type: vscode_settings
99
- artifact_types: []
100
97
 
101
98
  iflow:
102
99
  name: "iFlow"
@@ -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 = 'bmad') {
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
- if (!file.endsWith('.md')) {
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: path.join(dirPath, file),
150
- name: file.replace('.md', ''),
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
- const withoutExt = relativePath.replace('.md', '');
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
- const withoutExt = relativePath.replace('.md', '');
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 standaloneTasks) {
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 standaloneTools) {
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: standaloneTasks.length,
57
- tools: standaloneTools.length,
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 && typeof itemPath === 'string' && itemPath.startsWith('bmad/')) {
70
- itemPath = `{project-root}/${itemPath}`;
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 standaloneTasks) {
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 standaloneTools) {
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: standaloneTasks.length,
164
- tools: standaloneTools.length,
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 standaloneTasks) {
285
+ for (const task of tasks || []) {
189
286
  const commandContent = this.generateCommandContent(task, 'task');
190
- // Use underscore format: bmad_bmm_name.md
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 standaloneTools) {
296
+ for (const tool of tools || []) {
200
297
  const commandContent = this.generateCommandContent(tool, 'tool');
201
- // Use underscore format: bmad_bmm_name.md
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: standaloneTasks.length,
212
- tools: standaloneTools.length,
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 = 'bmad') {
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,10 @@
1
+ ---
2
+ name: '{{name}}'
3
+ description: '{{description}}'
4
+ ---
5
+
6
+ # {{name}}
7
+
8
+ Read the entire task file at: {project-root}/{{bmadFolderName}}/{{path}}
9
+
10
+ Follow all instructions in the task file exactly as written.
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: '{{name}}'
3
+ description: '{{description}}'
4
+ ---
5
+
6
+ # {{name}}
7
+
8
+ Read the entire tool file at: {project-root}/{{bmadFolderName}}/{{path}}
9
+
10
+ Follow all instructions in the tool file exactly as written.
@@ -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 = 'bmad'; // Default, can be overridden
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}', context_bmadFolder);
45
+ return pathStr.replaceAll('{project-root}', context.projectRoot).replaceAll('{bmad-folder}', context.bmadFolder);
46
46
  }
47
47
 
48
48
  /**