@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.
@@ -2210,9 +2210,50 @@ var init_directory_resolver = __esm({
2210
2210
  }
2211
2211
  });
2212
2212
 
2213
+ // src/utils/symlink-utils.js
2214
+ import fs2 from "fs";
2215
+ import { promises as fsPromises } from "fs";
2216
+ async function getEntryType(entry, fullPath) {
2217
+ try {
2218
+ const stats = await fsPromises.stat(fullPath);
2219
+ return {
2220
+ isFile: stats.isFile(),
2221
+ isDirectory: stats.isDirectory(),
2222
+ size: stats.size
2223
+ };
2224
+ } catch {
2225
+ return {
2226
+ isFile: entry.isFile(),
2227
+ isDirectory: entry.isDirectory(),
2228
+ size: 0
2229
+ };
2230
+ }
2231
+ }
2232
+ function getEntryTypeSync(entry, fullPath) {
2233
+ try {
2234
+ const stats = fs2.statSync(fullPath);
2235
+ return {
2236
+ isFile: stats.isFile(),
2237
+ isDirectory: stats.isDirectory(),
2238
+ size: stats.size
2239
+ };
2240
+ } catch {
2241
+ return {
2242
+ isFile: entry.isFile(),
2243
+ isDirectory: entry.isDirectory(),
2244
+ size: 0
2245
+ };
2246
+ }
2247
+ }
2248
+ var init_symlink_utils = __esm({
2249
+ "src/utils/symlink-utils.js"() {
2250
+ "use strict";
2251
+ }
2252
+ });
2253
+
2213
2254
  // src/downloader.js
2214
2255
  import axios from "axios";
2215
- import fs2 from "fs-extra";
2256
+ import fs3 from "fs-extra";
2216
2257
  import path2 from "path";
2217
2258
  import { createHash } from "crypto";
2218
2259
  import { promisify } from "util";
@@ -2249,7 +2290,7 @@ async function acquireFileLock(lockPath, version) {
2249
2290
  timestamp: Date.now()
2250
2291
  };
2251
2292
  try {
2252
- await fs2.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" });
2293
+ await fs3.writeFile(lockPath, JSON.stringify(lockData), { flag: "wx" });
2253
2294
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2254
2295
  console.log(`Acquired file lock: ${lockPath}`);
2255
2296
  }
