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