musubi-sdd 6.2.2 → 6.3.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.
Files changed (55) hide show
  1. package/README.ja.md +3 -3
  2. package/README.md +3 -3
  3. package/bin/musubi-dashboard.js +22 -13
  4. package/bin/musubi-design.js +3 -3
  5. package/bin/musubi-gaps.js +9 -9
  6. package/bin/musubi-init.js +14 -1310
  7. package/bin/musubi-requirements.js +1 -1
  8. package/bin/musubi-tasks.js +5 -5
  9. package/bin/musubi-trace.js +23 -23
  10. package/bin/musubi-upgrade.js +7 -2
  11. package/bin/musubi.js +1 -1
  12. package/package.json +2 -2
  13. package/src/analyzers/gap-detector.js +3 -3
  14. package/src/analyzers/traceability.js +17 -17
  15. package/src/cli/dashboard-cli.js +54 -60
  16. package/src/cli/init-generators.js +464 -0
  17. package/src/cli/init-helpers.js +884 -0
  18. package/src/constitutional/checker.js +67 -65
  19. package/src/constitutional/ci-reporter.js +50 -43
  20. package/src/constitutional/index.js +2 -2
  21. package/src/constitutional/phase-minus-one.js +22 -25
  22. package/src/constitutional/steering-sync.js +28 -39
  23. package/src/dashboard/index.js +2 -2
  24. package/src/dashboard/sprint-planner.js +17 -19
  25. package/src/dashboard/sprint-reporter.js +46 -37
  26. package/src/dashboard/transition-recorder.js +12 -18
  27. package/src/dashboard/workflow-dashboard.js +27 -38
  28. package/src/enterprise/error-recovery.js +109 -49
  29. package/src/enterprise/experiment-report.js +62 -36
  30. package/src/enterprise/index.js +5 -5
  31. package/src/enterprise/rollback-manager.js +28 -29
  32. package/src/enterprise/tech-article.js +41 -35
  33. package/src/generators/design.js +3 -3
  34. package/src/generators/requirements.js +5 -3
  35. package/src/generators/tasks.js +2 -2
  36. package/src/integrations/platforms.js +1 -1
  37. package/src/templates/agents/claude-code/CLAUDE.md +1 -1
  38. package/src/templates/agents/claude-code/skills/design-reviewer/SKILL.md +132 -113
  39. package/src/templates/agents/claude-code/skills/requirements-reviewer/SKILL.md +85 -56
  40. package/src/templates/agents/codex/AGENTS.md +2 -2
  41. package/src/templates/agents/cursor/AGENTS.md +2 -2
  42. package/src/templates/agents/gemini-cli/GEMINI.md +2 -2
  43. package/src/templates/agents/github-copilot/AGENTS.md +2 -2
  44. package/src/templates/agents/github-copilot/commands/sdd-requirements.prompt.md +23 -4
  45. package/src/templates/agents/qwen-code/QWEN.md +2 -2
  46. package/src/templates/agents/shared/AGENTS.md +1 -1
  47. package/src/templates/agents/windsurf/AGENTS.md +2 -2
  48. package/src/templates/skills/browser-agent.md +1 -1
  49. package/src/traceability/extractor.js +21 -20
  50. package/src/traceability/gap-detector.js +19 -17
  51. package/src/traceability/index.js +2 -2
  52. package/src/traceability/matrix-storage.js +20 -22
  53. package/src/validators/constitution.js +5 -2
  54. package/src/validators/critic-system.js +6 -6
  55. package/src/validators/traceability-validator.js +3 -3
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * SprintReporter Implementation
3
- *
3
+ *
4
4
  * Generates sprint completion reports.
5
- *
5
+ *
6
6
  * Requirement: IMP-6.2-003-04
7
7
  * Design: Section 4.4
8
8
  */
@@ -14,12 +14,12 @@ const path = require('path');
14
14
  * Default configuration
15
15
  */
16
16
  const DEFAULT_CONFIG = {
17
- storageDir: 'storage/reports'
17
+ storageDir: 'storage/reports',
18
18
  };
19
19
 
20
20
  /**
21
21
  * SprintReporter
22
- *
22
+ *
23
23
  * Generates and manages sprint reports.
24
24
  */
