@probelabs/probe 0.6.0-rc210 → 0.6.0-rc212

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 (32) hide show
  1. package/bin/binaries/probe-v0.6.0-rc212-aarch64-apple-darwin.tar.gz +0 -0
  2. package/bin/binaries/probe-v0.6.0-rc212-aarch64-unknown-linux-musl.tar.gz +0 -0
  3. package/bin/binaries/probe-v0.6.0-rc212-x86_64-apple-darwin.tar.gz +0 -0
  4. package/bin/binaries/probe-v0.6.0-rc212-x86_64-pc-windows-msvc.zip +0 -0
  5. package/bin/binaries/probe-v0.6.0-rc212-x86_64-unknown-linux-musl.tar.gz +0 -0
  6. package/build/agent/ProbeAgent.js +19 -3
  7. package/build/agent/index.js +639 -75
  8. package/build/agent/probeTool.js +11 -2
  9. package/build/agent/tools.js +8 -0
  10. package/build/index.js +6 -1
  11. package/build/search.js +2 -2
  12. package/build/tools/analyzeAll.js +624 -0
  13. package/build/tools/common.js +149 -85
  14. package/build/tools/langchain.js +1 -1
  15. package/build/tools/vercel.js +61 -2
  16. package/cjs/agent/ProbeAgent.cjs +9698 -6756
  17. package/cjs/index.cjs +9702 -6754
  18. package/package.json +1 -1
  19. package/src/agent/ProbeAgent.js +19 -3
  20. package/src/agent/probeTool.js +11 -2
  21. package/src/agent/tools.js +8 -0
  22. package/src/index.js +6 -1
  23. package/src/search.js +2 -2
  24. package/src/tools/analyzeAll.js +624 -0
  25. package/src/tools/common.js +149 -85
  26. package/src/tools/langchain.js +1 -1
  27. package/src/tools/vercel.js +61 -2
  28. package/bin/binaries/probe-v0.6.0-rc210-aarch64-apple-darwin.tar.gz +0 -0
  29. package/bin/binaries/probe-v0.6.0-rc210-aarch64-unknown-linux-musl.tar.gz +0 -0
  30. package/bin/binaries/probe-v0.6.0-rc210-x86_64-apple-darwin.tar.gz +0 -0
  31. package/bin/binaries/probe-v0.6.0-rc210-x86_64-pc-windows-msvc.zip +0 -0
  32. package/bin/binaries/probe-v0.6.0-rc210-x86_64-unknown-linux-musl.tar.gz +0 -0
@@ -46,6 +46,11 @@ export const bashSchema = z.object({
46
46
  env: z.record(z.string()).optional().describe('Additional environment variables (optional)')
47
47
  });
48
48
 
49
+ export const analyzeAllSchema = z.object({
50
+ question: z.string().min(1).describe('Free-form question to answer (e.g., "What features are customers using?", "List all API endpoints"). The AI will automatically plan the search strategy, process all matching data, and synthesize a comprehensive answer.'),
51
+ path: z.string().optional().default('.').describe('Directory path to search in')
52
+ });
53
+
49
54
  // Schema for the attempt_completion tool - flexible validation for direct XML response
