musubi-sdd 3.10.0 → 5.1.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 (44) hide show
  1. package/README.md +24 -19
  2. package/package.json +1 -1
  3. package/src/agents/agent-loop.js +532 -0
  4. package/src/agents/agentic/code-generator.js +767 -0
  5. package/src/agents/agentic/code-reviewer.js +698 -0
  6. package/src/agents/agentic/index.js +43 -0
  7. package/src/agents/function-tool.js +432 -0
  8. package/src/agents/index.js +45 -0
  9. package/src/agents/schema-generator.js +514 -0
  10. package/src/analyzers/ast-extractor.js +870 -0
  11. package/src/analyzers/context-optimizer.js +681 -0
  12. package/src/analyzers/repository-map.js +692 -0
  13. package/src/integrations/index.js +7 -1
  14. package/src/integrations/mcp/index.js +175 -0
  15. package/src/integrations/mcp/mcp-context-provider.js +472 -0
  16. package/src/integrations/mcp/mcp-discovery.js +436 -0
  17. package/src/integrations/mcp/mcp-tool-registry.js +467 -0
  18. package/src/integrations/mcp-connector.js +818 -0
  19. package/src/integrations/tool-discovery.js +589 -0
  20. package/src/managers/index.js +7 -0
  21. package/src/managers/skill-tools.js +565 -0
  22. package/src/monitoring/cost-tracker.js +7 -0
  23. package/src/monitoring/incident-manager.js +10 -0
  24. package/src/monitoring/observability.js +10 -0
  25. package/src/monitoring/quality-dashboard.js +491 -0
  26. package/src/monitoring/release-manager.js +10 -0
  27. package/src/orchestration/agent-skill-binding.js +655 -0
  28. package/src/orchestration/error-handler.js +827 -0
  29. package/src/orchestration/index.js +235 -1
  30. package/src/orchestration/mcp-tool-adapters.js +896 -0
  31. package/src/orchestration/reasoning/index.js +58 -0
  32. package/src/orchestration/reasoning/planning-engine.js +831 -0
  33. package/src/orchestration/reasoning/reasoning-engine.js +710 -0
  34. package/src/orchestration/reasoning/self-correction.js +751 -0
  35. package/src/orchestration/skill-executor.js +665 -0
  36. package/src/orchestration/skill-registry.js +650 -0
  37. package/src/orchestration/workflow-examples.js +1072 -0
  38. package/src/orchestration/workflow-executor.js +779 -0
  39. package/src/phase4-integration.js +248 -0
  40. package/src/phase5-integration.js +402 -0
  41. package/src/steering/steering-auto-update.js +572 -0
  42. package/src/steering/steering-validator.js +547 -0
  43. package/src/templates/template-constraints.js +646 -0
  44. package/src/validators/advanced-validation.js +580 -0
