@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,327 @@
1
+ /**
2
+ * Decompose Command
3
+ *
4
+ * Decomposes large goals into executable task graphs with dependency management.
5
+ * Supports per-file task generation, parallel execution, and intelligent dependency resolution.
6
+ */
7
+ import { Args, Command, Flags } from '@oclif/core';
8
+ import { join } from 'node:path';
9
+ import { createAgentRunner, isProviderSupported } from '../services/agents/agent-runner-factory.js';
10
+ import { FileManager } from '../services/file-system/file-manager.js';
11
+ import { GlobMatcher } from '../services/file-system/glob-matcher.js';
12
+ import { BatchProcessor } from '../services/orchestration/batch-processor.js';
13
+ import { DependencyGraphExecutor } from '../services/orchestration/dependency-graph-executor.js';
14
+ import { TaskDecompositionService } from '../services/orchestration/task-decomposition-service.js';
15
+ import { DecomposeSessionScaffolder } from '../services/scaffolding/decompose-session-scaffolder.js';
16
+ import * as colors from '../utils/colors.js';
17
+ import { formatBox, formatTable } from '../utils/formatters.js';
18
+ import { createLogger } from '../utils/logger.js';
19
+ /**
20
+ * Decompose Command
21
+ *
22
+ * Breaks down large goals into small, executable tasks with dependency management.
23
+ *
24
+ * @example
25
+ * ```bash
26
+ * # Decompose a goal with per-file mode
27
+ * bmad-cli decompose "Migrate project to TypeScript" --per-file --file-pattern "src/*.js"
28
+ *
29
+ * # Plan only without execution
30
+ * bmad-cli decompose "Add authentication" --plan-only
31
+ *
32
+ * # Execute with custom context
33
+ * bmad-cli decompose "Refactor API layer" --context package.json --context tsconfig.json --execute
34
+ * ```
35
+ */
36
+ export default class Decompose extends Command {
37
+ static args = {
38
+ goal: Args.string({
39
+ description: 'The big goal to decompose into executable tasks',
40
+ required: true,
41
+ }),
42
+ };
43
+ static description = 'Decompose large goals into executable task graphs with dependency management';
44
+ static examples = [
45
+ '<%= config.bin %> <%= command.id %> "Migrate project to TypeScript 100%" --per-file --file-pattern "src/*.js"',
46
+ '<%= config.bin %> <%= command.id %> "Add user authentication" --plan-only',
47
+ '<%= config.bin %> <%= command.id %> "Refactor API layer" --execute --context package.json',
48
+ '<%= config.bin %> <%= command.id %> "Implement dark mode" --max-parallel 5 --context src/theme.ts',
49
+ '<%= config.bin %> <%= command.id %> "Refactor codebase" --story-format --story-prefix "REFACTOR"',
50
+ '<%= config.bin %> <%= command.id %> "Design new API endpoints" --agent architect --plan-only',
51
+ ];
52
+ static flags = {
53
+ agent: Flags.string({
54
+ default: 'architect',
55
+ description: 'BMAD agent to use for planning/decomposition (the agent will choose best agents for each task)',
56
+ options: ['analyst', 'architect', 'dev', 'pm', 'quick-flow-solo-dev', 'sm', 'tea', 'tech-writer', 'ux-designer'],
57
+ }),
58
+ context: Flags.string({
59
+ description: 'Context files to provide to the planner (can be used multiple times)',
60
+ multiple: true,
61
+ }),
62
+ cwd: Flags.string({
63
+ description: 'Working directory path for task execution',
64
+ }),
65
+ execute: Flags.boolean({
66
+ default: false,
67
+ description: 'Execute tasks immediately after planning (skips confirmation)',
68
+ }),
69
+ 'file-pattern': Flags.string({
70
+ description: 'Glob pattern for per-file mode (e.g., "src/**/*.js")',
71
+ }),
72
+ 'max-parallel': Flags.integer({
73
+ default: 3,
74
+ description: 'Maximum number of tasks to run in parallel',
75
+ }),
76
+ 'output-dir': Flags.string({
77
+ default: 'docs/decompose-sessions',
78
+ description: 'Directory for session outputs',
79
+ }),
80
+ 'per-file': Flags.boolean({
81
+ default: false,
82
+ description: 'Create one task per file for file-heavy operations',
83
+ }),
84
+ 'plan-only': Flags.boolean({
85
+ default: false,
86
+ description: 'Generate task graph without executing',
87
+ }),
88
+ provider: Flags.string({
89
+ default: 'claude',
90
+ description: 'AI provider to use (claude or gemini)',
91
+ options: ['claude', 'gemini'],
92
+ }),
93
+ 'story-format': Flags.boolean({
94
+ default: false,
95
+ description: 'Format tasks as BMAD stories with acceptance criteria and proper structure',
96
+ }),
97
+ 'story-prefix': Flags.string({
98
+ description: 'Project prefix for story IDs (e.g., "MIGRATE", "REFACTOR") - used with --story-format',
99
+ }),
100
+ 'task-timeout': Flags.integer({
101
+ default: 1_800_000, // 30 minutes
102
+ description: 'Timeout per task in milliseconds',
103
+ }),
104
+ verbose: Flags.boolean({
105
+ char: 'v',
106
+ default: false,
107
+ description: 'Detailed output mode',
108
+ }),
109
+ };
110
+ cancelled = false;
111
+ fileManager;
112
+ logger;
113
+ scaffolder;
114
+ /**
115
+ * Main command execution
116
+ */
117
+ async run() {
118
+ const { args, flags } = await this.parse(Decompose);
119
+ try {
120
+ // Validate provider
121
+ if (!isProviderSupported(flags.provider)) {
122
+ this.error(`Unsupported provider: ${flags.provider}. Use 'claude' or 'gemini'.`, { exit: 1 });
123
+ }
124
+ // Validate per-file mode
125
+ if (flags['per-file'] && !flags['file-pattern']) {
126
+ this.error('--file-pattern is required when using --per-file mode', { exit: 1 });
127
+ }
128
+ // Validate story format mode
129
+ if (flags['story-format'] && !flags['story-prefix']) {
130
+ this.error('--story-prefix is required when using --story-format mode', { exit: 1 });
131
+ }
132
+ // Initialize services
133
+ await this.initializeServices(flags.provider);
134
+ // Register signal handlers
135
+ this.registerSignalHandlers();
136
+ // Show header
137
+ this.displayHeader(args.goal);
138
+ // Build options
139
+ const options = {
140
+ agent: flags.agent,
141
+ contextFiles: flags.context || [],
142
+ cwd: flags.cwd,
143
+ execute: flags.execute,
144
+ filePattern: flags['file-pattern'],
145
+ goal: args.goal,
146
+ maxParallel: flags['max-parallel'],
147
+ perFile: flags['per-file'],
148
+ planOnly: flags['plan-only'],
149
+ storyFormat: flags['story-format'],
150
+ storyPrefix: flags['story-prefix'],
151
+ taskTimeout: flags['task-timeout'],
152
+ };
153
+ // Log configuration if verbose
154
+ if (flags.verbose) {
155
+ this.logger.info({ options }, 'Decompose configuration');
156
+ }
157
+ // Create session directory
158
+ const timestamp = new Date().toISOString().replaceAll(/[:.]/g, '-').split('.')[0];
159
+ const sessionDir = join(flags['output-dir'], `session-${timestamp}`);
160
+ await this.fileManager.createDirectory(sessionDir);
161
+ this.log(colors.info(`📁 Session directory: ${sessionDir}\n`));
162
+ // Phase 1: Decompose goal into task graph
163
+ this.log(colors.bold('⚙️ Phase 1: Decomposing goal into task graph...\n'));
164
+ const decompositionService = new TaskDecompositionService(createAgentRunner(flags.provider, this.logger), this.fileManager, new GlobMatcher(this.fileManager, this.logger), this.logger);
165
+ const taskGraph = await decompositionService.decomposeGoal(options, sessionDir);
166
+ this.log(colors.success('✓ Task graph generated!\n'));
167
+ this.displayTaskGraphSummary(taskGraph);
168
+ // Phase 2: Scaffold session structure
169
+ this.log(colors.bold('\n⚙️ Phase 2: Creating session structure...\n'));
170
+ await this.scaffolder.createSessionStructure(sessionDir, options.storyFormat);
171
+ const sessionConfig = {
172
+ createdAt: new Date(),
173
+ goal: args.goal,
174
+ options,
175
+ outputDir: sessionDir,
176
+ sessionId: taskGraph.session.id,
177
+ };
178
+ await this.scaffolder.writeGoalFile(sessionDir, sessionConfig);
179
+ await this.scaffolder.writeTaskGraphFile(sessionDir, taskGraph);
180
+ await this.scaffolder.writeMasterPromptFile(sessionDir, taskGraph);
181
+ await this.scaffolder.writeTaskPromptFiles(sessionDir, taskGraph);
182
+ await this.scaffolder.writeSessionReadme(sessionDir, taskGraph, options.storyFormat);
183
+ this.log(colors.success('✓ Session structure created!\n'));
184
+ this.log(colors.info(`📄 Task graph saved to: ${sessionDir}/task-graph.yaml`));
185
+ this.log(colors.info(`📄 Session README: ${sessionDir}/SESSION_README.md\n`));
186
+ // Phase 3: Execute (if not plan-only)
187
+ if (flags['plan-only']) {
188
+ this.log(colors.info('\n💡 Plan-only mode: Task graph generated but not executed.'));
189
+ this.log(colors.info(`To execute, run: bmad-cli decompose "${args.goal}" --execute\n`));
190
+ }
191
+ else {
192
+ // Ask for confirmation unless --execute flag is set
193
+ if (!flags.execute) {
194
+ const confirmed = await this.confirmExecution(taskGraph);
195
+ if (!confirmed) {
196
+ this.log(colors.warning('\n⏸️ Execution cancelled. Task graph saved for later use.'));
197
+ this.log(colors.info(`To execute later, review: ${sessionDir}/task-graph.yaml\n`));
198
+ return;
199
+ }
200
+ }
201
+ this.log(colors.bold('\n⚙️ Phase 3: Executing task graph...\n'));
202
+ const executor = new DependencyGraphExecutor(taskGraph, createAgentRunner(flags.provider, this.logger), new BatchProcessor(flags['max-parallel'], 0, this.logger), this.fileManager, this.logger, flags.cwd);
203
+ const executionResult = await executor.execute((layerIndex, totalLayers, layerSize) => {
204
+ this.log(colors.info(`\n🔄 Starting Layer ${layerIndex + 1}/${totalLayers} (${layerSize} task${layerSize > 1 ? 's' : ''} in parallel)`));
205
+ }, (taskId, _layerIndex, _taskIndex, _totalTasks) => {
206
+ this.log(colors.dim(` → Executing ${taskId}...`));
207
+ });
208
+ // Write execution report
209
+ await this.scaffolder.writeExecutionReport(sessionDir, taskGraph, executionResult);
210
+ // Display results
211
+ this.displayExecutionResults(executionResult, sessionDir);
212
+ // Check for cancellation
213
+ if (this.cancelled) {
214
+ this.log('\n' + colors.warning('⚠️ Execution cancelled by user'));
215
+ process.exit(130);
216
+ }
217
+ // Exit with appropriate code
218
+ if (!executionResult.success) {
219
+ this.error('Task graph execution completed with failures', { exit: 1 });
220
+ }
221
+ this.log('\n' + colors.success('✅ All tasks completed successfully!'));
222
+ }
223
+ }
224
+ catch (error) {
225
+ this.logger.error({ error: error.message }, 'Decompose command failed');
226
+ this.log('\n' + colors.error('✗ Decompose failed:'));
227
+ this.log(colors.error(` ${error.message}`));
228
+ this.error('Execution failed', { exit: 1 });
229
+ }
230
+ }
231
+ /**
232
+ * Confirm execution with user
233
+ */
234
+ async confirmExecution(taskGraph) {
235
+ this.log(colors.warning('\n⚠️ Ready to execute task graph'));
236
+ this.log(colors.dim(` This will run ${taskGraph.metadata.totalTasks} tasks across ${taskGraph.metadata.executionLayers.length} layers.`));
237
+ this.log(colors.dim(` Estimated duration: ${taskGraph.metadata.estimatedDuration} minutes\n`));
238
+ // For now, default to yes in yolo mode
239
+ // In production, you'd use a proper prompt library
240
+ this.log(colors.info(' Press Ctrl+C to cancel, or Enter to continue...'));
241
+ // Simple confirmation
242
+ return true;
243
+ }
244
+ /**
245
+ * Display execution results
246
+ */
247
+ displayExecutionResults(result, sessionDir) {
248
+ this.log('\n' + colors.bold('╔════════════════════════════════════════════════════════╗'));
249
+ this.log(colors.bold('║ Execution Summary ║'));
250
+ this.log(colors.bold('╚════════════════════════════════════════════════════════╝') + '\n');
251
+ const headers = ['Metric', 'Value'];
252
+ const rows = [
253
+ ['Total Tasks', String(result.totalTasks)],
254
+ [colors.success('✓ Completed'), colors.success(String(result.completedTasks))],
255
+ [colors.error('✗ Failed'), result.failedTasks > 0 ? colors.error(String(result.failedTasks)) : '0'],
256
+ [colors.warning('⊘ Skipped'), result.skippedTasks > 0 ? colors.warning(String(result.skippedTasks)) : '0'],
257
+ ['Duration', `${Math.round(result.totalDuration / 1000)}s`],
258
+ ['Success Rate', `${Math.round((result.completedTasks / result.totalTasks) * 100)}%`],
259
+ ];
260
+ this.log(formatTable(headers, rows));
261
+ this.log(colors.info(`\n📊 Full execution report: ${sessionDir}/execution-report.yaml`));
262
+ this.log(colors.info(`📁 Task outputs: ${sessionDir}/outputs/\n`));
263
+ // Show failed tasks if any
264
+ if (result.failedTasks > 0) {
265
+ this.log(colors.error('\n❌ Failed Tasks:'));
266
+ for (const taskResult of result.taskResults) {
267
+ if (!taskResult.success) {
268
+ this.log(colors.error(` • ${taskResult.taskId}: ${taskResult.errors}`));
269
+ }
270
+ }
271
+ }
272
+ }
273
+ /**
274
+ * Display command header
275
+ */
276
+ displayHeader(goal) {
277
+ const banner = formatBox('DECOMPOSE: Break Big Goals into Executable Tasks', `Goal: ${goal}`);
278
+ this.log('\n' + colors.bold(banner) + '\n');
279
+ }
280
+ /**
281
+ * Display task graph summary
282
+ */
283
+ displayTaskGraphSummary(taskGraph) {
284
+ this.log(colors.bold('📊 Task Graph Summary:'));
285
+ this.log(colors.dim(' ├─ Total Tasks: ') + colors.info(String(taskGraph.metadata.totalTasks)));
286
+ this.log(colors.dim(' ├─ Estimated Duration: ') + colors.info(`${taskGraph.metadata.estimatedDuration} minutes`));
287
+ this.log(colors.dim(' ├─ Execution Layers: ') + colors.info(String(taskGraph.metadata.executionLayers.length)));
288
+ this.log(colors.dim(' ├─ Max Parallelism: ') + colors.info(String(taskGraph.metadata.maxParallelism)));
289
+ // Check if we have any story-formatted tasks (check if task IDs don't start with "task-")
290
+ const isStoryFormat = taskGraph.tasks.some((t) => !t.id.startsWith('task-'));
291
+ if (isStoryFormat) {
292
+ this.log(colors.dim(' ├─ Format Mode: ') + colors.info('BMAD Stories'));
293
+ }
294
+ if (taskGraph.metadata.perFileMode) {
295
+ this.log(colors.dim(' └─ Per-File Mode: ') +
296
+ colors.info(`Enabled (${taskGraph.metadata.totalFiles ?? 0} files)`));
297
+ }
298
+ // Show execution layers
299
+ this.log(colors.bold('\n📋 Execution Plan:'));
300
+ for (let i = 0; i < taskGraph.metadata.executionLayers.length; i++) {
301
+ const layer = taskGraph.metadata.executionLayers[i];
302
+ const symbol = i === taskGraph.metadata.executionLayers.length - 1 ? '└─' : '├─';
303
+ const taskWord = isStoryFormat ? 'story/stories' : 'task(s)';
304
+ this.log(colors.dim(` ${symbol} Layer ${i + 1}: `) + colors.info(`${layer.length} ${taskWord} in parallel`));
305
+ }
306
+ }
307
+ /**
308
+ * Initialize services
309
+ */
310
+ async initializeServices(provider) {
311
+ this.logger = createLogger({ namespace: 'commands:decompose' });
312
+ this.logger.info({ provider }, 'Initializing decompose services');
313
+ this.fileManager = new FileManager(this.logger);
314
+ this.scaffolder = new DecomposeSessionScaffolder(this.fileManager, this.logger);
315
+ }
316
+ /**
317
+ * Register signal handlers for graceful cancellation
318
+ */
319
+ registerSignalHandlers() {
320
+ process.on('SIGINT', () => {
321
+ if (!this.cancelled) {
322
+ this.cancelled = true;
323
+ this.log('\n' + colors.warning('⚠️ Cancellation requested. Finishing current tasks...'));
324
+ }
325
+ });
326
+ }
327
+ }
@@ -0,0 +1,18 @@
1
+ import { Command } from '@oclif/core';
2
+ /**
3
+ * Demo command that showcases all terminal output utilities
4
+ *
5
+ * Demonstrates colors, progress indicators, formatters, and countdown timers.
6
+ * Useful for verifying that all output utilities work correctly.
7
+ */
8
+ export default class Demo extends Command {
9
+ static description: string;
10
+ static examples: string[];
11
+ run(): Promise<void>;
12
+ /**
13
+ * Helper method to delay execution
14
+ *
15
+ * @param ms - Milliseconds to delay
16
+ */
17
+ private delay;
18
+ }
@@ -0,0 +1,107 @@
1
+ import { Command } from '@oclif/core';
2
+ import * as colors from '../utils/colors.js';
3
+ import { formatBox, formatCountdown, formatList, formatTable } from '../utils/formatters.js';
4
+ import { createLogger, generateCorrelationId } from '../utils/logger.js';
5
+ import { createSpinner, MultiStepProgress } from '../utils/progress.js';
6
+ /**
7
+ * Demo command that showcases all terminal output utilities
8
+ *
9
+ * Demonstrates colors, progress indicators, formatters, and countdown timers.
10
+ * Useful for verifying that all output utilities work correctly.
11
+ */
12
+ export default class Demo extends Command {
13
+ static description = 'Demonstrate terminal output utilities (colors, progress, formatters)';
14
+ static examples = ['<%= config.bin %> <%= command.id %>'];
15
+ async run() {
16
+ const logger = createLogger({ namespace: 'commands:demo' });
17
+ const correlationId = generateCorrelationId();
18
+ logger.info({ command: 'demo', correlationId }, 'Starting demo command');
19
+ try {
20
+ // Section 1: Color utilities
21
+ this.log('\n' + colors.bold('═══ Section 1: Color Utilities ═══\n'));
22
+ this.log(colors.success('This is a success message'));
23
+ this.log(colors.error('This is an error message'));
24
+ this.log(colors.warning('This is a warning message'));
25
+ this.log(colors.info('This is an info message'));
26
+ this.log(colors.highlight('This is highlighted text'));
27
+ this.log(colors.dim('This is dimmed text'));
28
+ // Section 2: Simple spinner
29
+ this.log('\n' + colors.bold('═══ Section 2: Simple Spinner ═══\n'));
30
+ const spinner = createSpinner('Loading data...');
31
+ spinner.start();
32
+ await this.delay(2000);
33
+ spinner.succeed('Data loaded successfully');
34
+ // Section 3: Multi-step progress
35
+ this.log('\n' + colors.bold('═══ Section 3: Multi-Step Progress ═══\n'));
36
+ const steps = ['Parse configuration', 'Validate inputs', 'Execute workflow'];
37
+ const progress = new MultiStepProgress(steps);
38
+ // Sequential execution with await is intentional for demo
39
+ for (let i = 0; i < steps.length; i++) {
40
+ progress.start(i);
41
+ // eslint-disable-next-line no-await-in-loop
42
+ await this.delay(1500);
43
+ }
44
+ progress.succeed('All workflow steps completed');
45
+ // Section 4: Table formatting
46
+ this.log('\n' + colors.bold('═══ Section 4: Table Formatting ═══\n'));
47
+ const table = formatTable(['Epic', 'Status', 'Stories'], [
48
+ ['Epic 1', 'Done', '5'],
49
+ ['Epic 2', 'In Progress', '3'],
50
+ ['Epic 3', 'Pending', '0'],
51
+ ]);
52
+ this.log(table);
53
+ // Section 5: List formatting
54
+ this.log('\n' + colors.bold('═══ Section 5: List Formatting ═══\n'));
55
+ this.log(colors.info('Unordered list:'));
56
+ const unorderedList = formatList(['First item', 'Second item', 'Third item'], false);
57
+ this.log(unorderedList);
58
+ this.log('\n' + colors.info('Ordered list:'));
59
+ const orderedList = formatList(['Initialize project', 'Install dependencies', 'Run tests'], true);
60
+ this.log(orderedList);
61
+ // Section 6: Box formatting
62
+ this.log('\n' + colors.bold('═══ Section 6: Box Formatting ═══\n'));
63
+ const box = formatBox('Summary', `Total Epics: 3
64
+ Success: 2
65
+ Failed: 0
66
+ Pending: 1`);
67
+ this.log(box);
68
+ // Section 7: Countdown timer
69
+ this.log('\n' + colors.bold('═══ Section 7: Countdown Timer ═══\n'));
70
+ this.log(colors.info('Countdown examples:'));
71
+ this.log(` 0 seconds: ${formatCountdown(0)}`);
72
+ this.log(` 45 seconds: ${formatCountdown(45)}`);
73
+ this.log(` 90 seconds: ${formatCountdown(90)}`);
74
+ this.log(` 3600 seconds: ${formatCountdown(3600)}`);
75
+ this.log(` 3665 seconds: ${formatCountdown(3665)}`);
76
+ // Live countdown demonstration
77
+ this.log('\n' + colors.info('Live countdown (5 seconds):'));
78
+ const countdownSpinner = createSpinner('Next operation in...');
79
+ countdownSpinner.start();
80
+ // Sequential execution with await is intentional for demo
81
+ for (let i = 5; i > 0; i--) {
82
+ countdownSpinner.text = `Next operation in ${formatCountdown(i)}...`;
83
+ // eslint-disable-next-line no-await-in-loop
84
+ await this.delay(1000);
85
+ }
86
+ countdownSpinner.succeed('Countdown complete!');
87
+ // Final message
88
+ this.log('\n' + colors.success('Demo completed successfully!'));
89
+ logger.info({ correlationId }, 'Demo command completed');
90
+ }
91
+ catch (error) {
92
+ logger.error({ correlationId, error }, 'Demo command failed');
93
+ this.log('\n' + colors.error('Demo encountered an error'));
94
+ throw error;
95
+ }
96
+ }
97
+ /**
98
+ * Helper method to delay execution
99
+ *
100
+ * @param ms - Milliseconds to delay
101
+ */
102
+ async delay(ms) {
103
+ return new Promise((resolve) => {
104
+ setTimeout(resolve, ms);
105
+ });
106
+ }
107
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Epics Create Command
3
+ *
4
+ * Creates epic files from a PRD document using Claude AI agents.
5
+ * Supports batch processing with progress indicators and idempotent behavior.
6
+ *
7
+ * @example
8
+ * ```bash
9
+ * bmad-workflow epics create docs/prd.md
10
+ * bmad-workflow epics create docs/prd.md --start 2 --count 3
11
+ * bmad-workflow epics create docs/prd.md --reference docs/architecture.md
12
+ * ```
13
+ */
14
+ import { Command } from '@oclif/core';
15
+ /**
16
+ * Epics Create Command
17
+ *
18
+ * Creates epic markdown files from a PRD document by:
19
+ * 1. Parsing epics from PRD
20
+ * 2. Filtering based on flags (start, count)
21
+ * 3. Skipping existing epic files (idempotent)
22
+ * 4. Creating placeholder files
23
+ * 5. Running Claude AI agents to populate content
24
+ * 6. Displaying progress and summary
25
+ */
26
+ export default class EpicsCreate extends Command {
27
+ static args: {
28
+ 'prd-path': import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
29
+ };
30
+ static description: string;
31
+ static examples: string[];
32
+ static flags: {
33
+ count: import("@oclif/core/interfaces").OptionFlag<number | undefined, import("@oclif/core/interfaces").CustomOptions>;
34
+ interval: import("@oclif/core/interfaces").OptionFlag<number, import("@oclif/core/interfaces").CustomOptions>;
35
+ prefix: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
36
+ reference: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
37
+ start: import("@oclif/core/interfaces").OptionFlag<number, 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
+ /**
44
+ * AI agent runner service (Claude or Gemini)
45
+ */
46
+ private agentRunner;
47
+ /**
48
+ * File manager service
49
+ */
50
+ private fileManager;
51
+ /**
52
+ * File scaffolder service
53
+ */
54
+ private fileScaffolder;
55
+ /**
56
+ * Logger instance
57
+ */
58
+ private logger;
59
+ /**
60
+ * Path resolver service
61
+ */
62
+ private pathResolver;
63
+ /**
64
+ * PRD parser service
65
+ */
66
+ private prdParser;
67
+ /**
68
+ * Execute the command
69
+ */
70
+ run(): Promise<void>;
71
+ /**
72
+ * Build Claude CLI prompt for epic population
73
+ */
74
+ private buildClaudePrompt;
75
+ /**
76
+ * Check for existing epic files to support idempotent behavior
77
+ */
78
+ private checkExistingEpics;
79
+ /**
80
+ * Create epic files using Claude AI agents
81
+ */
82
+ private createEpics;
83
+ /**
84
+ * Create a single epic file
85
+ */
86
+ private createSingleEpic;
87
+ /**
88
+ * Display initial status before creating epics
89
+ */
90
+ private displayInitialStatus;
91
+ /**
92
+ * Display summary report after creating epics
93
+ */
94
+ private displaySummary;
95
+ /**
96
+ * Filter epics based on start and count flags
97
+ */
98
+ private filterEpics;
99
+ /**
100
+ * Generate epic filename from epic data
101
+ */
102
+ private generateEpicFileName;
103
+ /**
104
+ * Generate scaffolded content for epic file
105
+ */
106
+ private generateScaffoldedContent;
107
+ /**
108
+ * Initialize service dependencies
109
+ */
110
+ private initializeServices;
111
+ /**
112
+ * Parse PRD file and extract epics
113
+ */
114
+ private parsePrd;
115
+ /**
116
+ * Validate PRD path exists
117
+ */
118
+ private validatePrdPath;
119
+ /**
120
+ * Wait for specified interval with countdown
121
+ */
122
+ private waitInterval;
123
+ }