@probelabs/probe 0.6.0-rc308 → 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-rc308-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-rc308-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-rc308-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-rc308-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-rc308-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 +12 -2
  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 +167 -29
  18. package/cjs/index.cjs +175 -29
  19. package/index.d.ts +63 -0
  20. package/package.json +1 -1
  21. package/src/agent/ProbeAgent.js +12 -2
  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/cjs/index.cjs CHANGED
@@ -2187,6 +2187,70 @@ var init_extract = __esm({
2187
2187
  }
2188
2188
  });
2189
2189
 
2190
+ // src/symbols.js
2191
+ async function symbols(options) {
2192
+ if (!options) {
2193
+ throw new Error("Options object is required");
2194
+ }
2195
+ if (!options.files || !Array.isArray(options.files) || options.files.length === 0) {
2196
+ throw new Error("At least one file path is required");
2197
+ }
2198
+ const binaryPath = await getBinaryPath(options.binaryOptions || {});
2199
+ const cwd = await validateCwdPath(options.cwd);
2200
+ const args = ["symbols", "--format", "json"];
2201
+ if (options.allowTests) {
2202
+ args.push("--allow-tests");
2203
+ }
2204
+ for (const file2 of options.files) {
2205
+ args.push(escapeString(file2));
2206
+ }
2207
+ if (process.env.DEBUG === "1") {
2208
+ console.error(`
2209
+ Symbols: files="${options.files.join(", ")}" cwd="${cwd}"`);
2210
+ }
2211
+ return new Promise((resolve9, reject2) => {
2212
+ const childProcess = (0, import_child_process5.spawn)(binaryPath, args, {
2213
+ stdio: ["pipe", "pipe", "pipe"],
2214
+ cwd
2215
+ });
2216
+ let stdout = "";
2217
+ let stderr = "";
2218
+ childProcess.stdout.on("data", (data2) => {
2219
+ stdout += data2.toString();
2220
+ });
2221
+ childProcess.stderr.on("data", (data2) => {
2222
+ stderr += data2.toString();
2223
+ });
2224
+ childProcess.on("close", (code) => {
2225
+ if (stderr && process.env.DEBUG === "1") {
2226
+ console.error(`stderr: ${stderr}`);
2227
+ }
2228
+ if (code !== 0) {
2229
+ reject2(new Error(`Symbols command failed with exit code ${code}: ${stderr}`));
2230
+ return;
2231
+ }
2232
+ try {
2233
+ const result = JSON.parse(stdout);
2234
+ resolve9(result);
2235
+ } catch (error40) {
2236
+ reject2(new Error(`Failed to parse symbols output: ${error40.message}. Output: ${stdout.substring(0, 200)}`));
2237
+ }
2238
+ });
2239
+ childProcess.on("error", (error40) => {
2240
+ reject2(new Error(`Failed to spawn symbols process: ${error40.message}`));
2241
+ });
2242
+ });
2243
+ }
2244
+ var import_child_process5;
2245
+ var init_symbols = __esm({
2246
+ "src/symbols.js"() {
2247
+ "use strict";
2248
+ import_child_process5 = require("child_process");
2249
+ init_utils();
2250
+ init_path_validation();
2251
+ }
2252
+ });
2253
+
2190
2254
  // src/grep.js
