@hyperdrive.bot/bmad-workflow 1.0.2

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 (129) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +1017 -0
  3. package/bin/dev +5 -0
  4. package/bin/dev.cmd +3 -0
  5. package/bin/dev.js +5 -0
  6. package/bin/run +5 -0
  7. package/bin/run.cmd +3 -0
  8. package/bin/run.js +5 -0
  9. package/dist/commands/config/show.d.ts +34 -0
  10. package/dist/commands/config/show.js +108 -0
  11. package/dist/commands/config/validate.d.ts +29 -0
  12. package/dist/commands/config/validate.js +131 -0
  13. package/dist/commands/decompose.d.ts +79 -0
  14. package/dist/commands/decompose.js +327 -0
  15. package/dist/commands/demo.d.ts +18 -0
  16. package/dist/commands/demo.js +107 -0
  17. package/dist/commands/epics/create.d.ts +123 -0
  18. package/dist/commands/epics/create.js +459 -0
  19. package/dist/commands/epics/list.d.ts +120 -0
  20. package/dist/commands/epics/list.js +280 -0
  21. package/dist/commands/hello/index.d.ts +12 -0
  22. package/dist/commands/hello/index.js +34 -0
  23. package/dist/commands/hello/world.d.ts +8 -0
  24. package/dist/commands/hello/world.js +24 -0
  25. package/dist/commands/prd/fix.d.ts +39 -0
  26. package/dist/commands/prd/fix.js +140 -0
  27. package/dist/commands/prd/validate.d.ts +112 -0
  28. package/dist/commands/prd/validate.js +302 -0
  29. package/dist/commands/stories/create.d.ts +95 -0
  30. package/dist/commands/stories/create.js +431 -0
  31. package/dist/commands/stories/develop.d.ts +91 -0
  32. package/dist/commands/stories/develop.js +460 -0
  33. package/dist/commands/stories/list.d.ts +84 -0
  34. package/dist/commands/stories/list.js +291 -0
  35. package/dist/commands/stories/move.d.ts +66 -0
  36. package/dist/commands/stories/move.js +273 -0
  37. package/dist/commands/stories/qa.d.ts +99 -0
  38. package/dist/commands/stories/qa.js +530 -0
  39. package/dist/commands/workflow.d.ts +97 -0
  40. package/dist/commands/workflow.js +390 -0
  41. package/dist/index.d.ts +1 -0
  42. package/dist/index.js +1 -0
  43. package/dist/models/agent-options.d.ts +50 -0
  44. package/dist/models/agent-options.js +1 -0
  45. package/dist/models/agent-result.d.ts +29 -0
  46. package/dist/models/agent-result.js +1 -0
  47. package/dist/models/index.d.ts +10 -0
  48. package/dist/models/index.js +10 -0
  49. package/dist/models/phase-result.d.ts +65 -0
  50. package/dist/models/phase-result.js +7 -0
  51. package/dist/models/provider.d.ts +28 -0
  52. package/dist/models/provider.js +18 -0
  53. package/dist/models/story.d.ts +154 -0
  54. package/dist/models/story.js +18 -0
  55. package/dist/models/workflow-config.d.ts +148 -0
  56. package/dist/models/workflow-config.js +1 -0
  57. package/dist/models/workflow-result.d.ts +164 -0
  58. package/dist/models/workflow-result.js +7 -0
  59. package/dist/services/agents/agent-runner-factory.d.ts +31 -0
  60. package/dist/services/agents/agent-runner-factory.js +44 -0
  61. package/dist/services/agents/agent-runner.d.ts +46 -0
  62. package/dist/services/agents/agent-runner.js +29 -0
  63. package/dist/services/agents/claude-agent-runner.d.ts +81 -0
  64. package/dist/services/agents/claude-agent-runner.js +332 -0
  65. package/dist/services/agents/gemini-agent-runner.d.ts +82 -0
  66. package/dist/services/agents/gemini-agent-runner.js +350 -0
  67. package/dist/services/agents/index.d.ts +7 -0
  68. package/dist/services/agents/index.js +7 -0
  69. package/dist/services/file-system/file-manager.d.ts +110 -0
  70. package/dist/services/file-system/file-manager.js +223 -0
  71. package/dist/services/file-system/glob-matcher.d.ts +75 -0
  72. package/dist/services/file-system/glob-matcher.js +126 -0
  73. package/dist/services/file-system/path-resolver.d.ts +183 -0
  74. package/dist/services/file-system/path-resolver.js +400 -0
  75. package/dist/services/logging/workflow-logger.d.ts +232 -0
  76. package/dist/services/logging/workflow-logger.js +552 -0
  77. package/dist/services/orchestration/batch-processor.d.ts +113 -0
  78. package/dist/services/orchestration/batch-processor.js +187 -0
  79. package/dist/services/orchestration/dependency-graph-executor.d.ts +60 -0
  80. package/dist/services/orchestration/dependency-graph-executor.js +447 -0
  81. package/dist/services/orchestration/index.d.ts +10 -0
  82. package/dist/services/orchestration/index.js +8 -0
  83. package/dist/services/orchestration/input-detector.d.ts +125 -0
  84. package/dist/services/orchestration/input-detector.js +381 -0
  85. package/dist/services/orchestration/story-queue.d.ts +94 -0
  86. package/dist/services/orchestration/story-queue.js +170 -0
  87. package/dist/services/orchestration/story-type-detector.d.ts +80 -0
  88. package/dist/services/orchestration/story-type-detector.js +258 -0
  89. package/dist/services/orchestration/task-decomposition-service.d.ts +67 -0
  90. package/dist/services/orchestration/task-decomposition-service.js +607 -0
  91. package/dist/services/orchestration/workflow-orchestrator.d.ts +659 -0
  92. package/dist/services/orchestration/workflow-orchestrator.js +2201 -0
  93. package/dist/services/parsers/epic-parser.d.ts +117 -0
  94. package/dist/services/parsers/epic-parser.js +264 -0
  95. package/dist/services/parsers/prd-fixer.d.ts +86 -0
  96. package/dist/services/parsers/prd-fixer.js +194 -0
  97. package/dist/services/parsers/prd-parser.d.ts +123 -0
  98. package/dist/services/parsers/prd-parser.js +286 -0
  99. package/dist/services/parsers/standalone-story-parser.d.ts +114 -0
  100. package/dist/services/parsers/standalone-story-parser.js +255 -0
  101. package/dist/services/parsers/story-parser-factory.d.ts +81 -0
  102. package/dist/services/parsers/story-parser-factory.js +108 -0
  103. package/dist/services/parsers/story-parser.d.ts +122 -0
  104. package/dist/services/parsers/story-parser.js +262 -0
  105. package/dist/services/scaffolding/decompose-session-scaffolder.d.ts +74 -0
  106. package/dist/services/scaffolding/decompose-session-scaffolder.js +315 -0
  107. package/dist/services/scaffolding/file-scaffolder.d.ts +94 -0
  108. package/dist/services/scaffolding/file-scaffolder.js +314 -0
  109. package/dist/services/validation/config-validator.d.ts +88 -0
  110. package/dist/services/validation/config-validator.js +167 -0
  111. package/dist/types/task-graph.d.ts +142 -0
  112. package/dist/types/task-graph.js +5 -0
  113. package/dist/utils/colors.d.ts +49 -0
  114. package/dist/utils/colors.js +50 -0
  115. package/dist/utils/error-formatter.d.ts +64 -0
  116. package/dist/utils/error-formatter.js +279 -0
  117. package/dist/utils/errors.d.ts +170 -0
  118. package/dist/utils/errors.js +233 -0
  119. package/dist/utils/formatters.d.ts +84 -0
  120. package/dist/utils/formatters.js +162 -0
  121. package/dist/utils/logger.d.ts +63 -0
  122. package/dist/utils/logger.js +78 -0
  123. package/dist/utils/progress.d.ts +104 -0
  124. package/dist/utils/progress.js +161 -0
  125. package/dist/utils/retry.d.ts +114 -0
  126. package/dist/utils/retry.js +160 -0
  127. package/dist/utils/shared-flags.d.ts +28 -0
  128. package/dist/utils/shared-flags.js +43 -0
  129. package/package.json +119 -0
