@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.
Files changed (3) hide show
  1. package/package.json +1 -2
  2. package/probeTool.js +45 -328
  3. 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-rc116",
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 { 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';
@@ -323,28 +324,18 @@ const baseImplementTool = {
323
324
  }
324
325
  };
325
326
 
326
- // Create the listFiles tool
327
+ // Wrapper for listFiles tool with ALLOWED_FOLDERS security
327
328
  const baseListFilesTool = {
328
- name: "listFiles",
329
- description: 'List files in a specified directory',
330
- inputSchema: {
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
- if (debug) {
381
- console.log(`[DEBUG] Listing files in directory: ${targetDir}`);
382
- }
383
-
384
- try {
385
- // Read the directory contents
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
- // Create the searchFiles tool
380
+ // Wrapper for searchFiles tool with ALLOWED_FOLDERS security
449
381
  const baseSearchFilesTool = {
450
- name: "searchFiles",
451
- description: 'Search for files using a glob pattern, recursively by default',
452
- inputSchema: {
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
- // Set glob options with timeout and limits
544
- const options = {
545
- cwd: targetDir,
546
- dot: true, // Include dotfiles
547
- nodir: true, // Only return files, not directories
548
- absolute: false, // Return paths relative to the search directory
549
- timeout: 10000, // 10 second timeout
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
- // Limit the number of results to prevent memory issues
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: limitedFiles.map(file => path.join(targetDirectory, file)),
667
- count: limitedFiles.length,
452
+ files: files.map(file => require('path').join(targetDirectory, file)),
453
+ count: files.length,
668
454
  totalMatches: files.length,
669
- limited: files.length > maxResults,
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.