xibecode 0.6.2 → 0.7.3
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.md +11 -2
- package/dist/commands/chat.d.ts +1 -0
- package/dist/commands/chat.d.ts.map +1 -1
- package/dist/commands/chat.js +2 -1
- package/dist/commands/chat.js.map +1 -1
- package/dist/commands/config.d.ts +2 -0
- package/dist/commands/config.d.ts.map +1 -1
- package/dist/commands/config.js +15 -0
- package/dist/commands/config.js.map +1 -1
- package/dist/commands/run-pr.d.ts +3 -0
- package/dist/commands/run-pr.d.ts.map +1 -1
- package/dist/commands/run-pr.js +470 -69
- package/dist/commands/run-pr.js.map +1 -1
- package/dist/commands/run.d.ts +3 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +36 -5
- package/dist/commands/run.js.map +1 -1
- package/dist/core/agent.d.ts +27 -0
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +90 -3
- package/dist/core/agent.js.map +1 -1
- package/dist/core/context-pruner.d.ts +19 -0
- package/dist/core/context-pruner.d.ts.map +1 -0
- package/dist/core/context-pruner.js +103 -0
- package/dist/core/context-pruner.js.map +1 -0
- package/dist/core/modes.d.ts.map +1 -1
- package/dist/core/modes.js +1 -0
- package/dist/core/modes.js.map +1 -1
- package/dist/core/session-memory.d.ts +45 -0
- package/dist/core/session-memory.d.ts.map +1 -0
- package/dist/core/session-memory.js +103 -0
- package/dist/core/session-memory.js.map +1 -0
- package/dist/core/tools.d.ts +3 -0
- package/dist/core/tools.d.ts.map +1 -1
- package/dist/core/tools.js +415 -357
- package/dist/core/tools.js.map +1 -1
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/config.d.ts +26 -2
- package/dist/utils/config.d.ts.map +1 -1
- package/dist/utils/config.js +43 -2
- package/dist/utils/config.js.map +1 -1
- package/dist/utils/safety.d.ts +21 -0
- package/dist/utils/safety.d.ts.map +1 -1
- package/dist/utils/safety.js +36 -0
- package/dist/utils/safety.js.map +1 -1
- package/package.json +2 -2
package/dist/core/tools.js
CHANGED
|
@@ -7,7 +7,7 @@ import { MODE_CONFIG, isToolAllowed, isValidMode } from './modes.js';
|
|
|
7
7
|
import { FileEditor } from './editor.js';
|
|
8
8
|
import { GitUtils } from '../utils/git.js';
|
|
9
9
|
import { TestRunnerDetector } from '../utils/testRunner.js';
|
|
10
|
-
import { SafetyChecker } from '../utils/safety.js';
|
|
10
|
+
import { SafetyChecker, sanitizePath, sanitizeUrl } from '../utils/safety.js';
|
|
11
11
|
import { PluginManager } from './plugins.js';
|
|
12
12
|
import { BrowserManager } from '../tools/browser.js';
|
|
13
13
|
import * as os from 'os';
|
|
@@ -86,6 +86,8 @@ export class CodingToolExecutor {
|
|
|
86
86
|
platform;
|
|
87
87
|
dryRun;
|
|
88
88
|
testCommandOverride;
|
|
89
|
+
/** Session-scoped tools synthesized by the agent (meta-agent). Execution is sandboxed via run_command. */
|
|
90
|
+
dynamicTools = new Map();
|
|
89
91
|
/**
|
|
90
92
|
* Creates a new CodingToolExecutor instance
|
|
91
93
|
*
|
|
@@ -297,401 +299,426 @@ export class CodingToolExecutor {
|
|
|
297
299
|
riskAssessment.warnings.push(`Suggestion: ${suggestion}`);
|
|
298
300
|
}
|
|
299
301
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
return { error: true, success: false, message: 'Missing required parameter: path (string). Example: {"path": "src/index.ts"}' };
|
|
304
|
-
}
|
|
305
|
-
return this.readFile(p.path, p.start_line, p.end_line);
|
|
302
|
+
try {
|
|
303
|
+
if (this.dynamicTools.has(toolName)) {
|
|
304
|
+
return this.runDynamicTool(toolName, p);
|
|
306
305
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
306
|
+
switch (toolName) {
|
|
307
|
+
case 'read_file': {
|
|
308
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
309
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string). Example: {"path": "src/index.ts"}' };
|
|
310
|
+
}
|
|
311
|
+
return this.readFile(p.path, p.start_line, p.end_line);
|
|
310
312
|
}
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
313
|
+
case 'read_multiple_files': {
|
|
314
|
+
if (!Array.isArray(p.paths) || p.paths.length === 0) {
|
|
315
|
+
return { error: true, success: false, message: 'Missing required parameter: paths (non-empty array of strings). Example: {"paths": ["file1.ts", "file2.ts"]}' };
|
|
316
|
+
}
|
|
317
|
+
const validPaths = p.paths.filter((x) => typeof x === 'string');
|
|
318
|
+
if (validPaths.length === 0) {
|
|
319
|
+
return { error: true, success: false, message: 'paths array must contain strings. Example: {"paths": ["file1.ts", "file2.ts"]}' };
|
|
320
|
+
}
|
|
321
|
+
return this.readMultipleFiles(validPaths);
|
|
314
322
|
}
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
323
|
+
case 'write_file': {
|
|
324
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
325
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
326
|
+
}
|
|
327
|
+
if (typeof p.content !== 'string') {
|
|
328
|
+
return { error: true, success: false, message: 'Missing required parameter: content (string)' };
|
|
329
|
+
}
|
|
330
|
+
return this.writeFile(p.path, p.content);
|
|
320
331
|
}
|
|
321
|
-
|
|
322
|
-
|
|
332
|
+
case 'edit_file': {
|
|
333
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
334
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
335
|
+
}
|
|
336
|
+
if (typeof p.search !== 'string') {
|
|
337
|
+
return { error: true, success: false, message: 'Missing required parameter: search (string)' };
|
|
338
|
+
}
|
|
339
|
+
if (typeof p.replace !== 'string') {
|
|
340
|
+
return { error: true, success: false, message: 'Missing required parameter: replace (string)' };
|
|
341
|
+
}
|
|
342
|
+
return this.editFile(p.path, p.search, p.replace, p.all);
|
|
323
343
|
}
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
344
|
+
case 'edit_lines': {
|
|
345
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
346
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
347
|
+
}
|
|
348
|
+
if (typeof p.start_line !== 'number' || typeof p.end_line !== 'number') {
|
|
349
|
+
return { error: true, success: false, message: 'Missing required parameters: start_line, end_line (numbers)' };
|
|
350
|
+
}
|
|
351
|
+
if (typeof p.new_content !== 'string') {
|
|
352
|
+
return { error: true, success: false, message: 'Missing required parameter: new_content (string)' };
|
|
353
|
+
}
|
|
354
|
+
return this.editLines(p.path, p.start_line, p.end_line, p.new_content);
|
|
329
355
|
}
|
|
330
|
-
|
|
331
|
-
|
|
356
|
+
case 'insert_at_line': {
|
|
357
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
358
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
359
|
+
}
|
|
360
|
+
if (typeof p.line !== 'number') {
|
|
361
|
+
return { error: true, success: false, message: 'Missing required parameter: line (number)' };
|
|
362
|
+
}
|
|
363
|
+
return this.insertAtLine(p.path, p.line, p.content ?? '');
|
|
332
364
|
}
|
|
333
|
-
|
|
334
|
-
|
|
365
|
+
case 'verified_edit': {
|
|
366
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
367
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
368
|
+
}
|
|
369
|
+
if (typeof p.start_line !== 'number' || typeof p.end_line !== 'number') {
|
|
370
|
+
return { error: true, success: false, message: 'Missing required parameters: start_line, end_line (numbers)' };
|
|
371
|
+
}
|
|
372
|
+
if (typeof p.old_content !== 'string') {
|
|
373
|
+
return { error: true, success: false, message: 'Missing required parameter: old_content (string) - the content currently at those lines' };
|
|
374
|
+
}
|
|
375
|
+
if (typeof p.new_content !== 'string') {
|
|
376
|
+
return { error: true, success: false, message: 'Missing required parameter: new_content (string)' };
|
|
377
|
+
}
|
|
378
|
+
return this.verifiedEditFile(p.path, p.start_line, p.end_line, p.old_content, p.new_content);
|
|
335
379
|
}
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
380
|
+
case 'list_directory':
|
|
381
|
+
return this.listDirectory(p.path || '.');
|
|
382
|
+
case 'search_files': {
|
|
383
|
+
if (!p.pattern || typeof p.pattern !== 'string') {
|
|
384
|
+
return { error: true, success: false, message: 'Missing required parameter: pattern (string). Example: {"pattern": "**/*.ts"}' };
|
|
385
|
+
}
|
|
386
|
+
return this.searchFiles(p.pattern, p.path);
|
|
341
387
|
}
|
|
342
|
-
|
|
343
|
-
|
|
388
|
+
case 'run_command': {
|
|
389
|
+
if (!p.command || typeof p.command !== 'string') {
|
|
390
|
+
return { error: true, success: false, message: 'Missing required parameter: command (string)' };
|
|
391
|
+
}
|
|
392
|
+
return this.runCommand(p.command, p.cwd, p.input, p.timeout);
|
|
344
393
|
}
|
|
345
|
-
|
|
346
|
-
|
|
394
|
+
case 'create_directory': {
|
|
395
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
396
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
397
|
+
}
|
|
398
|
+
return this.createDirectory(p.path);
|
|
347
399
|
}
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
return
|
|
400
|
+
case 'delete_file': {
|
|
401
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
402
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
403
|
+
}
|
|
404
|
+
return this.deleteFile(p.path);
|
|
353
405
|
}
|
|
354
|
-
|
|
355
|
-
|
|
406
|
+
case 'move_file': {
|
|
407
|
+
if (!p.source || typeof p.source !== 'string') {
|
|
408
|
+
return { error: true, success: false, message: 'Missing required parameter: source (string)' };
|
|
409
|
+
}
|
|
410
|
+
if (!p.destination || typeof p.destination !== 'string') {
|
|
411
|
+
return { error: true, success: false, message: 'Missing required parameter: destination (string)' };
|
|
412
|
+
}
|
|
413
|
+
return this.moveFile(p.source, p.destination);
|
|
356
414
|
}
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
return
|
|
415
|
+
case 'get_context': {
|
|
416
|
+
if (!Array.isArray(p.files)) {
|
|
417
|
+
return { error: true, success: false, message: 'Missing required parameter: files (array of strings)' };
|
|
418
|
+
}
|
|
419
|
+
return this.getContext(p.files.filter((f) => typeof f === 'string'));
|
|
362
420
|
}
|
|
363
|
-
|
|
364
|
-
|
|
421
|
+
case 'revert_file': {
|
|
422
|
+
if (!p.path || typeof p.path !== 'string') {
|
|
423
|
+
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
424
|
+
}
|
|
425
|
+
return this.revertFile(p.path, p.backup_index);
|
|
365
426
|
}
|
|
366
|
-
|
|
367
|
-
return
|
|
427
|
+
case 'run_tests': {
|
|
428
|
+
return this.runTests(p.command, p.cwd);
|
|
368
429
|
}
|
|
369
|
-
|
|
370
|
-
return
|
|
430
|
+
case 'get_test_status': {
|
|
431
|
+
return this.getTestStatus();
|
|
371
432
|
}
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
case 'list_directory':
|
|
375
|
-
return this.listDirectory(p.path || '.');
|
|
376
|
-
case 'search_files': {
|
|
377
|
-
if (!p.pattern || typeof p.pattern !== 'string') {
|
|
378
|
-
return { error: true, success: false, message: 'Missing required parameter: pattern (string). Example: {"pattern": "**/*.ts"}' };
|
|
433
|
+
case 'get_git_status': {
|
|
434
|
+
return this.getGitStatus();
|
|
379
435
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
case 'run_command': {
|
|
383
|
-
if (!p.command || typeof p.command !== 'string') {
|
|
384
|
-
return { error: true, success: false, message: 'Missing required parameter: command (string)' };
|
|
436
|
+
case 'get_git_diff_summary': {
|
|
437
|
+
return this.getGitDiffSummary(p.target);
|
|
385
438
|
}
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
case 'create_directory': {
|
|
389
|
-
if (!p.path || typeof p.path !== 'string') {
|
|
390
|
-
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
439
|
+
case 'get_git_changed_files': {
|
|
440
|
+
return this.getGitChangedFiles(p.target);
|
|
391
441
|
}
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
442
|
+
case 'git_commit': {
|
|
443
|
+
if (!p.message || typeof p.message !== 'string')
|
|
444
|
+
return { error: true, success: false, message: 'Missing message' };
|
|
445
|
+
return this.gitCommit(p.message, p.agent_name);
|
|
397
446
|
}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
return { error: true, success: false, message: 'Missing required parameter: source (string)' };
|
|
447
|
+
case 'git_blame_ai': {
|
|
448
|
+
if (!p.file_path || typeof p.file_path !== 'string')
|
|
449
|
+
return { error: true, success: false, message: 'Missing file_path' };
|
|
450
|
+
return this.gitBlameAi(p.file_path);
|
|
403
451
|
}
|
|
404
|
-
|
|
405
|
-
|
|
452
|
+
case 'create_git_checkpoint': {
|
|
453
|
+
if (!p.message || typeof p.message !== 'string') {
|
|
454
|
+
return { error: true, success: false, message: 'Missing required parameter: message (string)' };
|
|
455
|
+
}
|
|
456
|
+
return this.createGitCheckpoint(p.message, p.strategy);
|
|
406
457
|
}
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
458
|
+
case 'revert_to_git_checkpoint': {
|
|
459
|
+
if (!p.checkpoint_id || typeof p.checkpoint_id !== 'string') {
|
|
460
|
+
return { error: true, success: false, message: 'Missing required parameter: checkpoint_id (string)' };
|
|
461
|
+
}
|
|
462
|
+
if (!p.confirm) {
|
|
463
|
+
return { error: true, success: false, message: 'Revert requires explicit confirmation. Set confirm: true' };
|
|
464
|
+
}
|
|
465
|
+
return this.revertToGitCheckpoint(p.checkpoint_id, p.checkpoint_type, p.confirm);
|
|
412
466
|
}
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
case 'revert_file': {
|
|
416
|
-
if (!p.path || typeof p.path !== 'string') {
|
|
417
|
-
return { error: true, success: false, message: 'Missing required parameter: path (string)' };
|
|
467
|
+
case 'git_show_diff': {
|
|
468
|
+
return this.gitShowDiff(p.file_path, p.target);
|
|
418
469
|
}
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
case 'run_tests': {
|
|
422
|
-
return this.runTests(p.command, p.cwd);
|
|
423
|
-
}
|
|
424
|
-
case 'get_test_status': {
|
|
425
|
-
return this.getTestStatus();
|
|
426
|
-
}
|
|
427
|
-
case 'get_git_status': {
|
|
428
|
-
return this.getGitStatus();
|
|
429
|
-
}
|
|
430
|
-
case 'get_git_diff_summary': {
|
|
431
|
-
return this.getGitDiffSummary(p.target);
|
|
432
|
-
}
|
|
433
|
-
case 'get_git_changed_files': {
|
|
434
|
-
return this.getGitChangedFiles(p.target);
|
|
435
|
-
}
|
|
436
|
-
case 'git_commit': {
|
|
437
|
-
if (!p.message || typeof p.message !== 'string')
|
|
438
|
-
return { error: true, success: false, message: 'Missing message' };
|
|
439
|
-
return this.gitCommit(p.message, p.agent_name);
|
|
440
|
-
}
|
|
441
|
-
case 'git_blame_ai': {
|
|
442
|
-
if (!p.file_path || typeof p.file_path !== 'string')
|
|
443
|
-
return { error: true, success: false, message: 'Missing file_path' };
|
|
444
|
-
return this.gitBlameAi(p.file_path);
|
|
445
|
-
}
|
|
446
|
-
case 'create_git_checkpoint': {
|
|
447
|
-
if (!p.message || typeof p.message !== 'string') {
|
|
448
|
-
return { error: true, success: false, message: 'Missing required parameter: message (string)' };
|
|
470
|
+
case 'get_mcp_status': {
|
|
471
|
+
return this.getMCPStatus();
|
|
449
472
|
}
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
return
|
|
473
|
+
case 'grep_code': {
|
|
474
|
+
if (!p.pattern || typeof p.pattern !== 'string') {
|
|
475
|
+
return { error: true, success: false, message: 'Missing required parameter: pattern (string)' };
|
|
476
|
+
}
|
|
477
|
+
return this.grepCode(p.pattern, p.path, p.ignore_case, p.file_pattern, p.max_results);
|
|
455
478
|
}
|
|
456
|
-
|
|
457
|
-
|
|
479
|
+
case 'web_search': {
|
|
480
|
+
if (!p.query || typeof p.query !== 'string') {
|
|
481
|
+
return { error: true, success: false, message: 'Missing required parameter: query (string)' };
|
|
482
|
+
}
|
|
483
|
+
return this.webSearch(p.query, p.max_results);
|
|
458
484
|
}
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
case 'get_mcp_status': {
|
|
465
|
-
return this.getMCPStatus();
|
|
466
|
-
}
|
|
467
|
-
case 'grep_code': {
|
|
468
|
-
if (!p.pattern || typeof p.pattern !== 'string') {
|
|
469
|
-
return { error: true, success: false, message: 'Missing required parameter: pattern (string)' };
|
|
485
|
+
case 'fetch_url': {
|
|
486
|
+
if (!p.url || typeof p.url !== 'string') {
|
|
487
|
+
return { error: true, success: false, message: 'Missing required parameter: url (string)' };
|
|
488
|
+
}
|
|
489
|
+
return this.fetchUrl(p.url, p.max_length);
|
|
470
490
|
}
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
return
|
|
491
|
+
case 'update_memory': {
|
|
492
|
+
if (!p.content || typeof p.content !== 'string') {
|
|
493
|
+
return { error: true, success: false, message: 'Missing required parameter: content (string)' };
|
|
494
|
+
}
|
|
495
|
+
return this.updateMemory(p.content, p.append);
|
|
476
496
|
}
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
497
|
+
case 'remember_lesson': {
|
|
498
|
+
if (!this.memory) {
|
|
499
|
+
return { error: true, success: false, message: 'Memory system is not initialized.' };
|
|
500
|
+
}
|
|
501
|
+
if (!p.trigger || typeof p.trigger !== 'string')
|
|
502
|
+
return { error: true, success: false, message: 'Missing trigger' };
|
|
503
|
+
if (!p.action || typeof p.action !== 'string')
|
|
504
|
+
return { error: true, success: false, message: 'Missing action' };
|
|
505
|
+
if (!p.outcome || typeof p.outcome !== 'string')
|
|
506
|
+
return { error: true, success: false, message: 'Missing outcome' };
|
|
507
|
+
await this.memory.addMemory(p.trigger, p.action, p.outcome, p.tags || []);
|
|
508
|
+
return { success: true, message: 'Lesson learned and saved to neural memory.' };
|
|
509
|
+
}
|
|
510
|
+
case 'take_screenshot': {
|
|
511
|
+
if (!p.url || typeof p.url !== 'string')
|
|
512
|
+
return { error: true, success: false, message: 'Missing url' };
|
|
513
|
+
if (!p.path || typeof p.path !== 'string')
|
|
514
|
+
return { error: true, success: false, message: 'Missing path' };
|
|
515
|
+
return this.browserManager.takeScreenshot(p.url, p.path, p.fullPage !== false);
|
|
516
|
+
}
|
|
517
|
+
case 'get_console_logs': {
|
|
518
|
+
if (!p.url || typeof p.url !== 'string')
|
|
519
|
+
return { error: true, success: false, message: 'Missing url' };
|
|
520
|
+
return this.browserManager.getConsoleLogs(p.url);
|
|
521
|
+
}
|
|
522
|
+
case 'run_visual_test': {
|
|
523
|
+
if (!p.url || typeof p.url !== 'string')
|
|
524
|
+
return { error: true, success: false, message: 'Missing url' };
|
|
525
|
+
if (!p.baseline_path || typeof p.baseline_path !== 'string')
|
|
526
|
+
return { error: true, success: false, message: 'Missing baseline_path' };
|
|
527
|
+
const outputDir = p.output_dir || '.playwright-baselines';
|
|
528
|
+
return this.browserManager.runVisualTest(p.url, this.resolvePath(p.baseline_path), this.resolvePath(outputDir));
|
|
529
|
+
}
|
|
530
|
+
case 'check_accessibility': {
|
|
531
|
+
if (!p.url || typeof p.url !== 'string')
|
|
532
|
+
return { error: true, success: false, message: 'Missing url' };
|
|
533
|
+
return this.browserManager.checkAccessibility(p.url);
|
|
534
|
+
}
|
|
535
|
+
case 'measure_performance': {
|
|
536
|
+
if (!p.url || typeof p.url !== 'string')
|
|
537
|
+
return { error: true, success: false, message: 'Missing url' };
|
|
538
|
+
return this.browserManager.measurePerformance(p.url);
|
|
539
|
+
}
|
|
540
|
+
case 'test_responsive': {
|
|
541
|
+
if (!p.url || typeof p.url !== 'string')
|
|
542
|
+
return { error: true, success: false, message: 'Missing url' };
|
|
543
|
+
const outputDir = p.output_dir || '.responsive-screenshots';
|
|
544
|
+
return this.browserManager.testResponsive(p.url, this.resolvePath(outputDir), p.viewports);
|
|
545
|
+
}
|
|
546
|
+
case 'capture_network': {
|
|
547
|
+
if (!p.url || typeof p.url !== 'string')
|
|
548
|
+
return { error: true, success: false, message: 'Missing url' };
|
|
549
|
+
return this.browserManager.captureNetworkRequests(p.url);
|
|
550
|
+
}
|
|
551
|
+
case 'run_playwright_test': {
|
|
552
|
+
if (!p.test_path || typeof p.test_path !== 'string')
|
|
553
|
+
return { error: true, success: false, message: 'Missing test_path' };
|
|
554
|
+
return this.browserManager.runPlaywrightTest(this.resolvePath(p.test_path), {
|
|
555
|
+
headed: p.headed,
|
|
556
|
+
browser: p.browser,
|
|
557
|
+
timeout: p.timeout,
|
|
558
|
+
});
|
|
482
559
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return
|
|
560
|
+
case 'search_skills_sh': {
|
|
561
|
+
if (!p.query || typeof p.query !== 'string') {
|
|
562
|
+
return { error: true, success: false, message: 'Missing required parameter: query (string)' };
|
|
563
|
+
}
|
|
564
|
+
return this.searchSkillsSh(p.query);
|
|
488
565
|
}
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
return
|
|
494
|
-
}
|
|
495
|
-
if (!p.trigger || typeof p.trigger !== 'string')
|
|
496
|
-
return { error: true, success: false, message: 'Missing trigger' };
|
|
497
|
-
if (!p.action || typeof p.action !== 'string')
|
|
498
|
-
return { error: true, success: false, message: 'Missing action' };
|
|
499
|
-
if (!p.outcome || typeof p.outcome !== 'string')
|
|
500
|
-
return { error: true, success: false, message: 'Missing outcome' };
|
|
501
|
-
await this.memory.addMemory(p.trigger, p.action, p.outcome, p.tags || []);
|
|
502
|
-
return { success: true, message: 'Lesson learned and saved to neural memory.' };
|
|
503
|
-
}
|
|
504
|
-
case 'take_screenshot': {
|
|
505
|
-
if (!p.url || typeof p.url !== 'string')
|
|
506
|
-
return { error: true, success: false, message: 'Missing url' };
|
|
507
|
-
if (!p.path || typeof p.path !== 'string')
|
|
508
|
-
return { error: true, success: false, message: 'Missing path' };
|
|
509
|
-
return this.browserManager.takeScreenshot(p.url, p.path, p.fullPage !== false);
|
|
510
|
-
}
|
|
511
|
-
case 'get_console_logs': {
|
|
512
|
-
if (!p.url || typeof p.url !== 'string')
|
|
513
|
-
return { error: true, success: false, message: 'Missing url' };
|
|
514
|
-
return this.browserManager.getConsoleLogs(p.url);
|
|
515
|
-
}
|
|
516
|
-
case 'run_visual_test': {
|
|
517
|
-
if (!p.url || typeof p.url !== 'string')
|
|
518
|
-
return { error: true, success: false, message: 'Missing url' };
|
|
519
|
-
if (!p.baseline_path || typeof p.baseline_path !== 'string')
|
|
520
|
-
return { error: true, success: false, message: 'Missing baseline_path' };
|
|
521
|
-
const outputDir = p.output_dir || '.playwright-baselines';
|
|
522
|
-
return this.browserManager.runVisualTest(p.url, this.resolvePath(p.baseline_path), this.resolvePath(outputDir));
|
|
523
|
-
}
|
|
524
|
-
case 'check_accessibility': {
|
|
525
|
-
if (!p.url || typeof p.url !== 'string')
|
|
526
|
-
return { error: true, success: false, message: 'Missing url' };
|
|
527
|
-
return this.browserManager.checkAccessibility(p.url);
|
|
528
|
-
}
|
|
529
|
-
case 'measure_performance': {
|
|
530
|
-
if (!p.url || typeof p.url !== 'string')
|
|
531
|
-
return { error: true, success: false, message: 'Missing url' };
|
|
532
|
-
return this.browserManager.measurePerformance(p.url);
|
|
533
|
-
}
|
|
534
|
-
case 'test_responsive': {
|
|
535
|
-
if (!p.url || typeof p.url !== 'string')
|
|
536
|
-
return { error: true, success: false, message: 'Missing url' };
|
|
537
|
-
const outputDir = p.output_dir || '.responsive-screenshots';
|
|
538
|
-
return this.browserManager.testResponsive(p.url, this.resolvePath(outputDir), p.viewports);
|
|
539
|
-
}
|
|
540
|
-
case 'capture_network': {
|
|
541
|
-
if (!p.url || typeof p.url !== 'string')
|
|
542
|
-
return { error: true, success: false, message: 'Missing url' };
|
|
543
|
-
return this.browserManager.captureNetworkRequests(p.url);
|
|
544
|
-
}
|
|
545
|
-
case 'run_playwright_test': {
|
|
546
|
-
if (!p.test_path || typeof p.test_path !== 'string')
|
|
547
|
-
return { error: true, success: false, message: 'Missing test_path' };
|
|
548
|
-
return this.browserManager.runPlaywrightTest(this.resolvePath(p.test_path), {
|
|
549
|
-
headed: p.headed,
|
|
550
|
-
browser: p.browser,
|
|
551
|
-
timeout: p.timeout,
|
|
552
|
-
});
|
|
553
|
-
}
|
|
554
|
-
case 'search_skills_sh': {
|
|
555
|
-
if (!p.query || typeof p.query !== 'string') {
|
|
556
|
-
return { error: true, success: false, message: 'Missing required parameter: query (string)' };
|
|
566
|
+
case 'install_skill_from_skills_sh': {
|
|
567
|
+
if (!p.skill_id || typeof p.skill_id !== 'string') {
|
|
568
|
+
return { error: true, success: false, message: 'Missing required parameter: skill_id (string)' };
|
|
569
|
+
}
|
|
570
|
+
return this.installSkillFromSkillsSh(p.skill_id);
|
|
557
571
|
}
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
572
|
+
// AI Test Generation Tools
|
|
573
|
+
case 'generate_tests': {
|
|
574
|
+
if (!p.file_path || typeof p.file_path !== 'string') {
|
|
575
|
+
return { error: true, success: false, message: 'Missing required parameter: file_path (string)' };
|
|
576
|
+
}
|
|
577
|
+
return this.generateTests(p.file_path, {
|
|
578
|
+
framework: p.framework,
|
|
579
|
+
outputDir: p.output_dir,
|
|
580
|
+
includeEdgeCases: p.include_edge_cases,
|
|
581
|
+
includeMocks: p.include_mocks,
|
|
582
|
+
maxTestsPerFunction: p.max_tests_per_function,
|
|
583
|
+
}, p.write_file);
|
|
584
|
+
}
|
|
585
|
+
case 'preview_app': {
|
|
586
|
+
if (!p.url || typeof p.url !== 'string') {
|
|
587
|
+
return { error: true, success: false, message: 'Missing required parameter: url (string)' };
|
|
588
|
+
}
|
|
589
|
+
return this.visualFeedback.capture(p.url, { fullPage: p.full_page });
|
|
563
590
|
}
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
case 'preview_app': {
|
|
580
|
-
if (!p.url || typeof p.url !== 'string') {
|
|
581
|
-
return { error: true, success: false, message: 'Missing required parameter: url (string)' };
|
|
591
|
+
case 'mine_project_patterns': {
|
|
592
|
+
const patterns = await this.patternMiner.mine();
|
|
593
|
+
if (patterns.length === 0) {
|
|
594
|
+
return { success: true, message: 'No significant repeated patterns found in the project.' };
|
|
595
|
+
}
|
|
596
|
+
// Format for AI consumption
|
|
597
|
+
const summary = patterns.map(p => `Pattern: ${p.description}\n` +
|
|
598
|
+
`- Occurrences: ${p.frequency}\n` +
|
|
599
|
+
`- Locations: ${p.chunks.map(c => `${path.relative(this.workingDir, c.filePath)}:${c.startLine}`).join(', ')}\n` +
|
|
600
|
+
`- Example Code:\n\`\`\`typescript\n${p.chunks[0].content}\n\`\`\`\n`).join('\n---\n\n');
|
|
601
|
+
return {
|
|
602
|
+
success: true,
|
|
603
|
+
message: `Found ${patterns.length} pattern clusters.`,
|
|
604
|
+
patterns: summary
|
|
605
|
+
};
|
|
582
606
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
return { success: true, message:
|
|
589
|
-
}
|
|
590
|
-
// Format for AI consumption
|
|
591
|
-
const summary = patterns.map(p => `Pattern: ${p.description}\n` +
|
|
592
|
-
`- Occurrences: ${p.frequency}\n` +
|
|
593
|
-
`- Locations: ${p.chunks.map(c => `${path.relative(this.workingDir, c.filePath)}:${c.startLine}`).join(', ')}\n` +
|
|
594
|
-
`- Example Code:\n\`\`\`typescript\n${p.chunks[0].content}\n\`\`\`\n`).join('\n---\n\n');
|
|
595
|
-
return {
|
|
596
|
-
success: true,
|
|
597
|
-
message: `Found ${patterns.length} pattern clusters.`,
|
|
598
|
-
patterns: summary
|
|
599
|
-
};
|
|
600
|
-
}
|
|
601
|
-
case 'start_background_task': {
|
|
602
|
-
if (!p.prompt || typeof p.prompt !== 'string') {
|
|
603
|
-
return { error: true, success: false, message: 'Missing required parameter: prompt (string)' };
|
|
607
|
+
case 'start_background_task': {
|
|
608
|
+
if (!p.prompt || typeof p.prompt !== 'string') {
|
|
609
|
+
return { error: true, success: false, message: 'Missing required parameter: prompt (string)' };
|
|
610
|
+
}
|
|
611
|
+
const taskId = await this.backgroundAgent.startTask(p.prompt);
|
|
612
|
+
return { success: true, message: `Background task started with ID: ${taskId}`, task_id: taskId };
|
|
604
613
|
}
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const tasks = await this.backgroundAgent.listTasks();
|
|
610
|
-
const summary = tasks.map(t => `ID: ${t.id} | Status: ${t.status} | Started: ${new Date(t.startTime).toISOString()} | Prompt: "${t.prompt.substring(0, 50)}..."`).join('\n');
|
|
611
|
-
return { success: true, message: `Active Tasks:\n${summary || 'No active tasks.'}` };
|
|
612
|
-
}
|
|
613
|
-
case 'check_background_task': {
|
|
614
|
-
if (!p.task_id || typeof p.task_id !== 'string') {
|
|
615
|
-
return { error: true, success: false, message: 'Missing required parameter: task_id (string)' };
|
|
614
|
+
case 'list_background_tasks': {
|
|
615
|
+
const tasks = await this.backgroundAgent.listTasks();
|
|
616
|
+
const summary = tasks.map(t => `ID: ${t.id} | Status: ${t.status} | Started: ${new Date(t.startTime).toISOString()} | Prompt: "${t.prompt.substring(0, 50)}..."`).join('\n');
|
|
617
|
+
return { success: true, message: `Active Tasks:\n${summary || 'No active tasks.'}` };
|
|
616
618
|
}
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
return {
|
|
619
|
+
case 'check_background_task': {
|
|
620
|
+
if (!p.task_id || typeof p.task_id !== 'string') {
|
|
621
|
+
return { error: true, success: false, message: 'Missing required parameter: task_id (string)' };
|
|
622
|
+
}
|
|
623
|
+
const logs = await this.backgroundAgent.getTaskLogs(p.task_id);
|
|
624
|
+
return { success: true, logs };
|
|
623
625
|
}
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
return { success: true, message: `Active Tasks:\n${summary || 'No active tasks.'}` };
|
|
631
|
-
}
|
|
632
|
-
case 'check_background_task': {
|
|
633
|
-
if (!p.task_id || typeof p.task_id !== 'string') {
|
|
634
|
-
return { error: true, success: false, message: 'Missing required parameter: task_id (string)' };
|
|
626
|
+
case 'start_background_task': {
|
|
627
|
+
if (!p.prompt || typeof p.prompt !== 'string') {
|
|
628
|
+
return { error: true, success: false, message: 'Missing required parameter: prompt (string)' };
|
|
629
|
+
}
|
|
630
|
+
const taskId = await this.backgroundAgent.startTask(p.prompt);
|
|
631
|
+
return { success: true, message: `Background task started with ID: ${taskId}`, task_id: taskId };
|
|
635
632
|
}
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
if (!p.query || typeof p.query !== 'string') {
|
|
641
|
-
return { error: true, success: false, message: 'Missing required parameter: query (symbol name)' };
|
|
633
|
+
case 'list_background_tasks': {
|
|
634
|
+
const tasks = await this.backgroundAgent.listTasks();
|
|
635
|
+
const summary = tasks.map(t => `ID: ${t.id} | Status: ${t.status} | Started: ${new Date(t.startTime).toISOString()} | Prompt: "${t.prompt.substring(0, 50)}..."`).join('\n');
|
|
636
|
+
return { success: true, message: `Active Tasks:\n${summary || 'No active tasks.'}` };
|
|
642
637
|
}
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
return {
|
|
638
|
+
case 'check_background_task': {
|
|
639
|
+
if (!p.task_id || typeof p.task_id !== 'string') {
|
|
640
|
+
return { error: true, success: false, message: 'Missing required parameter: task_id (string)' };
|
|
641
|
+
}
|
|
642
|
+
const logs = await this.backgroundAgent.getTaskLogs(p.task_id);
|
|
643
|
+
return { success: true, logs };
|
|
649
644
|
}
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
return { success: true, message:
|
|
656
|
-
}
|
|
657
|
-
// If specific file requested, use it; otherwise default to first
|
|
658
|
-
const targetFile = (p.file_path && typeof p.file_path === 'string') ? p.file_path : files[0];
|
|
659
|
-
// Ensure target is in the list or we try to parse it anyway
|
|
660
|
-
const conflictData = await this.conflictSolver.parseConflicts(targetFile);
|
|
661
|
-
if (!conflictData) {
|
|
662
|
-
return { success: false, message: `Could not parse conflicts in ${targetFile}. Markers might be missing or malformed.`, other_files: files };
|
|
645
|
+
case 'search_code_graph': {
|
|
646
|
+
if (!p.query || typeof p.query !== 'string') {
|
|
647
|
+
return { error: true, success: false, message: 'Missing required parameter: query (symbol name)' };
|
|
648
|
+
}
|
|
649
|
+
const results = await this.codeGraph.findReferences(p.query);
|
|
650
|
+
return { success: true, message: results };
|
|
663
651
|
}
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
652
|
+
case 'analyze_code_for_tests': {
|
|
653
|
+
if (!p.file_path || typeof p.file_path !== 'string') {
|
|
654
|
+
return { error: true, success: false, message: 'Missing required parameter: file_path (string)' };
|
|
655
|
+
}
|
|
656
|
+
return this.analyzeCodeForTests(p.file_path);
|
|
657
|
+
}
|
|
658
|
+
case 'resolve_merge_conflicts': {
|
|
659
|
+
const files = await this.conflictSolver.findConflictingFiles();
|
|
660
|
+
if (files.length === 0) {
|
|
661
|
+
return { success: true, message: 'No merge conflicts found in the project.' };
|
|
662
|
+
}
|
|
663
|
+
// If specific file requested, use it; otherwise default to first
|
|
664
|
+
const targetFile = (p.file_path && typeof p.file_path === 'string') ? p.file_path : files[0];
|
|
665
|
+
// Ensure target is in the list or we try to parse it anyway
|
|
666
|
+
const conflictData = await this.conflictSolver.parseConflicts(targetFile);
|
|
667
|
+
if (!conflictData) {
|
|
668
|
+
return { success: false, message: `Could not parse conflicts in ${targetFile}. Markers might be missing or malformed.`, other_files: files };
|
|
669
|
+
}
|
|
670
|
+
return {
|
|
671
|
+
success: true,
|
|
672
|
+
message: `Found ${files.length} conflicting files. Showing conflicts for: ${targetFile}`,
|
|
673
|
+
conflicts: conflictData.conflicts.map(c => ({
|
|
674
|
+
id: c.index,
|
|
675
|
+
lines: `${c.startLine}-${c.endLine}`,
|
|
676
|
+
ours: c.ours,
|
|
677
|
+
theirs: c.theirs,
|
|
678
|
+
base: c.base
|
|
679
|
+
})),
|
|
680
|
+
other_conflicting_files: files.filter(f => f !== targetFile)
|
|
681
|
+
};
|
|
682
|
+
}
|
|
683
|
+
case 'delegate_subtask': {
|
|
684
|
+
if (!p.task || typeof p.task !== 'string')
|
|
685
|
+
return { error: true, success: false, message: 'Missing task' };
|
|
686
|
+
if (!p.worker_type || typeof p.worker_type !== 'string')
|
|
687
|
+
return { error: true, success: false, message: 'Missing worker_type (agent mode)' };
|
|
688
|
+
if (!isValidMode(p.worker_type)) {
|
|
689
|
+
return { error: true, success: false, message: `Invalid worker_type: ${p.worker_type}. Must be a valid AgentMode.` };
|
|
690
|
+
}
|
|
691
|
+
const result = await this.swarmOrchestrator.delegateSubtask(p.worker_type, p.task);
|
|
692
|
+
return {
|
|
693
|
+
success: result.success,
|
|
694
|
+
result: result.result,
|
|
695
|
+
status: result.status,
|
|
696
|
+
worker: p.worker_type
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
case 'synthesize_tool': {
|
|
700
|
+
const name = typeof p.name === 'string' ? p.name.trim() : '';
|
|
701
|
+
const description = typeof p.description === 'string' ? p.description.trim() : '';
|
|
702
|
+
const script = typeof p.script === 'string' ? p.script.trim() : '';
|
|
703
|
+
if (!name || !script) {
|
|
704
|
+
return { error: true, success: false, message: 'Missing required parameters: name (string), script (string). description is optional.' };
|
|
705
|
+
}
|
|
706
|
+
if (!/^[a-z][a-z0-9_]*$/.test(name)) {
|
|
707
|
+
return { error: true, success: false, message: 'Tool name must be lowercase letters, numbers, underscores only (e.g. my_helper).' };
|
|
708
|
+
}
|
|
709
|
+
const reserved = new Set(['read_file', 'write_file', 'run_command', 'synthesize_tool', 'get_context']);
|
|
710
|
+
if (reserved.has(name)) {
|
|
711
|
+
return { error: true, success: false, message: `Cannot override built-in tool: ${name}` };
|
|
712
|
+
}
|
|
713
|
+
this.dynamicTools.set(name, { description: description || name, script });
|
|
714
|
+
return { success: true, message: `Tool "${name}" registered. You can call it with the same name. Execution is sandboxed.` };
|
|
715
|
+
}
|
|
716
|
+
default:
|
|
717
|
+
return { error: true, success: false, message: `Unknown tool: ${toolName}. Available tools: read_file, read_multiple_files, write_file, edit_file, edit_lines, insert_at_line, verified_edit, list_directory, search_files, run_command, create_directory, delete_file, move_file, get_context, revert_file, run_tests, get_test_status, get_git_status, get_git_diff_summary, get_git_changed_files, create_git_checkpoint, revert_to_git_checkpoint, git_show_diff, get_mcp_status, grep_code, web_search, fetch_url, remember_lesson, synthesize_tool, take_screenshot, get_console_logs, run_visual_test, check_accessibility, measure_performance, test_responsive, capture_network, run_playwright_test, search_skills_sh, install_skill_from_skills_sh, preview_app, delegate_subtask` };
|
|
692
718
|
}
|
|
693
|
-
|
|
694
|
-
|
|
719
|
+
}
|
|
720
|
+
catch (err) {
|
|
721
|
+
return { error: true, success: false, message: err?.message ?? String(err) };
|
|
695
722
|
}
|
|
696
723
|
}
|
|
697
724
|
/**
|
|
@@ -1335,6 +1362,19 @@ export class CodingToolExecutor {
|
|
|
1335
1362
|
required: ['trigger', 'action', 'outcome']
|
|
1336
1363
|
}
|
|
1337
1364
|
},
|
|
1365
|
+
{
|
|
1366
|
+
name: 'synthesize_tool',
|
|
1367
|
+
description: 'Register a new session-scoped tool (meta-agent). Use when you need a reusable script for repeated operations or after repeated failures. The script runs in the same sandbox as run_command. Name must be lowercase with underscores (e.g. my_helper).',
|
|
1368
|
+
input_schema: {
|
|
1369
|
+
type: 'object',
|
|
1370
|
+
properties: {
|
|
1371
|
+
name: { type: 'string', description: 'Tool name (lowercase, letters/numbers/underscores only)' },
|
|
1372
|
+
description: { type: 'string', description: 'Short description of what the tool does' },
|
|
1373
|
+
script: { type: 'string', description: 'Shell command or script to run when the tool is invoked (e.g. "grep -r pattern src/")' }
|
|
1374
|
+
},
|
|
1375
|
+
required: ['name', 'script']
|
|
1376
|
+
}
|
|
1377
|
+
},
|
|
1338
1378
|
{
|
|
1339
1379
|
name: 'take_screenshot',
|
|
1340
1380
|
description: 'Take a screenshot of a web page. Useful for verifying UI appearance or capturing the state of a web app.',
|
|
@@ -1576,7 +1616,12 @@ export class CodingToolExecutor {
|
|
|
1576
1616
|
}
|
|
1577
1617
|
// Merge plugin tools
|
|
1578
1618
|
const pluginTools = this.pluginManager.getPluginTools();
|
|
1579
|
-
|
|
1619
|
+
const dynamicToolDefs = Array.from(this.dynamicTools.entries()).map(([name, def]) => ({
|
|
1620
|
+
name,
|
|
1621
|
+
description: `[Session tool] ${def.description}`,
|
|
1622
|
+
input_schema: { type: 'object', properties: {} },
|
|
1623
|
+
}));
|
|
1624
|
+
return [...coreTools, ...dynamicToolDefs, ...mcpTools, ...pluginTools];
|
|
1580
1625
|
}
|
|
1581
1626
|
/**
|
|
1582
1627
|
* Resolve relative file path to absolute path
|
|
@@ -1587,7 +1632,10 @@ export class CodingToolExecutor {
|
|
|
1587
1632
|
* @internal
|
|
1588
1633
|
*/
|
|
1589
1634
|
resolvePath(filePath) {
|
|
1590
|
-
|
|
1635
|
+
const result = sanitizePath(this.workingDir, filePath);
|
|
1636
|
+
if (!result.ok)
|
|
1637
|
+
throw new Error(result.message);
|
|
1638
|
+
return result.path;
|
|
1591
1639
|
}
|
|
1592
1640
|
/**
|
|
1593
1641
|
* Read file contents
|
|
@@ -1901,6 +1949,12 @@ export class CodingToolExecutor {
|
|
|
1901
1949
|
* @risk-level High
|
|
1902
1950
|
* @since 0.1.0
|
|
1903
1951
|
*/
|
|
1952
|
+
async runDynamicTool(toolName, _input) {
|
|
1953
|
+
const def = this.dynamicTools.get(toolName);
|
|
1954
|
+
if (!def)
|
|
1955
|
+
return { error: true, success: false, message: `Dynamic tool "${toolName}" not found` };
|
|
1956
|
+
return this.runCommand(def.script, this.workingDir, undefined, 60);
|
|
1957
|
+
}
|
|
1904
1958
|
async runCommand(command, cwd, input, timeout) {
|
|
1905
1959
|
const workDir = cwd ? this.resolvePath(cwd) : this.workingDir;
|
|
1906
1960
|
const timeoutMs = (timeout || 120) * 1000;
|
|
@@ -2584,8 +2638,12 @@ export class CodingToolExecutor {
|
|
|
2584
2638
|
}
|
|
2585
2639
|
// ── fetch_url: read any URL as text ────────────────────────
|
|
2586
2640
|
async fetchUrl(url, maxLength = 20000) {
|
|
2641
|
+
const urlResult = sanitizeUrl(url.trim());
|
|
2642
|
+
if (!urlResult.ok) {
|
|
2643
|
+
return { error: true, success: false, message: urlResult.message };
|
|
2644
|
+
}
|
|
2587
2645
|
try {
|
|
2588
|
-
const response = await fetch(url, {
|
|
2646
|
+
const response = await fetch(urlResult.url, {
|
|
2589
2647
|
headers: {
|
|
2590
2648
|
'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
2591
2649
|
'Accept': 'text/html,application/xhtml+xml,text/plain,application/json',
|
|
@@ -2628,7 +2686,7 @@ export class CodingToolExecutor {
|
|
|
2628
2686
|
const content = truncated ? text.slice(0, maxLength) + '\n... [truncated]' : text;
|
|
2629
2687
|
return {
|
|
2630
2688
|
success: true,
|
|
2631
|
-
url,
|
|
2689
|
+
url: urlResult.url,
|
|
2632
2690
|
contentType: contentType.split(';')[0],
|
|
2633
2691
|
length: text.length,
|
|
2634
2692
|
truncated,
|