musubi-sdd 0.8.4 → 0.8.6

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.ja.md CHANGED
@@ -15,7 +15,7 @@ MUSUBIは、6つの主要フレームワークのベスト機能を統合した
15
15
  - 📋 **憲法ガバナンス** - 9つの不変条項 + フェーズ-1ゲートによる品質保証
16
16
  - 📝 **EARS要件ジェネレーター** - 5つのEARSパターンで明確な要件を作成(v0.8.0)
17
17
  - 🏗️ **設計ドキュメントジェネレーター** - トレーサビリティ付きC4モデルとADRを作成(v0.8.2)
18
- - 🔄 **差分仕様** - ブラウンフィールドおよびグリーンフィールドプロジェクト対応
18
+ - 🔄 **変更管理システム** - ブラウンフィールドプロジェクト向け差分仕様(v0.8.6)
19
19
  - 🧭 **自動更新プロジェクトメモリ** - ステアリングシステムがアーキテクチャ、技術スタック、製品コンテキストを維持
20
20
  - 🚀 **自動オンボーディング** - `musubi-onboard` が既存プロジェクトを分析し、ステアリングドキュメントを生成(2-5分)
21
21
  - 🔄 **自動同期** - `musubi-sync` がコードベースの変更を検出し、ステアリングドキュメントを最新に保つ
@@ -129,6 +129,24 @@ musubi-tasks list --priority P0 # 重要タスクのみ表示
129
129
  musubi-tasks update 001 "In Progress" # タスクステータス更新
130
130
  musubi-tasks validate # タスク完全性を検証
131
131
  musubi-tasks graph # 依存関係グラフ表示
132
+
133
+ # エンドツーエンドトレーサビリティ(v0.8.5)
134
+ musubi-trace matrix # トレーサビリティマトリクス生成
135
+ musubi-trace matrix --format markdown > trace.md # Markdownにエクスポート
136
+ musubi-trace coverage # カバレッジ統計計算
137
+ musubi-trace coverage --min-coverage 100 # 100%カバレッジ要求
138
+ musubi-trace gaps # 孤立した要件/コード検出
139
+ musubi-trace requirement REQ-AUTH-001 # 特定要件をトレース
140
+ musubi-trace validate # 100%トレーサビリティ検証(第5条)
141
+
142
+ # ブラウンフィールドプロジェクト向け変更管理(v0.8.6)
143
+ musubi-change init CHANGE-001 --title "認証機能追加" # 変更提案を作成
144
+ musubi-change validate CHANGE-001 --verbose # 差分仕様を検証
145
+ musubi-change apply CHANGE-001 --dry-run # 変更をプレビュー
146
+ musubi-change apply CHANGE-001 # コードベースに変更を適用
147
+ musubi-change archive CHANGE-001 # specs/にアーカイブ
148
+ musubi-change list --status pending # 保留中の変更をリスト
149
+ musubi-change list --format json # JSON形式でリスト
132
150
  ```
133
151
 
134
152
  ### プロジェクトタイプ
package/README.md CHANGED
@@ -19,7 +19,7 @@ MUSUBI is a comprehensive SDD (Specification Driven Development) framework that
19
19
  - 📋 **Constitutional Governance** - 9 immutable articles + Phase -1 Gates for quality enforcement
20
20
  - 📝 **EARS Requirements Generator** - Create unambiguous requirements with 5 EARS patterns (v0.8.0)
21
21
  - 🏗️ **Design Document Generator** - Create C4 models and ADRs with traceability (v0.8.2)
22
- - 🔄 **Delta Specifications** - Brownfield and greenfield project support
22
+ - 🔄 **Change Management System** - Delta specifications for brownfield projects (v0.8.6)
23
23
  - 🧭 **Auto-Updating Project Memory** - Steering system maintains architecture, tech stack, and product context
24
24
  - 🚀 **Automatic Onboarding** - `musubi-onboard` analyzes existing projects and generates steering docs (2-5 minutes)
25
25
  - 🔄 **Auto-Sync** - `musubi-sync` detects codebase changes and keeps steering docs current
@@ -133,6 +133,24 @@ musubi-tasks list --priority P0 # List critical tasks
133
133
  musubi-tasks update 001 "In Progress" # Update task status
134
134
  musubi-tasks validate # Validate task completeness
135
135
  musubi-tasks graph # Show dependency graph
136
+
137
+ # End-to-end traceability (v0.8.5)
138
+ musubi-trace matrix # Generate traceability matrix
139
+ musubi-trace matrix --format markdown > trace.md # Export to markdown
140
+ musubi-trace coverage # Calculate coverage statistics
141
+ musubi-trace coverage --min-coverage 100 # Require 100% coverage
142
+ musubi-trace gaps # Detect orphaned requirements/code
143
+ musubi-trace requirement REQ-AUTH-001 # Trace specific requirement
144
+ musubi-trace validate # Validate 100% traceability (Article V)
145
+
146
+ # Change management for brownfield projects (v0.8.6)
147
+ musubi-change init CHANGE-001 --title "Add authentication" # Create change proposal
148
+ musubi-change validate CHANGE-001 --verbose # Validate delta specification
149
+ musubi-change apply CHANGE-001 --dry-run # Preview changes
150
+ musubi-change apply CHANGE-001 # Apply changes to codebase
151
+ musubi-change archive CHANGE-001 # Archive to specs/
152
+ musubi-change list --status pending # List pending changes
153
+ musubi-change list --format json # List in JSON format
136
154
  ```
