musubi-sdd 3.0.1 → 3.5.1

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 (49) hide show
  1. package/bin/musubi-change.js +623 -10
  2. package/bin/musubi-orchestrate.js +456 -0
  3. package/bin/musubi-trace.js +393 -0
  4. package/package.json +3 -2
  5. package/src/analyzers/impact-analyzer.js +682 -0
  6. package/src/integrations/cicd.js +782 -0
  7. package/src/integrations/documentation.js +740 -0
  8. package/src/integrations/examples.js +789 -0
  9. package/src/integrations/index.js +23 -0
  10. package/src/integrations/platforms.js +929 -0
  11. package/src/managers/delta-spec.js +484 -0
  12. package/src/monitoring/incident-manager.js +890 -0
  13. package/src/monitoring/index.js +633 -0
  14. package/src/monitoring/observability.js +938 -0
  15. package/src/monitoring/release-manager.js +622 -0
  16. package/src/orchestration/index.js +168 -0
  17. package/src/orchestration/orchestration-engine.js +409 -0
  18. package/src/orchestration/pattern-registry.js +319 -0
  19. package/src/orchestration/patterns/auto.js +386 -0
  20. package/src/orchestration/patterns/group-chat.js +395 -0
  21. package/src/orchestration/patterns/human-in-loop.js +506 -0
  22. package/src/orchestration/patterns/nested.js +322 -0
  23. package/src/orchestration/patterns/sequential.js +278 -0
  24. package/src/orchestration/patterns/swarm.js +395 -0
  25. package/src/orchestration/workflow-orchestrator.js +738 -0
  26. package/src/reporters/coverage-report.js +452 -0
  27. package/src/reporters/traceability-matrix-report.js +684 -0
  28. package/src/steering/advanced-validation.js +812 -0
  29. package/src/steering/auto-updater.js +670 -0
  30. package/src/steering/index.js +119 -0
  31. package/src/steering/quality-metrics.js +650 -0
  32. package/src/steering/template-constraints.js +789 -0
  33. package/src/templates/agents/claude-code/skills/agent-assistant/SKILL.md +22 -0
  34. package/src/templates/agents/claude-code/skills/issue-resolver/SKILL.md +21 -0
  35. package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +90 -28
  36. package/src/templates/agents/claude-code/skills/project-manager/SKILL.md +32 -0
  37. package/src/templates/agents/claude-code/skills/site-reliability-engineer/SKILL.md +27 -0
  38. package/src/templates/agents/claude-code/skills/steering/SKILL.md +30 -0
  39. package/src/templates/agents/claude-code/skills/test-engineer/SKILL.md +21 -0
  40. package/src/templates/agents/claude-code/skills/ui-ux-designer/SKILL.md +27 -0
  41. package/src/templates/agents/codex/AGENTS.md +36 -1
  42. package/src/templates/agents/cursor/AGENTS.md +36 -1
  43. package/src/templates/agents/gemini-cli/GEMINI.md +36 -1
  44. package/src/templates/agents/github-copilot/AGENTS.md +65 -1
  45. package/src/templates/agents/qwen-code/QWEN.md +36 -1
  46. package/src/templates/agents/windsurf/AGENTS.md +36 -1
  47. package/src/templates/shared/delta-spec-template.md +246 -0
  48. package/src/validators/delta-format.js +474 -0
  49. package/src/validators/traceability-validator.js +561 -0
