@probelabs/probe-chat 0.6.0-rc115 → 0.6.0-rc117
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/probeChat.js +46 -0
- package/probeTool.js +88 -307
- 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-rc117",
|
|
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/probeChat.js
CHANGED
|
@@ -275,9 +275,55 @@ export class ProbeChat {
|
|
|
275
275
|
|
|
276
276
|
if (this.debug) {
|
|
277
277
|
console.log(`[DEBUG] ProbeChat initialized with MCP ${agentOptions.enableMcp ? 'enabled' : 'disabled'}`);
|
|
278
|
+
|
|
279
|
+
// Log available tools after a short delay to allow MCP initialization
|
|
280
|
+
setTimeout(() => {
|
|
281
|
+
this.logAvailableTools();
|
|
282
|
+
}, 100);
|
|
278
283
|
}
|
|
279
284
|
}
|
|
280
285
|
|
|
286
|
+
/**
|
|
287
|
+
* Log all available tools (native + MCP) in debug mode
|
|
288
|
+
*/
|
|
289
|
+
logAvailableTools() {
|
|
290
|
+
if (!this.debug) return;
|
|
291
|
+
|
|
292
|
+
console.log('\n[DEBUG] ========================================');
|
|
293
|
+
console.log('[DEBUG] All Available Tools:');
|
|
294
|
+
console.log('[DEBUG] ========================================');
|
|
295
|
+
|
|
296
|
+
// Get native tools from agent
|
|
297
|
+
if (this.agent.toolImplementations) {
|
|
298
|
+
console.log('[DEBUG] Native Tools:');
|
|
299
|
+
const nativeTools = Object.keys(this.agent.toolImplementations);
|
|
300
|
+
nativeTools.forEach(toolName => {
|
|
301
|
+
const tool = this.agent.toolImplementations[toolName];
|
|
302
|
+
const desc = tool.description || 'No description';
|
|
303
|
+
console.log(`[DEBUG] - ${toolName}: ${desc}`);
|
|
304
|
+
});
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Get MCP tools if available
|
|
308
|
+
if (this.agent.mcpBridge && this.agent.mcpBridge.mcpTools) {
|
|
309
|
+
const mcpTools = Object.keys(this.agent.mcpBridge.mcpTools);
|
|
310
|
+
if (mcpTools.length > 0) {
|
|
311
|
+
console.log('[DEBUG] MCP Tools:');
|
|
312
|
+
mcpTools.forEach(toolName => {
|
|
313
|
+
const tool = this.agent.mcpBridge.mcpTools[toolName];
|
|
314
|
+
const desc = tool.description || 'No description';
|
|
315
|
+
console.log(`[DEBUG] - ${toolName}: ${desc}`);
|
|
316
|
+
});
|
|
317
|
+
} else {
|
|
318
|
+
console.log('[DEBUG] MCP Tools: None loaded');
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
console.log('[DEBUG] MCP Tools: MCP not enabled or not initialized');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
console.log('[DEBUG] ========================================\n');
|
|
325
|
+
}
|
|
326
|
+
|
|
281
327
|
/**
|
|
282
328
|
* Answer a question using the agentic flow with optional image support
|
|
283
329
|
* @param {string} message - The user's question
|
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';
|
|
@@ -81,6 +82,27 @@ const configOptions = {
|
|
|
81
82
|
debug: process.env.DEBUG_CHAT === '1'
|
|
82
83
|
};
|
|
83
84
|
|
|
85
|
+
// Helper function to truncate long argument values for logging
|
|
86
|
+
function truncateArgValue(value, maxLength = 200) {
|
|
87
|
+
if (typeof value !== 'string') {
|
|
88
|
+
value = JSON.stringify(value);
|
|
89
|
+
}
|
|
90
|
+
if (value.length <= maxLength * 2) {
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
// Show first 200 and last 200 characters
|
|
94
|
+
return `${value.substring(0, maxLength)}...${value.substring(value.length - maxLength)}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Helper function to format tool arguments for debug logging
|
|
98
|
+
function formatToolArgs(args) {
|
|
99
|
+
const formatted = {};
|
|
100
|
+
for (const [key, value] of Object.entries(args)) {
|
|
101
|
+
formatted[key] = truncateArgValue(value);
|
|
102
|
+
}
|
|
103
|
+
return formatted;
|
|
104
|
+
}
|
|
105
|
+
|
|
84
106
|
// Create the base tools using the imported generators
|
|
85
107
|
const baseSearchTool = searchTool(configOptions);
|
|
86
108
|
const baseQueryTool = queryTool(configOptions);
|
|
@@ -97,8 +119,15 @@ const wrapToolWithEmitter = (tool, toolName, baseExecute) => {
|
|
|
97
119
|
const toolSessionId = params.sessionId || defaultSessionId; // Fallback, but should always have sessionId
|
|
98
120
|
|
|
99
121
|
if (debug) {
|
|
100
|
-
console.log(
|
|
101
|
-
console.log(`[DEBUG]
|
|
122
|
+
console.log(`\n[DEBUG] ========================================`);
|
|
123
|
+
console.log(`[DEBUG] Tool Call: ${toolName}`);
|
|
124
|
+
console.log(`[DEBUG] Session: ${toolSessionId}`);
|
|
125
|
+
console.log(`[DEBUG] Arguments:`);
|
|
126
|
+
const formattedArgs = formatToolArgs(params);
|
|
127
|
+
for (const [key, value] of Object.entries(formattedArgs)) {
|
|
128
|
+
console.log(`[DEBUG] ${key}: ${value}`);
|
|
129
|
+
}
|
|
130
|
+
console.log(`[DEBUG] ========================================\n`);
|
|
102
131
|
}
|
|
103
132
|
|
|
104
133
|
// Register this tool execution (and reset cancel flag if needed)
|
|
@@ -295,28 +324,18 @@ const baseImplementTool = {
|
|
|
295
324
|
}
|
|
296
325
|
};
|
|
297
326
|
|
|
298
|
-
//
|
|
327
|
+
// Wrapper for listFiles tool with ALLOWED_FOLDERS security
|
|
299
328
|
const baseListFilesTool = {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
type: 'object',
|
|
304
|
-
properties: {
|
|
305
|
-
directory: {
|
|
306
|
-
type: 'string',
|
|
307
|
-
description: 'The directory path to list files from. Defaults to current directory if not specified.'
|
|
308
|
-
}
|
|
309
|
-
},
|
|
310
|
-
required: []
|
|
311
|
-
},
|
|
312
|
-
execute: async ({ directory = '.', sessionId }) => {
|
|
329
|
+
...packageListFilesToolInstance,
|
|
330
|
+
execute: async (params) => {
|
|
331
|
+
const { directory = '.', sessionId } = params;
|
|
313
332
|
const debug = process.env.DEBUG_CHAT === '1';
|
|
314
333
|
const currentWorkingDir = process.cwd();
|
|
315
|
-
|
|
334
|
+
|
|
316
335
|
// Get allowed folders from environment variable
|
|
317
336
|
const allowedFoldersEnv = process.env.ALLOWED_FOLDERS;
|
|
318
337
|
let allowedFolders = [];
|
|
319
|
-
|
|
338
|
+
|
|
320
339
|
if (allowedFoldersEnv) {
|
|
321
340
|
allowedFolders = allowedFoldersEnv.split(',').map(folder => folder.trim()).filter(folder => folder.length > 0);
|
|
322
341
|
}
|
|
@@ -331,13 +350,13 @@ const baseListFilesTool = {
|
|
|
331
350
|
}
|
|
332
351
|
}
|
|
333
352
|
|
|
334
|
-
const targetDir = path.resolve(currentWorkingDir, targetDirectory);
|
|
353
|
+
const targetDir = require('path').resolve(currentWorkingDir, targetDirectory);
|
|
335
354
|
|
|
336
355
|
// Validate that the target directory is within allowed folders
|
|
337
356
|
if (allowedFolders.length > 0) {
|
|
338
357
|
const isAllowed = allowedFolders.some(allowedFolder => {
|
|
339
|
-
const resolvedAllowedFolder = path.resolve(currentWorkingDir, allowedFolder);
|
|
340
|
-
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);
|
|
341
360
|
});
|
|
342
361
|
|
|
343
362
|
if (!isAllowed) {
|
|
@@ -345,88 +364,31 @@ const baseListFilesTool = {
|
|
|
345
364
|
if (debug) {
|
|
346
365
|
console.log(`[DEBUG] ${error}`);
|
|
347
366
|
}
|
|
348
|
-
return {
|
|
349
|
-
success: false,
|
|
350
|
-
directory: targetDir,
|
|
351
|
-
error: error,
|
|
352
|
-
timestamp: new Date().toISOString()
|
|
353
|
-
};
|
|
367
|
+
return `Error: ${error}`;
|
|
354
368
|
}
|
|
355
369
|
}
|
|
356
370
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
const files = await fs.promises.readdir(targetDir, { withFileTypes: true });
|
|
364
|
-
|
|
365
|
-
// Format the results
|
|
366
|
-
const result = files.map(file => {
|
|
367
|
-
const isDirectory = file.isDirectory();
|
|
368
|
-
return {
|
|
369
|
-
name: file.name,
|
|
370
|
-
type: isDirectory ? 'directory' : 'file',
|
|
371
|
-
path: path.join(targetDirectory, file.name)
|
|
372
|
-
};
|
|
373
|
-
});
|
|
374
|
-
|
|
375
|
-
if (debug) {
|
|
376
|
-
console.log(`[DEBUG] Found ${result.length} files/directories in ${targetDir}`);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
return {
|
|
380
|
-
success: true,
|
|
381
|
-
directory: targetDir,
|
|
382
|
-
files: result,
|
|
383
|
-
timestamp: new Date().toISOString()
|
|
384
|
-
};
|
|
385
|
-
} catch (error) {
|
|
386
|
-
console.error(`Error listing files in ${targetDir}:`, error);
|
|
387
|
-
return {
|
|
388
|
-
success: false,
|
|
389
|
-
directory: targetDir,
|
|
390
|
-
error: error.message || 'Unknown error listing files',
|
|
391
|
-
timestamp: new Date().toISOString()
|
|
392
|
-
};
|
|
393
|
-
}
|
|
371
|
+
// Call the package tool with workingDirectory parameter
|
|
372
|
+
return packageListFilesToolInstance.execute({
|
|
373
|
+
...params,
|
|
374
|
+
directory: targetDirectory,
|
|
375
|
+
workingDirectory: currentWorkingDir
|
|
376
|
+
});
|
|
394
377
|
}
|
|
395
378
|
};
|
|
396
379
|
|
|
397
|
-
//
|
|
380
|
+
// Wrapper for searchFiles tool with ALLOWED_FOLDERS security
|
|
398
381
|
const baseSearchFilesTool = {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
type: 'object',
|
|
403
|
-
properties: {
|
|
404
|
-
pattern: {
|
|
405
|
-
type: 'string',
|
|
406
|
-
description: 'The glob pattern to search for (e.g., "**/*.js", "*.md")'
|
|
407
|
-
},
|
|
408
|
-
directory: {
|
|
409
|
-
type: 'string',
|
|
410
|
-
description: 'The directory to search in. Defaults to current directory if not specified.'
|
|
411
|
-
},
|
|
412
|
-
recursive: {
|
|
413
|
-
type: 'boolean',
|
|
414
|
-
description: 'Whether to search recursively. Defaults to true.'
|
|
415
|
-
}
|
|
416
|
-
},
|
|
417
|
-
required: ['pattern']
|
|
418
|
-
},
|
|
419
|
-
execute: async ({ pattern, directory, recursive = true, sessionId }) => {
|
|
420
|
-
// Ensure directory defaults to current directory
|
|
421
|
-
directory = directory || '.';
|
|
422
|
-
|
|
382
|
+
...packageSearchFilesToolInstance,
|
|
383
|
+
execute: async (params) => {
|
|
384
|
+
const { pattern, directory = '.', recursive = true, sessionId } = params;
|
|
423
385
|
const debug = process.env.DEBUG_CHAT === '1';
|
|
424
386
|
const currentWorkingDir = process.cwd();
|
|
425
|
-
|
|
387
|
+
|
|
426
388
|
// Get allowed folders from environment variable
|
|
427
389
|
const allowedFoldersEnv = process.env.ALLOWED_FOLDERS;
|
|
428
390
|
let allowedFolders = [];
|
|
429
|
-
|
|
391
|
+
|
|
430
392
|
if (allowedFoldersEnv) {
|
|
431
393
|
allowedFolders = allowedFoldersEnv.split(',').map(folder => folder.trim()).filter(folder => folder.length > 0);
|
|
432
394
|
}
|
|
@@ -441,13 +403,13 @@ const baseSearchFilesTool = {
|
|
|
441
403
|
}
|
|
442
404
|
}
|
|
443
405
|
|
|
444
|
-
const targetDir = path.resolve(currentWorkingDir, targetDirectory);
|
|
406
|
+
const targetDir = require('path').resolve(currentWorkingDir, targetDirectory);
|
|
445
407
|
|
|
446
408
|
// Validate that the target directory is within allowed folders
|
|
447
409
|
if (allowedFolders.length > 0) {
|
|
448
410
|
const isAllowed = allowedFolders.some(allowedFolder => {
|
|
449
|
-
const resolvedAllowedFolder = path.resolve(currentWorkingDir, allowedFolder);
|
|
450
|
-
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);
|
|
451
413
|
});
|
|
452
414
|
|
|
453
415
|
if (!isAllowed) {
|
|
@@ -467,160 +429,34 @@ const baseSearchFilesTool = {
|
|
|
467
429
|
|
|
468
430
|
// Log execution parameters to stderr for visibility
|
|
469
431
|
console.error(`Executing searchFiles with params: pattern="${pattern}", directory="${targetDirectory}", recursive=${recursive}`);
|
|
470
|
-
console.error(`Resolved target directory: ${targetDir}`);
|
|
471
|
-
console.error(`Current working directory: ${currentWorkingDir}`);
|
|
472
|
-
|
|
473
|
-
if (debug) {
|
|
474
|
-
console.log(`[DEBUG] Searching for files with pattern: ${pattern}`);
|
|
475
|
-
console.log(`[DEBUG] In directory: ${targetDir}`);
|
|
476
|
-
console.log(`[DEBUG] Recursive: ${recursive}`);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
// Validate pattern to prevent overly complex patterns
|
|
480
|
-
if (pattern.includes('**/**') || pattern.split('*').length > 10) {
|
|
481
|
-
console.error(`Pattern too complex: ${pattern}`);
|
|
482
|
-
return {
|
|
483
|
-
success: false,
|
|
484
|
-
directory: targetDir,
|
|
485
|
-
pattern: pattern,
|
|
486
|
-
error: 'Pattern too complex. Please use a simpler glob pattern.',
|
|
487
|
-
timestamp: new Date().toISOString()
|
|
488
|
-
};
|
|
489
|
-
}
|
|
490
432
|
|
|
491
433
|
try {
|
|
492
|
-
//
|
|
493
|
-
const
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
maxDepth: recursive ? 10 : 1, // Limit recursion depth
|
|
500
|
-
};
|
|
501
|
-
|
|
502
|
-
// If not recursive, modify the pattern to only search the top level
|
|
503
|
-
const searchPattern = recursive ? pattern : pattern.replace(/^\*\*\//, '');
|
|
504
|
-
|
|
505
|
-
console.error(`Starting glob search with pattern: ${searchPattern} in ${targetDir}`);
|
|
506
|
-
console.error(`Glob options: ${JSON.stringify(options)}`);
|
|
507
|
-
|
|
508
|
-
// Use a safer approach with manual file searching if the pattern is simple enough
|
|
509
|
-
let files = [];
|
|
510
|
-
|
|
511
|
-
// For simple patterns like "*.js" or "bin/*.js", use a more direct approach
|
|
512
|
-
if (pattern.includes('*') && !pattern.includes('**') && pattern.split('/').length <= 2) {
|
|
513
|
-
console.error(`Using direct file search for simple pattern: ${pattern}`);
|
|
514
|
-
|
|
515
|
-
try {
|
|
516
|
-
// Handle patterns like "dir/*.ext" or "*.ext"
|
|
517
|
-
const parts = pattern.split('/');
|
|
518
|
-
let searchDir = targetDir;
|
|
519
|
-
let filePattern;
|
|
520
|
-
|
|
521
|
-
if (parts.length === 2) {
|
|
522
|
-
// Pattern like "dir/*.ext"
|
|
523
|
-
searchDir = path.join(targetDir, parts[0]);
|
|
524
|
-
filePattern = parts[1];
|
|
525
|
-
} else {
|
|
526
|
-
// Pattern like "*.ext"
|
|
527
|
-
filePattern = parts[0];
|
|
528
|
-
}
|
|
529
|
-
|
|
530
|
-
console.error(`Searching in directory: ${searchDir} for files matching: ${filePattern}`);
|
|
531
|
-
|
|
532
|
-
// Check if directory exists
|
|
533
|
-
try {
|
|
534
|
-
await fsPromises.access(searchDir);
|
|
535
|
-
} catch (err) {
|
|
536
|
-
console.error(`Directory does not exist: ${searchDir}`);
|
|
537
|
-
console.error(`Falling back to search in parent directory: ${targetDir}`);
|
|
538
|
-
// Fall back to searching in the parent directory instead of failing
|
|
539
|
-
searchDir = targetDir;
|
|
540
|
-
// Adjust the pattern to include the subdirectory in the search
|
|
541
|
-
filePattern = pattern; // Use the original pattern for glob matching
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
// Read directory contents
|
|
545
|
-
const dirEntries = await fsPromises.readdir(searchDir, { withFileTypes: true });
|
|
546
|
-
|
|
547
|
-
// Convert glob pattern to regex
|
|
548
|
-
const regexPattern = filePattern
|
|
549
|
-
.replace(/\./g, '\\.')
|
|
550
|
-
.replace(/\*/g, '.*');
|
|
551
|
-
const regex = new RegExp(`^${regexPattern}$`);
|
|
552
|
-
|
|
553
|
-
// Filter files based on pattern
|
|
554
|
-
files = dirEntries
|
|
555
|
-
.filter(entry => entry.isFile() && regex.test(entry.name))
|
|
556
|
-
.map(entry => {
|
|
557
|
-
const relativePath = parts.length === 2
|
|
558
|
-
? path.join(parts[0], entry.name)
|
|
559
|
-
: entry.name;
|
|
560
|
-
return relativePath;
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
console.error(`Direct search found ${files.length} files matching ${filePattern}`);
|
|
564
|
-
} catch (err) {
|
|
565
|
-
console.error(`Error in direct file search: ${err.message}`);
|
|
566
|
-
// Fall back to glob if direct search fails
|
|
567
|
-
console.error(`Falling back to glob search`);
|
|
568
|
-
|
|
569
|
-
// Create a promise that rejects after a timeout
|
|
570
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
571
|
-
setTimeout(() => reject(new Error('Search operation timed out after 10 seconds')), 10000);
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
// Use glob without promisify since it might already return a Promise
|
|
575
|
-
files = await Promise.race([
|
|
576
|
-
glob(searchPattern, options),
|
|
577
|
-
timeoutPromise
|
|
578
|
-
]);
|
|
579
|
-
}
|
|
580
|
-
} else {
|
|
581
|
-
console.error(`Using glob for complex pattern: ${pattern}`);
|
|
582
|
-
|
|
583
|
-
// Create a promise that rejects after a timeout
|
|
584
|
-
const timeoutPromise = new Promise((_, reject) => {
|
|
585
|
-
setTimeout(() => reject(new Error('Search operation timed out after 10 seconds')), 10000);
|
|
586
|
-
});
|
|
587
|
-
|
|
588
|
-
// Use glob without promisify since it might already return a Promise
|
|
589
|
-
files = await Promise.race([
|
|
590
|
-
glob(searchPattern, options),
|
|
591
|
-
timeoutPromise
|
|
592
|
-
]);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
console.error(`Search completed, found ${files.length} files in ${targetDir}`);
|
|
596
|
-
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
|
+
});
|
|
597
441
|
|
|
598
442
|
if (debug) {
|
|
599
443
|
console.log(`[DEBUG] Found ${files.length} files matching pattern ${pattern}`);
|
|
600
444
|
}
|
|
601
445
|
|
|
602
|
-
//
|
|
603
|
-
const maxResults = 1000;
|
|
604
|
-
const limitedFiles = files.length > maxResults ? files.slice(0, maxResults) : files;
|
|
605
|
-
|
|
606
|
-
if (files.length > maxResults) {
|
|
607
|
-
console.warn(`Warning: Limited results to ${maxResults} files out of ${files.length} total matches`);
|
|
608
|
-
}
|
|
609
|
-
|
|
446
|
+
// Return in the expected format for backward compatibility
|
|
610
447
|
return {
|
|
611
448
|
success: true,
|
|
612
449
|
directory: targetDir,
|
|
613
450
|
pattern: pattern,
|
|
614
451
|
recursive: recursive,
|
|
615
|
-
files:
|
|
616
|
-
count:
|
|
452
|
+
files: files.map(file => require('path').join(targetDirectory, file)),
|
|
453
|
+
count: files.length,
|
|
617
454
|
totalMatches: files.length,
|
|
618
|
-
limited:
|
|
455
|
+
limited: false,
|
|
619
456
|
timestamp: new Date().toISOString()
|
|
620
457
|
};
|
|
621
458
|
} catch (error) {
|
|
622
459
|
console.error(`Error searching files with pattern "${pattern}" in ${targetDir}:`, error);
|
|
623
|
-
console.error(`Search parameters: directory="${targetDirectory}", recursive=${recursive}, sessionId=${sessionId}`);
|
|
624
460
|
return {
|
|
625
461
|
success: false,
|
|
626
462
|
directory: targetDir,
|
|
@@ -640,71 +476,16 @@ export const implementToolInstance = wrapToolWithEmitter(baseImplementTool, 'imp
|
|
|
640
476
|
export const listFilesToolInstance = wrapToolWithEmitter(baseListFilesTool, 'listFiles', baseListFilesTool.execute);
|
|
641
477
|
export const searchFilesToolInstance = wrapToolWithEmitter(baseSearchFilesTool, 'searchFiles', baseSearchFilesTool.execute);
|
|
642
478
|
|
|
643
|
-
//
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
}
|
|
656
|
-
|
|
657
|
-
// Map old params ('keywords', 'folder') to new ones ('query', 'path')
|
|
658
|
-
const { keywords, folder, sessionId, ...rest } = params;
|
|
659
|
-
const mappedParams = {
|
|
660
|
-
query: keywords,
|
|
661
|
-
path: folder || '.', // Default path if folder is missing
|
|
662
|
-
sessionId: sessionId, // Pass session ID through
|
|
663
|
-
...rest // Pass other params like allow_tests, maxResults etc.
|
|
664
|
-
};
|
|
665
|
-
|
|
666
|
-
if (debug) {
|
|
667
|
-
console.log("[DEBUG] probeTool mapped params: ", mappedParams);
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
// Call the *wrapped* searchToolInstance execute function
|
|
671
|
-
// It will handle cancellation checks and event emitting internally
|
|
672
|
-
try {
|
|
673
|
-
// Note: The name emitted by searchToolInstance will be 'search', not 'probeTool' or 'searchCode'
|
|
674
|
-
const result = await searchToolInstance.execute(mappedParams);
|
|
675
|
-
|
|
676
|
-
// Format the result for backward compatibility if needed by caller
|
|
677
|
-
// The raw result from searchToolInstance is likely just the search results array/string
|
|
678
|
-
const formattedResult = {
|
|
679
|
-
results: result, // Assuming result is the direct data
|
|
680
|
-
command: `probe search --query "${keywords}" --path "${folder || '.'}"`, // Reconstruct approx command
|
|
681
|
-
timestamp: new Date().toISOString()
|
|
682
|
-
};
|
|
683
|
-
if (debug) {
|
|
684
|
-
console.log("[DEBUG] probeTool compatibility layer returning formatted result.");
|
|
685
|
-
}
|
|
686
|
-
return formattedResult;
|
|
479
|
+
// Log available tools at startup in debug mode
|
|
480
|
+
if (process.env.DEBUG_CHAT === '1') {
|
|
481
|
+
console.log('\n[DEBUG] ========================================');
|
|
482
|
+
console.log('[DEBUG] Probe Tools Loaded:');
|
|
483
|
+
console.log('[DEBUG] - search: Search for code patterns');
|
|
484
|
+
console.log('[DEBUG] - query: Semantic code search');
|
|
485
|
+
console.log('[DEBUG] - extract: Extract code snippets');
|
|
486
|
+
console.log('[DEBUG] - implement: Generate code implementations');
|
|
487
|
+
console.log('[DEBUG] - listFiles: List directory contents');
|
|
488
|
+
console.log('[DEBUG] - searchFiles: Search files by pattern');
|
|
489
|
+
console.log('[DEBUG] ========================================\n');
|
|
490
|
+
}
|
|
687
491
|
|
|
688
|
-
} catch (error) {
|
|
689
|
-
if (debug) {
|
|
690
|
-
console.error(`[DEBUG] Error in probeTool compatibility layer:`, error);
|
|
691
|
-
}
|
|
692
|
-
// Error is already emitted by the wrapped searchToolInstance, just re-throw
|
|
693
|
-
throw error;
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
};
|
|
697
|
-
// Export necessary items
|
|
698
|
-
export { DEFAULT_SYSTEM_MESSAGE, listFilesByLevel };
|
|
699
|
-
// Export the tool generator functions if needed elsewhere
|
|
700
|
-
export { searchTool, queryTool, extractTool };
|
|
701
|
-
|
|
702
|
-
// Export capabilities information for the new tools
|
|
703
|
-
export const toolCapabilities = {
|
|
704
|
-
search: "Search code using keywords and patterns",
|
|
705
|
-
query: "Query code with structured parameters for more precise results",
|
|
706
|
-
extract: "Extract code blocks and context from files",
|
|
707
|
-
implement: "Implement features or fix bugs using aider (requires --allow-edit)",
|
|
708
|
-
listFiles: "List files and directories in a specified location",
|
|
709
|
-
searchFiles: "Find files matching a glob pattern with recursive search capability"
|
|
710
|
-
};
|
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.
|