@hyperdrive.bot/bmad-workflow 1.0.21 → 1.0.23

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 (35) hide show
  1. package/assets/agents/dev-barry.md +69 -0
  2. package/assets/agents/dev.md +323 -0
  3. package/assets/agents/qa.md +92 -0
  4. package/assets/agents/sm-bob.md +65 -0
  5. package/assets/agents/sm.md +296 -0
  6. package/assets/config/default-config.yaml +6 -0
  7. package/assets/templates/epic-tmpl.yaml +277 -0
  8. package/assets/templates/prd-tmpl.yaml +261 -0
  9. package/assets/templates/qa-gate-tmpl.yaml +103 -0
  10. package/assets/templates/story-tmpl.yaml +138 -0
  11. package/dist/commands/eject.d.ts +76 -0
  12. package/dist/commands/eject.js +232 -0
  13. package/dist/commands/init.d.ts +47 -0
  14. package/dist/commands/init.js +265 -0
  15. package/dist/commands/stories/develop.js +1 -0
  16. package/dist/commands/stories/qa.d.ts +1 -0
  17. package/dist/commands/stories/qa.js +7 -0
  18. package/dist/commands/workflow.d.ts +6 -3
  19. package/dist/commands/workflow.js +106 -26
  20. package/dist/models/bmad-config-schema.d.ts +51 -0
  21. package/dist/models/bmad-config-schema.js +53 -0
  22. package/dist/services/agents/gemini-agent-runner.js +7 -2
  23. package/dist/services/agents/opencode-agent-runner.js +7 -2
  24. package/dist/services/file-system/asset-resolver.d.ts +117 -0
  25. package/dist/services/file-system/asset-resolver.js +234 -0
  26. package/dist/services/file-system/file-manager.d.ts +13 -0
  27. package/dist/services/file-system/file-manager.js +32 -0
  28. package/dist/services/file-system/path-resolver.d.ts +22 -1
  29. package/dist/services/file-system/path-resolver.js +36 -9
  30. package/dist/services/orchestration/dependency-graph-executor.js +1 -0
  31. package/dist/services/orchestration/workflow-orchestrator.d.ts +11 -0
  32. package/dist/services/orchestration/workflow-orchestrator.js +79 -10
  33. package/dist/utils/config-merge.d.ts +60 -0
  34. package/dist/utils/config-merge.js +52 -0
  35. package/package.json +4 -2
@@ -46,6 +46,7 @@
46
46
  import { isEpicStory } from '../../models/story.js';
47
47
  import { ParserError, ValidationError } from '../../utils/errors.js';
48
48
  import { runAgentWithRetry } from '../../utils/retry.js';
49
+ import { AssetResolver } from '../file-system/asset-resolver.js';
49
50
  import { PrdFixer } from '../parsers/prd-fixer.js';
50
51
  import { FileScaffolder } from '../scaffolding/file-scaffolder.js';
51
52
  import { BatchProcessor } from './batch-processor.js';
@@ -60,6 +61,7 @@ import { ReviewQueue } from '../review/review-queue.js';
60
61
  */