@@ -0,0 +1,431 @@
1
+ /**
2
+ * Stories Create Command
3
+ *
4
+ * Automatically generates story markdown files from an epic using parallel Claude AI agent execution.
5
+ * Creates story files with configurable concurrency and batch processing.
6
+ *
7
+ * @example
8
+ * ```bash
9
+ * bmad-workflow stories create docs/epics/epic-1-foundation.md
10
+ * bmad-workflow stories create epic.md --parallel 10
11
+ * bmad-workflow stories create epic.md --start 3
12
+ * bmad-workflow stories create epic.md --reference docs/architecture.md
13
+ * ```
14
+ */
15
+ import { Args, Command, Flags } from '@oclif/core';
16
+ import path from 'node:path';
17
+ import { createAgentRunner } from '../../services/agents/agent-runner-factory.js';
18
+ import { FileManager } from '../../services/file-system/file-manager.js';
19
+ import { PathResolver } from '../../services/file-system/path-resolver.js';
20
+ import { BatchProcessor } from '../../services/orchestration/batch-processor.js';
21
+ import { EpicParser } from '../../services/parsers/epic-parser.js';
22
+ import { FileScaffolder } from '../../services/scaffolding/file-scaffolder.js';
23
+ import * as colors from '../../utils/colors.js';
24
+ import { ValidationError } from '../../utils/errors.js';
25
+ import { createLogger, generateCorrelationId } from '../../utils/logger.js';
26
+ import { createSpinner } from '../../utils/progress.js';
27
+ import { agentFlags } from '../../utils/shared-flags.js';
28
+ /**
29
+ * Stories Create Command
30
+ *
31
+ * Creates story files from epic using Claude AI agents with parallel processing.
32
+ */
33
+ export default class StoriesCreateCommand extends Command {
34
+ static args = {
35
+ 'epic-path': Args.string({
36
+ description: 'Path to epic markdown file',
37
+ required: true,
38
+ }),
39
+ };
40
+ static description = 'Create story files from epic using Claude AI agents';
41
+ static examples = [
42
+ {
43
+ command: '<%= config.bin %> <%= command.id %> docs/epics/epic-1-foundation.md',
44
+ description: 'Create all stories from epic',
45
+ },
46
+ {
47
+ command: '<%= config.bin %> <%= command.id %> epic.md --parallel 10',
48
+ description: 'Create stories with 10 concurrent agents',
49
+ },
50
+ {
51
+ command: '<%= config.bin %> <%= command.id %> epic.md --start 3',
52
+ description: 'Create stories starting from story number 3',
53
+ },
54
+ {
55
+ command: '<%= config.bin %> <%= command.id %> epic.md --reference docs/architecture.md --reference docs/api-spec.md',
56
+ description: 'Create stories with additional context files',
57
+ },
58
+ {
59
+ command: '<%= config.bin %> <%= command.id %> epic.md --agent po --task create-next-story',
60
+ description: 'Use Product Owner agent with custom task for story creation',
61
+ },
62
+ ];
63
+ static flags = {
64
+ ...agentFlags,
65
+ interval: Flags.integer({
66
+ default: 30,
67
+ description: 'Seconds between batches',
68
+ }),
69
+ parallel: Flags.integer({
70
+ default: 7,
71
+ description: 'Max concurrent story creations',
72
+ }),
73
+ prefix: Flags.string({
74
+ description: 'Filename prefix for stories',
75
+ }),
76
+ reference: Flags.string({
77
+ description: 'Additional context files for AI agents',
78
+ multiple: true,
79
+ }),
80
+ start: Flags.integer({
81
+ description: 'Story number to start from',
82
+ }),
83
+ };
84
+ // Service instances
85
+ agentRunner;
86
+ batchProcessor;
87
+ epicParser;
88
+ fileManager;
89
+ fileScaffolder;
90
+ logger;
91
+ pathResolver;
92
+ /**
93
+ * Run the command
94
+ */
95
+ async run() {
96
+ const { args, flags } = await this.parse(StoriesCreateCommand);
97
+ const startTime = Date.now();
98
+ const correlationId = generateCorrelationId();
99
+ // Initialize services with selected provider
100
+ const provider = (flags.provider || 'claude');
101
+ this.initializeServices(flags.parallel, flags.interval, provider);
102
+ this.logger.info({
103
+ correlationId,
104
+ epicPath: args['epic-path'],
105
+ flags,
106
+ }, 'Starting stories create command');
107
+ try {
108
+ // Validate parallel flag
109
+ this.validateParallelFlag(flags.parallel);
110
+ // Parse epic and extract stories
111
+ const epicPath = path.resolve(args['epic-path']);
112
+ const stories = await this.parseEpicStories(epicPath);
113
+ // Filter stories based on --start flag
114
+ const filteredStories = this.filterStoriesByStart(stories, flags.start);
115
+ // Check for existing story files (idempotent)
116
+ const { existingStories, newStories } = await this.checkExistingStories(filteredStories, flags.prefix);
117
+ // Display summary before creation
118
+ this.logger.info({
119
+ existing: existingStories.length,
120
+ new: newStories.length,
121
+ total: filteredStories.length,
122
+ }, 'Story file check complete');
123
+ if (newStories.length === 0) {
124
+ this.log(colors.info('All story files already exist, nothing to create'));
125
+ return;
126
+ }
127
+ // Create stories with parallel processing
128
+ const results = await this.createStoriesInParallel(newStories, epicPath, flags);
129
+ // Display summary report
130
+ const duration = Date.now() - startTime;
131
+ this.displaySummaryReport(results, existingStories.length, duration);
132
+ this.logger.info({
133
+ correlationId,
134
+ created: results.filter((r) => r.success).length,
135
+ duration,
136
+ failed: results.filter((r) => !r.success).length,
137
+ }, 'Stories create command completed');
138
+ }
139
+ catch (error) {
140
+ const err = error;
141
+ this.logger.error({ correlationId, error: err }, 'Command failed');
142
+ this.error(colors.error(err.message), { exit: 1 });
143
+ }
144
+ }
145
+ /**
146
+ * Build Claude CLI prompt for story creation
147
+ */
148
+ buildClaudePrompt(options) {
149
+ const { agent = 'sm', epicPath, references, story, storyFilePath, task = 'draft' } = options;
150
+ const agentFile = agent;
151
+ const taskCommand = task;
152
+ let prompt = `@.bmad-core/agents/${agentFile}.md\n\n`;
153
+ prompt += 'Create story document from epic story definition:\n\n';
154
+ prompt += `Epic Number: ${story.epicNumber}\n`;
155
+ prompt += `Story Number: ${story.number}\n`;
156
+ prompt += `Story Title: ${story.title}\n\n`;
157
+ prompt += `Target file: @${storyFilePath}\n\n`;
158
+ prompt += 'References:\n';
159
+ prompt += `@${epicPath}\n`;
160
+ if (references && references.length > 0) {
161
+ for (const ref of references) {
162
+ prompt += `@${path.resolve(ref)}\n`;
163
+ }
164
+ }
165
+ prompt += '\nIMPORTANT: The target file has been pre-scaffolded with structure and metadata.\n';
166
+ prompt += '- DO NOT modify the Status section (already set to Draft)\n';
167
+ prompt += '- DO NOT modify the Created date in Change Log\n';
168
+ prompt += '- DO NOT change the document structure or section headers\n';
169
+ prompt += '- ONLY populate the empty content sections marked with [AI Agent will populate]\n';
170
+ prompt += '- Follow the template structure at @.bmad-core/templates/story-tmpl.yaml\n\n';
171
+ prompt += `Execute the *${taskCommand} command to populate the story document.\n`;
172
+ prompt += 'Update the file at the target path with the story content in the empty sections.\n';
173
+ return prompt;
174
+ }
175
+ /**
176
+ * Check for existing story files across all story directories
177
+ *
178
+ * Checks docs/stories, docs/qa/stories, and docs/done/stories to prevent
179
+ * recreating stories that have been moved for QA or marked as done.
180
+ */
181
+ async checkExistingStories(stories, prefix) {
182
+ const allStoryDirs = this.pathResolver.getAllStoryDirs();
183
+ const existingStories = [];
184
+ const newStories = [];
185
+ this.logger.info({ directories: allStoryDirs }, 'Checking for existing story files across all directories');
186
+ /* eslint-disable no-await-in-loop */
187
+ for (const story of stories) {
188
+ const filename = this.generateStoryFilename(story, prefix);
189
+ let foundInAnyDir = false;
190
+ // Check all story directories
191
+ for (const dir of allStoryDirs) {
192
+ const filePath = path.join(dir, filename);
193
+ const exists = await this.fileManager.fileExists(filePath);
194
+ if (exists) {
195
+ this.logger.info({ directory: dir, filePath, storyNumber: story.fullNumber }, 'Story already exists');
196
+ foundInAnyDir = true;
197
+ break;
198
+ }
199
+ }
200
+ if (foundInAnyDir) {
201
+ existingStories.push(story);
202
+ }
203
+ else {
204
+ this.logger.debug({ filename, storyNumber: story.fullNumber }, 'Story needs to be created');
205
+ newStories.push(story);
206
+ }
207
+ }
208
+ /* eslint-enable no-await-in-loop */
209
+ return { existingStories, newStories };
210
+ }
211
+ /**
212
+ * Create stories in parallel batches
213
+ */
214
+ async createStoriesInParallel(stories, epicPath, flags) {
215
+ const spinner = createSpinner('Creating stories...');
216
+ spinner.start();
217
+ const results = [];
218
+ try {
219
+ // Define processor function for each story
220
+ const processor = async (story) => this.createStory({
221
+ agent: flags.agent,
222
+ epicPath,
223
+ prefix: flags.prefix,
224
+ references: flags.reference,
225
+ story,
226
+ task: flags.task,
227
+ });
228
+ // Progress callback for batch updates
229
+ const onProgress = (info) => {
230
+ spinner.text = `Processing batch ${info.currentBatch} of ${info.totalBatches} (${info.completedItems}/${info.totalItems} completed)...`;
231
+ this.logger.info({
232
+ batchNum: info.currentBatch,
233
+ completedItems: info.completedItems,
234
+ totalBatches: info.totalBatches,
235
+ totalItems: info.totalItems,
236
+ }, 'Processing batch');
237
+ };
238
+ // Process stories in batches
239
+ const batchResults = await this.batchProcessor.processBatch(stories, processor, onProgress);
240
+ // Extract results from BatchResult wrapper
241
+ for (const batchResult of batchResults) {
242
+ if (batchResult.success && batchResult.result) {
243
+ results.push(batchResult.result);
244
+ }
245
+ else if (!batchResult.success) {
246
+ // Create error result for failed items
247
+ results.push({
248
+ error: batchResult.error?.message || 'Unknown error',
249
+ filePath: '',
250
+ story: stories[results.length],
251
+ success: false,
252
+ });
253
+ }
254
+ }
255
+ const successCount = results.filter((r) => r.success).length;
256
+ spinner.succeed(colors.success(`Created ${successCount} story files`));
257
+ }
258
+ catch (error) {
259
+ const err = error;
260
+ spinner.fail(colors.error(`Failed to create stories: ${err.message}`));
261
+ throw error;
262
+ }
263
+ return results;
264
+ }
265
+ /**
266
+ * Create a single story file
267
+ */
268
+ async createStory(options) {
269
+ const { agent, epicPath, prefix, references, story, task } = options;
270
+ const storyDir = this.pathResolver.getStoryDir();
271
+ const filename = this.generateStoryFilename(story, prefix);
272
+ const filePath = path.join(storyDir, filename);
273
+ const absolutePath = path.resolve(filePath);
274
+ this.logger.info({ filePath, storyNumber: story.fullNumber }, 'Creating story file');
275
+ try {
276
+ // Step 1: Create scaffolded file with structured sections and populated metadata
277
+ const scaffoldedContent = this.fileScaffolder.scaffoldStory({
278
+ epicNumber: story.epicNumber,
279
+ storyNumber: story.number,
280
+ storyTitle: story.title,
281
+ });
282
+ await this.fileManager.writeFile(filePath, scaffoldedContent);
283
+ this.logger.debug({ filePath }, 'Scaffolded file created');
284
+ // Step 2: Build Claude CLI prompt
285
+ const prompt = this.buildClaudePrompt({
286
+ agent,
287
+ epicPath,
288
+ references,
289
+ story,
290
+ storyFilePath: absolutePath,
291
+ task,
292
+ });
293
+ // Step 3: Run Claude agent
294
+ const result = await this.agentRunner.runAgent(prompt, {
295
+ agentType: 'sm',
296
+ timeout: 1_800_000, // 30 minutes
297
+ });
298
+ // Step 4: Verify file was updated
299
+ if (result.success) {
300
+ const content = await this.fileManager.readFile(filePath);
301
+ if (content === scaffoldedContent) {
302
+ throw new Error('AI agent did not update story file');
303
+ }
304
+ this.logger.info({ filePath, storyNumber: story.fullNumber }, 'Story created successfully');
305
+ return {
306
+ filePath,
307
+ story,
308
+ success: true,
309
+ };
310
+ }
311
+ // Cleanup placeholder file on failure
312
+ // await this.fileManager.deleteFile(filePath) // Not implemented in FileManager yet
313
+ throw new Error(`AI agent failed: ${result.errors}`);
314
+ }
315
+ catch (error) {
316
+ const err = error;
317
+ this.logger.error({ error: err, filePath, storyNumber: story.fullNumber }, 'Story creation failed');
318
+ return {
319
+ error: err.message,
320
+ filePath,
321
+ story,
322
+ success: false,
323
+ };
324
+ }
325
+ }
326
+ /**
327
+ * Display summary report
328
+ */
329
+ displaySummaryReport(results, skippedCount, duration) {
330
+ const successCount = results.filter((r) => r.success).length;
331
+ const failedCount = results.filter((r) => !r.success).length;
332
+ // Box drawing
333
+ const boxTop = '┌─────────────────────────────────────────┐';
334
+ const boxDivider = '├─────────────────────────────────────────┤';
335
+ const boxBottom = '└─────────────────────────────────────────┘';
336
+ this.log('');
337
+ this.log(boxTop);
338
+ this.log('│ Story Creation Summary │');
339
+ this.log(boxDivider);
340
+ this.log(`│ ${colors.success('Created:')} ${successCount.toString().padEnd(20)}│`);
341
+ this.log(`│ ${colors.dim('Skipped:')} ${skippedCount.toString().padEnd(20)}│`);
342
+ if (failedCount > 0) {
343
+ this.log(`│ ${colors.error('Failed:')} ${failedCount.toString().padEnd(20)}│`);
344
+ }
345
+ this.log(boxDivider);
346
+ this.log(`│ Duration: ${(duration / 1000).toFixed(2)}s${' '.repeat(15)}│`);
347
+ this.log(boxBottom);
348
+ // List failures if any
349
+ if (failedCount > 0) {
350
+ this.log('');
351
+ this.log(colors.bold('Failed Stories:'));
352
+ for (const result of results.filter((r) => !r.success)) {
353
+ this.log(colors.error(` ✗ ${result.story.fullNumber}: ${result.story.title} - ${result.error || 'Unknown error'}`));
354
+ }
355
+ }
356
+ this.log('');
357
+ }
358
+ /**
359
+ * Filter stories by start number
360
+ */
361
+ filterStoriesByStart(stories, startNumber) {
362
+ if (!startNumber) {
363
+ return stories;
364
+ }
365
+ const filtered = stories.filter((story) => story.number >= startNumber);
366
+ this.logger.info({
367
+ filtered: filtered.length,
368
+ startNumber,
369
+ total: stories.length,
370
+ }, 'Filtered stories by start number');
371
+ return filtered;
372
+ }
373
+ /**
374
+ * Generate story filename from story metadata
375
+ */
376
+ generateStoryFilename(story, prefix) {
377
+ // Generate slug from title
378
+ const slug = story.title
379
+ .toLowerCase()
380
+ .replaceAll(/[^\w\s-]/g, '')
381
+ .replaceAll(/\s+/g, '-')
382
+ .slice(0, 50);
383
+ // Build filename: {prefix}{epicNum}.{storyNum}-{slug}.md
384
+ const effectivePrefix = prefix || '';
385
+ return `${effectivePrefix}${story.epicNumber}.${story.number}-${slug}.md`;
386
+ }
387
+ /**
388
+ * Initialize service dependencies
389
+ */
390
+ initializeServices(maxConcurrency, intervalSeconds, provider = 'claude') {
391
+ this.logger = createLogger({ namespace: 'commands:stories:create' });
392
+ this.logger.info({ provider }, 'Initializing services with AI provider');
393
+ this.fileManager = new FileManager(this.logger);
394
+ this.pathResolver = new PathResolver(this.fileManager, this.logger);
395
+ this.epicParser = new EpicParser(this.logger);
396
+ this.agentRunner = createAgentRunner(provider, this.logger);
397
+ this.batchProcessor = new BatchProcessor(maxConcurrency, intervalSeconds * 1000, this.logger);
398
+ this.fileScaffolder = new FileScaffolder(this.logger);
399
+ this.logger.debug({ provider }, 'Services initialized successfully');
400
+ }
401
+ /**
402
+ * Parse epic file and extract stories
403
+ */
404
+ async parseEpicStories(epicPath) {
405
+ this.logger.info({ epicPath }, 'Reading epic file');
406
+ // Read epic file
407
+ const epicContent = await this.fileManager.readFile(epicPath);
408
+ this.logger.debug({ epicPath, length: epicContent.length }, 'Epic file read');
409
+ // Parse stories from epic
410
+ const stories = this.epicParser.parseStories(epicContent, epicPath);
411
+ if (stories.length === 0) {
412
+ throw new ValidationError('No stories found in epic. Expected format: ### Story 1.1: Title', {
413
+ epicPath,
414
+ suggestion: 'Ensure epic has story headers like: ### Story 1.1: Initialize Project',
415
+ });
416
+ }
417
+ this.logger.info({ epicPath, storyCount: stories.length }, 'Stories extracted from epic successfully');
418
+ return stories;
419
+ }
420
+ /**
421
+ * Validate parallel flag value
422
+ */
423
+ validateParallelFlag(parallel) {
424
+ if (parallel < 1 || parallel > 10) {
425
+ throw new ValidationError('Parallel flag must be between 1 and 10 to prevent overwhelming the system', {
426
+ actual: parallel,
427
+ suggestion: 'Use --parallel with a value between 1 and 10',
428
+ });
429
+ }
430
+ }
431
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Stories Develop Command
3
+ *
4
+ * Executes development workflow for stories matching glob patterns sequentially.
5
+ * Updates story status from Draft to Ready, spawns Claude dev agents, and moves
6
+ * completed stories to QA folder. Optionally runs QA workflow after development.
7
+ *
8
+ * @example
9
+ * ```bash
10
+ * bmad-workflow stories develop "docs/stories/AUTH-*.md"
11
+ * bmad-workflow stories develop "stories/**-auth-*.md" --interval 60
12
+ * bmad-workflow stories develop "stories/*.md" --reference docs/architecture.md
13
+ * bmad-workflow stories develop "stories/*.md" --qa --qa-retries 2
14
+ * ```
15
+ */
16
+ import { Command } from '@oclif/core';
17
+ /**
18
+ * Stories Develop Command
19
+ *
20
+ * Develops stories sequentially using Claude AI dev agents.
21
+ * CRITICAL: No parallel execution - stories are developed one at a time to prevent conflicts.
22
+ */
23
+ export default class StoriesDevelopCommand extends Command {
24
+ static args: {
25
+ pattern: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
26
+ };
27
+ static description: string;
28
+ static examples: {
29
+ command: string;
30
+ description: string;
31
+ }[];
32
+ static flags: {
33
+ interval: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
34
+ qa: import("@oclif/core/interfaces").BooleanFlag<boolean>;
35
+ 'qa-prompt': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
36
+ 'qa-retries': import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
37
+ reference: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
38
+ agent: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
39
+ cwd: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
40
+ provider: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
41
+ task: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
42
+ };
43
+ private agentRunner;
44
+ private fileManager;
45
+ private globMatcher;
46
+ private logger;
47
+ private pathResolver;
48
+ private storyParserFactory;
49
+ private storyTypeDetector;
50
+ /**
51
+ * Run the command
52
+ */
53
+ run(): Promise<void>;
54
+ /**
55
+ * Build Claude CLI prompt for story development
56
+ */
57
+ private buildClaudePrompt;
58
+ /**
59
+ * Develop stories sequentially (CRITICAL: No parallelism)
60
+ */
61
+ private developStoriesSequentially;
62
+ /**
63
+ * Develop a single story
64
+ */
65
+ private developStory;
66
+ /**
67
+ * Display countdown timer between stories
68
+ */
69
+ private displayCountdown;
70
+ /**
71
+ * Display summary report
72
+ */
73
+ private displaySummaryReport;
74
+ /**
75
+ * Initialize service dependencies
76
+ */
77
+ private initializeServices;
78
+ /**
79
+ * Match story files using glob pattern
80
+ */
81
+ private matchStoryFiles;
82
+ /**
83
+ * Run QA workflow for successfully developed stories
84
+ * Dynamically imports and delegates to StoriesQaCommand
85
+ */
86
+ private runQaWorkflow;
87
+ /**
88
+ * Sleep for specified milliseconds
89
+ */
90
+ private sleep;
91
+ }