50
55
  export const attemptCompletionSchema = {
51
56
  // Custom validation that requires result parameter but allows direct XML response
@@ -273,6 +278,52 @@ I have refactored the search module according to the requirements and verified t
273
278
  </attempt_completion>
274
279
  `;
275
280
 
281
+ export const analyzeAllToolDefinition = `
282
+ ## analyze_all
283
+ Description: Intelligent bulk data analysis tool. Process ALL data matching your question using a 3-phase approach:
284
+ 1. **PLANNING**: AI analyzes your question and determines the optimal search strategy
285
+ 2. **PROCESSING**: Map-reduce processes all matching data in parallel chunks
286
+ 3. **SYNTHESIS**: Comprehensive answer with evidence and organization
287
+
288
+ **Use this for questions requiring 100% data coverage:**
289
+ - "What features are customers using?"
290
+ - "List all API endpoints in the codebase"
291
+ - "Summarize the error handling patterns"
292
+ - "Count all TODO comments and their contexts"
293
+
294
+ **Do NOT use for:**
295
+ - Simple searches where a sample is sufficient
296
+ - Finding a specific function or class
297
+ - Quick exploration (use search instead)
298
+
299
+ **WARNING:** Makes multiple LLM calls - slower and costlier than search.
300
+
301
+ Parameters:
302
+ - question: (required) Free-form question to answer - the AI determines the best search strategy automatically
303
+ - path: (optional) Directory to search in (default: current directory)
304
+
305
+ <examples>
306
+
307
+ User: What are all the different tools available in this codebase?
308
+ <analyze_all>
309
+ <question>What are all the different tools available in this codebase and what do they do?</question>
310
+ <path>./src</path>
311
+ </analyze_all>
312
+
313
+ User: I need to understand all the error handling patterns
314
+ <analyze_all>
315
+ <question>What error handling patterns are used throughout the codebase? Include examples.</question>
316
+ </analyze_all>
317
+
318
+ User: Count and categorize all the environment variables
319
+ <analyze_all>
320
+ <question>What environment variables are used? Categorize them by purpose.</question>
321
+ <path>./src</path>
322
+ </analyze_all>
323
+
324
+ </examples>
325
+ `;
326
+
276
327
  export const bashToolDefinition = `
277
328
  ## bash
278
329
  Description: Execute bash commands for system exploration and development tasks. This tool has built-in security with allow/deny lists. By default, only safe read-only commands are allowed for code exploration.
@@ -334,6 +385,7 @@ export const queryDescription = 'Search code using ast-grep structural pattern m
334
385
  export const extractDescription = 'Extract code blocks from files based on file paths and optional line numbers. Use this tool to see complete context after finding relevant files.';
335
386
  export const delegateDescription = 'Automatically delegate big distinct tasks to specialized probe subagents within the agentic loop. Used by AI agents to break down complex requests into focused, parallel tasks.';
336
387
  export const bashDescription = 'Execute bash commands for system exploration and development tasks. Secure by default with built-in allow/deny lists.';
388
+ export const analyzeAllDescription = 'Answer questions that require analyzing ALL matching data in the codebase. Use for aggregate questions like "What features exist?", "List all API endpoints", "Count TODO comments". The AI automatically plans the search strategy, processes all results via map-reduce, and synthesizes a comprehensive answer. WARNING: Slower than search - only use when you need complete coverage.';
337
389
 
338
390
  // Valid tool names that should be parsed as tool calls
339
391
  // This is the canonical list - all other tool lists should reference this
@@ -342,6 +394,7 @@ export const DEFAULT_VALID_TOOLS = [
342
394
  'query',
343
395
  'extract',
344
396
  'delegate',
397
+ 'analyze_all',
345
398
  'listSkills',
346
399
  'useSkill',
347
400
  'listFiles',
@@ -379,6 +432,7 @@ function getValidParamsForTool(toolName) {
379
432
  query: querySchema,
380
433
  extract: extractSchema,
381
434
  delegate: delegateSchema,
435
+ analyze_all: analyzeAllSchema,
382
436
  listSkills: listSkillsSchema,
383
437
  useSkill: useSkillSchema,
384
438
  bash: bashSchema,
@@ -416,115 +470,125 @@ function getValidParamsForTool(toolName) {
416
470
 
417
471
  // Simple XML parser helper - safer string-based approach
418
472
  export function parseXmlToolCall(xmlString, validTools = DEFAULT_VALID_TOOLS) {
419
- // Look for each valid tool name specifically using string search
473
+ // Find the tool that appears EARLIEST in the string
474
+ // This prevents parameter tags (like <query> inside <analyze_all>) from being matched as tools
475
+ let earliestToolName = null;
476
+ let earliestOpenIndex = Infinity;
477
+
420
478
  for (const toolName of validTools) {
421
479
  const openTag = `<${toolName}>`;
422
- const closeTag = `</${toolName}>`;
423
-
424
480
  const openIndex = xmlString.indexOf(openTag);
425
- if (openIndex === -1) {
426
- continue; // Tool not found, try next tool
481
+ if (openIndex !== -1 && openIndex < earliestOpenIndex) {
482
+ earliestOpenIndex = openIndex;
483
+ earliestToolName = toolName;
427
484
  }
485
+ }
428
486
 
429
- // For attempt_completion, use lastIndexOf to find the LAST occurrence of closing tag
430
- // This prevents issues where the content contains the closing tag string (e.g., in regex patterns)
431
- // For other tools, use indexOf from the opening tag position
432
- let closeIndex;
433
- if (toolName === 'attempt_completion') {
434
- // Find the last occurrence of the closing tag in the entire string
435
- // This assumes attempt_completion doesn't have nested tags of the same name
436
- closeIndex = xmlString.lastIndexOf(closeTag);
437
- // Make sure the closing tag is after the opening tag
438
- if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
439
- closeIndex = -1; // Invalid, treat as no closing tag
440
- }
441
- } else {
442
- closeIndex = xmlString.indexOf(closeTag, openIndex + openTag.length);
443
- }
487
+ // No valid tool found
488
+ if (earliestToolName === null) {
489
+ return null;
490
+ }
444
491
 
445
- let hasClosingTag = closeIndex !== -1;
492
+ const toolName = earliestToolName;
493
+ const openTag = `<${toolName}>`;
494
+ const closeTag = `</${toolName}>`;
495
+ const openIndex = earliestOpenIndex;
446
496
 
447
- // If no closing tag found, use content until end of string
448
- // This makes the parser more resilient to AI formatting errors
449
- if (closeIndex === -1) {
450
- closeIndex = xmlString.length;
497
+ // For attempt_completion, use lastIndexOf to find the LAST occurrence of closing tag
498
+ // This prevents issues where the content contains the closing tag string (e.g., in regex patterns)
499
+ // For other tools, use indexOf from the opening tag position
500
+ let closeIndex;
501
+ if (toolName === 'attempt_completion') {
502
+ // Find the last occurrence of the closing tag in the entire string
503
+ // This assumes attempt_completion doesn't have nested tags of the same name
504
+ closeIndex = xmlString.lastIndexOf(closeTag);
505
+ // Make sure the closing tag is after the opening tag
506
+ if (closeIndex !== -1 && closeIndex <= openIndex + openTag.length) {
507
+ closeIndex = -1; // Invalid, treat as no closing tag
451
508
  }
509
+ } else {
510
+ closeIndex = xmlString.indexOf(closeTag, openIndex + openTag.length);
511
+ }
452
512
 
453
- // Extract the content between tags (or until end if no closing tag)
454
- const innerContent = xmlString.substring(
455
- openIndex + openTag.length,
456
- closeIndex
457
- );
513
+ let hasClosingTag = closeIndex !== -1;
458
514
 
459
- const params = {};
515
+ // If no closing tag found, use content until end of string
516
+ // This makes the parser more resilient to AI formatting errors
517
+ if (closeIndex === -1) {
518
+ closeIndex = xmlString.length;
519
+ }
460
520
 
461
- // Get valid parameters for this specific tool from its schema
462
- const validParams = getValidParamsForTool(toolName);
521
+ // Extract the content between tags (or until end if no closing tag)
522
+ const innerContent = xmlString.substring(
523
+ openIndex + openTag.length,
524
+ closeIndex
525
+ );
463
526
 
464
- // Parse parameters using string-based approach for better safety
465
- // Only look for parameters that are valid for this specific tool
466
- for (const paramName of validParams) {
467
- const paramOpenTag = `<${paramName}>`;
468
- const paramCloseTag = `</${paramName}>`;
527
+ const params = {};
469
528
 
470
- const paramOpenIndex = innerContent.indexOf(paramOpenTag);
471
- if (paramOpenIndex === -1) {
472
- continue; // Parameter not found
473
- }
529
+ // Get valid parameters for this specific tool from its schema
530
+ const validParams = getValidParamsForTool(toolName);
474
531
 
475
- let paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
476
-
477
- // Handle unclosed parameter tags - use content until next tag or end of content
478
- if (paramCloseIndex === -1) {
479
- // Find the next opening tag after this parameter
480
- let nextTagIndex = innerContent.length;
481
- for (const nextParam of validParams) {
482
- const nextOpenTag = `<${nextParam}>`;
483
- const nextIndex = innerContent.indexOf(nextOpenTag, paramOpenIndex + paramOpenTag.length);
484
- if (nextIndex !== -1 && nextIndex < nextTagIndex) {
485
- nextTagIndex = nextIndex;
486
- }
487
- }
488
- paramCloseIndex = nextTagIndex;
489
- }
532
+ // Parse parameters using string-based approach for better safety
533
+ // Only look for parameters that are valid for this specific tool
534
+ for (const paramName of validParams) {
535
+ const paramOpenTag = `<${paramName}>`;
536
+ const paramCloseTag = `</${paramName}>`;
537
+
538
+ const paramOpenIndex = innerContent.indexOf(paramOpenTag);
539
+ if (paramOpenIndex === -1) {
540
+ continue; // Parameter not found
541
+ }
490
542
 
491
- let paramValue = innerContent.substring(
492
- paramOpenIndex + paramOpenTag.length,
493
- paramCloseIndex
494
- ).trim();
495
-
496
- // Basic type inference (can be improved)
497
- if (paramValue.toLowerCase() === 'true') {
498
- paramValue = true;
499
- } else if (paramValue.toLowerCase() === 'false') {
500
- paramValue = false;
501
- } else if (!isNaN(paramValue) && paramValue.trim() !== '') {
502
- // Check if it's potentially a number (handle integers and floats)
503
- const num = Number(paramValue);
504
- if (Number.isFinite(num)) { // Use Number.isFinite to avoid Infinity/NaN
505
- paramValue = num;
543
+ let paramCloseIndex = innerContent.indexOf(paramCloseTag, paramOpenIndex + paramOpenTag.length);
544
+
545
+ // Handle unclosed parameter tags - use content until next tag or end of content
546
+ if (paramCloseIndex === -1) {
547
+ // Find the next opening tag after this parameter
548
+ let nextTagIndex = innerContent.length;
549
+ for (const nextParam of validParams) {
550
+ const nextOpenTag = `<${nextParam}>`;
551
+ const nextIndex = innerContent.indexOf(nextOpenTag, paramOpenIndex + paramOpenTag.length);
552
+ if (nextIndex !== -1 && nextIndex < nextTagIndex) {
553
+ nextTagIndex = nextIndex;
506
554
  }
507
- // Keep as string if not a valid finite number
508
555
  }
509
-
510
- params[paramName] = paramValue;
556
+ paramCloseIndex = nextTagIndex;
511
557
  }
512
558
 
513
- // Special handling for attempt_completion - use entire inner content as result
514
- if (toolName === 'attempt_completion') {
515
- params['result'] = innerContent.trim();
516
- // Remove command parameter if it was parsed by generic logic above (legacy compatibility)
517
- if (params.command) {
518
- delete params.command;
559
+ let paramValue = innerContent.substring(
560
+ paramOpenIndex + paramOpenTag.length,
561
+ paramCloseIndex
562
+ ).trim();
563
+
564
+ // Basic type inference (can be improved)
565
+ if (paramValue.toLowerCase() === 'true') {
566
+ paramValue = true;
567
+ } else if (paramValue.toLowerCase() === 'false') {
568
+ paramValue = false;
569
+ } else if (!isNaN(paramValue) && paramValue.trim() !== '') {
570
+ // Check if it's potentially a number (handle integers and floats)
571
+ const num = Number(paramValue);
572
+ if (Number.isFinite(num)) { // Use Number.isFinite to avoid Infinity/NaN
573
+ paramValue = num;
519
574
  }
575
+ // Keep as string if not a valid finite number
520
576
  }
521
577
 
522
- // Return the first valid tool found
523
- return { toolName, params };
578
+ params[paramName] = paramValue;
524
579
  }
525
580
 
526
- // No valid tool found
527
- return null;
581
+ // Special handling for attempt_completion - use entire inner content as result
582
+ if (toolName === 'attempt_completion') {
583
+ params['result'] = innerContent.trim();
584
+ // Remove command parameter if it was parsed by generic logic above (legacy compatibility)
585
+ if (params.command) {
586
+ delete params.command;
587
+ }
588
+ }
589
+
590
+ // Return the parsed tool call
591
+ return { toolName, params };
528
592
  }
529
593
 
530
594
  /**
@@ -16,7 +16,7 @@ export function createSearchTool(options = {}) {
16
16
  name: 'search',
17
17
  description: searchDescription,
18
18
  schema: searchSchema,
19
- func: async ({ query: searchQuery, path, allow_tests, exact, maxResults, maxTokens = 10000, language }) => {
19
+ func: async ({ query: searchQuery, path, allow_tests, exact, maxResults, maxTokens = 20000, language }) => {
20
20
  try {
21
21
  const results = await search({
22
22
  query: searchQuery,
@@ -8,7 +8,8 @@ import { search } from '../search.js';
8
8
  import { query } from '../query.js';
9
9
  import { extract } from '../extract.js';
10
10
  import { delegate } from '../delegate.js';
11
- import { searchSchema, querySchema, extractSchema, delegateSchema, searchDescription, queryDescription, extractDescription, delegateDescription, parseTargets, parseAndResolvePaths, resolveTargetPath } from './common.js';
11
+ import { analyzeAll } from './analyzeAll.js';
12
+ import { searchSchema, querySchema, extractSchema, delegateSchema, analyzeAllSchema, searchDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, parseTargets, parseAndResolvePaths, resolveTargetPath } from './common.js';
12
13
  import { formatErrorForAI } from '../utils/error-types.js';
13
14
 
14
15
  const CODE_SEARCH_SCHEMA = {
@@ -156,7 +157,7 @@ function buildSearchDelegateTask({ searchQuery, searchPath, exact, language, all
156
157
  export const searchTool = (options = {}) => {
157
158
  const {
158
159
  sessionId,
159
- maxTokens = 10000,
160
+ maxTokens = 20000,
160
161
  debug = false,
161
162
  outline = false,
162
163
  searchDelegate = false
@@ -568,3 +569,61 @@ export const delegateTool = (options = {}) => {
568
569
  }
569
570
  });
570
571
  };
572
+
573
+ /**
574
+ * Analyze All tool generator - intelligent 3-phase analysis using map-reduce
575
+ *
576
+ * @param {Object} [options] - Configuration options
577
+ * @param {string} [options.sessionId] - Session ID for caching
578
+ * @param {boolean} [options.debug=false] - Enable debug logging
579
+ * @param {string} [options.cwd] - Working directory
580
+ * @param {string[]} [options.allowedFolders] - Allowed folders
581
+ * @param {string} [options.provider] - AI provider
582
+ * @param {string} [options.model] - AI model
583
+ * @param {Object} [options.tracer] - Telemetry tracer
584
+ * @returns {Object} Configured analyze_all tool
585
+ */
586
+ export const analyzeAllTool = (options = {}) => {
587
+ const { sessionId, debug = false } = options;
588
+
589
+ return tool({
590
+ name: 'analyze_all',
591
+ description: analyzeAllDescription,
592
+ inputSchema: analyzeAllSchema,
593
+ execute: async ({ question, path }) => {
594
+ try {
595
+ // Parse and resolve path if provided
596
+ let searchPath = path || '.';
597
+ if (path && options.cwd) {
598
+ const resolvedPaths = parseAndResolvePaths(path, options.cwd);
599
+ if (resolvedPaths.length > 0) {
600
+ searchPath = resolvedPaths[0];
601
+ }
602
+ }
603
+
604
+ if (debug) {
605
+ console.error(`[analyze_all] Starting analysis`);
606
+ console.error(`[analyze_all] Question: ${question}`);
607
+ console.error(`[analyze_all] Path: ${searchPath}`);
608
+ }
609
+
610
+ const result = await analyzeAll({
611
+ question,
612
+ path: searchPath,
613
+ sessionId,
614
+ debug,
615
+ cwd: options.cwd,
616
+ allowedFolders: options.allowedFolders,
617
+ provider: options.provider,
618
+ model: options.model,
619
+ tracer: options.tracer
620
+ });
621
+
622
+ return result;
623
+ } catch (error) {
624
+ console.error('Error executing analyze_all:', error);
625
+ return formatErrorForAI(error);
626
+ }
627
+ }
628
+ });
629
+ };