@probelabs/probe 0.6.0-rc291 → 0.6.0-rc293

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/cjs/index.cjs CHANGED
@@ -27260,6 +27260,10 @@ var init_symbolEdit = __esm({
27260
27260
  });
27261
27261
 
27262
27262
  // src/tools/fileTracker.js
27263
+ function normalizePath(filePath) {
27264
+ if (!filePath) return filePath;
27265
+ return (0, import_path6.resolve)(filePath);
27266
+ }
27263
27267
  function computeContentHash(content) {
27264
27268
  const normalized = (content || "").split("\n").map((l) => l.trimEnd()).join("\n");
27265
27269
  return (0, import_crypto2.createHash)("sha256").update(normalized).digest("hex").slice(0, 16);
@@ -27322,10 +27326,11 @@ var init_fileTracker = __esm({
27322
27326
  * @param {string} resolvedPath - Absolute path to the file
27323
27327
  */
27324
27328
  markFileSeen(resolvedPath) {
27325
- this._seenFiles.add(resolvedPath);
27326
- this._textEditCounts.set(resolvedPath, 0);
27329
+ const normalized = normalizePath(resolvedPath);
27330
+ this._seenFiles.add(normalized);
27331
+ this._textEditCounts.set(normalized, 0);
27327
27332
  if (this.debug) {
27328
- console.error(`[FileTracker] Marked as seen: ${resolvedPath}`);
27333
+ console.error(`[FileTracker] Marked as seen: ${normalized}`);
27329
27334
  }
27330
27335
  }
27331
27336
  /**
@@ -27334,7 +27339,7 @@ var init_fileTracker = __esm({
27334
27339
  * @returns {boolean}
27335
27340
  */
27336
27341
  isFileSeen(resolvedPath) {
27337
- return this._seenFiles.has(resolvedPath);
27342
+ return this._seenFiles.has(normalizePath(resolvedPath));
27338
27343
  }
27339
27344
  /**
27340
27345
  * Store a content hash for a symbol in a file.
@@ -27346,7 +27351,7 @@ var init_fileTracker = __esm({
27346
27351
  * @param {string} [source='extract'] - How the content was obtained
27347
27352
  */
27348
27353
  trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = "extract") {
27349
- const key = `${resolvedPath}#${symbolName}`;
27354
+ const key = `${normalizePath(resolvedPath)}#${symbolName}`;
27350
27355
  const contentHash = computeContentHash(code);
27351
27356
  this._contentRecords.set(key, {
27352
27357
  contentHash,
@@ -27367,7 +27372,7 @@ var init_fileTracker = __esm({
27367
27372
  * @returns {Object|null} The stored record or null
27368
27373
  */
27369
27374
  getSymbolRecord(resolvedPath, symbolName) {
27370
- return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
27375
+ return this._contentRecords.get(`${normalizePath(resolvedPath)}#${symbolName}`) || null;
27371
27376
  }
27372
27377
  /**
27373
27378
  * Check if a symbol's current content matches what was stored.
@@ -27377,7 +27382,7 @@ var init_fileTracker = __esm({
27377
27382
  * @returns {{ok: boolean, reason?: string, message?: string}}
27378
27383
  */
27379
27384
  checkSymbolContent(resolvedPath, symbolName, currentCode) {
27380
- const key = `${resolvedPath}#${symbolName}`;
27385
+ const key = `${normalizePath(resolvedPath)}#${symbolName}`;
27381
27386
  const record2 = this._contentRecords.get(key);
27382
27387
  if (!record2) {
27383
27388
  return { ok: true };
@@ -27454,7 +27459,7 @@ var init_fileTracker = __esm({
27454
27459
  * @returns {{ok: boolean, reason?: string, message?: string}}
27455
27460
  */
27456
27461
  checkBeforeEdit(resolvedPath) {
27457
- if (!this._seenFiles.has(resolvedPath)) {
27462
+ if (!this._seenFiles.has(normalizePath(resolvedPath))) {
27458
27463
  return {
27459
27464
  ok: false,
27460
27465
  reason: "untracked",
@@ -27469,8 +27474,9 @@ var init_fileTracker = __esm({
27469
27474
  * @param {string} resolvedPath - Absolute path to the file
27470
27475
  */
27471
27476
  async trackFileAfterWrite(resolvedPath) {
27472
- this._seenFiles.add(resolvedPath);
27473
- this.invalidateFileRecords(resolvedPath);
27477
+ const normalized = normalizePath(resolvedPath);
27478
+ this._seenFiles.add(normalized);
27479
+ this.invalidateFileRecords(normalized);
27474
27480
  }
27475
27481
  /**
27476
27482
  * Record a text-mode edit (old_string/new_string) to a file.
@@ -27478,10 +27484,11 @@ var init_fileTracker = __esm({
27478
27484
  * @param {string} resolvedPath - Absolute path to the file
27479
27485
  */
27480
27486
  recordTextEdit(resolvedPath) {
27481
- const count = (this._textEditCounts.get(resolvedPath) || 0) + 1;
27482
- this._textEditCounts.set(resolvedPath, count);
27487
+ const normalized = normalizePath(resolvedPath);
27488
+ const count = (this._textEditCounts.get(normalized) || 0) + 1;
27489
+ this._textEditCounts.set(normalized, count);
27483
27490
  if (this.debug) {
27484
- console.error(`[FileTracker] Text edit #${count} for ${resolvedPath}`);
27491
+ console.error(`[FileTracker] Text edit #${count} for ${normalized}`);
27485
27492
  }
27486
27493
  }
27487
27494
  /**
@@ -27490,7 +27497,7 @@ var init_fileTracker = __esm({
27490
27497
  * @returns {{ok: boolean, editCount?: number, message?: string}}
27491
27498
  */
27492
27499
  checkTextEditStaleness(resolvedPath) {
27493
- const count = this._textEditCounts.get(resolvedPath) || 0;
27500
+ const count = this._textEditCounts.get(normalizePath(resolvedPath)) || 0;
27494
27501
  if (count >= this.maxConsecutiveTextEdits) {
27495
27502
  return {
27496
27503
  ok: false,
@@ -27519,7 +27526,7 @@ var init_fileTracker = __esm({
27519
27526
  * @param {string} resolvedPath - Absolute path to the file
27520
27527
  */
27521
27528
  invalidateFileRecords(resolvedPath) {
27522
- const prefix = resolvedPath + "#";
27529
+ const prefix = normalizePath(resolvedPath) + "#";
27523
27530
  for (const key of this._contentRecords.keys()) {
27524
27531
  if (key.startsWith(prefix)) {
27525
27532
  this._contentRecords.delete(key);
@@ -27535,7 +27542,7 @@ var init_fileTracker = __esm({
27535
27542
  * @returns {boolean}
27536
27543
  */
27537
27544
  isTracked(resolvedPath) {
27538
- return this.isFileSeen(resolvedPath);
27545
+ return this.isFileSeen(normalizePath(resolvedPath));
27539
27546
  }
27540
27547
  /**
27541
27548
  * Clear all tracking state.
@@ -31588,7 +31595,7 @@ var init_esm3 = __esm({
31588
31595
  });
31589
31596
 
31590
31597
  // node_modules/path-scurry/dist/esm/index.js
31591
- var import_node_path, import_node_url, import_fs4, actualFS, import_promises, realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache, normalize, normalizeNocaseCache, normalizeNocase, ResolveCache, ChildrenCache, setAsCwd, PathBase, PathWin32, PathPosix, PathScurryBase, PathScurryWin32, PathScurryPosix, PathScurryDarwin, Path, PathScurry;
31598
+ var import_node_path, import_node_url, import_fs4, actualFS, import_promises, realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache, normalize2, normalizeNocaseCache, normalizeNocase, ResolveCache, ChildrenCache, setAsCwd, PathBase, PathWin32, PathPosix, PathScurryBase, PathScurryWin32, PathScurryPosix, PathScurryDarwin, Path, PathScurry;
31592
31599
  var init_esm4 = __esm({
31593
31600
  "node_modules/path-scurry/dist/esm/index.js"() {
31594
31601
  init_esm2();
@@ -31643,7 +31650,7 @@ var init_esm4 = __esm({
31643
31650
  TYPEMASK = 1023;
31644
31651
  entToType = (s) => s.isFile() ? IFREG : s.isDirectory() ? IFDIR : s.isSymbolicLink() ? IFLNK : s.isCharacterDevice() ? IFCHR : s.isBlockDevice() ? IFBLK : s.isSocket() ? IFSOCK : s.isFIFO() ? IFIFO : UNKNOWN;
31645
31652
  normalizeCache = /* @__PURE__ */ new Map();
31646
- normalize = (s) => {
31653
+ normalize2 = (s) => {
31647
31654
  const c = normalizeCache.get(s);
31648
31655
  if (c)
31649
31656
  return c;
@@ -31656,7 +31663,7 @@ var init_esm4 = __esm({
31656
31663
  const c = normalizeNocaseCache.get(s);
31657
31664
  if (c)
31658
31665
  return c;
31659
- const n = normalize(s.toLowerCase());
31666
+ const n = normalize2(s.toLowerCase());
31660
31667
  normalizeNocaseCache.set(s, n);
31661
31668
  return n;
31662
31669
  };
@@ -31823,7 +31830,7 @@ var init_esm4 = __esm({
31823
31830
  */
31824
31831
  constructor(name15, type = UNKNOWN, root2, roots, nocase, children, opts) {
31825
31832
  this.name = name15;
31826
- this.#matchName = nocase ? normalizeNocase(name15) : normalize(name15);
31833
+ this.#matchName = nocase ? normalizeNocase(name15) : normalize2(name15);
31827
31834
  this.#type = type & TYPEMASK;
31828
31835
  this.nocase = nocase;
31829
31836
  this.roots = roots;
@@ -31916,7 +31923,7 @@ var init_esm4 = __esm({
31916
31923
  return this.parent || this;
31917
31924
  }
31918
31925
  const children = this.children();
31919
- const name15 = this.nocase ? normalizeNocase(pathPart) : normalize(pathPart);
31926
+ const name15 = this.nocase ? normalizeNocase(pathPart) : normalize2(pathPart);
31920
31927
  for (const p of children) {
31921
31928
  if (p.#matchName === name15) {
31922
31929
  return p;
@@ -32161,7 +32168,7 @@ var init_esm4 = __esm({
32161
32168
  * directly.
32162
32169
  */
32163
32170
  isNamed(n) {
32164
- return !this.nocase ? this.#matchName === normalize(n) : this.#matchName === normalizeNocase(n);
32171
+ return !this.nocase ? this.#matchName === normalize2(n) : this.#matchName === normalizeNocase(n);
32165
32172
  }
32166
32173
  /**
32167
32174
  * Return the Path object corresponding to the target of a symbolic link.
@@ -32300,7 +32307,7 @@ var init_esm4 = __esm({
32300
32307
  #readdirMaybePromoteChild(e, c) {
32301
32308
  for (let p = c.provisional; p < c.length; p++) {
32302
32309
  const pchild = c[p];
32303
- const name15 = this.nocase ? normalizeNocase(e.name) : normalize(e.name);
32310
+ const name15 = this.nocase ? normalizeNocase(e.name) : normalize2(e.name);
32304
32311
  if (name15 !== pchild.#matchName) {
32305
32312
  continue;
32306
32313
  }
@@ -53617,7 +53624,7 @@ var init_graph_builder = __esm({
53617
53624
  applyLinkStyles() {
53618
53625
  if (!this.pendingLinkStyles.length || !this.edges.length)
53619
53626
  return;
53620
- const normalize3 = (s) => {
53627
+ const normalize4 = (s) => {
53621
53628
  const out = {};
53622
53629
  for (const [kRaw, vRaw] of Object.entries(s)) {
53623
53630
  const k = kRaw.trim().toLowerCase();
@@ -53638,7 +53645,7 @@ var init_graph_builder = __esm({
53638
53645
  return out;
53639
53646
  };
53640
53647
  for (const cmd of this.pendingLinkStyles) {
53641
- const style = normalize3(cmd.props);
53648
+ const style = normalize4(cmd.props);
53642
53649
  for (const idx of cmd.indices) {
53643
53650
  if (idx >= 0 && idx < this.edges.length) {
53644
53651
  const e = this.edges[idx];
@@ -60905,7 +60912,7 @@ var require_layout = __commonJS({
60905
60912
  "use strict";
60906
60913
  var _ = require_lodash2();
60907
60914
  var acyclic = require_acyclic();
60908
- var normalize3 = require_normalize();
60915
+ var normalize4 = require_normalize();
60909
60916
  var rank = require_rank();
60910
60917
  var normalizeRanks = require_util().normalizeRanks;
60911
60918
  var parentDummyChains = require_parent_dummy_chains();
@@ -60967,7 +60974,7 @@ var require_layout = __commonJS({
60967
60974
  removeEdgeLabelProxies(g);
60968
60975
  });
60969
60976
  time3(" normalize.run", function() {
60970
- normalize3.run(g);
60977
+ normalize4.run(g);
60971
60978
  });
60972
60979
  time3(" parentDummyChains", function() {
60973
60980
  parentDummyChains(g);
@@ -60994,7 +61001,7 @@ var require_layout = __commonJS({
60994
61001
  removeBorderNodes(g);
60995
61002
  });
60996
61003
  time3(" normalize.undo", function() {
60997
- normalize3.undo(g);
61004
+ normalize4.undo(g);
60998
61005
  });
60999
61006
  time3(" fixupEdgeLabelCoords", function() {
61000
61007
  fixupEdgeLabelCoords(g);
@@ -67365,8 +67372,8 @@ var require_resolve = __commonJS({
67365
67372
  }
67366
67373
  return count;
67367
67374
  }
67368
- function getFullPath(resolver, id = "", normalize3) {
67369
- if (normalize3 !== false)
67375
+ function getFullPath(resolver, id = "", normalize4) {
67376
+ if (normalize4 !== false)
67370
67377
  id = normalizeId(id);
67371
67378
  const p = resolver.parse(id);
67372
67379
  return _getFullPath(resolver, p);
@@ -68706,7 +68713,7 @@ var require_fast_uri = __commonJS({
68706
68713
  "use strict";
68707
68714
  var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
68708
68715
  var { SCHEMES, getSchemeHandler } = require_schemes();
68709
- function normalize3(uri, options) {
68716
+ function normalize4(uri, options) {
68710
68717
  if (typeof uri === "string") {
68711
68718
  uri = /** @type {T} */
68712
68719
  serialize(parse11(uri, options), options);
@@ -68942,7 +68949,7 @@ var require_fast_uri = __commonJS({
68942
68949
  }
68943
68950
  var fastUri = {
68944
68951
  SCHEMES,
68945
- normalize: normalize3,
68952
+ normalize: normalize4,
68946
68953
  resolve: resolve9,
68947
68954
  resolveComponent,
68948
68955
  equal,
@@ -73149,9 +73156,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
73149
73156
  - Do not add code comments unless the logic is genuinely complex and non-obvious.
73150
73157
 
73151
73158
  # Before Implementation
73152
- - Focus on high-level design patterns and system organization
73153
- - Identify architectural patterns and component relationships
73154
- - Evaluate system structure and suggest architectural improvements
73159
+ - 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.
73160
+ - Read neighboring files \u2014 understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
73161
+ - Trace the call chain \u2014 follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
73155
73162
  - Focus on backward compatibility
73156
73163
  - Consider scalability, maintainability, and extensibility in your analysis
73157
73164
 
@@ -73176,6 +73183,20 @@ Before building or testing, determine the project's toolchain:
73176
73183
  - Read README for build/test instructions if the above are unclear
73177
73184
  - Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
73178
73185
 
73186
+ # File Editing Rules
73187
+ You have access to the \`edit\`, \`create\`, and \`multi_edit\` tools for modifying files. You MUST use these tools for ALL code changes. They are purpose-built, atomic, and safe.
73188
+
73189
+ DO NOT use sed, awk, echo/cat redirection, or heredocs to modify source code. These commands cause real damage in practice: truncated lines, duplicate code blocks, broken syntax. Every bad edit wastes iterations on fix-up commits.
73190
+
73191
+ Use the right tool:
73192
+ 1. To MODIFY existing code \u2192 \`edit\` tool (old_string \u2192 new_string, or start_line/end_line)
73193
+ 2. To CREATE a new file \u2192 \`create\` tool
73194
+ 3. To CHANGE multiple files at once \u2192 \`multi_edit\` tool
73195
+ 4. To READ code \u2192 \`extract\` or \`search\` tools
73196
+ 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.
73197
+
73198
+ 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.
73199
+
73179
73200
  # During Implementation
73180
73201
  - Always create a new branch before making changes to the codebase.
73181
73202
  - Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
@@ -73202,6 +73223,22 @@ Before committing or creating a PR, run through this checklist:
73202
73223
 
73203
73224
  Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
73204
73225
 
73226
+ # Output Integrity
73227
+ Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
73228
+
73229
+ - Only report PR URLs you actually created or updated with \`gh pr create\` or \`git push\`. If you checked out an existing PR but did NOT push changes to it, do NOT claim you updated it.
73230
+ - Describe what you ACTUALLY DID, not what you planned or intended to do. If you ran out of iterations, say so. If tests failed, say so.
73231
+ - Only list files you actually modified AND committed.
73232
+ - If you could not complete the task \u2014 ran out of iterations, tests failed, build broken, push rejected \u2014 report the real reason honestly.
73233
+
73234
+ NEVER claim success when:
73235
+ - You did not run \`git push\` successfully
73236
+ - Tests failed and you did not fix them
73237
+ - You hit the iteration limit before completing the work
73238
+ - You only analyzed/investigated but did not implement changes
73239
+
73240
+ A false success report is WORSE than an honest failure \u2014 it misleads the user into thinking work is done when it is not.
73241
+
73205
73242
  # GitHub Integration
73206
73243
  - Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
73207
73244
  - To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
@@ -92219,7 +92256,12 @@ function generateSandboxGlobals(options) {
92219
92256
  }
92220
92257
  return tryParseJSONValue(text);
92221
92258
  };
92222
- globals[name15] = traceToolCall(name15, rawMcpFn, tracer, logFn);
92259
+ const tracedFn = traceToolCall(name15, rawMcpFn, tracer, logFn);
92260
+ globals[name15] = tracedFn;
92261
+ const sanitized = name15.replace(/[^a-zA-Z0-9_$]/g, "_");
92262
+ if (sanitized !== name15) {
92263
+ globals[sanitized] = tracedFn;
92264
+ }
92223
92265
  }
92224
92266
  }
92225
92267
  if (llmCall) {
@@ -92574,9 +92616,17 @@ ${validation.errors.join("\n")}`,
92574
92616
  "dsl.duration_ms": elapsed,
92575
92617
  "dsl.error": e.message?.substring(0, 500)
92576
92618
  });
92619
+ let errorMsg = `Execution failed: ${e.message}`;
92620
+ if (e.message && e.message.includes("is not defined")) {
92621
+ const globalNames = Object.keys(toolGlobals).sort();
92622
+ errorMsg += `
92623
+ Available functions: ${globalNames.join(", ")}`;
92624
+ errorMsg += `
92625
+ Note: Tools with hyphens (e.g. "my-tool") are available with underscores: my_tool()`;
92626
+ }
92577
92627
  return {
92578
92628
  status: "error",
92579
- error: `Execution failed: ${e.message}`,
92629
+ error: errorMsg,
92580
92630
  logs
92581
92631
  };
92582
92632
  }
@@ -101391,7 +101441,7 @@ var init_vercel = __esm({
101391
101441
  name: "search",
101392
101442
  description: searchDelegate ? searchDelegateDescription : searchDescription,
101393
101443
  inputSchema: searchSchema,
101394
- execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
101444
+ execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
101395
101445
  if (!exact && searchQuery) {
101396
101446
  const originalQuery = searchQuery;
101397
101447
  searchQuery = autoQuoteSearchTerms(searchQuery);
@@ -101400,18 +101450,19 @@ var init_vercel = __esm({
101400
101450
  }
101401
101451
  }
101402
101452
  const effectiveMaxTokens = paramMaxTokens || maxTokens;
101453
+ const effectiveSearchCwd = workingDirectory || options.cwd || ".";
101403
101454
  let searchPaths;
101404
101455
  if (path9) {
101405
- searchPaths = parseAndResolvePaths(path9, options.cwd);
101456
+ searchPaths = parseAndResolvePaths(path9, effectiveSearchCwd);
101406
101457
  }
101407
101458
  if (!searchPaths || searchPaths.length === 0) {
101408
- searchPaths = [options.cwd || "."];
101459
+ searchPaths = [effectiveSearchCwd];
101409
101460
  }
101410
101461
  const searchPath = searchPaths.join(" ");
101411
101462
  const searchOptions = {
101412
101463
  query: searchQuery,
101413
101464
  path: searchPath,
101414
- cwd: options.cwd,
101465
+ cwd: effectiveSearchCwd,
101415
101466
  // Working directory for resolving relative paths
101416
101467
  allowTests: allow_tests ?? true,
101417
101468
  exact,
@@ -101462,7 +101513,7 @@ var init_vercel = __esm({
101462
101513
  try {
101463
101514
  const result = maybeAnnotate(await runRawSearch());
101464
101515
  if (options.fileTracker && typeof result === "string") {
101465
- options.fileTracker.trackFilesFromOutput(result, options.cwd || ".").catch(() => {
101516
+ options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
101466
101517
  });
101467
101518
  }
101468
101519
  return result;
@@ -101515,7 +101566,7 @@ var init_vercel = __esm({
101515
101566
  }
101516
101567
  const fallbackResult = maybeAnnotate(await runRawSearch());
101517
101568
  if (options.fileTracker && typeof fallbackResult === "string") {
101518
- options.fileTracker.trackFilesFromOutput(fallbackResult, options.cwd || ".").catch(() => {
101569
+ options.fileTracker.trackFilesFromOutput(fallbackResult, effectiveSearchCwd).catch(() => {
101519
101570
  });
101520
101571
  }
101521
101572
  return fallbackResult;
@@ -101578,7 +101629,7 @@ var init_vercel = __esm({
101578
101629
  try {
101579
101630
  const fallbackResult2 = maybeAnnotate(await runRawSearch());
101580
101631
  if (options.fileTracker && typeof fallbackResult2 === "string") {
101581
- options.fileTracker.trackFilesFromOutput(fallbackResult2, options.cwd || ".").catch(() => {
101632
+ options.fileTracker.trackFilesFromOutput(fallbackResult2, effectiveSearchCwd).catch(() => {
101582
101633
  });
101583
101634
  }
101584
101635
  return fallbackResult2;
@@ -101632,9 +101683,9 @@ var init_vercel = __esm({
101632
101683
  name: "extract",
101633
101684
  description: extractDescription,
101634
101685
  inputSchema: extractSchema,
101635
- execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
101686
+ execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format, workingDirectory }) => {
101636
101687
  try {
101637
- const effectiveCwd = options.cwd || ".";
101688
+ const effectiveCwd = workingDirectory || options.cwd || ".";
101638
101689
  if (debug) {
101639
101690
  if (targets) {
101640
101691
  console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
@@ -101893,7 +101944,14 @@ function lineTrimmedMatch(contentLines, searchLines) {
101893
101944
  }
101894
101945
  }
101895
101946
  if (allMatch) {
101896
- const matchedText = contentLines.slice(i, i + windowSize).join("\n");
101947
+ const windowLines = contentLines.slice(i, i + windowSize);
101948
+ const windowMinIndent = getMinIndent(windowLines);
101949
+ const searchMinIndent = getMinIndent(searchLines);
101950
+ const indentDiff = Math.abs(windowMinIndent - searchMinIndent);
101951
+ if (isIndentDiffTooLarge(windowLines, searchLines, indentDiff)) {
101952
+ continue;
101953
+ }
101954
+ const matchedText = windowLines.join("\n");
101897
101955
  matches.push(matchedText);
101898
101956
  }
101899
101957
  }
@@ -101923,6 +101981,15 @@ function whitespaceNormalizedMatch(content, search2) {
101923
101981
  actualEnd++;
101924
101982
  }
101925
101983
  const matchedText = content.substring(originalStart, actualEnd);
101984
+ const matchedLines = matchedText.split("\n");
101985
+ const searchLines = search2.split("\n");
101986
+ const matchMinIndent = getMinIndent(matchedLines);
101987
+ const searchMinIndent = getMinIndent(searchLines);
101988
+ const indentDiff = Math.abs(matchMinIndent - searchMinIndent);
101989
+ if (isIndentDiffTooLarge(matchedLines, searchLines, indentDiff)) {
101990
+ searchStart = idx + 1;
101991
+ continue;
101992
+ }
101926
101993
  matches.push(matchedText);
101927
101994
  searchStart = idx + 1;
101928
101995
  }
@@ -101974,6 +102041,10 @@ function indentFlexibleMatch(contentLines, searchLines) {
101974
102041
  }
101975
102042
  }
101976
102043
  if (allMatch) {
102044
+ const indentDiff = Math.abs(windowMinIndent - searchMinIndent);
102045
+ if (isIndentDiffTooLarge(windowLines, searchLines, indentDiff)) {
102046
+ continue;
102047
+ }
101977
102048
  const matchedText = windowLines.join("\n");
101978
102049
  matches.push(matchedText);
101979
102050
  }
@@ -101984,6 +102055,14 @@ function indentFlexibleMatch(contentLines, searchLines) {
101984
102055
  count: matches.length
101985
102056
  };
101986
102057
  }
102058
+ function isIndentDiffTooLarge(linesA, linesB, indentDiff) {
102059
+ if (indentDiff <= 0) return false;
102060
+ const sampleA = linesA.find((l) => l.trim().length > 0) || "";
102061
+ const sampleB = linesB.find((l) => l.trim().length > 0) || "";
102062
+ const useTabs = sampleA.startsWith(" ") || sampleB.startsWith(" ");
102063
+ const maxAllowedDiff = useTabs ? 1 : 4;
102064
+ return indentDiff > maxAllowedDiff;
102065
+ }
101987
102066
  function getMinIndent(lines) {
101988
102067
  let min = Infinity;
101989
102068
  for (const line of lines) {
@@ -102058,6 +102137,12 @@ function restoreIndentation(newStr, originalLines) {
102058
102137
  const targetIndent = detectBaseIndent(originalCode);
102059
102138
  const newIndent = detectBaseIndent(newStr);
102060
102139
  if (targetIndent !== newIndent) {
102140
+ const indentDiff = Math.abs(targetIndent.length - newIndent.length);
102141
+ const useTabs = targetIndent.includes(" ") || newIndent.includes(" ");
102142
+ const maxAllowedDiff = useTabs ? 1 : 4;
102143
+ if (indentDiff > maxAllowedDiff) {
102144
+ return { result: newStr, modifications };
102145
+ }
102061
102146
  const reindented = reindent(newStr, targetIndent);
102062
102147
  if (reindented !== newStr) {
102063
102148
  modifications.push(`reindented from "${newIndent}" to "${targetIndent}"`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc291",
3
+ "version": "0.6.0-rc293",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -234,7 +234,14 @@ export function generateSandboxGlobals(options) {
234
234
  }
235
235
  return tryParseJSONValue(text);
236
236
  };
237
- globals[name] = traceToolCall(name, rawMcpFn, tracer, logFn);
237
+ const tracedFn = traceToolCall(name, rawMcpFn, tracer, logFn);
238
+ globals[name] = tracedFn;
239
+ // Register sanitized alias for names with hyphens/dots/etc that aren't valid JS identifiers
240
+ // e.g. "workable-api" → also available as "workable_api"
241
+ const sanitized = name.replace(/[^a-zA-Z0-9_$]/g, '_');
242
+ if (sanitized !== name) {
243
+ globals[sanitized] = tracedFn;
244
+ }
238
245
  }
239
246
  }
240
247
 
@@ -181,9 +181,17 @@ export function createDSLRuntime(options) {
181
181
  'dsl.error': e.message?.substring(0, 500),
182
182
  });
183
183
 
184
+ // Enrich "X is not defined" errors with available tool names
185
+ let errorMsg = `Execution failed: ${e.message}`;
186
+ if (e.message && e.message.includes('is not defined')) {
187
+ const globalNames = Object.keys(toolGlobals).sort();
188
+ errorMsg += `\nAvailable functions: ${globalNames.join(', ')}`;
189
+ errorMsg += `\nNote: Tools with hyphens (e.g. "my-tool") are available with underscores: my_tool()`;
190
+ }
191
+
184
192
  return {
185
193
  status: 'error',
186
- error: `Execution failed: ${e.message}`,
194
+ error: errorMsg,
187
195
  logs,
188
196
  };
189
197
  }
@@ -90,9 +90,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
90
90
  - Do not add code comments unless the logic is genuinely complex and non-obvious.
91
91
 
92
92
  # Before Implementation
93
- - Focus on high-level design patterns and system organization
94
- - Identify architectural patterns and component relationships
95
- - Evaluate system structure and suggest architectural improvements
93
+ - 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
+ - Read neighboring files — understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
95
+ - Trace the call chain — follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
96
96
  - Focus on backward compatibility
97
97
  - Consider scalability, maintainability, and extensibility in your analysis
98
98
 
@@ -117,6 +117,20 @@ Before building or testing, determine the project's toolchain:
117
117
  - Read README for build/test instructions if the above are unclear
118
118
  - Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
119
119
 
120
+ # File Editing Rules
121
+ You have access to the \`edit\`, \`create\`, and \`multi_edit\` tools for modifying files. You MUST use these tools for ALL code changes. They are purpose-built, atomic, and safe.
122
+
123
+ DO NOT use sed, awk, echo/cat redirection, or heredocs to modify source code. These commands cause real damage in practice: truncated lines, duplicate code blocks, broken syntax. Every bad edit wastes iterations on fix-up commits.
124
+
125
+ Use the right tool:
126
+ 1. To MODIFY existing code → \`edit\` tool (old_string → new_string, or start_line/end_line)
127
+ 2. To CREATE a new file → \`create\` tool
128
+ 3. To CHANGE multiple files at once → \`multi_edit\` tool
129
+ 4. To READ code → \`extract\` or \`search\` tools
130
+ 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
+
132
+ 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.
133
+
120
134
  # During Implementation
121
135
  - Always create a new branch before making changes to the codebase.
122
136
  - Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
@@ -143,6 +157,22 @@ Before committing or creating a PR, run through this checklist:
143
157
 
144
158
  Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
145
159
 
160
+ # Output Integrity
161
+ Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
162
+
163
+ - Only report PR URLs you actually created or updated with \`gh pr create\` or \`git push\`. If you checked out an existing PR but did NOT push changes to it, do NOT claim you updated it.
164
+ - Describe what you ACTUALLY DID, not what you planned or intended to do. If you ran out of iterations, say so. If tests failed, say so.
165
+ - Only list files you actually modified AND committed.
166
+ - If you could not complete the task — ran out of iterations, tests failed, build broken, push rejected — report the real reason honestly.
167
+
168
+ NEVER claim success when:
169
+ - You did not run \`git push\` successfully
170
+ - Tests failed and you did not fix them
171
+ - You hit the iteration limit before completing the work
172
+ - You only analyzed/investigated but did not implement changes
173
+
174
+ A false success report is WORSE than an honest failure — it misleads the user into thinking work is done when it is not.
175
+
146
176
  # GitHub Integration
147
177
  - Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
148
178
  - To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
@@ -778,6 +778,7 @@ return table;
778
778
  - Do NOT define helper functions that call tools. Write all logic inline or use for..of loops.
779
779
  - Do NOT use regex literals (/pattern/) — use String methods like indexOf, includes, startsWith instead.
780
780
  - ONLY use functions listed below. Do NOT call functions that are not listed.
781
+ - MCP tools with hyphens in their names (e.g. \`workable-api\`) are available using underscores: \`workable_api()\`. Hyphens are not valid in JS identifiers.
781
782
 
782
783
  ### Available functions
783
784