25
25
  class SprintReporter {
@@ -46,12 +46,12 @@ class SprintReporter {
46
46
  start: sprint.startDate,
47
47
  end: sprint.endDate,
48
48
  startedAt: sprint.startedAt,
49
- completedAt: sprint.completedAt
49
+ completedAt: sprint.completedAt,
50
50
  },
51
51
  metrics: this.calculateMetrics(sprint),
52
52
  taskSummary: this.summarizeTasks(sprint),
53
53
  velocityAnalysis: this.analyzeVelocity(sprint),
54
- recommendations: this.generateRecommendations(sprint)
54
+ recommendations: this.generateRecommendations(sprint),
55
55
  };
56
56
 
57
57
  await this.saveReport(report);
@@ -81,18 +81,14 @@ class SprintReporter {
81
81
  totalPoints,
82
82
  completedPoints,
83
83
  remainingPoints: totalPoints - completedPoints,
84
- completionRate: tasks.length > 0
85
- ? Math.round((completedTasks.length / tasks.length) * 100)
86
- : 0,
87
- pointsCompletionRate: totalPoints > 0
88
- ? Math.round((completedPoints / totalPoints) * 100)
89
- : 0,
84
+ completionRate:
85
+ tasks.length > 0 ? Math.round((completedTasks.length / tasks.length) * 100) : 0,
86
+ pointsCompletionRate: totalPoints > 0 ? Math.round((completedPoints / totalPoints) * 100) : 0,
90
87
  plannedVelocity,
91
88
  actualVelocity,
92
89
  velocityDiff,
93
- velocityAccuracy: plannedVelocity > 0
94
- ? Math.round((actualVelocity / plannedVelocity) * 100)
95
- : 0
90
+ velocityAccuracy:
91
+ plannedVelocity > 0 ? Math.round((actualVelocity / plannedVelocity) * 100) : 0,
96
92
  };
97
93
  }
98
94
 
@@ -107,34 +103,34 @@ class SprintReporter {
107
103
  const byStatus = {
108
104
  todo: tasks.filter(t => t.status === 'todo'),
109
105
  inProgress: tasks.filter(t => t.status === 'in-progress'),
110
- done: tasks.filter(t => t.status === 'done')
106
+ done: tasks.filter(t => t.status === 'done'),
111
107
  };
112
108
 
113
109
  const byPriority = {
114
110
  critical: tasks.filter(t => t.priority === 'critical'),
115
111
  high: tasks.filter(t => t.priority === 'high'),
116
112
  medium: tasks.filter(t => t.priority === 'medium'),
117
- low: tasks.filter(t => t.priority === 'low')
113
+ low: tasks.filter(t => t.priority === 'low'),
118
114
  };
119
115
 
120
116
  const completedByPriority = {
121
117
  critical: byPriority.critical.filter(t => t.status === 'done').length,
122
118
  high: byPriority.high.filter(t => t.status === 'done').length,
123
119
  medium: byPriority.medium.filter(t => t.status === 'done').length,
124
- low: byPriority.low.filter(t => t.status === 'done').length
120
+ low: byPriority.low.filter(t => t.status === 'done').length,
125
121
  };
126
122
 
127
123
  return {
128
124
  byStatus: {
129
125
  todo: byStatus.todo.length,
130
126
  inProgress: byStatus.inProgress.length,
131
- done: byStatus.done.length
127
+ done: byStatus.done.length,
132
128
  },
133
129
  byPriority: {
134
130
  critical: byPriority.critical.length,
135
131
  high: byPriority.high.length,
136
132
  medium: byPriority.medium.length,
137
- low: byPriority.low.length
133
+ low: byPriority.low.length,
138
134
  },
139
135
  completedByPriority,
140
136
  incompleteTasks: [...byStatus.todo, ...byStatus.inProgress].map(t => ({
@@ -142,8 +138,8 @@ class SprintReporter {
142
138
  title: t.title,
143
139
  priority: t.priority,
144
140
  storyPoints: t.storyPoints,
145
- status: t.status
146
- }))
141
+ status: t.status,
142
+ })),
147
143
  };
148
144
  }
149
145
 
