@probelabs/probe-chat 0.6.0-rc116 → 0.6.0-rc118
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/package.json +1 -2
- package/probeTool.js +45 -328
- package/tools.js +0 -186
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@probelabs/probe-chat",
|
|
3
|
-
"version": "0.6.0-
|
|
3
|
+
"version": "0.6.0-rc118",
|
|
4
4
|
"description": "CLI and web interface for Probe code search (formerly @probelabs/probe-web and @probelabs/probe-chat)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -84,7 +84,6 @@
|
|
|
84
84
|
"probeChat.js",
|
|
85
85
|
"tokenCounter.js",
|
|
86
86
|
"tokenUsageDisplay.js",
|
|
87
|
-
"tools.js",
|
|
88
87
|
"webServer.js",
|
|
89
88
|
"auth.js",
|
|
90
89
|
"probeTool.js",
|
package/probeTool.js
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
// Import tool generators from @probelabs/probe package
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
// Import tool generators and instances from @probelabs/probe package
|
|
2
|
+
import {
|
|
3
|
+
searchTool,
|
|
4
|
+
queryTool,
|
|
5
|
+
extractTool,
|
|
6
|
+
DEFAULT_SYSTEM_MESSAGE,
|
|
7
|
+
listFilesToolInstance as packageListFilesToolInstance,
|
|
8
|
+
searchFilesToolInstance as packageSearchFilesToolInstance
|
|
9
|
+
} from '@probelabs/probe';
|
|
10
|
+
import { spawn } from 'child_process';
|
|
5
11
|
import { randomUUID } from 'crypto';
|
|
6
12
|
import { EventEmitter } from 'events';
|
|
7
|
-
import fs from 'fs';
|
|
8
|
-
import { promises as fsPromises } from 'fs';
|
|
9
|
-
import path from 'path';
|
|
10
|
-
import os from 'os';
|
|
11
|
-
import { glob } from 'glob';
|
|
12
13
|
|
|
13
14
|
// Import the new pluggable implementation tool
|
|
14
15
|
import { createImplementTool } from './implement/core/ImplementTool.js';
|
|
@@ -323,28 +324,18 @@ const baseImplementTool = {
|
|
|
323
324
|
}
|
|
324
325
|
};
|
|
325
326
|
|
|
326
|
-
//
|
|
327
|
+
// Wrapper for listFiles tool with ALLOWED_FOLDERS security
|
|
327
328
|
const baseListFilesTool = {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
type: 'object',
|
|
332
|
-
properties: {
|
|
333
|
-
directory: {
|
|
334
|
-
type: 'string',
|
|
335
|
-
description: 'The directory path to list files from. Defaults to current directory if not specified.'
|
|
336
|
-
}
|
|
337
|
-
},
|
|
338
|
-
required: []
|
|
339
|
-
},
|
|
340
|
-
execute: async ({ directory = '.', sessionId }) => {
|
|
329
|
+
...packageListFilesToolInstance,
|
|
330
|
+
execute: async (params) => {
|
|
331
|
+
const { directory = '.', sessionId } = params;
|
|
341
332
|
const debug = process.env.DEBUG_CHAT === '1';
|
|
342
333
|
const currentWorkingDir = process.cwd();
|
|
343
|
-
|
|
334
|
+
|
|
344
335
|
// Get allowed folders from environment variable
|
|
345
336
|
const allowedFoldersEnv = process.env.ALLOWED_FOLDERS;
|
|
346
337
|
let allowedFolders = [];
|
|
347
|
-
|
|
338
|
+
|
|
348
339
|
if (allowedFoldersEnv) {
|
|
349
340
|
allowedFolders = allowedFoldersEnv.split(',').map(folder => folder.trim()).filter(folder => folder.length > 0);
|
|
350
341
|
}
|
|
@@ -359,13 +350,13 @@ const baseListFilesTool = {
|
|
|
359
350
|
}
|
|
360
351
|
}
|
|
361
352
|
|
|
362
|
-
const targetDir = path.resolve(currentWorkingDir, targetDirectory);
|
|
353
|
+
const targetDir = require('path').resolve(currentWorkingDir, targetDirectory);
|
|
363
354
|
|
|
364
355
|
// Validate that the target directory is within allowed folders
|
|
365
356
|
if (allowedFolders.length > 0) {
|
|
366
357
|
const isAllowed = allowedFolders.some(allowedFolder => {
|
|
367
|
-
const resolvedAllowedFolder = path.resolve(currentWorkingDir, allowedFolder);
|
|
368
|
-
return targetDir === resolvedAllowedFolder || targetDir.startsWith(resolvedAllowedFolder + path.sep);
|
|
358
|
+
const resolvedAllowedFolder = require('path').resolve(currentWorkingDir, allowedFolder);
|
|
359
|
+
return targetDir === resolvedAllowedFolder || targetDir.startsWith(resolvedAllowedFolder + require('path').sep);
|
|
369
360
|
});
|
|
370
361
|
|
|
371
362
|
if (!isAllowed) {
|
|
@@ -377,107 +368,27 @@ const baseListFilesTool = {
|
|
|
377
368
|
}
|
|
378
369
|
}
|
|
379
370
|
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
const files = await fs.promises.readdir(targetDir, { withFileTypes: true });
|
|
387
|
-
|
|
388
|
-
// Format size for human readability
|
|
389
|
-
const formatSize = (size) => {
|
|
390
|
-
if (size < 1024) return `${size}B`;
|
|
391
|
-
if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}K`;
|
|
392
|
-
if (size < 1024 * 1024 * 1024) return `${(size / (1024 * 1024)).toFixed(1)}M`;
|
|
393
|
-
return `${(size / (1024 * 1024 * 1024)).toFixed(1)}G`;
|
|
394
|
-
};
|
|
395
|
-
|
|
396
|
-
// Format the results as ls-style output
|
|
397
|
-
const entries = await Promise.all(files.map(async (file) => {
|
|
398
|
-
const isDirectory = file.isDirectory();
|
|
399
|
-
const fullPath = path.join(targetDir, file.name);
|
|
400
|
-
|
|
401
|
-
let size = 0;
|
|
402
|
-
try {
|
|
403
|
-
const stats = await fs.promises.stat(fullPath);
|
|
404
|
-
size = stats.size;
|
|
405
|
-
} catch (statError) {
|
|
406
|
-
if (debug) {
|
|
407
|
-
console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
|
|
411
|
-
return {
|
|
412
|
-
name: file.name,
|
|
413
|
-
isDirectory,
|
|
414
|
-
size
|
|
415
|
-
};
|
|
416
|
-
}));
|
|
417
|
-
|
|
418
|
-
// Sort: directories first, then files, both alphabetically
|
|
419
|
-
entries.sort((a, b) => {
|
|
420
|
-
if (a.isDirectory && !b.isDirectory) return -1;
|
|
421
|
-
if (!a.isDirectory && b.isDirectory) return 1;
|
|
422
|
-
return a.name.localeCompare(b.name);
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
// Format entries
|
|
426
|
-
const formatted = entries.map(entry => {
|
|
427
|
-
const type = entry.isDirectory ? 'dir ' : 'file';
|
|
428
|
-
const sizeStr = formatSize(entry.size).padStart(8);
|
|
429
|
-
return `${type} ${sizeStr} ${entry.name}`;
|
|
430
|
-
});
|
|
431
|
-
|
|
432
|
-
if (debug) {
|
|
433
|
-
console.log(`[DEBUG] Found ${entries.length} files/directories in ${targetDir}`);
|
|
434
|
-
}
|
|
435
|
-
|
|
436
|
-
// Return as formatted text output
|
|
437
|
-
const header = `${targetDir}:\n`;
|
|
438
|
-
const output = header + formatted.join('\n');
|
|
439
|
-
|
|
440
|
-
return output;
|
|
441
|
-
} catch (error) {
|
|
442
|
-
console.error(`Error listing files in ${targetDir}:`, error);
|
|
443
|
-
return `Error: ${error.message || 'Unknown error listing files'}`;
|
|
444
|
-
}
|
|
371
|
+
// Call the package tool with workingDirectory parameter
|
|
372
|
+
return packageListFilesToolInstance.execute({
|
|
373
|
+
...params,
|
|
374
|
+
directory: targetDirectory,
|
|
375
|
+
workingDirectory: currentWorkingDir
|
|
376
|
+
});
|
|
445
377
|
}
|
|
446
378
|
};
|
|
447
379
|
|
|
448
|
-
//
|
|
380
|
+
// Wrapper for searchFiles tool with ALLOWED_FOLDERS security
|
|
449
381
|
const baseSearchFilesTool = {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
type: 'object',
|
|
454
|
-
properties: {
|
|
455
|
-
pattern: {
|
|
456
|
-
type: 'string',
|
|
457
|
-
description: 'The glob pattern to search for (e.g., "**/*.js", "*.md")'
|
|
458
|
-
},
|
|
459
|
-
directory: {
|
|
460
|
-
type: 'string',
|
|
461
|
-
description: 'The directory to search in. Defaults to current directory if not specified.'
|
|
462
|
-
},
|
|
463
|
-
recursive: {
|
|
464
|
-
type: 'boolean',
|
|
465
|
-
description: 'Whether to search recursively. Defaults to true.'
|
|
466
|
-
}
|
|
467
|
-
},
|
|
468
|
-
required: ['pattern']
|
|
469
|
-
},
|
|
470
|
-
execute: async ({ pattern, directory, recursive = true, sessionId }) => {
|
|
471
|
-
// Ensure directory defaults to current directory
|
|
472
|
-
directory = directory || '.';
|
|
473
|
-
|
|
382
|
+
...packageSearchFilesToolInstance,
|
|
383
|
+
execute: async (params) => {
|
|
384
|
+
const { pattern, directory = '.', recursive = true, sessionId } = params;
|
|
474
385
|
const debug = process.env.DEBUG_CHAT === '1';
|
|
475
386
|
const currentWorkingDir = process.cwd();
|
|
476
|
-
|
|
387
|
+
|
|
477
388
|
// Get allowed folders from environment variable
|
|
478
389
|
const allowedFoldersEnv = process.env.ALLOWED_FOLDERS;
|
|
479
390
|
let allowedFolders = [];
|
|
480
|
-
|
|
391
|
+
|
|
481
392
|
if (allowedFoldersEnv) {
|
|
482
393
|
allowedFolders = allowedFoldersEnv.split(',').map(folder => folder.trim()).filter(folder => folder.length > 0);
|
|
483
394
|
}
|
|
@@ -492,13 +403,13 @@ const baseSearchFilesTool = {
|
|
|
492
403
|
}
|
|
493
404
|
}
|
|
494
405
|
|
|
495
|
-
const targetDir = path.resolve(currentWorkingDir, targetDirectory);
|
|
406
|
+
const targetDir = require('path').resolve(currentWorkingDir, targetDirectory);
|
|
496
407
|
|
|
497
408
|
// Validate that the target directory is within allowed folders
|
|
498
409
|
if (allowedFolders.length > 0) {
|
|
499
410
|
const isAllowed = allowedFolders.some(allowedFolder => {
|
|
500
|
-
const resolvedAllowedFolder = path.resolve(currentWorkingDir, allowedFolder);
|
|
501
|
-
return targetDir === resolvedAllowedFolder || targetDir.startsWith(resolvedAllowedFolder + path.sep);
|
|
411
|
+
const resolvedAllowedFolder = require('path').resolve(currentWorkingDir, allowedFolder);
|
|
412
|
+
return targetDir === resolvedAllowedFolder || targetDir.startsWith(resolvedAllowedFolder + require('path').sep);
|
|
502
413
|
});
|
|
503
414
|
|
|
504
415
|
if (!isAllowed) {
|
|
@@ -518,160 +429,34 @@ const baseSearchFilesTool = {
|
|
|
518
429
|
|
|
519
430
|
// Log execution parameters to stderr for visibility
|
|
520
431
|
console.error(`Executing searchFiles with params: pattern="${pattern}", directory="${targetDirectory}", recursive=${recursive}`);
|
|
521
|
-
console.error(`Resolved target directory: ${targetDir}`);
|
|
522
|
-
console.error(`Current working directory: ${currentWorkingDir}`);
|
|
523
|
-
|
|
524
|
-
if (debug) {
|
|
525
|
-
console.log(`[DEBUG] Searching for files with pattern: ${pattern}`);
|
|
526
|
-
console.log(`[DEBUG] In directory: ${targetDir}`);
|
|
527
|
-
console.log(`[DEBUG] Recursive: ${recursive}`);
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
// Validate pattern to prevent overly complex patterns
|
|
531
|
-
if (pattern.includes('**/**') || pattern.split('*').length > 10) {
|
|
532
|
-
console.error(`Pattern too complex: ${pattern}`);
|
|
533
|
-
return {
|
|
534
|
-
success: false,
|
|
535
|
-
directory: targetDir,
|
|
536
|
-
pattern: pattern,
|
|
537
|
-
error: 'Pattern too complex. Please use a simpler glob pattern.',
|
|
538
|
-
timestamp: new Date().toISOString()
|
|
539
|
-
};
|
|
540
|
-
}
|
|
541
432
|
|
|
542
433
|
try {
|
|
543
|
-
//
|
|
544
|
-
const
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
maxDepth: recursive ? 10 : 1, // Limit recursion depth
|
|
551
|
-
};
|
|
552
|
-
|
|
553
|
-
// If not recursive, modify the pattern to only search the top level
|
|
554
|
-
const searchPattern = recursive ? pattern : pattern.replace(/^\*\*\//, '');
|
|
555
|
-
|
|
556
|
-
console.error(`Starting glob search with pattern: ${searchPattern} in ${targetDir}`);
|
|
557
|
-
console.error(`Glob options: ${JSON.stringify(options)}`);
|
|
558
|
-
|
|
559
|
-
// Use a safer approach with manual file searching if the pattern is simple enough
|
|
560
|
-
let files = [];
|
|
561
|
-
|
|
562
|
-
// For simple patterns like "*.js" or "bin/*.js", use a more direct approach
|
|
563
|
-
if (pattern.includes('*') && !pattern.includes('**') && pattern.split('/').length <= 2) {
|
|
564
|
-
console.error(`Using direct file search for simple pattern: ${pattern}`);
|
|
565
|
-
|
|
566
|
-
try {
|
|
567
|
-
// Handle patterns like "dir/*.ext" or "*.ext"
|
|
568
|
-
const parts = pattern.split('/');
|
|
569
|
-
let searchDir = targetDir;
|
|
570
|
-
let filePattern;
|
|
571
|
-
|
|
572
|
-
if (parts.length === 2) {
|
|
573
|
-
// Pattern like "dir/*.ext"
|
|
574
|
-
searchDir = path.join(targetDir, parts[0]);
|
|
575
|
-
filePattern = parts[1];
|
|
576
|
-
} else {
|
|
577
|
-
// Pattern like "*.ext"
|
|
578
|
-
filePattern = parts[0];
|
|
579
|
-
}
|
|
580
|
-
|
|
581
|
-
console.error(`Searching in directory: ${searchDir} for files matching: ${filePattern}`);
|
|
582
|
-
|
|
583
|
-
// Check if directory exists
|
|
584
|
-
try {
|
|
585
|
-
await fsPromises.access(searchDir);
|
|
586
|
-
} catch (err) {
|
|
587
|
-
console.error(`Directory does not exist: ${searchDir}`);
|
|
588
|
-
console.error(`Falling back to search in parent directory: ${targetDir}`);
|
|
589
|
-
// Fall back to searching in the parent directory instead of failing
|
|
590
|
-
searchDir = targetDir;
|
|
591
|
-
// Adjust the pattern to include the subdirectory in the search
|
|
592
|
-
filePattern = pattern; // Use the original pattern for glob matching
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
// Read directory contents
|
|
596
|
-
const dirEntries = await fsPromises.readdir(searchDir, { withFileTypes: true });
|
|
597
|
-
|
|
598
|
-
// Convert glob pattern to regex
|
|
599
|
-
const regexPattern = filePattern
|
|
600
|
-
.replace(/\./g, '\\.')
|
|
601
|
-
.replace(/\*/g, '.*');
|
|
602
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
603
|
-
|
|
604
|
-
// Filter files based on pattern
|
|
605
|
-
files = dirEntries
|
|
606
|
-
.filter(entry => entry.isFile() && regex.test(entry.name))
|
|
607
|
-
.map(entry => {
|
|
608
|
-
const relativePath = parts.length === 2
|
|
609
|
-
? path.join(parts[0], entry.name)
|
|
610
|
-
: entry.name;
|
|
611
|
-
return relativePath;
|
|
612
|
-
});
|
|
613
|
-
|
|
614
|
-
console.error(`Direct search found ${files.length} files matching ${filePattern}`);
|
|
615
|
-
} catch (err) {
|
|
616
|
-
console.error(`Error in direct file search: ${err.message}`);
|
|
617
|
-
// Fall back to glob if direct search fails
|
|
618
|
-
console.error(`Falling back to glob search`);
|
|
619
|
-
|
|
620
|
-
// Create a promise that rejects after a timeout
|
|
621
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
622
|
-
setTimeout(() => reject(new Error('Search operation timed out after 10 seconds')), 10000);
|
|
623
|
-
});
|
|
624
|
-
|
|
625
|
-
// Use glob without promisify since it might already return a Promise
|
|
626
|
-
files = await Promise.race([
|
|
627
|
-
glob(searchPattern, options),
|
|
628
|
-
timeoutPromise
|
|
629
|
-
]);
|
|
630
|
-
}
|
|
631
|
-
} else {
|
|
632
|
-
console.error(`Using glob for complex pattern: ${pattern}`);
|
|
633
|
-
|
|
634
|
-
// Create a promise that rejects after a timeout
|
|
635
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
636
|
-
setTimeout(() => reject(new Error('Search operation timed out after 10 seconds')), 10000);
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
// Use glob without promisify since it might already return a Promise
|
|
640
|
-
files = await Promise.race([
|
|
641
|
-
glob(searchPattern, options),
|
|
642
|
-
timeoutPromise
|
|
643
|
-
]);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
console.error(`Search completed, found ${files.length} files in ${targetDir}`);
|
|
647
|
-
console.error(`Pattern: ${pattern}, Recursive: ${recursive}`);
|
|
434
|
+
// Call the package tool with workingDirectory parameter
|
|
435
|
+
const files = await packageSearchFilesToolInstance.execute({
|
|
436
|
+
...params,
|
|
437
|
+
directory: targetDirectory,
|
|
438
|
+
recursive,
|
|
439
|
+
workingDirectory: currentWorkingDir
|
|
440
|
+
});
|
|
648
441
|
|
|
649
442
|
if (debug) {
|
|
650
443
|
console.log(`[DEBUG] Found ${files.length} files matching pattern ${pattern}`);
|
|
651
444
|
}
|
|
652
445
|
|
|
653
|
-
//
|
|
654
|
-
const maxResults = 1000;
|
|
655
|
-
const limitedFiles = files.length > maxResults ? files.slice(0, maxResults) : files;
|
|
656
|
-
|
|
657
|
-
if (files.length > maxResults) {
|
|
658
|
-
console.warn(`Warning: Limited results to ${maxResults} files out of ${files.length} total matches`);
|
|
659
|
-
}
|
|
660
|
-
|
|
446
|
+
// Return in the expected format for backward compatibility
|
|
661
447
|
return {
|
|
662
448
|
success: true,
|
|
663
449
|
directory: targetDir,
|
|
664
450
|
pattern: pattern,
|
|
665
451
|
recursive: recursive,
|
|
666
|
-
files:
|
|
667
|
-
count:
|
|
452
|
+
files: files.map(file => require('path').join(targetDirectory, file)),
|
|
453
|
+
count: files.length,
|
|
668
454
|
totalMatches: files.length,
|
|
669
|
-
limited:
|
|
455
|
+
limited: false,
|
|
670
456
|
timestamp: new Date().toISOString()
|
|
671
457
|
};
|
|
672
458
|
} catch (error) {
|
|
673
459
|
console.error(`Error searching files with pattern "${pattern}" in ${targetDir}:`, error);
|
|
674
|
-
console.error(`Search parameters: directory="${targetDirectory}", recursive=${recursive}, sessionId=${sessionId}`);
|
|
675
460
|
return {
|
|
676
461
|
success: false,
|
|
677
462
|
directory: targetDir,
|
|
@@ -704,71 +489,3 @@ if (process.env.DEBUG_CHAT === '1') {
|
|
|
704
489
|
console.log('[DEBUG] ========================================\n');
|
|
705
490
|
}
|
|
706
491
|
|
|
707
|
-
// --- Backward Compatibility Layer (probeTool mapping to searchToolInstance) ---
|
|
708
|
-
// This might be less relevant if the AI is strictly using the new XML format,
|
|
709
|
-
// but keep it for potential direct API calls or older UI elements.
|
|
710
|
-
export const probeTool = {
|
|
711
|
-
...searchToolInstance, // Inherit schema description etc. from the wrapped search tool
|
|
712
|
-
name: "search", // Explicitly set name
|
|
713
|
-
description: 'DEPRECATED: Use <search> tool instead. Search code using keywords.',
|
|
714
|
-
// parameters: searchSchema, // Use the imported schema
|
|
715
|
-
execute: async (params) => { // Expects { keywords, folder, ..., sessionId }
|
|
716
|
-
const debug = process.env.DEBUG_CHAT === '1';
|
|
717
|
-
if (debug) {
|
|
718
|
-
console.log(`[DEBUG] probeTool (Compatibility Layer) executing for session ${params.sessionId}`);
|
|
719
|
-
}
|
|
720
|
-
|
|
721
|
-
// Map old params ('keywords', 'folder') to new ones ('query', 'path')
|
|
722
|
-
const { keywords, folder, sessionId, ...rest } = params;
|
|
723
|
-
const mappedParams = {
|
|
724
|
-
query: keywords,
|
|
725
|
-
path: folder || '.', // Default path if folder is missing
|
|
726
|
-
sessionId: sessionId, // Pass session ID through
|
|
727
|
-
...rest // Pass other params like allow_tests, maxResults etc.
|
|
728
|
-
};
|
|
729
|
-
|
|
730
|
-
if (debug) {
|
|
731
|
-
console.log("[DEBUG] probeTool mapped params: ", mappedParams);
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// Call the *wrapped* searchToolInstance execute function
|
|
735
|
-
// It will handle cancellation checks and event emitting internally
|
|
736
|
-
try {
|
|
737
|
-
// Note: The name emitted by searchToolInstance will be 'search', not 'probeTool' or 'searchCode'
|
|
738
|
-
const result = await searchToolInstance.execute(mappedParams);
|
|
739
|
-
|
|
740
|
-
// Format the result for backward compatibility if needed by caller
|
|
741
|
-
// The raw result from searchToolInstance is likely just the search results array/string
|
|
742
|
-
const formattedResult = {
|
|
743
|
-
results: result, // Assuming result is the direct data
|
|
744
|
-
command: `probe search --query "${keywords}" --path "${folder || '.'}"`, // Reconstruct approx command
|
|
745
|
-
timestamp: new Date().toISOString()
|
|
746
|
-
};
|
|
747
|
-
if (debug) {
|
|
748
|
-
console.log("[DEBUG] probeTool compatibility layer returning formatted result.");
|
|
749
|
-
}
|
|
750
|
-
return formattedResult;
|
|
751
|
-
|
|
752
|
-
} catch (error) {
|
|
753
|
-
if (debug) {
|
|
754
|
-
console.error(`[DEBUG] Error in probeTool compatibility layer:`, error);
|
|
755
|
-
}
|
|
756
|
-
// Error is already emitted by the wrapped searchToolInstance, just re-throw
|
|
757
|
-
throw error;
|
|
758
|
-
}
|
|
759
|
-
}
|
|
760
|
-
};
|
|
761
|
-
// Export necessary items
|
|
762
|
-
export { DEFAULT_SYSTEM_MESSAGE, listFilesByLevel };
|
|
763
|
-
// Export the tool generator functions if needed elsewhere
|
|
764
|
-
export { searchTool, queryTool, extractTool };
|
|
765
|
-
|
|
766
|
-
// Export capabilities information for the new tools
|
|
767
|
-
export const toolCapabilities = {
|
|
768
|
-
search: "Search code using keywords and patterns",
|
|
769
|
-
query: "Query code with structured parameters for more precise results",
|
|
770
|
-
extract: "Extract code blocks and context from files",
|
|
771
|
-
implement: "Implement features or fix bugs using aider (requires --allow-edit)",
|
|
772
|
-
listFiles: "List files and directories in a specified location",
|
|
773
|
-
searchFiles: "Find files matching a glob pattern with recursive search capability"
|
|
774
|
-
};
|
package/tools.js
DELETED
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
// This file might become obsolete or significantly simplified if tools
|
|
2
|
-
// are only defined/described in the system prompt and not passed to Vercel AI SDK.
|
|
3
|
-
|
|
4
|
-
// Let's comment it out for now, assuming it's not directly used in the new flow.
|
|
5
|
-
// If specific exports are needed elsewhere (like DEFAULT_SYSTEM_MESSAGE), they
|
|
6
|
-
// should be moved or imported directly from @probelabs/probe.
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
// Import tool generators and schemas from @probelabs/probe package
|
|
10
|
-
import {
|
|
11
|
-
searchTool,
|
|
12
|
-
queryTool,
|
|
13
|
-
extractTool,
|
|
14
|
-
DEFAULT_SYSTEM_MESSAGE,
|
|
15
|
-
attemptCompletionSchema,
|
|
16
|
-
attemptCompletionToolDefinition,
|
|
17
|
-
searchSchema,
|
|
18
|
-
querySchema,
|
|
19
|
-
extractSchema,
|
|
20
|
-
searchToolDefinition,
|
|
21
|
-
queryToolDefinition,
|
|
22
|
-
extractToolDefinition,
|
|
23
|
-
// Add the implement tool definition import if it exists in @probelabs/probe
|
|
24
|
-
// If not, we define it here. Assuming it's not in the package yet:
|
|
25
|
-
} from '@probelabs/probe';
|
|
26
|
-
import { randomUUID } from 'crypto';
|
|
27
|
-
|
|
28
|
-
// Generate a session ID
|
|
29
|
-
const sessionId = process.env.PROBE_SESSION_ID || randomUUID();
|
|
30
|
-
console.error(`Generated session ID for search caching: ${sessionId}`);
|
|
31
|
-
|
|
32
|
-
// Debug mode
|
|
33
|
-
const debug = process.env.DEBUG_CHAT === '1';
|
|
34
|
-
|
|
35
|
-
// Configure tools with the session ID
|
|
36
|
-
const configOptions = {
|
|
37
|
-
sessionId,
|
|
38
|
-
debug
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
// Create configured tool instances
|
|
42
|
-
export const tools = {
|
|
43
|
-
searchTool: searchTool(configOptions),
|
|
44
|
-
queryTool: queryTool(configOptions),
|
|
45
|
-
extractTool: extractTool(configOptions),
|
|
46
|
-
// Note: The actual implement tool *instance* comes from probeTool.js
|
|
47
|
-
// This file primarily deals with definitions for the system prompt.
|
|
48
|
-
};
|
|
49
|
-
|
|
50
|
-
// Export individual tools for direct use
|
|
51
|
-
export const { searchTool: searchToolInstance, queryTool: queryToolInstance, extractTool: extractToolInstance } = tools;
|
|
52
|
-
|
|
53
|
-
// For backward compatibility, export the original tool objects
|
|
54
|
-
export {
|
|
55
|
-
searchToolInstance as searchTool,
|
|
56
|
-
queryToolInstance as queryTool,
|
|
57
|
-
extractToolInstance as extractTool,
|
|
58
|
-
DEFAULT_SYSTEM_MESSAGE,
|
|
59
|
-
// Export schemas
|
|
60
|
-
searchSchema,
|
|
61
|
-
querySchema,
|
|
62
|
-
extractSchema,
|
|
63
|
-
attemptCompletionSchema,
|
|
64
|
-
// Export tool definitions
|
|
65
|
-
searchToolDefinition,
|
|
66
|
-
queryToolDefinition,
|
|
67
|
-
extractToolDefinition,
|
|
68
|
-
attemptCompletionToolDefinition,
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
// Define the implement tool XML definition
|
|
72
|
-
export const implementToolDefinition = `
|
|
73
|
-
## implement
|
|
74
|
-
Description: Implement a given task. Can modify files. Can be used ONLY if task explicitly stated that something requires modification or implementation.
|
|
75
|
-
|
|
76
|
-
Parameters:
|
|
77
|
-
- task: (required) The task description. Should be as detailed as possible, ideally pointing to exact files which needs be modified or created.
|
|
78
|
-
- autoCommits: (optional) Whether to enable auto-commits in aider. Default is false.
|
|
79
|
-
|
|
80
|
-
Usage Example:
|
|
81
|
-
|
|
82
|
-
<examples>
|
|
83
|
-
|
|
84
|
-
User: Can you implement a function to calculate Fibonacci numbers in main.js?
|
|
85
|
-
<implement>
|
|
86
|
-
<task>Implement a recursive function to calculate the nth Fibonacci number in main.js</task>
|
|
87
|
-
</implement>
|
|
88
|
-
|
|
89
|
-
User: Can you implement a function to calculate Fibonacci numbers in main.js with auto-commits?
|
|
90
|
-
<implement>
|
|
91
|
-
<task>Implement a recursive function to calculate the nth Fibonacci number in main.js</task>
|
|
92
|
-
<autoCommits>true</autoCommits>
|
|
93
|
-
</implement>
|
|
94
|
-
|
|
95
|
-
</examples>
|
|
96
|
-
`;
|
|
97
|
-
|
|
98
|
-
// Define the listFiles tool XML definition
|
|
99
|
-
export const listFilesToolDefinition = `
|
|
100
|
-
## listFiles
|
|
101
|
-
Description: List files and directories in a specified location.
|
|
102
|
-
|
|
103
|
-
Parameters:
|
|
104
|
-
- directory: (optional) The directory path to list files from. Defaults to current directory if not specified.
|
|
105
|
-
|
|
106
|
-
Usage Example:
|
|
107
|
-
|
|
108
|
-
<examples>
|
|
109
|
-
|
|
110
|
-
User: Can you list the files in the src directory?
|
|
111
|
-
<listFiles>
|
|
112
|
-
<directory>src</directory>
|
|
113
|
-
</listFiles>
|
|
114
|
-
|
|
115
|
-
User: What files are in the current directory?
|
|
116
|
-
<listFiles>
|
|
117
|
-
</listFiles>
|
|
118
|
-
|
|
119
|
-
</examples>
|
|
120
|
-
`;
|
|
121
|
-
|
|
122
|
-
// Define the searchFiles tool XML definition
|
|
123
|
-
export const searchFilesToolDefinition = `
|
|
124
|
-
## searchFiles
|
|
125
|
-
Description: Find files with name matching a glob pattern with recursive search capability.
|
|
126
|
-
|
|
127
|
-
Parameters:
|
|
128
|
-
- pattern: (required) The glob pattern to search for (e.g., "**/*.js", "*.md").
|
|
129
|
-
- directory: (optional) The directory to search in. Defaults to current directory if not specified.
|
|
130
|
-
- recursive: (optional) Whether to search recursively. Defaults to true.
|
|
131
|
-
|
|
132
|
-
Usage Example:
|
|
133
|
-
|
|
134
|
-
<examples>
|
|
135
|
-
|
|
136
|
-
User: Can you find all JavaScript files in the project?
|
|
137
|
-
<searchFiles>
|
|
138
|
-
<pattern>**/*.js</pattern>
|
|
139
|
-
</searchFiles>
|
|
140
|
-
|
|
141
|
-
User: Find all markdown files in the docs directory, but only at the top level.
|
|
142
|
-
<searchFiles>
|
|
143
|
-
<pattern>*.md</pattern>
|
|
144
|
-
<directory>docs</directory>
|
|
145
|
-
<recursive>false</recursive>
|
|
146
|
-
</searchFiles>
|
|
147
|
-
|
|
148
|
-
</examples>
|
|
149
|
-
`;
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
// Import the XML parser function from @probelabs/probe
|
|
153
|
-
import { parseXmlToolCall } from '@probelabs/probe';
|
|
154
|
-
|
|
155
|
-
// Re-export the original parseXmlToolCall
|
|
156
|
-
export { parseXmlToolCall };
|
|
157
|
-
|
|
158
|
-
/**
|
|
159
|
-
* Enhanced XML parser that handles thinking tags
|
|
160
|
-
* This function removes any <thinking></thinking> tags from the input string
|
|
161
|
-
* before passing it to the original parseXmlToolCall function
|
|
162
|
-
* @param {string} xmlString - The XML string to parse
|
|
163
|
-
* @returns {Object|null} - The parsed tool call or null if no valid tool call found
|
|
164
|
-
*/
|
|
165
|
-
export function parseXmlToolCallWithThinking(xmlString) {
|
|
166
|
-
// Extract thinking content if present (for potential logging or analysis)
|
|
167
|
-
const thinkingMatch = xmlString.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
168
|
-
const thinkingContent = thinkingMatch ? thinkingMatch[1].trim() : null;
|
|
169
|
-
|
|
170
|
-
// Remove thinking tags and their content from the XML string
|
|
171
|
-
const cleanedXmlString = xmlString.replace(/<thinking>[\s\S]*?<\/thinking>/g, '').trim();
|
|
172
|
-
|
|
173
|
-
// Use the original parseXmlToolCall function to parse the cleaned XML string
|
|
174
|
-
const parsedTool = parseXmlToolCall(cleanedXmlString);
|
|
175
|
-
|
|
176
|
-
// If debugging is enabled, log the thinking content
|
|
177
|
-
if (process.env.DEBUG_CHAT === '1' && thinkingContent) {
|
|
178
|
-
console.log(`[DEBUG] AI Thinking Process:\n${thinkingContent}`);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
return parsedTool;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// If tool instances are needed directly (e.g., for API endpoints bypassing the LLM loop),
|
|
185
|
-
// they are now created and exported from probeTool.js.
|
|
186
|
-
// We should ensure those are imported where needed.
|