@probelabs/probe 0.6.0-rc307 → 0.6.0-rc309

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 (30) hide show
  1. package/README.md +25 -0
  2. package/bin/binaries/{probe-v0.6.0-rc307-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc309-aarch64-apple-darwin.tar.gz} +0 -0
  3. package/bin/binaries/{probe-v0.6.0-rc307-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc309-aarch64-unknown-linux-musl.tar.gz} +0 -0
  4. package/bin/binaries/{probe-v0.6.0-rc307-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc309-x86_64-apple-darwin.tar.gz} +0 -0
  5. package/bin/binaries/{probe-v0.6.0-rc307-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc309-x86_64-pc-windows-msvc.zip} +0 -0
  6. package/bin/binaries/{probe-v0.6.0-rc307-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc309-x86_64-unknown-linux-musl.tar.gz} +0 -0
  7. package/build/agent/ProbeAgent.js +17 -4
  8. package/build/agent/probeTool.js +9 -0
  9. package/build/agent/shared/prompts.js +4 -1
  10. package/build/agent/tools.js +6 -0
  11. package/build/index.js +6 -1
  12. package/build/symbols.js +86 -0
  13. package/build/tools/common.js +4 -0
  14. package/build/tools/index.js +2 -1
  15. package/build/tools/system-message.js +4 -0
  16. package/build/tools/vercel.js +36 -1
  17. package/cjs/agent/ProbeAgent.cjs +180 -40
  18. package/cjs/index.cjs +188 -40
  19. package/index.d.ts +63 -0
  20. package/package.json +1 -1
  21. package/src/agent/ProbeAgent.js +17 -4
  22. package/src/agent/probeTool.js +9 -0
  23. package/src/agent/shared/prompts.js +4 -1
  24. package/src/agent/tools.js +6 -0
  25. package/src/index.js +6 -1
  26. package/src/symbols.js +86 -0
  27. package/src/tools/common.js +4 -0
  28. package/src/tools/index.js +2 -1
  29. package/src/tools/system-message.js +4 -0
  30. package/src/tools/vercel.js +36 -1
package/README.md CHANGED
@@ -23,6 +23,7 @@ During installation, the package will automatically download the appropriate pro
23
23
  - **Search Code**: Search for patterns in your codebase using Elasticsearch-like query syntax
24
24
  - **Query Code**: Find specific code structures using tree-sitter patterns
25
25
  - **Extract Code**: Extract code blocks from files based on file paths and line numbers
26
+ - **List Symbols**: Get a file's table of contents — functions, classes, constants with line numbers and nesting
26
27
  - **Edit Code**: AI-powered code editing with text replacement (fuzzy matching), AST-aware symbol replace/insert across 16 languages, and line-targeted editing with optional hash-based integrity verification
27
28
  - **AI Tools Integration**: Ready-to-use tools for Vercel AI SDK, LangChain, and other AI frameworks
28
29
  - **System Message**: Default system message for AI assistants with instructions on using probe tools