@@ -154,7 +150,7 @@ class SprintReporter {
154
150
  */
155
151
  analyzeVelocity(sprint) {
156
152
  const metrics = this.calculateMetrics(sprint);
157
-
153
+
158
154
  let status;
159
155
  if (metrics.velocityAccuracy >= 90 && metrics.velocityAccuracy <= 110) {
160
156
  status = 'on-target';
@@ -171,7 +167,7 @@ class SprintReporter {
171
167
  actual: metrics.actualVelocity,
172
168
  difference: metrics.velocityDiff,
173
169
  accuracy: metrics.velocityAccuracy,
174
- status
170
+ status,
175
171
  };
176
172
  }
177
173
 
@@ -190,23 +186,26 @@ class SprintReporter {
190
186
  recommendations.push({
191
187
  type: 'velocity',
192
188
  severity: 'high',
193
- message: 'スプリントの実績ベロシティが計画の70%未満でした。次のスプリントでは計画ベロシティを下げることを検討してください。'
189
+ message:
190
+ 'スプリントの実績ベロシティが計画の70%未満でした。次のスプリントでは計画ベロシティを下げることを検討してください。',
194
191
  });
195
192
  } else if (metrics.velocityAccuracy > 130) {
196
193
  recommendations.push({
197
194
  type: 'velocity',
198
195
  severity: 'medium',
199
- message: '計画以上のベロシティを達成しました。次のスプリントでは計画ベロシティを上げることを検討してください。'
196
+ message:
197
+ '計画以上のベロシティを達成しました。次のスプリントでは計画ベロシティを上げることを検討してください。',
200
198
  });
201
199
  }
202
200
 
203
201
  // Incomplete critical tasks
204
- const incompleteCritical = taskSummary.byPriority.critical - taskSummary.completedByPriority.critical;
202
+ const incompleteCritical =
203
+ taskSummary.byPriority.critical - taskSummary.completedByPriority.critical;
205
204
  if (incompleteCritical > 0) {
206
205
  recommendations.push({
207
206
  type: 'priority',
208
207
  severity: 'critical',
209
- message: `${incompleteCritical}件のクリティカルタスクが未完了です。次のスプリントで優先的に対応してください。`
208
+ message: `${incompleteCritical}件のクリティカルタスクが未完了です。次のスプリントで優先的に対応してください。`,
210
209
  });
211
210
  }
212
211
 
@@ -215,7 +214,8 @@ class SprintReporter {
215
214
  recommendations.push({
216
215
  type: 'planning',
217
216
  severity: 'high',
218
- message: 'タスク完了率が50%未満です。タスクの見積もりや優先順位付けの改善を検討してください。'
217
+ message:
218
+ 'タスク完了率が50%未満です。タスクの見積もりや優先順位付けの改善を検討してください。',
219
219
  });
220
220
  }
221
221
 
@@ -224,7 +224,8 @@ class SprintReporter {
224
224
  recommendations.push({
225
225
  type: 'wip',
226
226
  severity: 'medium',
227
- message: '進行中のタスクが多すぎます。WIP制限を設けてフォーカスを高めることを検討してください。'
227
+ message:
228
+ '進行中のタスクが多すぎます。WIP制限を設けてフォーカスを高めることを検討してください。',
228
229
  });
229
230
  }
230
231
 
@@ -271,7 +272,7 @@ class SprintReporter {
271
272
  'on-target': '✅',
272
273
  'over-performing': '🚀',
273
274
  'slightly-under': '⚠️',
274
- 'under-performing': '❌'
275
+ 'under-performing': '❌',
275
276
  };
276
277
  lines.push(`Status: ${statusEmoji[va.status] || '❓'} **${va.status}**`);
277
278
  lines.push('');
@@ -286,10 +287,18 @@ class SprintReporter {
286
287
  lines.push('');
287
288
 
288
289
  lines.push('### By Priority');
289
- lines.push(`- 🔴 Critical: ${report.taskSummary.completedByPriority.critical}/${report.taskSummary.byPriority.critical}`);
290
- lines.push(`- 🟠 High: ${report.taskSummary.completedByPriority.high}/${report.taskSummary.byPriority.high}`);
291
- lines.push(`- 🟡 Medium: ${report.taskSummary.completedByPriority.medium}/${report.taskSummary.byPriority.medium}`);
292
- lines.push(`- 🟢 Low: ${report.taskSummary.completedByPriority.low}/${report.taskSummary.byPriority.low}`);
290
+ lines.push(
291
+ `- 🔴 Critical: ${report.taskSummary.completedByPriority.critical}/${report.taskSummary.byPriority.critical}`
292
+ );
293
+ lines.push(
294
+ `- 🟠 High: ${report.taskSummary.completedByPriority.high}/${report.taskSummary.byPriority.high}`
295
+ );
296
+ lines.push(
297
+ `- 🟡 Medium: ${report.taskSummary.completedByPriority.medium}/${report.taskSummary.byPriority.medium}`
298
+ );
299
+ lines.push(
300
+ `- 🟢 Low: ${report.taskSummary.completedByPriority.low}/${report.taskSummary.byPriority.low}`
301
+ );
293
302
  lines.push('');
294
303
 
295
304
  // Incomplete Tasks
@@ -310,7 +319,7 @@ class SprintReporter {
310
319
  critical: '🔴',
311
320
  high: '🟠',
312
321
  medium: '🟡',
313
- low: '🟢'
322
+ low: '🟢',
314
323
  };
315
324
  for (const rec of report.recommendations) {
316
325
  lines.push(`${severityEmoji[rec.severity] || '❓'} **${rec.type}**: ${rec.message}`);
@@ -327,7 +336,7 @@ class SprintReporter {
327
336
  */
328
337
  async saveReport(report) {
329
338
  await this.ensureStorageDir();
330
-
339
+
331
340
  const filePath = path.join(this.config.storageDir, `${report.id}.json`);
332
341
  await fs.writeFile(filePath, JSON.stringify(report, null, 2), 'utf-8');
333
342
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * TransitionRecorder Implementation
3
- *
3
+ *
4
4
  * Records stage transitions with timestamps and reviewers.
5
- *
5
+ *
6
6
  * Requirement: IMP-6.2-003-02
7
7
  * Design: Section 4.2
8
8
  */
@@ -14,12 +14,12 @@ const path = require('path');
14
14
  * Default configuration
15
15
  */
16
16
  const DEFAULT_CONFIG = {
17
- storageDir: 'storage/transitions'
17
+ storageDir: 'storage/transitions',
18
18
  };
19
19
 
20
20
  /**
21
21
  * TransitionRecorder
22
- *
22
+ *
23
23
  * Records and manages workflow stage transitions.
24
24
  */
25
25
  class TransitionRecorder {
@@ -37,10 +37,10 @@ class TransitionRecorder {
37
37
  * @returns {Promise<Object>} Created transition record
38
38
  */
39
39
  async recordTransition(featureId, transition) {
40
- const history = await this.getHistory(featureId) || {
40
+ const history = (await this.getHistory(featureId)) || {
41
41
  featureId,
42
42
  transitions: [],
43
- createdAt: new Date().toISOString()
43
+ createdAt: new Date().toISOString(),
44
44
  };
45
45
 
46
46
  const record = {
@@ -52,7 +52,7 @@ class TransitionRecorder {
52
52
  reviewResult: transition.reviewResult || null,
53
53
  artifacts: transition.artifacts || [],
54
54
  notes: transition.notes || null,
55
- timestamp: new Date().toISOString()
55
+ timestamp: new Date().toISOString(),
56
56
  };
57
57
 
58
58
  history.transitions.push(record);
@@ -70,10 +70,7 @@ class TransitionRecorder {
70
70
  */
71
71
  async getHistory(featureId) {
72
72
  try {
73
- const filePath = path.join(
74
- this.config.storageDir,
75
- `${featureId}-transitions.json`
76
- );
73
+ const filePath = path.join(this.config.storageDir, `${featureId}-transitions.json`);
77
74
  const content = await fs.readFile(filePath, 'utf-8');
78
75
  return JSON.parse(content);
79
76
  } catch {
@@ -146,7 +143,7 @@ class TransitionRecorder {
146
143
  successfulTransitions: 0,
147
144
  failedTransitions: 0,
148
145
  averageTime: 0,
149
- stageTransitions: {}
146
+ stageTransitions: {},
150
147
  };
151
148
  }
152
149
 
@@ -174,7 +171,7 @@ class TransitionRecorder {
174
171
  successfulTransitions: successful,
175
172
  failedTransitions: failed,
176
173
  averageTime,
177
- stageTransitions
174
+ stageTransitions,
178
175
  };
179
176
  }
180
177
 
@@ -185,11 +182,8 @@ class TransitionRecorder {
185
182
  */
186
183
  async saveHistory(featureId, history) {
187
184
  await this.ensureStorageDir();
188
-
189
- const filePath = path.join(
190
- this.config.storageDir,
191
- `${featureId}-transitions.json`
192
- );
185
+
186
+ const filePath = path.join(this.config.storageDir, `${featureId}-transitions.json`);
193
187
 
194
188
  await fs.writeFile(filePath, JSON.stringify(history, null, 2), 'utf-8');
195
189
  }
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * WorkflowDashboard Implementation
3
- *
3
+ *
4
4
  * Manages workflow state and progress visualization.
5
- *
5
+ *
6
6
  * Requirement: IMP-6.2-003-01
7
7
  * Design: Section 4.1
8
8
  */
@@ -13,13 +13,7 @@ const path = require('path');
13
13
  /**
14
14
  * Valid workflow stages
15
15
  */
16
- const WORKFLOW_STAGES = [
17
- 'steering',
18
- 'requirements',
19
- 'design',
20
- 'implementation',
21
- 'validation'
22
- ];
16
+ const WORKFLOW_STAGES = ['steering', 'requirements', 'design', 'implementation', 'validation'];
23
17
 
24
18
  /**
25
19
  * Stage statuses
@@ -28,19 +22,19 @@ const STAGE_STATUS = {
28
22
  NOT_STARTED: 'not-started',
29
23
  IN_PROGRESS: 'in-progress',
30
24
  COMPLETED: 'completed',
31
- BLOCKED: 'blocked'
25
+ BLOCKED: 'blocked',
32
26
  };
33
27
 
34
28
  /**
35
29
  * Default configuration
36
30
  */
37
31
  const DEFAULT_CONFIG = {
38
- storageDir: 'storage/workflows'
32
+ storageDir: 'storage/workflows',
39
33
  };
40
34
 
41
35
  /**
42
36
  * WorkflowDashboard
43
- *
37
+ *
44
38
  * Manages workflow state and progress for features.
45
39
  */
46
40
  class WorkflowDashboard {
@@ -60,13 +54,13 @@ class WorkflowDashboard {
60
54
  */
61
55
  async createWorkflow(featureId, options = {}) {
62
56
  const stages = {};
63
-
57
+
64
58
  for (const stage of WORKFLOW_STAGES) {
65
59
  stages[stage] = {
66
60
  status: STAGE_STATUS.NOT_STARTED,
67
61
  startedAt: null,
68
62
  completedAt: null,
69
- artifacts: []
63
+ artifacts: [],
70
64
  };
71
65
  }
72
66
 
@@ -83,7 +77,7 @@ class WorkflowDashboard {
83
77
  currentStage: 'steering',
84
78
  stages,
85
79
  blockers: [],
86
- metadata: options.metadata || {}
80
+ metadata: options.metadata || {},
87
81
  };
88
82
 
89
83
  this.workflows.set(featureId, workflow);
@@ -165,7 +159,7 @@ class WorkflowDashboard {
165
159
  severity: blocker.severity || 'medium',
166
160
  createdAt: new Date().toISOString(),
167
161
  resolvedAt: null,
168
- resolution: null
162
+ resolution: null,
169
163
  };
170
164
 
171
165
  workflow.blockers.push(blockerEntry);
@@ -203,9 +197,7 @@ class WorkflowDashboard {
203
197
  blocker.resolution = resolution;
204
198
 
205
199
  // Check if stage can be unblocked
206
- const stageBlockers = workflow.blockers.filter(
207
- b => b.stage === blocker.stage && !b.resolvedAt
208
- );
200
+ const stageBlockers = workflow.blockers.filter(b => b.stage === blocker.stage && !b.resolvedAt);
209
201
 
210
202
  if (stageBlockers.length === 0 && workflow.stages[blocker.stage]) {
211
203
  workflow.stages[blocker.stage].status = STAGE_STATUS.IN_PROGRESS;
@@ -239,7 +231,7 @@ class WorkflowDashboard {
239
231
  type: 'resolve-blocker',
240
232
  priority: 'high',
241
233
  description: `${unresolvedBlockers.length}件のブロッカーを解決してください`,
242
- blockers: unresolvedBlockers
234
+ blockers: unresolvedBlockers,
243
235
  });
244
236
  }
245
237
 
@@ -250,7 +242,7 @@ class WorkflowDashboard {
250
242
  type: 'create-artifact',
251
243
  priority: 'medium',
252
244
  description: 'プロジェクトメモリファイルを作成してください',
253
- artifacts: ['structure.md', 'tech.md', 'product.md']
245
+ artifacts: ['structure.md', 'tech.md', 'product.md'],
254
246
  });
255
247
  break;
256
248
  case 'requirements':
@@ -258,7 +250,7 @@ class WorkflowDashboard {
258
250
  type: 'create-artifact',
259
251
  priority: 'medium',
260
252
  description: 'EARS形式の要件ドキュメントを作成してください',
261
- artifacts: ['requirements.md']
253
+ artifacts: ['requirements.md'],
262
254
  });
263
255
  break;
264
256
  case 'design':
@@ -266,21 +258,21 @@ class WorkflowDashboard {
266
258
  type: 'create-artifact',
267
259
  priority: 'medium',
268
260
  description: 'C4設計ドキュメントとADRを作成してください',
269
- artifacts: ['design.md', 'adr-*.md']
261
+ artifacts: ['design.md', 'adr-*.md'],
270
262
  });
271
263
  break;
272
264
  case 'implementation':
273
265
  actions.push({
274
266
  type: 'run-review',
275
267
  priority: 'medium',
276
- description: 'レビューゲートを実行して実装を検証してください'
268
+ description: 'レビューゲートを実行して実装を検証してください',
277
269
  });
278
270
  break;
279
271
  case 'validation':
280
272
  actions.push({
281
273
  type: 'complete-validation',
282
274
  priority: 'medium',
283
- description: '全てのテストが通過していることを確認してください'
275
+ description: '全てのテストが通過していることを確認してください',
284
276
  });
285
277
  break;
286
278
  }
@@ -336,8 +328,8 @@ class WorkflowDashboard {
336
328
  stages: Object.entries(workflow.stages).map(([name, data]) => ({
337
329
  name,
338
330
  status: data.status,
339
- artifactCount: data.artifacts.length
340
- }))
331
+ artifactCount: data.artifacts.length,
332
+ })),
341
333
  };
342
334
  }
343
335
 
@@ -362,11 +354,8 @@ class WorkflowDashboard {
362
354
  */
363
355
  async saveWorkflow(workflow) {
364
356
  await this.ensureStorageDir();
365
-
366
- const filePath = path.join(
367
- this.config.storageDir,
368
- `${workflow.featureId}.json`
369
- );
357
+
358
+ const filePath = path.join(this.config.storageDir, `${workflow.featureId}.json`);
370
359
 
371
360
  await fs.writeFile(filePath, JSON.stringify(workflow, null, 2), 'utf-8');
372
361
  this.workflows.set(workflow.featureId, workflow);
@@ -398,7 +387,7 @@ class WorkflowDashboard {
398
387
  await this.ensureStorageDir();
399
388
  const files = await fs.readdir(this.config.storageDir);
400
389
  const workflows = [];
401
-
390
+
402
391
  for (const file of files) {
403
392
  if (file.endsWith('.json')) {
404
393
  const featureId = file.replace('.json', '');
@@ -408,7 +397,7 @@ class WorkflowDashboard {
408
397
  }
409
398
  }
410
399
  }
411
-
400
+
412
401
  return workflows;
413
402
  } catch {
414
403
  return [];
@@ -427,8 +416,8 @@ class WorkflowDashboard {
427
416
  }
428
417
  }
429
418
 
430
- module.exports = {
431
- WorkflowDashboard,
432
- WORKFLOW_STAGES,
433
- STAGE_STATUS
419
+ module.exports = {
420
+ WorkflowDashboard,
421
+ WORKFLOW_STAGES,
422
+ STAGE_STATUS,
434
423
  };