musubi-sdd 3.0.1 → 3.5.1
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/bin/musubi-change.js +623 -10
- package/bin/musubi-orchestrate.js +456 -0
- package/bin/musubi-trace.js +393 -0
- package/package.json +3 -2
- package/src/analyzers/impact-analyzer.js +682 -0
- package/src/integrations/cicd.js +782 -0
- package/src/integrations/documentation.js +740 -0
- package/src/integrations/examples.js +789 -0
- package/src/integrations/index.js +23 -0
- package/src/integrations/platforms.js +929 -0
- package/src/managers/delta-spec.js +484 -0
- package/src/monitoring/incident-manager.js +890 -0
- package/src/monitoring/index.js +633 -0
- package/src/monitoring/observability.js +938 -0
- package/src/monitoring/release-manager.js +622 -0
- package/src/orchestration/index.js +168 -0
- package/src/orchestration/orchestration-engine.js +409 -0
- package/src/orchestration/pattern-registry.js +319 -0
- package/src/orchestration/patterns/auto.js +386 -0
- package/src/orchestration/patterns/group-chat.js +395 -0
- package/src/orchestration/patterns/human-in-loop.js +506 -0
- package/src/orchestration/patterns/nested.js +322 -0
- package/src/orchestration/patterns/sequential.js +278 -0
- package/src/orchestration/patterns/swarm.js +395 -0
- package/src/orchestration/workflow-orchestrator.js +738 -0
- package/src/reporters/coverage-report.js +452 -0
- package/src/reporters/traceability-matrix-report.js +684 -0
- package/src/steering/advanced-validation.js +812 -0
- package/src/steering/auto-updater.js +670 -0
- package/src/steering/index.js +119 -0
- package/src/steering/quality-metrics.js +650 -0
- package/src/steering/template-constraints.js +789 -0
- package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +22 -0
- package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +21 -0
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +90 -28
- package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +32 -0
- package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +27 -0
- package/src/templates/agents/claude-code/skills/steering/SKILL.md +30 -0
- package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +21 -0
- package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +27 -0
- package/src/templates/agents/codex/AGENTS.md +36 -1
- package/src/templates/agents/cursor/AGENTS.md +36 -1
- package/src/templates/agents/gemini-cli/GEMINI.md +36 -1
- package/src/templates/agents/github-copilot/AGENTS.md +65 -1
- package/src/templates/agents/qwen-code/QWEN.md +36 -1
- package/src/templates/agents/windsurf/AGENTS.md +36 -1
- package/src/templates/shared/delta-spec-template.md +246 -0
- package/src/validators/delta-format.js +474 -0
- package/src/validators/traceability-validator.js +561 -0
|
@@ -0,0 +1,738 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WorkflowOrchestrator - Complex multi-pattern workflow execution
|
|
3
|
+
*
|
|
4
|
+
* Enables end-to-end workflows combining multiple orchestration patterns
|
|
5
|
+
* for complete SDD lifecycle (Research → Monitoring)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { PatternType, ExecutionContext, ExecutionStatus } = require('./orchestration-engine');
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Workflow step types
|
|
12
|
+
*/
|
|
13
|
+
const StepType = {
|
|
14
|
+
SKILL: 'skill', // Single skill execution
|
|
15
|
+
PATTERN: 'pattern', // Execute an orchestration pattern
|
|
16
|
+
CONDITIONAL: 'conditional', // Branch based on condition
|
|
17
|
+
PARALLEL: 'parallel', // Parallel steps
|
|
18
|
+
CHECKPOINT: 'checkpoint', // Save state checkpoint
|
|
19
|
+
HUMAN_GATE: 'human-gate' // Require human approval
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Workflow state
|
|
24
|
+
*/
|
|
25
|
+
const WorkflowState = {
|
|
26
|
+
PENDING: 'pending',
|
|
27
|
+
RUNNING: 'running',
|
|
28
|
+
PAUSED: 'paused',
|
|
29
|
+
COMPLETED: 'completed',
|
|
30
|
+
FAILED: 'failed',
|
|
31
|
+
CANCELLED: 'cancelled'
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* WorkflowOrchestrator - Complex workflow coordination
|
|
36
|
+
*/
|
|
37
|
+
class WorkflowOrchestrator {
|
|
38
|
+
constructor(engine, options = {}) {
|
|
39
|
+
this.engine = engine;
|
|
40
|
+
this.options = {
|
|
41
|
+
saveCheckpoints: options.saveCheckpoints || false,
|
|
42
|
+
checkpointStorage: options.checkpointStorage || new Map(),
|
|
43
|
+
maxRetries: options.maxRetries || 3,
|
|
44
|
+
retryDelay: options.retryDelay || 1000,
|
|
45
|
+
timeout: options.timeout || 300000, // 5 minutes default
|
|
46
|
+
...options
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
this.workflows = new Map();
|
|
50
|
+
this.activeExecutions = new Map();
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Register a workflow definition
|
|
55
|
+
* @param {string} name - Workflow name
|
|
56
|
+
* @param {object} definition - Workflow definition
|
|
57
|
+
*/
|
|
58
|
+
registerWorkflow(name, definition) {
|
|
59
|
+
if (!definition.steps || !Array.isArray(definition.steps)) {
|
|
60
|
+
throw new Error('Workflow must have steps array');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
this.workflows.set(name, {
|
|
64
|
+
name,
|
|
65
|
+
description: definition.description || '',
|
|
66
|
+
version: definition.version || '1.0.0',
|
|
67
|
+
steps: definition.steps,
|
|
68
|
+
inputs: definition.inputs || [],
|
|
69
|
+
outputs: definition.outputs || [],
|
|
70
|
+
onError: definition.onError || 'stop',
|
|
71
|
+
metadata: definition.metadata || {}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
return this;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Get workflow definition
|
|
79
|
+
* @param {string} name - Workflow name
|
|
80
|
+
* @returns {object|null} Workflow definition
|
|
81
|
+
*/
|
|
82
|
+
getWorkflow(name) {
|
|
83
|
+
return this.workflows.get(name) || null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* List all registered workflows
|
|
88
|
+
* @returns {string[]} Workflow names
|
|
89
|
+
*/
|
|
90
|
+
listWorkflows() {
|
|
91
|
+
return [...this.workflows.keys()];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Execute a workflow
|
|
96
|
+
* @param {string} workflowName - Name of workflow to execute
|
|
97
|
+
* @param {object} input - Initial input data
|
|
98
|
+
* @param {object} options - Execution options
|
|
99
|
+
* @returns {Promise<object>} Execution result
|
|
100
|
+
*/
|
|
101
|
+
async execute(workflowName, input = {}, options = {}) {
|
|
102
|
+
const workflow = this.workflows.get(workflowName);
|
|
103
|
+
if (!workflow) {
|
|
104
|
+
throw new Error(`Unknown workflow: ${workflowName}`);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const executionId = this._generateId();
|
|
108
|
+
const startTime = Date.now();
|
|
109
|
+
|
|
110
|
+
const execution = {
|
|
111
|
+
id: executionId,
|
|
112
|
+
workflow: workflowName,
|
|
113
|
+
state: WorkflowState.RUNNING,
|
|
114
|
+
currentStep: 0,
|
|
115
|
+
input,
|
|
116
|
+
output: {},
|
|
117
|
+
context: { ...input },
|
|
118
|
+
stepResults: [],
|
|
119
|
+
checkpoints: [],
|
|
120
|
+
startTime,
|
|
121
|
+
endTime: null,
|
|
122
|
+
error: null
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
this.activeExecutions.set(executionId, execution);
|
|
126
|
+
|
|
127
|
+
this.engine.emit('workflowStarted', {
|
|
128
|
+
executionId,
|
|
129
|
+
workflow: workflowName,
|
|
130
|
+
input
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
try {
|
|
134
|
+
// Execute each step
|
|
135
|
+
for (let i = 0; i < workflow.steps.length; i++) {
|
|
136
|
+
execution.currentStep = i;
|
|
137
|
+
const step = workflow.steps[i];
|
|
138
|
+
|
|
139
|
+
this.engine.emit('workflowStepStarted', {
|
|
140
|
+
executionId,
|
|
141
|
+
stepIndex: i,
|
|
142
|
+
step: step.name || step.type,
|
|
143
|
+
totalSteps: workflow.steps.length
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
const stepResult = await this._executeStep(step, execution, options);
|
|
147
|
+
|
|
148
|
+
execution.stepResults.push({
|
|
149
|
+
step: step.name || `step-${i}`,
|
|
150
|
+
type: step.type,
|
|
151
|
+
status: stepResult.status,
|
|
152
|
+
output: stepResult.output,
|
|
153
|
+
duration: stepResult.duration
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// Merge step output into context
|
|
157
|
+
if (stepResult.output) {
|
|
158
|
+
execution.context = {
|
|
159
|
+
...execution.context,
|
|
160
|
+
...stepResult.output,
|
|
161
|
+
[`step_${i}_result`]: stepResult.output
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
this.engine.emit('workflowStepCompleted', {
|
|
166
|
+
executionId,
|
|
167
|
+
stepIndex: i,
|
|
168
|
+
step: step.name || step.type,
|
|
169
|
+
result: stepResult
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Handle step failure
|
|
173
|
+
if (stepResult.status === ExecutionStatus.FAILED) {
|
|
174
|
+
if (workflow.onError === 'stop') {
|
|
175
|
+
throw new Error(`Step ${step.name || i} failed: ${stepResult.error}`);
|
|
176
|
+
}
|
|
177
|
+
// Continue on error
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Check if execution was cancelled or paused
|
|
181
|
+
if (execution.state === WorkflowState.CANCELLED) {
|
|
182
|
+
break;
|
|
183
|
+
}
|
|
184
|
+
if (execution.state === WorkflowState.PAUSED) {
|
|
185
|
+
// Save checkpoint and wait
|
|
186
|
+
await this._saveCheckpoint(execution);
|
|
187
|
+
return {
|
|
188
|
+
executionId,
|
|
189
|
+
state: WorkflowState.PAUSED,
|
|
190
|
+
resumeFrom: i + 1,
|
|
191
|
+
context: execution.context
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Workflow completed
|
|
197
|
+
execution.state = WorkflowState.COMPLETED;
|
|
198
|
+
execution.endTime = Date.now();
|
|
199
|
+
execution.output = this._extractOutputs(execution.context, workflow.outputs);
|
|
200
|
+
|
|
201
|
+
const result = {
|
|
202
|
+
executionId,
|
|
203
|
+
workflow: workflowName,
|
|
204
|
+
state: WorkflowState.COMPLETED,
|
|
205
|
+
output: execution.output,
|
|
206
|
+
stepResults: execution.stepResults,
|
|
207
|
+
duration: execution.endTime - startTime,
|
|
208
|
+
summary: this._createSummary(execution)
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
this.engine.emit('workflowCompleted', {
|
|
212
|
+
executionId,
|
|
213
|
+
result
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
return result;
|
|
217
|
+
|
|
218
|
+
} catch (error) {
|
|
219
|
+
execution.state = WorkflowState.FAILED;
|
|
220
|
+
execution.endTime = Date.now();
|
|
221
|
+
execution.error = error.message;
|
|
222
|
+
|
|
223
|
+
this.engine.emit('workflowFailed', {
|
|
224
|
+
executionId,
|
|
225
|
+
error,
|
|
226
|
+
stepResults: execution.stepResults
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
throw error;
|
|
230
|
+
} finally {
|
|
231
|
+
this.activeExecutions.delete(executionId);
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Execute a single workflow step
|
|
237
|
+
* @private
|
|
238
|
+
*/
|
|
239
|
+
async _executeStep(step, execution, options) {
|
|
240
|
+
const startTime = Date.now();
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
let output;
|
|
244
|
+
|
|
245
|
+
switch (step.type) {
|
|
246
|
+
case StepType.SKILL:
|
|
247
|
+
output = await this._executeSkillStep(step, execution);
|
|
248
|
+
break;
|
|
249
|
+
|
|
250
|
+
case StepType.PATTERN:
|
|
251
|
+
output = await this._executePatternStep(step, execution);
|
|
252
|
+
break;
|
|
253
|
+
|
|
254
|
+
case StepType.CONDITIONAL:
|
|
255
|
+
output = await this._executeConditionalStep(step, execution);
|
|
256
|
+
break;
|
|
257
|
+
|
|
258
|
+
case StepType.PARALLEL:
|
|
259
|
+
output = await this._executeParallelStep(step, execution);
|
|
260
|
+
break;
|
|
261
|
+
|
|
262
|
+
case StepType.CHECKPOINT:
|
|
263
|
+
output = await this._executeCheckpointStep(step, execution);
|
|
264
|
+
break;
|
|
265
|
+
|
|
266
|
+
case StepType.HUMAN_GATE:
|
|
267
|
+
output = await this._executeHumanGateStep(step, execution, options);
|
|
268
|
+
break;
|
|
269
|
+
|
|
270
|
+
default:
|
|
271
|
+
throw new Error(`Unknown step type: ${step.type}`);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
status: ExecutionStatus.COMPLETED,
|
|
276
|
+
output,
|
|
277
|
+
duration: Date.now() - startTime
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
} catch (error) {
|
|
281
|
+
return {
|
|
282
|
+
status: ExecutionStatus.FAILED,
|
|
283
|
+
error: error.message,
|
|
284
|
+
duration: Date.now() - startTime
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Execute skill step
|
|
291
|
+
* @private
|
|
292
|
+
*/
|
|
293
|
+
async _executeSkillStep(step, execution) {
|
|
294
|
+
const input = this._resolveInput(step.input, execution.context);
|
|
295
|
+
const skill = this.engine.getSkill(step.skill);
|
|
296
|
+
|
|
297
|
+
if (!skill) {
|
|
298
|
+
throw new Error(`Unknown skill: ${step.skill}`);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const parentContext = new ExecutionContext({
|
|
302
|
+
task: `Workflow skill: ${step.skill}`,
|
|
303
|
+
skill: step.skill,
|
|
304
|
+
input
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
return await this.engine.executeSkill(step.skill, input, parentContext);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Execute pattern step
|
|
312
|
+
* @private
|
|
313
|
+
*/
|
|
314
|
+
async _executePatternStep(step, execution) {
|
|
315
|
+
const input = this._resolveInput(step.input, execution.context);
|
|
316
|
+
|
|
317
|
+
const context = await this.engine.execute(step.pattern, {
|
|
318
|
+
input: { ...input, ...step.config }
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
return context.output;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Execute conditional step
|
|
326
|
+
* @private
|
|
327
|
+
*/
|
|
328
|
+
async _executeConditionalStep(step, execution) {
|
|
329
|
+
const condition = this._evaluateCondition(step.condition, execution.context);
|
|
330
|
+
|
|
331
|
+
const branchSteps = condition ? step.then : step.else;
|
|
332
|
+
|
|
333
|
+
if (branchSteps && branchSteps.length > 0) {
|
|
334
|
+
for (const branchStep of branchSteps) {
|
|
335
|
+
const result = await this._executeStep(branchStep, execution, {});
|
|
336
|
+
if (result.status === ExecutionStatus.FAILED) {
|
|
337
|
+
throw new Error(`Conditional branch failed: ${result.error}`);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return { branch: condition ? 'then' : 'else' };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/**
|
|
346
|
+
* Execute parallel step
|
|
347
|
+
* @private
|
|
348
|
+
*/
|
|
349
|
+
async _executeParallelStep(step, execution) {
|
|
350
|
+
const results = await Promise.allSettled(
|
|
351
|
+
step.steps.map(subStep =>
|
|
352
|
+
this._executeStep(subStep, execution, {})
|
|
353
|
+
)
|
|
354
|
+
);
|
|
355
|
+
|
|
356
|
+
const outputs = results.map((r, i) => ({
|
|
357
|
+
step: step.steps[i].name || `parallel-${i}`,
|
|
358
|
+
status: r.status === 'fulfilled' ? r.value.status : ExecutionStatus.FAILED,
|
|
359
|
+
output: r.status === 'fulfilled' ? r.value.output : null,
|
|
360
|
+
error: r.status === 'rejected' ? r.reason.message : null
|
|
361
|
+
}));
|
|
362
|
+
|
|
363
|
+
return { parallelResults: outputs };
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Execute checkpoint step
|
|
368
|
+
* @private
|
|
369
|
+
*/
|
|
370
|
+
async _executeCheckpointStep(step, execution) {
|
|
371
|
+
const checkpoint = {
|
|
372
|
+
id: this._generateId(),
|
|
373
|
+
name: step.name || 'checkpoint',
|
|
374
|
+
timestamp: Date.now(),
|
|
375
|
+
context: { ...execution.context }
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
execution.checkpoints.push(checkpoint);
|
|
379
|
+
|
|
380
|
+
if (this.options.saveCheckpoints) {
|
|
381
|
+
this.options.checkpointStorage.set(checkpoint.id, checkpoint);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
return { checkpointId: checkpoint.id };
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
/**
|
|
388
|
+
* Execute human gate step
|
|
389
|
+
* @private
|
|
390
|
+
*/
|
|
391
|
+
async _executeHumanGateStep(step, execution, options) {
|
|
392
|
+
if (!options.humanGate) {
|
|
393
|
+
// Auto-approve if no human gate configured
|
|
394
|
+
return { approved: true, autoApproved: true };
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
const question = this._resolveTemplate(step.question, execution.context);
|
|
398
|
+
const response = await options.humanGate.request(question, execution.context);
|
|
399
|
+
|
|
400
|
+
if (!response.approved) {
|
|
401
|
+
throw new Error(`Human gate rejected: ${response.feedback || 'No reason provided'}`);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
return response;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* Resolve input with context values
|
|
409
|
+
* @private
|
|
410
|
+
*/
|
|
411
|
+
_resolveInput(input, context) {
|
|
412
|
+
if (!input) return context;
|
|
413
|
+
if (typeof input === 'function') return input(context);
|
|
414
|
+
|
|
415
|
+
const resolved = {};
|
|
416
|
+
for (const [key, value] of Object.entries(input)) {
|
|
417
|
+
if (typeof value === 'string' && value.startsWith('$')) {
|
|
418
|
+
// Reference to context value
|
|
419
|
+
const path = value.slice(1);
|
|
420
|
+
resolved[key] = this._getValueByPath(context, path);
|
|
421
|
+
} else {
|
|
422
|
+
resolved[key] = value;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
return { ...context, ...resolved };
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/**
|
|
429
|
+
* Get value from object by dot path
|
|
430
|
+
* @private
|
|
431
|
+
*/
|
|
432
|
+
_getValueByPath(obj, path) {
|
|
433
|
+
return path.split('.').reduce((o, k) => (o || {})[k], obj);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Evaluate condition expression
|
|
438
|
+
* @private
|
|
439
|
+
*/
|
|
440
|
+
_evaluateCondition(condition, context) {
|
|
441
|
+
if (typeof condition === 'function') {
|
|
442
|
+
return condition(context);
|
|
443
|
+
}
|
|
444
|
+
if (typeof condition === 'string') {
|
|
445
|
+
// Simple path check
|
|
446
|
+
return !!this._getValueByPath(context, condition);
|
|
447
|
+
}
|
|
448
|
+
return !!condition;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Resolve template string with context values
|
|
453
|
+
* @private
|
|
454
|
+
*/
|
|
455
|
+
_resolveTemplate(template, context) {
|
|
456
|
+
if (typeof template !== 'string') return template;
|
|
457
|
+
return template.replace(/\$\{([^}]+)\}/g, (_, path) =>
|
|
458
|
+
this._getValueByPath(context, path) || ''
|
|
459
|
+
);
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
/**
|
|
463
|
+
* Extract specified outputs from context
|
|
464
|
+
* @private
|
|
465
|
+
*/
|
|
466
|
+
_extractOutputs(context, outputs) {
|
|
467
|
+
if (!outputs || outputs.length === 0) return context;
|
|
468
|
+
|
|
469
|
+
const result = {};
|
|
470
|
+
for (const output of outputs) {
|
|
471
|
+
result[output] = context[output];
|
|
472
|
+
}
|
|
473
|
+
return result;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Create execution summary
|
|
478
|
+
* @private
|
|
479
|
+
*/
|
|
480
|
+
_createSummary(execution) {
|
|
481
|
+
const completed = execution.stepResults.filter(s => s.status === ExecutionStatus.COMPLETED).length;
|
|
482
|
+
const failed = execution.stepResults.filter(s => s.status === ExecutionStatus.FAILED).length;
|
|
483
|
+
const total = execution.stepResults.length;
|
|
484
|
+
|
|
485
|
+
return {
|
|
486
|
+
totalSteps: total,
|
|
487
|
+
completedSteps: completed,
|
|
488
|
+
failedSteps: failed,
|
|
489
|
+
successRate: total > 0 ? (completed / total * 100).toFixed(1) + '%' : '0%',
|
|
490
|
+
duration: execution.endTime - execution.startTime,
|
|
491
|
+
checkpoints: execution.checkpoints.length
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
/**
|
|
496
|
+
* Save checkpoint
|
|
497
|
+
* @private
|
|
498
|
+
*/
|
|
499
|
+
async _saveCheckpoint(execution) {
|
|
500
|
+
if (this.options.saveCheckpoints) {
|
|
501
|
+
const checkpoint = {
|
|
502
|
+
id: this._generateId(),
|
|
503
|
+
executionId: execution.id,
|
|
504
|
+
workflow: execution.workflow,
|
|
505
|
+
currentStep: execution.currentStep,
|
|
506
|
+
context: execution.context,
|
|
507
|
+
timestamp: Date.now()
|
|
508
|
+
};
|
|
509
|
+
this.options.checkpointStorage.set(checkpoint.id, checkpoint);
|
|
510
|
+
return checkpoint;
|
|
511
|
+
}
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/**
|
|
516
|
+
* Resume workflow from checkpoint
|
|
517
|
+
* @param {string} checkpointId - Checkpoint ID to resume from
|
|
518
|
+
* @param {object} options - Execution options
|
|
519
|
+
*/
|
|
520
|
+
async resumeFromCheckpoint(checkpointId, options = {}) {
|
|
521
|
+
const checkpoint = this.options.checkpointStorage.get(checkpointId);
|
|
522
|
+
if (!checkpoint) {
|
|
523
|
+
throw new Error(`Checkpoint not found: ${checkpointId}`);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
const workflow = this.workflows.get(checkpoint.workflow);
|
|
527
|
+
if (!workflow) {
|
|
528
|
+
throw new Error(`Workflow not found: ${checkpoint.workflow}`);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
// Resume execution from checkpoint
|
|
532
|
+
const remainingSteps = workflow.steps.slice(checkpoint.currentStep);
|
|
533
|
+
const resumedWorkflow = {
|
|
534
|
+
...workflow,
|
|
535
|
+
steps: remainingSteps
|
|
536
|
+
};
|
|
537
|
+
|
|
538
|
+
// Temporarily register resumed workflow
|
|
539
|
+
const tempName = `${checkpoint.workflow}_resumed_${checkpointId}`;
|
|
540
|
+
this.registerWorkflow(tempName, resumedWorkflow);
|
|
541
|
+
|
|
542
|
+
try {
|
|
543
|
+
return await this.execute(tempName, checkpoint.context, options);
|
|
544
|
+
} finally {
|
|
545
|
+
this.workflows.delete(tempName);
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
/**
|
|
550
|
+
* Pause an active execution
|
|
551
|
+
* @param {string} executionId - Execution ID to pause
|
|
552
|
+
*/
|
|
553
|
+
pause(executionId) {
|
|
554
|
+
const execution = this.activeExecutions.get(executionId);
|
|
555
|
+
if (execution && execution.state === WorkflowState.RUNNING) {
|
|
556
|
+
execution.state = WorkflowState.PAUSED;
|
|
557
|
+
return true;
|
|
558
|
+
}
|
|
559
|
+
return false;
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
/**
|
|
563
|
+
* Cancel an active execution
|
|
564
|
+
* @param {string} executionId - Execution ID to cancel
|
|
565
|
+
*/
|
|
566
|
+
cancel(executionId) {
|
|
567
|
+
const execution = this.activeExecutions.get(executionId);
|
|
568
|
+
if (execution && execution.state === WorkflowState.RUNNING) {
|
|
569
|
+
execution.state = WorkflowState.CANCELLED;
|
|
570
|
+
return true;
|
|
571
|
+
}
|
|
572
|
+
return false;
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/**
|
|
576
|
+
* Generate unique ID
|
|
577
|
+
* @private
|
|
578
|
+
*/
|
|
579
|
+
_generateId() {
|
|
580
|
+
return `wf-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* Pre-defined SDD workflow templates
|
|
586
|
+
*/
|
|
587
|
+
const SDDWorkflowTemplates = {
|
|
588
|
+
/**
|
|
589
|
+
* Simple feature workflow: Requirements → Design → Tasks
|
|
590
|
+
*/
|
|
591
|
+
SIMPLE_FEATURE: {
|
|
592
|
+
name: 'simple-feature',
|
|
593
|
+
description: 'Simple feature development workflow',
|
|
594
|
+
steps: [
|
|
595
|
+
{
|
|
596
|
+
name: 'requirements',
|
|
597
|
+
type: StepType.SKILL,
|
|
598
|
+
skill: 'requirements-analyst',
|
|
599
|
+
input: { feature: '$featureName' }
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
name: 'design',
|
|
603
|
+
type: StepType.SKILL,
|
|
604
|
+
skill: 'software-architect',
|
|
605
|
+
input: { requirements: '$step_0_result' }
|
|
606
|
+
},
|
|
607
|
+
{
|
|
608
|
+
name: 'tasks',
|
|
609
|
+
type: StepType.SKILL,
|
|
610
|
+
skill: 'task-planner',
|
|
611
|
+
input: { design: '$step_1_result' }
|
|
612
|
+
}
|
|
613
|
+
],
|
|
614
|
+
outputs: ['requirements', 'design', 'tasks']
|
|
615
|
+
},
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Full SDD lifecycle workflow
|
|
619
|
+
*/
|
|
620
|
+
FULL_SDD: {
|
|
621
|
+
name: 'full-sdd',
|
|
622
|
+
description: 'Complete SDD lifecycle from research to deployment',
|
|
623
|
+
steps: [
|
|
624
|
+
{
|
|
625
|
+
name: 'research',
|
|
626
|
+
type: StepType.PATTERN,
|
|
627
|
+
pattern: PatternType.AUTO,
|
|
628
|
+
input: { task: 'Research: $featureName' }
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
name: 'requirements',
|
|
632
|
+
type: StepType.SKILL,
|
|
633
|
+
skill: 'requirements-analyst'
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
name: 'review-requirements',
|
|
637
|
+
type: StepType.HUMAN_GATE,
|
|
638
|
+
question: 'Please review the requirements for ${featureName}'
|
|
639
|
+
},
|
|
640
|
+
{
|
|
641
|
+
name: 'design',
|
|
642
|
+
type: StepType.PATTERN,
|
|
643
|
+
pattern: PatternType.GROUP_CHAT,
|
|
644
|
+
config: {
|
|
645
|
+
participants: ['software-architect', 'security-reviewer', 'ux-designer'],
|
|
646
|
+
topic: 'Design review'
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
name: 'implementation',
|
|
651
|
+
type: StepType.PATTERN,
|
|
652
|
+
pattern: PatternType.SEQUENTIAL,
|
|
653
|
+
config: {
|
|
654
|
+
skills: ['code-generator', 'test-engineer']
|
|
655
|
+
}
|
|
656
|
+
},
|
|
657
|
+
{
|
|
658
|
+
name: 'validation',
|
|
659
|
+
type: StepType.PATTERN,
|
|
660
|
+
pattern: PatternType.SWARM,
|
|
661
|
+
config: {
|
|
662
|
+
tasks: [
|
|
663
|
+
{ skill: 'code-reviewer' },
|
|
664
|
+
{ skill: 'security-reviewer' },
|
|
665
|
+
{ skill: 'accessibility-specialist' }
|
|
666
|
+
]
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
{
|
|
670
|
+
name: 'checkpoint',
|
|
671
|
+
type: StepType.CHECKPOINT,
|
|
672
|
+
name: 'pre-deployment'
|
|
673
|
+
},
|
|
674
|
+
{
|
|
675
|
+
name: 'deploy-approval',
|
|
676
|
+
type: StepType.HUMAN_GATE,
|
|
677
|
+
question: 'Approve deployment for ${featureName}?'
|
|
678
|
+
}
|
|
679
|
+
],
|
|
680
|
+
outputs: ['requirements', 'design', 'code', 'tests', 'reviews']
|
|
681
|
+
},
|
|
682
|
+
|
|
683
|
+
/**
|
|
684
|
+
* Code review workflow
|
|
685
|
+
*/
|
|
686
|
+
CODE_REVIEW: {
|
|
687
|
+
name: 'code-review',
|
|
688
|
+
description: 'Multi-perspective code review',
|
|
689
|
+
steps: [
|
|
690
|
+
{
|
|
691
|
+
name: 'parallel-review',
|
|
692
|
+
type: StepType.PARALLEL,
|
|
693
|
+
steps: [
|
|
694
|
+
{
|
|
695
|
+
type: StepType.SKILL,
|
|
696
|
+
skill: 'code-reviewer',
|
|
697
|
+
input: { focus: 'quality' }
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
type: StepType.SKILL,
|
|
701
|
+
skill: 'security-reviewer',
|
|
702
|
+
input: { focus: 'security' }
|
|
703
|
+
},
|
|
704
|
+
{
|
|
705
|
+
type: StepType.SKILL,
|
|
706
|
+
skill: 'performance-engineer',
|
|
707
|
+
input: { focus: 'performance' }
|
|
708
|
+
}
|
|
709
|
+
]
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
name: 'consolidate',
|
|
713
|
+
type: StepType.SKILL,
|
|
714
|
+
skill: 'documentation-writer',
|
|
715
|
+
input: { reviews: '$parallelResults' }
|
|
716
|
+
}
|
|
717
|
+
],
|
|
718
|
+
outputs: ['reviews', 'summary']
|
|
719
|
+
}
|
|
720
|
+
};
|
|
721
|
+
|
|
722
|
+
/**
|
|
723
|
+
* Create a workflow orchestrator
|
|
724
|
+
* @param {OrchestrationEngine} engine - Orchestration engine
|
|
725
|
+
* @param {object} options - Options
|
|
726
|
+
* @returns {WorkflowOrchestrator} Workflow orchestrator instance
|
|
727
|
+
*/
|
|
728
|
+
function createWorkflowOrchestrator(engine, options = {}) {
|
|
729
|
+
return new WorkflowOrchestrator(engine, options);
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
module.exports = {
|
|
733
|
+
WorkflowOrchestrator,
|
|
734
|
+
StepType,
|
|
735
|
+
WorkflowState,
|
|
736
|
+
SDDWorkflowTemplates,
|
|
737
|
+
createWorkflowOrchestrator
|
|
738
|
+
};
|