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 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
+ }