musubi-sdd 3.0.1 → 3.6.0

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 (62) hide show
  1. package/README.md +17 -3
  2. package/bin/musubi-change.js +623 -10
  3. package/bin/musubi-orchestrate.js +456 -0
  4. package/bin/musubi-trace.js +393 -0
  5. package/package.json +3 -2
  6. package/src/analyzers/impact-analyzer.js +682 -0
  7. package/src/integrations/cicd.js +782 -0
  8. package/src/integrations/documentation.js +740 -0
  9. package/src/integrations/examples.js +789 -0
  10. package/src/integrations/index.js +23 -0
  11. package/src/integrations/platforms.js +929 -0
  12. package/src/llm-providers/anthropic-provider.js +175 -0
  13. package/src/llm-providers/base-provider.js +221 -0
  14. package/src/llm-providers/copilot-provider.js +262 -0
  15. package/src/llm-providers/index.js +214 -0
  16. package/src/llm-providers/openai-provider.js +205 -0
  17. package/src/managers/delta-spec.js +484 -0
  18. package/src/monitoring/incident-manager.js +890 -0
  19. package/src/monitoring/index.js +633 -0
  20. package/src/monitoring/observability.js +938 -0
  21. package/src/monitoring/release-manager.js +622 -0
  22. package/src/orchestration/index.js +193 -0
  23. package/src/orchestration/orchestration-engine.js +409 -0
  24. package/src/orchestration/pattern-registry.js +319 -0
  25. package/src/orchestration/patterns/auto.js +386 -0
  26. package/src/orchestration/patterns/group-chat.js +395 -0
  27. package/src/orchestration/patterns/human-in-loop.js +506 -0
  28. package/src/orchestration/patterns/nested.js +322 -0
  29. package/src/orchestration/patterns/sequential.js +278 -0
  30. package/src/orchestration/patterns/swarm.js +502 -0
  31. package/src/orchestration/replanning/alternative-generator.js +508 -0
  32. package/src/orchestration/replanning/config.js +378 -0
  33. package/src/orchestration/replanning/index.js +40 -0
  34. package/src/orchestration/replanning/plan-evaluator.js +455 -0
  35. package/src/orchestration/replanning/plan-monitor.js +379 -0
  36. package/src/orchestration/replanning/replan-history.js +402 -0
  37. package/src/orchestration/replanning/replanning-engine.js +706 -0
  38. package/src/orchestration/workflow-orchestrator.js +738 -0
  39. package/src/reporters/coverage-report.js +452 -0
  40. package/src/reporters/traceability-matrix-report.js +684 -0
  41. package/src/steering/advanced-validation.js +812 -0
  42. package/src/steering/auto-updater.js +670 -0
  43. package/src/steering/index.js +119 -0
  44. package/src/steering/quality-metrics.js +650 -0
  45. package/src/steering/template-constraints.js +789 -0
  46. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +22 -0
  47. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +21 -0
  48. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +90 -28
  49. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +32 -0
  50. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +27 -0
  51. package/src/templates/agents/claude-code/skills/steering/SKILL.md +30 -0
  52. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +21 -0
  53. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +27 -0
  54. package/src/templates/agents/codex/AGENTS.md +36 -1
  55. package/src/templates/agents/cursor/AGENTS.md +36 -1
  56. package/src/templates/agents/gemini-cli/GEMINI.md +36 -1
  57. package/src/templates/agents/github-copilot/AGENTS.md +65 -1
  58. package/src/templates/agents/qwen-code/QWEN.md +36 -1
  59. package/src/templates/agents/windsurf/AGENTS.md +36 -1
  60. package/src/templates/shared/delta-spec-template.md +246 -0
  61. package/src/validators/delta-format.js +474 -0
  62. package/src/validators/traceability-validator.js +561 -0
package/README.md CHANGED
@@ -71,7 +71,15 @@ musubi init --windsurf # Windsurf IDE
71
71
 
72
72
  ---
73
73
 