@@ -2257,13 +2298,13 @@ async function acquireFileLock(lockPath, version) {
2257
2298
  } catch (error) {
2258
2299
  if (error.code === "EEXIST") {
2259
2300
  try {
2260
- const existingLock = JSON.parse(await fs2.readFile(lockPath, "utf-8"));
2301
+ const existingLock = JSON.parse(await fs3.readFile(lockPath, "utf-8"));
2261
2302
  const lockAge = Date.now() - existingLock.timestamp;
2262
2303
  if (lockAge > LOCK_TIMEOUT_MS) {
2263
2304
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2264
2305
  console.log(`Removing stale lock file (age: ${Math.round(lockAge / 1e3)}s, pid: ${existingLock.pid})`);
2265
2306
  }
2266
- await fs2.remove(lockPath);
2307
+ await fs3.remove(lockPath);
2267
2308
  return false;
2268
2309
  }
2269
2310
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
@@ -2275,7 +2316,7 @@ async function acquireFileLock(lockPath, version) {
2275
2316
  console.log(`Lock file corrupted, removing: ${readError.message}`);
2276
2317
  }
2277
2318
  try {
2278
- await fs2.remove(lockPath);
2319
+ await fs3.remove(lockPath);
2279
2320
  } catch {
2280
2321
  }
2281
2322
  return false;
@@ -2297,7 +2338,7 @@ async function acquireFileLock(lockPath, version) {
2297
2338
  }
2298
2339
  async function releaseFileLock(lockPath) {
2299
2340
  try {
2300
- await fs2.remove(lockPath);
2341
+ await fs3.remove(lockPath);
2301
2342
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2302
2343
  console.log(`Released file lock: ${lockPath}`);
2303
2344
  }
@@ -2310,13 +2351,13 @@ async function releaseFileLock(lockPath) {
2310
2351
  async function waitForFileLock(lockPath, binaryPath) {
2311
2352
  const startTime = Date.now();
2312
2353
  while (Date.now() - startTime < MAX_LOCK_WAIT_MS) {
2313
- if (await fs2.pathExists(binaryPath)) {
2354
+ if (await fs3.pathExists(binaryPath)) {
2314
2355
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2315
2356
  console.log(`Binary now available at ${binaryPath}, download completed by another process`);
2316
2357
  }
2317
2358
  return true;
2318
2359
  }
2319
- const lockExists = await fs2.pathExists(lockPath);
2360
+ const lockExists = await fs3.pathExists(lockPath);
2320
2361
  if (!lockExists) {
2321
2362
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2322
2363
  console.log(`Lock file removed but binary not found - download may have failed`);
@@ -2324,7 +2365,7 @@ async function waitForFileLock(lockPath, binaryPath) {
2324
2365
  return false;
2325
2366
  }
2326
2367
  try {
2327
- const lockData = JSON.parse(await fs2.readFile(lockPath, "utf-8"));
2368
+ const lockData = JSON.parse(await fs3.readFile(lockPath, "utf-8"));
2328
2369
  const lockAge = Date.now() - lockData.timestamp;
2329
2370
  if (lockAge > LOCK_TIMEOUT_MS) {
2330
2371
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
@@ -2642,13 +2683,13 @@ function findBestAsset(assets, osInfo, archInfo) {
2642
2683
  return bestAsset;
2643
2684
  }
2644
2685
  async function downloadAsset(asset, outputDir) {
2645
- await fs2.ensureDir(outputDir);
2686
+ await fs3.ensureDir(outputDir);
2646
2687
  const assetPath = path2.join(outputDir, asset.name);
2647
2688
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2648
2689
  console.log(`Downloading ${asset.name}...`);
2649
2690
  }
2650
2691
  const assetResponse = await axios.get(asset.url, { responseType: "arraybuffer" });
2651
- await fs2.writeFile(assetPath, Buffer.from(assetResponse.data));
2692
+ await fs3.writeFile(assetPath, Buffer.from(assetResponse.data));
2652
2693
  const checksumUrl = asset.checksumUrl || `${asset.url}.sha256`;
2653
2694
  const checksumFileName = asset.checksumName || `${asset.name}.sha256`;
2654
2695
  let checksumPath = null;
@@ -2658,7 +2699,7 @@ async function downloadAsset(asset, outputDir) {
2658
2699
  }
2659
2700
  const checksumResponse = await axios.get(checksumUrl);
2660
2701
  checksumPath = path2.join(outputDir, checksumFileName);
2661
- await fs2.writeFile(checksumPath, checksumResponse.data);
2702
+ await fs3.writeFile(checksumPath, checksumResponse.data);
2662
2703
  } catch (error) {
2663
2704
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2664
2705
  console.log("No checksum file found, skipping verification");
@@ -2673,9 +2714,9 @@ async function verifyChecksum(assetPath, checksumPath) {
2673
2714
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2674
2715
  console.log(`Verifying checksum...`);
2675
2716
  }
2676
- const checksumContent = await fs2.readFile(checksumPath, "utf-8");
2717
+ const checksumContent = await fs3.readFile(checksumPath, "utf-8");
2677
2718
  const expectedChecksum = checksumContent.trim().split(" ")[0];
2678
- const fileBuffer = await fs2.readFile(assetPath);
2719
+ const fileBuffer = await fs3.readFile(assetPath);
2679
2720
  const actualChecksum = createHash("sha256").update(fileBuffer).digest("hex");
2680
2721
  if (expectedChecksum !== actualChecksum) {
2681
2722
  console.error(`Checksum verification failed!`);
@@ -2698,7 +2739,7 @@ async function extractBinary(assetPath, outputDir) {
2698
2739
  const binaryPath = path2.join(outputDir, binaryName);
2699
2740
  try {
2700
2741
  const extractDir = path2.join(outputDir, "temp_extract");
2701
- await fs2.ensureDir(extractDir);
2742
+ await fs3.ensureDir(extractDir);
2702
2743
  if (assetName.endsWith(".tar.gz") || assetName.endsWith(".tgz")) {
2703
2744
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2704
2745
  console.log(`Extracting tar.gz to ${extractDir}...`);
@@ -2716,11 +2757,11 @@ async function extractBinary(assetPath, outputDir) {
2716
2757
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2717
2758
  console.log(`Copying binary directly to ${binaryPath}`);
2718
2759
  }
2719
- await fs2.copyFile(assetPath, binaryPath);
2760
+ await fs3.copyFile(assetPath, binaryPath);
2720
2761
  if (!isWindows) {
2721
- await fs2.chmod(binaryPath, 493);
2762
+ await fs3.chmod(binaryPath, 493);
2722
2763
  }
2723
- await fs2.remove(extractDir);
2764
+ await fs3.remove(extractDir);
2724
2765
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2725
2766
  console.log(`Binary installed to ${binaryPath}`);
2726
2767
  }
@@ -2730,13 +2771,14 @@ async function extractBinary(assetPath, outputDir) {
2730
2771
  console.log(`Searching for binary in extracted files...`);
2731
2772
  }
2732
2773
  const findBinary = async (dir) => {
2733
- const entries = await fs2.readdir(dir, { withFileTypes: true });
2774
+ const entries = await fs3.readdir(dir, { withFileTypes: true });
2734
2775
  for (const entry of entries) {
2735
2776
  const fullPath = path2.join(dir, entry.name);
2736
- if (entry.isDirectory()) {
2777
+ const entryType = await getEntryType(entry, fullPath);
2778
+ if (entryType.isDirectory) {
2737
2779
  const result = await findBinary(fullPath);
2738
2780
  if (result) return result;
2739
- } else if (entry.isFile()) {
2781
+ } else if (entryType.isFile) {
2740
2782
  if (entry.name === binaryName || entry.name === BINARY_NAME || isWindows && entry.name.endsWith(".exe")) {
2741
2783
  return fullPath;
2742
2784
  }
@@ -2746,7 +2788,7 @@ async function extractBinary(assetPath, outputDir) {
2746
2788
  };
2747
2789
  const binaryFilePath = await findBinary(extractDir);
2748
2790
  if (!binaryFilePath) {
2749
- const allFiles = await fs2.readdir(extractDir, { recursive: true });
2791
+ const allFiles = await fs3.readdir(extractDir, { recursive: true });
2750
2792
  console.error(`Binary not found in extracted files. Found: ${allFiles.join(", ")}`);
2751
2793
  throw new Error(`Binary not found in the archive.`);
2752
2794
  }
@@ -2754,11 +2796,11 @@ async function extractBinary(assetPath, outputDir) {
2754
2796
  console.log(`Found binary at ${binaryFilePath}`);
2755
2797
  console.log(`Copying binary to ${binaryPath}`);
2756
2798
  }
2757
- await fs2.copyFile(binaryFilePath, binaryPath);
2799
+ await fs3.copyFile(binaryFilePath, binaryPath);
2758
2800
  if (!isWindows) {
2759
- await fs2.chmod(binaryPath, 493);
2801
+ await fs3.chmod(binaryPath, 493);
2760
2802
  }
2761
- await fs2.remove(extractDir);
2803
+ await fs3.remove(extractDir);
2762
2804
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2763
2805
  console.log(`Binary successfully installed to ${binaryPath}`);
2764
2806
  }
@@ -2771,8 +2813,8 @@ async function extractBinary(assetPath, outputDir) {
2771
2813
  async function getVersionInfo(binDir) {
2772
2814
  try {
2773
2815
  const versionInfoPath = path2.join(binDir, "version-info.json");
2774
- if (await fs2.pathExists(versionInfoPath)) {
2775
- const content = await fs2.readFile(versionInfoPath, "utf-8");
2816
+ if (await fs3.pathExists(versionInfoPath)) {
2817
+ const content = await fs3.readFile(versionInfoPath, "utf-8");
2776
2818
  return JSON.parse(content);
2777
2819
  }
2778
2820
  return null;
@@ -2787,7 +2829,7 @@ async function saveVersionInfo(version, binDir) {
2787
2829
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2788
2830
  };
2789
2831
  const versionInfoPath = path2.join(binDir, "version-info.json");
2790
- await fs2.writeFile(versionInfoPath, JSON.stringify(versionInfo, null, 2));
2832
+ await fs3.writeFile(versionInfoPath, JSON.stringify(versionInfo, null, 2));
2791
2833
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2792
2834
  console.log(`Version info saved: ${version} at ${versionInfoPath}`);
2793
2835
  }
@@ -2802,11 +2844,11 @@ async function getPackageVersion() {
2802
2844
  ];
2803
2845
  for (const packageJsonPath of possiblePaths) {
2804
2846
  try {
2805
- if (fs2.existsSync(packageJsonPath)) {
2847
+ if (fs3.existsSync(packageJsonPath)) {
2806
2848
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2807
2849
  console.log(`Found package.json at: ${packageJsonPath}`);
2808
2850
  }
2809
- const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
2851
+ const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
2810
2852
  if (packageJson.version) {
2811
2853
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
2812
2854
  console.log(`Using version from package.json: ${packageJson.version}`);
@@ -2862,9 +2904,9 @@ async function doDownload(version) {
2862
2904
  const extractedBinaryPath = await extractBinary(assetPath, localDir);
2863
2905
  await saveVersionInfo(tagVersion, localDir);
2864
2906
  try {
2865
- await fs2.remove(assetPath);
2907
+ await fs3.remove(assetPath);
2866
2908
  if (checksumPath) {
2867
- await fs2.remove(checksumPath);
2909
+ await fs3.remove(checksumPath);
2868
2910
  }
2869
2911
  } catch (err) {
2870
2912
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
@@ -2885,7 +2927,7 @@ async function downloadProbeBinary(version) {
2885
2927
  const isWindows = os2.platform() === "win32";
2886
2928
  const binaryName = isWindows ? `${BINARY_NAME}.exe` : `${BINARY_NAME}-binary`;
2887
2929
  const binaryPath = path2.join(localDir, binaryName);
2888
- if (await fs2.pathExists(binaryPath)) {
2930
+ if (await fs3.pathExists(binaryPath)) {
2889
2931
  const versionInfo = await getVersionInfo(localDir);
2890
2932
  if (versionInfo && versionInfo.version === version) {
2891
2933
  if (process.env.DEBUG === "1" || process.env.VERBOSE === "1") {
@@ -2940,6 +2982,7 @@ var init_downloader = __esm({
2940
2982
  "use strict";
2941
2983
  init_utils();
2942
2984
  init_directory_resolver();
2985
+ init_symlink_utils();
2943
2986
  exec = promisify(execCallback);
2944
2987
  REPO_OWNER = "probelabs";
2945
2988
  REPO_NAME = "probe";
@@ -2955,11 +2998,11 @@ var init_downloader = __esm({
2955
2998
 
2956
2999
  // src/utils.js
2957
3000
  import path3 from "path";
2958
- import fs3 from "fs-extra";
3001
+ import fs4 from "fs-extra";
2959
3002
  import { fileURLToPath as fileURLToPath3 } from "url";
2960
3003
  async function getBinaryPath(options = {}) {
2961
3004
  const { forceDownload = false, version } = options;
2962
- if (process.env.PROBE_PATH && fs3.existsSync(process.env.PROBE_PATH) && !forceDownload) {
3005
+ if (process.env.PROBE_PATH && fs4.existsSync(process.env.PROBE_PATH) && !forceDownload) {
2963
3006
  probeBinaryPath = process.env.PROBE_PATH;
2964
3007
  return probeBinaryPath;
2965
3008
  }
@@ -2972,13 +3015,13 @@ async function getBinaryPath(options = {}) {
2972
3015
  const binaryName = isWindows ? "probe.exe" : "probe-binary";
2973
3016
  const localPackageBin = path3.resolve(__dirname3, "..", "bin");
2974
3017
  const localBinaryPath = path3.join(localPackageBin, binaryName);
2975
- if (fs3.existsSync(localBinaryPath) && !forceDownload) {
3018
+ if (fs4.existsSync(localBinaryPath) && !forceDownload) {
2976
3019
  probeBinaryPath = localBinaryPath;
2977
3020
  return probeBinaryPath;
2978
3021
  }
2979
3022
  const binDir = await getPackageBinDir();
2980
3023
  const binaryPath = path3.join(binDir, binaryName);
2981
- if (fs3.existsSync(binaryPath) && !forceDownload) {
3024
+ if (fs4.existsSync(binaryPath) && !forceDownload) {
2982
3025
  probeBinaryPath = binaryPath;
2983
3026
  return probeBinaryPath;
2984
3027
  }
@@ -9674,7 +9717,7 @@ Command failed with exit code ${result.exitCode}`;
9674
9717
 
9675
9718
  // src/tools/edit.js
9676
9719
  import { tool as tool3 } from "ai";
9677
- import { promises as fs4 } from "fs";
9720
+ import { promises as fs5 } from "fs";
9678
9721
  import { dirname, resolve as resolve3, isAbsolute, sep } from "path";
9679
9722
  import { existsSync as existsSync2 } from "fs";
9680
9723
  function isPathAllowed(filePath, allowedFolders) {
@@ -9762,7 +9805,7 @@ Important:
9762
9805
  if (!existsSync2(resolvedPath)) {
9763
9806
  return `Error editing file: File not found - ${file_path}`;
9764
9807
  }
9765
- const content = await fs4.readFile(resolvedPath, "utf-8");
9808
+ const content = await fs5.readFile(resolvedPath, "utf-8");
9766
9809
  if (!content.includes(old_string)) {
9767
9810
  return `Error editing file: String not found - the specified old_string was not found in ${file_path}`;
9768
9811
  }
@@ -9779,7 +9822,7 @@ Important:
9779
9822
  if (newContent === content) {
9780
9823
  return `Error editing file: No changes made - old_string and new_string might be the same`;
9781
9824
  }
9782
- await fs4.writeFile(resolvedPath, newContent, "utf-8");
9825
+ await fs5.writeFile(resolvedPath, newContent, "utf-8");
9783
9826
  const replacedCount = replace_all ? occurrences : 1;
9784
9827
  if (debug) {
9785
9828
  console.error(`[Edit] Successfully edited ${resolvedPath}, replaced ${replacedCount} occurrence(s)`);
@@ -9847,8 +9890,8 @@ Important:
9847
9890
  return `Error creating file: File already exists - ${file_path}. Use overwrite: true to replace it.`;
9848
9891
  }
9849
9892
  const dir = dirname(resolvedPath);
9850
- await fs4.mkdir(dir, { recursive: true });
9851
- await fs4.writeFile(resolvedPath, content, "utf-8");
9893
+ await fs5.mkdir(dir, { recursive: true });
9894
+ await fs5.writeFile(resolvedPath, content, "utf-8");
9852
9895
  const action = existsSync2(resolvedPath) && overwrite ? "overwrote" : "created";
9853
9896
  const bytes = Buffer.byteLength(content, "utf-8");
9854
9897
  if (debug) {
@@ -10118,7 +10161,7 @@ var init_tools = __esm({
10118
10161
  });
10119
10162
 
10120
10163
  // src/utils/file-lister.js
10121
- import fs5 from "fs";
10164
+ import fs6 from "fs";
10122
10165
  import path4 from "path";
10123
10166
  import { promisify as promisify6 } from "util";
10124
10167
  import { exec as exec4 } from "child_process";
@@ -10128,10 +10171,10 @@ async function listFilesByLevel(options) {
10128
10171
  maxFiles = 100,
10129
10172
  respectGitignore = true
10130
10173
  } = options;
10131
- if (!fs5.existsSync(directory)) {
10174
+ if (!fs6.existsSync(directory)) {
10132
10175
  throw new Error(`Directory does not exist: ${directory}`);
10133
10176
  }
10134
- const gitDirExists = fs5.existsSync(path4.join(directory, ".git"));
10177
+ const gitDirExists = fs6.existsSync(path4.join(directory, ".git"));
10135
10178
  if (gitDirExists && respectGitignore) {
10136
10179
  try {
10137
10180
  return await listFilesUsingGit(directory, maxFiles);
@@ -10162,8 +10205,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
10162
10205
  while (queue.length > 0 && result.length < maxFiles) {
10163
10206
  const { dir, level } = queue.shift();
10164
10207
  try {
10165
- const entries = fs5.readdirSync(dir, { withFileTypes: true });
10166
- const files = entries.filter((entry) => entry.isFile());
10208
+ const entries = fs6.readdirSync(dir, { withFileTypes: true });
10209
+ const files = entries.filter((entry) => {
10210
+ const fullPath = path4.join(dir, entry.name);
10211
+ return getEntryTypeSync(entry, fullPath).isFile;
10212
+ });
10167
10213
  for (const file of files) {
10168
10214
  if (result.length >= maxFiles) break;
10169
10215
  const filePath = path4.join(dir, file.name);
@@ -10171,7 +10217,10 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
10171
10217
  if (shouldIgnore(relativePath, ignorePatterns)) continue;
10172
10218
  result.push(relativePath);
10173
10219
  }
10174
- const dirs = entries.filter((entry) => entry.isDirectory());
10220
+ const dirs = entries.filter((entry) => {
10221
+ const fullPath = path4.join(dir, entry.name);
10222
+ return getEntryTypeSync(entry, fullPath).isDirectory;
10223
+ });
10175
10224
  for (const subdir of dirs) {
10176
10225
  const subdirPath = path4.join(dir, subdir.name);
10177
10226
  const relativeSubdirPath = path4.relative(directory, subdirPath);
@@ -10187,11 +10236,11 @@ async function listFilesByLevelManually(directory, maxFiles, respectGitignore) {
10187
10236
  }
10188
10237
  function loadGitignorePatterns(directory) {
10189
10238
  const gitignorePath = path4.join(directory, ".gitignore");
10190
- if (!fs5.existsSync(gitignorePath)) {
10239
+ if (!fs6.existsSync(gitignorePath)) {
10191
10240
  return [];
10192
10241
  }
10193
10242
  try {
10194
- const content = fs5.readFileSync(gitignorePath, "utf8");
10243
+ const content = fs6.readFileSync(gitignorePath, "utf8");
10195
10244
  return content.split("\n").map((line) => line.trim()).filter((line) => line && !line.startsWith("#"));
10196
10245
  } catch (error) {
10197
10246
  console.error(`Warning: Could not read .gitignore: ${error.message}`);
@@ -10213,6 +10262,7 @@ var execAsync3;
10213
10262
  var init_file_lister = __esm({
10214
10263
  "src/utils/file-lister.js"() {
10215
10264
  "use strict";
10265
+ init_symlink_utils();
10216
10266
  execAsync3 = promisify6(exec4);
10217
10267
  }
10218
10268
  });
@@ -15401,8 +15451,8 @@ var init_esm4 = __esm({
15401
15451
  *
15402
15452
  * @internal
15403
15453
  */
15404
- constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs8 = defaultFS } = {}) {
15405
- this.#fs = fsFromOption(fs8);
15454
+ constructor(cwd = process.cwd(), pathImpl, sep4, { nocase, childrenCacheSize = 16 * 1024, fs: fs9 = defaultFS } = {}) {
15455
+ this.#fs = fsFromOption(fs9);
15406
15456
  if (cwd instanceof URL || cwd.startsWith("file://")) {
15407
15457
  cwd = fileURLToPath4(cwd);
15408
15458
  }
@@ -15960,8 +16010,8 @@ var init_esm4 = __esm({
15960
16010
  /**
15961
16011
  * @internal
15962
16012
  */
15963
- newRoot(fs8) {
15964
- return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs8 });
16013
+ newRoot(fs9) {
16014
+ return new PathWin32(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs9 });
15965
16015
  }
15966
16016
  /**
15967
16017
  * Return true if the provided path string is an absolute path
@@ -15989,8 +16039,8 @@ var init_esm4 = __esm({
15989
16039
  /**
15990
16040
  * @internal
15991
16041
  */
15992
- newRoot(fs8) {
15993
- return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs8 });
16042
+ newRoot(fs9) {
16043
+ return new PathPosix(this.rootPath, IFDIR, void 0, this.roots, this.nocase, this.childrenCache(), { fs: fs9 });
15994
16044
  }
15995
16045
  /**
15996
16046
  * Return true if the provided path string is an absolute path
@@ -17130,8 +17180,8 @@ import { exec as exec5 } from "child_process";
17130
17180
  import { promisify as promisify7 } from "util";
17131
17181
  import { randomUUID as randomUUID2 } from "crypto";
17132
17182
  import { EventEmitter as EventEmitter2 } from "events";
17133
- import fs6 from "fs";
17134
- import { promises as fsPromises } from "fs";
17183
+ import fs7 from "fs";
17184
+ import { promises as fsPromises2 } from "fs";
17135
17185
  import path6 from "path";
17136
17186
  function isSessionCancelled(sessionId) {
17137
17187
  return activeToolExecutions.get(sessionId)?.cancelled || false;
@@ -17212,6 +17262,7 @@ var init_probeTool = __esm({
17212
17262
  "use strict";
17213
17263
  init_index();
17214
17264
  init_esm5();
17265
+ init_symlink_utils();
17215
17266
  toolCallEmitter = new EventEmitter2();
17216
17267
  activeToolExecutions = /* @__PURE__ */ new Map();
17217
17268
  wrapToolWithEmitter = (tool4, toolName, baseExecute) => {
@@ -17317,7 +17368,7 @@ var init_probeTool = __esm({
17317
17368
  console.log(`[DEBUG] Listing files in directory: ${targetDir}`);
17318
17369
  }
17319
17370
  try {
17320
- const files = await fsPromises.readdir(targetDir, { withFileTypes: true });
17371
+ const files = await fsPromises2.readdir(targetDir, { withFileTypes: true });
17321
17372
  const formatSize = (size) => {
17322
17373
  if (size < 1024) return `${size}B`;
17323
17374
  if (size < 1024 * 1024) return `${(size / 1024).toFixed(1)}K`;
@@ -17325,21 +17376,12 @@ var init_probeTool = __esm({
17325
17376
  return `${(size / (1024 * 1024 * 1024)).toFixed(1)}G`;
17326
17377
  };
17327
17378
  const entries = await Promise.all(files.map(async (file) => {
17328
- const isDirectory = file.isDirectory();
17329
17379
  const fullPath = path6.join(targetDir, file.name);
17330
- let size = 0;
17331
- try {
17332
- const stats = await fsPromises.stat(fullPath);
17333
- size = stats.size;
17334
- } catch (statError) {
17335
- if (debug) {
17336
- console.log(`[DEBUG] Could not stat file ${file.name}:`, statError.message);
17337
- }
17338
- }
17380
+ const entryType = await getEntryType(file, fullPath);
17339
17381
  return {
17340
17382
  name: file.name,
17341
- isDirectory,
17342
- size
17383
+ isDirectory: entryType.isDirectory,
17384
+ size: entryType.size
17343
17385
  };
17344
17386
  }));
17345
17387
  entries.sort((a, b) => {
@@ -31858,7 +31900,7 @@ function validateFlowchart(text, options = {}) {
31858
31900
  const byLine = /* @__PURE__ */ new Map();
31859
31901
  const collect = (arr) => {
31860
31902
  for (const e of arr || []) {
31861
- if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED")) {
31903
+ if (e && (e.code === "FL-LABEL-PARENS-UNQUOTED" || e.code === "FL-LABEL-AT-IN-UNQUOTED" || e.code === "FL-LABEL-QUOTE-IN-UNQUOTED")) {
31862
31904
  const ln = e.line ?? 0;
31863
31905
  const col = e.column ?? 1;
31864
31906
  const list = byLine.get(ln) || [];
@@ -31939,6 +31981,8 @@ function validateFlowchart(text, options = {}) {
31939
31981
  const covered = existing.some((r) => !(endCol < r.start || startCol > r.end));
31940
31982
  const hasParens = seg.includes("(") || seg.includes(")");
31941
31983
  const hasAt = seg.includes("@");
31984
+ const hasQuote = seg.includes('"');
31985
+ const isSingleQuoted = /^'[^]*'$/.test(trimmed);
31942
31986
  if (!covered && !isQuoted && !isParenWrapped && hasParens) {
31943
31987
  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;.' });
31944
31988
  existing.push({ start: startCol, end: endCol });
@@ -31949,6 +31993,11 @@ function validateFlowchart(text, options = {}) {
31949
31993
  existing.push({ start: startCol, end: endCol });
31950
31994
  byLine.set(ln, existing);
31951
31995
  }
31996
+ if (!covered && !isQuoted && !isSlashPair && hasQuote && !isSingleQuoted) {
31997
+ 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;"]' });
31998
+ existing.push({ start: startCol, end: endCol });
31999
+ byLine.set(ln, existing);
32000
+ }
31952
32001
  i = j;
31953
32002
  continue;
31954
32003
  } else {
@@ -58258,7 +58307,7 @@ __export(enhanced_claude_code_exports, {
58258
58307
  });
58259
58308
  import { spawn as spawn3 } from "child_process";
58260
58309
  import { randomBytes } from "crypto";
58261
- import fs7 from "fs/promises";
58310
+ import fs8 from "fs/promises";
58262
58311
  import path7 from "path";
58263
58312
  import os3 from "os";
58264
58313
  import { EventEmitter as EventEmitter4 } from "events";
@@ -58295,7 +58344,7 @@ async function createEnhancedClaudeCLIEngine(options = {}) {
58295
58344
  }
58296
58345
  }
58297
58346
  };
58298
- await fs7.writeFile(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
58347
+ await fs8.writeFile(mcpConfigPath, JSON.stringify(mcpConfig, null, 2));
58299
58348
  }
58300
58349
  if (debug) {
58301
58350
  console.log("[DEBUG] Enhanced Claude Code Engine");
@@ -58489,7 +58538,7 @@ ${opts.schema}`;
58489
58538
  }
58490
58539
  }
58491
58540
  if (mcpConfigPath) {
58492
- await fs7.unlink(mcpConfigPath).catch(() => {
58541
+ await fs8.unlink(mcpConfigPath).catch(() => {
58493
58542
  });
58494
58543
  if (debug) {
58495
58544
  console.log("[DEBUG] MCP config file removed");
@@ -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
 
@@ -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 ||
@@ -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);