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.
Files changed (49) hide show
  1. package/bin/musubi-change.js +623 -10
  2. package/bin/musubi-orchestrate.js +456 -0
  3. package/bin/musubi-trace.js +393 -0
  4. package/package.json +3 -2
  5. package/src/analyzers/impact-analyzer.js +682 -0
  6. package/src/integrations/cicd.js +782 -0
  7. package/src/integrations/documentation.js +740 -0
  8. package/src/integrations/examples.js +789 -0
  9. package/src/integrations/index.js +23 -0
  10. package/src/integrations/platforms.js +929 -0
  11. package/src/managers/delta-spec.js +484 -0
  12. package/src/monitoring/incident-manager.js +890 -0
  13. package/src/monitoring/index.js +633 -0
  14. package/src/monitoring/observability.js +938 -0
  15. package/src/monitoring/release-manager.js +622 -0
  16. package/src/orchestration/index.js +168 -0
  17. package/src/orchestration/orchestration-engine.js +409 -0
  18. package/src/orchestration/pattern-registry.js +319 -0
  19. package/src/orchestration/patterns/auto.js +386 -0
  20. package/src/orchestration/patterns/group-chat.js +395 -0
  21. package/src/orchestration/patterns/human-in-loop.js +506 -0
  22. package/src/orchestration/patterns/nested.js +322 -0
  23. package/src/orchestration/patterns/sequential.js +278 -0
  24. package/src/orchestration/patterns/swarm.js +395 -0
  25. package/src/orchestration/workflow-orchestrator.js +738 -0
  26. package/src/reporters/coverage-report.js +452 -0
  27. package/src/reporters/traceability-matrix-report.js +684 -0
  28. package/src/steering/advanced-validation.js +812 -0
  29. package/src/steering/auto-updater.js +670 -0
  30. package/src/steering/index.js +119 -0
  31. package/src/steering/quality-metrics.js +650 -0
  32. package/src/steering/template-constraints.js +789 -0
  33. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +22 -0
  34. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +21 -0
  35. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +90 -28
  36. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +32 -0
  37. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +27 -0
  38. package/src/templates/agents/claude-code/skills/steering/SKILL.md +30 -0
  39. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +21 -0
  40. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +27 -0
  41. package/src/templates/agents/codex/AGENTS.md +36 -1
  42. package/src/templates/agents/cursor/AGENTS.md +36 -1
  43. package/src/templates/agents/gemini-cli/GEMINI.md +36 -1
  44. package/src/templates/agents/github-copilot/AGENTS.md +65 -1
  45. package/src/templates/agents/qwen-code/QWEN.md +36 -1
  46. package/src/templates/agents/windsurf/AGENTS.md +36 -1
  47. package/src/templates/shared/delta-spec-template.md +246 -0
  48. package/src/validators/delta-format.js +474 -0
  49. package/src/validators/traceability-validator.js +561 -0