74
- ## 📊 What's New in v2.1.0
74
+ ## 📊 What's New in v3.6.0
75
+
76
+ - 🧠 **Dynamic Replanning Engine** - AI agents can now dynamically adjust plans when tasks fail
77
+ - 🔌 **LLM Provider Abstraction** - Multi-provider support (Copilot, Anthropic, OpenAI)
78
+ - 📡 **Real-time Plan Monitoring** - Detect failures, timeouts, and quality degradation
79
+ - 🔄 **Alternative Path Generation** - LLM-powered alternative strategies with confidence scoring
80
+ - 📝 **Replan History & Audit** - Full audit trail with JSONL persistence and export
81
+
82
+ ### Previous (v3.5.1)
75
83
 
76
84
  - 🔄 **Workflow Engine** - New `musubi-workflow` CLI for stage management and metrics
77
85
  - 📊 **Metrics Collection** - Track time per stage, iteration counts, feedback loops
@@ -80,7 +88,12 @@ musubi init --windsurf # Windsurf IDE
80
88
  - 🔄 **Retrospective Stage** - Stage 9 for continuous improvement
81
89
  - ✅ **Stage Validation Guide** - Checklists for stage transition validation
82
90
 
83
- ### Previous (v2.0.0)
91
+ ### Previous (v3.5.1)
92
+
93
+ - 🔧 **CLI Integration** - Added CLI command references to all 8 Claude Code skills
94
+ - 📚 **Platform Documentation** - CLI Commands section added to all 6 non-Claude platforms
95
+
96
+ ### v2.1.0
84
97
 
85
98
  - 🔌 **CodeGraphMCPServer Integration** - 14 MCP tools for enhanced code analysis
86
99
  - 🧠 **GraphRAG-Powered Search** - Semantic code understanding with Louvain community detection
@@ -89,7 +102,8 @@ musubi init --windsurf # Windsurf IDE
89
102
  ## Features
90
103
 
91
104
  - 🤖 **Multi-Agent Support** - Works with 7 AI coding agents (Claude Code, GitHub Copilot, Cursor, Gemini CLI, Codex CLI, Qwen Code, Windsurf)
92
- - 🔌 **MCP Server Integration** - CodeGraphMCPServer for advanced code analysis (NEW in v2.0.0)
105
+ - 🧠 **Dynamic Replanning** - AI agents dynamically adjust plans on failure with LLM-powered alternatives (NEW in v3.6.0)
106
+ - 🔌 **MCP Server Integration** - CodeGraphMCPServer for advanced code analysis (v2.0.0)
93
107
  - 📄 **Flexible Command Formats** - Supports Markdown, TOML, and AGENTS.md formats
94
108
  - 🎯 **25 Specialized Agents (All Platforms)** - Orchestrator, Steering, Requirements, Architecture, Development, Quality, Security, Infrastructure
95
109
  - Claude Code: Skills API (25 skills)
@@ -12,11 +12,18 @@
12
12
  * musubi-change archive <change-id> # Archive completed change
13
13
  * musubi-change list # List all changes
14
14
  * musubi-change validate <change-id> # Validate delta format
15
+ * musubi-change show <change-id> # Show change details
16
+ * musubi-change impact <change-id> # Show impact analysis
17
+ * musubi-change approve <change-id> # Approve change proposal
18
+ * musubi-change reject <change-id> # Reject change proposal
15
19
  */
16
20
 
17
21
  const { Command } = require('commander');
18
22
  const chalk = require('chalk');
19
23
  const ChangeManager = require('../src/managers/change.js');
24
+ const { DeltaSpecManager, DeltaType } = require('../src/managers/delta-spec.js');
25
+ const { DeltaFormatValidator } = require('../src/validators/delta-format.js');
26
+ const { ImpactAnalyzer } = require('../src/analyzers/impact-analyzer.js');
20
27
 
21
28
  const program = new Command();
22
29
 
@@ -117,23 +124,49 @@ program
117
124
  .description('Archive completed change to specs/')
118
125
  .option('--changes <dir>', 'Changes directory', 'storage/changes')
119
126
  .option('--specs <dir>', 'Specs archive directory', 'specs')