@@ -0,0 +1,491 @@
1
+ /**
2
+ * Quality Metrics Dashboard
3
+ * カバレッジメトリクス、Constitutional準拠メトリクス、プロジェクトヘルス指標
4
+ *
5
+ * Part of MUSUBI v5.0.0 - Production Readiness
6
+ *
7
+ * @module monitoring/quality-dashboard
8
+ * @version 1.0.0
9
+ *
10
+ * @traceability
11
+ * - Requirement: REQ-P5-001 (Quality Dashboard)
12
+ * - Design: docs/design/tdd-musubi-v5.0.0.md#3.1
13
+ * - Test: tests/monitoring/quality-dashboard.test.js
14
+ */
15
+
16
+ const EventEmitter = require('events');
17
+
18
+ /**
19
+ * Metric categories
20
+ */
21
+ const METRIC_CATEGORY = {
22
+ COVERAGE: 'coverage',
23
+ CONSTITUTIONAL: 'constitutional',
24
+ QUALITY: 'quality',
25
+ HEALTH: 'health',
26
+ PERFORMANCE: 'performance',
27
+ CUSTOM: 'custom'
28
+ };
29
+
30
+ /**
31
+ * Health status levels
32
+ */
33
+ const HEALTH_STATUS = {
34
+ HEALTHY: 'healthy', // 80-100%
35
+ WARNING: 'warning', // 50-79%
36
+ CRITICAL: 'critical', // 20-49%
37
+ FAILING: 'failing' // <20%
38
+ };
39
+
40
+ /**
41
+ * Constitutional articles
42
+ */
43
+ const CONSTITUTIONAL_ARTICLES = {
44
+ SINGLE_SOURCE_OF_TRUTH: 'article-1',
45
+ EXPLICIT_CONTRACTS: 'article-2',
46
+ TRACEABILITY: 'article-3',
47
+ AUTOMATED_VALIDATION: 'article-4',
48
+ MACHINE_READABLE: 'article-5',
49
+ INCREMENTAL_ADOPTION: 'article-6',
50
+ SEPARATION_OF_CONCERNS: 'article-7',
51
+ FEEDBACK_LOOPS: 'article-8',
52
+ GOVERNANCE: 'article-9'
53
+ };
54
+
55
+ /**
56
+ * Quality Dashboard Engine
57
+ */
58
+ class QualityDashboard extends EventEmitter {
59
+ /**
60
+ * @param {Object} options
61
+ * @param {Object} options.thresholds - Custom thresholds
62
+ * @param {boolean} options.autoCollect - Auto-collect metrics
63
+ * @param {number} options.collectInterval - Collection interval (ms)
64
+ */
65
+ constructor(options = {}) {
66
+ super();
67
+
68
+ this.thresholds = {
69
+ coverage: { healthy: 80, warning: 50, critical: 20 },
70
+ constitutional: { healthy: 90, warning: 70, critical: 50 },
71
+ quality: { healthy: 80, warning: 60, critical: 40 },
72
+ ...options.thresholds
73
+ };
74
+
75
+ this.autoCollect = options.autoCollect ?? false;
76
+ this.collectInterval = options.collectInterval ?? 60000;
77
+
78
+ this.metrics = new Map();
79
+ this.history = [];
80
+ this.collectors = new Map();
81
+ this.intervalId = null;
82
+
83
+ // Initialize default collectors
84
+ this.initializeDefaultCollectors();
85
+
86
+ if (this.autoCollect) {
87
+ this.startAutoCollection();
88
+ }
89
+ }
90
+
91
+ /**
92
+ * Initialize default metric collectors
93
+ */
94
+ initializeDefaultCollectors() {
95
+ // Coverage metrics collector
96
+ this.registerCollector('coverage', async (context) => ({
97
+ lines: context?.coverage?.lines ?? 0,
98
+ branches: context?.coverage?.branches ?? 0,
99
+ functions: context?.coverage?.functions ?? 0,
100
+ statements: context?.coverage?.statements ?? 0,
101
+ overall: context?.coverage?.overall ??
102
+ ((context?.coverage?.lines ?? 0) +
103
+ (context?.coverage?.branches ?? 0) +
104
+ (context?.coverage?.functions ?? 0) +
105
+ (context?.coverage?.statements ?? 0)) / 4
106
+ }));
107
+
108
+ // Constitutional compliance collector
109
+ this.registerCollector('constitutional', async (context) => {
110
+ const articles = context?.constitutional ?? {};
111
+ const scores = Object.values(CONSTITUTIONAL_ARTICLES).map(id => ({
112
+ id,
113
+ score: articles[id]?.score ?? 0,
114
+ compliant: articles[id]?.compliant ?? false
115
+ }));
116
+
117
+ const totalScore = scores.reduce((sum, s) => sum + s.score, 0) / scores.length;
118
+ const compliantCount = scores.filter(s => s.compliant).length;
119
+
120
+ return {
121
+ articles: scores,
122
+ totalScore,
123
+ compliantCount,
124
+ totalArticles: scores.length,
125
+ complianceRate: (compliantCount / scores.length) * 100
126
+ };
127
+ });
128
+
129
+ // Quality metrics collector
130
+ this.registerCollector('quality', async (context) => ({
131
+ codeComplexity: context?.quality?.complexity ?? 0,
132
+ maintainability: context?.quality?.maintainability ?? 0,
133
+ documentation: context?.quality?.documentation ?? 0,
134
+ testQuality: context?.quality?.testQuality ?? 0,
135
+ overall: context?.quality?.overall ??
136
+ ((context?.quality?.complexity ?? 0) +
137
+ (context?.quality?.maintainability ?? 0) +
138
+ (context?.quality?.documentation ?? 0) +
139
+ (context?.quality?.testQuality ?? 0)) / 4
140
+ }));
141
+
142
+ // Health metrics collector
143
+ this.registerCollector('health', async (context) => {
144
+ const coverage = await this.getMetric('coverage');
145
+ const constitutional = await this.getMetric('constitutional');
146
+ const quality = await this.getMetric('quality');
147
+
148
+ const coverageScore = coverage?.overall ?? 0;
149
+ const constitutionalScore = constitutional?.totalScore ?? 0;
150
+ const qualityScore = quality?.overall ?? 0;
151
+
152
+ const overall = (coverageScore + constitutionalScore + qualityScore) / 3;
153
+
154
+ return {
155
+ coverageScore,
156
+ constitutionalScore,
157
+ qualityScore,
158
+ overall,
159
+ status: this.calculateStatus(overall, 'quality'),
160
+ timestamp: new Date().toISOString()
161
+ };
162
+ });
163
+ }
164
+
165
+ /**
166
+ * Register a metric collector
167
+ * @param {string} name
168
+ * @param {Function} collector
169
+ */
170
+ registerCollector(name, collector) {
171
+ if (typeof collector !== 'function') {
172
+ throw new Error('Collector must be a function');
173
+ }
174
+ this.collectors.set(name, collector);
175
+ }
176
+
177
+ /**
178
+ * Unregister a collector
179
+ * @param {string} name
180
+ */
181
+ unregisterCollector(name) {
182
+ this.collectors.delete(name);
183
+ this.metrics.delete(name);
184
+ }
185
+
186
+ /**
187
+ * Collect metrics
188
+ * @param {Object} context - External context data
189
+ * @returns {Object}
190
+ */
191
+ async collect(context = {}) {
192
+ const results = {};
193
+ const timestamp = new Date().toISOString();
194
+
195
+ for (const [name, collector] of this.collectors) {
196
+ try {
197
+ const data = await collector(context);
198
+ results[name] = {
199
+ ...data,
200
+ collectedAt: timestamp
201
+ };
202
+ this.metrics.set(name, results[name]);
203
+ } catch (error) {
204
+ results[name] = {
205
+ error: error.message,
206
+ collectedAt: timestamp
207
+ };
208
+ }
209
+ }
210
+
211
+ const snapshot = {
212
+ timestamp,
213
+ metrics: { ...results }
214
+ };
215
+
216
+ this.history.push(snapshot);
217
+ this.emit('collected', snapshot);
218
+
219
+ return results;
220
+ }
221
+
222
+ /**
223
+ * Get a specific metric
224
+ * @param {string} name
225
+ * @returns {Object|null}
226
+ */
227
+ getMetric(name) {
228
+ return this.metrics.get(name) || null;
229
+ }
230
+
231
+ /**
232
+ * Get all metrics
233
+ * @returns {Object}
234
+ */
235
+ getAllMetrics() {
236
+ const result = {};
237
+ for (const [name, data] of this.metrics) {
238
+ result[name] = data;
239
+ }
240
+ return result;
241
+ }
242
+
243
+ /**
244
+ * Calculate health status from score
245
+ * @param {number} score
246
+ * @param {string} category
247
+ * @returns {string}
248
+ */
249
+ calculateStatus(score, category = 'quality') {
250
+ const thresholds = this.thresholds[category] || this.thresholds.quality;
251
+
252
+ if (score >= thresholds.healthy) return HEALTH_STATUS.HEALTHY;
253
+ if (score >= thresholds.warning) return HEALTH_STATUS.WARNING;
254
+ if (score >= thresholds.critical) return HEALTH_STATUS.CRITICAL;
255
+ return HEALTH_STATUS.FAILING;
256
+ }
257
+
258
+ /**
259
+ * Get project health summary
260
+ * @returns {Object}
261
+ */
262
+ getHealthSummary() {
263
+ const coverage = this.metrics.get('coverage');
264
+ const constitutional = this.metrics.get('constitutional');
265
+ const quality = this.metrics.get('quality');
266
+ const health = this.metrics.get('health');
267
+
268
+ return {
269
+ status: health?.status ?? HEALTH_STATUS.FAILING,
270
+ overall: health?.overall ?? 0,
271
+ breakdown: {
272
+ coverage: {
273
+ score: coverage?.overall ?? 0,
274
+ status: this.calculateStatus(coverage?.overall ?? 0, 'coverage')
275
+ },
276
+ constitutional: {
277
+ score: constitutional?.totalScore ?? 0,
278
+ status: this.calculateStatus(constitutional?.totalScore ?? 0, 'constitutional'),
279
+ compliantArticles: constitutional?.compliantCount ?? 0,
280
+ totalArticles: constitutional?.totalArticles ?? 9
281
+ },
282
+ quality: {
283
+ score: quality?.overall ?? 0,
284
+ status: this.calculateStatus(quality?.overall ?? 0, 'quality')
285
+ }
286
+ },
287
+ lastUpdated: health?.timestamp ?? null
288
+ };
289
+ }
290
+
291
+ /**
292
+ * Get trend data
293
+ * @param {string} metricName
294
+ * @param {number} limit
295
+ * @returns {Array}
296
+ */
297
+ getTrend(metricName, limit = 10) {
298
+ const data = this.history
299
+ .filter(h => h.metrics[metricName])
300
+ .slice(-limit)
301
+ .map(h => ({
302
+ timestamp: h.timestamp,
303
+ value: h.metrics[metricName]
304
+ }));
305
+
306
+ if (data.length < 2) {
307
+ return { data, trend: 'stable', change: 0 };
308
+ }
309
+
310
+ const first = this.extractValue(data[0].value);
311
+ const last = this.extractValue(data[data.length - 1].value);
312
+ const change = last - first;
313
+
314
+ let trend = 'stable';
315
+ if (change > 5) trend = 'improving';
316
+ else if (change < -5) trend = 'declining';
317
+
318
+ return { data, trend, change };
319
+ }
320
+
321
+ /**
322
+ * Extract numeric value from metric
323
+ */
324
+ extractValue(metric) {
325
+ if (typeof metric === 'number') return metric;
326
+ if (metric?.overall !== undefined) return metric.overall;
327
+ if (metric?.totalScore !== undefined) return metric.totalScore;
328
+ return 0;
329
+ }
330
+
331
+ /**
332
+ * Get history
333
+ * @param {Object} filter
334
+ * @returns {Array}
335
+ */
336
+ getHistory(filter = {}) {
337
+ let history = [...this.history];
338
+
339
+ if (filter.since) {
340
+ history = history.filter(h => new Date(h.timestamp) >= new Date(filter.since));
341
+ }
342
+
343
+ if (filter.limit) {
344
+ history = history.slice(-filter.limit);
345
+ }
346
+
347
+ return history;
348
+ }
349
+
350
+ /**
351
+ * Export dashboard as markdown report
352
+ * @returns {string}
353
+ */
354
+ exportReport() {
355
+ const summary = this.getHealthSummary();
356
+ let report = `# Quality Dashboard Report\n\n`;
357
+ report += `Generated: ${new Date().toISOString()}\n\n`;
358
+
359
+ // Overall Health
360
+ report += `## Overall Health\n\n`;
361
+ report += `| Metric | Score | Status |\n`;
362
+ report += `|--------|-------|--------|\n`;
363
+ report += `| **Overall** | ${summary.overall.toFixed(1)}% | ${this.statusEmoji(summary.status)} ${summary.status} |\n`;
364
+ report += `| Coverage | ${summary.breakdown.coverage.score.toFixed(1)}% | ${this.statusEmoji(summary.breakdown.coverage.status)} |\n`;
365
+ report += `| Constitutional | ${summary.breakdown.constitutional.score.toFixed(1)}% | ${this.statusEmoji(summary.breakdown.constitutional.status)} |\n`;
366
+ report += `| Quality | ${summary.breakdown.quality.score.toFixed(1)}% | ${this.statusEmoji(summary.breakdown.quality.status)} |\n\n`;
367
+
368
+ // Constitutional Compliance
369
+ const constitutional = this.metrics.get('constitutional');
370
+ if (constitutional?.articles) {
371
+ report += `## Constitutional Compliance\n\n`;
372
+ report += `Articles Compliant: ${summary.breakdown.constitutional.compliantArticles}/${summary.breakdown.constitutional.totalArticles}\n\n`;
373
+ report += `| Article | Score | Compliant |\n`;
374
+ report += `|---------|-------|----------|\n`;
375
+ for (const article of constitutional.articles) {
376
+ const check = article.compliant ? '✅' : '❌';
377
+ report += `| ${article.id} | ${article.score.toFixed(1)}% | ${check} |\n`;
378
+ }
379
+ report += '\n';
380
+ }
381
+
382
+ // Coverage Details
383
+ const coverage = this.metrics.get('coverage');
384
+ if (coverage) {
385
+ report += `## Coverage Details\n\n`;
386
+ report += `| Type | Coverage |\n`;
387
+ report += `|------|----------|\n`;
388
+ report += `| Lines | ${coverage.lines.toFixed(1)}% |\n`;
389
+ report += `| Branches | ${coverage.branches.toFixed(1)}% |\n`;
390
+ report += `| Functions | ${coverage.functions.toFixed(1)}% |\n`;
391
+ report += `| Statements | ${coverage.statements.toFixed(1)}% |\n\n`;
392
+ }
393
+
394
+ // Quality Details
395
+ const quality = this.metrics.get('quality');
396
+ if (quality) {
397
+ report += `## Quality Metrics\n\n`;
398
+ report += `| Metric | Score |\n`;
399
+ report += `|--------|-------|\n`;
400
+ report += `| Complexity | ${quality.codeComplexity.toFixed(1)} |\n`;
401
+ report += `| Maintainability | ${quality.maintainability.toFixed(1)} |\n`;
402
+ report += `| Documentation | ${quality.documentation.toFixed(1)} |\n`;
403
+ report += `| Test Quality | ${quality.testQuality.toFixed(1)} |\n\n`;
404
+ }
405
+
406
+ return report;
407
+ }
408
+
409
+ /**
410
+ * Get emoji for status
411
+ */
412
+ statusEmoji(status) {
413
+ const emojis = {
414
+ [HEALTH_STATUS.HEALTHY]: '🟢',
415
+ [HEALTH_STATUS.WARNING]: '🟡',
416
+ [HEALTH_STATUS.CRITICAL]: '🟠',
417
+ [HEALTH_STATUS.FAILING]: '🔴'
418
+ };
419
+ return emojis[status] || '⚪';
420
+ }
421
+
422
+ /**
423
+ * Start auto-collection
424
+ */
425
+ startAutoCollection() {
426
+ if (this.intervalId) return;
427
+ this.intervalId = setInterval(() => {
428
+ this.collect().catch(err => this.emit('error', err));
429
+ }, this.collectInterval);
430
+ }
431
+
432
+ /**
433
+ * Stop auto-collection
434
+ */
435
+ stopAutoCollection() {
436
+ if (this.intervalId) {
437
+ clearInterval(this.intervalId);
438
+ this.intervalId = null;
439
+ }
440
+ }
441
+
442
+ /**
443
+ * Clear all data
444
+ */
445
+ clear() {
446
+ this.metrics.clear();
447
+ this.history = [];
448
+ this.emit('cleared');
449
+ }
450
+
451
+ /**
452
+ * Set thresholds
453
+ * @param {string} category
454
+ * @param {Object} thresholds
455
+ */
456
+ setThresholds(category, thresholds) {
457
+ this.thresholds[category] = {
458
+ ...this.thresholds[category],
459
+ ...thresholds
460
+ };
461
+ }
462
+
463
+ /**
464
+ * Get stats
465
+ * @returns {Object}
466
+ */
467
+ getStats() {
468
+ return {
469
+ metricsCount: this.metrics.size,
470
+ collectorsCount: this.collectors.size,
471
+ historyCount: this.history.length,
472
+ autoCollecting: this.intervalId !== null,
473
+ thresholds: { ...this.thresholds }
474
+ };
475
+ }
476
+ }
477
+
478
+ /**
479
+ * Factory function
480
+ */
481
+ function createQualityDashboard(options = {}) {
482
+ return new QualityDashboard(options);
483
+ }
484
+
485
+ module.exports = {
486
+ QualityDashboard,
487
+ createQualityDashboard,
488
+ METRIC_CATEGORY,
489
+ HEALTH_STATUS,
490
+ CONSTITUTIONAL_ARTICLES
491
+ };
@@ -6,6 +6,16 @@
6
6
  * - Feature flag management
7
7
  * - Rollback procedures
8
8
  * - Release notes generation
9
+ *
10
+ * Part of MUSUBI v5.0.0 - Production Readiness
11
+ *
12
+ * @module monitoring/release-manager
13
+ * @version 1.0.0
14
+ *
15
+ * @traceability
16
+ * - Requirement: REQ-P5-003 (Release Automation)
17
+ * - Design: docs/design/tdd-musubi-v5.0.0.md#3.3
18
+ * - Test: tests/monitoring/release-manager.test.js
9
19
  */
10
20
 
11
21
  const { EventEmitter } = require('events');