@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.
- package/ai.mjs +8 -0
- package/package.json +48 -0
- package/src/cli/cli-interface.mjs +271 -0
- package/src/config.mjs +64 -0
- package/src/core/ai-assistant.mjs +540 -0
- package/src/core/ai-provider.mjs +579 -0
- package/src/core/history-manager.mjs +330 -0
- package/src/core/system-prompt.mjs +182 -0
- package/src/custom-tools/custom-tools-manager.mjs +310 -0
- package/src/execution/tool-executor.mjs +892 -0
- package/src/lib/README.md +114 -0
- package/src/lib/adapters/console-status-adapter.mjs +48 -0
- package/src/lib/adapters/network-llm-adapter.mjs +37 -0
- package/src/lib/index.mjs +101 -0
- package/src/lib/interfaces.d.ts +98 -0
- package/src/main.mjs +61 -0
- package/src/package/package-manager.mjs +223 -0
- package/src/quality/code-validator.mjs +126 -0
- package/src/quality/quality-evaluator.mjs +248 -0
- package/src/reasoning/reasoning-system.mjs +258 -0
- package/src/structured-dev/flow-manager.mjs +321 -0
- package/src/structured-dev/implementation-planner.mjs +223 -0
- package/src/structured-dev/manifest-manager.mjs +423 -0
- package/src/structured-dev/plan-executor.mjs +113 -0
- package/src/structured-dev/project-bootstrapper.mjs +523 -0
- package/src/tools/desktop-automation-tools.mjs +172 -0
- package/src/tools/file-tools.mjs +141 -0
- package/src/tools/tool-definitions.mjs +872 -0
- package/src/ui/console-styler.mjs +503 -0
- package/src/workspace/workspace-manager.mjs +215 -0
- package/themes.json +66 -0
|
@@ -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
|
+
}
|