127
+ .option('--force', 'Force archive even if not in implemented status')
120
128
  .action(async (changeId, options) => {
121
129
  try {
122
130
  const workspaceRoot = process.cwd();
123
- const manager = new ChangeManager(workspaceRoot);
131
+ const deltaManager = new DeltaSpecManager(workspaceRoot);
132
+ const changeManager = new ChangeManager(workspaceRoot);
124
133
 
125
134
  console.log(chalk.blue('📦 Archiving change...'));
126
135
 
127
- const result = await manager.archiveChange(changeId, {
128
- changesDir: options.changes,
129
- specsDir: options.specs,
130
- });
136
+ // Try DeltaSpecManager first (new workflow)
137
+ const delta = deltaManager.load(changeId);
138
+
139
+ if (delta) {
140
+ // Check status
141
+ if (delta.status !== 'implemented' && !options.force) {
142
+ console.log(chalk.yellow(`⚠ Change ${changeId} is in "${delta.status}" status.`));
143
+ console.log(chalk.dim('Use --force to archive anyway, or update status first:'));
144
+ console.log(chalk.dim(` musubi-change approve ${changeId}`));
145
+ console.log(chalk.dim(` # implement the change`));
146
+ console.log(chalk.dim(` musubi-change apply ${changeId}`));
147
+ process.exit(1);
148
+ }
131
149
 
132
- console.log(chalk.green('✓ Change archived successfully'));
133
- console.log(chalk.dim(`Source: ${result.source}`));
134
- console.log(chalk.dim(`Archive: ${result.archive}`));
135
- console.log();
136
- console.log(chalk.yellow('Delta merged to canonical specification'));
150
+ const result = deltaManager.archive(changeId);
151
+
152
+ console.log(chalk.green('✓ Change archived successfully'));
153
+ console.log(chalk.dim(`Merged to: ${result.mergedTo}`));
154
+ console.log(chalk.dim(`Archive: ${result.archivePath}`));
155
+ console.log();
156
+ console.log(chalk.yellow('Delta merged to canonical specification'));
157
+ } else {
158
+ // Fall back to ChangeManager (legacy workflow)
159
+ const result = await changeManager.archiveChange(changeId, {
160
+ changesDir: options.changes,
161
+ specsDir: options.specs,
162
+ });
163
+
164
+ console.log(chalk.green('✓ Change archived successfully'));
165
+ console.log(chalk.dim(`Source: ${result.source}`));
166
+ console.log(chalk.dim(`Archive: ${result.archive}`));
167
+ console.log();
168
+ console.log(chalk.yellow('Delta merged to canonical specification'));
169
+ }
137
170
  } catch (error) {
138
171
  console.error(chalk.red('✗ Failed to archive change'));
139
172
  console.error(chalk.dim(error.message));
@@ -248,4 +281,584 @@ program
248
281
  }
249
282
  });
250
283
 
