musubi-sdd 0.8.3 → 0.8.5
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 +18 -0
- package/README.md +18 -0
- package/bin/musubi-tasks.js +365 -0
- package/bin/musubi-trace.js +367 -0
- package/package.json +4 -2
- package/src/analyzers/traceability.js +524 -0
- package/src/generators/tasks.js +485 -0
package/README.ja.md
CHANGED
|
@@ -120,6 +120,24 @@ musubi-design add-c4 container --format plantuml # PlantUMLでコンテナ図
|
|
|
120
120
|
musubi-design add-adr "JWTトークン使用" # アーキテクチャ決定記録追加
|
|
121
121
|
musubi-design validate # 設計完全性を検証
|
|
122
122
|
musubi-design trace # 要件トレーサビリティ表示
|
|
123
|
+
|
|
124
|
+
# 設計をタスクに分解(v0.8.4)
|
|
125
|
+
musubi-tasks init "ユーザー認証" # タスク分解を初期化
|
|
126
|
+
musubi-tasks add "データベーススキーマ" # インタラクティブにタスク追加
|
|
127
|
+
musubi-tasks list # 全タスクリスト表示
|
|
128
|
+
musubi-tasks list --priority P0 # 重要タスクのみ表示
|
|
129
|
+
musubi-tasks update 001 "In Progress" # タスクステータス更新
|
|
130
|
+
musubi-tasks validate # タスク完全性を検証
|
|
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条)
|
|
123
141
|
```
|
|
124
142
|
|
|
125
143
|
### プロジェクトタイプ
|
package/README.md
CHANGED
|
@@ -124,6 +124,24 @@ musubi-design add-c4 container --format plantuml # Add Container with PlantUML
|
|
|
124
124
|
musubi-design add-adr "Use JWT for tokens" # Add Architecture Decision
|
|
125
125
|
musubi-design validate # Validate design completeness
|
|
126
126
|
musubi-design trace # Show requirements traceability
|
|
127
|
+
|
|
128
|
+
# Break down design into tasks (v0.8.4)
|
|
129
|
+
musubi-tasks init "User Authentication" # Initialize task breakdown
|
|
130
|
+
musubi-tasks add "Database Schema" # Add task interactively
|
|
131
|
+
musubi-tasks list # List all tasks
|
|
132
|
+
musubi-tasks list --priority P0 # List critical tasks
|
|
133
|
+
musubi-tasks update 001 "In Progress" # Update task status
|
|
134
|
+
musubi-tasks validate # Validate task completeness
|
|
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)
|
|
127
145
|
```
|
|
128
146
|
|
|
129
147
|
### Project Types
|
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* MUSUBI Task Breakdown System CLI
|
|
5
|
+
*
|
|
6
|
+
* Breaks down design into actionable implementation tasks
|
|
7
|
+
* Complies with Article I-IX and P0-P3 priority labels
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* musubi-tasks init <feature> # Initialize task breakdown
|
|
11
|
+
* musubi-tasks add <title> # Add task with interactive prompts
|
|
12
|
+
* musubi-tasks list # List all tasks
|
|
13
|
+
* musubi-tasks update <id> <status> # Update task status
|
|
14
|
+
* musubi-tasks validate # Validate task breakdown
|
|
15
|
+
* musubi-tasks graph # Generate dependency graph
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const { Command } = require('commander');
|
|
19
|
+
const chalk = require('chalk');
|
|
20
|
+
const TasksGenerator = require('../src/generators/tasks');
|
|
21
|
+
|
|
22
|
+
let inquirer;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Initialize inquirer (ESM module in v9+)
|
|
26
|
+
*/
|
|
27
|
+
async function getInquirer() {
|
|
28
|
+
if (!inquirer) {
|
|
29
|
+
inquirer = (await import('inquirer')).default;
|
|
30
|
+
}
|
|
31
|
+
return inquirer;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const program = new Command();
|
|
35
|
+
|
|
36
|
+
program
|
|
37
|
+
.name('musubi-tasks')
|
|
38
|
+
.description('MUSUBI Task Breakdown System - Break down design into actionable tasks')
|
|
39
|
+
.version('0.8.4');
|
|
40
|
+
|
|
41
|
+
// Initialize task breakdown
|
|
42
|
+
program
|
|
43
|
+
.command('init <feature>')
|
|
44
|
+
.description('Initialize task breakdown document from design')
|
|
45
|
+
.option('-o, --output <path>', 'Output directory', 'docs/tasks')
|
|
46
|
+
.option('-a, --author <name>', 'Author name')
|
|
47
|
+
.option('--project <name>', 'Project name')
|
|
48
|
+
.option('-d, --design <path>', 'Design document path')
|
|
49
|
+
.option('-r, --requirements <path>', 'Requirements document path')
|
|
50
|
+
.action(async (feature, options) => {
|
|
51
|
+
try {
|
|
52
|
+
console.log(chalk.bold(`\n📋 Initializing task breakdown for: ${feature}\n`));
|
|
53
|
+
|
|
54
|
+
const generator = new TasksGenerator(process.cwd());
|
|
55
|
+
const result = await generator.init(feature, options);
|
|
56
|
+
|
|
57
|
+
console.log(chalk.green('\n✓ Task breakdown document created'));
|
|
58
|
+
console.log(chalk.dim(` ${result.path}`));
|
|
59
|
+
console.log();
|
|
60
|
+
console.log(chalk.bold('Next steps:'));
|
|
61
|
+
console.log(chalk.dim(` 1. Edit ${result.path}`));
|
|
62
|
+
console.log(chalk.dim(' 2. Add tasks: musubi-tasks add <title>'));
|
|
63
|
+
console.log(chalk.dim(' 3. Validate: musubi-tasks validate'));
|
|
64
|
+
console.log(chalk.dim(' 4. Generate graph: musubi-tasks graph'));
|
|
65
|
+
console.log();
|
|
66
|
+
|
|
67
|
+
process.exit(0);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.error(chalk.red('✗ Error:'), error.message);
|
|
70
|
+
process.exit(1);
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
// Add task
|
|
75
|
+
program
|
|
76
|
+
.command('add <title>')
|
|
77
|
+
.description('Add new task with interactive prompts')
|
|
78
|
+
.option('-f, --file <path>', 'Task breakdown file path')
|
|
79
|
+
.action(async (title, options) => {
|
|
80
|
+
try {
|
|
81
|
+
console.log(chalk.bold(`\n📝 Adding task: ${title}\n`));
|
|
82
|
+
|
|
83
|
+
const generator = new TasksGenerator(process.cwd());
|
|
84
|
+
|
|
85
|
+
// Find task file
|
|
86
|
+
let taskFile = options.file;
|
|
87
|
+
if (!taskFile) {
|
|
88
|
+
const files = await generator.findTaskFiles();
|
|
89
|
+
if (files.length === 0) {
|
|
90
|
+
console.error(chalk.red('✗ No task files found'));
|
|
91
|
+
console.log(chalk.dim(' Run: musubi-tasks init <feature>'));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (files.length === 1) {
|
|
96
|
+
taskFile = files[0];
|
|
97
|
+
} else {
|
|
98
|
+
const inquirerInst = await getInquirer();
|
|
99
|
+
const answer = await inquirerInst.prompt([{
|
|
100
|
+
type: 'list',
|
|
101
|
+
name: 'file',
|
|
102
|
+
message: 'Select task file:',
|
|
103
|
+
choices: files
|
|
104
|
+
}]);
|
|
105
|
+
taskFile = answer.file;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Interactive prompts for task details
|
|
110
|
+
const inquirerInst2 = await getInquirer();
|
|
111
|
+
const answers = await inquirerInst2.prompt([
|
|
112
|
+
{
|
|
113
|
+
type: 'list',
|
|
114
|
+
name: 'priority',
|
|
115
|
+
message: 'Task priority:',
|
|
116
|
+
choices: [
|
|
117
|
+
{ name: 'P0 (Critical - Launch Blocker)', value: 'P0' },
|
|
118
|
+
{ name: 'P1 (High - Important)', value: 'P1' },
|
|
119
|
+
{ name: 'P2 (Medium - Nice to have)', value: 'P2' },
|
|
120
|
+
{ name: 'P3 (Low - Future)', value: 'P3' }
|
|
121
|
+
],
|
|
122
|
+
default: 'P1'
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
type: 'list',
|
|
126
|
+
name: 'storyPoints',
|
|
127
|
+
message: 'Story points (Fibonacci):',
|
|
128
|
+
choices: ['1', '2', '3', '5', '8', '13'],
|
|
129
|
+
default: '3'
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
type: 'input',
|
|
133
|
+
name: 'estimatedHours',
|
|
134
|
+
message: 'Estimated hours:',
|
|
135
|
+
default: '4',
|
|
136
|
+
validate: (input) => !isNaN(input) || 'Must be a number'
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: 'input',
|
|
140
|
+
name: 'assignee',
|
|
141
|
+
message: 'Assignee (optional):',
|
|
142
|
+
default: '[Unassigned]'
|
|
143
|
+
},
|
|
144
|
+
{
|
|
145
|
+
type: 'input',
|
|
146
|
+
name: 'description',
|
|
147
|
+
message: 'Task description:',
|
|
148
|
+
validate: (input) => input.length > 0 || 'Description is required'
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: 'input',
|
|
152
|
+
name: 'requirements',
|
|
153
|
+
message: 'Requirements (comma-separated REQ-XXX-NNN):',
|
|
154
|
+
filter: (input) => input.split(',').map(s => s.trim()).filter(s => s.length > 0)
|
|
155
|
+
},
|
|
156
|
+
{
|
|
157
|
+
type: 'input',
|
|
158
|
+
name: 'acceptance',
|
|
159
|
+
message: 'Acceptance criteria (semicolon-separated):',
|
|
160
|
+
filter: (input) => input.split(';').map(s => s.trim()).filter(s => s.length > 0)
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
type: 'input',
|
|
164
|
+
name: 'dependencies',
|
|
165
|
+
message: 'Dependencies (comma-separated TASK-XXX):',
|
|
166
|
+
filter: (input) => input.split(',').map(s => s.trim()).filter(s => s.length > 0)
|
|
167
|
+
}
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
const task = {
|
|
171
|
+
title,
|
|
172
|
+
priority: answers.priority,
|
|
173
|
+
storyPoints: parseInt(answers.storyPoints),
|
|
174
|
+
estimatedHours: parseFloat(answers.estimatedHours),
|
|
175
|
+
assignee: answers.assignee,
|
|
176
|
+
status: 'Not Started',
|
|
177
|
+
description: answers.description,
|
|
178
|
+
requirements: answers.requirements,
|
|
179
|
+
acceptance: answers.acceptance,
|
|
180
|
+
dependencies: answers.dependencies
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
const result = await generator.addTask(taskFile, task);
|
|
184
|
+
|
|
185
|
+
console.log(chalk.green('\n✓ Task added:'));
|
|
186
|
+
console.log(chalk.dim(` TASK-${result.id}: ${result.title}`));
|
|
187
|
+
console.log(chalk.dim(` Priority: ${result.priority}, Points: ${result.storyPoints}, Hours: ${result.estimatedHours}`));
|
|
188
|
+
console.log();
|
|
189
|
+
|
|
190
|
+
process.exit(0);
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.error(chalk.red('✗ Error:'), error.message);
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
// List all tasks
|
|
198
|
+
program
|
|
199
|
+
.command('list')
|
|
200
|
+
.description('List all tasks')
|
|
201
|
+
.option('-f, --file <path>', 'Specific task file')
|
|
202
|
+
.option('--format <type>', 'Output format (table|json|markdown)', 'table')
|
|
203
|
+
.option('--priority <level>', 'Filter by priority (P0|P1|P2|P3)')
|
|
204
|
+
.option('--status <status>', 'Filter by status')
|
|
205
|
+
.action(async (options) => {
|
|
206
|
+
try {
|
|
207
|
+
console.log(chalk.bold('\n📋 Task List\n'));
|
|
208
|
+
|
|
209
|
+
const generator = new TasksGenerator(process.cwd());
|
|
210
|
+
const result = await generator.list(options);
|
|
211
|
+
|
|
212
|
+
if (result.tasks.length === 0) {
|
|
213
|
+
console.log(chalk.yellow('No tasks found'));
|
|
214
|
+
console.log(chalk.dim(' Run: musubi-tasks add <title>'));
|
|
215
|
+
process.exit(0);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (options.format === 'json') {
|
|
219
|
+
console.log(JSON.stringify(result, null, 2));
|
|
220
|
+
} else if (options.format === 'markdown') {
|
|
221
|
+
result.tasks.forEach(t => {
|
|
222
|
+
console.log(`### TASK-${t.id}: ${t.title}`);
|
|
223
|
+
console.log(`**Priority**: ${t.priority} | **Points**: ${t.storyPoints} | **Status**: ${t.status}`);
|
|
224
|
+
console.log();
|
|
225
|
+
});
|
|
226
|
+
} else {
|
|
227
|
+
// Table format
|
|
228
|
+
console.log(chalk.bold('Summary:'));
|
|
229
|
+
console.log(chalk.dim(` Total: ${result.summary.total} tasks`));
|
|
230
|
+
console.log(chalk.dim(` P0: ${result.summary.p0}, P1: ${result.summary.p1}, P2: ${result.summary.p2}, P3: ${result.summary.p3}`));
|
|
231
|
+
console.log(chalk.dim(` Story Points: ${result.summary.totalPoints}, Hours: ${result.summary.totalHours}`));
|
|
232
|
+
console.log();
|
|
233
|
+
|
|
234
|
+
result.tasks.forEach(t => {
|
|
235
|
+
const priorityColor = {
|
|
236
|
+
'P0': chalk.red,
|
|
237
|
+
'P1': chalk.yellow,
|
|
238
|
+
'P2': chalk.blue,
|
|
239
|
+
'P3': chalk.gray
|
|
240
|
+
}[t.priority] || chalk.white;
|
|
241
|
+
|
|
242
|
+
console.log(priorityColor(` TASK-${t.id}: ${t.title}`));
|
|
243
|
+
console.log(chalk.dim(` ${t.priority} | ${t.storyPoints}pts | ${t.estimatedHours}h | ${t.status}`));
|
|
244
|
+
});
|
|
245
|
+
console.log();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
process.exit(0);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
console.error(chalk.red('✗ Error:'), error.message);
|
|
251
|
+
process.exit(1);
|
|
252
|
+
}
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
// Update task status
|
|
256
|
+
program
|
|
257
|
+
.command('update <id> <status>')
|
|
258
|
+
.description('Update task status')
|
|
259
|
+
.option('-f, --file <path>', 'Task breakdown file path')
|
|
260
|
+
.action(async (id, status, options) => {
|
|
261
|
+
try {
|
|
262
|
+
console.log(chalk.bold(`\n📝 Updating TASK-${id}\n`));
|
|
263
|
+
|
|
264
|
+
const generator = new TasksGenerator(process.cwd());
|
|
265
|
+
const result = await generator.updateStatus(id, status, options.file);
|
|
266
|
+
|
|
267
|
+
console.log(chalk.green('✓ Task updated:'));
|
|
268
|
+
console.log(chalk.dim(` TASK-${result.id}: ${result.title}`));
|
|
269
|
+
console.log(chalk.dim(` Status: ${result.oldStatus} → ${result.newStatus}`));
|
|
270
|
+
console.log();
|
|
271
|
+
|
|
272
|
+
process.exit(0);
|
|
273
|
+
} catch (error) {
|
|
274
|
+
console.error(chalk.red('✗ Error:'), error.message);
|
|
275
|
+
process.exit(1);
|
|
276
|
+
}
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
// Validate task breakdown
|
|
280
|
+
program
|
|
281
|
+
.command('validate')
|
|
282
|
+
.description('Validate task breakdown completeness')
|
|
283
|
+
.option('-f, --file <path>', 'Specific task file')
|
|
284
|
+
.option('-v, --verbose', 'Show detailed validation results')
|
|
285
|
+
.action(async (options) => {
|
|
286
|
+
try {
|
|
287
|
+
console.log(chalk.bold('\n🔍 Validating Task Breakdown\n'));
|
|
288
|
+
|
|
289
|
+
const generator = new TasksGenerator(process.cwd());
|
|
290
|
+
const results = await generator.validate(options.file);
|
|
291
|
+
|
|
292
|
+
if (results.passed) {
|
|
293
|
+
console.log(chalk.green('✓ All tasks valid\n'));
|
|
294
|
+
} else {
|
|
295
|
+
console.log(chalk.red('✗ Validation failed\n'));
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
console.log(chalk.bold('Summary:'));
|
|
299
|
+
console.log(chalk.dim(` Total: ${results.total}`));
|
|
300
|
+
console.log(chalk.green(` Valid: ${results.valid}`));
|
|
301
|
+
console.log(chalk.red(` Invalid: ${results.invalid}`));
|
|
302
|
+
console.log();
|
|
303
|
+
|
|
304
|
+
if (results.violations.length > 0) {
|
|
305
|
+
console.log(chalk.bold.red('Violations:'));
|
|
306
|
+
results.violations.forEach(v => {
|
|
307
|
+
console.log(chalk.red(` • ${v}`));
|
|
308
|
+
});
|
|
309
|
+
console.log();
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
if (options.verbose && results.details) {
|
|
313
|
+
console.log(chalk.bold('Details:'));
|
|
314
|
+
results.details.forEach(d => {
|
|
315
|
+
const icon = d.valid ? chalk.green('✓') : chalk.red('✗');
|
|
316
|
+
console.log(` ${icon} ${d.file}: ${d.message}`);
|
|
317
|
+
});
|
|
318
|
+
console.log();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
process.exit(results.passed ? 0 : 1);
|
|
322
|
+
} catch (error) {
|
|
323
|
+
console.error(chalk.red('✗ Error:'), error.message);
|
|
324
|
+
process.exit(1);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
// Generate dependency graph
|
|
329
|
+
program
|
|
330
|
+
.command('graph')
|
|
331
|
+
.description('Generate task dependency graph')
|
|
332
|
+
.option('-f, --file <path>', 'Task breakdown file path')
|
|
333
|
+
.option('--format <type>', 'Output format (mermaid|dot)', 'mermaid')
|
|
334
|
+
.action(async (options) => {
|
|
335
|
+
try {
|
|
336
|
+
console.log(chalk.bold('\n📊 Generating Dependency Graph\n'));
|
|
337
|
+
|
|
338
|
+
const generator = new TasksGenerator(process.cwd());
|
|
339
|
+
const result = await generator.generateGraph(options);
|
|
340
|
+
|
|
341
|
+
console.log(chalk.green('✓ Dependency graph generated\n'));
|
|
342
|
+
console.log(chalk.bold('Graph:'));
|
|
343
|
+
console.log(chalk.cyan(result.graph));
|
|
344
|
+
console.log();
|
|
345
|
+
|
|
346
|
+
if (result.parallelGroups) {
|
|
347
|
+
console.log(chalk.bold('Parallel Execution Groups:'));
|
|
348
|
+
result.parallelGroups.forEach((group, i) => {
|
|
349
|
+
console.log(chalk.dim(` Group ${i + 1}: ${group.join(', ')}`));
|
|
350
|
+
});
|
|
351
|
+
console.log();
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
process.exit(0);
|
|
355
|
+
} catch (error) {
|
|
356
|
+
console.error(chalk.red('✗ Error:'), error.message);
|
|
357
|
+
process.exit(1);
|
|
358
|
+
}
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
program.parse(process.argv);
|
|
362
|
+
|
|
363
|
+
if (!process.argv.slice(2).length) {
|
|
364
|
+
program.outputHelp();
|
|
365
|
+
}
|