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.
- package/README.ja.md +25 -1
- package/README.md +30 -1
- package/package.json +1 -1
- package/src/agents/agent-loop.js +532 -0
- package/src/agents/agentic/code-generator.js +767 -0
- package/src/agents/agentic/code-reviewer.js +698 -0
- package/src/agents/agentic/index.js +43 -0
- package/src/agents/function-tool.js +432 -0
- package/src/agents/index.js +45 -0
- package/src/agents/schema-generator.js +514 -0
- package/src/analyzers/ast-extractor.js +863 -0
- package/src/analyzers/context-optimizer.js +674 -0
- package/src/analyzers/repository-map.js +685 -0
- package/src/integrations/index.js +7 -1
- package/src/integrations/mcp/index.js +175 -0
- package/src/integrations/mcp/mcp-context-provider.js +472 -0
- package/src/integrations/mcp/mcp-discovery.js +436 -0
- package/src/integrations/mcp/mcp-tool-registry.js +467 -0
- package/src/integrations/mcp-connector.js +818 -0
- package/src/integrations/tool-discovery.js +589 -0
- package/src/managers/index.js +7 -0
- package/src/managers/skill-tools.js +565 -0
- package/src/monitoring/quality-dashboard.js +483 -0
- package/src/orchestration/agent-skill-binding.js +655 -0
- package/src/orchestration/error-handler.js +827 -0
- package/src/orchestration/index.js +235 -1
- package/src/orchestration/mcp-tool-adapters.js +896 -0
- package/src/orchestration/reasoning/index.js +58 -0
- package/src/orchestration/reasoning/planning-engine.js +831 -0
- package/src/orchestration/reasoning/reasoning-engine.js +710 -0
- package/src/orchestration/reasoning/self-correction.js +751 -0
- package/src/orchestration/skill-executor.js +665 -0
- package/src/orchestration/skill-registry.js +650 -0
- package/src/orchestration/workflow-examples.js +1072 -0
- package/src/orchestration/workflow-executor.js +779 -0
- package/src/phase4-integration.js +248 -0
- package/src/phase5-integration.js +402 -0
- package/src/steering/steering-auto-update.js +572 -0
- package/src/steering/steering-validator.js +547 -0
- package/src/templates/agents/claude-code/CLAUDE.md +9 -0
- package/src/templates/agents/claude-code/skills/constitution-enforcer/SKILL.md +26 -0
- package/src/templates/agents/claude-code/skills/orchestrator/SKILL.md +9 -0
- package/src/templates/agents/claude-code/skills/security-auditor/SKILL.md +34 -0
- package/src/templates/agents/codex/AGENTS.md +9 -0
- package/src/templates/agents/cursor/AGENTS.md +9 -0
- package/src/templates/agents/gemini-cli/GEMINI.md +9 -0
- package/src/templates/agents/github-copilot/AGENTS.md +9 -0
- package/src/templates/agents/qwen-code/QWEN.md +9 -0
- package/src/templates/agents/windsurf/AGENTS.md +9 -0
- package/src/templates/template-constraints.js +646 -0
- package/src/validators/advanced-validation.js +580 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file code-generator.js
|
|
3
|
+
* @description Autonomous code generation engine for agentic coding
|
|
4
|
+
* @version 1.0.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
const { EventEmitter } = require('events');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Generation mode types
|
|
14
|
+
* @enum {string}
|
|
15
|
+
*/
|
|
16
|
+
const GEN_MODE = {
|
|
17
|
+
CREATE: 'create',
|
|
18
|
+
MODIFY: 'modify',
|
|
19
|
+
EXTEND: 'extend',
|
|
20
|
+
REFACTOR: 'refactor'
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Language types
|
|
25
|
+
* @enum {string}
|
|
26
|
+
*/
|
|
27
|
+
const LANGUAGE = {
|
|
28
|
+
JAVASCRIPT: 'javascript',
|
|
29
|
+
TYPESCRIPT: 'typescript',
|
|
30
|
+
PYTHON: 'python',
|
|
31
|
+
JSON: 'json',
|
|
32
|
+
MARKDOWN: 'markdown',
|
|
33
|
+
YAML: 'yaml'
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* @typedef {Object} GenerationRequest
|
|
38
|
+
* @property {string} description - What to generate
|
|
39
|
+
* @property {string} [mode=GEN_MODE.CREATE] - Generation mode
|
|
40
|
+
* @property {string} [language] - Target language
|
|
41
|
+
* @property {string} [filePath] - Target file path
|
|
42
|
+
* @property {Object} [context] - Additional context
|
|
43
|
+
* @property {Object} [constraints] - Generation constraints
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* @typedef {Object} GenerationResult
|
|
48
|
+
* @property {string} id - Result identifier
|
|
49
|
+
* @property {boolean} success - Whether generation succeeded
|
|
50
|
+
* @property {string} code - Generated code
|
|
51
|
+
* @property {string} language - Code language
|
|
52
|
+
* @property {string} [filePath] - Target file path
|
|
53
|
+
* @property {Object} metadata - Generation metadata
|
|
54
|
+
* @property {string[]} [warnings] - Generation warnings
|
|
55
|
+
*/
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* @typedef {Object} CodeGeneratorOptions
|
|
59
|
+
* @property {Object} [templates={}] - Code templates
|
|
60
|
+
* @property {boolean} [addComments=true] - Add documentation comments
|
|
61
|
+
* @property {boolean} [addTyping=true] - Add type annotations where applicable
|
|
62
|
+
* @property {string} [style='standard'] - Code style
|
|
63
|
+
* @property {number} [indentSize=2] - Indentation size
|
|
64
|
+
* @property {boolean} [useTabs=false] - Use tabs for indentation
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Code templates for various patterns
|
|
69
|
+
*/
|
|
70
|
+
const TEMPLATES = {
|
|
71
|
+
javascript: {
|
|
72
|
+
class: `/**
|
|
73
|
+
* @class {name}
|
|
74
|
+
* @description {description}
|
|
75
|
+
*/
|
|
76
|
+
class {name} {
|
|
77
|
+
/**
|
|
78
|
+
* Create {name}
|
|
79
|
+
* @param {Object} options - Options
|
|
80
|
+
*/
|
|
81
|
+
constructor(options = {}) {
|
|
82
|
+
{properties}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
{methods}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
module.exports = { {name} };`,
|
|
89
|
+
|
|
90
|
+
function: `/**
|
|
91
|
+
* {description}
|
|
92
|
+
* @param {params}
|
|
93
|
+
* @returns {returns}
|
|
94
|
+
*/
|
|
95
|
+
function {name}({paramNames}) {
|
|
96
|
+
{body}
|
|
97
|
+
}`,
|
|
98
|
+
|
|
99
|
+
asyncFunction: `/**
|
|
100
|
+
* {description}
|
|
101
|
+
* @param {params}
|
|
102
|
+
* @returns {Promise<{returns}>}
|
|
103
|
+
*/
|
|
104
|
+
async function {name}({paramNames}) {
|
|
105
|
+
{body}
|
|
106
|
+
}`,
|
|
107
|
+
|
|
108
|
+
module: `/**
|
|
109
|
+
* @file {filename}
|
|
110
|
+
* @description {description}
|
|
111
|
+
* @version 1.0.0
|
|
112
|
+
*/
|
|
113
|
+
|
|
114
|
+
'use strict';
|
|
115
|
+
|
|
116
|
+
{imports}
|
|
117
|
+
|
|
118
|
+
{body}
|
|
119
|
+
|
|
120
|
+
module.exports = {
|
|
121
|
+
{exports}
|
|
122
|
+
};`,
|
|
123
|
+
|
|
124
|
+
test: `/**
|
|
125
|
+
* @file {filename}
|
|
126
|
+
* @description Tests for {testSubject}
|
|
127
|
+
*/
|
|
128
|
+
|
|
129
|
+
'use strict';
|
|
130
|
+
|
|
131
|
+
const { {imports} } = require('{importPath}');
|
|
132
|
+
|
|
133
|
+
describe('{testSubject}', () => {
|
|
134
|
+
{beforeEach}
|
|
135
|
+
|
|
136
|
+
{testCases}
|
|
137
|
+
});`
|
|
138
|
+
},
|
|
139
|
+
|
|
140
|
+
typescript: {
|
|
141
|
+
interface: `/**
|
|
142
|
+
* {description}
|
|
143
|
+
*/
|
|
144
|
+
export interface {name} {
|
|
145
|
+
{properties}
|
|
146
|
+
}`,
|
|
147
|
+
|
|
148
|
+
class: `/**
|
|
149
|
+
* @class {name}
|
|
150
|
+
* @description {description}
|
|
151
|
+
*/
|
|
152
|
+
export class {name} {
|
|
153
|
+
{properties}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Create {name}
|
|
157
|
+
*/
|
|
158
|
+
constructor({constructorParams}) {
|
|
159
|
+
{constructorBody}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
{methods}
|
|
163
|
+
}`,
|
|
164
|
+
|
|
165
|
+
function: `/**
|
|
166
|
+
* {description}
|
|
167
|
+
* @param {params}
|
|
168
|
+
* @returns {returns}
|
|
169
|
+
*/
|
|
170
|
+
export function {name}({paramNames}): {returnType} {
|
|
171
|
+
{body}
|
|
172
|
+
}`
|
|
173
|
+
},
|
|
174
|
+
|
|
175
|
+
python: {
|
|
176
|
+
class: `"""
|
|
177
|
+
{description}
|
|
178
|
+
"""
|
|
179
|
+
|
|
180
|
+
class {name}:
|
|
181
|
+
"""
|
|
182
|
+
{classDoc}
|
|
183
|
+
"""
|
|
184
|
+
|
|
185
|
+
def __init__(self{initParams}):
|
|
186
|
+
"""Initialize {name}."""
|
|
187
|
+
{initBody}
|
|
188
|
+
|
|
189
|
+
{methods}`,
|
|
190
|
+
|
|
191
|
+
function: `def {name}({paramNames}){returnType}:
|
|
192
|
+
"""
|
|
193
|
+
{description}
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
{argDocs}
|
|
197
|
+
|
|
198
|
+
Returns:
|
|
199
|
+
{returnDoc}
|
|
200
|
+
"""
|
|
201
|
+
{body}`,
|
|
202
|
+
|
|
203
|
+
asyncFunction: `async def {name}({paramNames}){returnType}:
|
|
204
|
+
"""
|
|
205
|
+
{description}
|
|
206
|
+
|
|
207
|
+
Args:
|
|
208
|
+
{argDocs}
|
|
209
|
+
|
|
210
|
+
Returns:
|
|
211
|
+
{returnDoc}
|
|
212
|
+
"""
|
|
213
|
+
{body}`
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Code Generator class for autonomous code generation
|
|
219
|
+
* @extends EventEmitter
|
|
220
|
+
*/
|
|
221
|
+
class CodeGenerator extends EventEmitter {
|
|
222
|
+
/**
|
|
223
|
+
* Create code generator
|
|
224
|
+
* @param {CodeGeneratorOptions} [options={}] - Generator options
|
|
225
|
+
*/
|
|
226
|
+
constructor(options = {}) {
|
|
227
|
+
super();
|
|
228
|
+
|
|
229
|
+
this.templates = { ...TEMPLATES, ...(options.templates || {}) };
|
|
230
|
+
this.addComments = options.addComments ?? true;
|
|
231
|
+
this.addTyping = options.addTyping ?? true;
|
|
232
|
+
this.style = options.style || 'standard';
|
|
233
|
+
this.indentSize = options.indentSize ?? 2;
|
|
234
|
+
this.useTabs = options.useTabs ?? false;
|
|
235
|
+
|
|
236
|
+
// State
|
|
237
|
+
this.history = [];
|
|
238
|
+
this.generationCounter = 0;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Generate code from request
|
|
243
|
+
* @param {GenerationRequest} request - Generation request
|
|
244
|
+
* @returns {Promise<GenerationResult>}
|
|
245
|
+
*/
|
|
246
|
+
async generate(request) {
|
|
247
|
+
const id = this.generateId();
|
|
248
|
+
const startTime = Date.now();
|
|
249
|
+
|
|
250
|
+
this.emit('generation:start', { id, request });
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
// Determine language
|
|
254
|
+
const language = request.language || this.detectLanguage(request);
|
|
255
|
+
|
|
256
|
+
// Select generation strategy
|
|
257
|
+
let code;
|
|
258
|
+
switch (request.mode || GEN_MODE.CREATE) {
|
|
259
|
+
case GEN_MODE.CREATE:
|
|
260
|
+
code = await this.generateNew(request, language);
|
|
261
|
+
break;
|
|
262
|
+
case GEN_MODE.MODIFY:
|
|
263
|
+
code = await this.generateModification(request, language);
|
|
264
|
+
break;
|
|
265
|
+
case GEN_MODE.EXTEND:
|
|
266
|
+
code = await this.generateExtension(request, language);
|
|
267
|
+
break;
|
|
268
|
+
case GEN_MODE.REFACTOR:
|
|
269
|
+
code = await this.generateRefactoring(request, language);
|
|
270
|
+
break;
|
|
271
|
+
default:
|
|
272
|
+
code = await this.generateNew(request, language);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
// Apply formatting
|
|
276
|
+
code = this.format(code, language);
|
|
277
|
+
|
|
278
|
+
const result = {
|
|
279
|
+
id,
|
|
280
|
+
success: true,
|
|
281
|
+
code,
|
|
282
|
+
language,
|
|
283
|
+
filePath: request.filePath,
|
|
284
|
+
metadata: {
|
|
285
|
+
mode: request.mode || GEN_MODE.CREATE,
|
|
286
|
+
duration: Date.now() - startTime,
|
|
287
|
+
linesOfCode: code.split('\n').length
|
|
288
|
+
},
|
|
289
|
+
warnings: []
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
// Store in history
|
|
293
|
+
this.history.push(result);
|
|
294
|
+
|
|
295
|
+
this.emit('generation:complete', { result });
|
|
296
|
+
|
|
297
|
+
return result;
|
|
298
|
+
|
|
299
|
+
} catch (error) {
|
|
300
|
+
const result = {
|
|
301
|
+
id,
|
|
302
|
+
success: false,
|
|
303
|
+
code: '',
|
|
304
|
+
language: request.language || 'unknown',
|
|
305
|
+
filePath: request.filePath,
|
|
306
|
+
metadata: {
|
|
307
|
+
mode: request.mode || GEN_MODE.CREATE,
|
|
308
|
+
duration: Date.now() - startTime,
|
|
309
|
+
error: error.message
|
|
310
|
+
},
|
|
311
|
+
warnings: [error.message]
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
this.emit('generation:error', { id, error: error.message });
|
|
315
|
+
|
|
316
|
+
return result;
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Generate new code
|
|
322
|
+
* @private
|
|
323
|
+
*/
|
|
324
|
+
async generateNew(request, language) {
|
|
325
|
+
const description = request.description.toLowerCase();
|
|
326
|
+
|
|
327
|
+
// Detect what to generate
|
|
328
|
+
if (description.includes('class')) {
|
|
329
|
+
return this.generateClass(request, language);
|
|
330
|
+
} else if (description.includes('function') || description.includes('method')) {
|
|
331
|
+
return this.generateFunction(request, language);
|
|
332
|
+
} else if (description.includes('interface') && language === LANGUAGE.TYPESCRIPT) {
|
|
333
|
+
return this.generateInterface(request, language);
|
|
334
|
+
} else if (description.includes('test')) {
|
|
335
|
+
return this.generateTest(request, language);
|
|
336
|
+
} else if (description.includes('module') || description.includes('file')) {
|
|
337
|
+
return this.generateModule(request, language);
|
|
338
|
+
} else {
|
|
339
|
+
// Default to function
|
|
340
|
+
return this.generateFunction(request, language);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Generate modification code
|
|
346
|
+
* @private
|
|
347
|
+
*/
|
|
348
|
+
async generateModification(request, language) {
|
|
349
|
+
const { context } = request;
|
|
350
|
+
|
|
351
|
+
if (!context || !context.existingCode) {
|
|
352
|
+
throw new Error('Modification requires existing code in context');
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Simple modification: add or update based on description
|
|
356
|
+
let modified = context.existingCode;
|
|
357
|
+
|
|
358
|
+
// Add comments if requested
|
|
359
|
+
if (this.addComments && request.description.includes('document')) {
|
|
360
|
+
modified = this.addDocumentation(modified, language);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
return modified;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Generate extension code
|
|
368
|
+
* @private
|
|
369
|
+
*/
|
|
370
|
+
async generateExtension(request, language) {
|
|
371
|
+
const { context } = request;
|
|
372
|
+
|
|
373
|
+
if (!context || !context.existingCode) {
|
|
374
|
+
throw new Error('Extension requires existing code in context');
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
const existing = context.existingCode;
|
|
378
|
+
const extension = await this.generateNew(request, language);
|
|
379
|
+
|
|
380
|
+
return `${existing}\n\n${extension}`;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
/**
|
|
384
|
+
* Generate refactoring
|
|
385
|
+
* @private
|
|
386
|
+
*/
|
|
387
|
+
async generateRefactoring(request, language) {
|
|
388
|
+
const { context } = request;
|
|
389
|
+
|
|
390
|
+
if (!context || !context.existingCode) {
|
|
391
|
+
throw new Error('Refactoring requires existing code in context');
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// Simple refactoring: improve structure
|
|
395
|
+
let refactored = context.existingCode;
|
|
396
|
+
|
|
397
|
+
// Add proper indentation
|
|
398
|
+
refactored = this.format(refactored, language);
|
|
399
|
+
|
|
400
|
+
return refactored;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/**
|
|
404
|
+
* Generate a class
|
|
405
|
+
* @private
|
|
406
|
+
*/
|
|
407
|
+
generateClass(request, language) {
|
|
408
|
+
const className = this.extractName(request.description, 'Class');
|
|
409
|
+
const template = this.templates[language]?.class || this.templates.javascript.class;
|
|
410
|
+
|
|
411
|
+
let code = template
|
|
412
|
+
.replace(/{name}/g, className)
|
|
413
|
+
.replace(/{description}/g, request.description)
|
|
414
|
+
.replace(/{properties}/g, this.generateProperties(request, language))
|
|
415
|
+
.replace(/{methods}/g, this.generateMethods(request, language));
|
|
416
|
+
|
|
417
|
+
if (language === LANGUAGE.TYPESCRIPT) {
|
|
418
|
+
code = code
|
|
419
|
+
.replace(/{constructorParams}/g, '')
|
|
420
|
+
.replace(/{constructorBody}/g, '');
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return code;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
/**
|
|
427
|
+
* Generate a function
|
|
428
|
+
* @private
|
|
429
|
+
*/
|
|
430
|
+
generateFunction(request, language) {
|
|
431
|
+
const funcName = this.extractName(request.description, 'function');
|
|
432
|
+
const isAsync = request.description.toLowerCase().includes('async');
|
|
433
|
+
|
|
434
|
+
const templateKey = isAsync ? 'asyncFunction' : 'function';
|
|
435
|
+
const template = this.templates[language]?.[templateKey] || this.templates.javascript[templateKey];
|
|
436
|
+
|
|
437
|
+
const params = this.extractParams(request.description);
|
|
438
|
+
|
|
439
|
+
let code = template
|
|
440
|
+
.replace(/{name}/g, funcName)
|
|
441
|
+
.replace(/{description}/g, request.description)
|
|
442
|
+
.replace(/{params}/g, params.map(p => `@param {${p.type}} ${p.name}`).join('\n * '))
|
|
443
|
+
.replace(/{paramNames}/g, params.map(p => p.name).join(', '))
|
|
444
|
+
.replace(/{returns}/g, 'any')
|
|
445
|
+
.replace(/{returnType}/g, ': any')
|
|
446
|
+
.replace(/{body}/g, '// TODO: Implement')
|
|
447
|
+
.replace(/{argDocs}/g, params.map(p => `${p.name}: ${p.type}`).join('\n '))
|
|
448
|
+
.replace(/{returnDoc}/g, 'Result');
|
|
449
|
+
|
|
450
|
+
return code;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
/**
|
|
454
|
+
* Generate an interface (TypeScript)
|
|
455
|
+
* @private
|
|
456
|
+
*/
|
|
457
|
+
generateInterface(request, language) {
|
|
458
|
+
if (language !== LANGUAGE.TYPESCRIPT) {
|
|
459
|
+
throw new Error('Interfaces are only supported in TypeScript');
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const interfaceName = this.extractName(request.description, 'Interface');
|
|
463
|
+
const template = this.templates.typescript.interface;
|
|
464
|
+
|
|
465
|
+
return template
|
|
466
|
+
.replace(/{name}/g, interfaceName)
|
|
467
|
+
.replace(/{description}/g, request.description)
|
|
468
|
+
.replace(/{properties}/g, ' // TODO: Add properties');
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Generate test code
|
|
473
|
+
* @private
|
|
474
|
+
*/
|
|
475
|
+
generateTest(request, language) {
|
|
476
|
+
const template = this.templates.javascript.test;
|
|
477
|
+
const testSubject = this.extractName(request.description, 'Subject');
|
|
478
|
+
const fileName = request.filePath ? path.basename(request.filePath) : 'test.test.js';
|
|
479
|
+
|
|
480
|
+
return template
|
|
481
|
+
.replace(/{filename}/g, fileName)
|
|
482
|
+
.replace(/{testSubject}/g, testSubject)
|
|
483
|
+
.replace(/{imports}/g, testSubject)
|
|
484
|
+
.replace(/{importPath}/g, `./${testSubject.toLowerCase()}`)
|
|
485
|
+
.replace(/{beforeEach}/g, `let instance;\n \n beforeEach(() => {\n instance = new ${testSubject}();\n });`)
|
|
486
|
+
.replace(/{testCases}/g, this.generateTestCases(testSubject));
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
/**
|
|
490
|
+
* Generate module code
|
|
491
|
+
* @private
|
|
492
|
+
*/
|
|
493
|
+
generateModule(request, language) {
|
|
494
|
+
const template = this.templates.javascript.module;
|
|
495
|
+
const fileName = request.filePath ? path.basename(request.filePath) : 'module.js';
|
|
496
|
+
|
|
497
|
+
return template
|
|
498
|
+
.replace(/{filename}/g, fileName)
|
|
499
|
+
.replace(/{description}/g, request.description)
|
|
500
|
+
.replace(/{imports}/g, '')
|
|
501
|
+
.replace(/{body}/g, '// TODO: Implement module')
|
|
502
|
+
.replace(/{exports}/g, '');
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Generate class properties
|
|
507
|
+
* @private
|
|
508
|
+
*/
|
|
509
|
+
generateProperties(request, language) {
|
|
510
|
+
const constraints = request.constraints || {};
|
|
511
|
+
const props = constraints.properties || [];
|
|
512
|
+
|
|
513
|
+
if (props.length === 0) {
|
|
514
|
+
return 'this.options = options;';
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
return props.map(p => `this.${p} = options.${p};`).join('\n ');
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
/**
|
|
521
|
+
* Generate class methods
|
|
522
|
+
* @private
|
|
523
|
+
*/
|
|
524
|
+
generateMethods(request, language) {
|
|
525
|
+
const constraints = request.constraints || {};
|
|
526
|
+
const methods = constraints.methods || [];
|
|
527
|
+
|
|
528
|
+
if (methods.length === 0) {
|
|
529
|
+
return `/**
|
|
530
|
+
* Main method
|
|
531
|
+
*/
|
|
532
|
+
execute() {
|
|
533
|
+
// TODO: Implement
|
|
534
|
+
}`;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
return methods.map(m => `/**
|
|
538
|
+
* ${m.description || m.name}
|
|
539
|
+
*/
|
|
540
|
+
${m.name}() {
|
|
541
|
+
// TODO: Implement
|
|
542
|
+
}`).join('\n \n ');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* Generate test cases
|
|
547
|
+
* @private
|
|
548
|
+
*/
|
|
549
|
+
generateTestCases(subject) {
|
|
550
|
+
return `describe('constructor', () => {
|
|
551
|
+
it('should create instance', () => {
|
|
552
|
+
expect(instance).toBeDefined();
|
|
553
|
+
});
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
describe('methods', () => {
|
|
557
|
+
it('should have required methods', () => {
|
|
558
|
+
// TODO: Add method tests
|
|
559
|
+
});
|
|
560
|
+
});`;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
/**
|
|
564
|
+
* Extract name from description
|
|
565
|
+
* @private
|
|
566
|
+
*/
|
|
567
|
+
extractName(description, defaultName) {
|
|
568
|
+
// Look for quoted names
|
|
569
|
+
const quotedMatch = description.match(/['"]([^'"]+)['"]/);
|
|
570
|
+
if (quotedMatch) return quotedMatch[1];
|
|
571
|
+
|
|
572
|
+
// Look for "called X" or "named X"
|
|
573
|
+
const namedMatch = description.match(/(?:called|named)\s+([a-zA-Z_][a-zA-Z0-9_]*)/i);
|
|
574
|
+
if (namedMatch) return namedMatch[1];
|
|
575
|
+
|
|
576
|
+
// Look for capitalized word after "a" or "an"
|
|
577
|
+
const articleMatch = description.match(/(?:a|an)\s+([A-Z][a-zA-Z0-9_]*)/);
|
|
578
|
+
if (articleMatch) return articleMatch[1];
|
|
579
|
+
|
|
580
|
+
return defaultName;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
/**
|
|
584
|
+
* Extract parameters from description
|
|
585
|
+
* @private
|
|
586
|
+
*/
|
|
587
|
+
extractParams(description) {
|
|
588
|
+
const params = [];
|
|
589
|
+
|
|
590
|
+
// Look for "with parameters X, Y, Z"
|
|
591
|
+
const paramMatch = description.match(/(?:with\s+)?param(?:eter)?s?\s+([^.]+)/i);
|
|
592
|
+
if (paramMatch) {
|
|
593
|
+
const paramList = paramMatch[1].split(/[,\s]+/).filter(p => p && !['and', 'or'].includes(p));
|
|
594
|
+
params.push(...paramList.map(name => ({ name, type: 'any' })));
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// Look for "takes X and Y"
|
|
598
|
+
const takesMatch = description.match(/takes\s+([^.]+)/i);
|
|
599
|
+
if (takesMatch && params.length === 0) {
|
|
600
|
+
const paramList = takesMatch[1].split(/[,\s]+/).filter(p => p && !['and', 'or', 'a', 'an'].includes(p));
|
|
601
|
+
params.push(...paramList.map(name => ({ name, type: 'any' })));
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
return params;
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
/**
|
|
608
|
+
* Detect language from request
|
|
609
|
+
* @private
|
|
610
|
+
*/
|
|
611
|
+
detectLanguage(request) {
|
|
612
|
+
if (request.filePath) {
|
|
613
|
+
const ext = path.extname(request.filePath);
|
|
614
|
+
const langMap = {
|
|
615
|
+
'.js': LANGUAGE.JAVASCRIPT,
|
|
616
|
+
'.ts': LANGUAGE.TYPESCRIPT,
|
|
617
|
+
'.py': LANGUAGE.PYTHON,
|
|
618
|
+
'.json': LANGUAGE.JSON,
|
|
619
|
+
'.md': LANGUAGE.MARKDOWN,
|
|
620
|
+
'.yaml': LANGUAGE.YAML,
|
|
621
|
+
'.yml': LANGUAGE.YAML
|
|
622
|
+
};
|
|
623
|
+
return langMap[ext] || LANGUAGE.JAVASCRIPT;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
// Check description for language hints
|
|
627
|
+
const desc = request.description.toLowerCase();
|
|
628
|
+
if (desc.includes('typescript') || desc.includes('ts')) return LANGUAGE.TYPESCRIPT;
|
|
629
|
+
if (desc.includes('python') || desc.includes('py')) return LANGUAGE.PYTHON;
|
|
630
|
+
|
|
631
|
+
return LANGUAGE.JAVASCRIPT;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Format code
|
|
636
|
+
* @private
|
|
637
|
+
*/
|
|
638
|
+
format(code, language) {
|
|
639
|
+
const indent = this.useTabs ? '\t' : ' '.repeat(this.indentSize);
|
|
640
|
+
|
|
641
|
+
// Normalize line endings
|
|
642
|
+
code = code.replace(/\r\n/g, '\n');
|
|
643
|
+
|
|
644
|
+
// Remove trailing whitespace
|
|
645
|
+
code = code.split('\n').map(line => line.trimEnd()).join('\n');
|
|
646
|
+
|
|
647
|
+
// Ensure single newline at end
|
|
648
|
+
code = code.trimEnd() + '\n';
|
|
649
|
+
|
|
650
|
+
return code;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
/**
|
|
654
|
+
* Add documentation to code
|
|
655
|
+
* @private
|
|
656
|
+
*/
|
|
657
|
+
addDocumentation(code, language) {
|
|
658
|
+
// Simple documentation addition
|
|
659
|
+
if (language === LANGUAGE.JAVASCRIPT || language === LANGUAGE.TYPESCRIPT) {
|
|
660
|
+
if (!code.startsWith('/**') && !code.startsWith('//')) {
|
|
661
|
+
code = '// Auto-documented code\n' + code;
|
|
662
|
+
}
|
|
663
|
+
} else if (language === LANGUAGE.PYTHON) {
|
|
664
|
+
if (!code.startsWith('"""') && !code.startsWith('#')) {
|
|
665
|
+
code = '# Auto-documented code\n' + code;
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
return code;
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
/**
|
|
673
|
+
* Generate unique ID
|
|
674
|
+
* @private
|
|
675
|
+
*/
|
|
676
|
+
generateId() {
|
|
677
|
+
return `gen-${++this.generationCounter}-${Date.now().toString(36)}`;
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
/**
|
|
681
|
+
* Get generation history
|
|
682
|
+
* @param {number} [count] - Number of items to return
|
|
683
|
+
* @returns {GenerationResult[]}
|
|
684
|
+
*/
|
|
685
|
+
getHistory(count) {
|
|
686
|
+
if (count) {
|
|
687
|
+
return this.history.slice(-count);
|
|
688
|
+
}
|
|
689
|
+
return [...this.history];
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
/**
|
|
693
|
+
* Clear history
|
|
694
|
+
*/
|
|
695
|
+
clearHistory() {
|
|
696
|
+
this.history = [];
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
/**
|
|
700
|
+
* Get statistics
|
|
701
|
+
* @returns {Object}
|
|
702
|
+
*/
|
|
703
|
+
getStats() {
|
|
704
|
+
const successful = this.history.filter(r => r.success).length;
|
|
705
|
+
const byLanguage = {};
|
|
706
|
+
const byMode = {};
|
|
707
|
+
let totalLines = 0;
|
|
708
|
+
|
|
709
|
+
for (const result of this.history) {
|
|
710
|
+
byLanguage[result.language] = (byLanguage[result.language] || 0) + 1;
|
|
711
|
+
byMode[result.metadata.mode] = (byMode[result.metadata.mode] || 0) + 1;
|
|
712
|
+
totalLines += result.metadata.linesOfCode || 0;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
return {
|
|
716
|
+
totalGenerations: this.history.length,
|
|
717
|
+
successful,
|
|
718
|
+
failed: this.history.length - successful,
|
|
719
|
+
successRate: this.history.length > 0 ? successful / this.history.length : 0,
|
|
720
|
+
byLanguage,
|
|
721
|
+
byMode,
|
|
722
|
+
totalLinesGenerated: totalLines
|
|
723
|
+
};
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/**
|
|
727
|
+
* Add custom template
|
|
728
|
+
* @param {string} language - Language
|
|
729
|
+
* @param {string} name - Template name
|
|
730
|
+
* @param {string} template - Template content
|
|
731
|
+
*/
|
|
732
|
+
addTemplate(language, name, template) {
|
|
733
|
+
if (!this.templates[language]) {
|
|
734
|
+
this.templates[language] = {};
|
|
735
|
+
}
|
|
736
|
+
this.templates[language][name] = template;
|
|
737
|
+
}
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
/**
|
|
741
|
+
* Create code generator
|
|
742
|
+
* @param {CodeGeneratorOptions} [options={}] - Generator options
|
|
743
|
+
* @returns {CodeGenerator}
|
|
744
|
+
*/
|
|
745
|
+
function createCodeGenerator(options = {}) {
|
|
746
|
+
return new CodeGenerator(options);
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
/**
|
|
750
|
+
* Generate code from description
|
|
751
|
+
* @param {string} description - What to generate
|
|
752
|
+
* @param {Object} [options={}] - Generation options
|
|
753
|
+
* @returns {Promise<GenerationResult>}
|
|
754
|
+
*/
|
|
755
|
+
async function generateCode(description, options = {}) {
|
|
756
|
+
const generator = createCodeGenerator(options);
|
|
757
|
+
return generator.generate({ description, ...options });
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
module.exports = {
|
|
761
|
+
CodeGenerator,
|
|
762
|
+
createCodeGenerator,
|
|
763
|
+
generateCode,
|
|
764
|
+
GEN_MODE,
|
|
765
|
+
LANGUAGE,
|
|
766
|
+
TEMPLATES
|
|
767
|
+
};
|