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,565 @@
1
+ /**
2
+ * @fileoverview Skill Tools Manager
3
+ * @description Manage allowed-tools configuration for MUSUBI skills
4
+ * @version 3.11.0
5
+ *
6
+ * Features:
7
+ * - Load skill tool configurations from YAML
8
+ * - Validate tool availability
9
+ * - Generate optimized tool sets per skill
10
+ * - Tool dependency resolution
11
+ * - Skill-aware tool filtering
12
+ */
13
+
14
+ 'use strict';
15
+
16
+ const fs = require('fs');
17
+ const path = require('path');
18
+ const yaml = require('js-yaml');
19
+ const { EventEmitter } = require('events');
20
+
21
+ /**
22
+ * Default tool sets for common skill categories
23
+ */
24
+ const DEFAULT_TOOL_SETS = {
25
+ requirements: [
26
+ 'file_read',
27
+ 'file_write',
28
+ 'search_files',
29
+ 'read_resource'
30
+ ],
31
+ design: [
32
+ 'file_read',
33
+ 'file_write',
34
+ 'search_files',
35
+ 'create_directory',
36
+ 'read_resource'
37
+ ],
38
+ implementation: [
39
+ 'file_read',
40
+ 'file_write',
41
+ 'search_files',
42
+ 'create_directory',
43
+ 'run_command',
44
+ 'read_resource',
45
+ 'code_analysis'
46
+ ],
47
+ testing: [
48
+ 'file_read',
49
+ 'file_write',
50
+ 'search_files',
51
+ 'run_command',
52
+ 'test_runner'
53
+ ],
54
+ validation: [
55
+ 'file_read',
56
+ 'search_files',
57
+ 'read_resource',
58
+ 'validate'
59
+ ],
60
+ documentation: [
61
+ 'file_read',
62
+ 'file_write',
63
+ 'search_files',
64
+ 'read_resource'
65
+ ],
66
+ analysis: [
67
+ 'file_read',
68
+ 'search_files',
69
+ 'code_analysis',
70
+ 'read_resource'
71
+ ],
72
+ deployment: [
73
+ 'file_read',
74
+ 'run_command',
75
+ 'deploy'
76
+ ]
77
+ };
78
+
79
+ /**
80
+ * Tool restriction levels
81
+ */
82
+ const RestrictionLevel = {
83
+ NONE: 'none', // No restrictions
84
+ STANDARD: 'standard', // Default restrictions
85
+ STRICT: 'strict', // Minimal tools only
86
+ CUSTOM: 'custom' // Custom configuration
87
+ };
88
+
89
+ /**
90
+ * Skill Tool Configuration
91
+ */
92
+ class SkillToolConfig {
93
+ constructor(skillName, config = {}) {
94
+ this.skillName = skillName;
95
+ this.allowedTools = config.allowedTools || [];
96
+ this.deniedTools = config.deniedTools || [];
97
+ this.restrictionLevel = config.restrictionLevel || RestrictionLevel.STANDARD;
98
+ this.toolOverrides = config.toolOverrides || {};
99
+ this.inheritFrom = config.inheritFrom || null;
100
+ this.toolDependencies = config.toolDependencies || {};
101
+ }
102
+
103
+ /**
104
+ * Check if a tool is allowed
105
+ * @param {string} toolName - Tool name
106
+ * @returns {boolean} Whether tool is allowed
107
+ */
108
+ isToolAllowed(toolName) {
109
+ // Explicitly denied
110
+ if (this.deniedTools.includes(toolName)) {
111
+ return false;
112
+ }
113
+
114
+ // No restrictions
115
+ if (this.restrictionLevel === RestrictionLevel.NONE) {
116
+ return true;
117
+ }
118
+
119
+ // Explicitly allowed
120
+ if (this.allowedTools.includes(toolName)) {
121
+ return true;
122
+ }
123
+
124
+ // If allowed list is specified, deny everything else
125
+ if (this.allowedTools.length > 0) {
126
+ return false;
127
+ }
128
+
129
+ // Standard mode allows common tools
130
+ return this.restrictionLevel === RestrictionLevel.STANDARD;
131
+ }
132
+
133
+ /**
134
+ * Get effective tool list with dependencies resolved
135
+ * @param {Map<string, Array<string>>} dependencyMap - Tool dependencies
136
+ * @returns {Array<string>} Resolved tool list
137
+ */
138
+ getEffectiveTools(dependencyMap = new Map()) {
139
+ const tools = new Set(this.allowedTools);
140
+
141
+ // Add dependencies
142
+ for (const tool of this.allowedTools) {
143
+ const deps = this.toolDependencies[tool] || dependencyMap.get(tool) || [];
144
+ for (const dep of deps) {
145
+ tools.add(dep);
146
+ }
147
+ }
148
+
149
+ // Remove denied
150
+ for (const denied of this.deniedTools) {
151
+ tools.delete(denied);
152
+ }
153
+
154
+ return Array.from(tools);
155
+ }
156
+
157
+ toJSON() {
158
+ return {
159
+ skillName: this.skillName,
160
+ allowedTools: this.allowedTools,
161
+ deniedTools: this.deniedTools,
162
+ restrictionLevel: this.restrictionLevel,
163
+ toolOverrides: this.toolOverrides,
164
+ inheritFrom: this.inheritFrom
165
+ };
166
+ }
167
+ }
168
+
169
+ /**
170
+ * Skill Tools Manager
171
+ */
172
+ class SkillToolsManager extends EventEmitter {
173
+ constructor(options = {}) {
174
+ super();
175
+ this.options = {
176
+ configDir: options.configDir || '.musubi/tools',
177
+ defaultToolSet: options.defaultToolSet || 'standard',
178
+ enableInheritance: options.enableInheritance !== false,
179
+ ...options
180
+ };
181
+
182
+ this.skillConfigs = new Map();
183
+ this.toolDependencies = new Map();
184
+ this.availableTools = new Set();
185
+ }
186
+
187
+ /**
188
+ * Load tool configuration from file
189
+ * @param {string} configPath - Path to config file
190
+ * @returns {Promise<Object>} Loaded configuration
191
+ */
192
+ async loadConfig(configPath) {
193
+ try {
194
+ const content = await fs.promises.readFile(configPath, 'utf-8');
195
+ const ext = path.extname(configPath).toLowerCase();
196
+
197
+ let config;
198
+ if (ext === '.yaml' || ext === '.yml') {
199
+ config = yaml.load(content);
200
+ } else if (ext === '.json') {
201
+ config = JSON.parse(content);
202
+ } else {
203
+ throw new Error(`Unsupported config format: ${ext}`);
204
+ }
205
+
206
+ await this._processConfig(config);
207
+ this.emit('configLoaded', configPath);
208
+ return config;
209
+
210
+ } catch (error) {
211
+ if (error.code === 'ENOENT') {
212
+ return null;
213
+ }
214
+ throw error;
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Load tool configuration from string
220
+ * @param {string} content - YAML or JSON content
221
+ * @param {string} format - 'yaml' or 'json'
222
+ * @returns {Object} Loaded configuration
223
+ */
224
+ loadConfigFromString(content, format = 'yaml') {
225
+ let config;
226
+ if (format === 'yaml') {
227
+ config = yaml.load(content);
228
+ } else {
229
+ config = JSON.parse(content);
230
+ }
231
+
232
+ this._processConfig(config);
233
+ return config;
234
+ }
235
+
236
+ /**
237
+ * Process loaded configuration
238
+ * @private
239
+ */
240
+ _processConfig(config) {
241
+ // Load tool dependencies
242
+ if (config.toolDependencies) {
243
+ for (const [tool, deps] of Object.entries(config.toolDependencies)) {
244
+ this.toolDependencies.set(tool, deps);
245
+ }
246
+ }
247
+
248
+ // Load skill configurations
249
+ if (config.skills) {
250
+ for (const [skillName, skillConfig] of Object.entries(config.skills)) {
251
+ this.setSkillConfig(skillName, skillConfig);
252
+ }
253
+ }
254
+
255
+ // Load global defaults
256
+ if (config.defaults) {
257
+ this.options.defaultToolSet = config.defaults.toolSet || this.options.defaultToolSet;
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Set configuration for a skill
263
+ * @param {string} skillName - Skill name
264
+ * @param {Object} config - Tool configuration
265
+ * @returns {SkillToolConfig} Created configuration
266
+ */
267
+ setSkillConfig(skillName, config) {
268
+ const skillConfig = new SkillToolConfig(skillName, config);
269
+
270
+ // Handle inheritance
271
+ if (this.options.enableInheritance && skillConfig.inheritFrom) {
272
+ const parentConfig = this.skillConfigs.get(skillConfig.inheritFrom);
273
+ if (parentConfig) {
274
+ // Merge parent allowed tools
275
+ const parentTools = new Set(parentConfig.allowedTools);
276
+ for (const tool of skillConfig.allowedTools) {
277
+ parentTools.add(tool);
278
+ }
279
+ skillConfig.allowedTools = Array.from(parentTools);
280
+ }
281
+ }
282
+
283
+ this.skillConfigs.set(skillName, skillConfig);
284
+ this.emit('skillConfigSet', skillName, skillConfig);
285
+ return skillConfig;
286
+ }
287
+
288
+ /**
289
+ * Get configuration for a skill
290
+ * @param {string} skillName - Skill name
291
+ * @returns {SkillToolConfig|null} Skill configuration
292
+ */
293
+ getSkillConfig(skillName) {
294
+ return this.skillConfigs.get(skillName) || null;
295
+ }
296
+
297
+ /**
298
+ * Get allowed tools for a skill
299
+ * @param {string} skillName - Skill name
300
+ * @param {Object} options - Options
301
+ * @returns {Array<string>} Allowed tools
302
+ */
303
+ getAllowedTools(skillName, options = {}) {
304
+ const config = this.skillConfigs.get(skillName);
305
+
306
+ if (!config) {
307
+ // Return default tools based on skill category
308
+ return this._getDefaultToolsForSkill(skillName);
309
+ }
310
+
311
+ let tools = config.getEffectiveTools(this.toolDependencies);
312
+
313
+ // Filter by available tools if specified
314
+ if (options.filterByAvailable && this.availableTools.size > 0) {
315
+ tools = tools.filter(t => this.availableTools.has(t));
316
+ }
317
+
318
+ return tools;
319
+ }
320
+
321
+ /**
322
+ * Get default tools for a skill based on naming convention
323
+ * @private
324
+ */
325
+ _getDefaultToolsForSkill(skillName) {
326
+ const lowerName = skillName.toLowerCase();
327
+
328
+ for (const [category, tools] of Object.entries(DEFAULT_TOOL_SETS)) {
329
+ if (lowerName.includes(category)) {
330
+ return [...tools];
331
+ }
332
+ }
333
+
334
+ // Return minimal set
335
+ return ['file_read', 'search_files'];
336
+ }
337
+
338
+ /**
339
+ * Set available tools from MCP discovery
340
+ * @param {Array<string>} tools - Available tool names
341
+ */
342
+ setAvailableTools(tools) {
343
+ this.availableTools = new Set(tools);
344
+ this.emit('availableToolsUpdated', tools);
345
+ }
346
+
347
+ /**
348
+ * Validate tool availability for a skill
349
+ * @param {string} skillName - Skill name
350
+ * @returns {Object} Validation result
351
+ */
352
+ validateToolAvailability(skillName) {
353
+ const allowedTools = this.getAllowedTools(skillName);
354
+ const available = [];
355
+ const missing = [];
356
+
357
+ for (const tool of allowedTools) {
358
+ if (this.availableTools.size === 0 || this.availableTools.has(tool)) {
359
+ available.push(tool);
360
+ } else {
361
+ missing.push(tool);
362
+ }
363
+ }
364
+
365
+ return {
366
+ valid: missing.length === 0,
367
+ available,
368
+ missing,
369
+ coverage: available.length / allowedTools.length
370
+ };
371
+ }
372
+
373
+ /**
374
+ * Generate optimized tool configuration for a skill
375
+ * @param {string} skillName - Skill name
376
+ * @param {Object} context - Execution context
377
+ * @returns {Object} Optimized tool config
378
+ */
379
+ generateOptimizedConfig(skillName, context = {}) {
380
+ const config = this.getSkillConfig(skillName);
381
+ const allowedTools = this.getAllowedTools(skillName, { filterByAvailable: true });
382
+
383
+ // Apply context-based optimization
384
+ let optimizedTools = [...allowedTools];
385
+
386
+ if (context.readOnly) {
387
+ // Remove write operations
388
+ optimizedTools = optimizedTools.filter(t =>
389
+ !t.includes('write') && !t.includes('create') && !t.includes('delete')
390
+ );
391
+ }
392
+
393
+ if (context.noNetwork) {
394
+ // Remove network operations
395
+ optimizedTools = optimizedTools.filter(t =>
396
+ !t.includes('http') && !t.includes('api') && !t.includes('fetch')
397
+ );
398
+ }
399
+
400
+ if (context.minimalPermissions) {
401
+ // Keep only essential tools
402
+ optimizedTools = optimizedTools.filter(t =>
403
+ t.includes('read') || t.includes('search') || t.includes('list')
404
+ );
405
+ }
406
+
407
+ return {
408
+ skillName,
409
+ allowedTools: optimizedTools,
410
+ restrictionLevel: config?.restrictionLevel || RestrictionLevel.STANDARD,
411
+ context
412
+ };
413
+ }
414
+
415
+ /**
416
+ * Auto-configure skills based on skill definitions
417
+ * @param {Array<Object>} skills - Skill definitions
418
+ * @returns {Map<string, SkillToolConfig>} Generated configurations
419
+ */
420
+ autoConfigureSkills(skills) {
421
+ const configs = new Map();
422
+
423
+ for (const skill of skills) {
424
+ const skillName = skill.name || skill.id;
425
+
426
+ // Determine category from skill metadata
427
+ const category = this._detectSkillCategory(skill);
428
+ const defaultTools = DEFAULT_TOOL_SETS[category] || DEFAULT_TOOL_SETS.validation;
429
+
430
+ // Merge with explicitly defined tools
431
+ const allowedTools = skill.allowedTools
432
+ ? [...new Set([...defaultTools, ...skill.allowedTools])]
433
+ : defaultTools;
434
+
435
+ const config = this.setSkillConfig(skillName, {
436
+ allowedTools,
437
+ restrictionLevel: skill.restrictionLevel || RestrictionLevel.STANDARD
438
+ });
439
+
440
+ configs.set(skillName, config);
441
+ }
442
+
443
+ return configs;
444
+ }
445
+
446
+ /**
447
+ * Detect skill category from definition
448
+ * @private
449
+ */
450
+ _detectSkillCategory(skill) {
451
+ const text = `${skill.name || ''} ${skill.description || ''} ${skill.purpose || ''}`.toLowerCase();
452
+
453
+ const categoryKeywords = {
454
+ requirements: ['requirement', 'ears', 'spec', 'feature'],
455
+ design: ['design', 'architecture', 'c4', 'adr'],
456
+ implementation: ['implement', 'code', 'develop', 'build'],
457
+ testing: ['test', 'spec', 'coverage', 'assertion'],
458
+ validation: ['validate', 'check', 'verify', 'lint'],
459
+ documentation: ['document', 'readme', 'guide', 'doc'],
460
+ analysis: ['analyze', 'audit', 'review', 'inspect'],
461
+ deployment: ['deploy', 'release', 'publish', 'ci']
462
+ };
463
+
464
+ for (const [category, keywords] of Object.entries(categoryKeywords)) {
465
+ for (const keyword of keywords) {
466
+ if (text.includes(keyword)) {
467
+ return category;
468
+ }
469
+ }
470
+ }
471
+
472
+ return 'validation'; // Default category
473
+ }
474
+
475
+ /**
476
+ * Export all configurations
477
+ * @returns {Object} Configuration export
478
+ */
479
+ exportConfig() {
480
+ const skills = {};
481
+ for (const [name, config] of this.skillConfigs) {
482
+ skills[name] = config.toJSON();
483
+ }
484
+
485
+ const toolDependencies = {};
486
+ for (const [tool, deps] of this.toolDependencies) {
487
+ toolDependencies[tool] = deps;
488
+ }
489
+
490
+ return {
491
+ version: '1.0.0',
492
+ exportedAt: new Date().toISOString(),
493
+ defaults: {
494
+ toolSet: this.options.defaultToolSet
495
+ },
496
+ toolDependencies,
497
+ skills
498
+ };
499
+ }
500
+
501
+ /**
502
+ * Save configuration to file
503
+ * @param {string} filePath - Output file path
504
+ * @returns {Promise<void>}
505
+ */
506
+ async saveConfig(filePath) {
507
+ const config = this.exportConfig();
508
+ const ext = path.extname(filePath).toLowerCase();
509
+
510
+ let content;
511
+ if (ext === '.yaml' || ext === '.yml') {
512
+ content = yaml.dump(config, { indent: 2 });
513
+ } else {
514
+ content = JSON.stringify(config, null, 2);
515
+ }
516
+
517
+ await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
518
+ await fs.promises.writeFile(filePath, content, 'utf-8');
519
+
520
+ this.emit('configSaved', filePath);
521
+ }
522
+
523
+ /**
524
+ * Clear all configurations
525
+ */
526
+ clear() {
527
+ this.skillConfigs.clear();
528
+ this.toolDependencies.clear();
529
+ this.availableTools.clear();
530
+ }
531
+
532
+ /**
533
+ * Get summary statistics
534
+ * @returns {Object} Statistics
535
+ */
536
+ getStats() {
537
+ const skills = Array.from(this.skillConfigs.values());
538
+
539
+ const byRestriction = {};
540
+ for (const level of Object.values(RestrictionLevel)) {
541
+ byRestriction[level] = skills.filter(s => s.restrictionLevel === level).length;
542
+ }
543
+
544
+ const allTools = new Set();
545
+ for (const config of skills) {
546
+ for (const tool of config.allowedTools) {
547
+ allTools.add(tool);
548
+ }
549
+ }
550
+
551
+ return {
552
+ totalSkills: this.skillConfigs.size,
553
+ totalUniqueTools: allTools.size,
554
+ availableTools: this.availableTools.size,
555
+ byRestrictionLevel: byRestriction
556
+ };
557
+ }
558
+ }
559
+
560
+ module.exports = {
561
+ SkillToolsManager,
562
+ SkillToolConfig,
563
+ RestrictionLevel,
564
+ DEFAULT_TOOL_SETS
565
+ };
@@ -3,6 +3,13 @@
3
3
  * @module monitoring/cost-tracker
4
4
  * @version 1.0.0
5
5
  * @description Tracks LLM API usage and costs across providers
6
+ *
7
+ * Part of MUSUBI v5.0.0 - Production Readiness
8
+ *
9
+ * @traceability
10
+ * - Requirement: REQ-P5-005 (Cost Tracking)
11
+ * - Design: docs/design/tdd-musubi-v5.0.0.md#3.5
12
+ * - Test: tests/monitoring/cost-tracker.test.js
6
13
  */
7
14
 
8
15
  'use strict';
@@ -6,6 +6,16 @@
6
6
  * - Runbook execution
7
7
  * - Post-mortem generation
8
8
  * - On-call management
9
+ *
10
+ * Part of MUSUBI v5.0.0 - Production Readiness
11
+ *
12
+ * @module monitoring/incident-manager
13
+ * @version 1.0.0
14
+ *
15
+ * @traceability
16
+ * - Requirement: REQ-P5-002 (Incident Management)
17
+ * - Design: docs/design/tdd-musubi-v5.0.0.md#3.2
18
+ * - Test: tests/monitoring/incident-manager.test.js
9
19
  */
10
20
 
11
21
  const { EventEmitter } = require('events');
@@ -6,6 +6,16 @@
6
6
  * - Metrics collection
7
7
  * - Distributed tracing
8
8
  * - Correlation IDs
9
+ *
10
+ * Part of MUSUBI v5.0.0 - Production Readiness
11
+ *
12
+ * @module monitoring/observability
13
+ * @version 1.0.0
14
+ *
15
+ * @traceability
16
+ * - Requirement: REQ-P5-004 (Observability Integration)
17
+ * - Design: docs/design/tdd-musubi-v5.0.0.md#3.4
18
+ * - Test: tests/monitoring/observability.test.js
9
19
  */
10
20
 
11
21
  const { EventEmitter } = require('events');