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,670 @@
1
+ /**
2
+ * Steering Auto-Update Module
3
+ *
4
+ * Automatically updates steering files based on agent work:
5
+ * - Detects project changes
6
+ * - Updates structure.md, tech.md, product.md
7
+ * - Maintains project.yml consistency
8
+ * - Supports domain-specific custom rules
9
+ */
10
+
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const { EventEmitter } = require('events');
14
+
15
+ /**
16
+ * Steering File Types
17
+ */
18
+ const SteeringFileType = {
19
+ STRUCTURE: 'structure',
20
+ TECH: 'tech',
21
+ PRODUCT: 'product',
22
+ PROJECT: 'project',
23
+ RULES: 'rules',
24
+ CUSTOM: 'custom'
25
+ };
26
+
27
+ /**
28
+ * Update Trigger Types
29
+ */
30
+ const UpdateTrigger = {
31
+ FILE_ADDED: 'file-added',
32
+ FILE_MODIFIED: 'file-modified',
33
+ FILE_DELETED: 'file-deleted',
34
+ DEPENDENCY_ADDED: 'dependency-added',
35
+ DEPENDENCY_REMOVED: 'dependency-removed',
36
+ CONFIG_CHANGED: 'config-changed',
37
+ MANUAL: 'manual'
38
+ };
39
+
40
+ /**
41
+ * Change detection for steering updates
42
+ */
43
+ class ChangeDetector {
44
+ constructor(options = {}) {
45
+ this.patterns = options.patterns || {
46
+ structure: [
47
+ /^src\//,
48
+ /^lib\//,
49
+ /^packages\//,
50
+ /^components\//
51
+ ],
52
+ tech: [
53
+ /package\.json$/,
54
+ /requirements\.txt$/,
55
+ /Gemfile$/,
56
+ /go\.mod$/,
57
+ /Cargo\.toml$/,
58
+ /\.config\.(js|ts|json)$/
59
+ ],
60
+ product: [
61
+ /README\.md$/,
62
+ /docs\//,
63
+ /\.env\.example$/
64
+ ]
65
+ };
66
+ }
67
+
68
+ /**
69
+ * Detect which steering files need updates based on changed files
70
+ */
71
+ detectAffectedSteering(changedFiles) {
72
+ const affected = new Set();
73
+
74
+ for (const file of changedFiles) {
75
+ for (const [steeringType, patterns] of Object.entries(this.patterns)) {
76
+ for (const pattern of patterns) {
77
+ if (pattern.test(file)) {
78
+ affected.add(steeringType);
79
+ break;
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ return [...affected];
86
+ }
87
+
88
+ /**
89
+ * Analyze file changes
90
+ */
91
+ analyzeChanges(changes) {
92
+ const analysis = {
93
+ addedFiles: [],
94
+ modifiedFiles: [],
95
+ deletedFiles: [],
96
+ addedDependencies: [],
97
+ removedDependencies: [],
98
+ affectedSteering: []
99
+ };
100
+
101
+ for (const change of changes) {
102
+ if (change.type === 'add') {
103
+ analysis.addedFiles.push(change.path);
104
+ } else if (change.type === 'modify') {
105
+ analysis.modifiedFiles.push(change.path);
106
+ } else if (change.type === 'delete') {
107
+ analysis.deletedFiles.push(change.path);
108
+ }
109
+ }
110
+
111
+ const allFiles = [
112
+ ...analysis.addedFiles,
113
+ ...analysis.modifiedFiles,
114
+ ...analysis.deletedFiles
115
+ ];
116
+
117
+ analysis.affectedSteering = this.detectAffectedSteering(allFiles);
118
+
119
+ return analysis;
120
+ }
121
+ }
122
+
123
+ /**
124
+ * Steering file updater
125
+ */
126
+ class SteeringUpdater {
127
+ constructor(options = {}) {
128
+ this.steeringDir = options.steeringDir || 'steering';
129
+ this.dryRun = options.dryRun || false;
130
+ this.backupEnabled = options.backup !== false;
131
+ }
132
+
133
+ /**
134
+ * Update structure.md based on project analysis
135
+ */
136
+ generateStructureUpdate(analysis) {
137
+ const updates = [];
138
+
139
+ // Detect new directories
140
+ const newDirs = new Set();
141
+ for (const file of analysis.addedFiles) {
142
+ const dir = path.dirname(file);
143
+ if (dir !== '.') {
144
+ newDirs.add(dir.split('/')[0]);
145
+ }
146
+ }
147
+
148
+ if (newDirs.size > 0) {
149
+ updates.push({
150
+ section: 'directories',
151
+ action: 'add',
152
+ content: [...newDirs].map(d => `- \`${d}/\` - [TODO: Add description]`)
153
+ });
154
+ }
155
+
156
+ // Detect removed directories
157
+ const removedDirs = new Set();
158
+ for (const file of analysis.deletedFiles) {
159
+ const dir = path.dirname(file);
160
+ if (dir !== '.') {
161
+ removedDirs.add(dir.split('/')[0]);
162
+ }
163
+ }
164
+
165
+ if (removedDirs.size > 0) {
166
+ updates.push({
167
+ section: 'directories',
168
+ action: 'review',
169
+ content: [...removedDirs].map(d => `- \`${d}/\` may need removal or update`)
170
+ });
171
+ }
172
+
173
+ return updates;
174
+ }
175
+
176
+ /**
177
+ * Update tech.md based on dependency changes
178
+ */
179
+ generateTechUpdate(analysis, packageJson = null) {
180
+ const updates = [];
181
+
182
+ if (packageJson) {
183
+ const deps = packageJson.dependencies || {};
184
+ const devDeps = packageJson.devDependencies || {};
185
+
186
+ updates.push({
187
+ section: 'dependencies',
188
+ action: 'sync',
189
+ content: {
190
+ runtime: Object.keys(deps),
191
+ development: Object.keys(devDeps)
192
+ }
193
+ });
194
+
195
+ // Detect framework
196
+ const frameworks = [];
197
+ if (deps.react || deps['react-dom']) frameworks.push('React');
198
+ if (deps.vue) frameworks.push('Vue.js');
199
+ if (deps.next) frameworks.push('Next.js');
200
+ if (deps.express) frameworks.push('Express');
201
+ if (deps.fastify) frameworks.push('Fastify');
202
+ if (deps.jest || devDeps.jest) frameworks.push('Jest');
203
+ if (deps.typescript || devDeps.typescript) frameworks.push('TypeScript');
204
+
205
+ if (frameworks.length > 0) {
206
+ updates.push({
207
+ section: 'frameworks',
208
+ action: 'update',
209
+ content: frameworks
210
+ });
211
+ }
212
+ }
213
+
214
+ return updates;
215
+ }
216
+
217
+ /**
218
+ * Update product.md based on README changes
219
+ */
220
+ generateProductUpdate(analysis, readme = null) {
221
+ const updates = [];
222
+
223
+ if (readme) {
224
+ // Extract title
225
+ const titleMatch = readme.match(/^#\s+(.+)$/m);
226
+ if (titleMatch) {
227
+ updates.push({
228
+ section: 'name',
229
+ action: 'sync',
230
+ content: titleMatch[1]
231
+ });
232
+ }
233
+
234
+ // Extract description
235
+ const descMatch = readme.match(/^#\s+.+\n\n(.+?)(?:\n\n|$)/s);
236
+ if (descMatch) {
237
+ updates.push({
238
+ section: 'description',
239
+ action: 'sync',
240
+ content: descMatch[1].trim()
241
+ });
242
+ }
243
+ }
244
+
245
+ return updates;
246
+ }
247
+
248
+ /**
249
+ * Apply updates to a steering file
250
+ */
251
+ applyUpdates(filePath, updates) {
252
+ const results = [];
253
+
254
+ for (const update of updates) {
255
+ results.push({
256
+ file: filePath,
257
+ section: update.section,
258
+ action: update.action,
259
+ applied: !this.dryRun,
260
+ content: update.content
261
+ });
262
+ }
263
+
264
+ return results;
265
+ }
266
+ }
267
+
268
+ /**
269
+ * Project.yml synchronizer
270
+ */
271
+ class ProjectYmlSync {
272
+ constructor(options = {}) {
273
+ this.projectYmlPath = options.path || 'steering/project.yml';
274
+ }
275
+
276
+ /**
277
+ * Parse project.yml content
278
+ */
279
+ parse(content) {
280
+ const data = {
281
+ name: '',
282
+ version: '',
283
+ description: '',
284
+ tech_stack: [],
285
+ features: [],
286
+ agents: []
287
+ };
288
+
289
+ // Simple YAML-like parsing
290
+ const lines = content.split('\n');
291
+ let currentKey = null;
292
+ let inList = false;
293
+
294
+ for (const line of lines) {
295
+ const trimmed = line.trim();
296
+
297
+ if (trimmed.startsWith('#') || trimmed === '') continue;
298
+
299
+ const keyMatch = line.match(/^(\w+):\s*(.*)$/);
300
+ if (keyMatch) {
301
+ currentKey = keyMatch[1];
302
+ const value = keyMatch[2].trim();
303
+
304
+ if (value && !value.startsWith('-')) {
305
+ data[currentKey] = value;
306
+ inList = false;
307
+ } else {
308
+ inList = true;
309
+ if (!Array.isArray(data[currentKey])) {
310
+ data[currentKey] = [];
311
+ }
312
+ }
313
+ } else if (trimmed.startsWith('-') && currentKey) {
314
+ const item = trimmed.slice(1).trim();
315
+ if (!Array.isArray(data[currentKey])) {
316
+ data[currentKey] = [];
317
+ }
318
+ data[currentKey].push(item);
319
+ }
320
+ }
321
+
322
+ return data;
323
+ }
324
+
325
+ /**
326
+ * Generate project.yml content
327
+ */
328
+ generate(data) {
329
+ let content = '';
330
+
331
+ content += `# Project Configuration\n`;
332
+ content += `# Auto-generated by MUSUBI Steering Auto-Update\n\n`;
333
+
334
+ if (data.name) content += `name: ${data.name}\n`;
335
+ if (data.version) content += `version: ${data.version}\n`;
336
+ if (data.description) content += `description: ${data.description}\n`;
337
+
338
+ if (data.tech_stack && data.tech_stack.length > 0) {
339
+ content += `\ntech_stack:\n`;
340
+ for (const tech of data.tech_stack) {
341
+ content += ` - ${tech}\n`;
342
+ }
343
+ }
344
+
345
+ if (data.features && data.features.length > 0) {
346
+ content += `\nfeatures:\n`;
347
+ for (const feature of data.features) {
348
+ content += ` - ${feature}\n`;
349
+ }
350
+ }
351
+
352
+ if (data.agents && data.agents.length > 0) {
353
+ content += `\nagents:\n`;
354
+ for (const agent of data.agents) {
355
+ content += ` - ${agent}\n`;
356
+ }
357
+ }
358
+
359
+ return content;
360
+ }
361
+
362
+ /**
363
+ * Sync project.yml with package.json
364
+ */
365
+ syncWithPackageJson(projectData, packageJson) {
366
+ const updated = { ...projectData };
367
+
368
+ if (packageJson.name) updated.name = packageJson.name;
369
+ if (packageJson.version) updated.version = packageJson.version;
370
+ if (packageJson.description) updated.description = packageJson.description;
371
+
372
+ // Sync tech stack from dependencies
373
+ const techStack = new Set(updated.tech_stack || []);
374
+ const deps = { ...packageJson.dependencies, ...packageJson.devDependencies };
375
+
376
+ const techMapping = {
377
+ 'react': 'React',
378
+ 'vue': 'Vue.js',
379
+ 'angular': 'Angular',
380
+ 'next': 'Next.js',
381
+ 'express': 'Express',
382
+ 'fastify': 'Fastify',
383
+ 'typescript': 'TypeScript',
384
+ 'jest': 'Jest',
385
+ 'mocha': 'Mocha',
386
+ 'webpack': 'Webpack',
387
+ 'vite': 'Vite',
388
+ 'tailwindcss': 'Tailwind CSS',
389
+ 'prisma': 'Prisma',
390
+ 'mongoose': 'Mongoose'
391
+ };
392
+
393
+ for (const [dep, tech] of Object.entries(techMapping)) {
394
+ if (deps[dep]) {
395
+ techStack.add(tech);
396
+ }
397
+ }
398
+
399
+ updated.tech_stack = [...techStack];
400
+
401
+ return updated;
402
+ }
403
+ }
404
+
405
+ /**
406
+ * Custom steering rules
407
+ */
408
+ class CustomSteeringRules {
409
+ constructor(options = {}) {
410
+ this.rulesDir = options.rulesDir || 'steering/custom';
411
+ this.rules = new Map();
412
+ }
413
+
414
+ /**
415
+ * Load custom rules from directory
416
+ */
417
+ loadRules(content) {
418
+ // Parse custom rules from markdown format
419
+ const rules = [];
420
+ const rulePattern = /## Rule: (.+)\n([\s\S]*?)(?=## Rule:|$)/g;
421
+
422
+ let match;
423
+ while ((match = rulePattern.exec(content)) !== null) {
424
+ const name = match[1].trim();
425
+ const body = match[2].trim();
426
+
427
+ const rule = {
428
+ name,
429
+ pattern: null,
430
+ action: 'warn',
431
+ message: ''
432
+ };
433
+
434
+ // Parse pattern
435
+ const patternMatch = body.match(/Pattern:\s*`([^`]+)`/);
436
+ if (patternMatch) {
437
+ rule.pattern = patternMatch[1];
438
+ }
439
+
440
+ // Parse action
441
+ const actionMatch = body.match(/Action:\s*(warn|error|update)/);
442
+ if (actionMatch) {
443
+ rule.action = actionMatch[1];
444
+ }
445
+
446
+ // Parse message
447
+ const messageMatch = body.match(/Message:\s*(.+)/);
448
+ if (messageMatch) {
449
+ rule.message = messageMatch[1];
450
+ }
451
+
452
+ rules.push(rule);
453
+ }
454
+
455
+ return rules;
456
+ }
457
+
458
+ /**
459
+ * Register a custom rule
460
+ */
461
+ registerRule(rule) {
462
+ this.rules.set(rule.name, rule);
463
+ return this;
464
+ }
465
+
466
+ /**
467
+ * Apply custom rules to changes
468
+ */
469
+ applyRules(changes) {
470
+ const results = [];
471
+
472
+ for (const [name, rule] of this.rules) {
473
+ for (const change of changes) {
474
+ if (rule.pattern && new RegExp(rule.pattern).test(change.path)) {
475
+ results.push({
476
+ rule: name,
477
+ file: change.path,
478
+ action: rule.action,
479
+ message: rule.message
480
+ });
481
+ }
482
+ }
483
+ }
484
+
485
+ return results;
486
+ }
487
+ }
488
+
489
+ /**
490
+ * Steering Auto-Updater
491
+ */
492
+ class SteeringAutoUpdater extends EventEmitter {
493
+ constructor(options = {}) {
494
+ super();
495
+ this.projectRoot = options.projectRoot || process.cwd();
496
+ this.steeringDir = options.steeringDir || 'steering';
497
+ this.dryRun = options.dryRun || false;
498
+
499
+ this.detector = new ChangeDetector(options.detectorOptions);
500
+ this.updater = new SteeringUpdater({
501
+ steeringDir: this.steeringDir,
502
+ dryRun: this.dryRun,
503
+ backup: options.backup
504
+ });
505
+ this.projectSync = new ProjectYmlSync({
506
+ path: path.join(this.steeringDir, 'project.yml')
507
+ });
508
+ this.customRules = new CustomSteeringRules({
509
+ rulesDir: path.join(this.steeringDir, 'custom')
510
+ });
511
+
512
+ this.updateHistory = [];
513
+ }
514
+
515
+ /**
516
+ * Analyze project and generate update suggestions
517
+ */
518
+ analyze(changes, context = {}) {
519
+ const analysis = this.detector.analyzeChanges(changes);
520
+
521
+ const suggestions = {
522
+ structure: [],
523
+ tech: [],
524
+ product: [],
525
+ project: [],
526
+ custom: []
527
+ };
528
+
529
+ // Generate structure updates
530
+ if (analysis.affectedSteering.includes('structure')) {
531
+ suggestions.structure = this.updater.generateStructureUpdate(analysis);
532
+ }
533
+
534
+ // Generate tech updates
535
+ if (analysis.affectedSteering.includes('tech') || context.packageJson) {
536
+ suggestions.tech = this.updater.generateTechUpdate(analysis, context.packageJson);
537
+ }
538
+
539
+ // Generate product updates
540
+ if (analysis.affectedSteering.includes('product') || context.readme) {
541
+ suggestions.product = this.updater.generateProductUpdate(analysis, context.readme);
542
+ }
543
+
544
+ // Apply custom rules
545
+ suggestions.custom = this.customRules.applyRules(changes);
546
+
547
+ return {
548
+ analysis,
549
+ suggestions,
550
+ affectedFiles: analysis.affectedSteering.map(type =>
551
+ path.join(this.steeringDir, `${type}.md`)
552
+ )
553
+ };
554
+ }
555
+
556
+ /**
557
+ * Apply updates based on suggestions
558
+ */
559
+ applyUpdates(suggestions) {
560
+ const results = {
561
+ applied: [],
562
+ skipped: [],
563
+ errors: []
564
+ };
565
+
566
+ for (const [type, updates] of Object.entries(suggestions)) {
567
+ if (updates.length === 0) continue;
568
+
569
+ try {
570
+ const filePath = path.join(this.steeringDir, `${type}.md`);
571
+ const applied = this.updater.applyUpdates(filePath, updates);
572
+ results.applied.push(...applied);
573
+
574
+ this.emit('updated', { type, updates: applied });
575
+ } catch (error) {
576
+ results.errors.push({
577
+ type,
578
+ error: error.message
579
+ });
580
+ }
581
+ }
582
+
583
+ // Record in history
584
+ this.updateHistory.push({
585
+ timestamp: new Date(),
586
+ results
587
+ });
588
+
589
+ return results;
590
+ }
591
+
592
+ /**
593
+ * Sync project.yml with package.json
594
+ */
595
+ syncProjectYml(projectData, packageJson) {
596
+ const updated = this.projectSync.syncWithPackageJson(projectData, packageJson);
597
+
598
+ this.emit('projectSynced', {
599
+ before: projectData,
600
+ after: updated
601
+ });
602
+
603
+ return updated;
604
+ }
605
+
606
+ /**
607
+ * Register a custom rule
608
+ */
609
+ registerCustomRule(rule) {
610
+ this.customRules.registerRule(rule);
611
+ return this;
612
+ }
613
+
614
+ /**
615
+ * Get update history
616
+ */
617
+ getHistory() {
618
+ return [...this.updateHistory];
619
+ }
620
+
621
+ /**
622
+ * Validate steering consistency
623
+ */
624
+ validateConsistency(steeringFiles) {
625
+ const issues = [];
626
+
627
+ // Check for missing files
628
+ const requiredFiles = ['structure.md', 'tech.md', 'product.md'];
629
+ for (const file of requiredFiles) {
630
+ if (!steeringFiles.includes(file)) {
631
+ issues.push({
632
+ type: 'missing',
633
+ file,
634
+ severity: 'error',
635
+ message: `Required steering file ${file} is missing`
636
+ });
637
+ }
638
+ }
639
+
640
+ // Check for empty sections (would require file content)
641
+
642
+ return {
643
+ valid: issues.filter(i => i.severity === 'error').length === 0,
644
+ issues
645
+ };
646
+ }
647
+ }
648
+
649
+ /**
650
+ * Create steering auto-updater
651
+ */
652
+ function createSteeringAutoUpdater(options = {}) {
653
+ return new SteeringAutoUpdater(options);
654
+ }
655
+
656
+ module.exports = {
657
+ // Classes
658
+ ChangeDetector,
659
+ SteeringUpdater,
660
+ ProjectYmlSync,
661
+ CustomSteeringRules,
662
+ SteeringAutoUpdater,
663
+
664
+ // Constants
665
+ SteeringFileType,
666
+ UpdateTrigger,
667
+
668
+ // Factory
669
+ createSteeringAutoUpdater
670
+ };