@@ -297,6 +298,7 @@ const agent5 = new ProbeAgent({
297
298
  - `search` - Semantic code search
298
299
  - `query` - Tree-sitter pattern matching
299
300
  - `extract` - Extract code blocks
301
+ - `symbols` - List all symbols in a file with line numbers
300
302
  - `listFiles` - List files and directories
301
303
  - `searchFiles` - Find files by glob pattern
302
304
  - `bash` - Execute bash commands (requires `enableBash: true`)
@@ -709,6 +711,29 @@ const results = await extract({
709
711
  - `forceDownload`: Force download even if binary exists
710
712
  - `version`: Specific version to download
711
713
 
714
+ ### Symbols
715
+
716
+ ```javascript
717
+ import { symbols } from '@probelabs/probe';
718
+
719
+ const result = await symbols({
720
+ files: ['src/main.rs'],
721
+ cwd: '/path/to/project',
722
+ allowTests: false
723
+ });
724
+
725
+ // result is an array of FileSymbols
726
+ // result[0].file => 'src/main.rs'
727
+ // result[0].symbols => [{ name, kind, signature, line, end_line, children }, ...]
728
+ ```
729
+
730
+ #### Parameters
731
+
732
+ - `files` (required): Array of file paths to list symbols from
733
+ - `cwd`: Working directory for resolving relative paths
734
+ - `allowTests`: Include test functions/methods (default: false)
735
+ - `binaryOptions`: Options for getting the binary
736
+
712
737
  ### Binary Management
713
738
 
714
739
  ```javascript
@@ -56,6 +56,7 @@ import {
56
56
  searchFilesSchema,
57
57
  readImageSchema,
58
58
  readMediaSchema,
59
+ symbolsSchema,
59
60
  listSkillsSchema,
60
61
  useSkillSchema
61
62
  } from './tools.js';
@@ -945,6 +946,9 @@ export class ProbeAgent {
945
946
  if (isToolAllowed('searchFiles')) {
946
947
  this.toolImplementations.searchFiles = searchFilesToolInstance;
947
948
  }
949
+ if (wrappedTools.symbolsToolInstance && isToolAllowed('symbols')) {
950
+ this.toolImplementations.symbols = wrappedTools.symbolsToolInstance;
951
+ }
948
952
 
949
953
  if (this.enableSkills) {
950
954
  const registry = this._getSkillsRegistry();
@@ -2225,6 +2229,10 @@ export class ProbeAgent {
2225
2229
  schema: searchFilesSchema,
2226
2230
  description: 'Find files matching a glob pattern with recursive search capability.'
2227
2231
  },
2232
+ symbols: {
2233
+ schema: symbolsSchema,
2234
+ description: 'List all symbols (functions, classes, structs, constants, etc.) in a file. Returns a hierarchical tree with line numbers — like a table of contents for code.'
2235
+ },
2228
2236
  readMedia: {
2229
2237
  schema: readMediaSchema,
2230
2238
  description: 'Read and load a media file (image or PDF document) for AI analysis. Supports: png, jpg, jpeg, webp, bmp, svg, pdf.'
@@ -3019,7 +3027,8 @@ export class ProbeAgent {
3019
3027
  ${searchToolDesc1}
3020
3028
  - extract: Extract specific code sections with context
3021
3029
  - listFiles: Browse directory contents
3022
- - searchFiles: Find files by name patterns`;
3030
+ - searchFiles: Find files by name patterns
3031
+ - symbols: List all symbols in a file (functions, classes, constants, etc.) with line numbers`;
3023
3032
 
3024
3033
  if (this.enableBash) {
3025
3034
  systemPrompt += `\n- bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) — always use search and extract tools instead, they are faster and more accurate.`;
@@ -3085,7 +3094,8 @@ ${extractGuidance1}
3085
3094
  ${searchToolDesc2}
3086
3095
  - extract: Extract specific code sections with context
3087
3096
  - listFiles: Browse directory contents
3088
- - searchFiles: Find files by name patterns`;
3097
+ - searchFiles: Find files by name patterns
3098
+ - symbols: List all symbols in a file (functions, classes, constants, etc.) with line numbers`;
3089
3099
 
3090
3100
  if (this.enableBash) {
3091
3101
  systemPrompt += `\n- bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) — always use search and extract tools instead, they are faster and more accurate.`;
@@ -3814,10 +3824,13 @@ or
3814
3824
  const jsonStr = responseText.replace(/^```(?:json)?\s*/, '').replace(/\s*```$/, '');
3815
3825
  const decision = JSON.parse(jsonStr);
3816
3826
 
3827
+ let grantedMs = 0;
3828
+ let grantedMin = 0;
3829
+
3817
3830
  if (decision.extend && decision.minutes > 0) {
3818
3831
  const requestedMs = Math.min(decision.minutes, maxPerReqMin) * 60000;
3819
- const grantedMs = Math.min(requestedMs, remainingBudgetMs, negotiatedTimeoutState.maxPerRequestMs);
3820
- const grantedMin = Math.round(grantedMs / 60000 * 10) / 10;
3832
+ grantedMs = Math.min(requestedMs, remainingBudgetMs, negotiatedTimeoutState.maxPerRequestMs);
3833
+ grantedMin = Math.round(grantedMs / 60000 * 10) / 10;
3821
3834
 
3822
3835
  // Update state
3823
3836
  negotiatedTimeoutState.extensionsUsed++;
@@ -265,6 +265,15 @@ export function createWrappedTools(baseTools) {
265
265
  );
266
266
  }
267
267
 
268
+ // Wrap symbols tool
269
+ if (baseTools.symbolsTool) {
270
+ wrappedTools.symbolsToolInstance = wrapToolWithEmitter(
271
+ baseTools.symbolsTool,
272
+ 'symbols',
273
+ baseTools.symbolsTool.execute
274
+ );
275
+ }
276
+
268
277
  return wrappedTools;
269
278
  }
270
279
 
@@ -12,6 +12,7 @@ CRITICAL - ALWAYS search before answering:
12
12
  You must NEVER answer questions about the codebase from memory or general knowledge. ALWAYS use the search and extract tools first to find the actual code, then base your answer ONLY on what you found. Even if you think you know the answer, you MUST verify it against the actual code. Your answers must be grounded in code evidence, not assumptions.
13
13
 
14
14
  When exploring code:
15
+ - Use the symbols tool to get a quick overview of a file's structure (functions, classes, constants) before diving into details with extract
15
16
  - Provide clear, concise explanations based on user request
16
17
  - Find and highlight the most relevant code snippets, if required
17
18
  - Trace function calls and data flow through the system — follow the FULL call chain, not just the entry point
@@ -43,6 +44,7 @@ You think like a code explorer — you understand that codebases have layers:
43
44
 
44
45
  When searching:
45
46
  - Search for the MAIN concept first, then think: "what RELATED subsystems would a real codebase have?"
47
+ - Use symbols to get a file's table of contents (functions, classes, constants with line numbers) before extracting — it helps you pick the right symbols to extract
46
48
  - Use extract to READ the code you find — look for function calls, type references, and imports that point to OTHER relevant code
47
49
  - If you find middleware, check: are there org-level or tenant-level variants?
48
50
  - If you find algorithms, check: are there different storage backends?
@@ -90,6 +92,7 @@ If the solution is clear, you can jump to implementation right away. If not, ask
90
92
  - Do not add code comments unless the logic is genuinely complex and non-obvious.
91
93
 
92
94
  # Before Implementation
95
+ - Use symbols to get a quick overview of file structure (functions, classes, constants with line numbers) before reading or editing files.
93
96
  - Read tests first — find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
94
97
  - Read neighboring files — understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
95
98
  - Trace the call chain — follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
@@ -126,7 +129,7 @@ Use the right tool:
126
129
  1. To MODIFY existing code → \`edit\` tool (old_string → new_string, or start_line/end_line)
127
130
  2. To CREATE a new file → \`create\` tool
128
131
  3. To CHANGE multiple files at once → \`multi_edit\` tool
129
- 4. To READ code → \`extract\` or \`search\` tools
132
+ 4. To READ code → \`extract\` or \`search\` tools. Use \`symbols\` to get a file's table of contents (functions, classes, constants with line numbers) before extracting.
130
133
  5. If \`edit\` fails with "file has not been read yet" → use \`extract\` with the EXACT same file path you will pass to \`edit\`. Relative vs absolute path mismatch causes this error. Use the same path format consistently. If it still fails, use bash \`cat\` to read the file, then use \`create\` to write the entire modified file. Do NOT fall back to sed.
131
134
 
132
135
  Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands, git operations, and read-only file inspection (cat, head, tail). sed/awk should ONLY be used for trivial non-code tasks (e.g., config file tweaks) where the replacement is a simple literal string swap.
@@ -5,6 +5,7 @@ import {
5
5
  extractTool,
6
6
  delegateTool,
7
7
  analyzeAllTool,
8
+ symbolsTool,
8
9
  createExecutePlanTool,
9
10
  createCleanupExecutePlanTool,
10
11
  bashTool,
@@ -27,6 +28,7 @@ import {
27
28
  searchFilesSchema,
28
29
  readImageSchema,
29
30
  readMediaSchema,
31
+ symbolsSchema,
30
32
  listSkillsSchema,
31
33
  useSkillSchema
32
34
  } from '../index.js';
@@ -52,6 +54,9 @@ export function createTools(configOptions) {
52
54
  if (isToolAllowed('extract')) {
53
55
  tools.extractTool = extractTool(configOptions);
54
56
  }
57
+ if (isToolAllowed('symbols')) {
58
+ tools.symbolsTool = symbolsTool(configOptions);
59
+ }
55
60
  if (configOptions.enableDelegate && isToolAllowed('delegate')) {
56
61
  tools.delegateTool = delegateTool(configOptions);
57
62
  }
@@ -111,6 +116,7 @@ export {
111
116
  searchFilesSchema,
112
117
  readImageSchema,
113
118
  readMediaSchema,
119
+ symbolsSchema,
114
120
  listSkillsSchema,
115
121
  useSkillSchema
116
122
  };
package/build/index.js CHANGED
@@ -14,6 +14,7 @@ dotenv.config();
14
14
  import { search } from './search.js';
15
15
  import { query } from './query.js';
16
16
  import { extract } from './extract.js';
17
+ import { symbols } from './symbols.js';
17
18
  import { grep } from './grep.js';
18
19
  import { delegate } from './delegate.js';
19
20
  import { getBinaryPath, setBinaryPath } from './utils.js';
@@ -33,6 +34,7 @@ import {
33
34
  searchFilesSchema,
34
35
  readImageSchema,
35
36
  readMediaSchema,
37
+ symbolsSchema,
36
38
  listSkillsSchema,
37
39
  useSkillSchema
38
40
  } from './tools/common.js';
@@ -41,7 +43,7 @@ import {
41
43
  createSchema,
42
44
  multiEditSchema
43
45
  } from './tools/edit.js';
44
- import { searchTool, queryTool, extractTool, delegateTool, analyzeAllTool } from './tools/vercel.js';
46
+ import { searchTool, queryTool, extractTool, delegateTool, analyzeAllTool, symbolsTool } from './tools/vercel.js';
45
47
  import { createExecutePlanTool, createCleanupExecutePlanTool } from './tools/executePlan.js';
46
48
  import { bashTool } from './tools/bash.js';
47
49
  import { editTool, createTool, multiEditTool } from './tools/edit.js';
@@ -62,6 +64,7 @@ export {
62
64
  search,
63
65
  query,
64
66
  extract,
67
+ symbols,
65
68
  grep,
66
69
  delegate,
67
70
  getBinaryPath,
@@ -91,6 +94,7 @@ export {
91
94
  extractTool,
92
95
  delegateTool,
93
96
  analyzeAllTool,
97
+ symbolsTool,
94
98
  createExecutePlanTool,
95
99
  createCleanupExecutePlanTool,
96
100
  bashTool,
@@ -117,6 +121,7 @@ export {
117
121
  searchFilesSchema,
118
122
  readImageSchema,
119
123
  readMediaSchema,
124
+ symbolsSchema,
120
125
  listSkillsSchema,
121
126
  useSkillSchema,
122
127
  // Export task management
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Symbols functionality for the probe package
3
+ * @module symbols
4
+ */
5
+
6
+ import { spawn } from 'child_process';
7
+ import { getBinaryPath, escapeString } from './utils.js';
8
+ import { validateCwdPath } from './utils/path-validation.js';
9
+
10
+ /**
11
+ * List symbols (functions, structs, classes, constants, etc.) in files
12
+ *
13
+ * @param {Object} options - Symbols options
14
+ * @param {string[]} options.files - Files to list symbols from
15
+ * @param {string} [options.cwd] - Working directory for resolving relative file paths
16
+ * @param {boolean} [options.allowTests] - Include test functions/methods
17
+ * @param {Object} [options.binaryOptions] - Options for getting the binary
18
+ * @returns {Promise<Object[]>} - Array of FileSymbols objects (parsed JSON)
19
+ * @throws {Error} If the command fails
20
+ */
21
+ export async function symbols(options) {
22
+ if (!options) {
23
+ throw new Error('Options object is required');
24
+ }
25
+
26
+ if (!options.files || !Array.isArray(options.files) || options.files.length === 0) {
27
+ throw new Error('At least one file path is required');
28
+ }
29
+
30
+ const binaryPath = await getBinaryPath(options.binaryOptions || {});
31
+ const cwd = await validateCwdPath(options.cwd);
32
+
33
+ const args = ['symbols', '--format', 'json'];
34
+
35
+ if (options.allowTests) {
36
+ args.push('--allow-tests');
37
+ }
38
+
39
+ for (const file of options.files) {
40
+ args.push(escapeString(file));
41
+ }
42
+
43
+ if (process.env.DEBUG === '1') {
44
+ console.error(`\nSymbols: files="${options.files.join(', ')}" cwd="${cwd}"`);
45
+ }
46
+
47
+ return new Promise((resolve, reject) => {
48
+ const childProcess = spawn(binaryPath, args, {
49
+ stdio: ['pipe', 'pipe', 'pipe'],
50
+ cwd
51
+ });
52
+
53
+ let stdout = '';
54
+ let stderr = '';
55
+
56
+ childProcess.stdout.on('data', (data) => {
57
+ stdout += data.toString();
58
+ });
59
+
60
+ childProcess.stderr.on('data', (data) => {
61
+ stderr += data.toString();
62
+ });
63
+
64
+ childProcess.on('close', (code) => {
65
+ if (stderr && process.env.DEBUG === '1') {
66
+ console.error(`stderr: ${stderr}`);
67
+ }
68
+
69
+ if (code !== 0) {
70
+ reject(new Error(`Symbols command failed with exit code ${code}: ${stderr}`));
71
+ return;
72
+ }
73
+
74
+ try {
75
+ const result = JSON.parse(stdout);
76
+ resolve(result);
77
+ } catch (error) {
78
+ reject(new Error(`Failed to parse symbols output: ${error.message}. Output: ${stdout.substring(0, 200)}`));
79
+ }
80
+ });
81
+
82
+ childProcess.on('error', (error) => {
83
+ reject(new Error(`Failed to spawn symbols process: ${error.message}`));
84
+ });
85
+ });
86
+ }
@@ -72,6 +72,10 @@ export const readMediaSchema = z.object({
72
72
  path: z.string().describe('Path to the media file to read. Supports images (png, jpg, jpeg, webp, bmp, svg) and documents (pdf).')
73
73
  });
74
74
 
75
+ export const symbolsSchema = z.object({
76
+ file: z.string().describe('Path to the file to list symbols from. Returns a hierarchical tree of functions, classes, structs, constants, etc. with line numbers and nesting (e.g., methods inside classes/impl blocks).')
77
+ });
78
+
75
79
  export const bashSchema = z.object({
76
80
  command: z.string().describe('The bash command to execute'),
77
81
  workingDirectory: z.string().optional().describe('Directory to execute the command in (optional)'),
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  // Export Vercel AI SDK tool generators
7
- export { searchTool, queryTool, extractTool, delegateTool } from './vercel.js';
7
+ export { searchTool, queryTool, extractTool, delegateTool, symbolsTool } from './vercel.js';
8
8
  export { bashTool } from './bash.js';
9
9
  export { editTool, createTool, multiEditTool } from './edit.js';
10
10
 
@@ -31,6 +31,7 @@ export {
31
31
  searchFilesSchema,
32
32
  readImageSchema,
33
33
  readMediaSchema,
34
+ symbolsSchema,
34
35
  listSkillsSchema,
35
36
  useSkillSchema
36
37
  } from './common.js';
@@ -33,6 +33,10 @@ You are Probe, a specialized code intelligence assistant. Your objective is to a
33
33
  * **Purpose:** Retrieve specific code blocks or entire files *after* \`search\` or \`query\` identifies the target.
34
34
  * **Syntax:** Optional \`#symbol\` (e.g., \`#MyClass\`), \`#Lstart-Lend\` (e.g., \`#L50-L75\`).
35
35
  * **Mandatory Argument:** \`path\` (specific file path, e.g., \`"src/utils/helpers.go"\`, or dependency file like \`"go:github.com/gin-gonic/gin/context.go"\`).
36
+ * \`symbols\`
37
+ * **Purpose:** List all symbols (functions, classes, structs, constants, etc.) in a file — a table of contents with line numbers and nesting.
38
+ * **Syntax:** \`file\` (path to the file to list symbols from).
39
+ * **Use When:** You need to understand a file's structure before extracting specific parts, or to find the right symbol name/line number for \`extract\`.
36
40
 
37
41
  [Examples]
38
42
 
@@ -7,9 +7,10 @@ import { tool, generateText } from 'ai';
7
7
  import { search } from '../search.js';
8
8
  import { query } from '../query.js';
9
9
  import { extract } from '../extract.js';
10
+ import { symbols } from '../symbols.js';
10
11
  import { delegate } from '../delegate.js';
11
12
  import { analyzeAll } from './analyzeAll.js';
12
- import { searchSchema, searchDelegateSchema, querySchema, extractSchema, delegateSchema, analyzeAllSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, parseTargets, parseAndResolvePaths, resolveTargetPath } from './common.js';
13
+ import { searchSchema, searchDelegateSchema, querySchema, extractSchema, symbolsSchema, delegateSchema, analyzeAllSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, analyzeAllDescription, parseTargets, parseAndResolvePaths, resolveTargetPath } from './common.js';
13
14
  import { existsSync } from 'fs';
14
15
  import { formatErrorForAI } from '../utils/error-types.js';
15
16
  import { annotateOutputWithHashes } from './hashline.js';
@@ -1344,3 +1345,37 @@ export const analyzeAllTool = (options = {}) => {
1344
1345
  }
1345
1346
  });
1346
1347
  };
1348
+
1349
+ export const symbolsTool = (options = {}) => {
1350
+ return tool({
1351
+ name: 'symbols',
1352
+ description: 'List all symbols (functions, classes, structs, constants, etc.) in a file. Returns a hierarchical tree with line numbers — like a table of contents for code files.',
1353
+ inputSchema: symbolsSchema,
1354
+ execute: async ({ file }) => {
1355
+ try {
1356
+ let filePath = file;
1357
+ if (options.cwd) {
1358
+ const resolvedPaths = parseAndResolvePaths(file, options.cwd);
1359
+ if (resolvedPaths.length > 0) {
1360
+ filePath = resolvedPaths[0];
1361
+ }
1362
+ }
1363
+
1364
+ const result = await symbols({
1365
+ files: [filePath],
1366
+ cwd: options.cwd,
1367
+ binaryOptions: options.binaryOptions
1368
+ });
1369
+
1370
+ // Schema accepts single file, so return first result directly
1371
+ if (result && result.length > 0) {
1372
+ return JSON.stringify(result[0], null, 2);
1373
+ }
1374
+ return JSON.stringify({ file, symbols: [] }, null, 2);
1375
+ } catch (error) {
1376
+ console.error('Error executing symbols:', error);
1377
+ return formatErrorForAI(error);
1378
+ }
1379
+ }
1380
+ });
1381
+ };