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,514 @@
1
+ /**
2
+ * MUSUBI Schema Generator
3
+ *
4
+ * Generates JSON Schema from various sources including
5
+ * JavaScript functions, TypeScript types, and JSDoc comments.
6
+ *
7
+ * @module agents/schema-generator
8
+ */
9
+
10
+ const fs = require('fs');
11
+ const path = require('path');
12
+
13
+ /**
14
+ * @typedef {Object} SchemaGeneratorOptions
15
+ * @property {boolean} [strict=false] - Enable strict mode
16
+ * @property {boolean} [includeExamples=true] - Include examples in schema
17
+ * @property {string} [defaultType='string'] - Default type for unknown
18
+ */
19
+
20
+ /**
21
+ * Schema Generator for function tools
22
+ */
23
+ class SchemaGenerator {
24
+ /**
25
+ * @param {SchemaGeneratorOptions} options
26
+ */
27
+ constructor(options = {}) {
28
+ this.strict = options.strict ?? false;
29
+ this.includeExamples = options.includeExamples ?? true;
30
+ this.defaultType = options.defaultType ?? 'string';
31
+ }
32
+
33
+ /**
34
+ * Generate schema from a function
35
+ * @param {Function} fn
36
+ * @returns {Object} JSON Schema
37
+ */
38
+ fromFunction(fn) {
39
+ const fnString = fn.toString();
40
+ const params = this.extractParameters(fnString);
41
+ const jsdoc = this.extractJSDoc(fnString);
42
+
43
+ return this.buildSchema(params, jsdoc);
44
+ }
45
+
46
+ /**
47
+ * Generate schema from JSDoc comment
48
+ * @param {string} jsdoc
49
+ * @returns {Object} JSON Schema
50
+ */
51
+ fromJSDoc(jsdoc) {
52
+ const parsed = this.parseJSDoc(jsdoc);
53
+ return this.buildSchemaFromParsed(parsed);
54
+ }
55
+
56
+ /**
57
+ * Generate schema from a class method
58
+ * @param {Object} instance - Class instance
59
+ * @param {string} methodName - Method name
60
+ * @returns {Object} JSON Schema
61
+ */
62
+ fromMethod(instance, methodName) {
63
+ const method = instance[methodName];
64
+ if (typeof method !== 'function') {
65
+ throw new Error(`${methodName} is not a method`);
66
+ }
67
+ return this.fromFunction(method.bind(instance));
68
+ }
69
+
70
+ /**
71
+ * Generate schema from file (extracts functions)
72
+ * @param {string} filePath
73
+ * @returns {Object<string, Object>} Map of function names to schemas
74
+ */
75
+ fromFile(filePath) {
76
+ const content = fs.readFileSync(filePath, 'utf-8');
77
+ const functions = this.extractFunctions(content);
78
+ const schemas = {};
79
+
80
+ for (const fn of functions) {
81
+ schemas[fn.name] = this.buildSchema(fn.params, fn.jsdoc);
82
+ }
83
+
84
+ return schemas;
85
+ }
86
+
87
+ /**
88
+ * Extract function parameters from function string
89
+ * @param {string} fnString
90
+ * @returns {Array}
91
+ */
92
+ extractParameters(fnString) {
93
+ // Match function parameters
94
+ const paramMatch = fnString.match(/\(([^)]*)\)/);
95
+ if (!paramMatch) return [];
96
+
97
+ const paramString = paramMatch[1];
98
+ if (!paramString.trim()) return [];
99
+
100
+ return this.parseParameterString(paramString);
101
+ }
102
+
103
+ /**
104
+ * Parse parameter string into structured format
105
+ * @param {string} paramString
106
+ * @returns {Array}
107
+ */
108
+ parseParameterString(paramString) {
109
+ const params = [];
110
+ let depth = 0;
111
+ let current = '';
112
+ let inDefault = false;
113
+
114
+ for (const char of paramString) {
115
+ if (char === '{' || char === '[' || char === '(') depth++;
116
+ if (char === '}' || char === ']' || char === ')') depth--;
117
+
118
+ if (char === '=' && depth === 0) {
119
+ inDefault = true;
120
+ }
121
+
122
+ if (char === ',' && depth === 0) {
123
+ if (current.trim()) {
124
+ params.push(this.parseParameter(current.trim()));
125
+ }
126
+ current = '';
127
+ inDefault = false;
128
+ continue;
129
+ }
130
+
131
+ current += char;
132
+ }
133
+
134
+ if (current.trim()) {
135
+ params.push(this.parseParameter(current.trim()));
136
+ }
137
+
138
+ return params;
139
+ }
140
+
141
+ /**
142
+ * Parse a single parameter
143
+ * @param {string} param
144
+ * @returns {Object}
145
+ */
146
+ parseParameter(param) {
147
+ // Handle destructuring
148
+ if (param.startsWith('{')) {
149
+ const match = param.match(/\{([^}]+)\}/);
150
+ if (match) {
151
+ const destructured = match[1].split(',').map(p => p.trim().split(':')[0].split('=')[0].trim());
152
+ return {
153
+ name: 'options',
154
+ type: 'object',
155
+ destructured,
156
+ required: !param.includes('=')
157
+ };
158
+ }
159
+ }
160
+
161
+ // Handle default values
162
+ const [nameWithType, defaultValue] = param.split('=').map(p => p.trim());
163
+ const name = nameWithType.replace(/:\s*\w+$/, '').trim();
164
+
165
+ // Try to infer type from default value
166
+ let type = this.defaultType;
167
+ if (defaultValue !== undefined) {
168
+ type = this.inferTypeFromValue(defaultValue);
169
+ }
170
+
171
+ return {
172
+ name,
173
+ type,
174
+ required: defaultValue === undefined,
175
+ default: defaultValue ? this.parseDefaultValue(defaultValue) : undefined
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Infer type from a default value string
181
+ * @param {string} valueStr
182
+ * @returns {string}
183
+ */
184
+ inferTypeFromValue(valueStr) {
185
+ valueStr = valueStr.trim();
186
+
187
+ if (valueStr === 'true' || valueStr === 'false') return 'boolean';
188
+ if (valueStr === 'null') return 'null';
189
+ if (valueStr === '[]' || valueStr.startsWith('[')) return 'array';
190
+ if (valueStr === '{}' || valueStr.startsWith('{')) return 'object';
191
+ if (/^['"`]/.test(valueStr)) return 'string';
192
+ if (/^-?\d+$/.test(valueStr)) return 'integer';
193
+ if (/^-?\d+\.\d+$/.test(valueStr)) return 'number';
194
+
195
+ return this.defaultType;
196
+ }
197
+
198
+ /**
199
+ * Parse default value string to actual value
200
+ * @param {string} valueStr
201
+ * @returns {*}
202
+ */
203
+ parseDefaultValue(valueStr) {
204
+ try {
205
+ // Handle string literals
206
+ if (/^['"`]/.test(valueStr)) {
207
+ return valueStr.slice(1, -1);
208
+ }
209
+ return JSON.parse(valueStr);
210
+ } catch {
211
+ return valueStr;
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Extract JSDoc comment from function string
217
+ * @param {string} fnString
218
+ * @returns {Object|null}
219
+ */
220
+ extractJSDoc(fnString) {
221
+ const match = fnString.match(/\/\*\*[\s\S]*?\*\//);
222
+ if (!match) return null;
223
+ return this.parseJSDoc(match[0]);
224
+ }
225
+
226
+ /**
227
+ * Parse JSDoc comment
228
+ * @param {string} jsdoc
229
+ * @returns {Object}
230
+ */
231
+ parseJSDoc(jsdoc) {
232
+ const result = {
233
+ description: '',
234
+ params: [],
235
+ returns: null,
236
+ example: null,
237
+ throws: []
238
+ };
239
+
240
+ const lines = jsdoc.split('\n')
241
+ .map(l => l.replace(/^\s*\*\s?/, '').trim())
242
+ .filter(l => l && !l.startsWith('/'));
243
+
244
+ let currentTag = null;
245
+ let buffer = [];
246
+
247
+ for (const line of lines) {
248
+ if (line.startsWith('@')) {
249
+ // Process previous buffer
250
+ if (currentTag === null && buffer.length) {
251
+ result.description = buffer.join(' ');
252
+ }
253
+ buffer = [];
254
+
255
+ if (line.startsWith('@param')) {
256
+ currentTag = 'param';
257
+ const parsed = this.parseParamTag(line);
258
+ if (parsed) result.params.push(parsed);
259
+ } else if (line.startsWith('@returns') || line.startsWith('@return')) {
260
+ currentTag = 'returns';
261
+ const match = line.match(/@returns?\s+\{([^}]+)\}\s*(.*)/);
262
+ if (match) {
263
+ result.returns = { type: match[1], description: match[2] || '' };
264
+ }
265
+ } else if (line.startsWith('@example')) {
266
+ currentTag = 'example';
267
+ result.example = '';
268
+ } else if (line.startsWith('@throws')) {
269
+ currentTag = 'throws';
270
+ const match = line.match(/@throws\s+\{([^}]+)\}\s*(.*)/);
271
+ if (match) {
272
+ result.throws.push({ type: match[1], description: match[2] || '' });
273
+ }
274
+ } else {
275
+ currentTag = 'other';
276
+ }
277
+ } else {
278
+ buffer.push(line);
279
+ if (currentTag === 'example') {
280
+ result.example += (result.example ? '\n' : '') + line;
281
+ }
282
+ }
283
+ }
284
+
285
+ if (currentTag === null && buffer.length) {
286
+ result.description = buffer.join(' ');
287
+ }
288
+
289
+ return result;
290
+ }
291
+
292
+ /**
293
+ * Parse @param tag
294
+ * @param {string} line
295
+ * @returns {Object|null}
296
+ */
297
+ parseParamTag(line) {
298
+ const match = line.match(/@param\s+\{([^}]+)\}\s+(\[)?(\w+)(?:\])?(?:\s*-?\s*(.*))?/);
299
+ if (!match) return null;
300
+
301
+ const [, type, optional, name, description] = match;
302
+ return {
303
+ name,
304
+ type: type.toLowerCase(),
305
+ required: !optional,
306
+ description: description || ''
307
+ };
308
+ }
309
+
310
+ /**
311
+ * Build schema from extracted params and JSDoc
312
+ * @param {Array} params
313
+ * @param {Object|null} jsdoc
314
+ * @returns {Object}
315
+ */
316
+ buildSchema(params, jsdoc) {
317
+ const properties = {};
318
+ const required = [];
319
+
320
+ // Merge param info from function signature and JSDoc
321
+ const paramMap = new Map();
322
+
323
+ for (const param of params) {
324
+ paramMap.set(param.name, { ...param });
325
+ }
326
+
327
+ if (jsdoc?.params) {
328
+ for (const docParam of jsdoc.params) {
329
+ const existing = paramMap.get(docParam.name);
330
+ if (existing) {
331
+ Object.assign(existing, {
332
+ type: docParam.type || existing.type,
333
+ description: docParam.description || existing.description,
334
+ required: docParam.required ?? existing.required
335
+ });
336
+ } else {
337
+ paramMap.set(docParam.name, docParam);
338
+ }
339
+ }
340
+ }
341
+
342
+ // Build properties
343
+ for (const [name, param] of paramMap) {
344
+ if (param.destructured) {
345
+ // Expand destructured object
346
+ for (const prop of param.destructured) {
347
+ properties[prop] = {
348
+ type: 'string',
349
+ description: `Property: ${prop}`
350
+ };
351
+ }
352
+ } else {
353
+ properties[name] = this.typeToSchema(param.type, param);
354
+ if (param.description) {
355
+ properties[name].description = param.description;
356
+ }
357
+ if (param.default !== undefined) {
358
+ properties[name].default = param.default;
359
+ }
360
+ }
361
+
362
+ if (param.required && !param.destructured) {
363
+ required.push(name);
364
+ }
365
+ }
366
+
367
+ const schema = {
368
+ type: 'object',
369
+ properties
370
+ };
371
+
372
+ if (required.length > 0) {
373
+ schema.required = required;
374
+ }
375
+
376
+ if (this.strict) {
377
+ schema.additionalProperties = false;
378
+ }
379
+
380
+ return schema;
381
+ }
382
+
383
+ /**
384
+ * Build schema from parsed JSDoc
385
+ * @param {Object} parsed
386
+ * @returns {Object}
387
+ */
388
+ buildSchemaFromParsed(parsed) {
389
+ return this.buildSchema([], parsed);
390
+ }
391
+
392
+ /**
393
+ * Convert type string to JSON Schema
394
+ * @param {string} typeStr
395
+ * @param {Object} [param]
396
+ * @returns {Object}
397
+ */
398
+ typeToSchema(typeStr, param = {}) {
399
+ const type = typeStr.toLowerCase();
400
+
401
+ // Handle array types
402
+ if (type.endsWith('[]')) {
403
+ const itemType = type.slice(0, -2);
404
+ return {
405
+ type: 'array',
406
+ items: this.typeToSchema(itemType)
407
+ };
408
+ }
409
+
410
+ // Handle union types
411
+ if (type.includes('|')) {
412
+ const types = type.split('|').map(t => this.typeToSchema(t.trim()));
413
+ return { anyOf: types };
414
+ }
415
+
416
+ // Handle common types
417
+ const typeMap = {
418
+ 'string': { type: 'string' },
419
+ 'number': { type: 'number' },
420
+ 'integer': { type: 'integer' },
421
+ 'int': { type: 'integer' },
422
+ 'boolean': { type: 'boolean' },
423
+ 'bool': { type: 'boolean' },
424
+ 'object': { type: 'object' },
425
+ 'array': { type: 'array' },
426
+ 'any': { type: 'object' },
427
+ 'null': { type: 'null' },
428
+ '*': { type: 'object' }
429
+ };
430
+
431
+ return typeMap[type] || { type: 'string' };
432
+ }
433
+
434
+ /**
435
+ * Extract all functions from file content
436
+ * @param {string} content
437
+ * @returns {Array}
438
+ */
439
+ extractFunctions(content) {
440
+ const functions = [];
441
+
442
+ // Match function declarations with JSDoc
443
+ const regex = /(\/\*\*[\s\S]*?\*\/)\s*(?:async\s+)?function\s+(\w+)\s*\(([^)]*)\)/g;
444
+ let match;
445
+
446
+ while ((match = regex.exec(content)) !== null) {
447
+ const [, jsdoc, name, params] = match;
448
+ functions.push({
449
+ name,
450
+ params: this.parseParameterString(params),
451
+ jsdoc: this.parseJSDoc(jsdoc)
452
+ });
453
+ }
454
+
455
+ // Match arrow functions with JSDoc
456
+ const arrowRegex = /(\/\*\*[\s\S]*?\*\/)\s*(?:const|let|var)\s+(\w+)\s*=\s*(?:async\s+)?\(([^)]*)\)\s*=>/g;
457
+
458
+ while ((match = arrowRegex.exec(content)) !== null) {
459
+ const [, jsdoc, name, params] = match;
460
+ functions.push({
461
+ name,
462
+ params: this.parseParameterString(params),
463
+ jsdoc: this.parseJSDoc(jsdoc)
464
+ });
465
+ }
466
+
467
+ return functions;
468
+ }
469
+
470
+ /**
471
+ * Generate OpenAI function tool schema
472
+ * @param {string} name
473
+ * @param {string} description
474
+ * @param {Object} parameters
475
+ * @returns {Object}
476
+ */
477
+ toOpenAITool(name, description, parameters) {
478
+ return {
479
+ type: 'function',
480
+ function: {
481
+ name,
482
+ description,
483
+ parameters
484
+ }
485
+ };
486
+ }
487
+
488
+ /**
489
+ * Generate Anthropic tool schema
490
+ * @param {string} name
491
+ * @param {string} description
492
+ * @param {Object} parameters
493
+ * @returns {Object}
494
+ */
495
+ toAnthropicTool(name, description, parameters) {
496
+ return {
497
+ name,
498
+ description,
499
+ input_schema: parameters
500
+ };
501
+ }
502
+ }
503
+
504
+ /**
505
+ * Create a schema generator with default options
506
+ */
507
+ function createSchemaGenerator(options = {}) {
508
+ return new SchemaGenerator(options);
509
+ }
510
+
511
+ module.exports = {
512
+ SchemaGenerator,
513
+ createSchemaGenerator
514
+ };