@hyperdrive.bot/bmad-workflow 1.0.16 → 1.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/stories/qa.js +29 -73
- package/dist/commands/workflow.d.ts +81 -0
- package/dist/commands/workflow.js +386 -13
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +1 -0
- package/dist/models/workflow-callbacks.d.ts +251 -0
- package/dist/models/workflow-callbacks.js +10 -0
- package/dist/services/WorkflowReporter.d.ts +165 -0
- package/dist/services/WorkflowReporter.js +691 -0
- package/dist/services/agents/claude-agent-runner.js +6 -1
- package/dist/services/orchestration/workflow-orchestrator.d.ts +33 -1
- package/dist/services/orchestration/workflow-orchestrator.js +869 -275
- package/dist/services/scaffolding/workflow-session-scaffolder.d.ts +182 -0
- package/dist/services/scaffolding/workflow-session-scaffolder.js +236 -0
- package/dist/utils/colors.d.ts +10 -10
- package/dist/utils/colors.js +15 -15
- package/dist/utils/listr2-helpers.d.ts +216 -0
- package/dist/utils/listr2-helpers.js +334 -0
- package/package.json +3 -2
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Callbacks
|
|
3
|
+
*
|
|
4
|
+
* Interfaces for lifecycle event callbacks that consumers can register
|
|
5
|
+
* to observe workflow execution without coupling to orchestration internals.
|
|
6
|
+
*
|
|
7
|
+
* Callbacks are fire-and-forget (synchronous but non-blocking) and errors
|
|
8
|
+
* in callbacks do not interrupt workflow execution.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Context for phase lifecycle events
|
|
12
|
+
*
|
|
13
|
+
* Provided when a workflow phase (epic, story, dev, qa) starts or completes.
|
|
14
|
+
*/
|
|
15
|
+
export interface PhaseContext {
|
|
16
|
+
/**
|
|
17
|
+
* Phase name: 'epic' | 'story' | 'dev' | 'qa'
|
|
18
|
+
*/
|
|
19
|
+
phaseName: string;
|
|
20
|
+
/**
|
|
21
|
+
* Phase start timestamp (epoch ms)
|
|
22
|
+
*/
|
|
23
|
+
startTime: number;
|
|
24
|
+
/**
|
|
25
|
+
* Phase end timestamp (epoch ms, only in onPhaseComplete)
|
|
26
|
+
*/
|
|
27
|
+
endTime?: number;
|
|
28
|
+
/**
|
|
29
|
+
* Phase duration in milliseconds (only in onPhaseComplete)
|
|
30
|
+
*/
|
|
31
|
+
duration?: number;
|
|
32
|
+
/**
|
|
33
|
+
* Total items to process in this phase
|
|
34
|
+
*/
|
|
35
|
+
itemCount: number;
|
|
36
|
+
/**
|
|
37
|
+
* Number of successfully processed items (only in onPhaseComplete)
|
|
38
|
+
*/
|
|
39
|
+
successCount?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Number of failed items (only in onPhaseComplete)
|
|
42
|
+
*/
|
|
43
|
+
failureCount?: number;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the phase was skipped
|
|
46
|
+
*/
|
|
47
|
+
skipped?: boolean;
|
|
48
|
+
/**
|
|
49
|
+
* Additional metadata specific to the phase
|
|
50
|
+
*/
|
|
51
|
+
metadata?: Record<string, unknown>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Context for layer/batch lifecycle events
|
|
55
|
+
*
|
|
56
|
+
* Provided when a batch of items starts or completes processing.
|
|
57
|
+
* Layers represent groups of items processed in parallel.
|
|
58
|
+
*/
|
|
59
|
+
export interface LayerContext {
|
|
60
|
+
/**
|
|
61
|
+
* Phase this layer belongs to
|
|
62
|
+
*/
|
|
63
|
+
phaseName: string;
|
|
64
|
+
/**
|
|
65
|
+
* Zero-based layer index within the phase
|
|
66
|
+
*/
|
|
67
|
+
layerIndex: number;
|
|
68
|
+
/**
|
|
69
|
+
* Total number of layers in the phase
|
|
70
|
+
*/
|
|
71
|
+
totalLayers: number;
|
|
72
|
+
/**
|
|
73
|
+
* Number of items (spawns) in this layer
|
|
74
|
+
*/
|
|
75
|
+
spawnCount: number;
|
|
76
|
+
/**
|
|
77
|
+
* Whether items in this layer are processed in parallel
|
|
78
|
+
*/
|
|
79
|
+
parallel: boolean;
|
|
80
|
+
/**
|
|
81
|
+
* Layer start timestamp (epoch ms)
|
|
82
|
+
*/
|
|
83
|
+
startTime: number;
|
|
84
|
+
/**
|
|
85
|
+
* Layer end timestamp (epoch ms, only in onLayerComplete)
|
|
86
|
+
*/
|
|
87
|
+
endTime?: number;
|
|
88
|
+
/**
|
|
89
|
+
* Layer duration in milliseconds (only in onLayerComplete)
|
|
90
|
+
*/
|
|
91
|
+
duration?: number;
|
|
92
|
+
/**
|
|
93
|
+
* Number of successful spawns (only in onLayerComplete)
|
|
94
|
+
*/
|
|
95
|
+
successCount?: number;
|
|
96
|
+
/**
|
|
97
|
+
* Number of failed spawns (only in onLayerComplete)
|
|
98
|
+
*/
|
|
99
|
+
failureCount?: number;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Context for spawn (agent execution) lifecycle events
|
|
103
|
+
*
|
|
104
|
+
* Provided when an individual agent execution starts, completes, or produces output.
|
|
105
|
+
*/
|
|
106
|
+
export interface SpawnContext {
|
|
107
|
+
/**
|
|
108
|
+
* Unique identifier for this spawn
|
|
109
|
+
*/
|
|
110
|
+
spawnId: string;
|
|
111
|
+
/**
|
|
112
|
+
* Phase this spawn belongs to
|
|
113
|
+
*/
|
|
114
|
+
phaseName: string;
|
|
115
|
+
/**
|
|
116
|
+
* Type of agent being spawned: 'architect' | 'sm' | 'dev' | 'qa'
|
|
117
|
+
*/
|
|
118
|
+
agentType: string;
|
|
119
|
+
/**
|
|
120
|
+
* Item identifier (epic number, story number, etc.)
|
|
121
|
+
*/
|
|
122
|
+
itemId: string;
|
|
123
|
+
/**
|
|
124
|
+
* Item title or description
|
|
125
|
+
*/
|
|
126
|
+
itemTitle?: string;
|
|
127
|
+
/**
|
|
128
|
+
* Output file path (if applicable)
|
|
129
|
+
*/
|
|
130
|
+
outputPath?: string;
|
|
131
|
+
/**
|
|
132
|
+
* The prompt sent to the agent
|
|
133
|
+
*/
|
|
134
|
+
prompt?: string;
|
|
135
|
+
/**
|
|
136
|
+
* Spawn start timestamp (epoch ms)
|
|
137
|
+
*/
|
|
138
|
+
startTime: number;
|
|
139
|
+
/**
|
|
140
|
+
* Spawn end timestamp (epoch ms, only in onSpawnComplete)
|
|
141
|
+
*/
|
|
142
|
+
endTime?: number;
|
|
143
|
+
/**
|
|
144
|
+
* Spawn duration in milliseconds (only in onSpawnComplete)
|
|
145
|
+
*/
|
|
146
|
+
duration?: number;
|
|
147
|
+
/**
|
|
148
|
+
* Whether the spawn succeeded (only in onSpawnComplete)
|
|
149
|
+
*/
|
|
150
|
+
success?: boolean;
|
|
151
|
+
/**
|
|
152
|
+
* Agent output text (only in onSpawnComplete or onSpawnOutput)
|
|
153
|
+
*/
|
|
154
|
+
output?: string;
|
|
155
|
+
/**
|
|
156
|
+
* Error message if spawn failed (only in onSpawnComplete)
|
|
157
|
+
*/
|
|
158
|
+
error?: string;
|
|
159
|
+
/**
|
|
160
|
+
* Worker ID for pipelined execution
|
|
161
|
+
*/
|
|
162
|
+
workerId?: number;
|
|
163
|
+
/**
|
|
164
|
+
* Additional metadata
|
|
165
|
+
*/
|
|
166
|
+
metadata?: Record<string, unknown>;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Context for error events
|
|
170
|
+
*
|
|
171
|
+
* Provided when an error occurs during workflow execution.
|
|
172
|
+
*/
|
|
173
|
+
export interface ErrorContext {
|
|
174
|
+
/**
|
|
175
|
+
* Error message
|
|
176
|
+
*/
|
|
177
|
+
message: string;
|
|
178
|
+
/**
|
|
179
|
+
* Phase where the error occurred (if applicable)
|
|
180
|
+
*/
|
|
181
|
+
phaseName?: string;
|
|
182
|
+
/**
|
|
183
|
+
* Spawn ID where the error occurred (if applicable)
|
|
184
|
+
*/
|
|
185
|
+
spawnId?: string;
|
|
186
|
+
/**
|
|
187
|
+
* Item identifier related to the error (if applicable)
|
|
188
|
+
*/
|
|
189
|
+
itemId?: string;
|
|
190
|
+
/**
|
|
191
|
+
* Error stack trace (if available)
|
|
192
|
+
*/
|
|
193
|
+
stack?: string;
|
|
194
|
+
/**
|
|
195
|
+
* Original error object
|
|
196
|
+
*/
|
|
197
|
+
error?: Error;
|
|
198
|
+
/**
|
|
199
|
+
* Timestamp when error occurred (epoch ms)
|
|
200
|
+
*/
|
|
201
|
+
timestamp: number;
|
|
202
|
+
/**
|
|
203
|
+
* Whether the error is recoverable (workflow can continue)
|
|
204
|
+
*/
|
|
205
|
+
recoverable: boolean;
|
|
206
|
+
/**
|
|
207
|
+
* Additional context about the error
|
|
208
|
+
*/
|
|
209
|
+
metadata?: Record<string, unknown>;
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Workflow lifecycle callbacks
|
|
213
|
+
*
|
|
214
|
+
* Optional callback functions that consumers can register to observe
|
|
215
|
+
* workflow execution events. All callbacks are wrapped in try-catch
|
|
216
|
+
* to prevent errors from interrupting workflow execution.
|
|
217
|
+
*/
|
|
218
|
+
export interface WorkflowCallbacks {
|
|
219
|
+
/**
|
|
220
|
+
* Called when a phase starts execution
|
|
221
|
+
*/
|
|
222
|
+
onPhaseStart?: (context: PhaseContext) => void;
|
|
223
|
+
/**
|
|
224
|
+
* Called when a phase completes execution
|
|
225
|
+
*/
|
|
226
|
+
onPhaseComplete?: (context: PhaseContext) => void;
|
|
227
|
+
/**
|
|
228
|
+
* Called when a batch/layer starts processing
|
|
229
|
+
*/
|
|
230
|
+
onLayerStart?: (context: LayerContext) => void;
|
|
231
|
+
/**
|
|
232
|
+
* Called when a batch/layer completes processing
|
|
233
|
+
*/
|
|
234
|
+
onLayerComplete?: (context: LayerContext) => void;
|
|
235
|
+
/**
|
|
236
|
+
* Called when an agent spawn starts
|
|
237
|
+
*/
|
|
238
|
+
onSpawnStart?: (context: SpawnContext) => void;
|
|
239
|
+
/**
|
|
240
|
+
* Called when an agent spawn completes
|
|
241
|
+
*/
|
|
242
|
+
onSpawnComplete?: (context: SpawnContext) => void;
|
|
243
|
+
/**
|
|
244
|
+
* Called when an agent produces streaming output
|
|
245
|
+
*/
|
|
246
|
+
onSpawnOutput?: (context: SpawnContext, output: string) => void;
|
|
247
|
+
/**
|
|
248
|
+
* Called when an error occurs
|
|
249
|
+
*/
|
|
250
|
+
onError?: (context: ErrorContext) => void;
|
|
251
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workflow Callbacks
|
|
3
|
+
*
|
|
4
|
+
* Interfaces for lifecycle event callbacks that consumers can register
|
|
5
|
+
* to observe workflow execution without coupling to orchestration internals.
|
|
6
|
+
*
|
|
7
|
+
* Callbacks are fire-and-forget (synchronous but non-blocking) and errors
|
|
8
|
+
* in callbacks do not interrupt workflow execution.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowReporter
|
|
3
|
+
*
|
|
4
|
+
* Real-time progress visualization for multi-layer parallel workflow execution
|
|
5
|
+
* using listr2. Implements the WorkflowCallbacks interface to observe workflow
|
|
6
|
+
* lifecycle events without coupling to orchestration internals.
|
|
7
|
+
*
|
|
8
|
+
* Features:
|
|
9
|
+
* - Top-level phase task groups with emoji-annotated titles
|
|
10
|
+
* - Nested concurrent task groups for parallel spawn layers
|
|
11
|
+
* - Live status updates for spawn transitions (queued → running → completed/failed)
|
|
12
|
+
* - Collapsible layer summaries on completion
|
|
13
|
+
*/
|
|
14
|
+
import type { WorkflowCallbacks } from '../models/workflow-callbacks.js';
|
|
15
|
+
/**
|
|
16
|
+
* Configuration options for WorkflowReporter
|
|
17
|
+
*/
|
|
18
|
+
export interface WorkflowReporterOptions {
|
|
19
|
+
/**
|
|
20
|
+
* Maximum terminal width for output formatting (default: 80)
|
|
21
|
+
*/
|
|
22
|
+
maxWidth?: number;
|
|
23
|
+
/**
|
|
24
|
+
* Disable color output
|
|
25
|
+
*/
|
|
26
|
+
noColor?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Force non-TTY mode (use simple renderer)
|
|
29
|
+
* When true or when process.stdout.isTTY is false, disables spinners
|
|
30
|
+
* and uses plain-text output without ANSI escape codes
|
|
31
|
+
*/
|
|
32
|
+
nonTTY?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Enable verbose output mode (show spawn output inline in real-time)
|
|
35
|
+
* When enabled, spawn outputs are streamed with visual delimiters
|
|
36
|
+
*/
|
|
37
|
+
verbose?: boolean;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Summary data for the final dashboard display
|
|
41
|
+
*/
|
|
42
|
+
export interface DashboardSummary {
|
|
43
|
+
/** Number of failed spawns */
|
|
44
|
+
failedCount: number;
|
|
45
|
+
/** Number of successful spawns */
|
|
46
|
+
passedCount: number;
|
|
47
|
+
/** Per-phase breakdown */
|
|
48
|
+
phases: Array<{
|
|
49
|
+
duration: number;
|
|
50
|
+
failed: number;
|
|
51
|
+
name: string;
|
|
52
|
+
passed: number;
|
|
53
|
+
}>;
|
|
54
|
+
/** Absolute path to the session report file */
|
|
55
|
+
sessionReportPath?: string;
|
|
56
|
+
/** Total duration in milliseconds */
|
|
57
|
+
totalDuration: number;
|
|
58
|
+
/** Total number of spawns across all phases */
|
|
59
|
+
totalSpawns: number;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* WorkflowReporter provides real-time structured progress visualization
|
|
63
|
+
* of multi-layer parallel execution using listr2.
|
|
64
|
+
*
|
|
65
|
+
* Usage:
|
|
66
|
+
* ```typescript
|
|
67
|
+
* const reporter = new WorkflowReporter({ verbose: false })
|
|
68
|
+
* orchestrator.setCallbacks(reporter.getCallbacks())
|
|
69
|
+
* // ... workflow execution ...
|
|
70
|
+
* reporter.dispose()
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare class WorkflowReporter {
|
|
74
|
+
private currentPhaseIndex;
|
|
75
|
+
private currentPhaseName;
|
|
76
|
+
private disposed;
|
|
77
|
+
private readonly options;
|
|
78
|
+
private phaseOrder;
|
|
79
|
+
private phases;
|
|
80
|
+
private listrInstance;
|
|
81
|
+
private listrPromise;
|
|
82
|
+
private phaseTransitionChain;
|
|
83
|
+
private phaseTaskResolver;
|
|
84
|
+
private phaseTaskOutput;
|
|
85
|
+
private phaseTaskTitle;
|
|
86
|
+
private currentTaskPhase;
|
|
87
|
+
private pendingTitle;
|
|
88
|
+
private pendingOutput;
|
|
89
|
+
private tickerInterval;
|
|
90
|
+
private tickerFrame;
|
|
91
|
+
private static readonly SPINNER_FRAMES;
|
|
92
|
+
constructor(options?: WorkflowReporterOptions);
|
|
93
|
+
/**
|
|
94
|
+
* Display final boxed dashboard with workflow summary (AC: #4)
|
|
95
|
+
*/
|
|
96
|
+
displayFinalDashboard(summary: DashboardSummary): void;
|
|
97
|
+
/**
|
|
98
|
+
* Dispose of resources and clean up listr2 instance
|
|
99
|
+
*/
|
|
100
|
+
dispose(): void;
|
|
101
|
+
/**
|
|
102
|
+
* Get the workflow callbacks interface for registering with orchestrator
|
|
103
|
+
*/
|
|
104
|
+
getCallbacks(): WorkflowCallbacks;
|
|
105
|
+
/**
|
|
106
|
+
* Get the configured max width for output formatting
|
|
107
|
+
*/
|
|
108
|
+
getMaxWidth(): number;
|
|
109
|
+
/**
|
|
110
|
+
* Check if reporter has been disposed
|
|
111
|
+
*/
|
|
112
|
+
isDisposed(): boolean;
|
|
113
|
+
/**
|
|
114
|
+
* Check if reporter is in TTY mode
|
|
115
|
+
*/
|
|
116
|
+
isTTYMode(): boolean;
|
|
117
|
+
/**
|
|
118
|
+
* Check if reporter is in verbose mode
|
|
119
|
+
*/
|
|
120
|
+
isVerboseMode(): boolean;
|
|
121
|
+
/**
|
|
122
|
+
* Wait for all listr2 tasks to complete (called after orchestrator finishes)
|
|
123
|
+
*/
|
|
124
|
+
waitForCompletion(): Promise<void>;
|
|
125
|
+
/**
|
|
126
|
+
* Clean up the current listr2 instance — resolve pending tasks and wait for
|
|
127
|
+
* the renderer to finish so the next phase gets a clean terminal.
|
|
128
|
+
*/
|
|
129
|
+
private cleanupCurrentListr;
|
|
130
|
+
/**
|
|
131
|
+
* Create a new listr2 instance for a phase. The phase gets a single top-level
|
|
132
|
+
* task with a spinner. Spawn progress is piped as task output (shown below the
|
|
133
|
+
* spinner line). The task resolves when handlePhaseComplete fires.
|
|
134
|
+
*
|
|
135
|
+
* Phase transitions are chained: old instance is fully cleaned up before the
|
|
136
|
+
* new one starts rendering, preventing two renderers fighting over stdout.
|
|
137
|
+
*/
|
|
138
|
+
private createPhaseListr;
|
|
139
|
+
/**
|
|
140
|
+
* Display a phase banner. In verbose mode, shows a full formatBox banner.
|
|
141
|
+
* In non-verbose TTY mode, the listr2 spinner is the only display (no banner).
|
|
142
|
+
* In non-TTY mode, shows a plain-text header.
|
|
143
|
+
*/
|
|
144
|
+
private displayPhaseBanner;
|
|
145
|
+
private displayPlainTextDashboard;
|
|
146
|
+
private displayRichDashboard;
|
|
147
|
+
private handlePhaseStart;
|
|
148
|
+
private handlePhaseComplete;
|
|
149
|
+
private handleLayerStart;
|
|
150
|
+
private handleLayerComplete;
|
|
151
|
+
private handleSpawnStart;
|
|
152
|
+
/**
|
|
153
|
+
* Live-tick the phase title — rotates through running spawns with elapsed time.
|
|
154
|
+
* Called every 2s by the ticker interval AND once immediately on spawn start.
|
|
155
|
+
*/
|
|
156
|
+
private stopTicker;
|
|
157
|
+
/**
|
|
158
|
+
* Build and render all spawn lines with per-item spinners + elapsed time.
|
|
159
|
+
* Called every second by the ticker interval AND once on spawn start/complete.
|
|
160
|
+
*/
|
|
161
|
+
private tickSpawns;
|
|
162
|
+
private handleSpawnComplete;
|
|
163
|
+
private handleSpawnOutput;
|
|
164
|
+
private handleError;
|
|
165
|
+
}
|