@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe-chat",
3
- "version": "0.6.0-rc115",
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 { searchTool, queryTool, extractTool, DEFAULT_SYSTEM_MESSAGE, listFilesByLevel } from '@probelabs/probe';
3
- import { exec, spawn } from 'child_process';
4
- import { promisify } from 'util';
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(`[DEBUG] probeTool: Executing ${toolName} for session ${toolSessionId}`);
101
- console.log(`[DEBUG] probeTool: Received params:`, params);
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
- // Create the listFiles tool
327
+ // Wrapper for listFiles tool with ALLOWED_FOLDERS security
299
328
  const baseListFilesTool = {
300
- name: "listFiles",
301
- description: 'List files in a specified directory',
302
- inputSchema: {
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
- if (debug) {
358
- console.log(`[DEBUG] Listing files in directory: ${targetDir}`);
359
- }
360
-
361
- try {
362
- // Read the directory contents
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
- // Create the searchFiles tool
380
+ // Wrapper for searchFiles tool with ALLOWED_FOLDERS security
398
381
  const baseSearchFilesTool = {
399
- name: "searchFiles",
400
- description: 'Search for files using a glob pattern, recursively by default',
401
- inputSchema: {
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
- // Set glob options with timeout and limits
493
- const options = {
494
- cwd: targetDir,
495
- dot: true, // Include dotfiles
496
- nodir: true, // Only return files, not directories
497
- absolute: false, // Return paths relative to the search directory
498
- timeout: 10000, // 10 second timeout
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
- // Limit the number of results to prevent memory issues
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: limitedFiles.map(file => path.join(targetDirectory, file)),
616
- count: limitedFiles.length,
452
+ files: files.map(file => require('path').join(targetDirectory, file)),
453
+ count: files.length,
617
454
  totalMatches: files.length,
618
- limited: files.length > maxResults,
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
- // --- Backward Compatibility Layer (probeTool mapping to searchToolInstance) ---
644
- // This might be less relevant if the AI is strictly using the new XML format,
645
- // but keep it for potential direct API calls or older UI elements.
646
- export const probeTool = {
647
- ...searchToolInstance, // Inherit schema description etc. from the wrapped search tool
648
- name: "search", // Explicitly set name
649
- description: 'DEPRECATED: Use <search> tool instead. Search code using keywords.',
650
- // parameters: searchSchema, // Use the imported schema
651
- execute: async (params) => { // Expects { keywords, folder, ..., sessionId }
652
- const debug = process.env.DEBUG_CHAT === '1';
653
- if (debug) {
654
- console.log(`[DEBUG] probeTool (Compatibility Layer) executing for session ${params.sessionId}`);
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.