@sschepis/robodev 1.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.
@@ -0,0 +1,892 @@
1
+ // Tool execution logic
2
+ // Handles the execution of all different types of tools
3
+
4
+ import path from 'path';
5
+ import { fileURLToPath } from 'url';
6
+ import { exec } from 'child_process';
7
+ import util from 'util';
8
+ import { VM } from 'vm2';
9
+ import { consoleStyler } from '../ui/console-styler.mjs';
10
+ import { FileTools } from '../tools/file-tools.mjs';
11
+ import { DesktopAutomationTools } from '../tools/desktop-automation-tools.mjs';
12
+ import { ManifestManager } from '../structured-dev/manifest-manager.mjs';
13
+ import { FlowManager } from '../structured-dev/flow-manager.mjs';
14
+ import { ImplementationPlanner } from '../structured-dev/implementation-planner.mjs';
15
+ import { PlanExecutor } from '../structured-dev/plan-executor.mjs';
16
+ import { CodeValidator } from '../quality/code-validator.mjs';
17
+
18
+ const execPromise = util.promisify(exec);
19
+
20
+ export class ToolExecutor {
21
+ constructor(packageManager, customToolsManager, workspaceManager, aiAssistantClass = null) {
22
+ this.packageManager = packageManager;
23
+ this.customToolsManager = customToolsManager;
24
+ this.workspaceManager = workspaceManager;
25
+ this.aiAssistantClass = aiAssistantClass;
26
+ this.currentTodos = null;
27
+ this.errorHistory = [];
28
+ this.recursionLevel = 0;
29
+
30
+ this.fileTools = new FileTools();
31
+ this.desktopTools = new DesktopAutomationTools();
32
+
33
+ // Initialize structured development managers
34
+ // Use the workspace root if available, otherwise process.cwd()
35
+ const workspaceRoot = workspaceManager?.workspaceRoot || process.cwd();
36
+
37
+ // Re-initialize file tools with the correct workspace root
38
+ this.fileTools = new FileTools(workspaceRoot);
39
+
40
+ this.manifestManager = new ManifestManager(workspaceRoot);
41
+ this.flowManager = new FlowManager(this.manifestManager);
42
+ this.implementationPlanner = new ImplementationPlanner(this.manifestManager);
43
+ this.codeValidator = new CodeValidator(workspaceRoot);
44
+
45
+ // Pass aiAssistantClass if available, otherwise it will fail at runtime if execute_implementation_plan is called
46
+ this.planExecutor = new PlanExecutor(this.manifestManager, this.aiAssistantClass);
47
+
48
+ // Initialize tool registry
49
+ this.toolRegistry = new Map();
50
+ this.registerBuiltInTools();
51
+ }
52
+
53
+ // Register all built-in tools
54
+ registerBuiltInTools() {
55
+ // NPM and JavaScript tools
56
+ this.registerTool('execute_npm_function', this.executeNpmFunction.bind(this));
57
+ this.registerTool('execute_javascript', this.executeJavaScript.bind(this));
58
+
59
+ // Task management tools
60
+ this.registerTool('create_todo_list', this.createTodoList.bind(this));
61
+ this.registerTool('update_todo_status', this.updateTodoStatus.bind(this));
62
+
63
+ // Reasoning and quality tools
64
+ this.registerTool('analyze_and_recover', this.analyzeAndRecover.bind(this));
65
+ this.registerTool('embellish_request', this.embellishRequest.bind(this));
66
+ this.registerTool('evaluate_response_quality', this.evaluateResponseQuality.bind(this));
67
+
68
+ // Utility tools
69
+ this.registerTool('speak_text', this.speakText.bind(this));
70
+ this.registerTool('search_web', this.searchWeb.bind(this));
71
+
72
+ // Custom tool management
73
+ this.registerTool('list_custom_tools', this.listCustomTools.bind(this));
74
+ this.registerTool('remove_custom_tool', this.removeCustomTool.bind(this));
75
+ this.registerTool('export_tools', this.exportTools.bind(this));
76
+
77
+ // System tools
78
+ this.registerTool('manage_workspace', this.manageWorkspace.bind(this));
79
+ this.registerTool('call_ai_assistant', this.callAiAssistant.bind(this));
80
+
81
+ // Structured Development tools
82
+ this.registerTool('init_structured_dev', this.initStructuredDev.bind(this));
83
+ this.registerTool('bootstrap_project', this.bootstrapProject.bind(this));
84
+ this.registerTool('submit_technical_design', this.submitTechnicalDesign.bind(this));
85
+ this.registerTool('approve_design', this.approveDesign.bind(this));
86
+ this.registerTool('lock_interfaces', this.lockInterfaces.bind(this));
87
+ this.registerTool('submit_critique', this.submitCritique.bind(this));
88
+ this.registerTool('read_manifest', this.readManifest.bind(this));
89
+ this.registerTool('visualize_architecture', this.visualizeArchitecture.bind(this));
90
+ this.registerTool('rollback_to_snapshot', this.rollbackToSnapshot.bind(this));
91
+
92
+ // Multi-Agent Implementation Tools
93
+ this.registerTool('create_implementation_plan', this.createImplementationPlan.bind(this));
94
+ this.registerTool('execute_implementation_plan', this.executeImplementationPlan.bind(this));
95
+
96
+ // File system tools
97
+ this.registerTool('read_file', this.fileTools.readFile.bind(this.fileTools));
98
+ this.registerTool('write_file', this.writeFileWithValidation.bind(this));
99
+ this.registerTool('list_files', this.fileTools.listFiles.bind(this.fileTools));
100
+
101
+ // Desktop automation tools
102
+ this.registerTool('mouse_move', this.desktopTools.moveMouse.bind(this.desktopTools));
103
+ this.registerTool('mouse_click', this.desktopTools.clickMouse.bind(this.desktopTools));
104
+ this.registerTool('keyboard_type', this.desktopTools.typeText.bind(this.desktopTools));
105
+ this.registerTool('keyboard_press', this.desktopTools.pressKey.bind(this.desktopTools));
106
+ this.registerTool('screen_capture', this.desktopTools.captureScreen.bind(this.desktopTools));
107
+ }
108
+
109
+ // Register a new tool
110
+ registerTool(name, handler) {
111
+ this.toolRegistry.set(name, handler);
112
+ }
113
+
114
+ // Execute a tool call and return the result
115
+ async executeTool(toolCall) {
116
+ const functionName = toolCall.function.name;
117
+ let toolResultText = '';
118
+
119
+ try {
120
+ consoleStyler.log('tools', `🔧 Starting ${functionName}...`);
121
+ const args = JSON.parse(toolCall.function.arguments);
122
+
123
+ // Log tool arguments for debugging (truncated for readability)
124
+ const argsSummary = Object.keys(args).length > 0
125
+ ? Object.keys(args).map(key => `${key}: ${typeof args[key]}`).join(', ')
126
+ : 'no arguments';
127
+ consoleStyler.log('tools', ` Parameters: ${argsSummary}`, { indent: true });
128
+
129
+ // Check if tool is registered
130
+ if (this.toolRegistry.has(functionName)) {
131
+ const handler = this.toolRegistry.get(functionName);
132
+ toolResultText = await handler(args);
133
+ }
134
+ // Check if it's a custom tool
135
+ else if (this.customToolsManager.hasCustomTool(functionName)) {
136
+ toolResultText = await this.customToolsManager.executeCustomTool(functionName, args);
137
+ }
138
+ else {
139
+ throw new Error(`Unknown tool: ${functionName}`);
140
+ }
141
+
142
+ return {
143
+ role: 'tool',
144
+ tool_call_id: toolCall.id,
145
+ name: functionName,
146
+ content: toolResultText,
147
+ };
148
+
149
+ } catch (error) {
150
+ consoleStyler.log('error', `Tool Error: ${error.message}`);
151
+ return {
152
+ role: 'tool',
153
+ tool_call_id: toolCall.id,
154
+ name: functionName,
155
+ content: `Error: ${error.message}`,
156
+ };
157
+ }
158
+ }
159
+
160
+ // Execute npm function tool
161
+ async executeNpmFunction(args) {
162
+ const { packageName, functionName: funcToCall, args: funcArgs } = args;
163
+
164
+ consoleStyler.log('packages', `Loading package: ${packageName}`);
165
+
166
+ let module;
167
+ try {
168
+ module = await this.packageManager.importPackage(packageName);
169
+ consoleStyler.log('packages', `✓ Package ${packageName} loaded successfully`);
170
+ } catch (e) {
171
+ if (e.code === 'ERR_MODULE_NOT_FOUND') {
172
+ consoleStyler.log('packages', `Package ${packageName} not found, installing...`);
173
+ try {
174
+ await this.packageManager.installPackage(packageName);
175
+ consoleStyler.log('packages', `✓ Package ${packageName} installed successfully`);
176
+ module = await this.packageManager.importPackage(packageName);
177
+ } catch (installError) {
178
+ if (installError.message.startsWith('COMPATIBILITY_SKIP:')) {
179
+ consoleStyler.log('error', `Package ${packageName} incompatible with current Node.js version`);
180
+ throw new Error(`Cannot use ${packageName} due to Node.js compatibility issues. Please use built-in alternatives.`);
181
+ }
182
+ throw installError;
183
+ }
184
+ } else {
185
+ throw e;
186
+ }
187
+ }
188
+
189
+ const func = funcToCall === 'default' ? module.default : module[funcToCall];
190
+ if (typeof func !== 'function') {
191
+ throw new Error(`'${funcToCall}' is not a function in package '${packageName}'.`);
192
+ }
193
+
194
+ const toolResult = await func(...funcArgs);
195
+ return toolResult === undefined ? "Function executed successfully" : (typeof toolResult === 'string' ? toolResult : JSON.stringify(toolResult, null, 2));
196
+ }
197
+
198
+ // Execute JavaScript code tool
199
+ async executeJavaScript(args) {
200
+ const {
201
+ code: codeToRun,
202
+ npm_packages: packagesToInstall,
203
+ save_as_tool = false,
204
+ tool_name,
205
+ tool_description,
206
+ tool_category = 'utility'
207
+ } = args;
208
+
209
+ if (packagesToInstall && packagesToInstall.length > 0) {
210
+ consoleStyler.log('packages', `Installing ${packagesToInstall.length} package(s): ${packagesToInstall.join(', ')}`);
211
+ const results = await this.packageManager.installPackages(packagesToInstall);
212
+ this.packageManager.logInstallationResults(results);
213
+ }
214
+
215
+ consoleStyler.log('working', 'Setting up JavaScript execution environment...');
216
+ // Set up require function for CommonJS modules
217
+ const require = await this.packageManager.setupCommonJSRequire();
218
+
219
+ consoleStyler.log('working', 'Executing JavaScript code...');
220
+
221
+ // Use vm2 for sandboxed execution
222
+ const vm = new VM({
223
+ timeout: 30000, // 30s timeout
224
+ sandbox: {
225
+ console: console,
226
+ require: require,
227
+ process: {
228
+ env: { ...process.env },
229
+ cwd: process.cwd
230
+ },
231
+ Buffer: Buffer,
232
+ setTimeout: setTimeout,
233
+ clearTimeout: clearTimeout,
234
+ setInterval: setInterval,
235
+ clearInterval: clearInterval
236
+ }
237
+ });
238
+
239
+ // Execute the code in the sandbox
240
+ let toolResult;
241
+ try {
242
+ // Fix: vm2 does not support dynamic import(), so we replace it with require()
243
+ // This assumes the module is available via CommonJS require (which we inject)
244
+ const processedCode = codeToRun.replace(/import\s*\(\s*(['"`].*?['"`])\s*\)/g, 'Promise.resolve(require($1))');
245
+
246
+ // Always wrap in an async IIFE so bare `return` statements work
247
+ // and async/await is supported naturally.
248
+ const wrappedCode = `(async () => { ${processedCode} })()`;
249
+ toolResult = await vm.run(wrappedCode);
250
+
251
+ // If result is a promise (from async code), await it
252
+ if (toolResult && typeof toolResult.then === 'function') {
253
+ toolResult = await toolResult;
254
+ }
255
+ } catch (err) {
256
+ throw new Error(`Execution error: ${err.message}`);
257
+ }
258
+
259
+ let resultText = toolResult === undefined ? "Code executed successfully" : JSON.stringify(toolResult, null, 2);
260
+
261
+ consoleStyler.log('working', '✓ JavaScript execution completed');
262
+
263
+ // Handle tool creation if requested
264
+ if (save_as_tool) {
265
+ if (!tool_name || !tool_description) {
266
+ resultText += '\n\n✗ Tool creation failed: tool_name and tool_description are required when save_as_tool is true';
267
+ } else {
268
+ consoleStyler.log('tools', `Creating tool: ${tool_name}`);
269
+ const toolCreationResult = await this.processIntoTool(
270
+ codeToRun,
271
+ tool_name,
272
+ tool_description,
273
+ tool_category,
274
+ packagesToInstall || []
275
+ );
276
+ resultText += '\n\n' + toolCreationResult;
277
+ }
278
+ }
279
+
280
+ return resultText;
281
+ }
282
+
283
+ // Create todo list tool
284
+ async createTodoList(args) {
285
+ const { task_description, todos } = args;
286
+
287
+ this.currentTodos = {
288
+ task: task_description,
289
+ items: todos,
290
+ created_at: new Date().toISOString()
291
+ };
292
+
293
+ // Use the enhanced todo list display
294
+ const todoDisplay = consoleStyler.formatTodoList(this.currentTodos);
295
+ console.log(todoDisplay);
296
+
297
+ return `Todo list created with ${todos.length} steps`;
298
+ }
299
+
300
+ // Update todo status tool
301
+ async updateTodoStatus(args) {
302
+ const { step_index, status, result } = args;
303
+
304
+ if (this.currentTodos && this.currentTodos.items[step_index]) {
305
+ this.currentTodos.items[step_index].status = status;
306
+ if (result) {
307
+ this.currentTodos.items[step_index].result = result;
308
+ }
309
+
310
+ const todo = this.currentTodos.items[step_index];
311
+ const statusText = status === 'completed' ? 'completed' : status === 'in_progress' ? 'in progress' : 'pending';
312
+ consoleStyler.log('todo', `Step ${step_index + 1} ${statusText}: ${todo.step}${result ? ` - ${result}` : ''}`);
313
+
314
+ // Show current task being worked on
315
+ if (status === 'in_progress') {
316
+ consoleStyler.log('working', `Currently working on: ${todo.step}`);
317
+ }
318
+
319
+ return `Step ${step_index + 1} status updated to ${status}`;
320
+ } else {
321
+ return `Error: Invalid step index or no active todo list`;
322
+ }
323
+ }
324
+
325
+ // Analyze and recover tool
326
+ async analyzeAndRecover(args) {
327
+ const { error_message, failed_approach, recovery_strategy, alternative_code } = args;
328
+
329
+ this.errorHistory.push({
330
+ error: error_message,
331
+ approach: failed_approach,
332
+ strategy: recovery_strategy,
333
+ timestamp: new Date().toISOString()
334
+ });
335
+
336
+ consoleStyler.log('recovery', `🔍 Analyzing error: ${error_message}`, { box: true });
337
+ consoleStyler.log('recovery', `Failed approach: ${failed_approach}`);
338
+ consoleStyler.log('recovery', `Attempting recovery strategy: ${recovery_strategy}`);
339
+
340
+ let recoveryResult = "";
341
+
342
+ switch (recovery_strategy) {
343
+ case 'retry_with_alternative':
344
+ if (alternative_code) {
345
+ try {
346
+ await this.packageManager.setupCommonJSRequire();
347
+ const result = await Promise.resolve(eval(alternative_code));
348
+ recoveryResult = result === undefined ? "Recovery successful - code executed" : `Recovery successful: ${JSON.stringify(result)}`;
349
+ } catch (e) {
350
+ recoveryResult = `Recovery failed: ${e.message}`;
351
+ consoleStyler.log('recovery', `✗ Alternative also failed: ${e.message}`);
352
+ }
353
+ } else {
354
+ recoveryResult = "No alternative code provided";
355
+ }
356
+ break;
357
+
358
+ case 'simplify_approach':
359
+ recoveryResult = "Breaking down into simpler steps";
360
+ break;
361
+
362
+ case 'change_method':
363
+ recoveryResult = "Switching to different method";
364
+ break;
365
+
366
+ case 'install_dependencies':
367
+ recoveryResult = "Installing missing dependencies";
368
+ break;
369
+
370
+ case 'fix_syntax':
371
+ recoveryResult = "Fixing syntax errors";
372
+ break;
373
+
374
+ default:
375
+ recoveryResult = "Unknown recovery strategy";
376
+ }
377
+
378
+ return recoveryResult;
379
+ }
380
+
381
+ // Embellish request tool
382
+ async embellishRequest(args) {
383
+ const { original_request, embellished_request, technical_requirements, reasoning_effort, reasoning_justification } = args;
384
+
385
+ consoleStyler.log('reasoning', `🧠 Request embellishment complete:`);
386
+ consoleStyler.log('reasoning', ` Original: ${original_request.substring(0, 60)}...`, { indent: true });
387
+ consoleStyler.log('reasoning', ` Enhanced: ${embellished_request.substring(0, 60)}...`, { indent: true });
388
+ consoleStyler.log('reasoning', ` Technical requirements: ${technical_requirements ? technical_requirements.length : 0}`, { indent: true });
389
+ consoleStyler.log('reasoning', ` Reasoning effort: ${reasoning_effort}`, { indent: true });
390
+
391
+ return `Request embellished with ${technical_requirements ? technical_requirements.length : 0} technical requirements. Reasoning: ${reasoning_effort}`;
392
+ }
393
+
394
+ // Evaluate response quality tool
395
+ async evaluateResponseQuality(args) {
396
+ const { original_query, ai_response, quality_rating = 0, evaluation_reasoning = "No reasoning", remedy_suggestion = "" } = args;
397
+
398
+ if (quality_rating < 4) {
399
+ consoleStyler.log('quality', `Poor quality detected (${quality_rating}/10)`, { box: true });
400
+ if (remedy_suggestion) {
401
+ consoleStyler.log('quality', `Remedy: ${remedy_suggestion}`);
402
+ }
403
+
404
+ return `Quality rating ${quality_rating}/10 - retry needed with remedy: ${remedy_suggestion}`;
405
+ } else {
406
+ consoleStyler.log('quality', `Quality rating ${quality_rating}/10 - response approved`);
407
+ return `Quality rating ${quality_rating}/10 - response approved`;
408
+ }
409
+ }
410
+
411
+ // Speak text tool (Text-to-Speech)
412
+ async speakText(args) {
413
+ const {
414
+ text,
415
+ voice_id = 'tQ4MEZFJOzsahSEEZtHK',
416
+ stability = 0.5,
417
+ similarity_boost = 0.75
418
+ } = args;
419
+
420
+ const spinner = consoleStyler.startSpinner('tts', 'Converting text to speech...');
421
+
422
+ try {
423
+ // Clean the text (remove markdown formatting)
424
+ const cleanText = text
425
+ .replace(/```[\s\S]*?```/g, '') // Remove code blocks
426
+ .replace(/`[^`]+`/g, '') // Remove inline code
427
+ .replace(/\*\*([^*]+)\*\*/g, '$1') // Remove bold markdown
428
+ .replace(/\*([^*]+)\*/g, '$1') // Remove italic markdown
429
+ .replace(/#{1,6}\s+/g, '') // Remove headers
430
+ .replace(/\|[^|\n]*\|/g, '') // Remove table rows
431
+ .replace(/\n+/g, ' ') // Replace newlines with spaces
432
+ .trim();
433
+
434
+ // Get ElevenLabs API key from environment
435
+ const apiKey = process.env.ELEVENLABS_API_KEY;
436
+ if (!apiKey) {
437
+ throw new Error('ELEVENLABS_API_KEY environment variable not set');
438
+ }
439
+
440
+ // Call ElevenLabs API
441
+ const response = await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${voice_id}`, {
442
+ method: 'POST',
443
+ headers: {
444
+ 'Accept': 'audio/mpeg',
445
+ 'Content-Type': 'application/json',
446
+ 'xi-api-key': apiKey
447
+ },
448
+ body: JSON.stringify({
449
+ text: cleanText,
450
+ model_id: 'eleven_monolingual_v1',
451
+ voice_settings: {
452
+ stability: stability,
453
+ similarity_boost: similarity_boost
454
+ }
455
+ })
456
+ });
457
+
458
+ if (!response.ok) {
459
+ throw new Error(`ElevenLabs API error: ${response.status} ${response.statusText}`);
460
+ }
461
+
462
+ // Save audio file
463
+ const audioBuffer = await response.arrayBuffer();
464
+ const audioFilePath = path.join(process.cwd(), 'temp_speech.mp3');
465
+
466
+ // Write audio file
467
+ const fs = await import('fs');
468
+ fs.writeFileSync(audioFilePath, Buffer.from(audioBuffer));
469
+
470
+ // Play audio (platform-specific)
471
+ const os = await import('os');
472
+ const platform = os.platform();
473
+
474
+ let playCommand;
475
+ if (platform === 'darwin') { // macOS
476
+ playCommand = `afplay "${audioFilePath}"`;
477
+ } else if (platform === 'linux') {
478
+ playCommand = `mpg123 "${audioFilePath}" || aplay "${audioFilePath}" || paplay "${audioFilePath}"`;
479
+ } else if (platform === 'win32') {
480
+ playCommand = `powershell -c "(New-Object Media.SoundPlayer '${audioFilePath}').PlaySync()"`;
481
+ } else {
482
+ throw new Error(`Unsupported platform: ${platform}`);
483
+ }
484
+
485
+ // Execute play command
486
+ await execPromise(playCommand);
487
+
488
+ // Clean up temp file
489
+ setTimeout(() => {
490
+ try {
491
+ fs.unlinkSync(audioFilePath);
492
+ } catch (e) {
493
+ // Ignore cleanup errors
494
+ }
495
+ }, 1000);
496
+
497
+ consoleStyler.succeedSpinner('tts', 'Speech playback completed');
498
+ return `Text converted to speech and played successfully. Used voice ${voice_id} with ${cleanText.length} characters.`;
499
+
500
+ } catch (error) {
501
+ consoleStyler.failSpinner('tts', `Error: ${error.message}`);
502
+ return `Error converting text to speech: ${error.message}`;
503
+ }
504
+ }
505
+
506
+ // List custom tools
507
+ async listCustomTools(args) {
508
+ const { category, show_usage = false } = args;
509
+ return await this.customToolsManager.listCustomTools(category, show_usage);
510
+ }
511
+
512
+ // Remove custom tool
513
+ async removeCustomTool(args) {
514
+ const { tool_name } = args;
515
+ const result = await this.customToolsManager.removeCustomTool(tool_name);
516
+ return result.message;
517
+ }
518
+
519
+ // Export tools
520
+ async exportTools(args) {
521
+ const { output_file, tools: toolsToExport } = args;
522
+ const result = await this.customToolsManager.exportTools(output_file, toolsToExport);
523
+ return result.message;
524
+ }
525
+
526
+ // Manage workspace
527
+ async manageWorkspace(args) {
528
+ if (!this.workspaceManager) {
529
+ return "Workspace manager not available";
530
+ }
531
+
532
+ return await this.workspaceManager.manageWorkspace(args);
533
+ }
534
+
535
+ // Structured Development Tools Handlers
536
+
537
+ async initStructuredDev(args) {
538
+ const targetDir = args.target_dir || null;
539
+ consoleStyler.log('system', `Initializing Structured Development${targetDir ? ` at ${targetDir}` : ''}...`);
540
+ return await this.flowManager.initStructuredDev(targetDir);
541
+ }
542
+
543
+ async bootstrapProject(args) {
544
+ const targetDir = args.target_dir || this.manifestManager.workingDir;
545
+ consoleStyler.log('system', `Bootstrapping project at ${targetDir}...`);
546
+
547
+ // Create a ManifestManager for the target directory
548
+ const bootstrapManifest = new ManifestManager(targetDir);
549
+ const bootstrapFlow = new FlowManager(bootstrapManifest);
550
+ return await bootstrapFlow.initStructuredDev(targetDir);
551
+ }
552
+
553
+ async submitTechnicalDesign(args) {
554
+ const { feature_id, design_doc } = args;
555
+ consoleStyler.log('system', `Submitting technical design for ${feature_id}...`);
556
+ return await this.flowManager.submitTechnicalDesign(feature_id, design_doc);
557
+ }
558
+
559
+ async approveDesign(args) {
560
+ const { feature_id, feedback } = args;
561
+ consoleStyler.log('system', `Approving design for ${feature_id}...`);
562
+ return await this.flowManager.approveDesign(feature_id, feedback);
563
+ }
564
+
565
+ async lockInterfaces(args) {
566
+ const { feature_id, interface_definitions } = args;
567
+ consoleStyler.log('system', `Locking interfaces for ${feature_id}...`);
568
+ return await this.flowManager.lockInterfaces(feature_id, interface_definitions);
569
+ }
570
+
571
+ async submitCritique(args) {
572
+ const { feature_id, critique } = args;
573
+ consoleStyler.log('system', `Submitting critique for ${feature_id}...`);
574
+ return await this.flowManager.submitCritique(feature_id, critique);
575
+ }
576
+
577
+ async readManifest(args) {
578
+ consoleStyler.log('system', 'Reading manifest...');
579
+ const content = await this.flowManager.readManifest();
580
+ if (!content) return "No manifest found.";
581
+ return content;
582
+ }
583
+
584
+ async visualizeArchitecture(args) {
585
+ consoleStyler.log('system', 'Generating architecture visualization...');
586
+ return await this.flowManager.visualizeArchitecture();
587
+ }
588
+
589
+ async rollbackToSnapshot(args) {
590
+ const { snapshot_id } = args;
591
+ if (!snapshot_id) {
592
+ consoleStyler.log('system', 'Listing available snapshots...');
593
+ const snapshots = await this.manifestManager.listSnapshots();
594
+ return `Available Snapshots:\n${snapshots.join('\n')}\n\nUse this tool again with a snapshot_id to restore one.`;
595
+ }
596
+ consoleStyler.log('system', `Rolling back to snapshot: ${snapshot_id}...`);
597
+ return await this.manifestManager.restoreSnapshot(snapshot_id);
598
+ }
599
+
600
+ // Create implementation plan
601
+ async createImplementationPlan(args) {
602
+ const { output_file, num_developers = 3 } = args;
603
+ consoleStyler.log('system', `Generating multi-agent implementation plan for ${num_developers} developers...`);
604
+ const result = await this.implementationPlanner.createExecutionPlan(output_file, num_developers);
605
+
606
+ if (result.success) {
607
+ // Display summary
608
+ consoleStyler.log('system', `✓ Plan created at ${result.plan_path}`, { box: true });
609
+ consoleStyler.log('system', `Stages: ${result.plan.stages.length}`);
610
+ result.plan.stages.forEach(stage => {
611
+ consoleStyler.log('system', ` Stage ${stage.id}: ${stage.tasks.join(', ')}`, { indent: true });
612
+ });
613
+ return result.message;
614
+ } else {
615
+ return `Failed to create plan: ${result.message}`;
616
+ }
617
+ }
618
+
619
+ // Execute implementation plan
620
+ async executeImplementationPlan(args) {
621
+ const { plan_file = 'implementation-plan.json' } = args;
622
+
623
+ // Ensure AI Assistant class is available
624
+ if (!this.planExecutor.AiAssistant) {
625
+ // Fallback: If not passed in constructor, try to use the one from recursive calls
626
+ // This is tricky. Ideally it should be passed in.
627
+ return "Error: AI Assistant class not available for agent execution. This tool requires the system to be initialized with self-replication capabilities.";
628
+ }
629
+
630
+ const planPath = path.resolve(this.manifestManager.workingDir, plan_file);
631
+ consoleStyler.log('system', `Executing implementation plan from ${planPath}...`);
632
+
633
+ const result = await this.planExecutor.executePlan(planPath);
634
+
635
+ if (result.success) {
636
+ return `Execution completed successfully. ${result.message}`;
637
+ } else {
638
+ return `Execution failed: ${result.message}`;
639
+ }
640
+ }
641
+
642
+ // Call AI assistant recursively
643
+ async callAiAssistant(args) {
644
+ const { query, context, recursion_level = 0 } = args;
645
+
646
+ // Check recursion depth
647
+ const currentLevel = Math.max(this.recursionLevel, recursion_level);
648
+ if (currentLevel >= 3) {
649
+ consoleStyler.log('error', 'Maximum recursion depth reached (3 levels)', { box: true });
650
+ return "Error: Maximum recursion depth (3 levels) reached. Cannot make recursive AI calls.";
651
+ }
652
+
653
+ if (!this.aiAssistantClass) {
654
+ consoleStyler.log('error', 'AI Assistant class not available for recursive calls');
655
+ return "Error: AI Assistant class not available for recursive calls.";
656
+ }
657
+
658
+ try {
659
+ consoleStyler.log('ai', `🔄 Initiating recursive AI call (level ${currentLevel + 1})`, { box: true });
660
+ consoleStyler.log('ai', ` Query: ${query.substring(0, 100)}...`, { indent: true });
661
+ consoleStyler.log('ai', ` Context: ${context}`, { indent: true });
662
+
663
+ // Create new AI assistant instance for recursive call
664
+ const recursiveAssistant = new this.aiAssistantClass(process.cwd());
665
+ await recursiveAssistant.initializeCustomTools();
666
+
667
+ // Set recursion level in the tool executor
668
+ recursiveAssistant.toolExecutor.recursionLevel = currentLevel + 1;
669
+
670
+ // Add context to the query
671
+ const contextualQuery = `RECURSIVE CALL (Level ${currentLevel + 1}): ${context}\n\nQuery: ${query}`;
672
+
673
+ // Execute the recursive call
674
+ const response = await recursiveAssistant.run(contextualQuery);
675
+
676
+ consoleStyler.log('ai', `✅ Recursive AI call completed (level ${currentLevel + 1})`);
677
+ consoleStyler.log('ai', ` Response length: ${response.length} characters`, { indent: true });
678
+
679
+ return `Recursive AI Assistant Response:\n\nContext: ${context}\nQuery: ${query}\n\nResponse: ${response}`;
680
+
681
+ } catch (error) {
682
+ consoleStyler.log('error', `Recursive AI call failed: ${error.message}`, { box: true });
683
+ return `Error in recursive AI call: ${error.message}`;
684
+ }
685
+ }
686
+
687
+ // Write file with validation wrapper
688
+ async writeFileWithValidation(args) {
689
+ const { path, content } = args;
690
+
691
+ // 1. Perform the write
692
+ const writeResult = await this.fileTools.writeFile(args);
693
+
694
+ // If write failed, return immediately
695
+ if (writeResult.startsWith('Error:')) {
696
+ return writeResult;
697
+ }
698
+
699
+ // 2. Perform validation
700
+ // We only validate if we have a validator and it's a code file
701
+ if (this.codeValidator) {
702
+ const validationErrors = await this.codeValidator.validateFile(path);
703
+
704
+ if (validationErrors) {
705
+ return `${writeResult}\n\n⚠️ Validation Errors Detected:\n${validationErrors}\n\nPlease review and fix these errors in your next step.`;
706
+ } else {
707
+ return `${writeResult}\n✓ No validation errors detected.`;
708
+ }
709
+ }
710
+
711
+ return writeResult;
712
+ }
713
+
714
+ // Process code into a tool (helper method)
715
+ async processIntoTool(originalCode, toolName, toolDescription, category, npmPackages = []) {
716
+ try {
717
+ consoleStyler.log('tools', `Processing code into tool: ${toolName}`);
718
+ // This would need the AI generation logic moved here or injected
719
+ // For now, return a placeholder
720
+ return `✗ Tool creation not yet implemented in refactored version`;
721
+ } catch (error) {
722
+ consoleStyler.log('error', `Tool processing failed: ${error.message}`);
723
+ return `✗ Tool creation failed: ${error.message}`;
724
+ }
725
+ }
726
+
727
+ // Get current todos
728
+ getCurrentTodos() {
729
+ return this.currentTodos;
730
+ }
731
+
732
+ // Get error history
733
+ getErrorHistory() {
734
+ return this.errorHistory;
735
+ }
736
+
737
+ // Search the web using Serper.dev API
738
+ async searchWeb(args) {
739
+ const {
740
+ query,
741
+ type = 'search',
742
+ num = 10,
743
+ location,
744
+ lang = 'en',
745
+ safe = 'active'
746
+ } = args;
747
+
748
+ // Your API key
749
+ const apiKey = process.env.SERPER_API_KEY || '7edbc239394bb9b75ce5543fb6987ba4256b3269';
750
+
751
+ consoleStyler.log('working', `🔍 Searching web for: "${query}"`);
752
+ consoleStyler.log('working', ` Search type: ${type}, Results: ${num}`, { indent: true });
753
+
754
+ try {
755
+ const searchParams = {
756
+ q: query,
757
+ type: type,
758
+ num: num,
759
+ lang: lang,
760
+ safe: safe
761
+ };
762
+
763
+ // Add location if specified
764
+ if (location) {
765
+ searchParams.location = location;
766
+ consoleStyler.log('working', ` Location: ${location}`, { indent: true });
767
+ }
768
+
769
+ const response = await fetch('https://google.serper.dev/search', {
770
+ method: 'POST',
771
+ headers: {
772
+ 'X-API-KEY': apiKey,
773
+ 'Content-Type': 'application/json'
774
+ },
775
+ body: JSON.stringify(searchParams)
776
+ });
777
+
778
+ if (!response.ok) {
779
+ throw new Error(`Serper API error: ${response.status} ${response.statusText}`);
780
+ }
781
+
782
+ const data = await response.json();
783
+
784
+ consoleStyler.log('tools', `✓ Web search completed - found ${data.organic?.length || 0} results`);
785
+
786
+ // Format the results
787
+ let formattedResults = `# Web Search Results for: "${query}"\n\n`;
788
+
789
+ // Add search summary if available
790
+ if (data.searchParameters) {
791
+ formattedResults += `**Search Parameters:**\n`;
792
+ formattedResults += `- Query: ${data.searchParameters.q}\n`;
793
+ formattedResults += `- Type: ${data.searchParameters.type || 'search'}\n`;
794
+ formattedResults += `- Results: ${data.searchParameters.num || 10}\n`;
795
+ if (data.searchParameters.location) {
796
+ formattedResults += `- Location: ${data.searchParameters.location}\n`;
797
+ }
798
+ formattedResults += `\n`;
799
+ }
800
+
801
+ // Add answer box if available
802
+ if (data.answerBox) {
803
+ formattedResults += `## Quick Answer\n`;
804
+ formattedResults += `**${data.answerBox.title || 'Answer'}**\n`;
805
+ formattedResults += `${data.answerBox.answer || data.answerBox.snippet}\n`;
806
+ if (data.answerBox.source) {
807
+ formattedResults += `*Source: ${data.answerBox.source}*\n`;
808
+ }
809
+ formattedResults += `\n`;
810
+ }
811
+
812
+ // Add knowledge graph if available
813
+ if (data.knowledgeGraph) {
814
+ formattedResults += `## Knowledge Graph\n`;
815
+ formattedResults += `**${data.knowledgeGraph.title}**\n`;
816
+ if (data.knowledgeGraph.description) {
817
+ formattedResults += `${data.knowledgeGraph.description}\n`;
818
+ }
819
+ if (data.knowledgeGraph.source) {
820
+ formattedResults += `*Source: ${data.knowledgeGraph.source.name}*\n`;
821
+ }
822
+ formattedResults += `\n`;
823
+ }
824
+
825
+ // Add organic results
826
+ if (data.organic && data.organic.length > 0) {
827
+ formattedResults += `## Search Results\n\n`;
828
+
829
+ data.organic.forEach((result, index) => {
830
+ formattedResults += `### ${index + 1}. ${result.title}\n`;
831
+ formattedResults += `**URL:** ${result.link}\n`;
832
+ if (result.snippet) {
833
+ formattedResults += `**Description:** ${result.snippet}\n`;
834
+ }
835
+ if (result.date) {
836
+ formattedResults += `**Date:** ${result.date}\n`;
837
+ }
838
+ formattedResults += `\n`;
839
+ });
840
+ }
841
+
842
+ // Add news results if available
843
+ if (data.news && data.news.length > 0) {
844
+ formattedResults += `## Related News\n\n`;
845
+
846
+ data.news.forEach((news, index) => {
847
+ formattedResults += `### ${index + 1}. ${news.title}\n`;
848
+ formattedResults += `**URL:** ${news.link}\n`;
849
+ if (news.snippet) {
850
+ formattedResults += `**Description:** ${news.snippet}\n`;
851
+ }
852
+ if (news.date) {
853
+ formattedResults += `**Date:** ${news.date}\n`;
854
+ }
855
+ if (news.source) {
856
+ formattedResults += `**Source:** ${news.source}\n`;
857
+ }
858
+ formattedResults += `\n`;
859
+ });
860
+ }
861
+
862
+ // Add people also ask if available
863
+ if (data.peopleAlsoAsk && data.peopleAlsoAsk.length > 0) {
864
+ formattedResults += `## People Also Ask\n\n`;
865
+
866
+ data.peopleAlsoAsk.forEach((question, index) => {
867
+ formattedResults += `${index + 1}. ${question.question}\n`;
868
+ if (question.snippet) {
869
+ formattedResults += ` ${question.snippet}\n`;
870
+ }
871
+ formattedResults += `\n`;
872
+ });
873
+ }
874
+
875
+ // Add related searches if available
876
+ if (data.relatedSearches && data.relatedSearches.length > 0) {
877
+ formattedResults += `## Related Searches\n\n`;
878
+
879
+ data.relatedSearches.forEach((search, index) => {
880
+ formattedResults += `${index + 1}. ${search.query}\n`;
881
+ });
882
+ formattedResults += `\n`;
883
+ }
884
+
885
+ return formattedResults;
886
+
887
+ } catch (error) {
888
+ consoleStyler.log('error', `Web search failed: ${error.message}`, { box: true });
889
+ return `Error performing web search: ${error.message}`;
890
+ }
891
+ }
892
+ }