@@ -0,0 +1,506 @@
1
+ /**
2
+ * HumanInLoopPattern - Validation gates with human interaction
3
+ *
4
+ * Enables human validation at key points in the workflow.
5
+ * Supports approval gates, feedback collection, and decision points.
6
+ */
7
+
8
+ const { BasePattern } = require('../pattern-registry');
9
+ const { PatternType, ExecutionContext, ExecutionStatus } = require('../orchestration-engine');
10
+
11
+ /**
12
+ * Gate type
13
+ */
14
+ const GateType = {
15
+ APPROVAL: 'approval', // Yes/No approval
16
+ REVIEW: 'review', // Review with feedback
17
+ DECISION: 'decision', // Multiple choice decision
18
+ CONFIRMATION: 'confirmation' // Simple confirmation
19
+ };
20
+
21
+ /**
22
+ * Gate result
23
+ */
24
+ const GateResult = {
25
+ APPROVED: 'approved',
26
+ REJECTED: 'rejected',
27
+ NEEDS_CHANGES: 'needs-changes',
28
+ TIMEOUT: 'timeout',
29
+ SKIPPED: 'skipped'
30
+ };
31
+
32
+ /**
33
+ * HumanInLoopPattern - Human validation gates
34
+ */
35
+ class HumanInLoopPattern extends BasePattern {
36
+ constructor(options = {}) {
37
+ super({
38
+ name: PatternType.HUMAN_IN_LOOP,
39
+ type: PatternType.HUMAN_IN_LOOP,
40
+ description: 'Enable human validation gates at key workflow points',
41
+ version: '1.0.0',
42
+ tags: ['validation', 'human', 'approval', 'gate'],
43
+ useCases: [
44
+ 'Quality gates',
45
+ 'Approval workflows',
46
+ 'Decision points',
47
+ 'Review processes'
48
+ ],
49
+ complexity: 'medium',
50
+ supportsParallel: false,
51
+ requiresHuman: true
52
+ });
53
+
54
+ this.options = {
55
+ timeout: options.timeout || 300000, // 5 minutes
56
+ autoApproveOnTimeout: options.autoApproveOnTimeout || false,
57
+ collectFeedback: options.collectFeedback || true,
58
+ notifyOnGate: options.notifyOnGate || true,
59
+ ...options
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Validate the execution context
65
+ * @param {ExecutionContext} context - Execution context
66
+ * @param {OrchestrationEngine} engine - Orchestration engine
67
+ * @returns {object} Validation result
68
+ */
69
+ validate(context, engine) {
70
+ const errors = [];
71
+ const input = context.input;
72
+
73
+ // Check for workflow definition
74
+ if (!input.workflow || !Array.isArray(input.workflow)) {
75
+ errors.push('HumanInLoop pattern requires input.workflow array');
76
+ } else if (input.workflow.length === 0) {
77
+ errors.push('HumanInLoop pattern requires at least one workflow step');
78
+ } else {
79
+ // Validate each step
80
+ for (let i = 0; i < input.workflow.length; i++) {
81
+ const step = input.workflow[i];
82
+
83
+ if (!step.skill && !step.gate) {
84
+ errors.push(`Workflow step ${i + 1} requires either skill or gate`);
85
+ }
86
+
87
+ if (step.skill && !engine.getSkill(step.skill)) {
88
+ errors.push(`Unknown skill in workflow: ${step.skill}`);
89
+ }
90
+
91
+ if (step.gate && !Object.values(GateType).includes(step.gate)) {
92
+ errors.push(`Invalid gate type: ${step.gate}`);
93
+ }
94
+ }
95
+ }
96
+
97
+ return {
98
+ valid: errors.length === 0,
99
+ errors
100
+ };
101
+ }
102
+
103
+ /**
104
+ * Execute human-in-loop pattern
105
+ * @param {ExecutionContext} context - Execution context
106
+ * @param {OrchestrationEngine} engine - Orchestration engine
107
+ * @returns {Promise<object>} Execution result
108
+ */
109
+ async execute(context, engine) {
110
+ const validation = this.validate(context, engine);
111
+ if (!validation.valid) {
112
+ throw new Error(`Validation failed: ${validation.errors.join(', ')}`);
113
+ }
114
+
115
+ const { workflow, initialInput = {} } = context.input;
116
+ const results = [];
117
+ let currentInput = { ...initialInput };
118
+ let aborted = false;
119
+
120
+ engine.emit('humanInLoopStarted', {
121
+ context,
122
+ workflow: workflow.map(s => s.skill || s.gate),
123
+ totalSteps: workflow.length
124
+ });
125
+
126
+ for (let i = 0; i < workflow.length && !aborted; i++) {
127
+ const step = workflow[i];
128
+ const stepNumber = i + 1;
129
+
130
+ engine.emit('humanInLoopStepStarted', {
131
+ context,
132
+ step,
133
+ stepNumber,
134
+ totalSteps: workflow.length
135
+ });
136
+
137
+ try {
138
+ let stepResult;
139
+
140
+ if (step.skill) {
141
+ // Execute skill
142
+ stepResult = await this._executeSkillStep(
143
+ step,
144
+ currentInput,
145
+ context,
146
+ engine,
147
+ stepNumber
148
+ );
149
+ } else if (step.gate) {
150
+ // Execute gate
151
+ stepResult = await this._executeGateStep(
152
+ step,
153
+ currentInput,
154
+ context,
155
+ engine,
156
+ stepNumber
157
+ );
158
+ }
159
+
160
+ results.push({
161
+ step: stepNumber,
162
+ type: step.skill ? 'skill' : 'gate',
163
+ name: step.skill || step.gate,
164
+ status: stepResult.status,
165
+ result: stepResult
166
+ });
167
+
168
+ // Check if gate rejected
169
+ if (step.gate && stepResult.result === GateResult.REJECTED) {
170
+ aborted = true;
171
+ engine.emit('humanInLoopAborted', {
172
+ context,
173
+ step,
174
+ stepNumber,
175
+ reason: 'Gate rejected'
176
+ });
177
+ } else if (step.gate && stepResult.result === GateResult.NEEDS_CHANGES) {
178
+ // Could implement retry logic here
179
+ if (step.retryOnNeededChanges) {
180
+ // Go back to previous skill step
181
+ const prevSkillIndex = this._findPreviousSkillIndex(workflow, i);
182
+ if (prevSkillIndex >= 0) {
183
+ i = prevSkillIndex - 1; // Will increment in loop
184
+ currentInput = {
185
+ ...currentInput,
186
+ feedback: stepResult.feedback,
187
+ needsChanges: true
188
+ };
189
+ continue;
190
+ }
191
+ }
192
+ }
193
+
194
+ // Update input for next step
195
+ if (stepResult.output) {
196
+ currentInput = { ...currentInput, ...stepResult.output };
197
+ }
198
+
199
+ engine.emit('humanInLoopStepCompleted', {
200
+ context,
201
+ step,
202
+ stepNumber,
203
+ result: stepResult
204
+ });
205
+
206
+ } catch (error) {
207
+ results.push({
208
+ step: stepNumber,
209
+ type: step.skill ? 'skill' : 'gate',
210
+ name: step.skill || step.gate,
211
+ status: ExecutionStatus.FAILED,
212
+ error: error.message
213
+ });
214
+
215
+ engine.emit('humanInLoopStepFailed', {
216
+ context,
217
+ step,
218
+ stepNumber,
219
+ error
220
+ });
221
+
222
+ if (!step.continueOnError) {
223
+ aborted = true;
224
+ }
225
+ }
226
+ }
227
+
228
+ const summary = this._createSummary(results, aborted);
229
+
230
+ engine.emit('humanInLoopCompleted', {
231
+ context,
232
+ results,
233
+ summary,
234
+ aborted
235
+ });
236
+
237
+ return {
238
+ results,
239
+ summary,
240
+ aborted,
241
+ finalOutput: currentInput
242
+ };
243
+ }
244
+
245
+ /**
246
+ * Execute a skill step
247
+ * @private
248
+ */
249
+ async _executeSkillStep(step, input, parentContext, engine, stepNumber) {
250
+ const stepContext = new ExecutionContext({
251
+ task: `HIL Step ${stepNumber}: ${step.skill}`,
252
+ skill: step.skill,
253
+ input: { ...input, ...step.input },
254
+ parentId: parentContext.id,
255
+ metadata: {
256
+ pattern: PatternType.HUMAN_IN_LOOP,
257
+ stepNumber,
258
+ stepType: 'skill'
259
+ }
260
+ });
261
+
262
+ parentContext.children.push(stepContext);
263
+ stepContext.start();
264
+
265
+ const output = await engine.executeSkill(step.skill, stepContext.input, parentContext);
266
+ stepContext.complete(output);
267
+
268
+ return {
269
+ status: ExecutionStatus.COMPLETED,
270
+ output
271
+ };
272
+ }
273
+
274
+ /**
275
+ * Execute a gate step
276
+ * @private
277
+ */
278
+ async _executeGateStep(step, input, parentContext, engine, stepNumber) {
279
+ const stepContext = new ExecutionContext({
280
+ task: `HIL Gate ${stepNumber}: ${step.gate}`,
281
+ skill: null,
282
+ input,
283
+ parentId: parentContext.id,
284
+ metadata: {
285
+ pattern: PatternType.HUMAN_IN_LOOP,
286
+ stepNumber,
287
+ stepType: 'gate',
288
+ gateType: step.gate
289
+ }
290
+ });
291
+
292
+ parentContext.children.push(stepContext);
293
+ stepContext.start();
294
+ stepContext.waitForHuman();
295
+
296
+ // Emit gate notification
297
+ if (this.options.notifyOnGate) {
298
+ engine.emit('humanInLoopGateReached', {
299
+ context: parentContext,
300
+ gate: step,
301
+ stepNumber,
302
+ input
303
+ });
304
+ }
305
+
306
+ // Build gate question
307
+ const question = this._buildGateQuestion(step, input);
308
+
309
+ // Request human validation
310
+ let response;
311
+ try {
312
+ response = await this._requestWithTimeout(
313
+ () => engine.requestHumanValidation(stepContext, question),
314
+ this.options.timeout
315
+ );
316
+ } catch (error) {
317
+ if (error.message.includes('timeout')) {
318
+ response = this.options.autoApproveOnTimeout
319
+ ? { approved: true, feedback: 'Auto-approved on timeout' }
320
+ : { approved: false, feedback: 'Timeout waiting for human response' };
321
+
322
+ stepContext.complete({
323
+ result: this.options.autoApproveOnTimeout
324
+ ? GateResult.APPROVED
325
+ : GateResult.TIMEOUT,
326
+ feedback: response.feedback
327
+ });
328
+
329
+ return {
330
+ status: ExecutionStatus.COMPLETED,
331
+ result: this.options.autoApproveOnTimeout
332
+ ? GateResult.APPROVED
333
+ : GateResult.TIMEOUT,
334
+ feedback: response.feedback
335
+ };
336
+ }
337
+ throw error;
338
+ }
339
+
340
+ // Process response
341
+ const gateResult = this._processGateResponse(step.gate, response);
342
+
343
+ stepContext.complete({
344
+ result: gateResult,
345
+ feedback: response.feedback,
346
+ response
347
+ });
348
+
349
+ return {
350
+ status: ExecutionStatus.COMPLETED,
351
+ result: gateResult,
352
+ feedback: response.feedback,
353
+ output: response.output
354
+ };
355
+ }
356
+
357
+ /**
358
+ * Request with timeout
359
+ * @private
360
+ */
361
+ _requestWithTimeout(fn, timeout) {
362
+ return new Promise((resolve, reject) => {
363
+ const timer = setTimeout(() => {
364
+ reject(new Error(`Gate timeout after ${timeout}ms`));
365
+ }, timeout);
366
+
367
+ Promise.resolve(fn())
368
+ .then(result => {
369
+ clearTimeout(timer);
370
+ resolve(result);
371
+ })
372
+ .catch(error => {
373
+ clearTimeout(timer);
374
+ reject(error);
375
+ });
376
+ });
377
+ }
378
+
379
+ /**
380
+ * Build gate question
381
+ * @private
382
+ */
383
+ _buildGateQuestion(step, input) {
384
+ const context = step.context || '';
385
+ const prompt = step.prompt || this._getDefaultPrompt(step.gate);
386
+
387
+ let question = prompt;
388
+ if (context) {
389
+ question = `${context}\n\n${prompt}`;
390
+ }
391
+
392
+ // Include relevant input data
393
+ if (step.showInput && input) {
394
+ question += `\n\nContext:\n${JSON.stringify(input, null, 2)}`;
395
+ }
396
+
397
+ return question;
398
+ }
399
+
400
+ /**
401
+ * Get default prompt for gate type
402
+ * @private
403
+ */
404
+ _getDefaultPrompt(gateType) {
405
+ switch (gateType) {
406
+ case GateType.APPROVAL:
407
+ return 'Do you approve this to proceed? (yes/no)';
408
+ case GateType.REVIEW:
409
+ return 'Please review the above and provide feedback.';
410
+ case GateType.DECISION:
411
+ return 'Please make a decision.';
412
+ case GateType.CONFIRMATION:
413
+ return 'Please confirm to continue.';
414
+ default:
415
+ return 'Please respond.';
416
+ }
417
+ }
418
+
419
+ /**
420
+ * Process gate response
421
+ * @private
422
+ */
423
+ _processGateResponse(gateType, response) {
424
+ if (response.approved === true) {
425
+ return GateResult.APPROVED;
426
+ }
427
+
428
+ if (response.approved === false) {
429
+ if (response.needsChanges) {
430
+ return GateResult.NEEDS_CHANGES;
431
+ }
432
+ return GateResult.REJECTED;
433
+ }
434
+
435
+ if (response.skipped) {
436
+ return GateResult.SKIPPED;
437
+ }
438
+
439
+ // Default based on gate type
440
+ switch (gateType) {
441
+ case GateType.CONFIRMATION:
442
+ return response.confirmed ? GateResult.APPROVED : GateResult.REJECTED;
443
+ default:
444
+ return GateResult.APPROVED; // Default to approved for review gates
445
+ }
446
+ }
447
+
448
+ /**
449
+ * Find previous skill index
450
+ * @private
451
+ */
452
+ _findPreviousSkillIndex(workflow, currentIndex) {
453
+ for (let i = currentIndex - 1; i >= 0; i--) {
454
+ if (workflow[i].skill) {
455
+ return i;
456
+ }
457
+ }
458
+ return -1;
459
+ }
460
+
461
+ /**
462
+ * Create execution summary
463
+ * @private
464
+ */
465
+ _createSummary(results, aborted) {
466
+ const skillSteps = results.filter(r => r.type === 'skill');
467
+ const gateSteps = results.filter(r => r.type === 'gate');
468
+
469
+ const approvedGates = gateSteps.filter(r =>
470
+ r.result?.result === GateResult.APPROVED
471
+ ).length;
472
+
473
+ const rejectedGates = gateSteps.filter(r =>
474
+ r.result?.result === GateResult.REJECTED
475
+ ).length;
476
+
477
+ return {
478
+ totalSteps: results.length,
479
+ skillSteps: skillSteps.length,
480
+ gateSteps: gateSteps.length,
481
+ approvedGates,
482
+ rejectedGates,
483
+ aborted,
484
+ completed: !aborted,
485
+ gateApprovalRate: gateSteps.length > 0
486
+ ? (approvedGates / gateSteps.length * 100).toFixed(1) + '%'
487
+ : 'N/A'
488
+ };
489
+ }
490
+ }
491
+
492
+ /**
493
+ * Create a human-in-loop pattern with custom options
494
+ * @param {object} options - Pattern options
495
+ * @returns {HumanInLoopPattern} HumanInLoop pattern instance
496
+ */
497
+ function createHumanInLoopPattern(options = {}) {
498
+ return new HumanInLoopPattern(options);
499
+ }
500
+
501
+ module.exports = {
502
+ HumanInLoopPattern,
503
+ GateType,
504
+ GateResult,
505
+ createHumanInLoopPattern
506
+ };