284
+ // Show change details
285
+ program
286
+ .command('show <change-id>')
287
+ .description('Show detailed information about a change')
288
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
289
+ .option('--format <format>', 'Output format (text|json)', 'text')
290
+ .action(async (changeId, options) => {
291
+ try {
292
+ const workspaceRoot = process.cwd();
293
+ const deltaManager = new DeltaSpecManager(workspaceRoot);
294
+
295
+ const delta = deltaManager.load(changeId);
296
+ if (!delta) {
297
+ console.error(chalk.red(`✗ Change not found: ${changeId}`));
298
+ process.exit(1);
299
+ }
300
+
301
+ if (options.format === 'json') {
302
+ console.log(JSON.stringify(delta, null, 2));
303
+ return;
304
+ }
305
+
306
+ // Text format
307
+ console.log();
308
+ console.log(chalk.bold.blue(`Delta Specification: ${delta.id}`));
309
+ console.log(chalk.dim('─'.repeat(50)));
310
+ console.log();
311
+
312
+ const typeColors = {
313
+ ADDED: chalk.green,
314
+ MODIFIED: chalk.blue,
315
+ REMOVED: chalk.red,
316
+ RENAMED: chalk.yellow
317
+ };
318
+ const typeColor = typeColors[delta.type] || chalk.white;
319
+
320
+ console.log(`${chalk.bold('Type:')} ${typeColor(delta.type)}`);
321
+ console.log(`${chalk.bold('Target:')} ${delta.target}`);
322
+ console.log(`${chalk.bold('Status:')} ${delta.status}`);
323
+ console.log(`${chalk.bold('Created:')} ${delta.createdAt}`);
324
+ console.log(`${chalk.bold('Updated:')} ${delta.updatedAt}`);
325
+ console.log();
326
+ console.log(chalk.bold('Description:'));
327
+ console.log(chalk.dim(delta.description));
328
+
329
+ if (delta.rationale) {
330
+ console.log();
331
+ console.log(chalk.bold('Rationale:'));
332
+ console.log(chalk.dim(delta.rationale));
333
+ }
334
+
335
+ if (delta.impactedAreas && delta.impactedAreas.length > 0) {
336
+ console.log();
337
+ console.log(chalk.bold('Impacted Areas:'));
338
+ delta.impactedAreas.forEach(area => {
339
+ console.log(chalk.dim(` • ${area}`));
340
+ });
341
+ }
342
+
343
+ if (delta.before) {
344
+ console.log();
345
+ console.log(chalk.bold('Before State:'));
346
+ console.log(chalk.dim(typeof delta.before === 'object'
347
+ ? JSON.stringify(delta.before, null, 2)
348
+ : delta.before));
349
+ }
350
+
351
+ if (delta.after) {
352
+ console.log();
353
+ console.log(chalk.bold('After State:'));
354
+ console.log(chalk.dim(typeof delta.after === 'object'
355
+ ? JSON.stringify(delta.after, null, 2)
356
+ : delta.after));
357
+ }
358
+
359
+ console.log();
360
+ } catch (error) {
361
+ console.error(chalk.red('✗ Failed to show change'));
362
+ console.error(chalk.dim(error.message));
363
+ process.exit(1);
364
+ }
365
+ });
366
+
367
+ // Impact analysis
368
+ program
369
+ .command('impact <change-id>')
370
+ .description('Show detailed impact analysis for a change')
371
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
372
+ .option('--full', 'Show full impact report with affected files')
373
+ .option('--format <format>', 'Output format (text|json|markdown)', 'text')
374
+ .action(async (changeId, options) => {
375
+ try {
376
+ const workspaceRoot = process.cwd();
377
+ const deltaManager = new DeltaSpecManager(workspaceRoot);
378
+ const impactAnalyzer = new ImpactAnalyzer(workspaceRoot);
379
+
380
+ console.log(chalk.blue('🔍 Analyzing impact...'));
381
+ console.log();
382
+
383
+ // Load delta
384
+ const delta = deltaManager.load(changeId);
385
+ if (!delta) {
386
+ console.error(chalk.red(`✗ Change not found: ${changeId}`));
387
+ process.exit(1);
388
+ }
389
+
390
+ // Run full impact analysis
391
+ const report = await impactAnalyzer.analyzeImpact(delta);
392
+
393
+ if (options.format === 'json') {
394
+ console.log(JSON.stringify(report, null, 2));
395
+ return;
396
+ }
397
+
398
+ if (options.format === 'markdown') {
399
+ console.log(impactAnalyzer.generateSummary(report));
400
+ return;
401
+ }
402
+
403
+ // Text format
404
+ console.log(chalk.bold.blue(`Impact Report: ${report.id}`));
405
+ console.log(chalk.dim('─'.repeat(50)));
406
+ console.log();
407
+
408
+ console.log(`${chalk.bold('Type:')} ${report.type}`);
409
+ console.log(`${chalk.bold('Target:')} ${report.target}`);
410
+ console.log(`${chalk.bold('Analyzed:')} ${report.timestamp}`);
411
+ console.log();
412
+
413
+ // Summary
414
+ console.log(chalk.bold('Summary:'));
415
+ console.log(` Total Affected: ${report.summary.totalAffected}`);
416
+ console.log();
417
+
418
+ // Impact by level
419
+ const levelColors = {
420
+ critical: chalk.red,
421
+ high: chalk.yellow,
422
+ medium: chalk.blue,
423
+ low: chalk.green,
424
+ info: chalk.dim
425
+ };
426
+
427
+ console.log(chalk.bold('Impact Levels:'));
428
+ Object.entries(report.summary.byLevel).forEach(([level, count]) => {
429
+ if (count > 0) {
430
+ const color = levelColors[level] || chalk.white;
431
+ console.log(` ${color(`${level}: ${count}`)}`);
432
+ }
433
+ });
434
+ console.log();
435
+
436
+ // Impact by category
437
+ console.log(chalk.bold('Categories:'));
438
+ Object.entries(report.summary.byCategory).forEach(([category, count]) => {
439
+ if (count > 0) {
440
+ console.log(` ${category}: ${count}`);
441
+ }
442
+ });
443
+ console.log();
444
+
445
+ // Show affected items if --full
446
+ if (options.full && report.affectedItems.length > 0) {
447
+ console.log(chalk.bold('Affected Items:'));
448
+ report.affectedItems.slice(0, 20).forEach(item => {
449
+ const levelColor = levelColors[item.level] || chalk.white;
450
+ console.log(` ${levelColor('●')} ${item.path}`);
451
+ console.log(chalk.dim(` ${item.reason}`));
452
+ });
453
+ if (report.affectedItems.length > 20) {
454
+ console.log(chalk.dim(` ... and ${report.affectedItems.length - 20} more`));
455
+ }
456
+ console.log();
457
+ }
458
+
459
+ // Risks
460
+ if (report.risks.length > 0) {
461
+ console.log(chalk.bold.red('Risks:'));
462
+ report.risks.forEach(risk => {
463
+ const riskColor = risk.level === 'high' ? chalk.red : chalk.yellow;
464
+ console.log(riskColor(` ⚠ ${risk.description}`));
465
+ console.log(chalk.dim(` Mitigation: ${risk.mitigation}`));
466
+ });
467
+ console.log();
468
+ }
469
+
470
+ // Recommendations
471
+ if (report.recommendations.length > 0) {
472
+ console.log(chalk.bold('Recommendations:'));
473
+ report.recommendations.forEach(rec => {
474
+ const emoji = {
475
+ critical: '🔴',
476
+ high: '🟠',
477
+ medium: '🟡',
478
+ info: 'ℹ️'
479
+ }[rec.priority] || '•';
480
+ console.log(` ${emoji} ${rec.message}`);
481
+ });
482
+ console.log();
483
+ }
484
+ } catch (error) {
485
+ console.error(chalk.red('✗ Failed to analyze impact'));
486
+ console.error(chalk.dim(error.message));
487
+ process.exit(1);
488
+ }
489
+ });
490
+
491
+ // Approve change
492
+ program
493
+ .command('approve <change-id>')
494
+ .description('Approve a change proposal')
495
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
496
+ .action(async (changeId, options) => {
497
+ try {
498
+ const workspaceRoot = process.cwd();
499
+ const deltaManager = new DeltaSpecManager(workspaceRoot);
500
+
501
+ const delta = deltaManager.updateStatus(changeId, 'approved');
502
+
503
+ console.log(chalk.green(`✓ Change ${changeId} approved`));
504
+ console.log(chalk.dim(`Status updated to: approved`));
505
+ console.log();
506
+ console.log(chalk.yellow('Next steps:'));
507
+ console.log(chalk.dim(`1. Implement the change`));
508
+ console.log(chalk.dim(`2. Run: musubi-change apply ${changeId}`));
509
+ } catch (error) {
510
+ console.error(chalk.red('✗ Failed to approve change'));
511
+ console.error(chalk.dim(error.message));
512
+ process.exit(1);
513
+ }
514
+ });
515
+
516
+ // Reject change
517
+ program
518
+ .command('reject <change-id>')
519
+ .description('Reject a change proposal')
520
+ .option('--reason <reason>', 'Rejection reason')
521
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
522
+ .action(async (changeId, options) => {
523
+ try {
524
+ const workspaceRoot = process.cwd();
525
+ const deltaManager = new DeltaSpecManager(workspaceRoot);
526
+
527
+ const delta = deltaManager.updateStatus(changeId, 'rejected');
528
+
529
+ console.log(chalk.red(`✗ Change ${changeId} rejected`));
530
+ if (options.reason) {
531
+ console.log(chalk.dim(`Reason: ${options.reason}`));
532
+ }
533
+ console.log(chalk.dim(`Status updated to: rejected`));
534
+ } catch (error) {
535
+ console.error(chalk.red('✗ Failed to reject change'));
536
+ console.error(chalk.dim(error.message));
537
+ process.exit(1);
538
+ }
539
+ });
540
+
541
+ // Create delta (new, using DeltaSpecManager)
542
+ program
543
+ .command('create')
544
+ .description('Create a new delta specification interactively')
545
+ .option('-i, --id <id>', 'Delta ID (e.g., DELTA-AUTH-001)')
546
+ .option('-t, --type <type>', 'Delta type (ADDED, MODIFIED, REMOVED, RENAMED)')
547
+ .option('--target <target>', 'Target requirement or component')
548
+ .option('-d, --description <description>', 'Change description')
549
+ .option('-r, --rationale <rationale>', 'Reason for change')
550
+ .option('--impact <areas>', 'Comma-separated impacted areas')
551
+ .action(async (options) => {
552
+ try {
553
+ const workspaceRoot = process.cwd();
554
+ const deltaManager = new DeltaSpecManager(workspaceRoot);
555
+ const validator = new DeltaFormatValidator();
556
+
557
+ // Validate inputs
558
+ if (!options.id) {
559
+ console.error(chalk.red('✗ Delta ID is required (--id)'));
560
+ process.exit(1);
561
+ }
562
+ if (!options.type) {
563
+ console.error(chalk.red('✗ Delta type is required (--type)'));
564
+ console.log(chalk.dim('Valid types: ADDED, MODIFIED, REMOVED, RENAMED'));
565
+ process.exit(1);
566
+ }
567
+ if (!options.target) {
568
+ console.error(chalk.red('✗ Target is required (--target)'));
569
+ process.exit(1);
570
+ }
571
+ if (!options.description) {
572
+ console.error(chalk.red('✗ Description is required (--description)'));
573
+ process.exit(1);
574
+ }
575
+
576
+ const impactedAreas = options.impact
577
+ ? options.impact.split(',').map(a => a.trim())
578
+ : [];
579
+
580
+ const delta = deltaManager.create({
581
+ id: options.id,
582
+ type: options.type.toUpperCase(),
583
+ target: options.target,
584
+ description: options.description,
585
+ rationale: options.rationale || '',
586
+ impactedAreas
587
+ });
588
+
589
+ // Validate the created delta
590
+ const validation = validator.validate(delta);
591
+
592
+ console.log(chalk.green(`✓ Delta specification created: ${delta.id}`));
593
+ console.log(chalk.dim(`Location: storage/changes/${delta.id}/`));
594
+
595
+ if (validation.warnings.length > 0) {
596
+ console.log();
597
+ console.log(chalk.yellow('Warnings:'));
598
+ validation.warnings.forEach(w => {
599
+ console.log(chalk.dim(` • ${w.message}`));
600
+ });
601
+ }
602
+
603
+ console.log();
604
+ console.log(chalk.yellow('Next steps:'));
605
+ console.log(chalk.dim(`1. Review: musubi-change show ${delta.id}`));
606
+ console.log(chalk.dim(`2. Approve: musubi-change approve ${delta.id}`));
607
+ console.log(chalk.dim(`3. Apply: musubi-change apply ${delta.id}`));
608
+ } catch (error) {
609
+ console.error(chalk.red('✗ Failed to create delta'));
610
+ console.error(chalk.dim(error.message));
611
+ process.exit(1);
612
+ }
613
+ });
614
+
615
+ // Validate all changes
616
+ program
617
+ .command('validate-all')
618
+ .description('Validate all delta specifications in the changes directory')
619
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
620
+ .option('--strict', 'Enable strict validation mode')
621
+ .action(async (options) => {
622
+ try {
623
+ const workspaceRoot = process.cwd();
624
+ const path = require('path');
625
+ const changesDir = path.join(workspaceRoot, options.changes);
626
+ const validator = new DeltaFormatValidator({ strict: options.strict });
627
+
628
+ console.log(chalk.blue('🔍 Validating all delta specifications...'));
629
+ console.log();
630
+
631
+ const result = validator.validateDirectory(changesDir);
632
+
633
+ if (result.summary.total === 0) {
634
+ console.log(chalk.dim('No delta specifications found.'));
635
+ return;
636
+ }
637
+
638
+ result.results.forEach(r => {
639
+ if (r.valid) {
640
+ console.log(chalk.green(`✓ ${r.id}`));
641
+ } else {
642
+ console.log(chalk.red(`✗ ${r.id}`));
643
+ r.errors.forEach(e => {
644
+ console.log(chalk.dim(` ${e.message}`));
645
+ });
646
+ }
647
+ });
648
+
649
+ console.log();
650
+ console.log(chalk.bold('Summary:'));
651
+ console.log(` Total: ${result.summary.total}`);
652
+ console.log(chalk.green(` Valid: ${result.summary.valid}`));
653
+ console.log(chalk.red(` Invalid: ${result.summary.invalid}`));
654
+
655
+ if (!result.valid) {
656
+ process.exit(1);
657
+ }
658
+ } catch (error) {
659
+ console.error(chalk.red('✗ Failed to validate'));
660
+ console.error(chalk.dim(error.message));
661
+ process.exit(1);
662
+ }
663
+ });
664
+
665
+ // Diff command - show before/after comparison
666
+ program
667
+ .command('diff <change-id>')
668
+ .description('Show before/after diff for a change')
669
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
670
+ .option('--context <lines>', 'Lines of context to show', '3')
671
+ .action(async (changeId, options) => {
672
+ try {
673
+ const workspaceRoot = process.cwd();
674
+ const deltaManager = new DeltaSpecManager(workspaceRoot);
675
+
676
+ const delta = deltaManager.load(changeId);
677
+ if (!delta) {
678
+ console.error(chalk.red(`✗ Change not found: ${changeId}`));
679
+ process.exit(1);
680
+ }
681
+
682
+ console.log();
683
+ console.log(chalk.bold.blue(`Diff: ${delta.id}`));
684
+ console.log(chalk.dim('─'.repeat(50)));
685
+ console.log();
686
+
687
+ const typeColors = {
688
+ ADDED: chalk.green,
689
+ MODIFIED: chalk.yellow,
690
+ REMOVED: chalk.red,
691
+ RENAMED: chalk.cyan
692
+ };
693
+ const typeColor = typeColors[delta.type] || chalk.white;
694
+
695
+ console.log(`${chalk.bold('Type:')} ${typeColor(delta.type)}`);
696
+ console.log(`${chalk.bold('Target:')} ${delta.target}`);
697
+ console.log();
698
+
699
+ switch (delta.type) {
700
+ case 'ADDED':
701
+ console.log(chalk.green('+ New Component'));
702
+ console.log(chalk.dim('─'.repeat(40)));
703
+ if (delta.after) {
704
+ if (typeof delta.after === 'object') {
705
+ console.log(chalk.green(JSON.stringify(delta.after, null, 2)));
706
+ } else {
707
+ console.log(chalk.green(delta.after));
708
+ }
709
+ } else {
710
+ console.log(chalk.green(delta.description));
711
+ }
712
+ break;
713
+
714
+ case 'REMOVED':
715
+ console.log(chalk.red('- Removed Component'));
716
+ console.log(chalk.dim('─'.repeat(40)));
717
+ if (delta.before) {
718
+ if (typeof delta.before === 'object') {
719
+ console.log(chalk.red(JSON.stringify(delta.before, null, 2)));
720
+ } else {
721
+ console.log(chalk.red(delta.before));
722
+ }
723
+ } else {
724
+ console.log(chalk.red(`Removing: ${delta.target}`));
725
+ }
726
+ break;
727
+
728
+ case 'MODIFIED':
729
+ console.log(chalk.yellow('~ Modified Component'));
730
+ console.log(chalk.dim('─'.repeat(40)));
731
+ console.log();
732
+ console.log(chalk.red.bold('BEFORE:'));
733
+ if (delta.before) {
734
+ if (typeof delta.before === 'object') {
735
+ Object.entries(delta.before).forEach(([key, value]) => {
736
+ console.log(chalk.red(`- ${key}: ${JSON.stringify(value)}`));
737
+ });
738
+ } else {
739
+ console.log(chalk.red(`- ${delta.before}`));
740
+ }
741
+ }
742
+ console.log();
743
+ console.log(chalk.green.bold('AFTER:'));
744
+ if (delta.after) {
745
+ if (typeof delta.after === 'object') {
746
+ Object.entries(delta.after).forEach(([key, value]) => {
747
+ console.log(chalk.green(`+ ${key}: ${JSON.stringify(value)}`));
748
+ });
749
+ } else {
750
+ console.log(chalk.green(`+ ${delta.after}`));
751
+ }
752
+ }
753
+ break;
754
+
755
+ case 'RENAMED':
756
+ console.log(chalk.cyan('⇒ Renamed Component'));
757
+ console.log(chalk.dim('─'.repeat(40)));
758
+ console.log(chalk.red(`- ${delta.before || delta.target}`));
759
+ console.log(chalk.green(`+ ${delta.after || 'new name'}`));
760
+ break;
761
+
762
+ default:
763
+ console.log(chalk.dim('No diff information available.'));
764
+ }
765
+
766
+ console.log();
767
+ console.log(chalk.bold('Description:'));
768
+ console.log(chalk.dim(delta.description));
769
+
770
+ if (delta.rationale) {
771
+ console.log();
772
+ console.log(chalk.bold('Rationale:'));
773
+ console.log(chalk.dim(delta.rationale));
774
+ }
775
+
776
+ console.log();
777
+ } catch (error) {
778
+ console.error(chalk.red('✗ Failed to show diff'));
779
+ console.error(chalk.dim(error.message));
780
+ process.exit(1);
781
+ }
782
+ });
783
+
784
+ // Status command - show workflow status summary
785
+ program
786
+ .command('status')
787
+ .description('Show status summary of all changes')
788
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
789
+ .action(async (options) => {
790
+ try {
791
+ const workspaceRoot = process.cwd();
792
+ const deltaManager = new DeltaSpecManager(workspaceRoot);
793
+
794
+ const deltas = deltaManager.list();
795
+
796
+ if (deltas.length === 0) {
797
+ console.log(chalk.dim('No delta specifications found.'));
798
+ return;
799
+ }
800
+
801
+ const statusCounts = {
802
+ proposed: 0,
803
+ approved: 0,
804
+ rejected: 0,
805
+ implemented: 0,
806
+ archived: 0
807
+ };
808
+
809
+ const typeCounts = {
810
+ ADDED: 0,
811
+ MODIFIED: 0,
812
+ REMOVED: 0,
813
+ RENAMED: 0
814
+ };
815
+
816
+ deltas.forEach(d => {
817
+ statusCounts[d.status] = (statusCounts[d.status] || 0) + 1;
818
+ typeCounts[d.type] = (typeCounts[d.type] || 0) + 1;
819
+ });
820
+
821
+ console.log();
822
+ console.log(chalk.bold.blue('Change Status Summary'));
823
+ console.log(chalk.dim('─'.repeat(40)));
824
+ console.log();
825
+
826
+ console.log(chalk.bold('By Status:'));
827
+ console.log(` ${chalk.yellow('●')} Proposed: ${statusCounts.proposed}`);
828
+ console.log(` ${chalk.blue('●')} Approved: ${statusCounts.approved}`);
829
+ console.log(` ${chalk.red('●')} Rejected: ${statusCounts.rejected}`);
830
+ console.log(` ${chalk.cyan('●')} Implemented: ${statusCounts.implemented}`);
831
+ console.log(` ${chalk.green('●')} Archived: ${statusCounts.archived}`);
832
+ console.log();
833
+
834
+ console.log(chalk.bold('By Type:'));
835
+ console.log(` ${chalk.green('+')} ADDED: ${typeCounts.ADDED}`);
836
+ console.log(` ${chalk.yellow('~')} MODIFIED: ${typeCounts.MODIFIED}`);
837
+ console.log(` ${chalk.red('-')} REMOVED: ${typeCounts.REMOVED}`);
838
+ console.log(` ${chalk.cyan('⇒')} RENAMED: ${typeCounts.RENAMED}`);
839
+ console.log();
840
+
841
+ console.log(`${chalk.bold('Total:')} ${deltas.length} delta(s)`);
842
+ console.log();
843
+
844
+ // Show pending items
845
+ const pending = deltas.filter(d =>
846
+ d.status === 'proposed' || d.status === 'approved'
847
+ );
848
+
849
+ if (pending.length > 0) {
850
+ console.log(chalk.bold('Pending Actions:'));
851
+ pending.forEach(d => {
852
+ const statusColor = d.status === 'proposed' ? chalk.yellow : chalk.blue;
853
+ console.log(` ${statusColor('●')} ${d.id} (${d.status})`);
854
+ });
855
+ console.log();
856
+ }
857
+ } catch (error) {
858
+ console.error(chalk.red('✗ Failed to get status'));
859
+ console.error(chalk.dim(error.message));
860
+ process.exit(1);
861
+ }
862
+ });
863
+
251
864
  program.parse();