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.
- package/README.md +17 -3
- 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/llm-providers/anthropic-provider.js +175 -0
- package/src/llm-providers/base-provider.js +221 -0
- package/src/llm-providers/copilot-provider.js +262 -0
- package/src/llm-providers/index.js +214 -0
- package/src/llm-providers/openai-provider.js +205 -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 +193 -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 +502 -0
- package/src/orchestration/replanning/alternative-generator.js +508 -0
- package/src/orchestration/replanning/config.js +378 -0
- package/src/orchestration/replanning/index.js +40 -0
- package/src/orchestration/replanning/plan-evaluator.js +455 -0
- package/src/orchestration/replanning/plan-monitor.js +379 -0
- package/src/orchestration/replanning/replan-history.js +402 -0
- package/src/orchestration/replanning/replanning-engine.js +706 -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/README.md
CHANGED
|
@@ -71,7 +71,15 @@ musubi init --windsurf # Windsurf IDE
|
|
|
71
71
|
|
|
72
72
|
---
|
|
73
73
|
|
|
74
|
-
## 📊 What's New in
|
|
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 (
|
|
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
|
-
-
|
|
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)
|
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();
|