@@ -0,0 +1,684 @@
1
+ /**
2
+ * MUSUBI Traceability Matrix Report Generator
3
+ *
4
+ * Generates interactive HTML reports for bidirectional traceability
5
+ * Implements Constitutional Article V: Complete Traceability
6
+ */
7
+
8
+ const fs = require('fs-extra');
9
+ const path = require('path');
10
+
11
+ /**
12
+ * Report format options
13
+ */
14
+ const ReportFormat = {
15
+ HTML: 'html',
16
+ MARKDOWN: 'markdown',
17
+ JSON: 'json',
18
+ };
19
+
20
+ /**
21
+ * TraceabilityMatrixReport - Generates visual traceability reports
22
+ */
23
+ class TraceabilityMatrixReport {
24
+ constructor(workspaceRoot, options = {}) {
25
+ this.workspaceRoot = workspaceRoot;
26
+ this.options = {
27
+ outputDir: options.outputDir || 'traceability-reports',
28
+ theme: options.theme || 'light',
29
+ interactive: options.interactive !== false,
30
+ includeOrphaned: options.includeOrphaned !== false,
31
+ ...options,
32
+ };
33
+ }
34
+
35
+ /**
36
+ * Generate traceability matrix report
37
+ */
38
+ async generate(traceabilityData, format = ReportFormat.HTML) {
39
+ const reportData = this.prepareReportData(traceabilityData);
40
+
41
+ switch (format) {
42
+ case ReportFormat.HTML:
43
+ return this.generateHTML(reportData);
44
+ case ReportFormat.MARKDOWN:
45
+ return this.generateMarkdown(reportData);
46
+ case ReportFormat.JSON:
47
+ return this.generateJSON(reportData);
48
+ default:
49
+ return this.generateHTML(reportData);
50
+ }
51
+ }
52
+
53
+ /**
54
+ * Prepare report data
55
+ */
56
+ prepareReportData(traceabilityData) {
57
+ const { forward, backward, orphaned, completeness } = traceabilityData;
58
+
59
+ return {
60
+ timestamp: new Date().toISOString(),
61
+ forward: forward || [],
62
+ backward: backward || [],
63
+ orphaned: orphaned || { requirements: [], design: [], tasks: [], code: [], tests: [] },
64
+ completeness: completeness || {
65
+ forwardComplete: 0,
66
+ forwardTotal: 0,
67
+ forwardPercentage: 0,
68
+ backwardComplete: 0,
69
+ backwardTotal: 0,
70
+ backwardPercentage: 0,
71
+ },
72
+ summary: this.calculateSummary(traceabilityData),
73
+ };
74
+ }
75
+
76
+ /**
77
+ * Calculate summary statistics
78
+ */
79
+ calculateSummary(data) {
80
+ const forward = data.forward || [];
81
+ const backward = data.backward || [];
82
+ const orphaned = data.orphaned || {};
83
+
84
+ return {
85
+ totalRequirements: forward.length,
86
+ completeChains: forward.filter(f => f.complete).length,
87
+ incompleteChains: forward.filter(f => !f.complete).length,
88
+ totalTests: backward.length,
89
+ orphanedCount: {
90
+ requirements: (orphaned.requirements || []).length,
91
+ design: (orphaned.design || []).length,
92
+ tasks: (orphaned.tasks || []).length,
93
+ code: (orphaned.code || []).length,
94
+ tests: (orphaned.tests || []).length,
95
+ },
96
+ totalOrphaned: Object.values(orphaned).reduce((sum, arr) => sum + (arr?.length || 0), 0),
97
+ };
98
+ }
99
+
100
+ /**
101
+ * Generate HTML report
102
+ */
103
+ generateHTML(data) {
104
+ const { theme } = this.options;
105
+ const isDark = theme === 'dark';
106
+
107
+ return `<!DOCTYPE html>
108
+ <html lang="en">
109
+ <head>
110
+ <meta charset="UTF-8">
111
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
112
+ <title>Traceability Matrix Report - MUSUBI</title>
113
+ <style>
114
+ :root {
115
+ --bg-primary: ${isDark ? '#1a1a2e' : '#f5f5f5'};
116
+ --bg-secondary: ${isDark ? '#16213e' : '#ffffff'};
117
+ --text-primary: ${isDark ? '#eee' : '#333'};
118
+ --text-secondary: ${isDark ? '#aaa' : '#666'};
119
+ --border-color: ${isDark ? '#333' : '#ddd'};
120
+ --success: #28a745;
121
+ --warning: #ffc107;
122
+ --danger: #dc3545;
123
+ --info: #17a2b8;
124
+ }
125
+ * { box-sizing: border-box; }
126
+ body {
127
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
128
+ background: var(--bg-primary);
129
+ color: var(--text-primary);
130
+ margin: 0;
131
+ padding: 20px;
132
+ line-height: 1.6;
133
+ }
134
+ .container { max-width: 1400px; margin: 0 auto; }
135
+ .header {
136
+ text-align: center;
137
+ margin-bottom: 30px;
138
+ padding: 20px;
139
+ background: var(--bg-secondary);
140
+ border-radius: 12px;
141
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
142
+ }
143
+ .header h1 { margin: 0 0 10px; font-size: 2em; }
144
+ .timestamp { color: var(--text-secondary); font-size: 0.9em; }
145
+ .stats-grid {
146
+ display: grid;
147
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
148
+ gap: 20px;
149
+ margin-bottom: 30px;
150
+ }
151
+ .stat-card {
152
+ background: var(--bg-secondary);
153
+ border-radius: 12px;
154
+ padding: 20px;
155
+ text-align: center;
156
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
157
+ }
158
+ .stat-card .value { font-size: 2.5em; font-weight: bold; margin: 10px 0; }
159
+ .stat-card .label { color: var(--text-secondary); }
160
+ .stat-card.success .value { color: var(--success); }
161
+ .stat-card.warning .value { color: var(--warning); }
162
+ .stat-card.danger .value { color: var(--danger); }
163
+ .section {
164
+ background: var(--bg-secondary);
165
+ border-radius: 12px;
166
+ padding: 20px;
167
+ margin-bottom: 20px;
168
+ box-shadow: 0 2px 4px rgba(0,0,0,0.1);
169
+ }
170
+ .section h2 { margin-top: 0; border-bottom: 2px solid var(--border-color); padding-bottom: 10px; }
171
+ .tabs {
172
+ display: flex;
173
+ gap: 10px;
174
+ margin-bottom: 20px;
175
+ }
176
+ .tab {
177
+ padding: 10px 20px;
178
+ background: var(--bg-primary);
179
+ border: none;
180
+ border-radius: 8px;
181
+ cursor: pointer;
182
+ color: var(--text-primary);
183
+ transition: background 0.3s;
184
+ }
185
+ .tab:hover { background: var(--border-color); }
186
+ .tab.active { background: var(--info); color: white; }
187
+ .matrix-table {
188
+ width: 100%;
189
+ border-collapse: collapse;
190
+ margin-top: 15px;
191
+ }
192
+ .matrix-table th, .matrix-table td {
193
+ padding: 12px;
194
+ text-align: left;
195
+ border-bottom: 1px solid var(--border-color);
196
+ }
197
+ .matrix-table th { background: var(--bg-primary); font-weight: 600; }
198
+ .matrix-table tr:hover { background: var(--bg-primary); }
199
+ .badge {
200
+ display: inline-block;
201
+ padding: 4px 8px;
202
+ border-radius: 4px;
203
+ font-size: 0.8em;
204
+ margin: 2px;
205
+ }
206
+ .badge.complete { background: #d4edda; color: #155724; }
207
+ .badge.incomplete { background: #f8d7da; color: #721c24; }
208
+ .badge.link { background: #cce5ff; color: #004085; }
209
+ .badge.orphan { background: #fff3cd; color: #856404; }
210
+ .collapsible {
211
+ cursor: pointer;
212
+ user-select: none;
213
+ }
214
+ .collapsible::before { content: '▶ '; transition: transform 0.3s; }
215
+ .collapsible.open::before { content: '▼ '; }
216
+ .collapsible-content { display: none; padding-left: 20px; }
217
+ .collapsible-content.open { display: block; }
218
+ .progress-container { display: flex; align-items: center; gap: 10px; }
219
+ .progress { flex: 1; background: var(--border-color); border-radius: 10px; height: 20px; overflow: hidden; }
220
+ .progress-bar { height: 100%; border-radius: 10px; transition: width 0.5s; }
221
+ .progress-bar.high { background: linear-gradient(90deg, #28a745, #5cb85c); }
222
+ .progress-bar.medium { background: linear-gradient(90deg, #ffc107, #ffda44); }
223
+ .progress-bar.low { background: linear-gradient(90deg, #dc3545, #e74c5c); }
224
+ .legend { display: flex; gap: 20px; flex-wrap: wrap; margin-top: 10px; }
225
+ .legend-item { display: flex; align-items: center; gap: 5px; }
226
+ .legend-color { width: 12px; height: 12px; border-radius: 3px; }
227
+ .chain-viz {
228
+ display: flex;
229
+ align-items: center;
230
+ gap: 5px;
231
+ flex-wrap: wrap;
232
+ }
233
+ .chain-node {
234
+ padding: 6px 12px;
235
+ border-radius: 6px;
236
+ font-size: 0.9em;
237
+ }
238
+ .chain-arrow { color: var(--text-secondary); }
239
+ .chain-node.req { background: #cce5ff; color: #004085; }
240
+ .chain-node.design { background: #d4edda; color: #155724; }
241
+ .chain-node.task { background: #fff3cd; color: #856404; }
242
+ .chain-node.code { background: #e2e3e5; color: #383d41; }
243
+ .chain-node.test { background: #f5c6cb; color: #721c24; }
244
+ .chain-node.missing { background: #f8d7da; color: #721c24; border: 2px dashed #dc3545; }
245
+ .filter-bar {
246
+ display: flex;
247
+ gap: 10px;
248
+ margin-bottom: 20px;
249
+ flex-wrap: wrap;
250
+ }
251
+ .filter-bar input {
252
+ padding: 10px 15px;
253
+ border: 1px solid var(--border-color);
254
+ border-radius: 8px;
255
+ background: var(--bg-primary);
256
+ color: var(--text-primary);
257
+ flex: 1;
258
+ min-width: 200px;
259
+ }
260
+ .filter-bar select {
261
+ padding: 10px 15px;
262
+ border: 1px solid var(--border-color);
263
+ border-radius: 8px;
264
+ background: var(--bg-primary);
265
+ color: var(--text-primary);
266
+ }
267
+ @media (max-width: 768px) {
268
+ .stats-grid { grid-template-columns: repeat(2, 1fr); }
269
+ .tabs { flex-wrap: wrap; }
270
+ }
271
+ </style>
272
+ </head>
273
+ <body>
274
+ <div class="container">
275
+ <div class="header">
276
+ <h1>📊 Traceability Matrix Report</h1>
277
+ <p class="timestamp">Generated: ${data.timestamp}</p>
278
+ <p>MUSUBI - Ultimate Specification Driven Development</p>
279
+ </div>
280
+
281
+ <!-- Statistics Overview -->
282
+ <div class="stats-grid">
283
+ <div class="stat-card ${data.completeness.forwardPercentage >= 80 ? 'success' : data.completeness.forwardPercentage >= 60 ? 'warning' : 'danger'}">
284
+ <div class="label">Forward Traceability</div>
285
+ <div class="value">${data.completeness.forwardPercentage}%</div>
286
+ <div class="label">${data.completeness.forwardComplete}/${data.completeness.forwardTotal} Complete</div>
287
+ </div>
288
+ <div class="stat-card ${data.completeness.backwardPercentage >= 80 ? 'success' : data.completeness.backwardPercentage >= 60 ? 'warning' : 'danger'}">
289
+ <div class="label">Backward Traceability</div>
290
+ <div class="value">${data.completeness.backwardPercentage}%</div>
291
+ <div class="label">${data.completeness.backwardComplete}/${data.completeness.backwardTotal} Complete</div>
292
+ </div>
293
+ <div class="stat-card ${data.summary.totalOrphaned === 0 ? 'success' : 'warning'}">
294
+ <div class="label">Orphaned Items</div>
295
+ <div class="value">${data.summary.totalOrphaned}</div>
296
+ <div class="label">Needs Linking</div>
297
+ </div>
298
+ <div class="stat-card">
299
+ <div class="label">Total Requirements</div>
300
+ <div class="value">${data.summary.totalRequirements}</div>
301
+ <div class="label">Tracked</div>
302
+ </div>
303
+ </div>
304
+
305
+ <!-- Forward Traceability Section -->
306
+ <div class="section">
307
+ <h2>🔗 Forward Traceability (Requirements → Tests)</h2>
308
+ <p>Tracing each requirement through design, tasks, code, and tests.</p>
309
+
310
+ <div class="progress-container">
311
+ <span>Progress:</span>
312
+ <div class="progress">
313
+ <div class="progress-bar ${data.completeness.forwardPercentage >= 80 ? 'high' : data.completeness.forwardPercentage >= 60 ? 'medium' : 'low'}"
314
+ style="width: ${data.completeness.forwardPercentage}%"></div>
315
+ </div>
316
+ <span>${data.completeness.forwardPercentage}%</span>
317
+ </div>
318
+
319
+ <div class="filter-bar">
320
+ <input type="text" id="forward-search" placeholder="Search requirements..." onkeyup="filterForward()">
321
+ <select id="forward-filter" onchange="filterForward()">
322
+ <option value="all">All</option>
323
+ <option value="complete">Complete Only</option>
324
+ <option value="incomplete">Incomplete Only</option>
325
+ </select>
326
+ </div>
327
+
328
+ <table class="matrix-table" id="forward-table">
329
+ <thead>
330
+ <tr>
331
+ <th>Requirement</th>
332
+ <th>Design</th>
333
+ <th>Tasks</th>
334
+ <th>Code</th>
335
+ <th>Tests</th>
336
+ <th>Status</th>
337
+ </tr>
338
+ </thead>
339
+ <tbody>
340
+ ${this.generateForwardRows(data.forward)}
341
+ </tbody>
342
+ </table>
343
+ </div>
344
+
345
+ <!-- Backward Traceability Section -->
346
+ <div class="section">
347
+ <h2>🔙 Backward Traceability (Tests → Requirements)</h2>
348
+ <p>Tracing each test back to its originating requirements.</p>
349
+
350
+ <div class="progress-container">
351
+ <span>Progress:</span>
352
+ <div class="progress">
353
+ <div class="progress-bar ${data.completeness.backwardPercentage >= 80 ? 'high' : data.completeness.backwardPercentage >= 60 ? 'medium' : 'low'}"
354
+ style="width: ${data.completeness.backwardPercentage}%"></div>
355
+ </div>
356
+ <span>${data.completeness.backwardPercentage}%</span>
357
+ </div>
358
+
359
+ <table class="matrix-table" id="backward-table">
360
+ <thead>
361
+ <tr>
362
+ <th>Test</th>
363
+ <th>Code</th>
364
+ <th>Tasks</th>
365
+ <th>Design</th>
366
+ <th>Requirements</th>
367
+ <th>Status</th>
368
+ </tr>
369
+ </thead>
370
+ <tbody>
371
+ ${this.generateBackwardRows(data.backward)}
372
+ </tbody>
373
+ </table>
374
+ </div>
375
+
376
+ <!-- Orphaned Items Section -->
377
+ ${this.options.includeOrphaned ? this.generateOrphanedSection(data.orphaned, data.summary) : ''}
378
+
379
+ <!-- Legend -->
380
+ <div class="section">
381
+ <h2>📋 Legend</h2>
382
+ <div class="legend">
383
+ <div class="legend-item">
384
+ <div class="legend-color" style="background: #d4edda;"></div>
385
+ <span>Complete Chain</span>
386
+ </div>
387
+ <div class="legend-item">
388
+ <div class="legend-color" style="background: #f8d7da;"></div>
389
+ <span>Incomplete Chain</span>
390
+ </div>
391
+ <div class="legend-item">
392
+ <div class="legend-color" style="background: #cce5ff;"></div>
393
+ <span>Linked Item</span>
394
+ </div>
395
+ <div class="legend-item">
396
+ <div class="legend-color" style="background: #fff3cd;"></div>
397
+ <span>Orphaned Item</span>
398
+ </div>
399
+ </div>
400
+ </div>
401
+ </div>
402
+
403
+ <script>
404
+ ${this.options.interactive ? this.generateInteractiveScript() : ''}
405
+ </script>
406
+ </body>
407
+ </html>`;
408
+ }
409
+
410
+ /**
411
+ * Generate forward traceability rows
412
+ */
413
+ generateForwardRows(forward) {
414
+ if (!forward || forward.length === 0) {
415
+ return '<tr><td colspan="6" style="text-align: center;">No requirements found</td></tr>';
416
+ }
417
+
418
+ return forward.map(item => {
419
+ const req = item.requirement;
420
+ const reqId = req?.id || req?.file || 'Unknown';
421
+
422
+ return `
423
+ <tr data-complete="${item.complete}">
424
+ <td><code>${this.escapeHtml(reqId)}</code></td>
425
+ <td>${this.formatLinks(item.design, 'design')}</td>
426
+ <td>${this.formatLinks(item.tasks, 'task')}</td>
427
+ <td>${this.formatLinks(item.code, 'code')}</td>
428
+ <td>${this.formatLinks(item.tests, 'test')}</td>
429
+ <td>
430
+ <span class="badge ${item.complete ? 'complete' : 'incomplete'}">
431
+ ${item.complete ? '✓ Complete' : '✗ Incomplete'}
432
+ </span>
433
+ </td>
434
+ </tr>
435
+ `;
436
+ }).join('');
437
+ }
438
+
439
+ /**
440
+ * Generate backward traceability rows
441
+ */
442
+ generateBackwardRows(backward) {
443
+ if (!backward || backward.length === 0) {
444
+ return '<tr><td colspan="6" style="text-align: center;">No tests found</td></tr>';
445
+ }
446
+
447
+ return backward.map(item => {
448
+ const test = item.test;
449
+ const testId = test?.file || test?.id || 'Unknown';
450
+
451
+ return `
452
+ <tr data-complete="${item.complete}">
453
+ <td><code>${this.escapeHtml(path.basename(testId))}</code></td>
454
+ <td>${this.formatLinks(item.code, 'code')}</td>
455
+ <td>${this.formatLinks(item.tasks, 'task')}</td>
456
+ <td>${this.formatLinks(item.design, 'design')}</td>
457
+ <td>${this.formatLinks(item.requirements, 'req')}</td>
458
+ <td>
459
+ <span class="badge ${item.complete ? 'complete' : 'incomplete'}">
460
+ ${item.complete ? '✓ Traced' : '✗ Untraced'}
461
+ </span>
462
+ </td>
463
+ </tr>
464
+ `;
465
+ }).join('');
466
+ }
467
+
468
+ /**
469
+ * Format linked items
470
+ */
471
+ formatLinks(items, type) {
472
+ if (!items || items.length === 0) {
473
+ return `<span class="badge missing">None</span>`;
474
+ }
475
+
476
+ return items.map(item => {
477
+ const id = item?.id || item?.file || 'Unknown';
478
+ const displayId = path.basename(id);
479
+ return `<span class="badge link chain-node ${type}">${this.escapeHtml(displayId)}</span>`;
480
+ }).join(' ');
481
+ }
482
+
483
+ /**
484
+ * Generate orphaned items section
485
+ */
486
+ generateOrphanedSection(orphaned, summary) {
487
+ if (summary.totalOrphaned === 0) {
488
+ return `
489
+ <div class="section">
490
+ <h2>🎉 Orphaned Items</h2>
491
+ <p style="color: var(--success);">All items are properly linked! No orphaned items found.</p>
492
+ </div>
493
+ `;
494
+ }
495
+
496
+ return `
497
+ <div class="section">
498
+ <h2>⚠️ Orphaned Items (${summary.totalOrphaned})</h2>
499
+ <p>These items are not linked to any requirements and may need attention.</p>
500
+
501
+ ${this.generateOrphanedList('Requirements', orphaned.requirements)}
502
+ ${this.generateOrphanedList('Design', orphaned.design)}
503
+ ${this.generateOrphanedList('Tasks', orphaned.tasks)}
504
+ ${this.generateOrphanedList('Code', orphaned.code)}
505
+ ${this.generateOrphanedList('Tests', orphaned.tests)}
506
+ </div>
507
+ `;
508
+ }
509
+
510
+ /**
511
+ * Generate orphaned list for a category
512
+ */
513
+ generateOrphanedList(category, items) {
514
+ if (!items || items.length === 0) return '';
515
+
516
+ return `
517
+ <div style="margin: 15px 0;">
518
+ <h3 class="collapsible" onclick="toggleCollapsible(this)">${category} (${items.length})</h3>
519
+ <div class="collapsible-content">
520
+ <ul>
521
+ ${items.map(item => {
522
+ const id = item?.id || item?.file || 'Unknown';
523
+ return `<li><code>${this.escapeHtml(id)}</code></li>`;
524
+ }).join('')}
525
+ </ul>
526
+ </div>
527
+ </div>
528
+ `;
529
+ }
530
+
531
+ /**
532
+ * Generate interactive JavaScript
533
+ */
534
+ generateInteractiveScript() {
535
+ return `
536
+ function filterForward() {
537
+ const search = document.getElementById('forward-search').value.toLowerCase();
538
+ const filter = document.getElementById('forward-filter').value;
539
+ const rows = document.querySelectorAll('#forward-table tbody tr');
540
+
541
+ rows.forEach(row => {
542
+ const text = row.textContent.toLowerCase();
543
+ const isComplete = row.getAttribute('data-complete') === 'true';
544
+
545
+ let show = text.includes(search);
546
+
547
+ if (filter === 'complete') show = show && isComplete;
548
+ if (filter === 'incomplete') show = show && !isComplete;
549
+
550
+ row.style.display = show ? '' : 'none';
551
+ });
552
+ }
553
+
554
+ function toggleCollapsible(element) {
555
+ element.classList.toggle('open');
556
+ const content = element.nextElementSibling;
557
+ if (content) {
558
+ content.classList.toggle('open');
559
+ }
560
+ }
561
+
562
+ // Auto-expand orphaned sections
563
+ document.querySelectorAll('.collapsible').forEach(el => {
564
+ if (el.textContent.includes('(0)')) {
565
+ el.style.display = 'none';
566
+ el.nextElementSibling.style.display = 'none';
567
+ }
568
+ });
569
+ `;
570
+ }
571
+
572
+ /**
573
+ * Escape HTML
574
+ */
575
+ escapeHtml(text) {
576
+ if (!text) return '';
577
+ return String(text)
578
+ .replace(/&/g, '&amp;')
579
+ .replace(/</g, '&lt;')
580
+ .replace(/>/g, '&gt;')
581
+ .replace(/"/g, '&quot;')
582
+ .replace(/'/g, '&#039;');
583
+ }
584
+
585
+ /**
586
+ * Generate Markdown report
587
+ */
588
+ generateMarkdown(data) {
589
+ let md = `# Traceability Matrix Report
590
+
591
+ Generated: ${data.timestamp}
592
+
593
+ ## Summary
594
+
595
+ | Metric | Value |
596
+ |--------|-------|
597
+ | Total Requirements | ${data.summary.totalRequirements} |
598
+ | Complete Chains | ${data.summary.completeChains} |
599
+ | Incomplete Chains | ${data.summary.incompleteChains} |
600
+ | Total Tests | ${data.summary.totalTests} |
601
+ | Orphaned Items | ${data.summary.totalOrphaned} |
602
+
603
+ ## Completeness
604
+
605
+ | Direction | Complete | Total | Percentage |
606
+ |-----------|----------|-------|------------|
607
+ | Forward (Req → Test) | ${data.completeness.forwardComplete} | ${data.completeness.forwardTotal} | ${data.completeness.forwardPercentage}% |
608
+ | Backward (Test → Req) | ${data.completeness.backwardComplete} | ${data.completeness.backwardTotal} | ${data.completeness.backwardPercentage}% |
609
+
610
+ ## Forward Traceability
611
+
612
+ | Requirement | Design | Tasks | Code | Tests | Status |
613
+ |-------------|--------|-------|------|-------|--------|
614
+ `;
615
+
616
+ for (const item of data.forward) {
617
+ const req = item.requirement;
618
+ const reqId = req?.id || req?.file || 'Unknown';
619
+ md += `| ${reqId} | ${item.design.length} | ${item.tasks.length} | ${item.code.length} | ${item.tests.length} | ${item.complete ? '✓' : '✗'} |\n`;
620
+ }
621
+
622
+ md += `
623
+ ## Backward Traceability
624
+
625
+ | Test | Code | Tasks | Design | Requirements | Status |
626
+ |------|------|-------|--------|--------------|--------|
627
+ `;
628
+
629
+ for (const item of data.backward) {
630
+ const test = item.test;
631
+ const testFile = test?.file ? path.basename(test.file) : 'Unknown';
632
+ md += `| ${testFile} | ${item.code.length} | ${item.tasks.length} | ${item.design.length} | ${item.requirements.length} | ${item.complete ? '✓' : '✗'} |\n`;
633
+ }
634
+
635
+ if (data.summary.totalOrphaned > 0) {
636
+ md += `
637
+ ## Orphaned Items
638
+
639
+ `;
640
+ const categories = ['requirements', 'design', 'tasks', 'code', 'tests'];
641
+ for (const cat of categories) {
642
+ const items = data.orphaned[cat] || [];
643
+ if (items.length > 0) {
644
+ md += `### ${cat.charAt(0).toUpperCase() + cat.slice(1)} (${items.length})
645
+
646
+ `;
647
+ for (const item of items) {
648
+ md += `- ${item?.id || item?.file || 'Unknown'}\n`;
649
+ }
650
+ md += '\n';
651
+ }
652
+ }
653
+ }
654
+
655
+ return md;
656
+ }
657
+
658
+ /**
659
+ * Generate JSON report
660
+ */
661
+ generateJSON(data) {
662
+ return JSON.stringify(data, null, 2);
663
+ }
664
+
665
+ /**
666
+ * Save report to file
667
+ */
668
+ async saveReport(report, filename, format = ReportFormat.HTML) {
669
+ const outputDir = path.join(this.workspaceRoot, this.options.outputDir);
670
+ await fs.ensureDir(outputDir);
671
+
672
+ const extension = format === ReportFormat.HTML ? '.html' : format === ReportFormat.MARKDOWN ? '.md' : '.json';
673
+ const fullPath = path.join(outputDir, filename + extension);
674
+
675
+ await fs.writeFile(fullPath, report, 'utf8');
676
+
677
+ return fullPath;
678
+ }
679
+ }
680
+
681
+ module.exports = {
682
+ TraceabilityMatrixReport,
683
+ ReportFormat,
684
+ };