61
62
  export class WorkflowOrchestrator {
62
63
  agentRunner;
64
+ assetResolver;
63
65
  batchProcessor;
64
66
  callbacks;
65
67
  epicParser;
@@ -80,6 +82,7 @@ export class WorkflowOrchestrator {
80
82
  * @param config - Configuration object containing all service dependencies
81
83
  */
82
84
  constructor(config) {
85
+ this.assetResolver = config.assetResolver ?? new AssetResolver({});
83
86
  this.inputDetector = config.inputDetector;
84
87
  this.prdParser = config.prdParser;
85
88
  this.epicParser = config.epicParser;
@@ -173,7 +176,9 @@ export class WorkflowOrchestrator {
173
176
  const { cwd, outputPath, prdPath, references, smAgent } = options;
174
177
  const referencesText = references.length > 0 ? `\nReferences: ${references.join(', ')}` : '';
175
178
  const cwdText = cwd ? `\n\nWorking directory: ${cwd}` : '';
176
- const agentRef = smAgent ? `@${smAgent}` : `@.bmad-core/agents/sm.md`;
179
+ const resolved = this.assetResolver.resolveAgent('sm', smAgent);
180
+ const agentRef = `@${resolved.path}`;
181
+ const templateResolved = this.assetResolver.resolveTemplate('epic-tmpl.yaml');
177
182
  return `${agentRef}${cwdText}
178
183
 
179
184
  Create epic '${epic.number}: ${epic.title}' for PRD '${prdPath}'.${referencesText}.
@@ -182,7 +187,7 @@ IMPORTANT: The file at '${outputPath}' has been pre-scaffolded with structure an
182
187
  - DO NOT modify the Epic Header section (Epic ID, Status: Draft, Created date are already set)
183
188
  - DO NOT change the document structure or section headers
184
189
  - ONLY populate the empty content sections marked with [AI Agent will populate]
185
- - Follow the template structure at @.bmad-core/templates/epic-tmpl.yaml for content guidance
190
+ - Follow the template structure at @${templateResolved.path} for content guidance
186
191
 
187
192
  Write output to: '${outputPath}'`;
188
193
  }
@@ -220,7 +225,9 @@ Write output to: '${outputPath}'`;
220
225
  const { cwd, epicPath, outputPath, references, smAgent } = options;
221
226
  const referencesText = references.length > 0 ? `\nReferences: ${references.join(', ')}` : '';
222
227
  const cwdText = cwd ? `\n\nWorking directory: ${cwd}` : '';
223
- const agentRef = smAgent ? `@${smAgent}` : `@.bmad-core/agents/sm.md`;
228
+ const resolved = this.assetResolver.resolveAgent('sm', smAgent);
229
+ const agentRef = `@${resolved.path}`;
230
+ const templateResolved = this.assetResolver.resolveTemplate('story-tmpl.yaml');
224
231
  return `${agentRef}${cwdText}
225
232
 
226
233
  Create story '${story.fullNumber}: ${story.title}' for epic '${epicPath}'. ${referencesText}
@@ -230,7 +237,7 @@ IMPORTANT: The file at '${outputPath}' has been pre-scaffolded with structure an
230
237
  - DO NOT modify the Created date in Change Log
231
238
  - DO NOT change the document structure or section headers
232
239
  - ONLY populate the empty content sections marked with [AI Agent will populate]
233
- - Follow the template structure at @.bmad-core/templates/story-tmpl.yaml for content guidance
240
+ - Follow the template structure at @${templateResolved.path} for content guidance
234
241
 
235
242
  Write output to: ${outputPath}`;
236
243
  }
@@ -626,14 +633,17 @@ Write output to: ${outputPath}`;
626
633
  // Build prompt with auto-detected references
627
634
  const mcpPrefix = await this.getMcpPromptPrefix('dev', 'dev', config);
628
635
  let prompt = mcpPrefix ? `${mcpPrefix}\n\n` : '';
629
- prompt += config.devAgent ? `@${config.devAgent}\n\n` : `@.bmad-core/agents/dev.md\n\n`;
636
+ const devResolved = this.assetResolver.resolveAgent('dev', config.devAgent);
637
+ prompt += `@${devResolved.path}\n\n`;
630
638
  // Add working directory instruction if specified
631
639
  if (config.cwd) {
632
640
  prompt += `Working directory: ${config.cwd}\n\n`;
633
641
  }
634
642
  prompt += `*develop-story ${storyFilePath}\n\n`;
635
- // Combine auto-detected references with user-provided references
636
- const allReferences = [...autoReferences, ...(config.references || [])];
643
+ // Parse Dev Context section for sidecars and deploy target
644
+ const devContext = this.parseDevContext(storyContent);
645
+ // Combine auto-detected references with user-provided references and sidecars
646
+ const allReferences = [...autoReferences, ...(config.references || []), ...devContext.sidecarRefs];
637
647
  if (allReferences.length > 0) {
638
648
  prompt += 'References:\n';
639
649
  for (const ref of allReferences) {
@@ -641,6 +651,10 @@ Write output to: ${outputPath}`;
641
651
  }
642
652
  prompt += '\n';
643
653
  }
654
+ // Add deploy target context if specified in story
655
+ if (devContext.deployTarget) {
656
+ prompt += `Deploy target: ${devContext.deployTarget}\n\n`;
657
+ }
644
658
  prompt += '*yolo mode*\n';
645
659
  // Generate unique spawn ID
646
660
  const spawnId = `dev-${story.fullNumber}-${storyStartTime}`;
@@ -659,6 +673,7 @@ Write output to: ${outputPath}`;
659
673
  // Execute dev agent with retry on timeout/killed
660
674
  const result = await runAgentWithRetry(this.agentRunner, prompt, {
661
675
  agentType: 'dev',
676
+ cwd: config.cwd,
662
677
  references: config.references,
663
678
  timeout: config.timeout ?? 2_700_000,
664
679
  }, {
@@ -901,14 +916,17 @@ Write output to: ${outputPath}`;
901
916
  // Build prompt with auto-detected references
902
917
  const mcpPrefix = await this.getMcpPromptPrefix('dev', 'dev', config);
903
918
  let prompt = mcpPrefix ? `${mcpPrefix}\n\n` : '';
904
- prompt += config.devAgent ? `@${config.devAgent}\n\n` : `@.bmad-core/agents/dev.md\n\n`;
919
+ const devResolved = this.assetResolver.resolveAgent('dev', config.devAgent);
920
+ prompt += `@${devResolved.path}\n\n`;
905
921
  // Add working directory instruction if specified
906
922
  if (config.cwd) {
907
923
  prompt += `Working directory: ${config.cwd}\n\n`;
908
924
  }
909
925
  prompt += `Implement story: ${storyFilePath}\n\n`;
910
- // Combine auto-detected references with user-provided references
911
- const allReferences = [...autoReferences, ...(config.references || [])];
926
+ // Parse Dev Context section for sidecars and deploy target
927
+ const devContext = this.parseDevContext(storyContent);
928
+ // Combine auto-detected references with user-provided references and sidecars
929
+ const allReferences = [...autoReferences, ...(config.references || []), ...devContext.sidecarRefs];
912
930
  if (allReferences.length > 0) {
913
931
  prompt += 'References:\n';
914
932
  for (const ref of allReferences) {
@@ -916,6 +934,10 @@ Write output to: ${outputPath}`;
916
934
  }
917
935
  prompt += '\n';
918
936
  }
937
+ // Add deploy target context if specified in story
938
+ if (devContext.deployTarget) {
939
+ prompt += `Deploy target: ${devContext.deployTarget}\n\n`;
940
+ }
919
941
  prompt += '*yolo mode*\n';
920
942
  // Generate unique spawn ID and timestamp
921
943
  const spawnStartTime = Date.now();
@@ -933,6 +955,7 @@ Write output to: ${outputPath}`;
933
955
  });
934
956
  const result = await runAgentWithRetry(this.agentRunner, prompt, {
935
957
  agentType: 'dev',
958
+ cwd: config.cwd,
936
959
  references: config.references,
937
960
  timeout: config.timeout ?? 2_700_000,
938
961
  }, {
@@ -1258,6 +1281,7 @@ Write output to: ${outputPath}`;
1258
1281
  // Step 3: Run Claude agent to populate content sections
1259
1282
  const result = await runAgentWithRetry(this.agentRunner, prompt, {
1260
1283
  agentType: 'architect',
1284
+ cwd: config.cwd,
1261
1285
  references: config.references,
1262
1286
  timeout: config.timeout ?? 2_700_000,
1263
1287
  }, {
@@ -2250,6 +2274,7 @@ Write output to: ${outputPath}`;
2250
2274
  // Step 4: Run Claude agent to populate content sections
2251
2275
  const result = await runAgentWithRetry(this.agentRunner, prompt, {
2252
2276
  agentType: 'sm',
2277
+ cwd: config.cwd,
2253
2278
  references: config.references,
2254
2279
  timeout: config.timeout ?? 2_700_000,
2255
2280
  }, {
@@ -2576,6 +2601,7 @@ Write output to: ${outputPath}`;
2576
2601
  // Step 4: Run Claude agent to populate content sections
2577
2602
  const result = await runAgentWithRetry(this.agentRunner, prompt, {
2578
2603
  agentType: 'sm',
2604
+ cwd: config.cwd,
2579
2605
  references: config.references,
2580
2606
  timeout: config.timeout ?? 2_700_000,
2581
2607
  }, {
@@ -3085,6 +3111,49 @@ Write output to: ${outputPath}`;
3085
3111
  async sleep(ms) {
3086
3112
  await new Promise((resolve) => setTimeout(resolve, ms));
3087
3113
  }
3114
+ /**
3115
+ * Parse the `### Dev Context` section from a story file's content.
3116
+ *
3117
+ * Returns sidecar reference paths and an optional deploy target string.
3118
+ * If the section is missing or empty, returns empty arrays / undefined (backward compatible).
3119
+ */
3120
+ parseDevContext(storyContent) {
3121
+ const sidecarRefs = [];
3122
+ let deployTarget;
3123
+ // Find the ### Dev Context section
3124
+ const devCtxMatch = storyContent.match(/^### Dev Context\s*$/m);
3125
+ if (!devCtxMatch || devCtxMatch.index === undefined) {
3126
+ return { deployTarget, sidecarRefs };
3127
+ }
3128
+ // Extract section content up to the next heading or end of file
3129
+ const sectionStart = devCtxMatch.index + devCtxMatch[0].length;
3130
+ const nextHeadingMatch = storyContent.slice(sectionStart).match(/^#{1,3} /m);
3131
+ const sectionEnd = nextHeadingMatch?.index !== undefined ? sectionStart + nextHeadingMatch.index : storyContent.length;
3132
+ const sectionContent = storyContent.slice(sectionStart, sectionEnd);
3133
+ // Parse Sidecars line — supports "- **Sidecars:**" or "- Sidecars:" (flexible)
3134
+ const sidecarsMatch = sectionContent.match(/^-\s+\*{0,2}Sidecars:?\*{0,2}\s*(.+)$/im);
3135
+ if (sidecarsMatch?.[1]) {
3136
+ const raw = sidecarsMatch[1].trim();
3137
+ for (const part of raw.split(',')) {
3138
+ const name = part.trim();
3139
+ if (name.length === 0)
3140
+ continue;
3141
+ // If already a path, use as-is; otherwise prefix with _bmad/bmm/agents/
3142
+ if (name.includes('/')) {
3143
+ sidecarRefs.push(name);
3144
+ }
3145
+ else {
3146
+ sidecarRefs.push(`_bmad/bmm/agents/${name}`);
3147
+ }
3148
+ }
3149
+ }
3150
+ // Parse Deploy target line
3151
+ const deployMatch = sectionContent.match(/^-\s+\*{0,2}Deploy target:?\*{0,2}\s*(.+)$/im);
3152
+ if (deployMatch?.[1]) {
3153
+ deployTarget = deployMatch[1].trim();
3154
+ }
3155
+ return { deployTarget, sidecarRefs };
3156
+ }
3088
3157
  /**
3089
3158
  * Update story status in file
3090
3159
  *
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Config Merge Utility
3
+ *
4
+ * Implements three-layer merge for execution config:
5
+ * CLI flags (highest) → .bmad-workflow.yaml → hardcoded defaults (lowest)
6
+ *
7
+ * Pure function — no side effects, easy to test.
8
+ */
9
+ import type { ConfigExecutionDefaults } from '../services/file-system/asset-resolver.js';
10
+ /**
11
+ * Merged execution config with all fields resolved
12
+ */
13
+ export interface MergedExecutionConfig {
14
+ epicInterval: number;
15
+ maxRetries: number;
16
+ model: string | undefined;
17
+ parallel: number;
18
+ pipeline: boolean;
19
+ prdInterval: number;
20
+ provider: 'claude' | 'gemini' | 'opencode';
21
+ qa: boolean;
22
+ retryBackoffMs: number;
23
+ storyInterval: number;
24
+ timeout: number;
25
+ }
26
+ /**
27
+ * CLI flags that may be undefined when user didn't pass them.
28
+ * Only fields explicitly passed by the user should be present.
29
+ */
30
+ export interface CliExecutionFlags {
31
+ epicInterval?: number;
32
+ maxRetries?: number;
33
+ model?: string;
34
+ parallel?: number;
35
+ pipeline?: boolean;
36
+ prdInterval?: number;
37
+ provider?: string;
38
+ qa?: boolean;
39
+ retryBackoffMs?: number;
40
+ storyInterval?: number;
41
+ timeout?: number;
42
+ }
43
+ /**
44
+ * Hardcoded defaults — final fallback when neither CLI nor config provides a value
45
+ */
46
+ export declare const EXECUTION_DEFAULTS: MergedExecutionConfig;
47
+ /**
48
+ * Merge execution config from three layers: CLI flags → config file → hardcoded defaults.
49
+ *
50
+ * For each field, the first defined value wins (left to right):
51
+ * 1. CLI flag (explicit user input)
52
+ * 2. Config file value (from .bmad-workflow.yaml)
53
+ * 3. Hardcoded default
54
+ *
55
+ * @param cliFlags - Flags explicitly passed via CLI (undefined = not passed)
56
+ * @param configFile - Execution defaults from .bmad-workflow.yaml
57
+ * @param defaults - Hardcoded fallback values
58
+ * @returns Fully resolved execution config
59
+ */
60
+ export declare function mergeExecutionConfig(cliFlags: CliExecutionFlags, configFile: ConfigExecutionDefaults, defaults?: MergedExecutionConfig): MergedExecutionConfig;
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Config Merge Utility
3
+ *
4
+ * Implements three-layer merge for execution config:
5
+ * CLI flags (highest) → .bmad-workflow.yaml → hardcoded defaults (lowest)
6
+ *
7
+ * Pure function — no side effects, easy to test.
8
+ */
9
+ /**
10
+ * Hardcoded defaults — final fallback when neither CLI nor config provides a value
11
+ */
12
+ export const EXECUTION_DEFAULTS = {
13
+ epicInterval: 60,
14
+ maxRetries: 0,
15
+ model: undefined,
16
+ parallel: 3,
17
+ pipeline: true,
18
+ prdInterval: 60,
19
+ provider: 'claude',
20
+ qa: false,
21
+ retryBackoffMs: 5000,
22
+ storyInterval: 60,
23
+ timeout: 2_700_000,
24
+ };
25
+ /**
26
+ * Merge execution config from three layers: CLI flags → config file → hardcoded defaults.
27
+ *
28
+ * For each field, the first defined value wins (left to right):
29
+ * 1. CLI flag (explicit user input)
30
+ * 2. Config file value (from .bmad-workflow.yaml)
31
+ * 3. Hardcoded default
32
+ *
33
+ * @param cliFlags - Flags explicitly passed via CLI (undefined = not passed)
34
+ * @param configFile - Execution defaults from .bmad-workflow.yaml
35
+ * @param defaults - Hardcoded fallback values
36
+ * @returns Fully resolved execution config
37
+ */
38
+ export function mergeExecutionConfig(cliFlags, configFile, defaults = EXECUTION_DEFAULTS) {
39
+ return {
40
+ epicInterval: cliFlags.epicInterval ?? defaults.epicInterval,
41
+ maxRetries: cliFlags.maxRetries ?? defaults.maxRetries,
42
+ model: cliFlags.model ?? configFile.model ?? defaults.model,
43
+ parallel: cliFlags.parallel ?? configFile.parallel ?? defaults.parallel,
44
+ pipeline: cliFlags.pipeline ?? configFile.pipeline ?? defaults.pipeline,
45
+ prdInterval: cliFlags.prdInterval ?? defaults.prdInterval,
46
+ provider: cliFlags.provider ?? configFile.provider ?? defaults.provider,
47
+ qa: cliFlags.qa ?? configFile.qa_enabled ?? defaults.qa,
48
+ retryBackoffMs: cliFlags.retryBackoffMs ?? defaults.retryBackoffMs,
49
+ storyInterval: cliFlags.storyInterval ?? defaults.storyInterval,
50
+ timeout: cliFlags.timeout ?? configFile.timeout ?? defaults.timeout,
51
+ };
52
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@hyperdrive.bot/bmad-workflow",
3
3
  "description": "AI-driven development workflow orchestration CLI for BMAD projects",
4
- "version": "1.0.21",
4
+ "version": "1.0.23",
5
5
  "author": {
6
6
  "name": "DevSquad",
7
7
  "email": "marcelo@devsquad.email",
@@ -15,6 +15,7 @@
15
15
  },
16
16
  "dependencies": {
17
17
  "@hyperdrive.bot/plugin-telemetry": "file:../telemetry-plugin",
18
+ "@inquirer/prompts": "^8.3.2",
18
19
  "@oclif/core": "^4",
19
20
  "@oclif/plugin-help": "^6",
20
21
  "bcrypt": "^6.0.0",
@@ -71,7 +72,8 @@
71
72
  "dist/",
72
73
  "oclif.manifest.json",
73
74
  "README.md",
74
- "LICENSE"
75
+ "LICENSE",
76
+ "assets/"
75
77
  ],
76
78
  "homepage": "https://gitlab.com/dev_squad/repo/cli/bmad-orchestrator#readme",
77
79
  "keywords": [