@probelabs/probe 0.6.0-rc173 → 0.6.0-rc174

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
@@ -104,7 +104,7 @@ var require_package = __commonJS({
104
104
  // node_modules/dotenv/lib/main.js
105
105
  var require_main = __commonJS({
106
106
  "node_modules/dotenv/lib/main.js"(exports2, module2) {
107
- var fs8 = require("fs");
107
+ var fs9 = require("fs");
108
108
  var path8 = require("path");
109
109
  var os4 = require("os");
110
110
  var crypto2 = require("crypto");
@@ -213,7 +213,7 @@ var require_main = __commonJS({
213
213
  if (options && options.path && options.path.length > 0) {
214
214
  if (Array.isArray(options.path)) {
215
215
  for (const filepath of options.path) {
216
- if (fs8.existsSync(filepath)) {
216
+ if (fs9.existsSync(filepath)) {
217
217
  possibleVaultPath = filepath.endsWith(".vault") ? filepath : `${filepath}.vault`;
218
218
  }
219
219
  }
@@ -223,7 +223,7 @@ var require_main = __commonJS({
223
223
  } else {
224
224
  possibleVaultPath = path8.resolve(process.cwd(), ".env.vault");
225
225
  }
226
- if (fs8.existsSync(possibleVaultPath)) {
226
+ if (fs9.existsSync(possibleVaultPath)) {
227
227
  return possibleVaultPath;
228
228
  }
229
229
  return null;
@@ -272,7 +272,7 @@ var require_main = __commonJS({
272
272
  const parsedAll = {};
273
273
  for (const path9 of optionPaths) {
274
274
  try {
275
- const parsed = DotenvModule.parse(fs8.readFileSync(path9, { encoding }));
275
+ const parsed = DotenvModule.parse(fs9.readFileSync(path9, { encoding }));
276
276
  DotenvModule.populate(parsedAll, parsed, options);
277
277
  } catch (e4) {
278
278
  if (debug) {
@@ -563,6 +563,48 @@ var init_directory_resolver = __esm({
563
563
  }
564
564
  });
565
565
 
566
+ // src/utils/symlink-utils.js
567
+ async function getEntryType(entry, fullPath) {
568
+ try {
569
+ const stats = await import_fs2.promises.stat(fullPath);
570
+ return {
571
+ isFile: stats.isFile(),
572
+ isDirectory: stats.isDirectory(),
573
+ size: stats.size
574
+ };
575
+ } catch {
576
+ return {
577
+ isFile: entry.isFile(),
578
+ isDirectory: entry.isDirectory(),
579
+ size: 0
580
+ };
581
+ }
582
+ }
583
+ function getEntryTypeSync(entry, fullPath) {
584
+ try {
585
+ const stats = import_fs.default.statSync(fullPath);
586
+ return {
587
+ isFile: stats.isFile(),
588
+ isDirectory: stats.isDirectory(),
589
+ size: stats.size
590
+ };
591
+ } catch {
592
+ return {
593
+ isFile: entry.isFile(),
594
+ isDirectory: entry.isDirectory(),
595
+ size: 0
596
+ };
597
+ }
598
+ }
599
+ var import_fs, import_fs2;
600
+ var init_symlink_utils = __esm({
601
+ "src/utils/symlink-utils.js"() {
602
+ "use strict";
603
+ import_fs = __toESM(require("fs"), 1);
604
+ import_fs2 = require("fs");
605
+ }
606
+ });
607
+
566
608
  // src/downloader.js
567
609
  function sanitizeError(err) {
568
610
  try {
@@ -1077,10 +1119,11 @@ async function extractBinary(assetPath, outputDir) {
1077
1119
  const entries = await import_fs_extra2.default.readdir(dir, { withFileTypes: true });
1078
1120
  for (const entry of entries) {
1079
1121
  const fullPath = import_path2.default.join(dir, entry.name);
1080
- if (entry.isDirectory()) {
1122
+ const entryType = await getEntryType(entry, fullPath);
1123
+ if (entryType.isDirectory) {
1081
1124
  const result = await findBinary(fullPath);
1082
1125
  if (result) return result;
1083
- } else if (entry.isFile()) {
1126
+ } else if (entryType.isFile) {
1084
1127
  if (entry.name === binaryName || entry.name === BINARY_NAME || isWindows && entry.name.endsWith(".exe")) {
1085
1128
  return fullPath;
1086
1129
  }
@@ -1293,6 +1336,7 @@ var init_downloader = __esm({
1293
1336
  import_url2 = require("url");
1294
1337
  init_utils();
1295
1338
  init_directory_resolver();
1339
+ init_symlink_utils();
1296
1340
  exec = (0, import_util.promisify)(import_child_process.exec);
1297
1341
  REPO_OWNER = "probelabs";
1298
1342
  REPO_NAME = "probe";
@@ -20478,7 +20522,7 @@ var require_dist_cjs56 = __commonJS({
20478
20522
  var httpAuthSchemes = (init_httpAuthSchemes2(), __toCommonJS(httpAuthSchemes_exports));
20479
20523
  var propertyProvider = require_dist_cjs24();
20480
20524
  var sharedIniFileLoader = require_dist_cjs42();
20481
- var fs8 = require("fs");
20525
+ var fs9 = require("fs");
20482
20526
  var fromEnvSigningName = ({ logger: logger2, signingName } = {}) => async () => {
20483
20527
  logger2?.debug?.("@aws-sdk/token-providers - fromEnvSigningName");
20484
20528
  if (!signingName) {
@@ -20524,7 +20568,7 @@ var require_dist_cjs56 = __commonJS({
20524
20568
  throw new propertyProvider.TokenProviderError(`Value not present for '${key}' in SSO Token${forRefresh ? ". Cannot refresh" : ""}. ${REFRESH_MESSAGE}`, false);
20525
20569
  }
20526
20570
  };
20527
- var { writeFile } = fs8.promises;
20571
+ var { writeFile } = fs9.promises;
20528
20572
  var writeSSOTokenToFile = (id, ssoToken) => {
20529
20573
  const tokenFilepath = sharedIniFileLoader.getSSOTokenFilepath(id);
20530
20574
  const tokenString = JSON.stringify(ssoToken, null, 2);
@@ -38202,22 +38246,22 @@ var init_esm3 = __esm({
38202
38246
  });
38203
38247
 
38204
38248
  // node_modules/path-scurry/dist/esm/index.js
38205
- var import_node_path, import_node_url, import_fs, actualFS, import_promises, realpathSync, 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;
38249
+ var import_node_path, import_node_url, import_fs3, actualFS, import_promises, realpathSync, 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;
38206
38250
  var init_esm4 = __esm({
38207
38251
  "node_modules/path-scurry/dist/esm/index.js"() {
38208
38252
  init_esm2();
38209
38253
  import_node_path = require("node:path");
38210
38254
  import_node_url = require("node:url");
38211
- import_fs = require("fs");
38255
+ import_fs3 = require("fs");
38212
38256
  actualFS = __toESM(require("node:fs"), 1);
38213
38257
  import_promises = require("node:fs/promises");
38214
38258
  init_esm3();
38215
- realpathSync = import_fs.realpathSync.native;
38259
+ realpathSync = import_fs3.realpathSync.native;
38216
38260
  defaultFS = {
38217
- lstatSync: import_fs.lstatSync,
38218
- readdir: import_fs.readdir,
38219
- readdirSync: import_fs.readdirSync,
38220
- readlinkSync: import_fs.readlinkSync,
38261
+ lstatSync: import_fs3.lstatSync,
38262
+ readdir: import_fs3.readdir,
38263
+ readdirSync: import_fs3.readdirSync,
38264
+ readlinkSync: import_fs3.readlinkSync,
38221
38265
  realpathSync,
38222
38266
  promises: {
38223
38267
  lstat: import_promises.lstat,
@@ -39328,8 +39372,8 @@ var init_esm4 = __esm({
39328
39372
  *
39329
39373
  * @internal
39330
39374
  */
39331
- constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs8 = defaultFS } = {}) {
39332
- this.#fs = fsFromOption(fs8);
39375
+ constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs9 = defaultFS } = {}) {
39376
+ this.#fs = fsFromOption(fs9);
39333
39377
  if (cwd instanceof URL || cwd.startsWith("file://")) {
39334
39378
  cwd = (0, import_node_url.fileURLToPath)(cwd);
39335
39379
  }
@@ -39887,8 +39931,8 @@ var init_esm4 = __esm({
39887
39931
  /**
39888
39932
  * @internal
39889
39933
  */
39890
- newRoot(fs8) {
39891
- return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs8 });
39934
+ newRoot(fs9) {
39935
+ return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs9 });
39892
39936
  }
39893
39937
  /**
39894
39938
  * Return true if the provided path string is an absolute path
@@ -39916,8 +39960,8 @@ var init_esm4 = __esm({
39916
39960
  /**
39917
39961
  * @internal
39918
39962
  */
39919
- newRoot(fs8) {
39920
- return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs8 });
39963
+ newRoot(fs9) {
39964
+ return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs9 });
39921
39965
  }
39922
39966
  /**
39923
39967
  * Return true if the provided path string is an absolute path
@@ -41126,7 +41170,7 @@ function createWrappedTools(baseTools) {
41126
41170
  }
41127
41171
  return wrappedTools;
41128
41172
  }
41129
- var import_child_process6, import_util11, import_crypto3, import_events, import_fs2, import_fs3, import_path4, toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
41173
+ var import_child_process6, import_util11, import_crypto3, import_events, import_fs4, import_fs5, import_path4, toolCallEmitter, activeToolExecutions, wrapToolWithEmitter, listFilesTool, searchFilesTool, listFilesToolInstance, searchFilesToolInstance;
41130
41174
  var init_probeTool = __esm({
41131
41175
  "src/agent/probeTool.js"() {
41132
41176
  "use strict";
@@ -41135,10 +41179,11 @@ var init_probeTool = __esm({
41135
41179
  import_util11 = require("util");
41136
41180
  import_crypto3 = require("crypto");
41137
41181
  import_events = require("events");
41138
- import_fs2 = __toESM(require("fs"), 1);
41139
- import_fs3 = require("fs");
41182
+ import_fs4 = __toESM(require("fs"), 1);
41183
+ import_fs5 = require("fs");
41140
41184
  import_path4 = __toESM(require("path"), 1);
41141
41185
  init_esm5();
41186
+ init_symlink_utils();
41142
41187
  toolCallEmitter = new import_events.EventEmitter();
41143
41188
  activeToolExecutions = /* @__PURE__ */ new Map();
41144
41189
  wrapToolWithEmitter = (tool4, toolName, baseExecute) => {
@@ -41244,7 +41289,7 @@ var init_probeTool = __esm({
41244
41289
  console.log(`[DEBUG] Listing files in directory: ${targetDir}`);
41245
41290
  }
41246
41291
  try {
41247
- const files = await import_fs3.promises.readdir(targetDir, { withFileTypes: true });
41292
+ const files = await import_fs5.promises.readdir(targetDir, { withFileTypes: true });
41248
41293
  const formatSize = (size) => {
41249
41294
  if (size < 1024) return `${size}B`;
41250
41295
  if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}K`;
@@ -41252,21 +41297,12 @@ var init_probeTool = __esm({
41252
41297
  return `${(size / (1024 * 1024 * 1024)).toFixed(1)}G`;
41253
41298
  };
41254
41299
  const entries = await Promise.all(files.map(async (file) => {
41255
- const isDirectory = file.isDirectory();
41256
41300
  const fullPath = import_path4.default.join(targetDir, file.name);
41257
- let size = 0;
41258
- try {
41259
- const stats = await import_fs3.promises.stat(fullPath);
41260
- size = stats.size;
41261
- } catch (statError) {
41262
- if (debug) {
41263
- console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
41264
- }
41265
- }
41301
+ const entryType = await getEntryType(file, fullPath);
41266
41302
  return {
41267
41303
  name: file.name,
41268
- isDirectory,
41269
- size
41304
+ isDirectory: entryType.isDirectory,
41305
+ size: entryType.size
41270
41306
  };
41271
41307
  }));
41272
41308
  entries.sort((a4, b4) => {
@@ -55485,7 +55521,7 @@ function validateFlowchart(text, options = {}) {
55485
55521
  const byLine = /* @__PURE__ */ new Map();
55486
55522
  const collect = (arr) => {
55487
55523
  for (const e4 of arr || []) {
55488
- if (e4 && (e4.code === "FL-LABEL-PARENS-UNQUOTED" || e4.code === "FL-LABEL-AT-IN-UNQUOTED")) {
55524
+ if (e4 && (e4.code === "FL-LABEL-PARENS-UNQUOTED" || e4.code === "FL-LABEL-AT-IN-UNQUOTED" || e4.code === "FL-LABEL-QUOTE-IN-UNQUOTED")) {
55489
55525
  const ln = e4.line ?? 0;
55490
55526
  const col = e4.column ?? 1;
55491
55527
  const list2 = byLine.get(ln) || [];
@@ -55566,6 +55602,8 @@ function validateFlowchart(text, options = {}) {
55566
55602
  const covered = existing.some((r4) => !(endCol < r4.start || startCol > r4.end));
55567
55603
  const hasParens = seg.includes("(") || seg.includes(")");
55568
55604
  const hasAt = seg.includes("@");
55605
+ const hasQuote = seg.includes('"');
55606
+ const isSingleQuoted = /^'[^]*'$/.test(trimmed);
55569
55607
  if (!covered && !isQuoted && !isParenWrapped && hasParens) {
55570
55608
  errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-PARENS-UNQUOTED", message: "Parentheses inside an unquoted label are not supported by Mermaid.", hint: 'Wrap the label in quotes, e.g., A["Mark (X)"] \u2014 or replace ( and ) with HTML entities: &#40; and &#41;.' });
55571
55609
  existing.push({ start: startCol, end: endCol });
@@ -55576,6 +55614,11 @@ function validateFlowchart(text, options = {}) {
55576
55614
  existing.push({ start: startCol, end: endCol });
55577
55615
  byLine.set(ln, existing);
55578
55616
  }
55617
+ if (!covered && !isQuoted && !isSlashPair && hasQuote && !isSingleQuoted) {
55618
+ errs.push({ line: ln, column: startCol, severity: "error", code: "FL-LABEL-QUOTE-IN-UNQUOTED", message: "Quotes are not allowed inside unquoted node labels. Use &quot; for quotes or wrap the entire label in quotes.", hint: 'Example: C["HTML Output: data-trigger-visibility=&quot;true&quot;"]' });
55619
+ existing.push({ start: startCol, end: endCol });
55620
+ byLine.set(ln, existing);
55621
+ }
55579
55622
  i4 = j4;
55580
55623
  continue;
55581
55624
  } else {
@@ -79637,11 +79680,11 @@ function loadMCPConfigurationFromPath(configPath) {
79637
79680
  if (!configPath) {
79638
79681
  throw new Error("Config path is required");
79639
79682
  }
79640
- if (!(0, import_fs4.existsSync)(configPath)) {
79683
+ if (!(0, import_fs6.existsSync)(configPath)) {
79641
79684
  throw new Error(`MCP configuration file not found: ${configPath}`);
79642
79685
  }
79643
79686
  try {
79644
- const content = (0, import_fs4.readFileSync)(configPath, "utf8");
79687
+ const content = (0, import_fs6.readFileSync)(configPath, "utf8");
79645
79688
  const config = JSON.parse(content);
79646
79689
  if (process.env.DEBUG === "1" || process.env.DEBUG_MCP === "1") {
79647
79690
  console.error(`[MCP DEBUG] Loaded configuration from: ${configPath}`);
@@ -79666,9 +79709,9 @@ function loadMCPConfiguration() {
79666
79709
  ].filter(Boolean);
79667
79710
  let config = null;
79668
79711
  for (const configPath of configPaths) {
79669
- if ((0, import_fs4.existsSync)(configPath)) {
79712
+ if ((0, import_fs6.existsSync)(configPath)) {
79670
79713
  try {
79671
- const content = (0, import_fs4.readFileSync)(configPath, "utf8");
79714
+ const content = (0, import_fs6.readFileSync)(configPath, "utf8");
79672
79715
  config = JSON.parse(content);
79673
79716
  if (process.env.DEBUG === "1" || process.env.DEBUG_MCP === "1") {
79674
79717
  console.error(`[MCP DEBUG] Loaded configuration from: ${configPath}`);
@@ -79764,11 +79807,11 @@ function parseEnabledServers(config) {
79764
79807
  }
79765
79808
  return servers;
79766
79809
  }
79767
- var import_fs4, import_path5, import_os3, import_url4, __filename4, __dirname4, DEFAULT_CONFIG;
79810
+ var import_fs6, import_path5, import_os3, import_url4, __filename4, __dirname4, DEFAULT_CONFIG;
79768
79811
  var init_config = __esm({
79769
79812
  "src/agent/mcp/config.js"() {
79770
79813
  "use strict";
79771
- import_fs4 = require("fs");
79814
+ import_fs6 = require("fs");
79772
79815
  import_path5 = require("path");
79773
79816
  import_os3 = require("os");
79774
79817
  import_url4 = require("url");
@@ -82637,7 +82680,7 @@ var ProbeAgent_exports = {};
82637
82680
  __export(ProbeAgent_exports, {
82638
82681
  ProbeAgent: () => ProbeAgent
82639
82682
  });
82640
- var import_dotenv, import_anthropic2, import_openai2, import_google2, import_ai2, import_crypto7, import_events4, import_fs5, import_promises3, import_path7, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
82683
+ var import_dotenv, import_anthropic2, import_openai2, import_google2, import_ai2, import_crypto7, import_events4, import_fs7, import_promises3, import_path7, MAX_TOOL_ITERATIONS, MAX_HISTORY_MESSAGES, MAX_IMAGE_FILE_SIZE, ProbeAgent;
82641
82684
  var init_ProbeAgent = __esm({
82642
82685
  "src/agent/ProbeAgent.js"() {
82643
82686
  "use strict";
@@ -82649,7 +82692,7 @@ var init_ProbeAgent = __esm({
82649
82692
  import_ai2 = require("ai");
82650
82693
  import_crypto7 = require("crypto");
82651
82694
  import_events4 = require("events");
82652
- import_fs5 = require("fs");
82695
+ import_fs7 = require("fs");
82653
82696
  import_promises3 = require("fs/promises");
82654
82697
  import_path7 = require("path");
82655
82698
  init_tokenCounter();
@@ -86743,7 +86786,7 @@ async function executeBashCommand(command, options = {}) {
86743
86786
  let cwd = workingDirectory;
86744
86787
  try {
86745
86788
  cwd = (0, import_path8.resolve)(cwd);
86746
- if (!(0, import_fs6.existsSync)(cwd)) {
86789
+ if (!(0, import_fs8.existsSync)(cwd)) {
86747
86790
  throw new Error(`Working directory does not exist: ${cwd}`);
86748
86791
  }
86749
86792
  } catch (error2) {
@@ -86955,7 +86998,7 @@ function validateExecutionOptions(options = {}) {
86955
86998
  if (options.workingDirectory) {
86956
86999
  if (typeof options.workingDirectory !== "string") {
86957
87000
  errors.push("workingDirectory must be a string");
86958
- } else if (!(0, import_fs6.existsSync)(options.workingDirectory)) {
87001
+ } else if (!(0, import_fs8.existsSync)(options.workingDirectory)) {
86959
87002
  errors.push(`workingDirectory does not exist: ${options.workingDirectory}`);
86960
87003
  }
86961
87004
  }
@@ -86968,13 +87011,13 @@ function validateExecutionOptions(options = {}) {
86968
87011
  warnings
86969
87012
  };
86970
87013
  }
86971
- var import_child_process9, import_path8, import_fs6;
87014
+ var import_child_process9, import_path8, import_fs8;
86972
87015
  var init_bashExecutor = __esm({
86973
87016
  "src/agent/bashExecutor.js"() {
86974
87017
  "use strict";
86975
87018
  import_child_process9 = require("child_process");
86976
87019
  import_path8 = require("path");
86977
- import_fs6 = require("fs");
87020
+ import_fs8 = require("fs");
86978
87021
  init_bashCommandUtils();
86979
87022
  }
86980
87023
  });
@@ -87170,14 +87213,14 @@ function parseFileToolOptions(options = {}) {
87170
87213
  defaultPath: options.defaultPath
87171
87214
  };
87172
87215
  }
87173
- var import_ai5, import_fs7, import_path10, import_fs8, editTool, createTool, editSchema, createSchema, editDescription, createDescription, editToolDefinition, createToolDefinition;
87216
+ var import_ai5, import_fs9, import_path10, import_fs10, editTool, createTool, editSchema, createSchema, editDescription, createDescription, editToolDefinition, createToolDefinition;
87174
87217
  var init_edit = __esm({
87175
87218
  "src/tools/edit.js"() {
87176
87219
  "use strict";
87177
87220
  import_ai5 = require("ai");
87178
- import_fs7 = require("fs");
87221
+ import_fs9 = require("fs");
87179
87222
  import_path10 = require("path");
87180
- import_fs8 = require("fs");
87223
+ import_fs10 = require("fs");
87181
87224
  editTool = (options = {}) => {
87182
87225
  const { debug, allowedFolders, defaultPath } = parseFileToolOptions(options);
87183
87226
  return (0, import_ai5.tool)({
@@ -87237,10 +87280,10 @@ Important:
87237
87280
  if (!isPathAllowed(resolvedPath2, allowedFolders)) {
87238
87281
  return `Error editing file: Permission denied - ${file_path} is outside allowed directories`;
87239
87282
  }
87240
- if (!(0, import_fs8.existsSync)(resolvedPath2)) {
87283
+ if (!(0, import_fs10.existsSync)(resolvedPath2)) {
87241
87284
  return `Error editing file: File not found - ${file_path}`;
87242
87285
  }
87243
- const content = await import_fs7.promises.readFile(resolvedPath2, "utf-8");
87286
+ const content = await import_fs9.promises.readFile(resolvedPath2, "utf-8");
87244
87287
  if (!content.includes(old_string)) {
87245
87288
  return `Error editing file: String not found - the specified old_string was not found in ${file_path}`;
87246
87289
  }
@@ -87257,7 +87300,7 @@ Important:
87257
87300
  if (newContent === content) {
87258
87301
  return `Error editing file: No changes made - old_string and new_string might be the same`;
87259
87302
  }
87260
- await import_fs7.promises.writeFile(resolvedPath2, newContent, "utf-8");
87303
+ await import_fs9.promises.writeFile(resolvedPath2, newContent, "utf-8");
87261
87304
  const replacedCount = replace_all ? occurrences : 1;
87262
87305
  if (debug) {
87263
87306
  console.error(`[Edit] Successfully edited ${resolvedPath2}, replaced ${replacedCount} occurrence(s)`);
@@ -87321,13 +87364,13 @@ Important:
87321
87364
  if (!isPathAllowed(resolvedPath2, allowedFolders)) {
87322
87365
  return `Error creating file: Permission denied - ${file_path} is outside allowed directories`;
87323
87366
  }
87324
- if ((0, import_fs8.existsSync)(resolvedPath2) && !overwrite) {
87367
+ if ((0, import_fs10.existsSync)(resolvedPath2) && !overwrite) {
87325
87368
  return `Error creating file: File already exists - ${file_path}. Use overwrite: true to replace it.`;
87326
87369
  }
87327
87370
  const dir = (0, import_path10.dirname)(resolvedPath2);
87328
- await import_fs7.promises.mkdir(dir, { recursive: true });
87329
- await import_fs7.promises.writeFile(resolvedPath2, content, "utf-8");
87330
- const action = (0, import_fs8.existsSync)(resolvedPath2) && overwrite ? "overwrote" : "created";
87371
+ await import_fs9.promises.mkdir(dir, { recursive: true });
87372
+ await import_fs9.promises.writeFile(resolvedPath2, content, "utf-8");
87373
+ const action = (0, import_fs10.existsSync)(resolvedPath2) && overwrite ? "overwrote" : "created";
87331
87374
  const bytes = Buffer.byteLength(content, "utf-8");
87332
87375
  if (debug) {
87333
87376
  console.error(`[Create] Successfully ${action} ${resolvedPath2}`);
@@ -87743,10 +87786,10 @@ async function listFilesByLevel(options) {
87743
87786
  maxFiles = 100,
87744
87787
  respectGitignore = true
87745
87788
  } = options;
87746
- if (!import_fs9.default.existsSync(directory)) {
87789
+ if (!import_fs11.default.existsSync(directory)) {
87747
87790
  throw new Error(`Directory does not exist: ${directory}`);
87748
87791
  }
87749
- const gitDirExists = import_fs9.default.existsSync(import_path11.default.join(directory, ".git"));
87792
+ const gitDirExists = import_fs11.default.existsSync(import_path11.default.join(directory, ".git"));
87750
87793
  if (gitDirExists && respectGitignore) {
87751
87794
  try {
87752
87795
  return await listFilesUsingGit(directory, maxFiles);
@@ -87777,8 +87820,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
87777
87820
  while (queue.length > 0 && result.length < maxFiles) {
87778
87821
  const { dir, level } = queue.shift();
87779
87822
  try {
87780
- const entries = import_fs9.default.readdirSync(dir, { withFileTypes: true });
87781
- const files = entries.filter((entry) => entry.isFile());
87823
+ const entries = import_fs11.default.readdirSync(dir, { withFileTypes: true });
87824
+ const files = entries.filter((entry) => {
87825
+ const fullPath = import_path11.default.join(dir, entry.name);
87826
+ return getEntryTypeSync(entry, fullPath).isFile;
87827
+ });
87782
87828
  for (const file of files) {
87783
87829
  if (result.length >= maxFiles) break;
87784
87830
  const filePath = import_path11.default.join(dir, file.name);
@@ -87786,7 +87832,10 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
87786
87832
  if (shouldIgnore(relativePath, ignorePatterns)) continue;
87787
87833
  result.push(relativePath);
87788
87834
  }
87789
- const dirs = entries.filter((entry) => entry.isDirectory());
87835
+ const dirs = entries.filter((entry) => {
87836
+ const fullPath = import_path11.default.join(dir, entry.name);
87837
+ return getEntryTypeSync(entry, fullPath).isDirectory;
87838
+ });
87790
87839
  for (const subdir of dirs) {
87791
87840
  const subdirPath = import_path11.default.join(dir, subdir.name);
87792
87841
  const relativeSubdirPath = import_path11.default.relative(directory, subdirPath);
@@ -87802,11 +87851,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
87802
87851
  }
87803
87852
  function loadGitignorePatterns(directory) {
87804
87853
  const gitignorePath = import_path11.default.join(directory, ".gitignore");
87805
- if (!import_fs9.default.existsSync(gitignorePath)) {
87854
+ if (!import_fs11.default.existsSync(gitignorePath)) {
87806
87855
  return [];
87807
87856
  }
87808
87857
  try {
87809
- const content = import_fs9.default.readFileSync(gitignorePath, "utf8");
87858
+ const content = import_fs11.default.readFileSync(gitignorePath, "utf8");
87810
87859
  return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
87811
87860
  } catch (error2) {
87812
87861
  console.error(`Warning: Could not read .gitignore: ${error2.message}`);
@@ -87824,14 +87873,15 @@ function shouldIgnore(filePath, ignorePatterns) {
87824
87873
  }
87825
87874
  return false;
87826
87875
  }
87827
- var import_fs9, import_path11, import_util12, import_child_process10, execAsync3;
87876
+ var import_fs11, import_path11, import_util12, import_child_process10, execAsync3;
87828
87877
  var init_file_lister = __esm({
87829
87878
  "src/utils/file-lister.js"() {
87830
87879
  "use strict";
87831
- import_fs9 = __toESM(require("fs"), 1);
87880
+ import_fs11 = __toESM(require("fs"), 1);
87832
87881
  import_path11 = __toESM(require("path"), 1);
87833
87882
  import_util12 = require("util");
87834
87883
  import_child_process10 = require("child_process");
87884
+ init_symlink_utils();
87835
87885
  execAsync3 = (0, import_util12.promisify)(import_child_process10.exec);
87836
87886
  }
87837
87887
  });
@@ -87846,11 +87896,11 @@ function initializeSimpleTelemetryFromOptions(options) {
87846
87896
  });
87847
87897
  return telemetry;
87848
87898
  }
87849
- var import_fs10, import_path12, SimpleTelemetry, SimpleAppTracer;
87899
+ var import_fs12, import_path12, SimpleTelemetry, SimpleAppTracer;
87850
87900
  var init_simpleTelemetry = __esm({
87851
87901
  "src/agent/simpleTelemetry.js"() {
87852
87902
  "use strict";
87853
- import_fs10 = require("fs");
87903
+ import_fs12 = require("fs");
87854
87904
  import_path12 = require("path");
87855
87905
  SimpleTelemetry = class {
87856
87906
  constructor(options = {}) {
@@ -87866,10 +87916,10 @@ var init_simpleTelemetry = __esm({
87866
87916
  initializeFileExporter() {
87867
87917
  try {
87868
87918
  const dir = (0, import_path12.dirname)(this.filePath);
87869
- if (!(0, import_fs10.existsSync)(dir)) {
87870
- (0, import_fs10.mkdirSync)(dir, { recursive: true });
87919
+ if (!(0, import_fs12.existsSync)(dir)) {
87920
+ (0, import_fs12.mkdirSync)(dir, { recursive: true });
87871
87921
  }
87872
- this.stream = (0, import_fs10.createWriteStream)(this.filePath, { flags: "a" });
87922
+ this.stream = (0, import_fs12.createWriteStream)(this.filePath, { flags: "a" });
87873
87923
  this.stream.on("error", (error2) => {
87874
87924
  console.error(`[SimpleTelemetry] Stream error: ${error2.message}`);
87875
87925
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc173",
3
+ "version": "0.6.0-rc174",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -79,7 +79,7 @@
79
79
  "@ai-sdk/openai": "^2.0.10",
80
80
  "@anthropic-ai/claude-agent-sdk": "^0.1.46",
81
81
  "@modelcontextprotocol/sdk": "^1.0.0",
82
- "@probelabs/maid": "^0.0.21",
82
+ "@probelabs/maid": "^0.0.22",
83
83
  "adm-zip": "^0.5.16",
84
84
  "ai": "^5.0.0",
85
85
  "ajv": "^8.17.1",
@@ -8,6 +8,7 @@ import fs from 'fs';
8
8
  import { promises as fsPromises } from 'fs';
9
9
  import path from 'path';
10
10
  import { glob } from 'glob';
11
+ import { getEntryType } from '../utils/symlink-utils.js';
11
12
 
12
13
  // Create an event emitter for tool calls (simplified for single-shot operations)
13
14
  export const toolCallEmitter = new EventEmitter();
@@ -287,23 +288,13 @@ export const listFilesTool = {
287
288
 
288
289
  // Format the results as ls-style output
289
290
  const entries = await Promise.all(files.map(async (file) => {
290
- const isDirectory = file.isDirectory();
291
291
  const fullPath = path.join(targetDir, file.name);
292
-
293
- let size = 0;
294
- try {
295
- const stats = await fsPromises.stat(fullPath);
296
- size = stats.size;
297
- } catch (statError) {
298
- if (debug) {
299
- console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
300
- }
301
- }
292
+ const entryType = await getEntryType(file, fullPath);
302
293
 
303
294
  return {
304
295
  name: file.name,
305
- isDirectory,
306
- size
296
+ isDirectory: entryType.isDirectory,
297
+ size: entryType.size
307
298
  };
308
299
  }));
309
300
 
package/src/downloader.js CHANGED
@@ -14,6 +14,7 @@ import os from 'os';
14
14
  import { fileURLToPath } from 'url';
15
15
  import { ensureBinDirectory } from './utils.js';
16
16
  import { getPackageBinDir } from './directory-resolver.js';
17
+ import { getEntryType } from './utils/symlink-utils.js';
17
18
 
18
19
  const exec = promisify(execCallback);
19
20
 
@@ -770,10 +771,13 @@ async function extractBinary(assetPath, outputDir) {
770
771
  for (const entry of entries) {
771
772
  const fullPath = path.join(dir, entry.name);
772
773
 
773
- if (entry.isDirectory()) {
774
+ // Use shared utility to follow symlinks and get actual target type
775
+ const entryType = await getEntryType(entry, fullPath);
776
+
777
+ if (entryType.isDirectory) {
774
778
  const result = await findBinary(fullPath);
775
779
  if (result) return result;
776
- } else if (entry.isFile()) {
780
+ } else if (entryType.isFile) {
777
781
  // Check if this is the binary we're looking for
778
782
  if (entry.name === binaryName ||
779
783
  entry.name === BINARY_NAME ||
package/src/extractor.js CHANGED
@@ -9,6 +9,7 @@ import tar from 'tar';
9
9
  import AdmZip from 'adm-zip';
10
10
  import os from 'os';
11
11
  import { fileURLToPath } from 'url';
12
+ import { getEntryType } from './utils/symlink-utils.js';
12
13
 
13
14
  const __filename = fileURLToPath(import.meta.url);
14
15
  const __dirname = path.dirname(__filename);
@@ -148,10 +149,13 @@ async function findBinary(dir, baseDir, binaryName, isWindows) {
148
149
  continue;
149
150
  }
150
151
 
151
- if (entry.isDirectory()) {
152
+ // Use shared utility to follow symlinks and get actual target type
153
+ const entryType = await getEntryType(entry, fullPath);
154
+
155
+ if (entryType.isDirectory) {
152
156
  const result = await findBinary(fullPath, baseDir, binaryName, isWindows);
153
157
  if (result) return result;
154
- } else if (entry.isFile()) {
158
+ } else if (entryType.isFile) {
155
159
  // Check if this is the binary we're looking for
156
160
  if (entry.name === binaryName ||
157
161
  entry.name === BINARY_NAME ||
@@ -7,6 +7,7 @@ import fs from 'fs';
7
7
  import path from 'path';
8
8
  import { promisify } from 'util';
9
9
  import { exec } from 'child_process';
10
+ import { getEntryTypeSync } from './symlink-utils.js';
10
11
 
11
12
  const execAsync = promisify(exec);
12
13
 
@@ -101,7 +102,10 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
101
102
  const entries = fs.readdirSync(dir, { withFileTypes: true });
102
103
 
103
104
  // Process files first (at current level)
104
- const files = entries.filter(entry => entry.isFile());
105
+ const files = entries.filter(entry => {
106
+ const fullPath = path.join(dir, entry.name);
107
+ return getEntryTypeSync(entry, fullPath).isFile;
108
+ });
105
109
  for (const file of files) {
106
110
  if (result.length >= maxFiles) break;
107
111
 
@@ -115,7 +119,10 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
115
119
  }
116
120
 
117
121
  // Then add directories to queue for next level
118
- const dirs = entries.filter(entry => entry.isDirectory());
122
+ const dirs = entries.filter(entry => {
123
+ const fullPath = path.join(dir, entry.name);
124
+ return getEntryTypeSync(entry, fullPath).isDirectory;
125
+ });
119
126
  for (const subdir of dirs) {
120
127
  const subdirPath = path.join(dir, subdir.name);
121
128
  const relativeSubdirPath = path.relative(directory, subdirPath);