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
package/bin/musubi-change.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
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();
|