137
155
 
138
156
  ### Project Types
@@ -0,0 +1,250 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MUSUBI Change Management CLI
5
+ *
6
+ * Manages delta specifications for brownfield projects
7
+ * Implements ADDED/MODIFIED/REMOVED/RENAMED change tracking
8
+ *
9
+ * Usage:
10
+ * musubi-change init <change-id> # Create change proposal
11
+ * musubi-change apply <change-id> # Apply change to codebase
12
+ * musubi-change archive <change-id> # Archive completed change
13
+ * musubi-change list # List all changes
14
+ * musubi-change validate <change-id> # Validate delta format
15
+ */
16
+
17
+ const { Command } = require('commander');
18
+ const chalk = require('chalk');
19
+ const ChangeManager = require('../src/managers/change.js');
20
+
21
+ const program = new Command();
22
+
23
+ program
24
+ .name('musubi-change')
25
+ .description('MUSUBI Change Management - Delta specifications for brownfield projects')
26
+ .version('0.8.6');
27
+
28
+ // Initialize change proposal
29
+ program
30
+ .command('init <change-id>')
31
+ .description('Create new change proposal with delta specification')
32
+ .option('-t, --title <title>', 'Change title')
33
+ .option('-d, --description <description>', 'Change description')
34
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
35
+ .option('--template <path>', 'Custom delta template')
36
+ .action(async (changeId, options) => {
37
+ try {
38
+ const workspaceRoot = process.cwd();
39
+ const manager = new ChangeManager(workspaceRoot);
40
+
41
+ console.log(chalk.blue('📝 Creating change proposal...'));
42
+ console.log(chalk.dim(`Change ID: ${changeId}`));
43
+
44
+ const result = await manager.initChange(changeId, {
45
+ title: options.title,
46
+ description: options.description,
47
+ changesDir: options.changes,
48
+ template: options.template
49
+ });
50
+
51
+ console.log(chalk.green('✓ Change proposal created successfully'));
52
+ console.log(chalk.dim(`Location: ${result.file}`));
53
+ console.log();
54
+ console.log(chalk.yellow('Next steps:'));
55
+ console.log(chalk.dim('1. Edit the delta specification'));
56
+ console.log(chalk.dim(`2. Run: musubi-change validate ${changeId}`));
57
+ console.log(chalk.dim(`3. Run: musubi-change apply ${changeId}`));
58
+ } catch (error) {
59
+ console.error(chalk.red('✗ Failed to create change proposal'));
60
+ console.error(chalk.dim(error.message));
61
+ process.exit(1);
62
+ }
63
+ });
64
+
65
+ // Apply change to codebase
66
+ program
67
+ .command('apply <change-id>')
68
+ .description('Apply change proposal to codebase')
69
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
70
+ .option('--dry-run', 'Preview changes without applying')
71
+ .option('--force', 'Force apply even with validation errors')
72
+ .action(async (changeId, options) => {
73
+ try {
74
+ const workspaceRoot = process.cwd();
75
+ const manager = new ChangeManager(workspaceRoot);
76
+
77
+ if (options.dryRun) {
78
+ console.log(chalk.blue('🔍 Previewing changes (dry run)...'));
79
+ } else {
80
+ console.log(chalk.blue('⚙️ Applying changes...'));
81
+ }
82
+
83
+ const result = await manager.applyChange(changeId, {
84
+ changesDir: options.changes,
85
+ dryRun: options.dryRun,
86
+ force: options.force
87
+ });
88
+
89
+ if (options.dryRun) {
90
+ console.log(chalk.yellow('\nPreview (no changes applied):'));
91
+ console.log(chalk.green(` ${result.stats.added} files to be added`));
92
+ console.log(chalk.blue(` ${result.stats.modified} files to be modified`));
93
+ console.log(chalk.red(` ${result.stats.removed} files to be removed`));
94
+ console.log(chalk.yellow(` ${result.stats.renamed} files to be renamed`));
95
+ } else {
96
+ console.log(chalk.green('\n✓ Changes applied successfully'));
97
+ console.log(chalk.green(` ${result.stats.added} files added`));
98
+ console.log(chalk.blue(` ${result.stats.modified} files modified`));
99
+ console.log(chalk.red(` ${result.stats.removed} files removed`));
100
+ console.log(chalk.yellow(` ${result.stats.renamed} files renamed`));
101
+
102
+ console.log();
103
+ console.log(chalk.yellow('Next steps:'));
104
+ console.log(chalk.dim('1. Test the changes'));
105
+ console.log(chalk.dim(`2. Run: musubi-change archive ${changeId}`));
106
+ }
107
+ } catch (error) {
108
+ console.error(chalk.red('✗ Failed to apply changes'));
109
+ console.error(chalk.dim(error.message));
110
+ process.exit(1);
111
+ }
112
+ });
113
+
114
+ // Archive completed change
115
+ program
116
+ .command('archive <change-id>')
117
+ .description('Archive completed change to specs/')
118
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
119
+ .option('--specs <dir>', 'Specs archive directory', 'specs')
120
+ .action(async (changeId, options) => {
121
+ try {
122
+ const workspaceRoot = process.cwd();
123
+ const manager = new ChangeManager(workspaceRoot);
124
+
125
+ console.log(chalk.blue('📦 Archiving change...'));
126
+
127
+ const result = await manager.archiveChange(changeId, {
128
+ changesDir: options.changes,
129
+ specsDir: options.specs
130
+ });
131
+
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'));
137
+ } catch (error) {
138
+ console.error(chalk.red('✗ Failed to archive change'));
139
+ console.error(chalk.dim(error.message));
140
+ process.exit(1);
141
+ }
142
+ });
143
+
144
+ // List all changes
145
+ program
146
+ .command('list')
147
+ .description('List all change proposals')
148
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
149
+ .option('--status <status>', 'Filter by status (pending|applied|archived)')
150
+ .option('--format <format>', 'Output format (table|json)', 'table')
151
+ .action(async (options) => {
152
+ try {
153
+ const workspaceRoot = process.cwd();
154
+ const manager = new ChangeManager(workspaceRoot);
155
+
156
+ const changes = await manager.listChanges({
157
+ changesDir: options.changes,
158
+ status: options.status
159
+ });
160
+
161
+ if (options.format === 'json') {
162
+ console.log(JSON.stringify(changes, null, 2));
163
+ return;
164
+ }
165
+
166
+ // Table format
167
+ console.log(chalk.bold('\nChange Proposals:\n'));
168
+
169
+ if (changes.length === 0) {
170
+ console.log(chalk.dim('No changes found'));
171
+ return;
172
+ }
173
+
174
+ console.log(chalk.dim('ID'.padEnd(20) + 'Title'.padEnd(40) + 'Status'.padEnd(15) + 'Date'));
175
+ console.log(chalk.dim('-'.repeat(90)));
176
+
177
+ changes.forEach(change => {
178
+ const statusColor = {
179
+ pending: chalk.yellow,
180
+ applied: chalk.blue,
181
+ archived: chalk.green
182
+ }[change.status] || chalk.white;
183
+
184
+ console.log(
185
+ change.id.padEnd(20) +
186
+ change.title.padEnd(40) +
187
+ statusColor(change.status.padEnd(15)) +
188
+ change.date
189
+ );
190
+ });
191
+
192
+ console.log();
193
+ console.log(chalk.dim(`Total: ${changes.length} change(s)`));
194
+ } catch (error) {
195
+ console.error(chalk.red('✗ Failed to list changes'));
196
+ console.error(chalk.dim(error.message));
197
+ process.exit(1);
198
+ }
199
+ });
200
+
201
+ // Validate delta format
202
+ program
203
+ .command('validate <change-id>')
204
+ .description('Validate delta specification format')
205
+ .option('--changes <dir>', 'Changes directory', 'storage/changes')
206
+ .option('-v, --verbose', 'Show detailed validation results')
207
+ .action(async (changeId, options) => {
208
+ try {
209
+ const workspaceRoot = process.cwd();
210
+ const manager = new ChangeManager(workspaceRoot);
211
+
212
+ console.log(chalk.blue('🔍 Validating delta specification...'));
213
+
214
+ const result = await manager.validateChange(changeId, {
215
+ changesDir: options.changes,
216
+ verbose: options.verbose
217
+ });
218
+
219
+ if (result.valid) {
220
+ console.log(chalk.green('✓ Delta specification is valid'));
221
+
222
+ if (options.verbose) {
223
+ console.log();
224
+ console.log(chalk.bold('Summary:'));
225
+ console.log(chalk.green(` ${result.stats.added} ADDED items`));
226
+ console.log(chalk.blue(` ${result.stats.modified} MODIFIED items`));
227
+ console.log(chalk.red(` ${result.stats.removed} REMOVED items`));
228
+ console.log(chalk.yellow(` ${result.stats.renamed} RENAMED items`));
229
+ }
230
+ } else {
231
+ console.log(chalk.red('✗ Validation failed'));
232
+ console.log();
233
+
234
+ result.errors.forEach(error => {
235
+ console.log(chalk.red(` • ${error.message}`));
236
+ if (error.line) {
237
+ console.log(chalk.dim(` Line ${error.line}`));
238
+ }
239
+ });
240
+
241
+ process.exit(1);
242
+ }
243
+ } catch (error) {
244
+ console.error(chalk.red('✗ Failed to validate change'));
245
+ console.error(chalk.dim(error.message));
246
+ process.exit(1);
247
+ }
248
+ });
249
+
250
+ program.parse();
File without changes
File without changes
@@ -0,0 +1,367 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * MUSUBI Traceability System CLI
5
+ *
6
+ * Provides end-to-end traceability from requirements to code to tests
7
+ * Ensures 100% coverage and detects gaps
8
+ *
9
+ * Usage:
10
+ * musubi-trace matrix # Generate full traceability matrix
11
+ * musubi-trace coverage # Calculate requirement coverage
12
+ * musubi-trace gaps # Detect orphaned requirements/code
13
+ * musubi-trace requirement <id> # Trace specific requirement
14
+ * musubi-trace validate # Validate 100% coverage
15
+ */
16
+
17
+ const { Command } = require('commander');
18
+ const chalk = require('chalk');
19
+ const TraceabilityAnalyzer = require('../src/analyzers/traceability');
20
+
21
+ const program = new Command();
22
+
23
+ program
24
+ .name('musubi-trace')
25
+ .description('MUSUBI Traceability System - End-to-end requirement traceability')
26
+ .version('0.8.5');
27
+
28
+ // Generate traceability matrix
29
+ program
30
+ .command('matrix')
31
+ .description('Generate full traceability matrix')
32
+ .option('-f, --format <type>', 'Output format (table|markdown|json|html)', 'table')
33
+ .option('-o, --output <path>', 'Output file path')
34
+ .option('--requirements <path>', 'Requirements directory', 'docs/requirements')
35
+ .option('--design <path>', 'Design directory', 'docs/design')
36
+ .option('--tasks <path>', 'Tasks directory', 'docs/tasks')
37
+ .option('--code <path>', 'Source code directory', 'src')
38
+ .option('--tests <path>', 'Tests directory', 'tests')
39
+ .action(async (options) => {
40
+ try {
41
+ console.log(chalk.bold('\n📊 Generating Traceability Matrix\n'));
42
+
43
+ const analyzer = new TraceabilityAnalyzer(process.cwd());
44
+ const matrix = await analyzer.generateMatrix(options);
45
+
46
+ if (options.output) {
47
+ const fs = require('fs-extra');
48
+ await fs.writeFile(options.output, analyzer.formatMatrix(matrix, options.format), 'utf-8');
49
+ console.log(chalk.green(`✓ Matrix saved to ${options.output}`));
50
+ } else {
51
+ console.log(analyzer.formatMatrix(matrix, options.format));
52
+ }
53
+
54
+ console.log();
55
+ console.log(chalk.bold('Summary:'));
56
+ console.log(chalk.dim(` Requirements: ${matrix.summary.totalRequirements}`));
57
+ console.log(chalk.dim(` With Design: ${matrix.summary.withDesign} (${matrix.summary.designCoverage}%)`));
58
+ console.log(chalk.dim(` With Tasks: ${matrix.summary.withTasks} (${matrix.summary.tasksCoverage}%)`));
59
+ console.log(chalk.dim(` With Code: ${matrix.summary.withCode} (${matrix.summary.codeCoverage}%)`));
60
+ console.log(chalk.dim(` With Tests: ${matrix.summary.withTests} (${matrix.summary.testsCoverage}%)`));
61
+ console.log();
62
+
63
+ process.exit(0);
64
+ } catch (error) {
65
+ console.error(chalk.red('✗ Error:'), error.message);
66
+ process.exit(1);
67
+ }
68
+ });
69
+
70
+ // Calculate coverage
71
+ program
72
+ .command('coverage')
73
+ .description('Calculate requirement coverage statistics')
74
+ .option('--requirements <path>', 'Requirements directory', 'docs/requirements')
75
+ .option('--design <path>', 'Design directory', 'docs/design')
76
+ .option('--tasks <path>', 'Tasks directory', 'docs/tasks')
77
+ .option('--code <path>', 'Source code directory', 'src')
78
+ .option('--tests <path>', 'Tests directory', 'tests')
79
+ .option('--min-coverage <percent>', 'Minimum required coverage', '100')
80
+ .action(async (options) => {
81
+ try {
82
+ console.log(chalk.bold('\n📈 Calculating Coverage\n'));
83
+
84
+ const analyzer = new TraceabilityAnalyzer(process.cwd());
85
+ const coverage = await analyzer.calculateCoverage(options);
86
+
87
+ const minCoverage = parseInt(options.minCoverage);
88
+
89
+ console.log(chalk.bold('Coverage Report:'));
90
+ console.log();
91
+
92
+ const stages = [
93
+ { name: 'Requirements → Design', value: coverage.designCoverage, color: coverage.designCoverage >= minCoverage ? chalk.green : chalk.red },
94
+ { name: 'Requirements → Tasks', value: coverage.tasksCoverage, color: coverage.tasksCoverage >= minCoverage ? chalk.green : chalk.red },
95
+ { name: 'Requirements → Code', value: coverage.codeCoverage, color: coverage.codeCoverage >= minCoverage ? chalk.green : chalk.red },
96
+ { name: 'Requirements → Tests', value: coverage.testsCoverage, color: coverage.testsCoverage >= minCoverage ? chalk.green : chalk.red }
97
+ ];
98
+
99
+ stages.forEach(stage => {
100
+ const bar = '█'.repeat(Math.floor(stage.value / 2));
101
+ console.log(stage.color(` ${stage.name.padEnd(25)} ${bar} ${stage.value}%`));
102
+ });
103
+
104
+ console.log();
105
+ console.log(chalk.bold('Overall Coverage:'));
106
+ const overallColor = coverage.overall >= minCoverage ? chalk.green : chalk.red;
107
+ console.log(overallColor(` ${coverage.overall}% (min: ${minCoverage}%)`));
108
+ console.log();
109
+
110
+ if (coverage.overall >= minCoverage) {
111
+ console.log(chalk.green('✓ Coverage meets requirements\n'));
112
+ process.exit(0);
113
+ } else {
114
+ console.log(chalk.red('✗ Coverage below minimum threshold\n'));
115
+ process.exit(1);
116
+ }
117
+ } catch (error) {
118
+ console.error(chalk.red('✗ Error:'), error.message);
119
+ process.exit(1);
120
+ }
121
+ });
122
+
123
+ // Detect gaps
124
+ program
125
+ .command('gaps')
126
+ .description('Detect orphaned requirements, design, tasks, and untested code')
127
+ .option('--requirements <path>', 'Requirements directory', 'docs/requirements')
128
+ .option('--design <path>', 'Design directory', 'docs/design')
129
+ .option('--tasks <path>', 'Tasks directory', 'docs/tasks')
130
+ .option('--code <path>', 'Source code directory', 'src')
131
+ .option('--tests <path>', 'Tests directory', 'tests')
132
+ .option('-v, --verbose', 'Show detailed gap information')
133
+ .action(async (options) => {
134
+ try {
135
+ console.log(chalk.bold('\n🔍 Detecting Gaps\n'));
136
+
137
+ const analyzer = new TraceabilityAnalyzer(process.cwd());
138
+ const gaps = await analyzer.detectGaps(options);
139
+
140
+ let hasGaps = false;
141
+
142
+ if (gaps.orphanedRequirements.length > 0) {
143
+ hasGaps = true;
144
+ console.log(chalk.red.bold('Orphaned Requirements (no design/tasks):'));
145
+ gaps.orphanedRequirements.forEach(req => {
146
+ console.log(chalk.red(` • ${req.id}: ${req.title}`));
147
+ if (options.verbose && req.file) {
148
+ console.log(chalk.dim(` ${req.file}`));
149
+ }
150
+ });
151
+ console.log();
152
+ }
153
+
154
+ if (gaps.orphanedDesign.length > 0) {
155
+ hasGaps = true;
156
+ console.log(chalk.yellow.bold('Orphaned Design (no requirements):'));
157
+ gaps.orphanedDesign.forEach(design => {
158
+ console.log(chalk.yellow(` • ${design.id}: ${design.title}`));
159
+ if (options.verbose && design.file) {
160
+ console.log(chalk.dim(` ${design.file}`));
161
+ }
162
+ });
163
+ console.log();
164
+ }
165
+
166
+ if (gaps.orphanedTasks.length > 0) {
167
+ hasGaps = true;
168
+ console.log(chalk.yellow.bold('Orphaned Tasks (no requirements):'));
169
+ gaps.orphanedTasks.forEach(task => {
170
+ console.log(chalk.yellow(` • ${task.id}: ${task.title}`));
171
+ if (options.verbose && task.file) {
172
+ console.log(chalk.dim(` ${task.file}`));
173
+ }
174
+ });
175
+ console.log();
176
+ }
177
+
178
+ if (gaps.untestedCode.length > 0) {
179
+ hasGaps = true;
180
+ console.log(chalk.red.bold('Untested Code (no test coverage):'));
181
+ gaps.untestedCode.forEach(code => {
182
+ console.log(chalk.red(` • ${code.file}:${code.function || code.class}`));
183
+ if (options.verbose && code.lines) {
184
+ console.log(chalk.dim(` Lines: ${code.lines}`));
185
+ }
186
+ });
187
+ console.log();
188
+ }
189
+
190
+ if (gaps.missingTests.length > 0) {
191
+ hasGaps = true;
192
+ console.log(chalk.red.bold('Missing Tests (requirements not tested):'));
193
+ gaps.missingTests.forEach(req => {
194
+ console.log(chalk.red(` • ${req.id}: ${req.title}`));
195
+ });
196
+ console.log();
197
+ }
198
+
199
+ if (!hasGaps) {
200
+ console.log(chalk.green('✓ No gaps detected - 100% traceability!\n'));
201
+ process.exit(0);
202
+ } else {
203
+ console.log(chalk.bold('Gap Summary:'));
204
+ console.log(chalk.dim(` Orphaned Requirements: ${gaps.orphanedRequirements.length}`));
205
+ console.log(chalk.dim(` Orphaned Design: ${gaps.orphanedDesign.length}`));
206
+ console.log(chalk.dim(` Orphaned Tasks: ${gaps.orphanedTasks.length}`));
207
+ console.log(chalk.dim(` Untested Code: ${gaps.untestedCode.length}`));
208
+ console.log(chalk.dim(` Missing Tests: ${gaps.missingTests.length}`));
209
+ console.log();
210
+ process.exit(1);
211
+ }
212
+ } catch (error) {
213
+ console.error(chalk.red('✗ Error:'), error.message);
214
+ process.exit(1);
215
+ }
216
+ });
217
+
218
+ // Trace specific requirement
219
+ program
220
+ .command('requirement <id>')
221
+ .description('Trace specific requirement through design, tasks, code, and tests')
222
+ .option('--requirements <path>', 'Requirements directory', 'docs/requirements')
223
+ .option('--design <path>', 'Design directory', 'docs/design')
224
+ .option('--tasks <path>', 'Tasks directory', 'docs/tasks')
225
+ .option('--code <path>', 'Source code directory', 'src')
226
+ .option('--tests <path>', 'Tests directory', 'tests')
227
+ .action(async (id, options) => {
228
+ try {
229
+ console.log(chalk.bold(`\n🔗 Tracing Requirement: ${id}\n`));
230
+
231
+ const analyzer = new TraceabilityAnalyzer(process.cwd());
232
+ const trace = await analyzer.traceRequirement(id, options);
233
+
234
+ if (!trace.requirement) {
235
+ console.log(chalk.red(`✗ Requirement ${id} not found\n`));
236
+ process.exit(1);
237
+ }
238
+
239
+ console.log(chalk.bold('Requirement:'));
240
+ console.log(chalk.cyan(` ${trace.requirement.id}: ${trace.requirement.title}`));
241
+ console.log(chalk.dim(` ${trace.requirement.file}`));
242
+ console.log();
243
+
244
+ if (trace.design.length > 0) {
245
+ console.log(chalk.bold('Design:'));
246
+ trace.design.forEach(d => {
247
+ console.log(chalk.green(` ✓ ${d.id}: ${d.title}`));
248
+ console.log(chalk.dim(` ${d.file}`));
249
+ });
250
+ console.log();
251
+ } else {
252
+ console.log(chalk.yellow('⚠ No design found\n'));
253
+ }
254
+
255
+ if (trace.tasks.length > 0) {
256
+ console.log(chalk.bold('Tasks:'));
257
+ trace.tasks.forEach(t => {
258
+ console.log(chalk.green(` ✓ ${t.id}: ${t.title} (${t.status})`));
259
+ console.log(chalk.dim(` ${t.file}`));
260
+ });
261
+ console.log();
262
+ } else {
263
+ console.log(chalk.yellow('⚠ No tasks found\n'));
264
+ }
265
+
266
+ if (trace.code.length > 0) {
267
+ console.log(chalk.bold('Code:'));
268
+ trace.code.forEach(c => {
269
+ console.log(chalk.green(` ✓ ${c.file}:${c.function || c.class}`));
270
+ console.log(chalk.dim(` Lines: ${c.lines}`));
271
+ });
272
+ console.log();
273
+ } else {
274
+ console.log(chalk.yellow('⚠ No code implementation found\n'));
275
+ }
276
+
277
+ if (trace.tests.length > 0) {
278
+ console.log(chalk.bold('Tests:'));
279
+ trace.tests.forEach(t => {
280
+ console.log(chalk.green(` ✓ ${t.file}:${t.test}`));
281
+ console.log(chalk.dim(` Status: ${t.status || 'passing'}`));
282
+ });
283
+ console.log();
284
+ } else {
285
+ console.log(chalk.red('✗ No tests found\n'));
286
+ }
287
+
288
+ const coverage = {
289
+ design: trace.design.length > 0,
290
+ tasks: trace.tasks.length > 0,
291
+ code: trace.code.length > 0,
292
+ tests: trace.tests.length > 0
293
+ };
294
+
295
+ const coveragePercent = Object.values(coverage).filter(Boolean).length * 25;
296
+ const coverageColor = coveragePercent === 100 ? chalk.green : coveragePercent >= 75 ? chalk.yellow : chalk.red;
297
+
298
+ console.log(chalk.bold('Coverage:'));
299
+ console.log(coverageColor(` ${coveragePercent}% (${Object.values(coverage).filter(Boolean).length}/4 stages)`));
300
+ console.log();
301
+
302
+ process.exit(coveragePercent === 100 ? 0 : 1);
303
+ } catch (error) {
304
+ console.error(chalk.red('✗ Error:'), error.message);
305
+ process.exit(1);
306
+ }
307
+ });
308
+
309
+ // Validate 100% coverage
310
+ program
311
+ .command('validate')
312
+ .description('Validate 100% traceability coverage (Constitutional Article V)')
313
+ .option('--requirements <path>', 'Requirements directory', 'docs/requirements')
314
+ .option('--design <path>', 'Design directory', 'docs/design')
315
+ .option('--tasks <path>', 'Tasks directory', 'docs/tasks')
316
+ .option('--code <path>', 'Source code directory', 'src')
317
+ .option('--tests <path>', 'Tests directory', 'tests')
318
+ .option('--strict', 'Fail on any gaps (default: true)', true)
319
+ .action(async (options) => {
320
+ try {
321
+ console.log(chalk.bold('\n🔍 Validating Traceability (Article V)\n'));
322
+
323
+ const analyzer = new TraceabilityAnalyzer(process.cwd());
324
+ const validation = await analyzer.validate(options);
325
+
326
+ console.log(chalk.bold('Article V: Complete Traceability'));
327
+ console.log();
328
+
329
+ if (validation.passed) {
330
+ console.log(chalk.green('✓ 100% traceability achieved'));
331
+ console.log(chalk.dim(` Requirements: ${validation.coverage.totalRequirements}`));
332
+ console.log(chalk.dim(` Design Coverage: ${validation.coverage.designCoverage}%`));
333
+ console.log(chalk.dim(` Tasks Coverage: ${validation.coverage.tasksCoverage}%`));
334
+ console.log(chalk.dim(` Code Coverage: ${validation.coverage.codeCoverage}%`));
335
+ console.log(chalk.dim(` Test Coverage: ${validation.coverage.testsCoverage}%`));
336
+ console.log();
337
+ process.exit(0);
338
+ } else {
339
+ console.log(chalk.red('✗ Traceability gaps detected'));
340
+ console.log();
341
+
342
+ if (validation.gaps.orphanedRequirements.length > 0) {
343
+ console.log(chalk.red(` Orphaned Requirements: ${validation.gaps.orphanedRequirements.length}`));
344
+ }
345
+ if (validation.gaps.untestedCode.length > 0) {
346
+ console.log(chalk.red(` Untested Code: ${validation.gaps.untestedCode.length}`));
347
+ }
348
+ if (validation.gaps.missingTests.length > 0) {
349
+ console.log(chalk.red(` Missing Tests: ${validation.gaps.missingTests.length}`));
350
+ }
351
+
352
+ console.log();
353
+ console.log(chalk.dim('Run `musubi-trace gaps` for detailed gap analysis'));
354
+ console.log();
355
+ process.exit(1);
356
+ }
357
+ } catch (error) {
358
+ console.error(chalk.red('✗ Error:'), error.message);
359
+ process.exit(1);
360
+ }
361
+ });
362
+
363
+ program.parse(process.argv);
364
+
365
+ if (!process.argv.slice(2).length) {
366
+ program.outputHelp();
367
+ }