2191
2255
  async function grep(options) {
2192
2256
  if (!options || !options.pattern) {
@@ -2230,14 +2294,14 @@ async function grep(options) {
2230
2294
  throw new Error(`Grep failed: ${errorMessage}`);
2231
2295
  }
2232
2296
  }
2233
- var import_child_process5, import_util5, execFileAsync2, GREP_FLAG_MAP;
2297
+ var import_child_process6, import_util5, execFileAsync2, GREP_FLAG_MAP;
2234
2298
  var init_grep = __esm({
2235
2299
  "src/grep.js"() {
2236
2300
  "use strict";
2237
- import_child_process5 = require("child_process");
2301
+ import_child_process6 = require("child_process");
2238
2302
  import_util5 = require("util");
2239
2303
  init_utils();
2240
- execFileAsync2 = (0, import_util5.promisify)(import_child_process5.execFile);
2304
+ execFileAsync2 = (0, import_util5.promisify)(import_child_process6.execFile);
2241
2305
  GREP_FLAG_MAP = {
2242
2306
  ignoreCase: "-i",
2243
2307
  lineNumbers: "-n",
@@ -27645,6 +27709,9 @@ function createTools(configOptions) {
27645
27709
  if (isToolAllowed("extract")) {
27646
27710
  tools2.extractTool = extractTool(configOptions);
27647
27711
  }
27712
+ if (isToolAllowed("symbols")) {
27713
+ tools2.symbolsTool = symbolsTool(configOptions);
27714
+ }
27648
27715
  if (configOptions.enableDelegate && isToolAllowed("delegate")) {
27649
27716
  tools2.delegateTool = delegateTool(configOptions);
27650
27717
  }
@@ -27813,7 +27880,7 @@ function resolveTargetPath(target, cwd) {
27813
27880
  }
27814
27881
  return filePart + suffix;
27815
27882
  }
27816
- var import_path6, searchDelegateSchema, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, readMediaSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
27883
+ var import_path6, searchDelegateSchema, searchSchema, searchAllSchema, querySchema, extractSchema, delegateSchema, listSkillsSchema, useSkillSchema, listFilesSchema, searchFilesSchema, readImageSchema, readMediaSchema, symbolsSchema, bashSchema, analyzeAllSchema, executePlanSchema, cleanupExecutePlanSchema, searchDescription, searchDelegateDescription, queryDescription, extractDescription, delegateDescription, bashDescription, analyzeAllDescription;
27817
27884
  var init_common = __esm({
27818
27885
  "src/tools/common.js"() {
27819
27886
  "use strict";
@@ -27872,6 +27939,9 @@ var init_common = __esm({
27872
27939
  readMediaSchema = external_exports2.object({
27873
27940
  path: external_exports2.string().describe("Path to the media file to read. Supports images (png, jpg, jpeg, webp, bmp, svg) and documents (pdf).")
27874
27941
  });
27942
+ symbolsSchema = external_exports2.object({
27943
+ file: external_exports2.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).")
27944
+ });
27875
27945
  bashSchema = external_exports2.object({
27876
27946
  command: external_exports2.string().describe("The bash command to execute"),
27877
27947
  workingDirectory: external_exports2.string().optional().describe("Directory to execute the command in (optional)"),
@@ -35281,14 +35351,21 @@ function createWrappedTools(baseTools) {
35281
35351
  baseTools.multiEditTool.execute
35282
35352
  );
35283
35353
  }
35354
+ if (baseTools.symbolsTool) {
35355
+ wrappedTools.symbolsToolInstance = wrapToolWithEmitter(
35356
+ baseTools.symbolsTool,
35357
+ "symbols",
35358
+ baseTools.symbolsTool.execute
35359
+ );
35360
+ }
35284
35361
  return wrappedTools;
35285
35362
  }
35286
- var import_child_process6, import_util13, import_crypto3, import_events, import_fs6, import_fs7, import_path8, toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
35363
+ var import_child_process7, import_util13, import_crypto3, import_events, import_fs6, import_fs7, import_path8, toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
35287
35364
  var init_probeTool = __esm({
35288
35365
  "src/agent/probeTool.js"() {
35289
35366
  "use strict";
35290
35367
  init_index();
35291
- import_child_process6 = require("child_process");
35368
+ import_child_process7 = require("child_process");
35292
35369
  import_util13 = require("util");
35293
35370
  import_crypto3 = require("crypto");
35294
35371
  import_events = require("events");
@@ -73938,6 +74015,7 @@ CRITICAL - ALWAYS search before answering:
73938
74015
  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.
73939
74016
 
73940
74017
  When exploring code:
74018
+ - Use the symbols tool to get a quick overview of a file's structure (functions, classes, constants) before diving into details with extract
73941
74019
  - Provide clear, concise explanations based on user request
73942
74020
  - Find and highlight the most relevant code snippets, if required
73943
74021
  - Trace function calls and data flow through the system \u2014 follow the FULL call chain, not just the entry point
@@ -73968,6 +74046,7 @@ You think like a code explorer \u2014 you understand that codebases have layers:
73968
74046
 
73969
74047
  When searching:
73970
74048
  - Search for the MAIN concept first, then think: "what RELATED subsystems would a real codebase have?"
74049
+ - Use symbols to get a file's table of contents (functions, classes, constants with line numbers) before extracting \u2014 it helps you pick the right symbols to extract
73971
74050
  - Use extract to READ the code you find \u2014 look for function calls, type references, and imports that point to OTHER relevant code
73972
74051
  - If you find middleware, check: are there org-level or tenant-level variants?
73973
74052
  - If you find algorithms, check: are there different storage backends?
@@ -74011,6 +74090,7 @@ If the solution is clear, you can jump to implementation right away. If not, ask
74011
74090
  - Do not add code comments unless the logic is genuinely complex and non-obvious.
74012
74091
 
74013
74092
  # Before Implementation
74093
+ - Use symbols to get a quick overview of file structure (functions, classes, constants with line numbers) before reading or editing files.
74014
74094
  - Read tests first \u2014 find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
74015
74095
  - Read neighboring files \u2014 understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
74016
74096
  - Trace the call chain \u2014 follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
@@ -74047,7 +74127,7 @@ Use the right tool:
74047
74127
  1. To MODIFY existing code \u2192 \`edit\` tool (old_string \u2192 new_string, or start_line/end_line)
74048
74128
  2. To CREATE a new file \u2192 \`create\` tool
74049
74129
  3. To CHANGE multiple files at once \u2192 \`multi_edit\` tool
74050
- 4. To READ code \u2192 \`extract\` or \`search\` tools
74130
+ 4. To READ code \u2192 \`extract\` or \`search\` tools. Use \`symbols\` to get a file's table of contents (functions, classes, constants with line numbers) before extracting.
74051
74131
  5. If \`edit\` fails with "file has not been read yet" \u2192 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.
74052
74132
 
74053
74133
  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.
@@ -76501,6 +76581,7 @@ var require_stringify = __commonJS({
76501
76581
  nullStr: "null",
76502
76582
  simpleKeys: false,
76503
76583
  singleQuote: null,
76584
+ trailingComma: false,
76504
76585
  trueStr: "true",
76505
76586
  verifyAliasOrder: true
76506
76587
  }, doc.schema.toStringOptions, options);
@@ -77018,12 +77099,19 @@ ${indent}${line}` : "\n";
77018
77099
  if (comment)
77019
77100
  reqNewline = true;
77020
77101
  let str = stringify.stringify(item, itemCtx, () => comment = null);
77021
- if (i < items.length - 1)
77102
+ reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
77103
+ if (i < items.length - 1) {
77022
77104
  str += ",";
77105
+ } else if (ctx.options.trailingComma) {
77106
+ if (ctx.options.lineWidth > 0) {
77107
+ reqNewline || (reqNewline = lines.reduce((sum, line) => sum + line.length + 2, 2) + (str.length + 2) > ctx.options.lineWidth);
77108
+ }
77109
+ if (reqNewline) {
77110
+ str += ",";
77111
+ }
77112
+ }
77023
77113
  if (comment)
77024
77114
  str += stringifyComment.lineComment(str, itemIndent, commentString(comment));
77025
- if (!reqNewline && (lines.length > linesAtValue || str.includes("\n")))
77026
- reqNewline = true;
77027
77115
  lines.push(str);
77028
77116
  linesAtValue = lines.length;
77029
77117
  }
@@ -80037,17 +80125,22 @@ var require_compose_node = __commonJS({
80037
80125
  case "block-map":
80038
80126
  case "block-seq":
80039
80127
  case "flow-collection":
80040
- node = composeCollection.composeCollection(CN, ctx, token, props, onError);
80041
- if (anchor)
80042
- node.anchor = anchor.source.substring(1);
80128
+ try {
80129
+ node = composeCollection.composeCollection(CN, ctx, token, props, onError);
80130
+ if (anchor)
80131
+ node.anchor = anchor.source.substring(1);
80132
+ } catch (error40) {
80133
+ const message = error40 instanceof Error ? error40.message : String(error40);
80134
+ onError(token, "RESOURCE_EXHAUSTION", message);
80135
+ }
80043
80136
  break;
80044
80137
  default: {
80045
80138
  const message = token.type === "error" ? token.message : `Unsupported token (type: ${token.type})`;
80046
80139
  onError(token, "UNEXPECTED_TOKEN", message);
80047
- node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError);
80048
80140
  isSrcToken = false;
80049
80141
  }
80050
80142
  }
80143
+ node ?? (node = composeEmptyNode(ctx, token.offset, void 0, null, props, onError));
80051
80144
  if (anchor && node.anchor === "")
80052
80145
  onError(anchor, "BAD_ALIAS", "Anchor cannot be an empty string");
80053
80146
  if (atKey && ctx.options.stringKeys && (!identity2.isScalar(node) || typeof node.value !== "string" || node.tag && node.tag !== "tag:yaml.org,2002:str")) {
@@ -95036,7 +95129,7 @@ async function executeBashCommand(command, options = {}) {
95036
95129
  [cmd, ...cmdArgs] = args;
95037
95130
  useShell = false;
95038
95131
  }
95039
- const child = (0, import_child_process7.spawn)(cmd, cmdArgs, {
95132
+ const child = (0, import_child_process8.spawn)(cmd, cmdArgs, {
95040
95133
  cwd,
95041
95134
  env: processEnv,
95042
95135
  stdio: ["ignore", "pipe", "pipe"],
@@ -95232,11 +95325,11 @@ function validateExecutionOptions(options = {}) {
95232
95325
  warnings
95233
95326
  };
95234
95327
  }
95235
- var import_child_process7, import_path13, import_fs10;
95328
+ var import_child_process8, import_path13, import_fs10;
95236
95329
  var init_bashExecutor = __esm({
95237
95330
  "src/agent/bashExecutor.js"() {
95238
95331
  "use strict";
95239
- import_child_process7 = require("child_process");
95332
+ import_child_process8 = require("child_process");
95240
95333
  import_path13 = require("path");
95241
95334
  import_fs10 = require("fs");
95242
95335
  init_bashCommandUtils();
@@ -96762,7 +96855,7 @@ ${opts.schema}`;
96762
96855
  }
96763
96856
  }
96764
96857
  const toolCollector = [];
96765
- const proc2 = (0, import_child_process8.spawn)("sh", ["-c", shellCmd], {
96858
+ const proc2 = (0, import_child_process9.spawn)("sh", ["-c", shellCmd], {
96766
96859
  env: { ...process.env, FORCE_COLOR: "0" },
96767
96860
  stdio: ["ignore", "pipe", "pipe"]
96768
96861
  // Ignore stdin since echo handles it
@@ -97117,11 +97210,11 @@ function combinePrompts(systemPrompt, customPrompt, agent) {
97117
97210
  }
97118
97211
  return systemPrompt || "";
97119
97212
  }
97120
- var import_child_process8, import_crypto6, import_promises5, import_path15, import_os5, import_events3;
97213
+ var import_child_process9, import_crypto6, import_promises5, import_path15, import_os5, import_events3;
97121
97214
  var init_enhanced_claude_code = __esm({
97122
97215
  "src/agent/engines/enhanced-claude-code.js"() {
97123
97216
  "use strict";
97124
- import_child_process8 = require("child_process");
97217
+ import_child_process9 = require("child_process");
97125
97218
  import_crypto6 = require("crypto");
97126
97219
  import_promises5 = __toESM(require("fs/promises"), 1);
97127
97220
  import_path15 = __toESM(require("path"), 1);
@@ -97163,7 +97256,7 @@ async function createCodexEngine(options = {}) {
97163
97256
  if (debug) {
97164
97257
  console.log("[DEBUG] Starting Codex MCP server...");
97165
97258
  }
97166
- const codexProcess = (0, import_child_process9.spawn)("codex", ["mcp-server"], {
97259
+ const codexProcess = (0, import_child_process10.spawn)("codex", ["mcp-server"], {
97167
97260
  stdio: ["pipe", "pipe", "pipe"]
97168
97261
  });
97169
97262
  let requestId = 0;
@@ -97395,11 +97488,11 @@ function combinePrompts2(systemPrompt, customPrompt, agent) {
97395
97488
  }
97396
97489
  return systemPrompt || "";
97397
97490
  }
97398
- var import_child_process9, import_crypto7, import_readline;
97491
+ var import_child_process10, import_crypto7, import_readline;
97399
97492
  var init_codex = __esm({
97400
97493
  "src/agent/engines/codex.js"() {
97401
97494
  "use strict";
97402
- import_child_process9 = require("child_process");
97495
+ import_child_process10 = require("child_process");
97403
97496
  import_crypto7 = require("crypto");
97404
97497
  import_readline = require("readline");
97405
97498
  init_built_in_server();
@@ -98165,6 +98258,9 @@ var init_ProbeAgent = __esm({
98165
98258
  if (isToolAllowed("searchFiles")) {
98166
98259
  this.toolImplementations.searchFiles = searchFilesToolInstance;
98167
98260
  }
98261
+ if (wrappedTools.symbolsToolInstance && isToolAllowed("symbols")) {
98262
+ this.toolImplementations.symbols = wrappedTools.symbolsToolInstance;
98263
+ }
98168
98264
  if (this.enableSkills) {
98169
98265
  const registry2 = this._getSkillsRegistry();
98170
98266
  const { listSkillsToolInstance, useSkillToolInstance } = createSkillToolInstances({
@@ -99196,6 +99292,10 @@ var init_ProbeAgent = __esm({
99196
99292
  schema: searchFilesSchema,
99197
99293
  description: "Find files matching a glob pattern with recursive search capability."
99198
99294
  },
99295
+ symbols: {
99296
+ schema: symbolsSchema,
99297
+ description: "List all symbols (functions, classes, structs, constants, etc.) in a file. Returns a hierarchical tree with line numbers \u2014 like a table of contents for code."
99298
+ },
99199
99299
  readMedia: {
99200
99300
  schema: readMediaSchema,
99201
99301
  description: "Read and load a media file (image or PDF document) for AI analysis. Supports: png, jpg, jpeg, webp, bmp, svg, pdf."
@@ -99851,7 +99951,8 @@ ${this.architectureContext.content}
99851
99951
  ${searchToolDesc1}
99852
99952
  - extract: Extract specific code sections with context
99853
99953
  - listFiles: Browse directory contents
99854
- - searchFiles: Find files by name patterns`;
99954
+ - searchFiles: Find files by name patterns
99955
+ - symbols: List all symbols in a file (functions, classes, constants, etc.) with line numbers`;
99855
99956
  if (this.enableBash) {
99856
99957
  systemPrompt += `
99857
99958
  - bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) \u2014 always use search and extract tools instead, they are faster and more accurate.`;
@@ -99906,7 +100007,8 @@ Workspace: ${this.allowedFolders.join(", ")}`;
99906
100007
  ${searchToolDesc2}
99907
100008
  - extract: Extract specific code sections with context
99908
100009
  - listFiles: Browse directory contents
99909
- - searchFiles: Find files by name patterns`;
100010
+ - searchFiles: Find files by name patterns
100011
+ - symbols: List all symbols in a file (functions, classes, constants, etc.) with line numbers`;
99910
100012
  if (this.enableBash) {
99911
100013
  systemPrompt += `
99912
100014
  - bash: Execute bash commands for system operations (building, running tests, git, etc.). NEVER use bash for code exploration (no grep, cat, find, head, tail) \u2014 always use search and extract tools instead, they are faster and more accurate.`;
@@ -103351,7 +103453,7 @@ The "searches" field helps the caller understand what was attempted.
103351
103453
  - Deduplicate files across groups.
103352
103454
  </output-rules>`;
103353
103455
  }
103354
- var import_ai5, import_fs12, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool;
103456
+ var import_ai5, import_fs12, CODE_SEARCH_SCHEMA, searchTool, queryTool, extractTool, delegateTool, analyzeAllTool, symbolsTool;
103355
103457
  var init_vercel = __esm({
103356
103458
  "src/tools/vercel.js"() {
103357
103459
  "use strict";
@@ -103359,6 +103461,7 @@ var init_vercel = __esm({
103359
103461
  init_search();
103360
103462
  init_query();
103361
103463
  init_extract();
103464
+ init_symbols();
103362
103465
  init_delegate();
103363
103466
  init_analyzeAll();
103364
103467
  init_common();
@@ -104081,6 +104184,36 @@ Do NOT search for analogies or loosely related concepts. If the feature does not
104081
104184
  }
104082
104185
  });
104083
104186
  };
104187
+ symbolsTool = (options = {}) => {
104188
+ return (0, import_ai5.tool)({
104189
+ name: "symbols",
104190
+ description: "List all symbols (functions, classes, structs, constants, etc.) in a file. Returns a hierarchical tree with line numbers \u2014 like a table of contents for code files.",
104191
+ inputSchema: symbolsSchema,
104192
+ execute: async ({ file: file2 }) => {
104193
+ try {
104194
+ let filePath = file2;
104195
+ if (options.cwd) {
104196
+ const resolvedPaths = parseAndResolvePaths(file2, options.cwd);
104197
+ if (resolvedPaths.length > 0) {
104198
+ filePath = resolvedPaths[0];
104199
+ }
104200
+ }
104201
+ const result = await symbols({
104202
+ files: [filePath],
104203
+ cwd: options.cwd,
104204
+ binaryOptions: options.binaryOptions
104205
+ });
104206
+ if (result && result.length > 0) {
104207
+ return JSON.stringify(result[0], null, 2);
104208
+ }
104209
+ return JSON.stringify({ file: file2, symbols: [] }, null, 2);
104210
+ } catch (error40) {
104211
+ console.error("Error executing symbols:", error40);
104212
+ return formatErrorForAI(error40);
104213
+ }
104214
+ }
104215
+ });
104216
+ };
104084
104217
  }
104085
104218
  });
104086
104219
 
@@ -105241,6 +105374,10 @@ You are Probe, a specialized code intelligence assistant. Your objective is to a
105241
105374
  * **Purpose:** Retrieve specific code blocks or entire files *after* \`search\` or \`query\` identifies the target.
105242
105375
  * **Syntax:** Optional \`#symbol\` (e.g., \`#MyClass\`), \`#Lstart-Lend\` (e.g., \`#L50-L75\`).
105243
105376
  * **Mandatory Argument:** \`path\` (specific file path, e.g., \`"src/utils/helpers.go"\`, or dependency file like \`"go:github.com/gin-gonic/gin/context.go"\`).
105377
+ * \`symbols\`
105378
+ * **Purpose:** List all symbols (functions, classes, structs, constants, etc.) in a file \u2014 a table of contents with line numbers and nesting.
105379
+ * **Syntax:** \`file\` (path to the file to list symbols from).
105380
+ * **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\`.
105244
105381
 
105245
105382
  [Examples]
105246
105383
 
@@ -105375,6 +105512,8 @@ __export(tools_exports, {
105375
105512
  searchFilesSchema: () => searchFilesSchema,
105376
105513
  searchSchema: () => searchSchema,
105377
105514
  searchTool: () => searchTool,
105515
+ symbolsSchema: () => symbolsSchema,
105516
+ symbolsTool: () => symbolsTool,
105378
105517
  tools: () => tools,
105379
105518
  useSkillSchema: () => useSkillSchema
105380
105519
  });
@@ -105498,16 +105637,16 @@ function shouldIgnore(filePath, ignorePatterns) {
105498
105637
  }
105499
105638
  return false;
105500
105639
  }
105501
- var import_fs15, import_path18, import_util14, import_child_process10, execAsync3;
105640
+ var import_fs15, import_path18, import_util14, import_child_process11, execAsync3;
105502
105641
  var init_file_lister = __esm({
105503
105642
  "src/utils/file-lister.js"() {
105504
105643
  "use strict";
105505
105644
  import_fs15 = __toESM(require("fs"), 1);
105506
105645
  import_path18 = __toESM(require("path"), 1);
105507
105646
  import_util14 = require("util");
105508
- import_child_process10 = require("child_process");
105647
+ import_child_process11 = require("child_process");
105509
105648
  init_symlink_utils();
105510
- execAsync3 = (0, import_util14.promisify)(import_child_process10.exec);
105649
+ execAsync3 = (0, import_util14.promisify)(import_child_process11.exec);
105511
105650
  }
105512
105651
  });
105513
105652
 
@@ -105583,6 +105722,9 @@ __export(index_exports, {
105583
105722
  searchSchema: () => searchSchema,
105584
105723
  searchTool: () => searchTool,
105585
105724
  setBinaryPath: () => setBinaryPath,
105725
+ symbols: () => symbols,
105726
+ symbolsSchema: () => symbolsSchema,
105727
+ symbolsTool: () => symbolsTool,
105586
105728
  taskSchema: () => taskSchema,
105587
105729
  taskSystemPrompt: () => taskSystemPrompt,
105588
105730
  tools: () => tools_exports,
@@ -105596,6 +105738,7 @@ var init_index = __esm({
105596
105738
  init_search();
105597
105739
  init_query();
105598
105740
  init_extract();
105741
+ init_symbols();
105599
105742
  init_grep();
105600
105743
  init_delegate();
105601
105744
  init_utils();
@@ -105673,6 +105816,9 @@ init_index();
105673
105816
  searchSchema,
105674
105817
  searchTool,
105675
105818
  setBinaryPath,
105819
+ symbols,
105820
+ symbolsSchema,
105821
+ symbolsTool,
105676
105822
  taskSchema,
105677
105823
  taskSystemPrompt,
105678
105824
  tools,
package/index.d.ts CHANGED
@@ -408,6 +408,58 @@ export interface QueryParams {
408
408
  sessionId?: string;
409
409
  }
410
410
 
411
+ /**
412
+ * Symbols tool parameters
413
+ */
414
+ export interface SymbolsParams {
415
+ /** Files to list symbols from */
416
+ files: string[];
417
+ /** Working directory for resolving relative paths */
418
+ cwd?: string;
419
+ /** Include test functions/methods */
420
+ allowTests?: boolean;
421
+ /** Binary options */
422
+ binaryOptions?: {
423
+ forceDownload?: boolean;
424
+ version?: string;
425
+ };
426
+ }
427
+
428
+ /**
429
+ * A node in the symbol tree
430
+ */
431
+ export interface SymbolNode {
432
+ /** Symbol name */
433
+ name: string;
434
+ /** Symbol kind (function, struct, class, const, etc.) */
435
+ kind: string;
436
+ /** Clean signature without body */
437
+ signature: string;
438
+ /** Start line (1-indexed) */
439
+ line: number;
440
+ /** End line (1-indexed) */
441
+ end_line: number;
442
+ /** Nested symbols (methods inside classes/impl blocks, etc.) */
443
+ children?: SymbolNode[];
444
+ }
445
+
446
+ /**
447
+ * Symbols extracted from a single file
448
+ */
449
+ export interface FileSymbols {
450
+ /** File path */
451
+ file: string;
452
+ /** Top-level symbols with optional nesting */
453
+ symbols: SymbolNode[];
454
+ }
455
+
456
+ /**
457
+ * Symbols tool function type
458
+ */
459
+ export type SymbolsTool = (options?: SearchOptions) => {
460
+ execute(params: { file: string }): Promise<string>;
461
+ };
462
+
411
463
  /**
412
464
  * Extract tool parameters
413
465
  */
@@ -537,6 +589,11 @@ export declare function grep(options: {
537
589
  };
538
590
  }): Promise<string>;
539
591
 
592
+ /**
593
+ * List symbols (functions, structs, classes, constants, etc.) in files
594
+ */
595
+ export declare function symbols(options: SymbolsParams): Promise<FileSymbols[]>;
596
+
540
597
  /**
541
598
  * Extract code blocks from files
542
599
  */
@@ -564,6 +621,11 @@ export declare function queryTool(options?: SearchOptions): ReturnType<QueryTool
564
621
  */
565
622
  export declare function extractTool(options?: SearchOptions): ReturnType<ExtractTool>;
566
623
 
624
+ /**
625
+ * Create symbols tool instance
626
+ */
627
+ export declare function symbolsTool(options?: SearchOptions): ReturnType<SymbolsTool>;
628
+
567
629
  /**
568
630
  * Get the path to the probe binary
569
631
  */
@@ -778,6 +840,7 @@ export declare function createTool(options?: EditToolOptions): {
778
840
  export declare const searchSchema: any;
779
841
  export declare const querySchema: any;
780
842
  export declare const extractSchema: any;
843
+ export declare const symbolsSchema: any;
781
844
  export declare const editSchema: any;
782
845
  export declare const createSchema: any;
783
846
  export declare const attemptCompletionSchema: any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc308",
3
+ "version": "0.6.0-rc309",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -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.`;
@@ -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.