musubi-sdd 0.8.5 → 0.8.7
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 +20 -1
- package/README.md +20 -1
- package/bin/musubi-change.js +250 -0
- package/bin/musubi-design.js +0 -0
- package/bin/musubi-gaps.js +199 -0
- package/bin/musubi-tasks.js +0 -0
- package/bin/musubi-trace.js +0 -0
- package/package.json +4 -2
- package/src/analyzers/gap-detector.js +508 -0
- package/src/managers/change.js +476 -0
package/README.ja.md
CHANGED
|
@@ -15,7 +15,8 @@ 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
|
+
- 🔍 **ギャップ検出システム** - 孤立した要件とテストされていないコードを特定(v0.8.7)
|
|
19
20
|
- 🧭 **自動更新プロジェクトメモリ** - ステアリングシステムがアーキテクチャ、技術スタック、製品コンテキストを維持
|
|
20
21
|
- 🚀 **自動オンボーディング** - `musubi-onboard` が既存プロジェクトを分析し、ステアリングドキュメントを生成(2-5分)
|
|
21
22
|
- 🔄 **自動同期** - `musubi-sync` がコードベースの変更を検出し、ステアリングドキュメントを最新に保つ
|
|
@@ -138,6 +139,24 @@ musubi-trace coverage --min-coverage 100 # 100%カバレッジ要求
|
|
|
138
139
|
musubi-trace gaps # 孤立した要件/コード検出
|
|
139
140
|
musubi-trace requirement REQ-AUTH-001 # 特定要件をトレース
|
|
140
141
|
musubi-trace validate # 100%トレーサビリティ検証(第5条)
|
|
142
|
+
|
|
143
|
+
# ブラウンフィールドプロジェクト向け変更管理(v0.8.6)
|
|
144
|
+
musubi-change init CHANGE-001 --title "認証機能追加" # 変更提案を作成
|
|
145
|
+
musubi-change validate CHANGE-001 --verbose # 差分仕様を検証
|
|
146
|
+
musubi-change apply CHANGE-001 --dry-run # 変更をプレビュー
|
|
147
|
+
musubi-change apply CHANGE-001 # コードベースに変更を適用
|
|
148
|
+
musubi-change archive CHANGE-001 # specs/にアーカイブ
|
|
149
|
+
musubi-change list --status pending # 保留中の変更をリスト
|
|
150
|
+
musubi-change list --format json # JSON形式でリスト
|
|
151
|
+
|
|
152
|
+
# ギャップ検出とカバレッジ検証(v0.8.7)
|
|
153
|
+
musubi-gaps detect # 全ギャップを検出
|
|
154
|
+
musubi-gaps detect --verbose # 詳細なギャップ情報を表示
|
|
155
|
+
musubi-gaps requirements # 孤立した要件を検出
|
|
156
|
+
musubi-gaps code # テストされていないコードを検出
|
|
157
|
+
musubi-gaps coverage # カバレッジ統計を計算
|
|
158
|
+
musubi-gaps coverage --min-coverage 100 # 100%カバレッジを要求
|
|
159
|
+
musubi-gaps detect --format markdown > gaps.md # ギャップレポートをエクスポート
|
|
141
160
|
```
|
|
142
161
|
|
|
143
162
|
### プロジェクトタイプ
|
package/README.md
CHANGED
|
@@ -19,7 +19,8 @@ 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
|
-
- 🔄 **
|
|
22
|
+
- 🔄 **Change Management System** - Delta specifications for brownfield projects (v0.8.6)
|
|
23
|
+
- 🔍 **Gap Detection System** - Identify orphaned requirements and untested code (v0.8.7)
|
|
23
24
|
- 🧭 **Auto-Updating Project Memory** - Steering system maintains architecture, tech stack, and product context
|
|
24
25
|
- 🚀 **Automatic Onboarding** - `musubi-onboard` analyzes existing projects and generates steering docs (2-5 minutes)
|
|
25
26
|
- 🔄 **Auto-Sync** - `musubi-sync` detects codebase changes and keeps steering docs current
|
|
@@ -142,6 +143,24 @@ musubi-trace coverage --min-coverage 100 # Require 100% coverage
|
|
|
142
143
|
musubi-trace gaps # Detect orphaned requirements/code
|
|
143
144
|
musubi-trace requirement REQ-AUTH-001 # Trace specific requirement
|
|
144
145
|
musubi-trace validate # Validate 100% traceability (Article V)
|
|
146
|
+
|
|
147
|
+
# Change management for brownfield projects (v0.8.6)
|
|
148
|
+
musubi-change init CHANGE-001 --title "Add authentication" # Create change proposal
|
|
149
|
+
musubi-change validate CHANGE-001 --verbose # Validate delta specification
|
|
150
|
+
musubi-change apply CHANGE-001 --dry-run # Preview changes
|
|
151
|
+
musubi-change apply CHANGE-001 # Apply changes to codebase
|
|
152
|
+
musubi-change archive CHANGE-001 # Archive to specs/
|
|
153
|
+
musubi-change list --status pending # List pending changes
|
|
154
|
+
musubi-change list --format json # List in JSON format
|
|
155
|
+
|
|
156
|
+
# Gap detection and coverage validation (v0.8.7)
|
|
157
|
+
musubi-gaps detect # Detect all gaps
|
|
158
|
+
musubi-gaps detect --verbose # Show detailed gap information
|
|
159
|
+
musubi-gaps requirements # Detect orphaned requirements
|
|
160
|
+
musubi-gaps code # Detect untested code
|
|
161
|
+
musubi-gaps coverage # Calculate coverage statistics
|
|
162
|
+
musubi-gaps coverage --min-coverage 100 # Require 100% coverage
|
|
163
|
+
musubi-gaps detect --format markdown > gaps.md # Export gap report
|
|
145
164
|
```
|
|
146
165
|
|
|
147
166
|
### 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();
|
package/bin/musubi-design.js
CHANGED
|
File without changes
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MUSUBI Gap Detection CLI
|
|
5
|
+
* Identifies orphaned requirements, untested code, and missing traceability
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { Command } = require('commander');
|
|
9
|
+
const chalk = require('chalk');
|
|
10
|
+
const GapDetector = require('../src/analyzers/gap-detector');
|
|
11
|
+
|
|
12
|
+
const program = new Command();
|
|
13
|
+
|
|
14
|
+
program
|
|
15
|
+
.name('musubi-gaps')
|
|
16
|
+
.description('Detect gaps in requirements, code, and test coverage')
|
|
17
|
+
.version('0.8.7');
|
|
18
|
+
|
|
19
|
+
program
|
|
20
|
+
.command('detect')
|
|
21
|
+
.description('Detect all gaps (requirements, code, tests)')
|
|
22
|
+
.option('--requirements <dir>', 'Requirements directory', 'docs/requirements')
|
|
23
|
+
.option('--design <dir>', 'Design directory', 'docs/design')
|
|
24
|
+
.option('--tasks <dir>', 'Tasks directory', 'docs/tasks')
|
|
25
|
+
.option('--src <dir>', 'Source code directory', 'src')
|
|
26
|
+
.option('--tests <dir>', 'Test directory', 'tests')
|
|
27
|
+
.option('--format <format>', 'Output format (table|json|markdown)', 'table')
|
|
28
|
+
.option('--verbose', 'Show detailed gap information')
|
|
29
|
+
.action(async (options) => {
|
|
30
|
+
try {
|
|
31
|
+
console.log(chalk.blue('\n🔍 Detecting gaps in traceability...\n'));
|
|
32
|
+
|
|
33
|
+
const detector = new GapDetector({
|
|
34
|
+
requirementsDir: options.requirements,
|
|
35
|
+
designDir: options.design,
|
|
36
|
+
tasksDir: options.tasks,
|
|
37
|
+
srcDir: options.src,
|
|
38
|
+
testsDir: options.tests
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const gaps = await detector.detectAllGaps();
|
|
42
|
+
|
|
43
|
+
if (options.format === 'json') {
|
|
44
|
+
console.log(JSON.stringify(gaps, null, 2));
|
|
45
|
+
} else if (options.format === 'markdown') {
|
|
46
|
+
console.log(detector.formatMarkdown(gaps));
|
|
47
|
+
} else {
|
|
48
|
+
detector.displayTable(gaps, options.verbose);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const totalGaps = gaps.orphanedRequirements.length +
|
|
52
|
+
gaps.unimplementedRequirements.length +
|
|
53
|
+
gaps.untestedCode.length +
|
|
54
|
+
gaps.missingTests.length;
|
|
55
|
+
|
|
56
|
+
if (totalGaps === 0) {
|
|
57
|
+
console.log(chalk.green('\n✓ No gaps detected! 100% traceability achieved.\n'));
|
|
58
|
+
process.exit(0);
|
|
59
|
+
} else {
|
|
60
|
+
console.log(chalk.yellow(`\n⚠ Found ${totalGaps} gap(s). See details above.\n`));
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
program
|
|
70
|
+
.command('requirements')
|
|
71
|
+
.description('Detect orphaned requirements (no design/code)')
|
|
72
|
+
.option('--requirements <dir>', 'Requirements directory', 'docs/requirements')
|
|
73
|
+
.option('--design <dir>', 'Design directory', 'docs/design')
|
|
74
|
+
.option('--tasks <dir>', 'Tasks directory', 'docs/tasks')
|
|
75
|
+
.option('--format <format>', 'Output format (table|json|markdown)', 'table')
|
|
76
|
+
.action(async (options) => {
|
|
77
|
+
try {
|
|
78
|
+
console.log(chalk.blue('\n🔍 Detecting orphaned requirements...\n'));
|
|
79
|
+
|
|
80
|
+
const detector = new GapDetector({
|
|
81
|
+
requirementsDir: options.requirements,
|
|
82
|
+
designDir: options.design,
|
|
83
|
+
tasksDir: options.tasks
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const orphaned = await detector.detectOrphanedRequirements();
|
|
87
|
+
|
|
88
|
+
if (options.format === 'json') {
|
|
89
|
+
console.log(JSON.stringify(orphaned, null, 2));
|
|
90
|
+
} else if (options.format === 'markdown') {
|
|
91
|
+
console.log(detector.formatRequirementsMarkdown(orphaned));
|
|
92
|
+
} else {
|
|
93
|
+
detector.displayRequirementsTable(orphaned);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (orphaned.length === 0) {
|
|
97
|
+
console.log(chalk.green('\n✓ No orphaned requirements detected.\n'));
|
|
98
|
+
process.exit(0);
|
|
99
|
+
} else {
|
|
100
|
+
console.log(chalk.yellow(`\n⚠ Found ${orphaned.length} orphaned requirement(s).\n`));
|
|
101
|
+
process.exit(1);
|
|
102
|
+
}
|
|
103
|
+
} catch (error) {
|
|
104
|
+
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
program
|
|
110
|
+
.command('code')
|
|
111
|
+
.description('Detect untested code')
|
|
112
|
+
.option('--src <dir>', 'Source code directory', 'src')
|
|
113
|
+
.option('--tests <dir>', 'Test directory', 'tests')
|
|
114
|
+
.option('--format <format>', 'Output format (table|json|markdown)', 'table')
|
|
115
|
+
.action(async (options) => {
|
|
116
|
+
try {
|
|
117
|
+
console.log(chalk.blue('\n🔍 Detecting untested code...\n'));
|
|
118
|
+
|
|
119
|
+
const detector = new GapDetector({
|
|
120
|
+
srcDir: options.src,
|
|
121
|
+
testsDir: options.tests
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const untested = await detector.detectUntestedCode();
|
|
125
|
+
|
|
126
|
+
if (options.format === 'json') {
|
|
127
|
+
console.log(JSON.stringify(untested, null, 2));
|
|
128
|
+
} else if (options.format === 'markdown') {
|
|
129
|
+
console.log(detector.formatCodeMarkdown(untested));
|
|
130
|
+
} else {
|
|
131
|
+
detector.displayCodeTable(untested);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (untested.length === 0) {
|
|
135
|
+
console.log(chalk.green('\n✓ All code is tested.\n'));
|
|
136
|
+
process.exit(0);
|
|
137
|
+
} else {
|
|
138
|
+
console.log(chalk.yellow(`\n⚠ Found ${untested.length} untested file(s).\n`));
|
|
139
|
+
process.exit(1);
|
|
140
|
+
}
|
|
141
|
+
} catch (error) {
|
|
142
|
+
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
143
|
+
process.exit(1);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
program
|
|
148
|
+
.command('coverage')
|
|
149
|
+
.description('Calculate coverage statistics')
|
|
150
|
+
.option('--requirements <dir>', 'Requirements directory', 'docs/requirements')
|
|
151
|
+
.option('--design <dir>', 'Design directory', 'docs/design')
|
|
152
|
+
.option('--tasks <dir>', 'Tasks directory', 'docs/tasks')
|
|
153
|
+
.option('--src <dir>', 'Source code directory', 'src')
|
|
154
|
+
.option('--tests <dir>', 'Test directory', 'tests')
|
|
155
|
+
.option('--min-coverage <percent>', 'Minimum required coverage', '100')
|
|
156
|
+
.option('--format <format>', 'Output format (table|json|markdown)', 'table')
|
|
157
|
+
.action(async (options) => {
|
|
158
|
+
try {
|
|
159
|
+
console.log(chalk.blue('\n📊 Calculating coverage statistics...\n'));
|
|
160
|
+
|
|
161
|
+
const detector = new GapDetector({
|
|
162
|
+
requirementsDir: options.requirements,
|
|
163
|
+
designDir: options.design,
|
|
164
|
+
tasksDir: options.tasks,
|
|
165
|
+
srcDir: options.src,
|
|
166
|
+
testsDir: options.tests
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const coverage = await detector.calculateCoverage();
|
|
170
|
+
const minCoverage = parseFloat(options.minCoverage);
|
|
171
|
+
|
|
172
|
+
if (options.format === 'json') {
|
|
173
|
+
console.log(JSON.stringify(coverage, null, 2));
|
|
174
|
+
} else if (options.format === 'markdown') {
|
|
175
|
+
console.log(detector.formatCoverageMarkdown(coverage));
|
|
176
|
+
} else {
|
|
177
|
+
detector.displayCoverageTable(coverage);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const avgCoverage = (
|
|
181
|
+
coverage.requirements.implementationCoverage +
|
|
182
|
+
coverage.requirements.testCoverage +
|
|
183
|
+
coverage.code.testCoverage
|
|
184
|
+
) / 3;
|
|
185
|
+
|
|
186
|
+
if (avgCoverage >= minCoverage) {
|
|
187
|
+
console.log(chalk.green(`\n✓ Coverage ${avgCoverage.toFixed(1)}% meets minimum ${minCoverage}%\n`));
|
|
188
|
+
process.exit(0);
|
|
189
|
+
} else {
|
|
190
|
+
console.log(chalk.red(`\n✗ Coverage ${avgCoverage.toFixed(1)}% below minimum ${minCoverage}%\n`));
|
|
191
|
+
process.exit(1);
|
|
192
|
+
}
|
|
193
|
+
} catch (error) {
|
|
194
|
+
console.error(chalk.red(`\n✗ Error: ${error.message}\n`));
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
program.parse(process.argv);
|
package/bin/musubi-tasks.js
CHANGED
|
File without changes
|
package/bin/musubi-trace.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "musubi-sdd",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.7",
|
|
4
4
|
"description": "Ultimate Specification Driven Development Tool with 25 Agents for 7 AI Coding Platforms (Claude Code, GitHub Copilot, Cursor, Gemini CLI, Windsurf, Codex, Qwen Code)",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,9 @@
|
|
|
15
15
|
"musubi-requirements": "bin/musubi-requirements.js",
|
|
16
16
|
"musubi-design": "bin/musubi-design.js",
|
|
17
17
|
"musubi-tasks": "bin/musubi-tasks.js",
|
|
18
|
-
"musubi-trace": "bin/musubi-trace.js"
|
|
18
|
+
"musubi-trace": "bin/musubi-trace.js",
|
|
19
|
+
"musubi-gaps": "bin/musubi-gaps.js",
|
|
20
|
+
"musubi-change": "bin/musubi-change.js"
|
|
19
21
|
},
|
|
20
22
|
"scripts": {
|
|
21
23
|
"test": "jest",
|