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,589 @@
1
+ /**
2
+ * @fileoverview Tool Discovery Service
3
+ * @description Dynamic MCP tool detection and management
4
+ * @version 3.11.0
5
+ *
6
+ * Features:
7
+ * - Automatic tool discovery from MCP servers
8
+ * - Tool categorization and tagging
9
+ * - Skill-to-tool mapping
10
+ * - Tool capability matching
11
+ * - Tool usage analytics
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const { EventEmitter } = require('events');
17
+
18
+ /**
19
+ * Tool Categories
20
+ */
21
+ const ToolCategory = {
22
+ FILE_SYSTEM: 'file-system',
23
+ CODE_ANALYSIS: 'code-analysis',
24
+ SEARCH: 'search',
25
+ DOCUMENTATION: 'documentation',
26
+ TESTING: 'testing',
27
+ BUILD: 'build',
28
+ DEPLOYMENT: 'deployment',
29
+ MONITORING: 'monitoring',
30
+ DATABASE: 'database',
31
+ API: 'api',
32
+ AI: 'ai',
33
+ UTILITY: 'utility',
34
+ UNKNOWN: 'unknown'
35
+ };
36
+
37
+ /**
38
+ * Tool capability scores
39
+ */
40
+ const CapabilityScore = {
41
+ EXACT_MATCH: 1.0,
42
+ HIGH_MATCH: 0.8,
43
+ MEDIUM_MATCH: 0.6,
44
+ LOW_MATCH: 0.4,
45
+ NO_MATCH: 0.0
46
+ };
47
+
48
+ /**
49
+ * Category keywords for auto-detection
50
+ */
51
+ const CATEGORY_KEYWORDS = {
52
+ [ToolCategory.FILE_SYSTEM]: ['file', 'read', 'write', 'directory', 'path', 'fs', 'folder'],
53
+ [ToolCategory.CODE_ANALYSIS]: ['code', 'analyze', 'parse', 'ast', 'syntax', 'lint', 'semantic'],
54
+ [ToolCategory.SEARCH]: ['search', 'find', 'grep', 'query', 'lookup', 'index'],
55
+ [ToolCategory.DOCUMENTATION]: ['doc', 'document', 'readme', 'markdown', 'comment', 'jsdoc'],
56
+ [ToolCategory.TESTING]: ['test', 'spec', 'assert', 'mock', 'coverage', 'jest', 'mocha'],
57
+ [ToolCategory.BUILD]: ['build', 'compile', 'bundle', 'webpack', 'transpile', 'package'],
58
+ [ToolCategory.DEPLOYMENT]: ['deploy', 'release', 'publish', 'ci', 'cd', 'docker', 'kubernetes'],
59
+ [ToolCategory.MONITORING]: ['monitor', 'log', 'metric', 'alert', 'trace', 'health'],
60
+ [ToolCategory.DATABASE]: ['database', 'db', 'sql', 'query', 'migrate', 'schema'],
61
+ [ToolCategory.API]: ['api', 'rest', 'http', 'request', 'endpoint', 'graphql'],
62
+ [ToolCategory.AI]: ['ai', 'ml', 'llm', 'generate', 'embed', 'inference', 'model'],
63
+ [ToolCategory.UTILITY]: ['util', 'helper', 'convert', 'format', 'validate', 'transform']
64
+ };
65
+
66
+ /**
67
+ * Discovered Tool with metadata
68
+ */
69
+ class DiscoveredTool {
70
+ constructor(tool, options = {}) {
71
+ this.name = tool.name;
72
+ this.description = tool.description || '';
73
+ this.inputSchema = tool.inputSchema || {};
74
+ this.server = tool.server || options.server || 'unknown';
75
+ this.category = options.category || ToolCategory.UNKNOWN;
76
+ this.tags = options.tags || [];
77
+ this.capabilities = options.capabilities || [];
78
+ this.usageCount = 0;
79
+ this.lastUsed = null;
80
+ this.averageLatency = 0;
81
+ this.errorRate = 0;
82
+ this.discoveredAt = new Date();
83
+ }
84
+
85
+ /**
86
+ * Record tool usage
87
+ * @param {number} latency - Execution latency in ms
88
+ * @param {boolean} success - Whether execution succeeded
89
+ */
90
+ recordUsage(latency, success = true) {
91
+ this.usageCount++;
92
+ this.lastUsed = new Date();
93
+
94
+ // Update running average latency
95
+ this.averageLatency = (this.averageLatency * (this.usageCount - 1) + latency) / this.usageCount;
96
+
97
+ // Update error rate
98
+ if (!success) {
99
+ this.errorRate = ((this.errorRate * (this.usageCount - 1)) + 1) / this.usageCount;
100
+ } else {
101
+ this.errorRate = (this.errorRate * (this.usageCount - 1)) / this.usageCount;
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Get tool reliability score
107
+ * @returns {number} Score 0-1
108
+ */
109
+ getReliabilityScore() {
110
+ if (this.usageCount === 0) return 0.5; // Neutral for unused tools
111
+ return 1 - this.errorRate;
112
+ }
113
+
114
+ toJSON() {
115
+ return {
116
+ name: this.name,
117
+ description: this.description,
118
+ server: this.server,
119
+ category: this.category,
120
+ tags: this.tags,
121
+ capabilities: this.capabilities,
122
+ usageCount: this.usageCount,
123
+ lastUsed: this.lastUsed,
124
+ averageLatency: this.averageLatency,
125
+ reliability: this.getReliabilityScore(),
126
+ discoveredAt: this.discoveredAt
127
+ };
128
+ }
129
+ }
130
+
131
+ /**
132
+ * Tool Discovery Service
133
+ */
134
+ class ToolDiscovery extends EventEmitter {
135
+ constructor(options = {}) {
136
+ super();
137
+ this.options = {
138
+ autoDiscovery: true,
139
+ refreshInterval: 300000, // 5 minutes
140
+ enableAnalytics: true,
141
+ ...options
142
+ };
143
+
144
+ this.tools = new Map();
145
+ this.toolsByCategory = new Map();
146
+ this.toolsByServer = new Map();
147
+ this.skillToolMappings = new Map();
148
+ this._refreshTimer = null;
149
+ }
150
+
151
+ /**
152
+ * Discover tools from MCP connector
153
+ * @param {MCPConnector} connector - MCP connector instance
154
+ * @returns {Promise<Array<DiscoveredTool>>} Discovered tools
155
+ */
156
+ async discoverFromConnector(connector) {
157
+ const discoveredTools = [];
158
+ const allTools = connector.getAllTools();
159
+
160
+ for (const tool of allTools) {
161
+ const discovered = this._processTool(tool);
162
+ discoveredTools.push(discovered);
163
+ }
164
+
165
+ this.emit('discovered', discoveredTools);
166
+ return discoveredTools;
167
+ }
168
+
169
+ /**
170
+ * Process and categorize a tool
171
+ * @private
172
+ * @param {Object} tool - Raw tool definition
173
+ * @returns {DiscoveredTool} Processed tool
174
+ */
175
+ _processTool(tool) {
176
+ const category = this._detectCategory(tool);
177
+ const tags = this._extractTags(tool);
178
+ const capabilities = this._analyzeCapabilities(tool);
179
+
180
+ const discovered = new DiscoveredTool(tool, {
181
+ category,
182
+ tags,
183
+ capabilities
184
+ });
185
+
186
+ // Index the tool
187
+ this.tools.set(tool.name, discovered);
188
+
189
+ // Index by category
190
+ if (!this.toolsByCategory.has(category)) {
191
+ this.toolsByCategory.set(category, new Set());
192
+ }
193
+ this.toolsByCategory.get(category).add(tool.name);
194
+
195
+ // Index by server
196
+ const server = tool.server || 'unknown';
197
+ if (!this.toolsByServer.has(server)) {
198
+ this.toolsByServer.set(server, new Set());
199
+ }
200
+ this.toolsByServer.get(server).add(tool.name);
201
+
202
+ return discovered;
203
+ }
204
+
205
+ /**
206
+ * Detect tool category based on name and description
207
+ * @private
208
+ */
209
+ _detectCategory(tool) {
210
+ const text = `${tool.name} ${tool.description}`.toLowerCase();
211
+
212
+ let bestCategory = ToolCategory.UNKNOWN;
213
+ let bestScore = 0;
214
+
215
+ for (const [category, keywords] of Object.entries(CATEGORY_KEYWORDS)) {
216
+ let score = 0;
217
+ for (const keyword of keywords) {
218
+ if (text.includes(keyword)) {
219
+ score++;
220
+ }
221
+ }
222
+ if (score > bestScore) {
223
+ bestScore = score;
224
+ bestCategory = category;
225
+ }
226
+ }
227
+
228
+ return bestCategory;
229
+ }
230
+
231
+ /**
232
+ * Extract tags from tool definition
233
+ * @private
234
+ */
235
+ _extractTags(tool) {
236
+ const tags = new Set();
237
+ const text = `${tool.name} ${tool.description}`.toLowerCase();
238
+
239
+ // Extract from name (split by common separators)
240
+ const nameParts = tool.name.split(/[-_./]/);
241
+ for (const part of nameParts) {
242
+ if (part.length > 2) {
243
+ tags.add(part.toLowerCase());
244
+ }
245
+ }
246
+
247
+ // Extract common action verbs
248
+ const actionVerbs = ['read', 'write', 'create', 'update', 'delete', 'list', 'search', 'analyze', 'generate'];
249
+ for (const verb of actionVerbs) {
250
+ if (text.includes(verb)) {
251
+ tags.add(verb);
252
+ }
253
+ }
254
+
255
+ // Add category as tag
256
+ const category = this._detectCategory(tool);
257
+ if (category !== ToolCategory.UNKNOWN) {
258
+ tags.add(category);
259
+ }
260
+
261
+ return Array.from(tags);
262
+ }
263
+
264
+ /**
265
+ * Analyze tool capabilities based on schema
266
+ * @private
267
+ */
268
+ _analyzeCapabilities(tool) {
269
+ const capabilities = [];
270
+ const schema = tool.inputSchema || {};
271
+
272
+ // Analyze input parameters
273
+ if (schema.properties) {
274
+ const propNames = Object.keys(schema.properties);
275
+
276
+ if (propNames.some(p => p.includes('file') || p.includes('path'))) {
277
+ capabilities.push('file-operations');
278
+ }
279
+ if (propNames.some(p => p.includes('query') || p.includes('search'))) {
280
+ capabilities.push('search');
281
+ }
282
+ if (propNames.some(p => p.includes('code') || p.includes('source'))) {
283
+ capabilities.push('code-processing');
284
+ }
285
+ if (propNames.some(p => p.includes('url') || p.includes('endpoint'))) {
286
+ capabilities.push('network');
287
+ }
288
+ }
289
+
290
+ // Analyze based on tool name patterns
291
+ const name = tool.name.toLowerCase();
292
+ if (name.includes('list') || name.includes('get')) {
293
+ capabilities.push('read');
294
+ }
295
+ if (name.includes('create') || name.includes('write') || name.includes('add')) {
296
+ capabilities.push('write');
297
+ }
298
+ if (name.includes('delete') || name.includes('remove')) {
299
+ capabilities.push('delete');
300
+ }
301
+ if (name.includes('update') || name.includes('modify') || name.includes('edit')) {
302
+ capabilities.push('update');
303
+ }
304
+
305
+ return [...new Set(capabilities)];
306
+ }
307
+
308
+ /**
309
+ * Find tools matching criteria
310
+ * @param {Object} criteria - Search criteria
311
+ * @returns {Array<DiscoveredTool>} Matching tools
312
+ */
313
+ findTools(criteria = {}) {
314
+ let results = Array.from(this.tools.values());
315
+
316
+ // Filter by category
317
+ if (criteria.category) {
318
+ results = results.filter(t => t.category === criteria.category);
319
+ }
320
+
321
+ // Filter by server
322
+ if (criteria.server) {
323
+ results = results.filter(t => t.server === criteria.server);
324
+ }
325
+
326
+ // Filter by tags
327
+ if (criteria.tags && criteria.tags.length > 0) {
328
+ results = results.filter(t =>
329
+ criteria.tags.some(tag => t.tags.includes(tag))
330
+ );
331
+ }
332
+
333
+ // Filter by capabilities
334
+ if (criteria.capabilities && criteria.capabilities.length > 0) {
335
+ results = results.filter(t =>
336
+ criteria.capabilities.some(cap => t.capabilities.includes(cap))
337
+ );
338
+ }
339
+
340
+ // Filter by name pattern
341
+ if (criteria.namePattern) {
342
+ const pattern = new RegExp(criteria.namePattern, 'i');
343
+ results = results.filter(t => pattern.test(t.name));
344
+ }
345
+
346
+ // Filter by minimum reliability
347
+ if (typeof criteria.minReliability === 'number') {
348
+ results = results.filter(t => t.getReliabilityScore() >= criteria.minReliability);
349
+ }
350
+
351
+ // Sort by relevance or usage
352
+ if (criteria.sortBy === 'usage') {
353
+ results.sort((a, b) => b.usageCount - a.usageCount);
354
+ } else if (criteria.sortBy === 'reliability') {
355
+ results.sort((a, b) => b.getReliabilityScore() - a.getReliabilityScore());
356
+ } else if (criteria.sortBy === 'latency') {
357
+ results.sort((a, b) => a.averageLatency - b.averageLatency);
358
+ }
359
+
360
+ // Limit results
361
+ if (criteria.limit) {
362
+ results = results.slice(0, criteria.limit);
363
+ }
364
+
365
+ return results;
366
+ }
367
+
368
+ /**
369
+ * Match tools to a task description
370
+ * @param {string} taskDescription - Task description
371
+ * @param {Object} options - Matching options
372
+ * @returns {Array<Object>} Matched tools with scores
373
+ */
374
+ matchToolsToTask(taskDescription, options = {}) {
375
+ const { limit = 5, minScore = 0.3 } = options;
376
+ const matches = [];
377
+ const taskWords = taskDescription.toLowerCase().split(/\s+/);
378
+
379
+ for (const tool of this.tools.values()) {
380
+ let score = 0;
381
+ const toolText = `${tool.name} ${tool.description} ${tool.tags.join(' ')}`.toLowerCase();
382
+
383
+ // Calculate word overlap
384
+ for (const word of taskWords) {
385
+ if (word.length > 2 && toolText.includes(word)) {
386
+ score += 0.2;
387
+ }
388
+ }
389
+
390
+ // Bonus for capability match
391
+ for (const capability of tool.capabilities) {
392
+ if (taskDescription.toLowerCase().includes(capability)) {
393
+ score += 0.3;
394
+ }
395
+ }
396
+
397
+ // Bonus for category match
398
+ for (const [category, keywords] of Object.entries(CATEGORY_KEYWORDS)) {
399
+ if (tool.category === category) {
400
+ for (const keyword of keywords) {
401
+ if (taskDescription.toLowerCase().includes(keyword)) {
402
+ score += 0.15;
403
+ break;
404
+ }
405
+ }
406
+ }
407
+ }
408
+
409
+ // Apply reliability factor
410
+ score *= (0.5 + 0.5 * tool.getReliabilityScore());
411
+
412
+ // Normalize score to 0-1
413
+ score = Math.min(1.0, score);
414
+
415
+ if (score >= minScore) {
416
+ matches.push({ tool, score });
417
+ }
418
+ }
419
+
420
+ // Sort by score descending
421
+ matches.sort((a, b) => b.score - a.score);
422
+
423
+ return matches.slice(0, limit);
424
+ }
425
+
426
+ /**
427
+ * Map skills to available tools
428
+ * @param {Array<Object>} skills - Skill definitions
429
+ * @returns {Map<string, Array<string>>} Skill to tools mapping
430
+ */
431
+ mapSkillsToTools(skills) {
432
+ const mappings = new Map();
433
+
434
+ for (const skill of skills) {
435
+ const skillName = skill.name || skill.id;
436
+ const matchedTools = [];
437
+
438
+ // Match by allowed-tools if defined
439
+ if (skill.allowedTools && Array.isArray(skill.allowedTools)) {
440
+ for (const toolName of skill.allowedTools) {
441
+ if (this.tools.has(toolName)) {
442
+ matchedTools.push(toolName);
443
+ }
444
+ }
445
+ }
446
+
447
+ // Match by skill description/purpose
448
+ if (skill.description || skill.purpose) {
449
+ const taskMatch = this.matchToolsToTask(
450
+ skill.description || skill.purpose,
451
+ { limit: 3, minScore: 0.4 }
452
+ );
453
+ for (const { tool } of taskMatch) {
454
+ if (!matchedTools.includes(tool.name)) {
455
+ matchedTools.push(tool.name);
456
+ }
457
+ }
458
+ }
459
+
460
+ mappings.set(skillName, matchedTools);
461
+ }
462
+
463
+ this.skillToolMappings = mappings;
464
+ return mappings;
465
+ }
466
+
467
+ /**
468
+ * Get tools for a specific skill
469
+ * @param {string} skillName - Skill name
470
+ * @returns {Array<DiscoveredTool>} Tools for the skill
471
+ */
472
+ getToolsForSkill(skillName) {
473
+ const toolNames = this.skillToolMappings.get(skillName) || [];
474
+ return toolNames
475
+ .map(name => this.tools.get(name))
476
+ .filter(Boolean);
477
+ }
478
+
479
+ /**
480
+ * Record tool usage
481
+ * @param {string} toolName - Tool name
482
+ * @param {number} latency - Execution latency
483
+ * @param {boolean} success - Whether execution succeeded
484
+ */
485
+ recordToolUsage(toolName, latency, success = true) {
486
+ const tool = this.tools.get(toolName);
487
+ if (tool) {
488
+ tool.recordUsage(latency, success);
489
+ this.emit('toolUsed', { toolName, latency, success });
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Get analytics summary
495
+ * @returns {Object} Analytics data
496
+ */
497
+ getAnalytics() {
498
+ const tools = Array.from(this.tools.values());
499
+
500
+ const byCategory = {};
501
+ for (const [category, toolNames] of this.toolsByCategory) {
502
+ byCategory[category] = toolNames.size;
503
+ }
504
+
505
+ const byServer = {};
506
+ for (const [server, toolNames] of this.toolsByServer) {
507
+ byServer[server] = toolNames.size;
508
+ }
509
+
510
+ const topUsed = tools
511
+ .sort((a, b) => b.usageCount - a.usageCount)
512
+ .slice(0, 10)
513
+ .map(t => ({ name: t.name, usageCount: t.usageCount }));
514
+
515
+ const leastReliable = tools
516
+ .filter(t => t.usageCount > 0)
517
+ .sort((a, b) => a.getReliabilityScore() - b.getReliabilityScore())
518
+ .slice(0, 5)
519
+ .map(t => ({ name: t.name, reliability: t.getReliabilityScore() }));
520
+
521
+ return {
522
+ totalTools: this.tools.size,
523
+ byCategory,
524
+ byServer,
525
+ topUsed,
526
+ leastReliable,
527
+ totalUsage: tools.reduce((sum, t) => sum + t.usageCount, 0)
528
+ };
529
+ }
530
+
531
+ /**
532
+ * Start auto-refresh timer
533
+ */
534
+ startAutoRefresh(connector) {
535
+ if (this._refreshTimer) {
536
+ clearInterval(this._refreshTimer);
537
+ }
538
+
539
+ this._refreshTimer = setInterval(async () => {
540
+ try {
541
+ await this.discoverFromConnector(connector);
542
+ this.emit('refreshed');
543
+ } catch (error) {
544
+ this.emit('refreshError', error);
545
+ }
546
+ }, this.options.refreshInterval);
547
+ }
548
+
549
+ /**
550
+ * Stop auto-refresh timer
551
+ */
552
+ stopAutoRefresh() {
553
+ if (this._refreshTimer) {
554
+ clearInterval(this._refreshTimer);
555
+ this._refreshTimer = null;
556
+ }
557
+ }
558
+
559
+ /**
560
+ * Clear all discovered tools
561
+ */
562
+ clear() {
563
+ this.tools.clear();
564
+ this.toolsByCategory.clear();
565
+ this.toolsByServer.clear();
566
+ this.skillToolMappings.clear();
567
+ }
568
+
569
+ /**
570
+ * Export tool catalog
571
+ * @returns {Object} Catalog data
572
+ */
573
+ exportCatalog() {
574
+ return {
575
+ exportedAt: new Date().toISOString(),
576
+ tools: Array.from(this.tools.values()).map(t => t.toJSON()),
577
+ categories: Object.values(ToolCategory),
578
+ analytics: this.getAnalytics()
579
+ };
580
+ }
581
+ }
582
+
583
+ module.exports = {
584
+ ToolDiscovery,
585
+ DiscoveredTool,
586
+ ToolCategory,
587
+ CapabilityScore,
588
+ CATEGORY_KEYWORDS
589
+ };
@@ -12,6 +12,7 @@ const MemoryCondenser = require('./memory-condenser');
12
12
  const RepoSkillManager = require('./repo-skill-manager');
13
13
  const SkillLoader = require('./skill-loader');
14
14
  const WorkflowManager = require('./workflow');
15
+ const { SkillToolsManager, SkillToolConfig, RestrictionLevel, DEFAULT_TOOL_SETS } = require('./skill-tools');
15
16
 
16
17
  module.exports = {
17
18
  // Checkpoint
@@ -19,6 +20,12 @@ module.exports = {
19
20
  CheckpointState,
20
21
  CheckpointConfig,
21
22
 
23
+ // Skill Tools (Sprint 3.4)
24
+ SkillToolsManager,
25
+ SkillToolConfig,
26
+ RestrictionLevel,
27
+ DEFAULT_TOOL_SETS,
28
+
22
29
  // Other managers
23
30
  AgentMemory,
24
31
  ChangeManager,