@lzehrung/codegraph 2.2.1 → 2.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +1 -1
  2. package/codegraph-skill/codegraph/SKILL.md +2 -2
  3. package/dist/bin/cli.js +1376 -960
  4. package/dist/bin/queryIndexWorker.js +4 -0
  5. package/dist/cli/compileCache.d.ts +2 -2
  6. package/dist/cli/compileCache.js +2 -2
  7. package/dist/cli/doctor.d.ts.map +1 -1
  8. package/dist/cli/doctor.js +6 -3
  9. package/dist/cli/doctor.js.map +1 -1
  10. package/dist/cli/help.d.ts +1 -1
  11. package/dist/cli/help.d.ts.map +1 -1
  12. package/dist/cli/help.js +3 -3
  13. package/dist/cli/lifecycle.js +1 -1
  14. package/dist/cli/lifecycle.js.map +1 -1
  15. package/dist/indexer/build-cache/location.d.ts +4 -2
  16. package/dist/indexer/build-cache/location.d.ts.map +1 -1
  17. package/dist/indexer/build-cache/location.js +63 -15
  18. package/dist/indexer/build-cache/location.js.map +1 -1
  19. package/dist/indexer/build-cache/options.d.ts +15 -13
  20. package/dist/indexer/build-cache/options.d.ts.map +1 -1
  21. package/dist/indexer/build-cache/options.js +30 -38
  22. package/dist/indexer/build-cache/options.js.map +1 -1
  23. package/dist/indexer/build-cache/project-snapshot.d.ts.map +1 -1
  24. package/dist/indexer/build-cache/project-snapshot.js +103 -60
  25. package/dist/indexer/build-cache/project-snapshot.js.map +1 -1
  26. package/dist/indexer/build-index.d.ts.map +1 -1
  27. package/dist/indexer/build-index.js +43 -20
  28. package/dist/indexer/build-index.js.map +1 -1
  29. package/dist/indexer/imports.d.ts +4 -0
  30. package/dist/indexer/imports.d.ts.map +1 -1
  31. package/dist/indexer/imports.js +6 -5
  32. package/dist/indexer/imports.js.map +1 -1
  33. package/dist/indexer/locals-and-exports.d.ts.map +1 -1
  34. package/dist/indexer/locals-and-exports.js +10 -5
  35. package/dist/indexer/locals-and-exports.js.map +1 -1
  36. package/dist/indexer/navigation-references.d.ts +14 -1
  37. package/dist/indexer/navigation-references.d.ts.map +1 -1
  38. package/dist/indexer/navigation-references.js +29 -7
  39. package/dist/indexer/navigation-references.js.map +1 -1
  40. package/dist/indexer/navigation.d.ts.map +1 -1
  41. package/dist/indexer/navigation.js +72 -10
  42. package/dist/indexer/navigation.js.map +1 -1
  43. package/dist/indexer/types.d.ts +1 -0
  44. package/dist/indexer/types.d.ts.map +1 -1
  45. package/dist/languages/definitions/javascript.d.ts.map +1 -1
  46. package/dist/languages/definitions/javascript.js +1 -0
  47. package/dist/languages/definitions/javascript.js.map +1 -1
  48. package/dist/languages/definitions/typescript.d.ts.map +1 -1
  49. package/dist/languages/definitions/typescript.js +2 -0
  50. package/dist/languages/definitions/typescript.js.map +1 -1
  51. package/dist/languages/types.d.ts +4 -0
  52. package/dist/languages/types.d.ts.map +1 -1
  53. package/dist/languages.d.ts +1 -0
  54. package/dist/languages.d.ts.map +1 -1
  55. package/dist/languages.js +1 -0
  56. package/dist/languages.js.map +1 -1
  57. package/dist/lifecycle/gitignore.d.ts +1 -1
  58. package/dist/lifecycle/gitignore.d.ts.map +1 -1
  59. package/dist/lifecycle/gitignore.js +7 -12
  60. package/dist/lifecycle/gitignore.js.map +1 -1
  61. package/dist/lifecycle/manifest.d.ts.map +1 -1
  62. package/dist/lifecycle/manifest.js +10 -4
  63. package/dist/lifecycle/manifest.js.map +1 -1
  64. package/dist/util/git.d.ts +66 -1
  65. package/dist/util/git.d.ts.map +1 -1
  66. package/dist/util/git.js +173 -1
  67. package/dist/util/git.js.map +1 -1
  68. package/dist/util/projectFiles.d.ts +7 -6
  69. package/dist/util/projectFiles.d.ts.map +1 -1
  70. package/dist/util/projectFiles.js +268 -59
  71. package/dist/util/projectFiles.js.map +1 -1
  72. package/dist/util/resolution.d.ts.map +1 -1
  73. package/dist/util/resolution.js +2 -3
  74. package/dist/util/resolution.js.map +1 -1
  75. package/dist/worker/nativeWorkerPool.js +1 -1
  76. package/dist/worker/nativeWorkerPool.js.map +1 -1
  77. package/docs/mcp.md +1 -1
  78. package/package.json +3 -3
package/dist/bin/cli.js CHANGED
@@ -2091,6 +2091,7 @@ var init_typescript = __esm({
2091
2091
  extensions: [".ts", ".mts", ".cts"],
2092
2092
  structure: BASE_STRUCTURE,
2093
2093
  graph: BASE_GRAPH,
2094
+ supportsExportFromReferences: true,
2094
2095
  ...BASE_HELPERS,
2095
2096
  isTypeOnly: (stmtText) => /\b(import|export)\s+type\b/.test(stmtText),
2096
2097
  native: {
@@ -2112,6 +2113,7 @@ var init_typescript = __esm({
2112
2113
  ]
2113
2114
  },
2114
2115
  graph: BASE_GRAPH,
2116
+ supportsExportFromReferences: true,
2115
2117
  ...BASE_HELPERS,
2116
2118
  isTypeOnly: (stmtText) => /\b(import|export)\s+type\b/.test(stmtText),
2117
2119
  native: {
@@ -2148,6 +2150,7 @@ var init_javascript = __esm({
2148
2150
  JAVASCRIPT_DEF = {
2149
2151
  id: "js",
2150
2152
  extensions: [".js", ".jsx", ".mjs", ".cjs"],
2153
+ supportsExportFromReferences: true,
2151
2154
  structure: {
2152
2155
  blocks: [...ECMASCRIPT_CORE_FUNCTION_BLOCKS, ...ECMASCRIPT_MODULE_VAR_BLOCKS],
2153
2156
  splitPoints: [...ECMASCRIPT_CONTROL_SPLIT_POINTS],
@@ -4489,6 +4492,7 @@ function adaptDefinition(def) {
4489
4492
  membersAreImplicitlyInScope: def.membersAreImplicitlyInScope ?? true,
4490
4493
  supportsCrossModuleSymbols: def.supportsCrossModuleSymbols || false,
4491
4494
  isTypeOnly: def.isTypeOnly || (() => false),
4495
+ supportsExportFromReferences: def.supportsExportFromReferences ?? false,
4492
4496
  usesQueryDrivenLocals: def.usesQueryDrivenLocals || false,
4493
4497
  normalizeIdentifier: def.normalizeIdentifier || ((name) => name),
4494
4498
  ...def.native ? { native: def.native } : {}
@@ -4667,9 +4671,9 @@ var require_path = __commonJS({
4667
4671
  "use strict";
4668
4672
  Object.defineProperty(exports, "__esModule", { value: true });
4669
4673
  exports.convertPosixPathToPattern = exports.convertWindowsPathToPattern = exports.convertPathToPattern = exports.escapePosixPath = exports.escapeWindowsPath = exports.escape = exports.removeLeadingDotSegment = exports.makeAbsolute = exports.unixify = void 0;
4670
- var os11 = __require("os");
4674
+ var os12 = __require("os");
4671
4675
  var path99 = __require("path");
4672
- var IS_WINDOWS_PLATFORM = os11.platform() === "win32";
4676
+ var IS_WINDOWS_PLATFORM = os12.platform() === "win32";
4673
4677
  var LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2;
4674
4678
  var POSIX_UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\()|\\(?![!()*+?@[\]{|}]))/g;
4675
4679
  var WINDOWS_UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()[\]{}]|^!|[!+@](?=\())/g;
@@ -10187,8 +10191,8 @@ var require_settings4 = __commonJS({
10187
10191
  Object.defineProperty(exports, "__esModule", { value: true });
10188
10192
  exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0;
10189
10193
  var fs49 = __require("fs");
10190
- var os11 = __require("os");
10191
- var CPU_COUNT = Math.max(os11.cpus().length, 1);
10194
+ var os12 = __require("os");
10195
+ var CPU_COUNT = Math.max(os12.cpus().length, 1);
10192
10196
  exports.DEFAULT_FILE_SYSTEM_ADAPTER = {
10193
10197
  lstat: fs49.lstat,
10194
10198
  lstatSync: fs49.lstatSync,
@@ -13399,554 +13403,12 @@ var init_concurrency = __esm({
13399
13403
  }
13400
13404
  });
13401
13405
 
13402
- // dist/util/projectFiles.js
13403
- var projectFiles_exports = {};
13404
- __export(projectFiles_exports, {
13405
- DEFAULT_PROJECT_FILE_IGNORES: () => DEFAULT_PROJECT_FILE_IGNORES,
13406
- DEFAULT_PROJECT_MANIFESTS: () => DEFAULT_PROJECT_MANIFESTS,
13407
- DEFAULT_PROJECT_PATTERNS: () => DEFAULT_PROJECT_PATTERNS,
13408
- createDiscoveredFileMatcher: () => createDiscoveredFileMatcher,
13409
- discoverProjectFiles: () => discoverProjectFiles,
13410
- filterRealPathsWithinRoot: () => filterRealPathsWithinRoot,
13411
- isRelativePathInside: () => isRelativePathInside,
13412
- listProjectFiles: () => listProjectFiles,
13413
- matchesDiscoveryGlob: () => matchesDiscoveryGlob,
13414
- translateGlobRootIgnoreGlobsForScanRoot: () => translateGlobRootIgnoreGlobsForScanRoot
13415
- });
13416
- import fs6 from "node:fs";
13417
- import fsp4 from "node:fs/promises";
13418
- import path8 from "node:path";
13419
- function isPresent(value) {
13420
- return value !== void 0;
13421
- }
13422
- function normalizeGlobPattern(globPattern) {
13423
- return globPattern.trim().replace(/\\/g, "/");
13424
- }
13425
- function isIgnoreGlobReopenedByIncludes(ignoreGlob, includeGlobs) {
13426
- const literalSegments = ignoreGlob.split("/").filter((segment) => segment && !segment.includes("*"));
13427
- if (!literalSegments.length)
13428
- return false;
13429
- return includeGlobs.some((includeGlob) => {
13430
- const includeSegments = includeGlob.split("/");
13431
- return literalSegments.every((segment) => includeSegments.includes(segment));
13432
- });
13433
- }
13434
- function isLocationIndependentGlob(globPattern) {
13435
- return globPattern.startsWith("**/");
13436
- }
13437
- function translateGlobRootIgnoreGlobsForScanRoot(scanRoot, globRoot, ignoreGlobs) {
13438
- const relativeScanRoot = normalizePath(path8.relative(globRoot, scanRoot));
13439
- if (!relativeScanRoot || !isRelativePathInside(relativeScanRoot)) {
13440
- return ignoreGlobs.map(normalizeGlobPattern).filter(Boolean);
13441
- }
13442
- const rootPrefix = `${relativeScanRoot}/`;
13443
- return ignoreGlobs.map(normalizeGlobPattern).filter(Boolean).map((globPattern) => {
13444
- const rootRelativePattern = globPattern.startsWith("/") ? globPattern.slice(1) : globPattern;
13445
- if (isLocationIndependentGlob(rootRelativePattern))
13446
- return rootRelativePattern;
13447
- if (rootRelativePattern === relativeScanRoot || rootRelativePattern === `${relativeScanRoot}/**`)
13448
- return "**";
13449
- if (rootRelativePattern.startsWith(rootPrefix))
13450
- return rootRelativePattern.slice(rootPrefix.length) || "**";
13451
- return void 0;
13452
- }).filter(isPresent);
13453
- }
13454
- function stripGitignoreTrailingSpaces(line) {
13455
- let end = line.length;
13456
- while (end > 0 && line[end - 1] === " ") {
13457
- let slashCount = 0;
13458
- for (let i = end - 2; i >= 0 && line[i] === "\\"; i -= 1) {
13459
- slashCount += 1;
13460
- }
13461
- if (slashCount % 2 === 1) {
13462
- break;
13463
- }
13464
- end -= 1;
13465
- }
13466
- return line.slice(0, end);
13467
- }
13468
- function parseGitignoreRule(baseDir, rawLine) {
13469
- const trimmedLine = stripGitignoreTrailingSpaces(rawLine);
13470
- if (!trimmedLine)
13471
- return null;
13472
- if (trimmedLine.startsWith("#"))
13473
- return null;
13474
- const negated = trimmedLine.startsWith("!");
13475
- let pattern = negated ? trimmedLine.slice(1) : trimmedLine;
13476
- if (!pattern || pattern === "/")
13477
- return null;
13478
- const dirOnly = pattern.endsWith("/");
13479
- if (dirOnly) {
13480
- pattern = pattern.slice(0, -1);
13481
- }
13482
- if (pattern.startsWith("/")) {
13483
- pattern = pattern.slice(1);
13484
- }
13485
- pattern = pattern.replace(/^\\([#!])/, "$1").replace(/\\/g, "/");
13486
- if (!pattern)
13487
- return null;
13488
- const anchored = trimmedLine.startsWith("/") || negated && trimmedLine.startsWith("!/");
13489
- const baseMatcherPattern = anchored || pattern.includes("/") ? pattern : `**/${pattern}`;
13490
- const matcherPattern = dirOnly ? `${baseMatcherPattern}/**` : [baseMatcherPattern, `${baseMatcherPattern}/**`];
13491
- const matches = (0, import_picomatch.default)(matcherPattern, { dot: true });
13492
- return {
13493
- baseDir: normalizePath(baseDir),
13494
- negated,
13495
- dirOnly,
13496
- matches
13497
- };
13498
- }
13499
- async function loadGitignoreRules(projectRoot) {
13500
- const gitignoreFiles = await (0, import_fast_glob2.default)(["**/.gitignore"], {
13501
- cwd: projectRoot,
13502
- absolute: true,
13503
- dot: true,
13504
- followSymbolicLinks: false,
13505
- ignore: DEFAULT_PROJECT_FILE_IGNORES
13506
- });
13507
- gitignoreFiles.sort((left, right) => normalizePath(left).localeCompare(normalizePath(right)));
13508
- const rules = [];
13509
- for (const gitignoreFile of gitignoreFiles) {
13510
- if (isIgnoredByGitignore(gitignoreFile, rules)) {
13511
- continue;
13512
- }
13513
- try {
13514
- const baseDir = path8.dirname(gitignoreFile);
13515
- const raw = await fsp4.readFile(gitignoreFile, "utf8");
13516
- rules.push(...raw.split(/\r?\n/).map((line) => parseGitignoreRule(baseDir, line)).filter((rule) => !!rule));
13517
- } catch {
13518
- continue;
13519
- }
13520
- }
13521
- return rules;
13522
- }
13523
- async function loadGitignoreRulesForRootAliases(projectRoot) {
13524
- const roots = [projectRoot];
13525
- const realRoot = await fsp4.realpath(projectRoot);
13526
- if (normalizePath(realRoot) !== normalizePath(projectRoot)) {
13527
- roots.push(realRoot);
13528
- }
13529
- const rules = [];
13530
- for (const root of roots) {
13531
- rules.push(...await loadGitignoreRules(root));
13532
- }
13533
- return rules;
13534
- }
13535
- function isIgnoredByGitignore(absolutePath, rules) {
13536
- let ignored = false;
13537
- for (const rule of rules) {
13538
- const relativePath3 = path8.relative(rule.baseDir, absolutePath);
13539
- if (!isRelativePathInside(relativePath3)) {
13540
- continue;
13541
- }
13542
- const normalizedRelativePath2 = normalizePath(relativePath3);
13543
- if (rule.dirOnly && !normalizedRelativePath2.includes("/")) {
13544
- continue;
13545
- }
13546
- if (rule.matches(normalizedRelativePath2)) {
13547
- ignored = !rule.negated;
13548
- }
13549
- }
13550
- return ignored;
13551
- }
13552
- async function ensureDirectoryReadable(directoryPath, label) {
13553
- const resolvedPath = path8.resolve(directoryPath);
13554
- let stats;
13555
- try {
13556
- stats = await fsp4.stat(resolvedPath);
13557
- } catch (error51) {
13558
- throw new Error(`${label} does not exist or is not readable: ${resolvedPath} (${stringifyUnknown(error51)})`);
13559
- }
13560
- if (!stats.isDirectory()) {
13561
- throw new Error(`${label} is not a directory: ${resolvedPath}`);
13562
- }
13563
- return resolvedPath;
13564
- }
13565
- async function listProjectFiles(projectRoot, patterns = DEFAULT_PROJECT_PATTERNS, options) {
13566
- const root = await ensureDirectoryReadable(projectRoot, "Project root");
13567
- const globRoot = options?.globRoot ? await ensureDirectoryReadable(options.globRoot, "Glob root") : root;
13568
- const includeGlobs = (options?.includeGlobs ?? []).map(normalizeGlobPattern).filter(Boolean);
13569
- const includeMatchers = includeGlobs.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
13570
- const userIgnoreGlobs = (options?.ignoreGlobs ?? []).map(normalizeGlobPattern).filter(Boolean);
13571
- const userIgnoreMatchers = userIgnoreGlobs.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
13572
- const defaultIgnoreMatchers = DEFAULT_PROJECT_FILE_IGNORES.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
13573
- const patternMatchers = patterns.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
13574
- const translatedUserIgnoreGlobs = translateGlobRootIgnoreGlobsForScanRoot(root, globRoot, userIgnoreGlobs);
13575
- const fastGlobIgnoreGlobs = [...DEFAULT_PROJECT_FILE_IGNORES, ...translatedUserIgnoreGlobs];
13576
- const symlinkProbeIgnoreGlobs = includeGlobs.length ? [
13577
- ...translatedUserIgnoreGlobs,
13578
- ...DEFAULT_PROJECT_FILE_IGNORES.filter((ignoreGlob) => !isIgnoreGlobReopenedByIncludes(ignoreGlob, includeGlobs))
13579
- ] : fastGlobIgnoreGlobs;
13580
- try {
13581
- const useGitignore = options?.useGitignore ?? true;
13582
- const realRoot = await fsp4.realpath(root);
13583
- const gitignoreRules = !useGitignore ? [] : await loadGitignoreRulesForRootAliases(options?.gitignoreRoot ? await ensureDirectoryReadable(options.gitignoreRoot, "Gitignore root") : root);
13584
- const files = await (0, import_fast_glob2.default)(patterns, {
13585
- cwd: root,
13586
- absolute: true,
13587
- dot: true,
13588
- followSymbolicLinks: false,
13589
- ignore: fastGlobIgnoreGlobs
13590
- });
13591
- const includedOverrideFiles = includeGlobs.length ? await (0, import_fast_glob2.default)(translateGlobRootIgnoreGlobsForScanRoot(root, globRoot, includeGlobs), {
13592
- cwd: root,
13593
- absolute: true,
13594
- dot: true,
13595
- followSymbolicLinks: false,
13596
- ignore: translatedUserIgnoreGlobs
13597
- }) : [];
13598
- const symlinkOptions = {
13599
- globRoot,
13600
- ...options?.knownSymlinkDirectories !== void 0 ? { knownSymlinkDirectories: options.knownSymlinkDirectories } : {},
13601
- ...options?.onSymlinkDirectoriesDiscovered ? { onSymlinkDirectoriesDiscovered: options.onSymlinkDirectoriesDiscovered } : {}
13602
- };
13603
- const safeSymlinkDirectories = await resolveSafeSymlinkDirectories(root, realRoot, symlinkProbeIgnoreGlobs, symlinkOptions);
13604
- const linkedFiles = await listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, fastGlobIgnoreGlobs, {
13605
- ...symlinkOptions,
13606
- filterIgnoreGlobs: [...DEFAULT_PROJECT_FILE_IGNORES, ...userIgnoreGlobs],
13607
- resolvedSafeSymlinkDirectories: safeSymlinkDirectories
13608
- });
13609
- const linkedOverrideFiles = includeGlobs.length === 0 ? [] : await listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, translatedUserIgnoreGlobs, {
13610
- globRoot,
13611
- filterIgnoreGlobs: userIgnoreGlobs,
13612
- resolvedSafeSymlinkDirectories: safeSymlinkDirectories
13613
- });
13614
- const rootSafeFiles = await filterRealPathsWithinRootEntries([...files, ...includedOverrideFiles, ...linkedFiles, ...linkedOverrideFiles], realRoot);
13615
- const seen = /* @__PURE__ */ new Set();
13616
- return rootSafeFiles.map(({ path: filePath, realPath }) => ({ filePath: normalizePath(filePath), realPath })).filter(({ filePath, realPath }) => {
13617
- if (seen.has(filePath))
13618
- return false;
13619
- seen.add(filePath);
13620
- const rootRelative = normalizePath(path8.relative(root, filePath));
13621
- if (!isRelativePathInside(rootRelative))
13622
- return false;
13623
- if (!patternMatchers.some((matcher) => matcher(rootRelative)))
13624
- return false;
13625
- const matchesInclude = !includeMatchers.length || includeMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher));
13626
- if (!matchesInclude)
13627
- return false;
13628
- if (userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher))) {
13629
- return false;
13630
- }
13631
- const ignoredByDefault = defaultIgnoreMatchers.some((matcher) => matcher(rootRelative));
13632
- if (ignoredByDefault && !(includeMatchers.length > 0 && matchesInclude)) {
13633
- return false;
13634
- }
13635
- return !isIgnoredByGitignore(filePath, gitignoreRules) && !isIgnoredByGitignore(realPath, gitignoreRules);
13636
- }).map(({ filePath }) => filePath);
13637
- } catch (error51) {
13638
- logWithLevel(options?.logLevel, "debug", `listProjectFiles failed for ${root}: ${stringifyUnknown(error51)}`);
13639
- throw new Error(`Failed to list files in ${root}: ${stringifyUnknown(error51)}`);
13640
- }
13641
- }
13642
- function createDiscoveredFileMatcher(root, globRoot, patterns, options) {
13643
- const patternMatchers = patterns.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
13644
- const defaultIgnoreMatchers = DEFAULT_PROJECT_FILE_IGNORES.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
13645
- const includeMatchers = (options?.includeGlobs ?? []).map(normalizeGlobPattern).filter(Boolean).map((pattern) => (0, import_picomatch.default)(pattern, { dot: true }));
13646
- const userIgnoreMatchers = (options?.ignoreGlobs ?? []).map(normalizeGlobPattern).filter(Boolean).map((pattern) => (0, import_picomatch.default)(pattern, { dot: true }));
13647
- return (absolutePath) => {
13648
- const rootRelative = path8.relative(root, absolutePath);
13649
- if (!isRelativePathInside(rootRelative))
13650
- return false;
13651
- const normalizedRootRelative = normalizePath(rootRelative);
13652
- if (!patternMatchers.some((matcher) => matcher(normalizedRootRelative)))
13653
- return false;
13654
- const matchesInclude = !includeMatchers.length || includeMatchers.some((matcher) => matchesDiscoveryGlob(absolutePath, globRoot, matcher));
13655
- if (!matchesInclude)
13656
- return false;
13657
- if (defaultIgnoreMatchers.some((matcher) => matcher(normalizedRootRelative))) {
13658
- if (!(includeMatchers.length > 0 && matchesInclude))
13659
- return false;
13660
- }
13661
- return !userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(absolutePath, globRoot, matcher));
13662
- };
13663
- }
13664
- async function isSafeSymlinkDirectory(root, linkPath, realRoot) {
13665
- try {
13666
- if (!isRelativePathInside(path8.relative(root, linkPath)))
13667
- return false;
13668
- const [linkStats, realPath, targetStats] = await Promise.all([
13669
- fsp4.lstat(linkPath),
13670
- fsp4.realpath(linkPath),
13671
- fsp4.stat(linkPath)
13672
- ]);
13673
- if (!linkStats.isSymbolicLink())
13674
- return false;
13675
- if (!targetStats.isDirectory())
13676
- return false;
13677
- if (!isFilePathWithinRoot(realRoot, realPath))
13678
- return false;
13679
- return normalizePath(realPath) !== normalizePath(realRoot);
13680
- } catch {
13681
- return false;
13682
- }
13683
- }
13684
- async function resolveSafeSymlinkDirectories(root, realRoot, ignore, options) {
13685
- if (options.knownSymlinkDirectories !== void 0) {
13686
- const verified = await mapLimitSemaphore(Array.from(new Set(options.knownSymlinkDirectories)), REALPATH_FILTER_CONCURRENCY, async (linkPath) => await isSafeSymlinkDirectory(root, linkPath, realRoot) ? linkPath : null);
13687
- const resolved = verified.filter((entry) => entry !== null);
13688
- options.onSymlinkDirectoriesDiscovered?.(resolved);
13689
- return resolved;
13690
- }
13691
- const entries = await (0, import_fast_glob2.default)(["**/*"], {
13692
- cwd: root,
13693
- absolute: true,
13694
- dot: true,
13695
- onlyFiles: false,
13696
- followSymbolicLinks: false,
13697
- objectMode: true,
13698
- ignore
13699
- });
13700
- const candidates = await mapLimitSemaphore(entries.filter((entry) => entry.dirent.isSymbolicLink()).map((entry) => entry.path), REALPATH_FILTER_CONCURRENCY, async (linkPath) => await isSafeSymlinkDirectory(root, linkPath, realRoot) ? linkPath : null);
13701
- const discovered = candidates.filter((entry) => entry !== null);
13702
- options.onSymlinkDirectoriesDiscovered?.(discovered);
13703
- return discovered;
13704
- }
13705
- async function listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, ignore, options = {}) {
13706
- const globRoot = options.globRoot ?? root;
13707
- const filterIgnoreGlobs = options.filterIgnoreGlobs ?? ignore;
13708
- const rootRelativeIgnoreMatchers = filterIgnoreGlobs.map(normalizeGlobPattern).filter(Boolean).map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
13709
- const locationIndependentIgnores = ignore.map(normalizeGlobPattern).filter(isLocationIndependentGlob);
13710
- const safeSymlinkDirectories = options.resolvedSafeSymlinkDirectories ?? await resolveSafeSymlinkDirectories(root, realRoot, ignore, options);
13711
- if (!safeSymlinkDirectories.length)
13712
- return [];
13713
- const filesByPath = /* @__PURE__ */ new Map();
13714
- const filesByDirectory = await mapLimitSemaphore(Array.from(safeSymlinkDirectories), REALPATH_FILTER_CONCURRENCY, async (directory) => (await (0, import_fast_glob2.default)(patterns, {
13715
- cwd: directory,
13716
- absolute: true,
13717
- dot: true,
13718
- followSymbolicLinks: false,
13719
- ignore: locationIndependentIgnores,
13720
- ...options.onlyFiles !== void 0 ? { onlyFiles: options.onlyFiles } : {},
13721
- ...options.markDirectories !== void 0 ? { markDirectories: options.markDirectories } : {}
13722
- })).filter((filePath) => {
13723
- const cleanPath = filePath.endsWith("/") ? filePath.slice(0, -1) : filePath;
13724
- return !rootRelativeIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(cleanPath, globRoot, matcher));
13725
- }));
13726
- for (const files of filesByDirectory) {
13727
- for (const file2 of files) {
13728
- filesByPath.set(normalizePath(file2), file2);
13729
- }
13730
- }
13731
- return [...filesByPath.values()];
13732
- }
13733
- async function filterRealPathsWithinRootEntries(paths, realRoot) {
13734
- const filtered = await mapLimitSemaphore(paths, REALPATH_FILTER_CONCURRENCY, async (filePath) => {
13735
- try {
13736
- const realPath = await fsp4.realpath(filePath);
13737
- return isFilePathWithinRoot(realRoot, realPath) ? { path: filePath, realPath } : null;
13738
- } catch {
13739
- return null;
13740
- }
13741
- });
13742
- return filtered.filter((entry) => entry !== null);
13743
- }
13744
- async function filterRealPathsWithinRoot(paths, realRoot) {
13745
- const entries = await filterRealPathsWithinRootEntries(paths, realRoot);
13746
- return entries.map((entry) => entry.path);
13747
- }
13748
- function toProjectGlob(pattern) {
13749
- return pattern.startsWith("**/") ? pattern : `**/${pattern}`;
13750
- }
13751
- async function buildProjectFileInfo(def, filePath) {
13752
- const normalizedPath = normalizePath(filePath);
13753
- const projectRoot = normalizePath(path8.dirname(filePath));
13754
- let name = null;
13755
- if (def.parseName && def.kind === "file") {
13756
- try {
13757
- const raw = await fsp4.readFile(filePath, "utf8");
13758
- name = def.parseName(raw, filePath);
13759
- } catch {
13760
- name = null;
13761
- }
13762
- }
13763
- if (!name && def.nameFromPath) {
13764
- if (def.nameFromPath === "file") {
13765
- name = trimToNull(path8.basename(filePath, path8.extname(filePath)));
13766
- } else {
13767
- name = trimToNull(path8.basename(projectRoot));
13768
- }
13769
- }
13770
- return {
13771
- path: normalizedPath,
13772
- kind: def.kind,
13773
- type: def.type,
13774
- role: def.role,
13775
- projectRoot,
13776
- ...name ? { name } : {}
13777
- };
13778
- }
13779
- async function discoverProjectFiles(projectRoot, options) {
13780
- const root = await ensureDirectoryReadable(projectRoot, "Project root");
13781
- try {
13782
- const realRoot = await fsp4.realpath(root);
13783
- const allPatterns = PROJECT_FILE_DEFINITIONS.flatMap((def) => def.patterns.map(toProjectGlob));
13784
- const matches = await (0, import_fast_glob2.default)(allPatterns, {
13785
- cwd: root,
13786
- absolute: true,
13787
- dot: true,
13788
- followSymbolicLinks: false,
13789
- ignore: DEFAULT_PROJECT_FILE_IGNORES,
13790
- markDirectories: true,
13791
- onlyFiles: false
13792
- });
13793
- const linkedMatches = await listEntriesFromSafeSymlinkDirectories(root, realRoot, allPatterns, DEFAULT_PROJECT_FILE_IGNORES, {
13794
- markDirectories: true,
13795
- onlyFiles: false,
13796
- ...options?.knownSymlinkDirectories !== void 0 ? { knownSymlinkDirectories: options.knownSymlinkDirectories } : {},
13797
- ...options?.onSymlinkDirectoriesDiscovered ? { onSymlinkDirectoriesDiscovered: options.onSymlinkDirectoriesDiscovered } : {}
13798
- });
13799
- const rootSafeMatches = await filterRealPathsWithinRoot([...matches, ...linkedMatches].map((match) => match.endsWith("/") ? match.slice(0, -1) : match), realRoot);
13800
- const projectFileDefinitionMatchers = PROJECT_FILE_DEFINITIONS.map((definition) => definition.patterns.map((pattern) => pattern.includes("*") || pattern.includes("?") ? new RegExp("^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$") : void 0));
13801
- const entries = [];
13802
- const matchTasks = rootSafeMatches.map(async (cleanMatch) => {
13803
- const stats = await fsp4.stat(cleanMatch);
13804
- const isDir = stats.isDirectory();
13805
- const fileName = path8.basename(cleanMatch);
13806
- for (let definitionIndex = 0; definitionIndex < PROJECT_FILE_DEFINITIONS.length; definitionIndex++) {
13807
- const def = PROJECT_FILE_DEFINITIONS[definitionIndex];
13808
- if (isDir && def.kind !== "dir")
13809
- continue;
13810
- if (!isDir && def.kind !== "file")
13811
- continue;
13812
- const matchesPattern = def.patterns.some((pattern, patternIndex) => {
13813
- const matcher = projectFileDefinitionMatchers[definitionIndex][patternIndex];
13814
- return matcher ? matcher.test(fileName) : pattern === fileName;
13815
- });
13816
- if (matchesPattern) {
13817
- entries.push(await buildProjectFileInfo(def, cleanMatch));
13818
- }
13819
- }
13820
- });
13821
- await Promise.all(matchTasks);
13822
- const byKey2 = /* @__PURE__ */ new Map();
13823
- for (const entry of entries) {
13824
- const key = `${entry.path}::${entry.type}::${entry.role}`;
13825
- const existing = byKey2.get(key);
13826
- if (!existing || !existing.name && entry.name) {
13827
- byKey2.set(key, entry);
13828
- }
13829
- }
13830
- return Array.from(byKey2.values()).sort((a, b) => {
13831
- if (a.path === b.path)
13832
- return a.type.localeCompare(b.type);
13833
- return a.path.localeCompare(b.path);
13834
- });
13835
- } catch (error51) {
13836
- logWithLevel(options?.logLevel, "debug", `discoverProjectFiles failed for ${root}: ${stringifyUnknown(error51)}`);
13837
- throw new Error(`Failed to discover project files in ${root}: ${stringifyUnknown(error51)}`);
13838
- }
13839
- }
13840
- var import_fast_glob2, import_picomatch, DEFAULT_PROJECT_FILE_IGNORES, DEFAULT_PROJECT_MANIFESTS, DEFAULT_PROJECT_PATTERNS, REALPATH_FILTER_CONCURRENCY;
13841
- var init_projectFiles = __esm({
13842
- "dist/util/projectFiles.js"() {
13843
- "use strict";
13844
- import_fast_glob2 = __toESM(require_out4(), 1);
13845
- import_picomatch = __toESM(require_picomatch4(), 1);
13846
- init_logging();
13847
- init_ast();
13848
- init_paths();
13849
- init_discoveryPath();
13850
- init_discoveryPath();
13851
- init_definitions();
13852
- init_parsers();
13853
- init_concurrency();
13854
- DEFAULT_PROJECT_FILE_IGNORES = [
13855
- "**/node_modules/**",
13856
- "**/.git/**",
13857
- // Never treat Codegraph's own on-disk state as project input. `.codegraph/` is the
13858
- // lifecycle manifest root; `.codegraph-cache/` is the indexer disk cache. Either can
13859
- // contain JSON/markdown/source-looking files that would otherwise match discovery
13860
- // patterns and create perpetual "new untracked file" / reindex churn.
13861
- "**/.codegraph/**",
13862
- "**/.codegraph-cache/**",
13863
- "**/dist/**",
13864
- "**/build/**",
13865
- "**/target/**",
13866
- "**/.venv/**",
13867
- "**/venv/**",
13868
- "**/site-packages/**",
13869
- "**/__pycache__/**",
13870
- "**/vendor/bundle/**",
13871
- "**/.build/**",
13872
- "**/Pods/**"
13873
- ];
13874
- DEFAULT_PROJECT_MANIFESTS = [
13875
- "package.json",
13876
- "package-lock.json",
13877
- "pnpm-lock.yaml",
13878
- "yarn.lock",
13879
- "bun.lockb",
13880
- "pnpm-workspace.yaml",
13881
- "lerna.json",
13882
- "nx.json",
13883
- "turbo.json",
13884
- "tsconfig.json",
13885
- "jsconfig.json",
13886
- "requirements.txt",
13887
- "requirements.in",
13888
- "pyproject.toml",
13889
- "Pipfile",
13890
- "Pipfile.lock",
13891
- "poetry.lock",
13892
- "setup.py",
13893
- "setup.cfg",
13894
- "Cargo.toml",
13895
- "Cargo.lock",
13896
- "rust-toolchain",
13897
- "rust-toolchain.toml",
13898
- "go.mod",
13899
- "go.sum",
13900
- "go.work",
13901
- "Gemfile",
13902
- "Gemfile.lock",
13903
- "*.gemspec",
13904
- "pom.xml",
13905
- "mvnw",
13906
- "build.gradle",
13907
- "build.gradle.kts",
13908
- "settings.gradle",
13909
- "settings.gradle.kts",
13910
- "gradle.properties",
13911
- "gradlew",
13912
- "*.csproj",
13913
- "*.fsproj",
13914
- "*.vbproj",
13915
- "*.sln",
13916
- "Directory.Build.props",
13917
- "Directory.Build.targets",
13918
- "global.json",
13919
- "composer.json",
13920
- "composer.lock",
13921
- "Package.swift",
13922
- "Package.resolved",
13923
- "CMakeLists.txt",
13924
- "CMakePresets.json",
13925
- "CMakeUserPresets.json",
13926
- "Makefile",
13927
- "makefile",
13928
- "GNUmakefile",
13929
- "configure.ac",
13930
- "configure.in",
13931
- "meson.build",
13932
- "meson_options.txt",
13933
- "conanfile.txt",
13934
- "conanfile.py",
13935
- "vcpkg.json"
13936
- ];
13937
- DEFAULT_PROJECT_PATTERNS = [
13938
- "**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,py,pyi,php,vue,svelte,astro,hbs,handlebars,md,mdx,rst,adoc,asciidoc,go,java,cs,rb,rs,html,htm,css,scss,less,kt,kts,swift,zig,c,h,cc,cpp,cxx,c++,hpp,hh,hxx,ipp,tpp,inl,sql}",
13939
- ...DEFAULT_PROJECT_MANIFESTS.map((name) => `**/${name}`)
13940
- ];
13941
- REALPATH_FILTER_CONCURRENCY = 64;
13942
- }
13943
- });
13944
-
13945
13406
  // dist/util/git.js
13946
13407
  var git_exports = {};
13947
13408
  __export(git_exports, {
13948
13409
  DEFAULT_GIT_TIMEOUT_MS: () => DEFAULT_GIT_TIMEOUT_MS,
13949
13410
  assertSafeRevision: () => assertSafeRevision,
13411
+ clearGitDiscoveryCacheForTests: () => clearGitDiscoveryCacheForTests,
13950
13412
  clearGitRepositoryCheckCacheForTests: () => clearGitRepositoryCheckCacheForTests,
13951
13413
  decodeGitPath: () => decodeGitPath,
13952
13414
  getGitBlobHashes: () => getGitBlobHashes,
@@ -13956,16 +13418,22 @@ __export(git_exports, {
13956
13418
  isGitIndexSentinel: () => isGitIndexSentinel,
13957
13419
  isGitPathIgnored: () => isGitPathIgnored,
13958
13420
  isGitPathTracked: () => isGitPathTracked,
13421
+ isGitProjectRootIgnored: () => isGitProjectRootIgnored,
13959
13422
  isGitRepo: () => isGitRepo,
13960
13423
  isGitWorktreeSentinel: () => isGitWorktreeSentinel,
13961
13424
  listChangedFiles: () => listChangedFiles,
13425
+ listGitExcludeFiles: () => listGitExcludeFiles,
13426
+ listGitSubmoduleDirectories: () => listGitSubmoduleDirectories,
13427
+ listTrackedFiles: () => listTrackedFiles,
13962
13428
  listUntrackedFiles: () => listUntrackedFiles,
13963
13429
  runGit: () => runGit,
13964
13430
  setGitExecutableForTests: () => setGitExecutableForTests
13965
13431
  });
13966
13432
  import { spawn } from "node:child_process";
13967
13433
  import { StringDecoder } from "node:string_decoder";
13968
- import path9 from "node:path";
13434
+ import fsp4 from "node:fs/promises";
13435
+ import os2 from "node:os";
13436
+ import path8 from "node:path";
13969
13437
  function decodeGitPath(rawPath) {
13970
13438
  if (!rawPath.startsWith('"') || !rawPath.endsWith('"')) {
13971
13439
  return rawPath;
@@ -14009,6 +13477,10 @@ function setGitExecutableForTests(executable) {
14009
13477
  function clearGitRepositoryCheckCacheForTests() {
14010
13478
  gitRepositoryChecks.clear();
14011
13479
  }
13480
+ function clearGitDiscoveryCacheForTests() {
13481
+ gitDiscoveryRootIgnoredChecks.clear();
13482
+ gitExcludeFileChecks.clear();
13483
+ }
14012
13484
  function resolveGitTimeoutMs(timeoutMs) {
14013
13485
  if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
14014
13486
  return Math.floor(timeoutMs);
@@ -14219,7 +13691,7 @@ async function getGitHead(projectRoot) {
14219
13691
  }
14220
13692
  }
14221
13693
  async function isGitRepo(projectRoot) {
14222
- const resolvedRoot = path9.resolve(projectRoot);
13694
+ const resolvedRoot = path8.resolve(projectRoot);
14223
13695
  const cached2 = gitRepositoryChecks.get(resolvedRoot);
14224
13696
  if (cached2)
14225
13697
  return await cached2;
@@ -14240,6 +13712,15 @@ async function isGitPathTracked(projectRoot, file2) {
14240
13712
  async function isGitPathIgnored(projectRoot, file2) {
14241
13713
  return await runGitPathPredicate(projectRoot, ["check-ignore", "--quiet", "--no-index", "--", normalizePath(file2)]);
14242
13714
  }
13715
+ async function isGitProjectRootIgnored(projectRoot) {
13716
+ const resolvedRoot = path8.resolve(projectRoot);
13717
+ const cached2 = gitDiscoveryRootIgnoredChecks.get(resolvedRoot);
13718
+ if (cached2)
13719
+ return await cached2;
13720
+ const check2 = isGitPathIgnored(resolvedRoot, resolvedRoot);
13721
+ gitDiscoveryRootIgnoredChecks.set(resolvedRoot, check2);
13722
+ return await check2;
13723
+ }
14243
13724
  async function runGitPathPredicate(projectRoot, args) {
14244
13725
  try {
14245
13726
  await runGit(projectRoot, args);
@@ -14258,7 +13739,7 @@ async function getGitBlobHashes(projectRoot, files, opts) {
14258
13739
  const gitAvailable = opts?.gitAvailable ?? true;
14259
13740
  if (!gitAvailable)
14260
13741
  return /* @__PURE__ */ new Map();
14261
- const relFileSet = new Set(files.map((file2) => normalizePath(path9.relative(projectRoot, file2))).filter((rel) => rel && !rel.startsWith("..") && !path9.isAbsolute(rel) && rel !== "."));
13742
+ const relFileSet = new Set(files.map((file2) => normalizePath(path8.relative(projectRoot, file2))).filter((rel) => rel && !rel.startsWith("..") && !path8.isAbsolute(rel) && rel !== "."));
14262
13743
  if (!relFileSet.size)
14263
13744
  return /* @__PURE__ */ new Map();
14264
13745
  try {
@@ -14279,7 +13760,7 @@ async function getGitBlobHashes(projectRoot, files, opts) {
14279
13760
  const hash2 = hashes[i];
14280
13761
  if (!hash2)
14281
13762
  continue;
14282
- const abs = normalizePath(path9.resolve(projectRoot, rel));
13763
+ const abs = normalizePath(path8.resolve(projectRoot, rel));
14283
13764
  out.set(abs, hash2);
14284
13765
  }
14285
13766
  return out;
@@ -14293,7 +13774,7 @@ async function hashGitPaths(projectRoot, trackedRel) {
14293
13774
  let currentBatch = [];
14294
13775
  let currentBatchBytes = 0;
14295
13776
  for (const rel of trackedRel) {
14296
- const absolutePath = path9.resolve(projectRoot, rel);
13777
+ const absolutePath = path8.resolve(projectRoot, rel);
14297
13778
  const pathBytes = Buffer.byteLength(absolutePath, "utf8") + 1;
14298
13779
  const wouldExceedBatchLimit = currentBatch.length && currentBatchBytes + pathBytes > MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES;
14299
13780
  if (wouldExceedBatchLimit) {
@@ -14337,7 +13818,120 @@ async function listChangedFiles(projectRoot, opts) {
14337
13818
  const relFiles = stdout.split("\0").filter(Boolean);
14338
13819
  const out = [];
14339
13820
  for (const rel of relFiles) {
14340
- const abs = normalizePath(path9.resolve(projectRoot, rel));
13821
+ const abs = normalizePath(path8.resolve(projectRoot, rel));
13822
+ if (abs)
13823
+ out.push(abs);
13824
+ }
13825
+ return Array.from(new Set(out));
13826
+ } catch (error51) {
13827
+ throw createGitError(projectRoot, args, error51);
13828
+ }
13829
+ }
13830
+ async function listTrackedFiles(projectRoot, opts) {
13831
+ if (!(opts?.gitAvailable ?? true))
13832
+ return [];
13833
+ const args = ["ls-files", "-z"];
13834
+ if (opts?.recurseSubmodules)
13835
+ args.push("--recurse-submodules");
13836
+ try {
13837
+ const { stdout } = await runGit(projectRoot, args, { maxBuffer: 64 * 1024 * 1024 });
13838
+ const out = [];
13839
+ for (const rel of stdout.split("\0").filter(Boolean)) {
13840
+ const abs = normalizePath(path8.resolve(projectRoot, rel));
13841
+ if (abs)
13842
+ out.push(abs);
13843
+ }
13844
+ return Array.from(new Set(out));
13845
+ } catch (error51) {
13846
+ throw createGitError(projectRoot, args, error51);
13847
+ }
13848
+ }
13849
+ async function listGitSubmoduleDirectories(projectRoot, opts) {
13850
+ if (!(opts?.gitAvailable ?? true))
13851
+ return [];
13852
+ const args = ["ls-files", "--stage", "-z"];
13853
+ try {
13854
+ const { stdout } = await runGit(projectRoot, args, { maxBuffer: 64 * 1024 * 1024 });
13855
+ const direct = [];
13856
+ for (const record2 of stdout.split("\0").filter(Boolean)) {
13857
+ if (!record2.startsWith("160000 "))
13858
+ continue;
13859
+ const tabIndex = record2.indexOf(" ");
13860
+ if (tabIndex < 0)
13861
+ continue;
13862
+ const rel = record2.slice(tabIndex + 1);
13863
+ if (rel)
13864
+ direct.push(normalizePath(path8.resolve(projectRoot, rel)));
13865
+ }
13866
+ if (!opts?.recurse || !direct.length)
13867
+ return Array.from(new Set(direct));
13868
+ const nested = await Promise.all(direct.map(async (directory) => await listGitSubmoduleDirectories(directory, opts)));
13869
+ return Array.from(/* @__PURE__ */ new Set([...direct, ...nested.flat()]));
13870
+ } catch (error51) {
13871
+ throw createGitError(projectRoot, args, error51);
13872
+ }
13873
+ }
13874
+ async function listGitExcludeFiles(projectRoot, opts) {
13875
+ if (!(opts?.gitAvailable ?? true))
13876
+ return [];
13877
+ const resolvedRoot = path8.resolve(projectRoot);
13878
+ const cached2 = gitExcludeFileChecks.get(resolvedRoot);
13879
+ if (cached2)
13880
+ return await cached2;
13881
+ const check2 = listGitExcludeFilesUncached(resolvedRoot);
13882
+ gitExcludeFileChecks.set(resolvedRoot, check2);
13883
+ return await check2;
13884
+ }
13885
+ async function listGitExcludeFilesUncached(projectRoot) {
13886
+ let baseDir;
13887
+ try {
13888
+ const { stdout } = await runGit(projectRoot, ["rev-parse", "--show-toplevel"]);
13889
+ baseDir = normalizePath(stdout.trim());
13890
+ } catch {
13891
+ return [];
13892
+ }
13893
+ if (!baseDir)
13894
+ return [];
13895
+ const candidates = [];
13896
+ try {
13897
+ const { stdout } = await runGit(projectRoot, ["rev-parse", "--git-path", "info/exclude"]);
13898
+ const resolved = stdout.trim();
13899
+ if (resolved)
13900
+ candidates.push(normalizePath(path8.resolve(projectRoot, resolved)));
13901
+ } catch {
13902
+ }
13903
+ try {
13904
+ const { stdout } = await runGit(projectRoot, ["config", "--get", "core.excludesFile"]);
13905
+ const configured = stdout.trim();
13906
+ if (configured) {
13907
+ const expanded = configured.startsWith("~") ? path8.join(os2.homedir(), configured.slice(1)) : path8.resolve(projectRoot, configured);
13908
+ candidates.push(normalizePath(expanded));
13909
+ }
13910
+ } catch {
13911
+ }
13912
+ const sources = [];
13913
+ for (const file2 of Array.from(new Set(candidates))) {
13914
+ try {
13915
+ if ((await fsp4.stat(file2)).isFile())
13916
+ sources.push({ file: file2, baseDir });
13917
+ } catch {
13918
+ }
13919
+ }
13920
+ return sources;
13921
+ }
13922
+ async function listUntrackedFiles(projectRoot, opts) {
13923
+ const gitAvailable = opts?.gitAvailable ?? true;
13924
+ if (!gitAvailable)
13925
+ return [];
13926
+ const args = ["ls-files", "--others", "-z"];
13927
+ if (opts?.respectGitignore ?? true)
13928
+ args.push("--exclude-standard");
13929
+ try {
13930
+ const stdout = await runGitCollectStdout(projectRoot, args);
13931
+ const relFiles = stdout.toString().split("\0").filter(Boolean);
13932
+ const out = [];
13933
+ for (const rel of relFiles) {
13934
+ const abs = normalizePath(path8.resolve(projectRoot, rel));
14341
13935
  if (abs)
14342
13936
  out.push(abs);
14343
13937
  }
@@ -14346,82 +13940,719 @@ async function listChangedFiles(projectRoot, opts) {
14346
13940
  throw createGitError(projectRoot, args, error51);
14347
13941
  }
14348
13942
  }
14349
- async function listUntrackedFiles(projectRoot, opts) {
14350
- const gitAvailable = opts?.gitAvailable ?? true;
14351
- if (!gitAvailable)
13943
+ async function getUnifiedDiff(projectRoot, opts) {
13944
+ let args = [
13945
+ "diff",
13946
+ "--no-ext-diff",
13947
+ "--no-textconv",
13948
+ "--find-renames",
13949
+ "--unified=0",
13950
+ "--no-color",
13951
+ "--diff-filter=ACDMRTUXB"
13952
+ ];
13953
+ if (opts.base) {
13954
+ const head = opts.head ?? "HEAD";
13955
+ args = gitDiffArgs(opts.base, head, ["--unified=0", "--no-color", "--diff-filter=ACDMRTUXB"]);
13956
+ } else if (opts.changedSince) {
13957
+ args.push("--end-of-options", assertSafeRevision(opts.changedSince, "changedSince"));
13958
+ } else {
13959
+ return "";
13960
+ }
13961
+ args.push("--");
13962
+ try {
13963
+ return await runGitCollectStdout(projectRoot, args);
13964
+ } catch (error51) {
13965
+ throw createGitError(projectRoot, args, error51);
13966
+ }
13967
+ }
13968
+ function createGitError(projectRoot, args, error51) {
13969
+ let detail = stringifyUnknown(error51);
13970
+ if (typeof error51 === "object" && error51 !== null && "stderr" in error51 && typeof error51.stderr === "string" && error51.stderr.trim()) {
13971
+ detail = error51.stderr.trim();
13972
+ } else if (error51 instanceof Error && error51.message) {
13973
+ detail = error51.message;
13974
+ }
13975
+ return new Error(`git ${args.join(" ")} failed in ${projectRoot}: ${detail}`);
13976
+ }
13977
+ var DEFAULT_GIT_TIMEOUT_MS, gitRepositoryChecks, gitDiscoveryRootIgnoredChecks, gitExcludeFileChecks, MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES, gitExecutableForTests, GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES, UNSAFE_REVISION_CHARACTERS;
13978
+ var init_git = __esm({
13979
+ "dist/util/git.js"() {
13980
+ "use strict";
13981
+ init_ast();
13982
+ init_paths();
13983
+ init_logging();
13984
+ DEFAULT_GIT_TIMEOUT_MS = 3e4;
13985
+ gitRepositoryChecks = /* @__PURE__ */ new Map();
13986
+ gitDiscoveryRootIgnoredChecks = /* @__PURE__ */ new Map();
13987
+ gitExcludeFileChecks = /* @__PURE__ */ new Map();
13988
+ MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES = 24 * 1024;
13989
+ gitExecutableForTests = null;
13990
+ GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES = {
13991
+ a: 7,
13992
+ b: 8,
13993
+ f: 12,
13994
+ n: 10,
13995
+ r: 13,
13996
+ t: 9,
13997
+ v: 11
13998
+ };
13999
+ UNSAFE_REVISION_CHARACTERS = /[\0\r\n]/;
14000
+ }
14001
+ });
14002
+
14003
+ // dist/util/projectFiles.js
14004
+ var projectFiles_exports = {};
14005
+ __export(projectFiles_exports, {
14006
+ DEFAULT_PROJECT_FILE_IGNORES: () => DEFAULT_PROJECT_FILE_IGNORES,
14007
+ DEFAULT_PROJECT_MANIFESTS: () => DEFAULT_PROJECT_MANIFESTS,
14008
+ DEFAULT_PROJECT_PATTERNS: () => DEFAULT_PROJECT_PATTERNS,
14009
+ createDiscoveredFileMatcher: () => createDiscoveredFileMatcher,
14010
+ discoverProjectFiles: () => discoverProjectFiles,
14011
+ filterRealPathsWithinRoot: () => filterRealPathsWithinRoot,
14012
+ isRelativePathInside: () => isRelativePathInside,
14013
+ listProjectFiles: () => listProjectFiles,
14014
+ matchesDiscoveryGlob: () => matchesDiscoveryGlob,
14015
+ translateGlobRootIgnoreGlobsForScanRoot: () => translateGlobRootIgnoreGlobsForScanRoot
14016
+ });
14017
+ import fs6 from "node:fs";
14018
+ import fsp5 from "node:fs/promises";
14019
+ import path9 from "node:path";
14020
+ function isPresent(value) {
14021
+ return value !== void 0;
14022
+ }
14023
+ function normalizeGlobPattern(globPattern) {
14024
+ return globPattern.trim().replace(/\\/g, "/");
14025
+ }
14026
+ function isIgnoreGlobReopenedByIncludes(ignoreGlob, includeGlobs) {
14027
+ const literalSegments = ignoreGlob.split("/").filter((segment) => segment && !segment.includes("*"));
14028
+ if (!literalSegments.length)
14029
+ return false;
14030
+ return includeGlobs.some((includeGlob) => {
14031
+ const includeSegments = includeGlob.split("/");
14032
+ return literalSegments.every((segment) => includeSegments.includes(segment));
14033
+ });
14034
+ }
14035
+ function isLocationIndependentGlob(globPattern) {
14036
+ return globPattern.startsWith("**/");
14037
+ }
14038
+ function translateGlobRootIgnoreGlobsForScanRoot(scanRoot, globRoot, ignoreGlobs) {
14039
+ const relativeScanRoot = normalizePath(path9.relative(globRoot, scanRoot));
14040
+ if (!relativeScanRoot || !isRelativePathInside(relativeScanRoot)) {
14041
+ return ignoreGlobs.map(normalizeGlobPattern).filter(Boolean);
14042
+ }
14043
+ const rootPrefix = `${relativeScanRoot}/`;
14044
+ return ignoreGlobs.map(normalizeGlobPattern).filter(Boolean).map((globPattern) => {
14045
+ const rootRelativePattern = globPattern.startsWith("/") ? globPattern.slice(1) : globPattern;
14046
+ if (isLocationIndependentGlob(rootRelativePattern))
14047
+ return rootRelativePattern;
14048
+ if (rootRelativePattern === relativeScanRoot || rootRelativePattern === `${relativeScanRoot}/**`)
14049
+ return "**";
14050
+ if (rootRelativePattern.startsWith(rootPrefix))
14051
+ return rootRelativePattern.slice(rootPrefix.length) || "**";
14052
+ return void 0;
14053
+ }).filter(isPresent);
14054
+ }
14055
+ function stripGitignoreTrailingSpaces(line) {
14056
+ let end = line.length;
14057
+ while (end > 0 && line[end - 1] === " ") {
14058
+ let slashCount = 0;
14059
+ for (let i = end - 2; i >= 0 && line[i] === "\\"; i -= 1) {
14060
+ slashCount += 1;
14061
+ }
14062
+ if (slashCount % 2 === 1) {
14063
+ break;
14064
+ }
14065
+ end -= 1;
14066
+ }
14067
+ return line.slice(0, end);
14068
+ }
14069
+ function parseGitignoreRule(baseDir, rawLine) {
14070
+ const trimmedLine = stripGitignoreTrailingSpaces(rawLine);
14071
+ if (!trimmedLine)
14072
+ return null;
14073
+ if (trimmedLine.startsWith("#"))
14074
+ return null;
14075
+ const negated = trimmedLine.startsWith("!");
14076
+ let pattern = negated ? trimmedLine.slice(1) : trimmedLine;
14077
+ if (!pattern || pattern === "/")
14078
+ return null;
14079
+ const dirOnly = pattern.endsWith("/");
14080
+ if (dirOnly) {
14081
+ pattern = pattern.slice(0, -1);
14082
+ }
14083
+ if (pattern.startsWith("/")) {
14084
+ pattern = pattern.slice(1);
14085
+ }
14086
+ pattern = pattern.replace(/^\\([#!])/, "$1").replace(/\\/g, "/");
14087
+ if (!pattern)
14088
+ return null;
14089
+ const anchored = trimmedLine.startsWith("/") || negated && trimmedLine.startsWith("!/");
14090
+ const baseMatcherPattern = anchored || pattern.includes("/") ? pattern : `**/${pattern}`;
14091
+ const matcherPattern = dirOnly ? `${baseMatcherPattern}/**` : [baseMatcherPattern, `${baseMatcherPattern}/**`];
14092
+ const matches = (0, import_picomatch.default)(matcherPattern, { dot: true });
14093
+ return {
14094
+ baseDir: normalizePath(baseDir),
14095
+ negated,
14096
+ dirOnly,
14097
+ matches
14098
+ };
14099
+ }
14100
+ async function buildGitignoreIndex(sources) {
14101
+ const sorted = [...sources].map(({ file: file2, baseDir }) => ({ file: normalizePath(file2), baseDir: normalizePath(baseDir) })).sort((left, right) => {
14102
+ const leftFile = left.file;
14103
+ const rightFile = right.file;
14104
+ const depthDelta = leftFile.split("/").length - rightFile.split("/").length;
14105
+ if (depthDelta !== 0)
14106
+ return depthDelta;
14107
+ if (leftFile < rightFile)
14108
+ return -1;
14109
+ return leftFile > rightFile ? 1 : 0;
14110
+ });
14111
+ const gitignoreIndex = { hasRules: false, byBaseDir: /* @__PURE__ */ new Map() };
14112
+ for (const { file: file2, baseDir } of sorted) {
14113
+ if (isIgnoredByGitignore(file2, gitignoreIndex)) {
14114
+ continue;
14115
+ }
14116
+ let raw;
14117
+ try {
14118
+ raw = await fsp5.readFile(file2, "utf8");
14119
+ } catch {
14120
+ continue;
14121
+ }
14122
+ const baseKey = fileIdentityKey(baseDir);
14123
+ for (const line of raw.split(/\r?\n/)) {
14124
+ const rule = parseGitignoreRule(baseDir, line);
14125
+ if (!rule)
14126
+ continue;
14127
+ const existing = gitignoreIndex.byBaseDir.get(baseKey);
14128
+ if (existing)
14129
+ existing.rules.push(rule);
14130
+ else
14131
+ gitignoreIndex.byBaseDir.set(baseKey, { baseDir, rules: [rule] });
14132
+ gitignoreIndex.hasRules = true;
14133
+ }
14134
+ }
14135
+ return gitignoreIndex;
14136
+ }
14137
+ async function findGitignoreFiles(projectRoot) {
14138
+ return await (0, import_fast_glob2.default)(["**/.gitignore"], {
14139
+ cwd: projectRoot,
14140
+ absolute: true,
14141
+ dot: true,
14142
+ followSymbolicLinks: false,
14143
+ ignore: DEFAULT_PROJECT_FILE_IGNORES
14144
+ });
14145
+ }
14146
+ async function loadGitignoreIndexForRootAliases(projectRoot) {
14147
+ const roots = [projectRoot];
14148
+ const realRoot = await fsp5.realpath(projectRoot);
14149
+ if (normalizePath(realRoot) !== normalizePath(projectRoot)) {
14150
+ roots.push(realRoot);
14151
+ }
14152
+ const sources = [];
14153
+ for (const root of roots) {
14154
+ for (const file2 of await findGitignoreFiles(root)) {
14155
+ sources.push({ file: file2, baseDir: path9.dirname(file2) });
14156
+ }
14157
+ }
14158
+ return await buildGitignoreIndex(sources);
14159
+ }
14160
+ function isIgnoredByGitignore(absolutePath, gitignoreIndex) {
14161
+ if (!gitignoreIndex.hasRules)
14162
+ return false;
14163
+ const chain = [];
14164
+ let current = normalizePath(path9.dirname(absolutePath));
14165
+ for (; ; ) {
14166
+ const group = gitignoreIndex.byBaseDir.get(fileIdentityKey(current));
14167
+ if (group)
14168
+ chain.push(group);
14169
+ const parent = normalizePath(path9.dirname(current));
14170
+ if (parent === current)
14171
+ break;
14172
+ current = parent;
14173
+ }
14174
+ let ignored = false;
14175
+ for (let depth = chain.length - 1; depth >= 0; depth -= 1) {
14176
+ const group = chain[depth];
14177
+ const relativePath3 = path9.relative(group.baseDir, absolutePath);
14178
+ if (!isRelativePathInside(relativePath3)) {
14179
+ continue;
14180
+ }
14181
+ const normalizedRelativePath2 = normalizePath(relativePath3);
14182
+ const hasSeparator = normalizedRelativePath2.includes("/");
14183
+ for (const rule of group.rules) {
14184
+ if (rule.dirOnly && !hasSeparator) {
14185
+ continue;
14186
+ }
14187
+ if (rule.matches(normalizedRelativePath2)) {
14188
+ ignored = !rule.negated;
14189
+ }
14190
+ }
14191
+ }
14192
+ return ignored;
14193
+ }
14194
+ async function ensureDirectoryReadable(directoryPath, label) {
14195
+ const resolvedPath = path9.resolve(directoryPath);
14196
+ let stats;
14197
+ try {
14198
+ stats = await fsp5.stat(resolvedPath);
14199
+ } catch (error51) {
14200
+ throw new Error(`${label} does not exist or is not readable: ${resolvedPath} (${stringifyUnknown(error51)})`);
14201
+ }
14202
+ if (!stats.isDirectory()) {
14203
+ throw new Error(`${label} is not a directory: ${resolvedPath}`);
14204
+ }
14205
+ return resolvedPath;
14206
+ }
14207
+ function collectCandidateAncestorDirectories(root, files) {
14208
+ const rootKey = fileIdentityKey(root);
14209
+ const seen = /* @__PURE__ */ new Set([rootKey]);
14210
+ const directories = [normalizePath(root)];
14211
+ for (const file2 of files) {
14212
+ let current = normalizePath(path9.dirname(file2));
14213
+ while (fileIdentityKey(current) !== rootKey) {
14214
+ if (seen.has(fileIdentityKey(current)))
14215
+ break;
14216
+ seen.add(fileIdentityKey(current));
14217
+ directories.push(current);
14218
+ const parent = normalizePath(path9.dirname(current));
14219
+ if (parent === current)
14220
+ break;
14221
+ current = parent;
14222
+ }
14223
+ }
14224
+ return directories;
14225
+ }
14226
+ async function findGitIgnoreSources(root, files) {
14227
+ const directories = collectCandidateAncestorDirectories(root, files);
14228
+ const present = await mapLimitSemaphore(directories, REALPATH_FILTER_CONCURRENCY, async (directory) => {
14229
+ const candidate = normalizePath(path9.join(directory, ".gitignore"));
14230
+ try {
14231
+ return (await fsp5.stat(candidate)).isFile() ? { file: candidate, baseDir: directory } : null;
14232
+ } catch {
14233
+ return null;
14234
+ }
14235
+ });
14236
+ const sources = present.filter((entry) => entry !== null);
14237
+ sources.push(...await listGitExcludeFiles(root));
14238
+ return sources;
14239
+ }
14240
+ async function listGitCandidateFiles(root, logLevel) {
14241
+ try {
14242
+ if (!await isGitRepo(root))
14243
+ return null;
14244
+ if (await isGitProjectRootIgnored(root))
14245
+ return null;
14246
+ const [tracked, untracked, submoduleDirectories] = await Promise.all([
14247
+ listTrackedFiles(root, { recurseSubmodules: true, ...logLevel === void 0 ? {} : { logLevel } }),
14248
+ listUntrackedFiles(root, { respectGitignore: true }),
14249
+ listGitSubmoduleDirectories(root, { recurse: true })
14250
+ ]);
14251
+ const submoduleUntracked = await Promise.all(submoduleDirectories.map(async (directory) => await listUntrackedFiles(directory, { respectGitignore: true })));
14252
+ const files = Array.from(/* @__PURE__ */ new Set([...tracked, ...untracked, ...submoduleUntracked.flat()])).sort();
14253
+ return { files, gitignoreFiles: await findGitIgnoreSources(root, files) };
14254
+ } catch (error51) {
14255
+ logWithLevel(logLevel, "debug", `Git discovery unavailable for ${root}: ${stringifyUnknown(error51)}`);
14256
+ return null;
14257
+ }
14258
+ }
14259
+ async function listProjectFiles(projectRoot, patterns = DEFAULT_PROJECT_PATTERNS, options) {
14260
+ const root = await ensureDirectoryReadable(projectRoot, "Project root");
14261
+ const globRoot = options?.globRoot ? await ensureDirectoryReadable(options.globRoot, "Glob root") : root;
14262
+ const includeGlobs = (options?.includeGlobs ?? []).map(normalizeGlobPattern).filter(Boolean);
14263
+ const includeMatchers = includeGlobs.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
14264
+ const userIgnoreGlobs = (options?.ignoreGlobs ?? []).map(normalizeGlobPattern).filter(Boolean);
14265
+ const userIgnoreMatchers = userIgnoreGlobs.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
14266
+ const defaultIgnoreMatchers = DEFAULT_PROJECT_FILE_IGNORES.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
14267
+ const patternMatchers = patterns.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
14268
+ const translatedUserIgnoreGlobs = translateGlobRootIgnoreGlobsForScanRoot(root, globRoot, userIgnoreGlobs);
14269
+ const fastGlobIgnoreGlobs = [...DEFAULT_PROJECT_FILE_IGNORES, ...translatedUserIgnoreGlobs];
14270
+ const symlinkProbeIgnoreGlobs = includeGlobs.length ? [
14271
+ ...translatedUserIgnoreGlobs,
14272
+ ...DEFAULT_PROJECT_FILE_IGNORES.filter((ignoreGlob) => !isIgnoreGlobReopenedByIncludes(ignoreGlob, includeGlobs))
14273
+ ] : fastGlobIgnoreGlobs;
14274
+ try {
14275
+ const useGitignore = options?.useGitignore ?? true;
14276
+ const realRoot = await fsp5.realpath(root);
14277
+ const gitCandidates = useGitignore && options?.gitignoreRoot === void 0 && normalizePath(realRoot) === normalizePath(root) ? await listGitCandidateFiles(root, options?.logLevel) : null;
14278
+ let gitignoreIndex = EMPTY_GITIGNORE_INDEX;
14279
+ if (useGitignore && gitCandidates) {
14280
+ gitignoreIndex = await buildGitignoreIndex(gitCandidates.gitignoreFiles);
14281
+ } else if (useGitignore) {
14282
+ const gitignoreRoot = options?.gitignoreRoot ? await ensureDirectoryReadable(options.gitignoreRoot, "Gitignore root") : root;
14283
+ gitignoreIndex = await loadGitignoreIndexForRootAliases(gitignoreRoot);
14284
+ }
14285
+ const files = gitCandidates ? gitCandidates.files : await (0, import_fast_glob2.default)(patterns, {
14286
+ cwd: root,
14287
+ absolute: true,
14288
+ dot: true,
14289
+ followSymbolicLinks: false,
14290
+ ignore: fastGlobIgnoreGlobs
14291
+ });
14292
+ const includedOverrideFiles = includeGlobs.length && !gitCandidates ? await (0, import_fast_glob2.default)(translateGlobRootIgnoreGlobsForScanRoot(root, globRoot, includeGlobs), {
14293
+ cwd: root,
14294
+ absolute: true,
14295
+ dot: true,
14296
+ followSymbolicLinks: false,
14297
+ ignore: translatedUserIgnoreGlobs
14298
+ }) : [];
14299
+ const symlinkOptions = {
14300
+ globRoot,
14301
+ ...gitCandidates ? { candidatePaths: gitCandidates.files } : {},
14302
+ ...options?.knownSymlinkDirectories !== void 0 ? { knownSymlinkDirectories: options.knownSymlinkDirectories } : {},
14303
+ ...options?.onSymlinkDirectoriesDiscovered ? { onSymlinkDirectoriesDiscovered: options.onSymlinkDirectoriesDiscovered } : {}
14304
+ };
14305
+ const safeSymlinkDirectories = await resolveSafeSymlinkDirectories(root, realRoot, symlinkProbeIgnoreGlobs, symlinkOptions);
14306
+ const linkedFiles = await listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, fastGlobIgnoreGlobs, {
14307
+ ...symlinkOptions,
14308
+ filterIgnoreGlobs: [...DEFAULT_PROJECT_FILE_IGNORES, ...userIgnoreGlobs],
14309
+ resolvedSafeSymlinkDirectories: safeSymlinkDirectories
14310
+ });
14311
+ const linkedOverrideFiles = includeGlobs.length === 0 ? [] : await listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, translatedUserIgnoreGlobs, {
14312
+ globRoot,
14313
+ filterIgnoreGlobs: userIgnoreGlobs,
14314
+ resolvedSafeSymlinkDirectories: safeSymlinkDirectories
14315
+ });
14316
+ const rootSafeFiles = await filterRealPathsWithinRootEntries([...files, ...includedOverrideFiles, ...linkedFiles, ...linkedOverrideFiles], realRoot);
14317
+ const seen = /* @__PURE__ */ new Set();
14318
+ return rootSafeFiles.map(({ path: filePath, realPath }) => ({ filePath: normalizePath(filePath), realPath })).filter(({ filePath, realPath }) => {
14319
+ if (seen.has(filePath))
14320
+ return false;
14321
+ seen.add(filePath);
14322
+ const rootRelative = normalizePath(path9.relative(root, filePath));
14323
+ if (!isRelativePathInside(rootRelative))
14324
+ return false;
14325
+ if (!patternMatchers.some((matcher) => matcher(rootRelative)))
14326
+ return false;
14327
+ const matchesInclude = !includeMatchers.length || includeMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher));
14328
+ if (!matchesInclude)
14329
+ return false;
14330
+ if (userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher))) {
14331
+ return false;
14332
+ }
14333
+ const ignoredByDefault = defaultIgnoreMatchers.some((matcher) => matcher(rootRelative));
14334
+ if (ignoredByDefault && !(includeMatchers.length > 0 && matchesInclude)) {
14335
+ return false;
14336
+ }
14337
+ if (isIgnoredByGitignore(filePath, gitignoreIndex))
14338
+ return false;
14339
+ return normalizePath(realPath) === filePath || !isIgnoredByGitignore(realPath, gitignoreIndex);
14340
+ }).map(({ filePath }) => filePath);
14341
+ } catch (error51) {
14342
+ logWithLevel(options?.logLevel, "debug", `listProjectFiles failed for ${root}: ${stringifyUnknown(error51)}`);
14343
+ throw new Error(`Failed to list files in ${root}: ${stringifyUnknown(error51)}`);
14344
+ }
14345
+ }
14346
+ function createDiscoveredFileMatcher(root, globRoot, patterns, options) {
14347
+ const patternMatchers = patterns.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
14348
+ const defaultIgnoreMatchers = DEFAULT_PROJECT_FILE_IGNORES.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
14349
+ const includeMatchers = (options?.includeGlobs ?? []).map(normalizeGlobPattern).filter(Boolean).map((pattern) => (0, import_picomatch.default)(pattern, { dot: true }));
14350
+ const userIgnoreMatchers = (options?.ignoreGlobs ?? []).map(normalizeGlobPattern).filter(Boolean).map((pattern) => (0, import_picomatch.default)(pattern, { dot: true }));
14351
+ return (absolutePath) => {
14352
+ const rootRelative = path9.relative(root, absolutePath);
14353
+ if (!isRelativePathInside(rootRelative))
14354
+ return false;
14355
+ const normalizedRootRelative = normalizePath(rootRelative);
14356
+ if (!patternMatchers.some((matcher) => matcher(normalizedRootRelative)))
14357
+ return false;
14358
+ const matchesInclude = !includeMatchers.length || includeMatchers.some((matcher) => matchesDiscoveryGlob(absolutePath, globRoot, matcher));
14359
+ if (!matchesInclude)
14360
+ return false;
14361
+ if (defaultIgnoreMatchers.some((matcher) => matcher(normalizedRootRelative))) {
14362
+ if (!(includeMatchers.length > 0 && matchesInclude))
14363
+ return false;
14364
+ }
14365
+ return !userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(absolutePath, globRoot, matcher));
14366
+ };
14367
+ }
14368
+ async function isSafeSymlinkDirectory(root, linkPath, realRoot) {
14369
+ try {
14370
+ if (!isRelativePathInside(path9.relative(root, linkPath)))
14371
+ return false;
14372
+ if (!(await fsp5.lstat(linkPath)).isSymbolicLink())
14373
+ return false;
14374
+ const [realPath, targetStats] = await Promise.all([fsp5.realpath(linkPath), fsp5.stat(linkPath)]);
14375
+ if (!targetStats.isDirectory())
14376
+ return false;
14377
+ if (!isFilePathWithinRoot(realRoot, realPath))
14378
+ return false;
14379
+ return normalizePath(realPath) !== normalizePath(realRoot);
14380
+ } catch {
14381
+ return false;
14382
+ }
14383
+ }
14384
+ async function resolveSafeSymlinkDirectories(root, realRoot, ignore, options) {
14385
+ if (options.knownSymlinkDirectories !== void 0) {
14386
+ const verified = await mapLimitSemaphore(Array.from(new Set(options.knownSymlinkDirectories)), REALPATH_FILTER_CONCURRENCY, async (linkPath) => await isSafeSymlinkDirectory(root, linkPath, realRoot) ? linkPath : null);
14387
+ const resolved = verified.filter((entry) => entry !== null);
14388
+ options.onSymlinkDirectoriesDiscovered?.(resolved, "known");
14389
+ return resolved;
14390
+ }
14391
+ if (options.candidatePaths !== void 0) {
14392
+ const ignoreMatchers = ignore.map(normalizeGlobPattern).filter(Boolean).map((pattern) => (0, import_picomatch.default)(pattern, { dot: true }));
14393
+ const candidatePaths = Array.from(new Set(options.candidatePaths)).filter((candidatePath) => {
14394
+ const relativePath3 = normalizePath(path9.relative(root, candidatePath));
14395
+ if (!isRelativePathInside(relativePath3))
14396
+ return false;
14397
+ return !ignoreMatchers.some((matcher) => matcher(relativePath3));
14398
+ });
14399
+ const verified = await mapLimitSemaphore(candidatePaths, REALPATH_FILTER_CONCURRENCY, async (linkPath) => await isSafeSymlinkDirectory(root, linkPath, realRoot) ? linkPath : null);
14400
+ const discovered2 = verified.filter((entry) => entry !== null);
14401
+ options.onSymlinkDirectoriesDiscovered?.(discovered2, "git-candidates");
14402
+ return discovered2;
14403
+ }
14404
+ const entries = await (0, import_fast_glob2.default)(["**/*"], {
14405
+ cwd: root,
14406
+ absolute: true,
14407
+ dot: true,
14408
+ onlyFiles: false,
14409
+ followSymbolicLinks: false,
14410
+ objectMode: true,
14411
+ ignore
14412
+ });
14413
+ const candidates = await mapLimitSemaphore(entries.filter((entry) => entry.dirent.isSymbolicLink()).map((entry) => entry.path), REALPATH_FILTER_CONCURRENCY, async (linkPath) => await isSafeSymlinkDirectory(root, linkPath, realRoot) ? linkPath : null);
14414
+ const discovered = candidates.filter((entry) => entry !== null);
14415
+ options.onSymlinkDirectoriesDiscovered?.(discovered, "filesystem");
14416
+ return discovered;
14417
+ }
14418
+ async function listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, ignore, options = {}) {
14419
+ const globRoot = options.globRoot ?? root;
14420
+ const filterIgnoreGlobs = options.filterIgnoreGlobs ?? ignore;
14421
+ const rootRelativeIgnoreMatchers = filterIgnoreGlobs.map(normalizeGlobPattern).filter(Boolean).map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
14422
+ const locationIndependentIgnores = ignore.map(normalizeGlobPattern).filter(isLocationIndependentGlob);
14423
+ const safeSymlinkDirectories = options.resolvedSafeSymlinkDirectories ?? await resolveSafeSymlinkDirectories(root, realRoot, ignore, options);
14424
+ if (!safeSymlinkDirectories.length)
14352
14425
  return [];
14353
- const args = ["ls-files", "--others", "-z"];
14354
- if (opts?.respectGitignore ?? true)
14355
- args.push("--exclude-standard");
14356
- try {
14357
- const stdout = await runGitCollectStdout(projectRoot, args);
14358
- const relFiles = stdout.toString().split("\0").filter(Boolean);
14359
- const out = [];
14360
- for (const rel of relFiles) {
14361
- const abs = normalizePath(path9.resolve(projectRoot, rel));
14362
- if (abs)
14363
- out.push(abs);
14426
+ const filesByPath = /* @__PURE__ */ new Map();
14427
+ const filesByDirectory = await mapLimitSemaphore(Array.from(safeSymlinkDirectories), REALPATH_FILTER_CONCURRENCY, async (directory) => (await (0, import_fast_glob2.default)(patterns, {
14428
+ cwd: directory,
14429
+ absolute: true,
14430
+ dot: true,
14431
+ followSymbolicLinks: false,
14432
+ ignore: locationIndependentIgnores,
14433
+ ...options.onlyFiles !== void 0 ? { onlyFiles: options.onlyFiles } : {},
14434
+ ...options.markDirectories !== void 0 ? { markDirectories: options.markDirectories } : {}
14435
+ })).filter((filePath) => {
14436
+ const cleanPath = filePath.endsWith("/") ? filePath.slice(0, -1) : filePath;
14437
+ return !rootRelativeIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(cleanPath, globRoot, matcher));
14438
+ }));
14439
+ for (const files of filesByDirectory) {
14440
+ for (const file2 of files) {
14441
+ filesByPath.set(normalizePath(file2), file2);
14364
14442
  }
14365
- return Array.from(new Set(out));
14366
- } catch (error51) {
14367
- throw createGitError(projectRoot, args, error51);
14368
14443
  }
14444
+ return [...filesByPath.values()];
14369
14445
  }
14370
- async function getUnifiedDiff(projectRoot, opts) {
14371
- let args = [
14372
- "diff",
14373
- "--no-ext-diff",
14374
- "--no-textconv",
14375
- "--find-renames",
14376
- "--unified=0",
14377
- "--no-color",
14378
- "--diff-filter=ACDMRTUXB"
14379
- ];
14380
- if (opts.base) {
14381
- const head = opts.head ?? "HEAD";
14382
- args = gitDiffArgs(opts.base, head, ["--unified=0", "--no-color", "--diff-filter=ACDMRTUXB"]);
14383
- } else if (opts.changedSince) {
14384
- args.push("--end-of-options", assertSafeRevision(opts.changedSince, "changedSince"));
14385
- } else {
14386
- return "";
14446
+ async function filterRealPathsWithinRootEntries(paths, realRoot) {
14447
+ const filtered = await mapLimitSemaphore(paths, REALPATH_FILTER_CONCURRENCY, async (filePath) => {
14448
+ try {
14449
+ const realPath = await fsp5.realpath(filePath);
14450
+ return isFilePathWithinRoot(realRoot, realPath) ? { path: filePath, realPath } : null;
14451
+ } catch {
14452
+ return null;
14453
+ }
14454
+ });
14455
+ return filtered.filter((entry) => entry !== null);
14456
+ }
14457
+ async function filterRealPathsWithinRoot(paths, realRoot) {
14458
+ const entries = await filterRealPathsWithinRootEntries(paths, realRoot);
14459
+ return entries.map((entry) => entry.path);
14460
+ }
14461
+ function toProjectGlob(pattern) {
14462
+ return pattern.startsWith("**/") ? pattern : `**/${pattern}`;
14463
+ }
14464
+ async function buildProjectFileInfo(def, filePath) {
14465
+ const normalizedPath = normalizePath(filePath);
14466
+ const projectRoot = normalizePath(path9.dirname(filePath));
14467
+ let name = null;
14468
+ if (def.parseName && def.kind === "file") {
14469
+ try {
14470
+ const raw = await fsp5.readFile(filePath, "utf8");
14471
+ name = def.parseName(raw, filePath);
14472
+ } catch {
14473
+ name = null;
14474
+ }
14387
14475
  }
14388
- args.push("--");
14389
- try {
14390
- return await runGitCollectStdout(projectRoot, args);
14391
- } catch (error51) {
14392
- throw createGitError(projectRoot, args, error51);
14476
+ if (!name && def.nameFromPath) {
14477
+ if (def.nameFromPath === "file") {
14478
+ name = trimToNull(path9.basename(filePath, path9.extname(filePath)));
14479
+ } else {
14480
+ name = trimToNull(path9.basename(projectRoot));
14481
+ }
14393
14482
  }
14483
+ return {
14484
+ path: normalizedPath,
14485
+ kind: def.kind,
14486
+ type: def.type,
14487
+ role: def.role,
14488
+ projectRoot,
14489
+ ...name ? { name } : {}
14490
+ };
14394
14491
  }
14395
- function createGitError(projectRoot, args, error51) {
14396
- let detail = stringifyUnknown(error51);
14397
- if (typeof error51 === "object" && error51 !== null && "stderr" in error51 && typeof error51.stderr === "string" && error51.stderr.trim()) {
14398
- detail = error51.stderr.trim();
14399
- } else if (error51 instanceof Error && error51.message) {
14400
- detail = error51.message;
14492
+ async function discoverProjectFiles(projectRoot, options) {
14493
+ const root = await ensureDirectoryReadable(projectRoot, "Project root");
14494
+ try {
14495
+ const realRoot = await fsp5.realpath(root);
14496
+ const allPatterns = PROJECT_FILE_DEFINITIONS.flatMap((def) => def.patterns.map(toProjectGlob));
14497
+ const matches = await (0, import_fast_glob2.default)(allPatterns, {
14498
+ cwd: root,
14499
+ absolute: true,
14500
+ dot: true,
14501
+ followSymbolicLinks: false,
14502
+ ignore: DEFAULT_PROJECT_FILE_IGNORES,
14503
+ markDirectories: true,
14504
+ onlyFiles: false
14505
+ });
14506
+ const linkedMatches = await listEntriesFromSafeSymlinkDirectories(root, realRoot, allPatterns, DEFAULT_PROJECT_FILE_IGNORES, {
14507
+ markDirectories: true,
14508
+ onlyFiles: false,
14509
+ ...options?.knownSymlinkDirectories !== void 0 ? { knownSymlinkDirectories: options.knownSymlinkDirectories } : {},
14510
+ ...options?.onSymlinkDirectoriesDiscovered ? { onSymlinkDirectoriesDiscovered: options.onSymlinkDirectoriesDiscovered } : {}
14511
+ });
14512
+ const rootSafeMatches = await filterRealPathsWithinRoot([...matches, ...linkedMatches].map((match) => match.endsWith("/") ? match.slice(0, -1) : match), realRoot);
14513
+ const projectFileDefinitionMatchers = PROJECT_FILE_DEFINITIONS.map((definition) => definition.patterns.map((pattern) => pattern.includes("*") || pattern.includes("?") ? new RegExp("^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$") : void 0));
14514
+ const entries = [];
14515
+ const matchTasks = rootSafeMatches.map(async (cleanMatch) => {
14516
+ const stats = await fsp5.stat(cleanMatch);
14517
+ const isDir = stats.isDirectory();
14518
+ const fileName = path9.basename(cleanMatch);
14519
+ for (let definitionIndex = 0; definitionIndex < PROJECT_FILE_DEFINITIONS.length; definitionIndex++) {
14520
+ const def = PROJECT_FILE_DEFINITIONS[definitionIndex];
14521
+ if (isDir && def.kind !== "dir")
14522
+ continue;
14523
+ if (!isDir && def.kind !== "file")
14524
+ continue;
14525
+ const matchesPattern = def.patterns.some((pattern, patternIndex) => {
14526
+ const matcher = projectFileDefinitionMatchers[definitionIndex][patternIndex];
14527
+ return matcher ? matcher.test(fileName) : pattern === fileName;
14528
+ });
14529
+ if (matchesPattern) {
14530
+ entries.push(await buildProjectFileInfo(def, cleanMatch));
14531
+ }
14532
+ }
14533
+ });
14534
+ await Promise.all(matchTasks);
14535
+ const byKey2 = /* @__PURE__ */ new Map();
14536
+ for (const entry of entries) {
14537
+ const key = `${entry.path}::${entry.type}::${entry.role}`;
14538
+ const existing = byKey2.get(key);
14539
+ if (!existing || !existing.name && entry.name) {
14540
+ byKey2.set(key, entry);
14541
+ }
14542
+ }
14543
+ return Array.from(byKey2.values()).sort((a, b) => {
14544
+ if (a.path === b.path)
14545
+ return a.type.localeCompare(b.type);
14546
+ return a.path.localeCompare(b.path);
14547
+ });
14548
+ } catch (error51) {
14549
+ logWithLevel(options?.logLevel, "debug", `discoverProjectFiles failed for ${root}: ${stringifyUnknown(error51)}`);
14550
+ throw new Error(`Failed to discover project files in ${root}: ${stringifyUnknown(error51)}`);
14401
14551
  }
14402
- return new Error(`git ${args.join(" ")} failed in ${projectRoot}: ${detail}`);
14403
14552
  }
14404
- var DEFAULT_GIT_TIMEOUT_MS, gitRepositoryChecks, MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES, gitExecutableForTests, GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES, UNSAFE_REVISION_CHARACTERS;
14405
- var init_git = __esm({
14406
- "dist/util/git.js"() {
14553
+ var import_fast_glob2, import_picomatch, DEFAULT_PROJECT_FILE_IGNORES, DEFAULT_PROJECT_MANIFESTS, DEFAULT_PROJECT_PATTERNS, REALPATH_FILTER_CONCURRENCY, EMPTY_GITIGNORE_INDEX;
14554
+ var init_projectFiles = __esm({
14555
+ "dist/util/projectFiles.js"() {
14407
14556
  "use strict";
14557
+ import_fast_glob2 = __toESM(require_out4(), 1);
14558
+ import_picomatch = __toESM(require_picomatch4(), 1);
14559
+ init_logging();
14408
14560
  init_ast();
14409
14561
  init_paths();
14410
- init_logging();
14411
- DEFAULT_GIT_TIMEOUT_MS = 3e4;
14412
- gitRepositoryChecks = /* @__PURE__ */ new Map();
14413
- MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES = 24 * 1024;
14414
- gitExecutableForTests = null;
14415
- GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES = {
14416
- a: 7,
14417
- b: 8,
14418
- f: 12,
14419
- n: 10,
14420
- r: 13,
14421
- t: 9,
14422
- v: 11
14423
- };
14424
- UNSAFE_REVISION_CHARACTERS = /[\0\r\n]/;
14562
+ init_discoveryPath();
14563
+ init_discoveryPath();
14564
+ init_definitions();
14565
+ init_parsers();
14566
+ init_concurrency();
14567
+ init_git();
14568
+ DEFAULT_PROJECT_FILE_IGNORES = [
14569
+ "**/node_modules/**",
14570
+ "**/.git/**",
14571
+ // Never treat Codegraph's own on-disk state as project input. `.codegraph/` contains
14572
+ // lifecycle metadata and the indexer disk cache. Keep the legacy cache ignore so
14573
+ // pre-migration checkouts stay excluded from discovery.
14574
+ "**/.codegraph/**",
14575
+ "**/.codegraph-cache/**",
14576
+ "**/dist/**",
14577
+ "**/build/**",
14578
+ "**/target/**",
14579
+ "**/.venv/**",
14580
+ "**/venv/**",
14581
+ "**/site-packages/**",
14582
+ "**/__pycache__/**",
14583
+ "**/vendor/bundle/**",
14584
+ "**/.build/**",
14585
+ "**/Pods/**"
14586
+ ];
14587
+ DEFAULT_PROJECT_MANIFESTS = [
14588
+ "package.json",
14589
+ "package-lock.json",
14590
+ "pnpm-lock.yaml",
14591
+ "yarn.lock",
14592
+ "bun.lockb",
14593
+ "pnpm-workspace.yaml",
14594
+ "lerna.json",
14595
+ "nx.json",
14596
+ "turbo.json",
14597
+ "tsconfig.json",
14598
+ "jsconfig.json",
14599
+ "requirements.txt",
14600
+ "requirements.in",
14601
+ "pyproject.toml",
14602
+ "Pipfile",
14603
+ "Pipfile.lock",
14604
+ "poetry.lock",
14605
+ "setup.py",
14606
+ "setup.cfg",
14607
+ "Cargo.toml",
14608
+ "Cargo.lock",
14609
+ "rust-toolchain",
14610
+ "rust-toolchain.toml",
14611
+ "go.mod",
14612
+ "go.sum",
14613
+ "go.work",
14614
+ "Gemfile",
14615
+ "Gemfile.lock",
14616
+ "*.gemspec",
14617
+ "pom.xml",
14618
+ "mvnw",
14619
+ "build.gradle",
14620
+ "build.gradle.kts",
14621
+ "settings.gradle",
14622
+ "settings.gradle.kts",
14623
+ "gradle.properties",
14624
+ "gradlew",
14625
+ "*.csproj",
14626
+ "*.fsproj",
14627
+ "*.vbproj",
14628
+ "*.sln",
14629
+ "Directory.Build.props",
14630
+ "Directory.Build.targets",
14631
+ "global.json",
14632
+ "composer.json",
14633
+ "composer.lock",
14634
+ "Package.swift",
14635
+ "Package.resolved",
14636
+ "CMakeLists.txt",
14637
+ "CMakePresets.json",
14638
+ "CMakeUserPresets.json",
14639
+ "Makefile",
14640
+ "makefile",
14641
+ "GNUmakefile",
14642
+ "configure.ac",
14643
+ "configure.in",
14644
+ "meson.build",
14645
+ "meson_options.txt",
14646
+ "conanfile.txt",
14647
+ "conanfile.py",
14648
+ "vcpkg.json"
14649
+ ];
14650
+ DEFAULT_PROJECT_PATTERNS = [
14651
+ "**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,py,pyi,php,vue,svelte,astro,hbs,handlebars,md,mdx,rst,adoc,asciidoc,go,java,cs,rb,rs,html,htm,css,scss,less,kt,kts,swift,zig,c,h,cc,cpp,cxx,c++,hpp,hh,hxx,ipp,tpp,inl,sql}",
14652
+ ...DEFAULT_PROJECT_MANIFESTS.map((name) => `**/${name}`)
14653
+ ];
14654
+ REALPATH_FILTER_CONCURRENCY = 64;
14655
+ EMPTY_GITIGNORE_INDEX = { hasRules: false, byBaseDir: /* @__PURE__ */ new Map() };
14425
14656
  }
14426
14657
  });
14427
14658
 
@@ -14519,13 +14750,13 @@ var init_projectSymbols = __esm({
14519
14750
  });
14520
14751
 
14521
14752
  // dist/util/resolution/jvm.js
14522
- import fsp5 from "node:fs/promises";
14753
+ import fsp6 from "node:fs/promises";
14523
14754
  import path10 from "node:path";
14524
14755
  async function readJvmSymbolIndex(filePath, cache, options) {
14525
14756
  const cached2 = cache.get(filePath);
14526
14757
  if (cached2)
14527
14758
  return cached2;
14528
- const source = await fsp5.readFile(filePath, "utf8");
14759
+ const source = await fsp6.readFile(filePath, "utf8");
14529
14760
  const packageName = source.match(options.packagePattern)?.[1] ?? null;
14530
14761
  const symbols = /* @__PURE__ */ new Set();
14531
14762
  for (const match of source.matchAll(options.declarationPattern)) {
@@ -14680,7 +14911,7 @@ var init_findFirstExisting = __esm({
14680
14911
  });
14681
14912
 
14682
14913
  // dist/util/resolution/files.js
14683
- import fsp6 from "node:fs/promises";
14914
+ import fsp7 from "node:fs/promises";
14684
14915
  import path12 from "node:path";
14685
14916
  async function findNearestFile(startDir, stopDir, fileName) {
14686
14917
  let dir = path12.resolve(startDir);
@@ -14700,7 +14931,7 @@ async function findNearestFile(startDir, stopDir, fileName) {
14700
14931
  }
14701
14932
  async function isDirectory(p) {
14702
14933
  try {
14703
- const st = await fsp6.stat(p);
14934
+ const st = await fsp7.stat(p);
14704
14935
  return st.isDirectory();
14705
14936
  } catch {
14706
14937
  return false;
@@ -14715,7 +14946,7 @@ var init_files = __esm({
14715
14946
 
14716
14947
  // dist/util/resolution/go.js
14717
14948
  import fs7 from "node:fs";
14718
- import fsp7 from "node:fs/promises";
14949
+ import fsp8 from "node:fs/promises";
14719
14950
  import path13 from "node:path";
14720
14951
  function stripInlineComment(line) {
14721
14952
  const idx = line.indexOf("//");
@@ -14725,7 +14956,7 @@ async function parseGoMod(moduleRoot) {
14725
14956
  const modPath = path13.join(moduleRoot, "go.mod");
14726
14957
  if (!await fileExists(modPath))
14727
14958
  return null;
14728
- const raw = await fsp7.readFile(modPath, "utf8");
14959
+ const raw = await fsp8.readFile(modPath, "utf8");
14729
14960
  const lines = raw.split(/\r?\n/);
14730
14961
  let modulePath = null;
14731
14962
  const replacements = /* @__PURE__ */ new Map();
@@ -14761,7 +14992,7 @@ async function parseGoMod(moduleRoot) {
14761
14992
  };
14762
14993
  }
14763
14994
  async function parseGoWork(goWorkPath) {
14764
- const content = await fsp7.readFile(goWorkPath, "utf8");
14995
+ const content = await fsp8.readFile(goWorkPath, "utf8");
14765
14996
  const lines = content.split(/\r?\n/);
14766
14997
  const modules = [];
14767
14998
  let inUseBlock = false;
@@ -14790,7 +15021,7 @@ async function parseGoWork(goWorkPath) {
14790
15021
  }
14791
15022
  async function findGoPackageEntry(dirPath) {
14792
15023
  try {
14793
- const stat = await fsp7.stat(dirPath);
15024
+ const stat = await fsp8.stat(dirPath);
14794
15025
  if (!stat.isDirectory())
14795
15026
  return null;
14796
15027
  } catch {
@@ -14798,7 +15029,7 @@ async function findGoPackageEntry(dirPath) {
14798
15029
  }
14799
15030
  let entries = [];
14800
15031
  try {
14801
- entries = await fsp7.readdir(dirPath, { withFileTypes: true });
15032
+ entries = await fsp8.readdir(dirPath, { withFileTypes: true });
14802
15033
  } catch {
14803
15034
  return null;
14804
15035
  }
@@ -14950,7 +15181,7 @@ var init_node = __esm({
14950
15181
  });
14951
15182
 
14952
15183
  // dist/util/resolution/phpComposer.js
14953
- import fsp8 from "node:fs/promises";
15184
+ import fsp9 from "node:fs/promises";
14954
15185
  import path15 from "node:path";
14955
15186
  function readComposerNamespaceDirs(value, composerDir) {
14956
15187
  const result = /* @__PURE__ */ new Map();
@@ -15000,7 +15231,7 @@ async function loadPhpComposerConfig(composerPath) {
15000
15231
  return await cached2;
15001
15232
  const pending = (async () => {
15002
15233
  try {
15003
- const raw = await fsp8.readFile(composerPath, "utf8");
15234
+ const raw = await fsp9.readFile(composerPath, "utf8");
15004
15235
  const parsed = JSON.parse(raw);
15005
15236
  const composerDir = path15.dirname(composerPath);
15006
15237
  const autoload = parsed.autoload && typeof parsed.autoload === "object" ? parsed.autoload : {};
@@ -15137,7 +15368,7 @@ async function getPhpComposerAutoloadFiles(composerPath, composerConfig) {
15137
15368
  }
15138
15369
  for (const root of roots) {
15139
15370
  try {
15140
- const stat = await fsp8.stat(root.path);
15371
+ const stat = await fsp9.stat(root.path);
15141
15372
  if (stat.isDirectory()) {
15142
15373
  const files = await listProjectFiles(root.path, ["**/*.php"]);
15143
15374
  for (const filePath of files) {
@@ -15177,7 +15408,7 @@ async function phpFileMatchesNamespacePrefixes(filePath, namespacePrefixes) {
15177
15408
  }
15178
15409
  let source;
15179
15410
  try {
15180
- source = await fsp8.readFile(filePath, "utf8");
15411
+ source = await fsp9.readFile(filePath, "utf8");
15181
15412
  } catch {
15182
15413
  return false;
15183
15414
  }
@@ -15213,7 +15444,7 @@ var init_phpComposer = __esm({
15213
15444
  });
15214
15445
 
15215
15446
  // dist/util/resolution/php.js
15216
- import fsp9 from "node:fs/promises";
15447
+ import fsp10 from "node:fs/promises";
15217
15448
  import path16 from "node:path";
15218
15449
  async function resolvePhpPathLikeSpecifier(projectRoot, fromFile, spec) {
15219
15450
  let base = path16.resolve(path16.dirname(fromFile), spec);
@@ -15266,7 +15497,7 @@ async function readPhpSymbolIndex(filePath) {
15266
15497
  const cached2 = phpSymbolIndexCache.get(filePath);
15267
15498
  if (cached2)
15268
15499
  return cached2;
15269
- const source = await fsp9.readFile(filePath, "utf8");
15500
+ const source = await fsp10.readFile(filePath, "utf8");
15270
15501
  const packageEntries = extractPhpTopLevelPackageEntries(source);
15271
15502
  const primaryEntry = packageEntries[0] ?? {
15272
15503
  packageName: "",
@@ -15715,7 +15946,7 @@ var init_php2 = __esm({
15715
15946
 
15716
15947
  // dist/util/resolution/python.js
15717
15948
  import fs8 from "node:fs";
15718
- import fsp10 from "node:fs/promises";
15949
+ import fsp11 from "node:fs/promises";
15719
15950
  import path17 from "node:path";
15720
15951
  function pythonResolutionCacheKey(projectRoot, fromFile, moduleName, importDotCount) {
15721
15952
  const normalizedRoot = normalizePath(path17.resolve(projectRoot));
@@ -15727,7 +15958,7 @@ async function findPythonPackageAnchor(startDir) {
15727
15958
  let topWithInit = startDir;
15728
15959
  while (true) {
15729
15960
  try {
15730
- await fsp10.access(path17.join(dir, "__init__.py"), fs8.constants.R_OK);
15961
+ await fsp11.access(path17.join(dir, "__init__.py"), fs8.constants.R_OK);
15731
15962
  topWithInit = dir;
15732
15963
  } catch {
15733
15964
  }
@@ -15773,7 +16004,7 @@ async function resolvePythonModule(projectRoot, fromFile, moduleName, importDotC
15773
16004
  resolvePythonModuleCache.set(cacheKey3, res);
15774
16005
  return res;
15775
16006
  }
15776
- await fsp10.access(c, fs8.constants.R_OK);
16007
+ await fsp11.access(c, fs8.constants.R_OK);
15777
16008
  {
15778
16009
  const res = normalizePath(path17.resolve(c));
15779
16010
  resolvePythonModuleCache.set(cacheKey3, res);
@@ -15811,7 +16042,7 @@ async function resolvePythonModule(projectRoot, fromFile, moduleName, importDotC
15811
16042
  resolvePythonModuleCache.set(cacheKey3, res);
15812
16043
  return res;
15813
16044
  }
15814
- await fsp10.access(c, fs8.constants.R_OK);
16045
+ await fsp11.access(c, fs8.constants.R_OK);
15815
16046
  {
15816
16047
  const res = normalizePath(path17.resolve(c));
15817
16048
  resolvePythonModuleCache.set(cacheKey3, res);
@@ -17983,7 +18214,7 @@ var require_lib2 = __commonJS({
17983
18214
 
17984
18215
  // dist/util/resolution/tsconfig.js
17985
18216
  import fs9 from "node:fs";
17986
- import fsp11 from "node:fs/promises";
18217
+ import fsp12 from "node:fs/promises";
17987
18218
  import { createRequire } from "node:module";
17988
18219
  import path19 from "node:path";
17989
18220
  async function findNearestTsconfig(startFromFile, projectRoot) {
@@ -17993,7 +18224,7 @@ async function findNearestTsconfig(startFromFile, projectRoot) {
17993
18224
  while (isFilePathWithinRoot(resolvedProjectRoot, dir)) {
17994
18225
  const candidate = path19.join(dir, "tsconfig.json");
17995
18226
  try {
17996
- await fsp11.access(candidate, fs9.constants.R_OK);
18227
+ await fsp12.access(candidate, fs9.constants.R_OK);
17997
18228
  return candidate;
17998
18229
  } catch {
17999
18230
  }
@@ -18054,7 +18285,7 @@ async function loadTsconfigConfig(cfgPath, projectRoot, seen = /* @__PURE__ */ n
18054
18285
  return { baseUrl: path19.dirname(normalizedCfgPath).replace(/\\/g, "/"), paths: {} };
18055
18286
  }
18056
18287
  seen.add(cfgKey);
18057
- const raw = await fsp11.readFile(cfgPath, "utf8");
18288
+ const raw = await fsp12.readFile(cfgPath, "utf8");
18058
18289
  const json3 = parseJsonc(raw);
18059
18290
  const cfgDir = path19.dirname(cfgPath);
18060
18291
  const compilerOptions = json3.compilerOptions;
@@ -18177,7 +18408,7 @@ __export(resolution_exports, {
18177
18408
  resolveSpecifier: () => resolveSpecifier
18178
18409
  });
18179
18410
  import fs10 from "node:fs";
18180
- import fsp12 from "node:fs/promises";
18411
+ import fsp13 from "node:fs/promises";
18181
18412
  import path20 from "node:path";
18182
18413
  function getResolveSpecifierCacheEntry(key) {
18183
18414
  return lruMapGet(resolveSpecifierCache, key);
@@ -18233,7 +18464,7 @@ async function resolvePathLikeModule(projectRoot, spec, resolutionExtensions) {
18233
18464
  return path20.resolve(path20.join(p, "index" + e));
18234
18465
  }
18235
18466
  if (await fileExists(p)) {
18236
- const st = await fsp12.stat(p);
18467
+ const st = await fsp13.stat(p);
18237
18468
  if (!st.isDirectory())
18238
18469
  return path20.resolve(p);
18239
18470
  }
@@ -18361,8 +18592,7 @@ async function resolveSpecifier(fromFile, spec, projectRoot, matchPath, workspac
18361
18592
  }
18362
18593
  const fromExt = path20.extname(fromFile).toLowerCase();
18363
18594
  const prefersPathLikeFallback = [".go", ".java", ".cs", ".rb", ".rs", ".swift"].includes(fromExt);
18364
- const shouldTryPathLikeFallback = prefersPathLikeFallback || spec.includes("/") || spec.includes(".");
18365
- if (shouldTryPathLikeFallback) {
18595
+ if (prefersPathLikeFallback) {
18366
18596
  const pathLike = await resolvePathLikeModule(projectRoot, spec, opts?.resolutionExtensions);
18367
18597
  if (pathLike && isFilePathWithinRoot(projectRoot, pathLike)) {
18368
18598
  setResolveSpecifierCacheEntry(cacheKey3, pathLike);
@@ -18581,7 +18811,7 @@ var init_confinedFile = __esm({
18581
18811
  });
18582
18812
 
18583
18813
  // dist/util/workerThreads.js
18584
- import os2 from "node:os";
18814
+ import os3 from "node:os";
18585
18815
  function resolveWorkerThreadCount(options) {
18586
18816
  const max = options?.max ?? 64;
18587
18817
  const requested = options?.requested;
@@ -18592,7 +18822,7 @@ function resolveWorkerThreadCount(options) {
18592
18822
  if (typeof defaultCount === "number" && Number.isFinite(defaultCount) && defaultCount > 0) {
18593
18823
  return Math.min(Math.max(Math.floor(defaultCount), 1), max);
18594
18824
  }
18595
- return Math.min(Math.max(os2.availableParallelism() - 1, 1), max);
18825
+ return Math.min(Math.max(os3.availableParallelism() - 1, 1), max);
18596
18826
  }
18597
18827
  var init_workerThreads = __esm({
18598
18828
  "dist/util/workerThreads.js"() {
@@ -18997,17 +19227,17 @@ var init_sfc = __esm({
18997
19227
  });
18998
19228
 
18999
19229
  // dist/languages/filePrep.js
19000
- import fsp13 from "node:fs/promises";
19230
+ import fsp14 from "node:fs/promises";
19001
19231
  async function prepareSourceInput(file2, opts) {
19002
19232
  const framework = detectSFCFramework(file2);
19003
19233
  if (framework) {
19004
- const rawSource2 = opts?.source ?? await fsp13.readFile(file2, "utf8");
19234
+ const rawSource2 = opts?.source ?? await fsp14.readFile(file2, "utf8");
19005
19235
  return prepareSFCSourceInput(rawSource2, framework);
19006
19236
  }
19007
19237
  const sup = supportForFile(file2, opts?.languageExtensions);
19008
19238
  if (!sup)
19009
19239
  throw new UnsupportedParserInputError(file2);
19010
- const rawSource = opts?.source ?? await fsp13.readFile(file2, "utf8");
19240
+ const rawSource = opts?.source ?? await fsp14.readFile(file2, "utf8");
19011
19241
  return {
19012
19242
  source: rawSource,
19013
19243
  sup
@@ -20959,7 +21189,7 @@ var init_angularjs = __esm({
20959
21189
  });
20960
21190
 
20961
21191
  // dist/graphs/angularjs.js
20962
- import fsp14 from "node:fs/promises";
21192
+ import fsp15 from "node:fs/promises";
20963
21193
  async function collectAngularJsFrameworkEdges(projectRoot, files, workspaceConfig, parsed) {
20964
21194
  const jsFiles = files.filter((file2) => file2.toLowerCase().endsWith(".js"));
20965
21195
  if (!jsFiles.length)
@@ -20972,7 +21202,7 @@ async function collectAngularJsFrameworkEdges(projectRoot, files, workspaceConfi
20972
21202
  continue;
20973
21203
  }
20974
21204
  try {
20975
- const source = await fsp14.readFile(file2, "utf8");
21205
+ const source = await fsp15.readFile(file2, "utf8");
20976
21206
  contexts.push({ file: file2, source });
20977
21207
  } catch {
20978
21208
  continue;
@@ -24722,7 +24952,7 @@ var init_extractFacts = __esm({
24722
24952
 
24723
24953
  // dist/sql/sourceGraph.js
24724
24954
  import crypto2 from "node:crypto";
24725
- import fsp15 from "node:fs/promises";
24955
+ import fsp16 from "node:fs/promises";
24726
24956
  function isSqlFile(filePath, languageExtensions) {
24727
24957
  return supportForFile(filePath, languageExtensions)?.id === "sql";
24728
24958
  }
@@ -24851,7 +25081,7 @@ function buildSqlModuleIndex(filePath, source) {
24851
25081
  };
24852
25082
  }
24853
25083
  async function readSqlFacts(filePath) {
24854
- return extractSqlFactsFromSource(filePath, await fsp15.readFile(filePath, "utf8"));
25084
+ return extractSqlFactsFromSource(filePath, await fsp16.readFile(filePath, "utf8"));
24855
25085
  }
24856
25086
  async function buildSqlFactCache(allFiles, languageExtensions) {
24857
25087
  const sqlFiles2 = Array.from(new Set(allFiles.map(normalizePath).filter((file2) => isSqlFile(file2, languageExtensions)))).sort((left, right) => left.localeCompare(right));
@@ -25287,7 +25517,7 @@ var init_adjacency = __esm({
25287
25517
  });
25288
25518
 
25289
25519
  // dist/worker/nativeExtractWorker.js
25290
- import fsp16 from "node:fs/promises";
25520
+ import fsp17 from "node:fs/promises";
25291
25521
  import { createRequire as createRequire3 } from "node:module";
25292
25522
  import path32 from "node:path";
25293
25523
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -25490,9 +25720,9 @@ var init_nativeExtractWorker = __esm({
25490
25720
  localNativePackageRoot2 = path32.resolve(path32.dirname(fileURLToPath3(import.meta.url)), "../../packages/codegraph-native");
25491
25721
  runExtraction = createNativeExtractor({
25492
25722
  loadBinding: loadProductionBinding,
25493
- readFile: fsp16.readFile,
25723
+ readFile: fsp17.readFile,
25494
25724
  statFile: async (filePath) => {
25495
- const stat = await fsp16.stat(filePath);
25725
+ const stat = await fsp17.stat(filePath);
25496
25726
  return { size: stat.size };
25497
25727
  }
25498
25728
  });
@@ -26712,8 +26942,11 @@ async function collectImportsForFile(file2, projectRoot, opts) {
26712
26942
  });
26713
26943
  return imports;
26714
26944
  }
26715
- const tsCfg = resolvedSup.id === "ts" || resolvedSup.id === "tsx" ? await loadNearestTsconfigFor(file2, projectRoot, opts?.logLevel) : void 0;
26716
- const workspaceConfig = await loadWorkspaceConfig(projectRoot);
26945
+ let matchPath = opts?.matchPath;
26946
+ if ((resolvedSup.id === "ts" || resolvedSup.id === "tsx") && !matchPath) {
26947
+ ({ matchPath } = await loadNearestTsconfigFor(file2, projectRoot, opts?.logLevel));
26948
+ }
26949
+ const workspaceConfig = opts?.workspaceConfig ?? await loadWorkspaceConfig(projectRoot);
26717
26950
  const resolvedImportCache = /* @__PURE__ */ new Map();
26718
26951
  const resolveFrom = async (from, phpImportType) => {
26719
26952
  const cacheKey3 = `${from}\0${phpImportType ?? ""}`;
@@ -26723,7 +26956,7 @@ async function collectImportsForFile(file2, projectRoot, opts) {
26723
26956
  const resolutionHints = opts?.graphOptions?.resolutionHints;
26724
26957
  const resolved = (async () => {
26725
26958
  const result = await resolveImportSpecifier(projectRoot, file2, from, resolvedSup.id, {
26726
- ...tsCfg?.matchPath ? { matchPath: tsCfg.matchPath } : {},
26959
+ ...matchPath ? { matchPath } : {},
26727
26960
  ...workspaceConfig ? { workspaceConfig } : {},
26728
26961
  resolveNodeModules: !!opts?.graphOptions?.resolveNodeModules,
26729
26962
  ...resolutionHints ? { resolutionHints } : {},
@@ -27653,9 +27886,11 @@ function collectLocalsAndExportsFromSource(file2, source, support, imports = [],
27653
27886
  }
27654
27887
  }
27655
27888
  }
27656
- const methodSupplementTree = ensureTree();
27657
- if (methodSupplementTree) {
27658
- supplementMethodLocalsFromSyntaxTree(methodSupplementTree.rootNode);
27889
+ if (support.id === "ts" || support.id === "tsx" || support.id === "js") {
27890
+ const methodSupplementTree = ensureTree();
27891
+ if (methodSupplementTree) {
27892
+ supplementMethodLocalsFromSyntaxTree(methodSupplementTree.rootNode);
27893
+ }
27659
27894
  }
27660
27895
  function supplementMethodLocalsFromSyntaxTree(node) {
27661
27896
  if (METHOD_LIKE_BINDING_NODE_TYPES.has(node.type)) {
@@ -28036,7 +28271,8 @@ function collectLocalsAndExportsFromSource(file2, source, support, imports = [],
28036
28271
  throw error51;
28037
28272
  }
28038
28273
  }
28039
- if (support.id === "ts" || support.id === "tsx" || support.id === "js") {
28274
+ const isJsLike = support.id === "ts" || support.id === "tsx" || support.id === "js";
28275
+ if (isJsLike) {
28040
28276
  appendJsLikeRegexFallbackExports(file2, source, locals, exports);
28041
28277
  }
28042
28278
  if (support.id === "python") {
@@ -29796,7 +30032,7 @@ var init_reports = __esm({
29796
30032
  // dist/indexer/build-cache/location.js
29797
30033
  import crypto4 from "node:crypto";
29798
30034
  import fs16 from "node:fs";
29799
- import os3 from "node:os";
30035
+ import os4 from "node:os";
29800
30036
  import path35 from "node:path";
29801
30037
  function isWritableDirectory(candidate) {
29802
30038
  try {
@@ -29807,7 +30043,7 @@ function isWritableDirectory(candidate) {
29807
30043
  }
29808
30044
  function isForbiddenAnchor(candidate) {
29809
30045
  const resolved = path35.resolve(candidate);
29810
- const home = path35.resolve(os3.homedir());
30046
+ const home = path35.resolve(os4.homedir());
29811
30047
  const parsed = path35.parse(resolved);
29812
30048
  return fileIdentityKey(resolved) === fileIdentityKey(home) || fileIdentityKey(resolved) === fileIdentityKey(parsed.root);
29813
30049
  }
@@ -29831,7 +30067,7 @@ function findRepositoryAnchor(projectRoot) {
29831
30067
  return { anchor: start, layer: "project" };
29832
30068
  }
29833
30069
  function resolveCodegraphUserCacheRoot2() {
29834
- const base = process.platform === "win32" ? process.env.LOCALAPPDATA?.trim() || path35.join(os3.homedir(), "AppData", "Local") : process.env.XDG_CACHE_HOME?.trim() || path35.join(os3.homedir(), ".cache");
30070
+ const base = process.platform === "win32" ? process.env.LOCALAPPDATA?.trim() || path35.join(os4.homedir(), "AppData", "Local") : process.env.XDG_CACHE_HOME?.trim() || path35.join(os4.homedir(), ".cache");
29835
30071
  return path35.join(base, "codegraph");
29836
30072
  }
29837
30073
  function projectCacheNamespace(projectRoot, anchor) {
@@ -29865,7 +30101,33 @@ function assertAnchorNotForbidden(resolution) {
29865
30101
  return;
29866
30102
  if (!isForbiddenAnchor(resolution.anchor))
29867
30103
  return;
29868
- throw new Error(`Cache anchor "${resolution.anchor}" (from ${CONFIGURED_ANCHOR_LAYER_LABELS[resolution.layer]}) is the home directory or a filesystem root. codegraph refuses to use it as a cache root. Use a subdirectory instead, e.g. "${path35.join(resolution.anchor, ".codegraph-cache")}".`);
30104
+ throw new Error(`Cache anchor "${resolution.anchor}" (from ${CONFIGURED_ANCHOR_LAYER_LABELS[resolution.layer]}) is the home directory or a filesystem root. codegraph refuses to use it as a cache root. Use a subdirectory instead, e.g. "${path35.join(resolution.anchor, PROJECT_CACHE_DIRECTORY)}".`);
30105
+ }
30106
+ function migrateLegacyProjectCache(candidate, legacyCandidates, logLevel) {
30107
+ if (fs16.existsSync(candidate))
30108
+ return;
30109
+ const legacy = legacyCandidates.find(({ path: legacyPath }) => fs16.existsSync(legacyPath));
30110
+ if (!legacy)
30111
+ return;
30112
+ try {
30113
+ fs16.mkdirSync(path35.dirname(candidate), { recursive: true });
30114
+ fs16.renameSync(legacy.path, candidate);
30115
+ } catch (error51) {
30116
+ if (logLevel === "debug")
30117
+ console.debug(`Unable to migrate legacy cache from ${legacy.path}:`, error51);
30118
+ return;
30119
+ }
30120
+ let current = path35.dirname(legacy.path);
30121
+ for (; ; ) {
30122
+ try {
30123
+ fs16.rmdirSync(current);
30124
+ } catch {
30125
+ break;
30126
+ }
30127
+ if (fileIdentityKey(current) === fileIdentityKey(legacy.cleanupBoundary))
30128
+ break;
30129
+ current = path35.dirname(current);
30130
+ }
29869
30131
  }
29870
30132
  function resolveCacheLocation(projectRoot, opts) {
29871
30133
  const root = path35.resolve(projectRoot);
@@ -29876,34 +30138,57 @@ function resolveCacheLocation(projectRoot, opts) {
29876
30138
  const anchor = anchorWritable ? resolution.anchor : root;
29877
30139
  const effectiveLayer = anchorWritable ? resolution.layer : "project";
29878
30140
  const sameRoot2 = fileIdentityKey(anchor) === fileIdentityKey(root);
29879
- if (sameRoot2 && !opts?.cacheDir && !process.env.CODEGRAPH_CACHE_DIR?.trim() && (!opts?.cacheLocation || opts.cacheLocation === "project")) {
29880
- return { path: path35.join(root, ".codegraph-cache", "index-v1"), anchor, layer: effectiveLayer };
30141
+ const explicitBase = opts?.cacheDir?.trim() || process.env.CODEGRAPH_CACHE_DIR?.trim();
30142
+ if (sameRoot2 && !explicitBase && (!opts?.cacheLocation || opts.cacheLocation === "project")) {
30143
+ const candidate2 = path35.join(root, PROJECT_CACHE_RELATIVE_PATH);
30144
+ migrateLegacyProjectCache(candidate2, [
30145
+ {
30146
+ path: path35.join(root, LEGACY_PROJECT_CACHE_RELATIVE_PATH),
30147
+ cleanupBoundary: path35.join(root, ".codegraph-cache")
30148
+ }
30149
+ ], opts?.logLevel);
30150
+ return { path: candidate2, anchor, layer: effectiveLayer };
29881
30151
  }
29882
30152
  const namespace = projectCacheNamespace(root, effectiveLayer === "git" || effectiveLayer === "manifest" ? anchor : void 0);
29883
- const explicitBase = opts?.cacheDir?.trim() || process.env.CODEGRAPH_CACHE_DIR?.trim();
29884
30153
  if (explicitBase) {
29885
30154
  const configured = path35.resolve(explicitBase);
29886
30155
  const configuredPath = path35.basename(configured) === namespace ? configured : path35.join(configured, namespace);
29887
30156
  return { path: configuredPath, anchor, layer: effectiveLayer };
29888
30157
  }
29889
- const base = opts?.cacheLocation === "user" ? resolveCodegraphUserCacheRoot2() : path35.join(anchor, ".codegraph-cache");
29890
- const candidate = path35.join(path35.resolve(base), "index-v1", namespace);
29891
- if (!sameRoot2 && !opts?.cacheLocation) {
29892
- const legacy = path35.join(root, ".codegraph-cache", "index-v1");
29893
- if (!fs16.existsSync(candidate) && fs16.existsSync(legacy)) {
29894
- return { path: legacy, anchor: root, layer: "project" };
30158
+ if (opts?.cacheLocation === "user") {
30159
+ return {
30160
+ path: path35.join(resolveCodegraphUserCacheRoot2(), "index-v1", namespace),
30161
+ anchor,
30162
+ layer: effectiveLayer
30163
+ };
30164
+ }
30165
+ const candidate = path35.join(anchor, PROJECT_CACHE_RELATIVE_PATH, namespace);
30166
+ const legacyCandidates = [
30167
+ {
30168
+ path: path35.join(anchor, LEGACY_PROJECT_CACHE_RELATIVE_PATH, namespace),
30169
+ cleanupBoundary: path35.join(anchor, ".codegraph-cache")
29895
30170
  }
30171
+ ];
30172
+ if (!sameRoot2 && !opts?.cacheLocation) {
30173
+ legacyCandidates.push({
30174
+ path: path35.join(root, LEGACY_PROJECT_CACHE_RELATIVE_PATH),
30175
+ cleanupBoundary: path35.join(root, ".codegraph-cache")
30176
+ });
29896
30177
  }
30178
+ migrateLegacyProjectCache(candidate, legacyCandidates, opts?.logLevel);
29897
30179
  return { path: candidate, anchor, layer: effectiveLayer };
29898
30180
  }
29899
30181
  function cacheRoot(projectRoot, opts) {
29900
30182
  return resolveCacheLocation(projectRoot, opts).path;
29901
30183
  }
29902
- var CONFIGURED_ANCHOR_LAYER_LABELS;
30184
+ var PROJECT_CACHE_DIRECTORY, PROJECT_CACHE_RELATIVE_PATH, LEGACY_PROJECT_CACHE_RELATIVE_PATH, CONFIGURED_ANCHOR_LAYER_LABELS;
29903
30185
  var init_location = __esm({
29904
30186
  "dist/indexer/build-cache/location.js"() {
29905
30187
  "use strict";
29906
30188
  init_paths();
30189
+ PROJECT_CACHE_DIRECTORY = ".codegraph";
30190
+ PROJECT_CACHE_RELATIVE_PATH = path35.join(PROJECT_CACHE_DIRECTORY, "cache", "index-v1");
30191
+ LEGACY_PROJECT_CACHE_RELATIVE_PATH = path35.join(".codegraph-cache", "index-v1");
29907
30192
  CONFIGURED_ANCHOR_LAYER_LABELS = {
29908
30193
  explicit: "--cache-dir (or an absolute cache.location)",
29909
30194
  environment: "CODEGRAPH_CACHE_DIR",
@@ -29915,19 +30200,9 @@ var init_location = __esm({
29915
30200
  // dist/indexer/build-cache/options.js
29916
30201
  import crypto5 from "node:crypto";
29917
30202
  import path36 from "node:path";
29918
- function functionSource(value) {
29919
- return typeof value === "function" ? Function.prototype.toString.call(value) : void 0;
29920
- }
29921
30203
  function languageDefinitionFingerprintDescriptor(definition) {
29922
30204
  const native = definition.native;
29923
- const nativeNormalizeQuery = functionSource(native?.normalizeQuery);
29924
- const classifyDefinition = functionSource(definition.classifyDefinition);
29925
- const isDeclarationName = functionSource(definition.isDeclarationName);
29926
- const scopeDeclarationNames = definition.scopeDeclarationNames === "all" ? "all" : functionSource(definition.scopeDeclarationNames);
29927
- const normalizeIdentifier = functionSource(definition.normalizeIdentifier);
29928
- const createsBlockScope = functionSource(definition.createsBlockScope);
29929
- const createsFunctionScope = functionSource(definition.createsFunctionScope);
29930
- const isTypeOnly = functionSource(definition.isTypeOnly);
30205
+ const scopeDeclarationNames = definition.scopeDeclarationNames === "all" ? "all" : void 0;
29931
30206
  return {
29932
30207
  id: definition.id,
29933
30208
  extensions: [...definition.extensions].sort(),
@@ -29938,38 +30213,38 @@ function languageDefinitionFingerprintDescriptor(definition) {
29938
30213
  ...native ? {
29939
30214
  native: {
29940
30215
  authoritativeKinds: [...native.authoritativeKinds ?? []].sort(),
29941
- notes: [...native.notes ?? []],
29942
- ...nativeNormalizeQuery ? { normalizeQuery: nativeNormalizeQuery } : {}
30216
+ notes: [...native.notes ?? []]
29943
30217
  }
29944
30218
  } : {},
29945
30219
  behavior: {
29946
- // Booleans serialize under the same defaults adaptDefinition applies, so the
29947
- // fingerprint tracks effective behavior rather than incidental optionality.
30220
+ // Function-valued behavior fields are covered by LANGUAGE_BEHAVIOR_EPOCH.
30221
+ // Bundlers rewrite their source text, so hashing it would make equivalent
30222
+ // CLI and library builds invalidate one another's caches.
29948
30223
  usesQueryDrivenLocals: definition.usesQueryDrivenLocals ?? false,
29949
30224
  membersAreImplicitlyInScope: definition.membersAreImplicitlyInScope ?? true,
29950
- ...classifyDefinition ? { classifyDefinition } : {},
29951
- ...isDeclarationName ? { isDeclarationName } : {},
29952
- ...scopeDeclarationNames ? { scopeDeclarationNames } : {},
29953
- ...normalizeIdentifier ? { normalizeIdentifier } : {},
29954
- ...createsBlockScope ? { createsBlockScope } : {},
29955
- ...createsFunctionScope ? { createsFunctionScope } : {},
29956
- ...isTypeOnly ? { isTypeOnly } : {}
30225
+ supportsExportFromReferences: definition.supportsExportFromReferences ?? false,
30226
+ ...scopeDeclarationNames ? { scopeDeclarationNames } : {}
29957
30227
  }
29958
30228
  };
29959
30229
  }
29960
- function getImplementationFingerprint() {
29961
- if (cachedImplementationFingerprint)
29962
- return cachedImplementationFingerprint;
30230
+ function getImplementationFingerprintForEpoch(languageBehaviorEpoch) {
29963
30231
  const definitions = getAllLanguages().map(languageDefinitionFingerprintDescriptor).sort((left, right) => left.id.localeCompare(right.id));
29964
30232
  const hash2 = crypto5.createHash("sha256");
29965
- hash2.update("codegraph-implementation-fingerprint-v2");
30233
+ hash2.update("codegraph-implementation-fingerprint-v3");
29966
30234
  hash2.update("\0");
29967
30235
  hash2.update(String(CORE_ALGORITHM_EPOCH));
29968
30236
  hash2.update("\0");
30237
+ hash2.update(String(languageBehaviorEpoch));
30238
+ hash2.update("\0");
29969
30239
  hash2.update(getCodegraphVersion());
29970
30240
  hash2.update("\0");
29971
30241
  hash2.update(JSON.stringify(definitions));
29972
- cachedImplementationFingerprint = hash2.digest("hex");
30242
+ return hash2.digest("hex");
30243
+ }
30244
+ function getImplementationFingerprint() {
30245
+ if (cachedImplementationFingerprint)
30246
+ return cachedImplementationFingerprint;
30247
+ cachedImplementationFingerprint = getImplementationFingerprintForEpoch(LANGUAGE_BEHAVIOR_EPOCH);
29973
30248
  return cachedImplementationFingerprint;
29974
30249
  }
29975
30250
  function normalizeManifestBuildOptions(opts) {
@@ -30139,7 +30414,7 @@ function graphOptionsEqual(a, b) {
30139
30414
  }
30140
30415
  return true;
30141
30416
  }
30142
- var CORE_ALGORITHM_EPOCH, cachedImplementationFingerprint;
30417
+ var CORE_ALGORITHM_EPOCH, LANGUAGE_BEHAVIOR_EPOCH, cachedImplementationFingerprint;
30143
30418
  var init_options2 = __esm({
30144
30419
  "dist/indexer/build-cache/options.js"() {
30145
30420
  "use strict";
@@ -30151,6 +30426,7 @@ var init_options2 = __esm({
30151
30426
  init_treeSitterNative();
30152
30427
  init_languages();
30153
30428
  CORE_ALGORITHM_EPOCH = 2;
30429
+ LANGUAGE_BEHAVIOR_EPOCH = 1;
30154
30430
  }
30155
30431
  });
30156
30432
 
@@ -30158,7 +30434,7 @@ var init_options2 = __esm({
30158
30434
  import { brotliCompressSync, brotliDecompressSync, constants as zlibConstants } from "node:zlib";
30159
30435
  import crypto6 from "node:crypto";
30160
30436
  import fs17 from "node:fs";
30161
- import fsp17 from "node:fs/promises";
30437
+ import fsp18 from "node:fs/promises";
30162
30438
  import path37 from "node:path";
30163
30439
  function memoryCacheKey(projectRoot, file2) {
30164
30440
  return `${normalizePath(projectRoot)}::${file2}`;
@@ -30310,14 +30586,14 @@ function pruneDiskModuleCache(projectRoot, liveFiles, opts) {
30310
30586
  }
30311
30587
  }
30312
30588
  async function fileContentHash(file2) {
30313
- const buffer = await fsp17.readFile(file2);
30589
+ const buffer = await fsp18.readFile(file2);
30314
30590
  const hash2 = crypto6.createHash("sha1");
30315
30591
  hash2.update(buffer);
30316
30592
  return hash2.digest("hex");
30317
30593
  }
30318
30594
  async function fileStatSignature(file2, strict, opts) {
30319
30595
  try {
30320
- const stat = await fsp17.stat(file2);
30596
+ const stat = await fsp18.stat(file2);
30321
30597
  const useStrict = strict ?? true;
30322
30598
  const shouldHash = useStrict || !!opts?.includeContentHash;
30323
30599
  const contentHash = shouldHash ? await fileContentHash(file2) : void 0;
@@ -30375,7 +30651,7 @@ async function cacheSignatureForFile(file2, sigInfo, opts) {
30375
30651
  }
30376
30652
  async function buildBloomFilterForFile(file2, opts) {
30377
30653
  try {
30378
- const source = await fsp17.readFile(file2, "utf8");
30654
+ const source = await fsp18.readFile(file2, "utf8");
30379
30655
  const support = supportForFile(file2, opts?.languageExtensions);
30380
30656
  if (!support)
30381
30657
  return null;
@@ -31085,7 +31361,7 @@ __export(units_exports, {
31085
31361
  shouldKeepUnit: () => shouldKeepUnit
31086
31362
  });
31087
31363
  import crypto7 from "node:crypto";
31088
- import fsp18 from "node:fs/promises";
31364
+ import fsp19 from "node:fs/promises";
31089
31365
  import path39 from "node:path";
31090
31366
  function lineSpan(unit) {
31091
31367
  return Math.max(1, unit.endLine - unit.startLine + 1);
@@ -31474,7 +31750,7 @@ async function buildDuplicateUnitsForFile(index, file2, projectRoot, minTokens,
31474
31750
  let source = index.parsed?.get(fileIdentityKey(file2))?.source;
31475
31751
  if (source === void 0) {
31476
31752
  try {
31477
- source = await fsp18.readFile(file2, "utf8");
31753
+ source = await fsp19.readFile(file2, "utf8");
31478
31754
  } catch {
31479
31755
  return [];
31480
31756
  }
@@ -48363,8 +48639,8 @@ __export(config_exports, {
48363
48639
  mergeDiscoveryOptions: () => mergeDiscoveryOptions,
48364
48640
  mergeGraphOptions: () => mergeGraphOptions
48365
48641
  });
48366
- import fsp19 from "node:fs/promises";
48367
- import os4 from "node:os";
48642
+ import fsp20 from "node:fs/promises";
48643
+ import os5 from "node:os";
48368
48644
  import path40 from "node:path";
48369
48645
  function uniq(values) {
48370
48646
  return Array.from(new Set(values.map((value) => value.trim().replace(/\\/g, "/")).filter(Boolean)));
@@ -48434,10 +48710,10 @@ function normalizeConfigLanguageExtensions(extensions) {
48434
48710
  return normalizeLanguageExtensions(extensions);
48435
48711
  }
48436
48712
  async function loadUserCacheLocation() {
48437
- const configRoot = process.platform === "win32" ? process.env.APPDATA?.trim() || path40.join(os4.homedir(), "AppData", "Roaming") : process.env.XDG_CONFIG_HOME?.trim() || path40.join(os4.homedir(), ".config");
48713
+ const configRoot = process.platform === "win32" ? process.env.APPDATA?.trim() || path40.join(os5.homedir(), "AppData", "Roaming") : process.env.XDG_CONFIG_HOME?.trim() || path40.join(os5.homedir(), ".config");
48438
48714
  const configPath = path40.join(configRoot, "codegraph", "config.json");
48439
48715
  try {
48440
- const parsedJson = JSON.parse(await fsp19.readFile(configPath, "utf8"));
48716
+ const parsedJson = JSON.parse(await fsp20.readFile(configPath, "utf8"));
48441
48717
  const parsed = codegraphConfigSchema.safeParse(parsedJson);
48442
48718
  if (!parsed.success)
48443
48719
  throw new Error(external_exports.prettifyError(parsed.error));
@@ -48453,7 +48729,7 @@ async function loadCodegraphConfig(projectRoot) {
48453
48729
  const configPath = path40.join(projectRoot, CODEGRAPH_CONFIG_FILE);
48454
48730
  let raw;
48455
48731
  try {
48456
- raw = await fsp19.readFile(configPath, "utf8");
48732
+ raw = await fsp20.readFile(configPath, "utf8");
48457
48733
  } catch (error51) {
48458
48734
  if (error51 instanceof Error && "code" in error51 && error51.code === "ENOENT") {
48459
48735
  return userCacheLocation ? { cache: { location: userCacheLocation } } : {};
@@ -48519,7 +48795,7 @@ var init_config = __esm({
48519
48795
  // dist/indexer/build-cache/manifest.js
48520
48796
  import crypto8 from "node:crypto";
48521
48797
  import fs19 from "node:fs";
48522
- import fsp20, {} from "node:fs/promises";
48798
+ import fsp21, {} from "node:fs/promises";
48523
48799
  import path41 from "node:path";
48524
48800
  async function collectWorkspaceManifestDependencyEdges(projectRoot, manifestFiles, discovery, logLevel) {
48525
48801
  const manifestPaths = manifestFiles ?? await listProjectFiles(projectRoot, ["**/package.json"], {
@@ -48532,7 +48808,7 @@ async function collectWorkspaceManifestDependencyEdges(projectRoot, manifestFile
48532
48808
  const parsedByPath = /* @__PURE__ */ new Map();
48533
48809
  for (const manifestPath of manifestPaths) {
48534
48810
  try {
48535
- const raw = await fsp20.readFile(manifestPath, "utf8");
48811
+ const raw = await fsp21.readFile(manifestPath, "utf8");
48536
48812
  const parsed = JSON.parse(raw);
48537
48813
  parsedByPath.set(manifestPath, parsed);
48538
48814
  if (typeof parsed.name === "string" && parsed.name.trim()) {
@@ -48605,7 +48881,7 @@ function normalizeIndexedFileInputs(projectRoot, files, label, realProjectRoot)
48605
48881
  return [...filesByIdentity.values()];
48606
48882
  }
48607
48883
  async function normalizeIndexedFileInputsWithinRoot(projectRoot, files, label) {
48608
- const realRoot = await fsp20.realpath(projectRoot);
48884
+ const realRoot = await fsp21.realpath(projectRoot);
48609
48885
  const normalized = normalizeIndexedFileInputs(projectRoot, files, label, realRoot);
48610
48886
  await Promise.all(normalized.map(async (file2) => {
48611
48887
  try {
@@ -48673,7 +48949,7 @@ async function computeConfigHash(projectRoot, logLevel) {
48673
48949
  let firstError;
48674
48950
  for (const file2 of configFiles) {
48675
48951
  try {
48676
- const content = await fsp20.readFile(file2, "utf8");
48952
+ const content = await fsp21.readFile(file2, "utf8");
48677
48953
  const relative = path41.relative(projectRoot, file2).replace(/\\/g, "/");
48678
48954
  hash2.update(relative);
48679
48955
  hash2.update(content);
@@ -48719,7 +48995,7 @@ function isManifestTempName(name, manifestName) {
48719
48995
  async function cleanupStaleManifestTemps(manifestPath) {
48720
48996
  let entries;
48721
48997
  try {
48722
- entries = await fsp20.readdir(path41.dirname(manifestPath), { withFileTypes: true });
48998
+ entries = await fsp21.readdir(path41.dirname(manifestPath), { withFileTypes: true });
48723
48999
  } catch {
48724
49000
  return;
48725
49001
  }
@@ -48730,15 +49006,15 @@ async function cleanupStaleManifestTemps(manifestPath) {
48730
49006
  continue;
48731
49007
  const candidate = path41.join(path41.dirname(manifestPath), entry.name);
48732
49008
  try {
48733
- const stat = await fsp20.lstat(candidate);
49009
+ const stat = await fsp21.lstat(candidate);
48734
49010
  if (stat.mtimeMs <= cutoff)
48735
- await fsp20.rm(candidate, { force: true });
49011
+ await fsp21.rm(candidate, { force: true });
48736
49012
  } catch {
48737
49013
  }
48738
49014
  }
48739
49015
  }
48740
49016
  async function fsyncManifestFile(filePath) {
48741
- const handle = await fsp20.open(filePath, "r+");
49017
+ const handle = await fsp21.open(filePath, "r+");
48742
49018
  try {
48743
49019
  await handle.sync();
48744
49020
  } finally {
@@ -48748,7 +49024,7 @@ async function fsyncManifestFile(filePath) {
48748
49024
  async function syncManifestDirectory(directoryPath) {
48749
49025
  let directory;
48750
49026
  try {
48751
- directory = await fsp20.open(directoryPath, "r");
49027
+ directory = await fsp21.open(directoryPath, "r");
48752
49028
  await directory.sync();
48753
49029
  } catch {
48754
49030
  } finally {
@@ -48771,14 +49047,14 @@ async function writeManifestAtomically(manifestPath, payload) {
48771
49047
  for (let attempt = 0; attempt <= retryDelays.length; attempt += 1) {
48772
49048
  const tempPath = manifestTempFilePath(manifestPath);
48773
49049
  try {
48774
- await fsp20.writeFile(tempPath, payload, "utf8");
49050
+ await fsp21.writeFile(tempPath, payload, "utf8");
48775
49051
  await fsyncManifestFile(tempPath);
48776
- await fsp20.rename(tempPath, manifestPath);
49052
+ await fsp21.rename(tempPath, manifestPath);
48777
49053
  await syncManifestDirectory(path41.dirname(manifestPath));
48778
49054
  return;
48779
49055
  } catch (error51) {
48780
49056
  try {
48781
- await fsp20.rm(tempPath, { force: true });
49057
+ await fsp21.rm(tempPath, { force: true });
48782
49058
  } catch {
48783
49059
  }
48784
49060
  const canRetry = attempt < retryDelays.length && isTransientFileContentionError(error51);
@@ -48801,7 +49077,7 @@ function recordManifestCorruption(report, artifact, reason, logLevel) {
48801
49077
  async function loadManifest(projectRoot, opts, report) {
48802
49078
  const manifestPath = manifestFilePath(projectRoot, opts);
48803
49079
  try {
48804
- const raw = await fsp20.readFile(manifestPath, "utf8");
49080
+ const raw = await fsp21.readFile(manifestPath, "utf8");
48805
49081
  const parsed = JSON.parse(raw);
48806
49082
  let invalidReason;
48807
49083
  if (parsed.version !== MANIFEST_VERSION && parsed.version !== 4 && parsed.version !== 3) {
@@ -48837,7 +49113,7 @@ async function loadManifest(projectRoot, opts, report) {
48837
49113
  async function writeManifest2(projectRoot, opts, manifest) {
48838
49114
  try {
48839
49115
  const manifestPath = manifestFilePath(projectRoot, opts);
48840
- await fsp20.mkdir(path41.dirname(manifestPath), { recursive: true });
49116
+ await fsp21.mkdir(path41.dirname(manifestPath), { recursive: true });
48841
49117
  await writeManifestAtomically(manifestPath, JSON.stringify(manifest, null, 2));
48842
49118
  return true;
48843
49119
  } catch (error51) {
@@ -49612,7 +49888,7 @@ var init_symbol_graph = __esm({
49612
49888
  });
49613
49889
 
49614
49890
  // dist/indexer/build-cache/project-snapshot.js
49615
- import fsp21 from "node:fs/promises";
49891
+ import fsp22 from "node:fs/promises";
49616
49892
  import { createHash as createHash3, randomUUID } from "node:crypto";
49617
49893
  import { brotliCompressSync as brotliCompressSync3, brotliDecompressSync as brotliDecompressSync3, constants as zlibConstants3 } from "node:zlib";
49618
49894
  import path43 from "node:path";
@@ -49660,53 +49936,52 @@ function transformHandle(root, value, toRelative) {
49660
49936
  return `${transformPath(root, file2, toRelative)}${value.slice(separator)}`;
49661
49937
  }
49662
49938
  function transformModule(root, module2, toRelative) {
49663
- const copy = structuredClone(module2);
49664
49939
  const file2 = (value) => transformPath(root, value, toRelative);
49665
- copy.file = file2(copy.file);
49666
- for (const local of copy.locals)
49667
- local.file = file2(local.file);
49668
- for (const entry of copy.exports) {
49669
- if (entry.type === "local") {
49670
- entry.target.file = file2(entry.target.file);
49671
- } else {
49672
- transformPersistedExportFromModule(root, entry, toRelative);
49673
- }
49674
- }
49675
- for (const binding of copy.imports) {
49676
- if (typeof binding.resolved === "string")
49677
- binding.resolved = file2(binding.resolved);
49678
- }
49679
- return copy;
49940
+ return {
49941
+ ...module2,
49942
+ file: file2(module2.file),
49943
+ locals: module2.locals.map((local) => ({ ...local, file: file2(local.file) })),
49944
+ exports: module2.exports.map((entry) => {
49945
+ if (entry.type === "local") {
49946
+ return { ...entry, target: { ...entry.target, file: file2(entry.target.file) } };
49947
+ }
49948
+ const copy = { ...entry };
49949
+ transformPersistedExportFromModule(root, copy, toRelative);
49950
+ return copy;
49951
+ }),
49952
+ imports: module2.imports.map((binding) => ({
49953
+ ...binding,
49954
+ ...typeof binding.resolved === "string" ? { resolved: file2(binding.resolved) } : {}
49955
+ }))
49956
+ };
49680
49957
  }
49681
49958
  function transformSnapshotPaths(payload, root, toRelative) {
49682
- const copy = structuredClone(payload);
49683
- copy.graph.nodes = copy.graph.nodes.map((node) => transformPath(root, node, toRelative));
49684
- copy.graph.edges = copy.graph.edges.map((edge) => ({
49685
- ...edge,
49686
- from: transformPath(root, edge.from, toRelative),
49687
- to: edge.to.type === "file" ? { ...edge.to, path: transformPath(root, edge.to.path, toRelative) } : edge.to
49688
- }));
49689
- copy.modules = copy.modules.map((module2) => transformModule(root, module2, toRelative));
49690
- if (copy.projectFiles) {
49691
- copy.projectFiles = copy.projectFiles.map((file2) => ({
49692
- ...file2,
49693
- path: transformPath(root, file2.path, toRelative),
49694
- projectRoot: transformPath(root, file2.projectRoot, toRelative)
49695
- }));
49696
- }
49697
- if (copy.bloomFilters) {
49698
- const bloomFilters = {};
49699
- for (const [file2, filter] of Object.entries(copy.bloomFilters)) {
49700
- bloomFilters[transformPath(root, file2, toRelative)] = filter;
49701
- }
49702
- copy.bloomFilters = bloomFilters;
49703
- }
49704
- const fileSignatures = {};
49705
- for (const [file2, signature] of Object.entries(copy.fileSignatures ?? {})) {
49706
- fileSignatures[transformPath(root, file2, toRelative)] = signature;
49707
- }
49708
- copy.fileSignatures = fileSignatures;
49709
- return copy;
49959
+ const bloomFilters = payload.bloomFilters ? Object.fromEntries(Object.entries(payload.bloomFilters).map(([file2, filter]) => [transformPath(root, file2, toRelative), filter])) : void 0;
49960
+ const fileSignatures = Object.fromEntries(Object.entries(payload.fileSignatures ?? {}).map(([file2, signature]) => [
49961
+ transformPath(root, file2, toRelative),
49962
+ signature
49963
+ ]));
49964
+ return {
49965
+ ...payload,
49966
+ graph: {
49967
+ nodes: payload.graph.nodes.map((node) => transformPath(root, node, toRelative)),
49968
+ edges: payload.graph.edges.map((edge) => ({
49969
+ ...edge,
49970
+ from: transformPath(root, edge.from, toRelative),
49971
+ to: edge.to.type === "file" ? { ...edge.to, path: transformPath(root, edge.to.path, toRelative) } : edge.to
49972
+ }))
49973
+ },
49974
+ modules: payload.modules.map((module2) => transformModule(root, module2, toRelative)),
49975
+ ...payload.projectFiles ? {
49976
+ projectFiles: payload.projectFiles.map((file2) => ({
49977
+ ...file2,
49978
+ path: transformPath(root, file2.path, toRelative),
49979
+ projectRoot: transformPath(root, file2.projectRoot, toRelative)
49980
+ }))
49981
+ } : {},
49982
+ ...bloomFilters ? { bloomFilters } : {},
49983
+ fileSignatures
49984
+ };
49710
49985
  }
49711
49986
  function migrateProjectSnapshotPayload(value, currentRoot) {
49712
49987
  if (!value || typeof value !== "object" || Array.isArray(value))
@@ -49764,7 +50039,7 @@ function sameSnapshotFileIdentity(left, right) {
49764
50039
  return left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
49765
50040
  }
49766
50041
  async function snapshotFileIdentity(snapshotPath) {
49767
- const stat = await fsp21.stat(snapshotPath);
50042
+ const stat = await fsp22.stat(snapshotPath);
49768
50043
  return {
49769
50044
  size: stat.size,
49770
50045
  mtimeMs: stat.mtimeMs,
@@ -49772,7 +50047,7 @@ async function snapshotFileIdentity(snapshotPath) {
49772
50047
  };
49773
50048
  }
49774
50049
  async function fsyncSnapshotFile(snapshotPath) {
49775
- const handle = await fsp21.open(snapshotPath, "r+");
50050
+ const handle = await fsp22.open(snapshotPath, "r+");
49776
50051
  try {
49777
50052
  await handle.sync();
49778
50053
  } finally {
@@ -49789,7 +50064,7 @@ function isSnapshotTempName(name, snapshotName) {
49789
50064
  async function cleanupStaleSnapshotTemps(snapshotPath) {
49790
50065
  let entries;
49791
50066
  try {
49792
- entries = await fsp21.readdir(path43.dirname(snapshotPath), { withFileTypes: true });
50067
+ entries = await fsp22.readdir(path43.dirname(snapshotPath), { withFileTypes: true });
49793
50068
  } catch {
49794
50069
  return;
49795
50070
  }
@@ -49800,26 +50075,26 @@ async function cleanupStaleSnapshotTemps(snapshotPath) {
49800
50075
  continue;
49801
50076
  const candidate = path43.join(path43.dirname(snapshotPath), entry.name);
49802
50077
  try {
49803
- const stat = await fsp21.lstat(candidate);
50078
+ const stat = await fsp22.lstat(candidate);
49804
50079
  if (stat.mtimeMs > cutoff)
49805
50080
  continue;
49806
- await fsp21.rm(candidate, { force: true });
50081
+ await fsp22.rm(candidate, { force: true });
49807
50082
  } catch {
49808
50083
  }
49809
50084
  }
49810
50085
  }
49811
50086
  async function writeSnapshotAtomically(snapshotPath, data) {
49812
- await fsp21.mkdir(path43.dirname(snapshotPath), { recursive: true });
50087
+ await fsp22.mkdir(path43.dirname(snapshotPath), { recursive: true });
49813
50088
  await cleanupStaleSnapshotTemps(snapshotPath);
49814
50089
  let tempPath = path43.join(path43.dirname(snapshotPath), `.${path43.basename(snapshotPath)}.${process.pid}.${randomUUID()}${SNAPSHOT_TEMP_SUFFIX}`);
49815
50090
  try {
49816
- await fsp21.writeFile(tempPath, data, { flag: "wx" });
50091
+ await fsp22.writeFile(tempPath, data, { flag: "wx" });
49817
50092
  await fsyncSnapshotFile(tempPath);
49818
- await fsp21.rename(tempPath, snapshotPath);
50093
+ await fsp22.rename(tempPath, snapshotPath);
49819
50094
  tempPath = void 0;
49820
50095
  } finally {
49821
50096
  if (tempPath) {
49822
- await fsp21.rm(tempPath, { force: true }).catch(() => void 0);
50097
+ await fsp22.rm(tempPath, { force: true }).catch(() => void 0);
49823
50098
  }
49824
50099
  }
49825
50100
  }
@@ -49833,6 +50108,14 @@ function setBoundedSnapshotCache(cache, key, value) {
49833
50108
  cache.delete(oldestKey);
49834
50109
  }
49835
50110
  }
50111
+ function freezeSnapshotPayload(value) {
50112
+ if (!value || typeof value !== "object")
50113
+ return value;
50114
+ Object.freeze(value);
50115
+ for (const child of Object.values(value))
50116
+ freezeSnapshotPayload(child);
50117
+ return value;
50118
+ }
49836
50119
  function materializeDetailedSymbolGraph(payload) {
49837
50120
  const graph = structuredClone(payload);
49838
50121
  return {
@@ -49853,6 +50136,9 @@ function recordSnapshotCorruption(report, artifact, reason, logLevel) {
49853
50136
  }
49854
50137
  logWithLevel(logLevel, "warn", `Warning: Corrupt cache artifact ${artifact}: ${reason}`);
49855
50138
  }
50139
+ function recordSnapshotInvalidation(artifact, reason, logLevel) {
50140
+ logWithLevel(logLevel, "debug", `Cache artifact invalidated ${artifact}: ${reason}`);
50141
+ }
49856
50142
  function isMissingArtifactError(error51) {
49857
50143
  return !!error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT";
49858
50144
  }
@@ -49863,7 +50149,7 @@ async function readParsedSnapshot(snapshotPath) {
49863
50149
  setBoundedSnapshotCache(parsedSnapshotCache, snapshotPath, cached2);
49864
50150
  return { identity: cached2.identity, payload: cached2.payload };
49865
50151
  }
49866
- const compressed = Buffer.from(await fsp21.readFile(snapshotPath));
50152
+ const compressed = Buffer.from(await fsp22.readFile(snapshotPath));
49867
50153
  const payload = decodeSnapshotPayload(compressed);
49868
50154
  const after = await snapshotFileIdentity(snapshotPath);
49869
50155
  const entry = { identity: before, compressed, payload };
@@ -49878,18 +50164,23 @@ function transformCachedProjectSnapshot(snapshotPath, identity, rawPayload, proj
49878
50164
  const cached2 = transformedSnapshotCache.get(snapshotPath);
49879
50165
  if (cached2 && cached2.root === projectRoot && sameSnapshotFileIdentity(cached2.identity, identity)) {
49880
50166
  setBoundedSnapshotCache(transformedSnapshotCache, snapshotPath, cached2);
49881
- return structuredClone(cached2.payload);
50167
+ return cached2.payload;
49882
50168
  }
49883
50169
  const migratedPayload = migrateProjectSnapshotPayload(rawPayload, projectRoot);
49884
50170
  const payload = migratedPayload && typeof migratedPayload === "object" && !Array.isArray(migratedPayload) ? transformSnapshotPaths(migratedPayload, projectRoot, false) : migratedPayload;
49885
- setBoundedSnapshotCache(transformedSnapshotCache, snapshotPath, { identity, root: projectRoot, payload });
49886
- return structuredClone(payload);
50171
+ const immutablePayload = freezeSnapshotPayload(payload);
50172
+ setBoundedSnapshotCache(transformedSnapshotCache, snapshotPath, {
50173
+ identity,
50174
+ root: projectRoot,
50175
+ payload: immutablePayload
50176
+ });
50177
+ return immutablePayload;
49887
50178
  }
49888
50179
  function transformCachedProjectSnapshotModules(snapshotPath, identity, rawPayload, projectRoot) {
49889
50180
  const cached2 = transformedSnapshotModulesCache.get(snapshotPath);
49890
50181
  if (cached2 && cached2.root === projectRoot && sameSnapshotFileIdentity(cached2.identity, identity)) {
49891
50182
  setBoundedSnapshotCache(transformedSnapshotModulesCache, snapshotPath, cached2);
49892
- return structuredClone(cached2.payload);
50183
+ return cached2.payload;
49893
50184
  }
49894
50185
  const migratedPayload = migrateProjectSnapshotPayload(rawPayload, projectRoot);
49895
50186
  if (!migratedPayload || typeof migratedPayload !== "object" || Array.isArray(migratedPayload)) {
@@ -49899,7 +50190,7 @@ function transformCachedProjectSnapshotModules(snapshotPath, identity, rawPayloa
49899
50190
  if (!Array.isArray(payload.modules) || !payload.fileSignatures || typeof payload.fileSignatures !== "object") {
49900
50191
  return migratedPayload;
49901
50192
  }
49902
- const modules = structuredClone(payload.modules).map((module2) => transformModule(projectRoot, module2, false));
50193
+ const modules = payload.modules.map((module2) => transformModule(projectRoot, module2, false));
49903
50194
  const fileSignatures = {};
49904
50195
  for (const [file2, signature] of Object.entries(payload.fileSignatures)) {
49905
50196
  fileSignatures[transformPath(projectRoot, file2, false)] = signature;
@@ -49914,12 +50205,13 @@ function transformCachedProjectSnapshotModules(snapshotPath, identity, rawPayloa
49914
50205
  modules,
49915
50206
  fileSignatures
49916
50207
  };
50208
+ const immutablePayload = freezeSnapshotPayload(transformed);
49917
50209
  setBoundedSnapshotCache(transformedSnapshotModulesCache, snapshotPath, {
49918
50210
  identity,
49919
50211
  root: projectRoot,
49920
- payload: transformed
50212
+ payload: immutablePayload
49921
50213
  });
49922
- return structuredClone(transformed);
50214
+ return immutablePayload;
49923
50215
  }
49924
50216
  function projectIndexManifestEntries(entries) {
49925
50217
  return new Map(Array.from(entries, ([file2, entry]) => [
@@ -49939,7 +50231,12 @@ async function tryLoadProjectIndexSnapshot(projectRoot, opts, manifestEntries, r
49939
50231
  const payload = transformCachedProjectSnapshot(projectSnapshotPath(projectRoot, opts), parsedSnapshot.identity, parsedSnapshot.payload, projectRoot);
49940
50232
  const nativeRuntimeFingerprint = getNativeRuntimeFingerprint(opts?.native);
49941
50233
  const implementationFingerprint = getImplementationFingerprint();
49942
- if (!isProjectIndexSnapshotPayload(payload) || payload.filesSignature !== filesSignature || payload.nativeMode !== normalizedSnapshotNativeMode(opts?.native) || payload.nativeRuntimeFingerprint !== nativeRuntimeFingerprint || payload.implementationFingerprint !== implementationFingerprint) {
50234
+ if (!isProjectIndexSnapshotPayload(payload)) {
50235
+ recordSnapshotCorruption(report, projectSnapshotPath(projectRoot, opts), "invalid project snapshot payload", opts?.logLevel);
50236
+ return null;
50237
+ }
50238
+ if (payload.filesSignature !== filesSignature || payload.nativeMode !== normalizedSnapshotNativeMode(opts?.native) || payload.nativeRuntimeFingerprint !== nativeRuntimeFingerprint || payload.implementationFingerprint !== implementationFingerprint) {
50239
+ recordSnapshotInvalidation(projectSnapshotPath(projectRoot, opts), "project snapshot identity mismatch", opts?.logLevel);
49943
50240
  return null;
49944
50241
  }
49945
50242
  const graph = {
@@ -49987,7 +50284,12 @@ async function tryLoadProjectSnapshotModules(projectRoot, opts, fileSignatures,
49987
50284
  const payload = transformCachedProjectSnapshotModules(projectSnapshotPath(projectRoot, opts), parsedSnapshot.identity, parsedSnapshot.payload, projectRoot);
49988
50285
  const nativeRuntimeFingerprint = getNativeRuntimeFingerprint(opts?.native);
49989
50286
  const implementationFingerprint = getImplementationFingerprint();
49990
- if (!isProjectSnapshotModulesPayload(payload) || payload.nativeMode !== normalizedSnapshotNativeMode(opts?.native) || payload.nativeRuntimeFingerprint !== nativeRuntimeFingerprint || payload.implementationFingerprint !== implementationFingerprint) {
50287
+ if (!isProjectSnapshotModulesPayload(payload)) {
50288
+ recordSnapshotCorruption(report, projectSnapshotPath(projectRoot, opts), "invalid project snapshot payload", opts?.logLevel);
50289
+ return null;
50290
+ }
50291
+ if (payload.nativeMode !== normalizedSnapshotNativeMode(opts?.native) || payload.nativeRuntimeFingerprint !== nativeRuntimeFingerprint || payload.implementationFingerprint !== implementationFingerprint) {
50292
+ recordSnapshotInvalidation(projectSnapshotPath(projectRoot, opts), "project snapshot identity mismatch", opts?.logLevel);
49991
50293
  return null;
49992
50294
  }
49993
50295
  const normalizedFileSignatures = new Map(Object.entries(payload.fileSignatures).map(([file2, signature]) => [fileIdentityKey(file2), signature]));
@@ -50019,7 +50321,13 @@ async function tryLoadPersistedBloomFilters(projectRoot, opts, report) {
50019
50321
  if (sidecarBloom) {
50020
50322
  return createPersistedBloomFilters(sidecarBloom.bloomFilters, sidecarBloom.fileSignatures, projectRoot);
50021
50323
  }
50022
- recordSnapshotCorruption(report, sidecarPath, "invalid bloom filter payload", opts?.logLevel);
50324
+ const sidecar = sidecarParsed;
50325
+ const identityMismatch = isUnknownRecord(sidecarParsed) && (sidecar.version !== BLOOM_FILTER_SNAPSHOT_VERSION || sidecar.implementationFingerprint !== getImplementationFingerprint() || typeof sidecar.projectSnapshotIdentity !== "string");
50326
+ if (identityMismatch) {
50327
+ recordSnapshotInvalidation(sidecarPath, "bloom filter identity mismatch", opts?.logLevel);
50328
+ } else {
50329
+ recordSnapshotCorruption(report, sidecarPath, "invalid bloom filter payload", opts?.logLevel);
50330
+ }
50023
50331
  } catch (error51) {
50024
50332
  const isMissing = error51 && typeof error51 === "object" && "code" in error51 && error51.code === "ENOENT";
50025
50333
  if (!isMissing) {
@@ -50229,10 +50537,18 @@ async function tryLoadDetailedSymbolGraphSnapshot(projectRoot, opts, index, repo
50229
50537
  ...migratedPayload,
50230
50538
  graph: transformDetailedGraph(migratedPayload.graph, projectRoot, false)
50231
50539
  } : migratedPayload;
50232
- if (!isDetailedSymbolGraphSnapshotPayload(payload) || payload.implementationFingerprint !== getImplementationFingerprint() || payload.projectSnapshotIdentity !== index.projectSnapshotIdentity) {
50540
+ if (!isDetailedSymbolGraphSnapshotPayload(payload)) {
50233
50541
  recordSnapshotCorruption(report, snapshotPath, "invalid detailed symbol graph payload", opts?.logLevel);
50234
50542
  return null;
50235
50543
  }
50544
+ if (payload.implementationFingerprint !== getImplementationFingerprint()) {
50545
+ recordSnapshotInvalidation(snapshotPath, "implementation fingerprint mismatch", opts?.logLevel);
50546
+ return null;
50547
+ }
50548
+ if (payload.projectSnapshotIdentity !== index.projectSnapshotIdentity) {
50549
+ recordSnapshotInvalidation(snapshotPath, "project snapshot identity mismatch", opts?.logLevel);
50550
+ return null;
50551
+ }
50236
50552
  const graph = materializeDetailedSymbolGraph(payload.graph);
50237
50553
  if (!await isDetailedSymbolGraphCompatibleWithProject(projectRoot, index, graph)) {
50238
50554
  recordSnapshotCorruption(report, snapshotPath, "detailed symbol graph does not match project", opts?.logLevel);
@@ -50695,21 +51011,21 @@ var init_build_cache = __esm({
50695
51011
 
50696
51012
  // dist/indexer/build-cache/resolver-environment.js
50697
51013
  import crypto9 from "node:crypto";
50698
- import fsp22 from "node:fs/promises";
51014
+ import fsp23 from "node:fs/promises";
50699
51015
  import path44 from "node:path";
50700
51016
  function normalizedRelativePath(projectRoot, target) {
50701
51017
  return path44.relative(projectRoot, target).replace(/\\/g, "/");
50702
51018
  }
50703
51019
  async function statInput(projectRoot, target) {
50704
51020
  try {
50705
- const stat = await fsp22.stat(target);
51021
+ const stat = await fsp23.stat(target);
50706
51022
  if (!stat.isFile())
50707
51023
  return null;
50708
51024
  const sampleBytes = Math.min(stat.size, RESOLUTION_INPUT_SAMPLE_BYTES);
50709
51025
  const hash2 = crypto9.createHash("sha256");
50710
51026
  if (sampleBytes) {
50711
51027
  const first = Buffer.allocUnsafe(sampleBytes);
50712
- const handle = await fsp22.open(target, "r");
51028
+ const handle = await fsp23.open(target, "r");
50713
51029
  try {
50714
51030
  const firstRead = await handle.read(first, 0, sampleBytes, 0);
50715
51031
  hash2.update(first.subarray(0, firstRead.bytesRead));
@@ -50757,7 +51073,7 @@ async function activeNodeModulesRoots(projectRoot, files) {
50757
51073
  return null;
50758
51074
  const roots = await Promise.all(candidates.map(async (candidate) => {
50759
51075
  try {
50760
- return (await fsp22.stat(candidate)).isDirectory() ? candidate : null;
51076
+ return (await fsp23.stat(candidate)).isDirectory() ? candidate : null;
50761
51077
  } catch {
50762
51078
  return null;
50763
51079
  }
@@ -50767,7 +51083,7 @@ async function activeNodeModulesRoots(projectRoot, files) {
50767
51083
  async function installedPackageManifests(projectRoot, root) {
50768
51084
  let entries;
50769
51085
  try {
50770
- entries = await fsp22.readdir(root, { withFileTypes: true });
51086
+ entries = await fsp23.readdir(root, { withFileTypes: true });
50771
51087
  } catch {
50772
51088
  return [];
50773
51089
  }
@@ -50778,7 +51094,7 @@ async function installedPackageManifests(projectRoot, root) {
50778
51094
  const entryPath = path44.join(root, entry.name);
50779
51095
  if (entry.name.startsWith("@")) {
50780
51096
  try {
50781
- const scopedEntries = await fsp22.readdir(entryPath, { withFileTypes: true });
51097
+ const scopedEntries = await fsp23.readdir(entryPath, { withFileTypes: true });
50782
51098
  for (const scopedEntry of scopedEntries) {
50783
51099
  if (scopedEntry.isDirectory())
50784
51100
  packageDirectories.push(path44.join(entryPath, scopedEntry.name));
@@ -50929,7 +51245,7 @@ var init_finalize = __esm({
50929
51245
  });
50930
51246
 
50931
51247
  // dist/indexer/build-manifest.js
50932
- import fsp23 from "node:fs/promises";
51248
+ import fsp24 from "node:fs/promises";
50933
51249
  import path45 from "node:path";
50934
51250
  import { performance as performance3 } from "node:perf_hooks";
50935
51251
  function toManifestFileEntry(entry) {
@@ -50949,7 +51265,7 @@ async function writeIndexManifestSnapshot(args) {
50949
51265
  const writeManifestStart = performance3.now();
50950
51266
  const lastCommit = await getGitHead(args.projectRoot);
50951
51267
  const configHashResult = await computeConfigHash(args.projectRoot, args.opts?.logLevel);
50952
- const projectRootMtimeMs = args.symlinkDirectories !== void 0 ? (await fsp23.stat(args.projectRoot)).mtimeMs : void 0;
51268
+ const projectRootMtimeMs = args.symlinkDirectories !== void 0 ? (await fsp24.stat(args.projectRoot)).mtimeMs : void 0;
50953
51269
  const configHash = recordConfigHashResult(args.manifestReport, configHashResult, args.opts?.logLevel);
50954
51270
  const manifestData = {
50955
51271
  version: MANIFEST_VERSION,
@@ -52374,7 +52690,7 @@ function createNativeWorkerPool(opts) {
52374
52690
  const workerPath = resolveNativeWorkerPath();
52375
52691
  return new Piscina({
52376
52692
  filename: workerPath,
52377
- minThreads: threads,
52693
+ minThreads: 1,
52378
52694
  maxThreads: threads,
52379
52695
  maxQueue: opts?.maxQueue ?? threads * 4,
52380
52696
  idleTimeout: 3e4,
@@ -52577,10 +52893,10 @@ var init_build_workers = __esm({
52577
52893
 
52578
52894
  // dist/indexer/incremental-plan.js
52579
52895
  import fs22 from "node:fs";
52580
- import fsp24 from "node:fs/promises";
52896
+ import fsp25 from "node:fs/promises";
52581
52897
  async function pathExists2(filePath) {
52582
52898
  try {
52583
- await fsp24.access(filePath);
52899
+ await fsp25.access(filePath);
52584
52900
  return true;
52585
52901
  } catch {
52586
52902
  return false;
@@ -52757,7 +53073,7 @@ __export(build_index_exports, {
52757
53073
  });
52758
53074
  import crypto10 from "node:crypto";
52759
53075
  import fs23 from "node:fs";
52760
- import fsp25 from "node:fs/promises";
53076
+ import fsp26 from "node:fs/promises";
52761
53077
  import path47 from "node:path";
52762
53078
  import { performance as performance5 } from "node:perf_hooks";
52763
53079
  function initParserBackendDegradationReport(report) {
@@ -52803,16 +53119,13 @@ function isConfinedFileReadError(error51) {
52803
53119
  function createEmptyModuleIndex(file2) {
52804
53120
  return { file: file2, exports: [], imports: [], locals: [] };
52805
53121
  }
52806
- async function resolveCrossModuleSymbolExports(file2, mod2, support, projectRoot, graphOptions, workspaceConfig, logLevel) {
52807
- if (!support.supportsCrossModuleSymbols)
53122
+ async function resolveCrossModuleSymbolExports(file2, mod2, support, projectRoot, graphOptions, workspaceConfig, matchPath) {
53123
+ if (!support.supportsCrossModuleSymbols || !isJsTsLanguage(support.id))
52808
53124
  return;
52809
- if (!isJsTsLanguage(support.id))
53125
+ const reexports = mod2.exports.filter((entry) => entry.type === "reexport" || entry.type === "exportStar" || entry.type === "namespaceReexport");
53126
+ if (!reexports.length)
52810
53127
  return;
52811
- const { matchPath } = await Promise.resolve().then(() => (init_resolution(), resolution_exports)).then((mod3) => mod3.loadNearestTsconfigFor(file2, projectRoot, logLevel));
52812
- for (const entry of mod2.exports) {
52813
- if (entry.type !== "reexport" && entry.type !== "exportStar" && entry.type !== "namespaceReexport") {
52814
- continue;
52815
- }
53128
+ for (const entry of reexports) {
52816
53129
  if (entry.fromModule.startsWith(".")) {
52817
53130
  entry.moduleSpecifier ??= entry.fromModule;
52818
53131
  const resolved = await resolveSpecifier(file2, entry.fromModule, projectRoot, matchPath, workspaceConfig, {
@@ -52876,6 +53189,8 @@ async function buildIndexedModuleForFile(args) {
52876
53189
  }
52877
53190
  const sharedImportOptions = {
52878
53191
  graphOptions: args.graphOptions,
53192
+ ...args.workspaceConfig ? { workspaceConfig: args.workspaceConfig } : {},
53193
+ ...args.matchPath ? { matchPath: args.matchPath } : {},
52879
53194
  ...args.opts?.native ? { native: args.opts.native } : {},
52880
53195
  ...args.opts?.logLevel ? { logLevel: args.opts.logLevel } : {},
52881
53196
  ...args.opts?.languageExtensions ? { languageExtensions: args.opts.languageExtensions } : {},
@@ -52909,7 +53224,7 @@ async function buildIndexedModuleForFile(args) {
52909
53224
  });
52910
53225
  }
52911
53226
  mod2.imports = imports;
52912
- await resolveCrossModuleSymbolExports(args.file, mod2, sup, args.projectRoot, args.graphOptions, args.workspaceConfig, args.opts?.logLevel);
53227
+ await resolveCrossModuleSymbolExports(args.file, mod2, sup, args.projectRoot, args.graphOptions, args.workspaceConfig, args.matchPath);
52913
53228
  const sigInfo = args.fileSignatures.get(args.file);
52914
53229
  const cacheable = !prepared.nativeFallbackReason && !lacksParserContext;
52915
53230
  let cacheWrite;
@@ -53093,7 +53408,7 @@ async function prepareFileSignatures(args) {
53093
53408
  args.trustedSources?.set(file2, source);
53094
53409
  return [file2, fileSignatureFromSource(source, gitSig)];
53095
53410
  }
53096
- const sigInfo = await fileSignature(file2, args.needsContentHash ? args.opts?.cacheStrict : false, gitSig, {
53411
+ const sigInfo = await fileSignature(file2, args.signatureStrict, gitSig, {
53097
53412
  forceContentHash: args.cacheEnabled && !gitSig
53098
53413
  });
53099
53414
  return [file2, sigInfo];
@@ -53106,13 +53421,13 @@ async function buildProjectIndexFromExport(projectRoot, opts, helperOpts) {
53106
53421
  async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperOpts) {
53107
53422
  clearResolutionCaches();
53108
53423
  await initializeFileIdentityCaseSensitivity(projectRoot);
53109
- const { normalizedProjectRoot, report, timings, totalStart, cacheMode, cacheEnabled, graphOptions, onFallbackImportExtraction } = createIndexBuildRunState(projectRoot, opts);
53424
+ const { normalizedProjectRoot, report, timings, totalStart, cacheEnabled, graphOptions, onFallbackImportExtraction } = createIndexBuildRunState(projectRoot, opts);
53110
53425
  const manifestMode = helperOpts?.manifestMode ?? "off";
53111
53426
  const useManifest = manifestMode !== "off";
53112
53427
  const shouldWriteManifest = manifestMode === "read-write";
53113
53428
  const projectFiles = helperOpts?.projectFiles;
53114
53429
  initManifestReport(report, useManifest, false);
53115
- const confinedRoot = helperOpts?.confineReads ? await fsp25.realpath(projectRoot) : void 0;
53430
+ const confinedRoot = helperOpts?.confineReads ? await fsp26.realpath(projectRoot) : void 0;
53116
53431
  const [trustedSources, normalizedFiles] = [
53117
53432
  confinedRoot ? /* @__PURE__ */ new Map() : void 0,
53118
53433
  Array.from(new Set(normalizeIndexedFileInputs(projectRoot, rawFiles ?? [], "Index file")))
@@ -53165,11 +53480,13 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
53165
53480
  const manifestEntriesForIndex = useManifest ? projectIndexManifestEntries2(cachedGraphEntries ?? []) : /* @__PURE__ */ new Map();
53166
53481
  const modules = /* @__PURE__ */ new Map();
53167
53482
  const gitAvailable = await isGitRepo(projectRoot);
53168
- const useGitSignatures = gitAvailable && (cacheMode !== "off" || opts?.cacheStrict);
53483
+ const needsPersistentSignatures = cacheEnabled || useManifest;
53484
+ const useGitSignatures = gitAvailable && needsPersistentSignatures;
53169
53485
  const gitSigMap = useGitSignatures ? await getGitBlobHashes(projectRoot, normalizedFiles, {
53170
53486
  gitAvailable,
53171
53487
  ...opts?.logLevel ? { logLevel: opts.logLevel } : {}
53172
53488
  }) : /* @__PURE__ */ new Map();
53489
+ const signatureStrict = needsPersistentSignatures ? opts?.cacheStrict : false;
53173
53490
  const conc = edgeProbeConcurrency;
53174
53491
  const languageExtensions = normalizeLanguageExtensions(opts?.languageExtensions);
53175
53492
  const sqlFiles2 = normalizedFiles.filter((file2) => {
@@ -53180,10 +53497,9 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
53180
53497
  const fileSignatures = await prepareFileSignatures({
53181
53498
  projectRoot,
53182
53499
  files: sqlFiles2,
53183
- opts,
53184
53500
  gitSigMap,
53185
53501
  cacheEnabled,
53186
- needsContentHash: true,
53502
+ signatureStrict,
53187
53503
  concurrency: conc,
53188
53504
  ...confinedRoot ? { confinedRoot, trustedSources } : {}
53189
53505
  });
@@ -53212,7 +53528,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
53212
53528
  trustedSources?.set(file2, source);
53213
53529
  sigInfo = fileSignatureFromSource(source, gitSig);
53214
53530
  } else {
53215
- sigInfo = await fileSignature(file2, opts?.cacheStrict, gitSig, {
53531
+ sigInfo = await fileSignature(file2, signatureStrict, gitSig, {
53216
53532
  forceContentHash: cacheEnabled && !gitSig
53217
53533
  });
53218
53534
  }
@@ -53240,6 +53556,16 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
53240
53556
  const persistedBloomFilters = bloomFilterCache ? await tryLoadPersistedBloomFilters(projectRoot, opts, report) : null;
53241
53557
  const parsedMap = /* @__PURE__ */ new Map();
53242
53558
  const workspaceConfig = await loadWorkspaceConfig(projectRoot);
53559
+ const tsconfigMatchPathByDirectory = /* @__PURE__ */ new Map();
53560
+ const loadMatchPathForFile = (file2) => {
53561
+ const directory = path47.dirname(file2);
53562
+ let matchPath = tsconfigMatchPathByDirectory.get(directory);
53563
+ if (!matchPath) {
53564
+ matchPath = loadNearestTsconfigFor(file2, projectRoot, opts?.logLevel).then((tsconfig) => tsconfig.matchPath);
53565
+ tsconfigMatchPathByDirectory.set(directory, matchPath);
53566
+ }
53567
+ return matchPath;
53568
+ };
53243
53569
  const parseStart = performance5.now();
53244
53570
  const graph = { nodes: new Set(normalizedFiles), edges: [] };
53245
53571
  const onFileEdges = manifestEntries ? (file2, entry) => {
@@ -53312,6 +53638,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
53312
53638
  ensureBuildProgressStarted();
53313
53639
  let graphContext;
53314
53640
  let cacheWrite;
53641
+ const matchPath = support.id === "ts" || support.id === "tsx" ? await loadMatchPathForFile(file2) : void 0;
53315
53642
  if (!mod2) {
53316
53643
  const built = await buildIndexedModuleForFile({
53317
53644
  file: file2,
@@ -53320,7 +53647,8 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
53320
53647
  opts,
53321
53648
  report,
53322
53649
  graphOptions,
53323
- workspaceConfig,
53650
+ ...workspaceConfig ? { workspaceConfig } : {},
53651
+ ...matchPath ? { matchPath } : {},
53324
53652
  workerSetup,
53325
53653
  ...resolverEnvironmentFingerprint !== void 0 ? { resolverEnvironmentFingerprint } : {},
53326
53654
  parsedMap,
@@ -53477,7 +53805,7 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
53477
53805
  const useDiskCache = (opts?.cache ?? "off") === "disk";
53478
53806
  const wantsMaxSymlinkCorrectness = !!opts?.cacheStrict || !!opts?.cacheVerify;
53479
53807
  const symlinkHintManifest = helperOpts?.ignoreExistingManifest || wantsMaxSymlinkCorrectness || !useDiskCache ? null : await loadManifest(projectRoot, opts);
53480
- const rootMtime = symlinkHintManifest ? (await fsp25.stat(projectRoot)).mtimeMs : void 0;
53808
+ const rootMtime = symlinkHintManifest ? (await fsp26.stat(projectRoot)).mtimeMs : void 0;
53481
53809
  const symlinkHintIsFresh = symlinkHintManifest?.symlinkDirectoriesRootMtimeMs !== void 0 && rootMtime === symlinkHintManifest.symlinkDirectoriesRootMtimeMs;
53482
53810
  const knownSymlinkDirectories = symlinkHintIsFresh ? symlinkHintManifest?.symlinkDirectories : void 0;
53483
53811
  let discoveredSymlinkDirectories = knownSymlinkDirectories;
@@ -53834,7 +54162,7 @@ async function buildProjectIndexIncremental(projectRoot, opts) {
53834
54162
  if (!entry?.sig)
53835
54163
  return true;
53836
54164
  try {
53837
- const stat = await fsp25.stat(file2);
54165
+ const stat = await fsp26.stat(file2);
53838
54166
  const prefix = `${stat.mtimeMs}:${stat.size}`;
53839
54167
  return !(entry.sig === prefix || entry.sig.startsWith(`${prefix}:`));
53840
54168
  } catch {
@@ -53852,6 +54180,16 @@ async function buildProjectIndexIncremental(projectRoot, opts) {
53852
54180
  }
53853
54181
  }
53854
54182
  const workspaceConfig = await loadWorkspaceConfig(projectRoot);
54183
+ const tsconfigMatchPathByDirectory = /* @__PURE__ */ new Map();
54184
+ const loadMatchPathForFile = (file2) => {
54185
+ const directory = path47.dirname(file2);
54186
+ let matchPath = tsconfigMatchPathByDirectory.get(directory);
54187
+ if (!matchPath) {
54188
+ matchPath = loadNearestTsconfigFor(file2, projectRoot, opts?.logLevel).then((tsconfig) => tsconfig.matchPath);
54189
+ tsconfigMatchPathByDirectory.set(directory, matchPath);
54190
+ }
54191
+ return matchPath;
54192
+ };
53855
54193
  const conc = buildConcurrency(opts);
53856
54194
  let workerSetup = emptyWorkerPoolSetup();
53857
54195
  try {
@@ -53863,10 +54201,9 @@ async function buildProjectIndexIncremental(projectRoot, opts) {
53863
54201
  const fileSignatures = await prepareFileSignatures({
53864
54202
  projectRoot,
53865
54203
  files: Array.from(allFiles),
53866
- opts,
53867
54204
  gitSigMap,
53868
54205
  cacheEnabled,
53869
- needsContentHash: cacheEnabled || manifestUsed || opts?.cacheStrict === true,
54206
+ signatureStrict: opts?.cacheStrict,
53870
54207
  concurrency: conc
53871
54208
  });
53872
54209
  const modules = /* @__PURE__ */ new Map();
@@ -53965,6 +54302,7 @@ async function buildProjectIndexIncremental(projectRoot, opts) {
53965
54302
  const support = supportForFile(file2, opts?.languageExtensions);
53966
54303
  if (!support)
53967
54304
  return [file2, createEmptyModuleIndex(file2)];
54305
+ const matchPath = support.id === "ts" || support.id === "tsx" ? await loadMatchPathForFile(file2) : void 0;
53968
54306
  const built = await buildIndexedModuleForFile({
53969
54307
  file: file2,
53970
54308
  support,
@@ -53972,7 +54310,8 @@ async function buildProjectIndexIncremental(projectRoot, opts) {
53972
54310
  opts,
53973
54311
  report,
53974
54312
  graphOptions,
53975
- workspaceConfig,
54313
+ ...workspaceConfig ? { workspaceConfig } : {},
54314
+ ...matchPath ? { matchPath } : {},
53976
54315
  workerSetup,
53977
54316
  parsedMap,
53978
54317
  parsedCacheMaxEntries: parsedCacheMaxEntries(opts),
@@ -55630,7 +55969,7 @@ var init_sessionLifecycle = __esm({
55630
55969
  });
55631
55970
 
55632
55971
  // dist/agent/session.js
55633
- import fsp26 from "node:fs/promises";
55972
+ import fsp27 from "node:fs/promises";
55634
55973
  import path49 from "node:path";
55635
55974
  function assertNoPrebuiltSessionWithBuildOptions(options, consumer) {
55636
55975
  if (options.session && options.buildOptions) {
@@ -55709,7 +56048,7 @@ async function collectAgentFileSignatures(files) {
55709
56048
  await mapLimit([...files], FILE_SIGNATURE_STAT_CONCURRENCY, async (file2) => {
55710
56049
  const resolvedFile = normalizePath(path49.resolve(file2));
55711
56050
  try {
55712
- const stat = await fsp26.stat(resolvedFile);
56051
+ const stat = await fsp27.stat(resolvedFile);
55713
56052
  if (!stat.isFile())
55714
56053
  return;
55715
56054
  signatures.set(resolvedFile, {
@@ -56070,13 +56409,13 @@ var init_errors4 = __esm({
56070
56409
  });
56071
56410
 
56072
56411
  // dist/lifecycle/gitignore.js
56073
- import fsp27 from "node:fs/promises";
56412
+ import fsp28 from "node:fs/promises";
56074
56413
  import path50 from "node:path";
56075
56414
  async function readGitignoreFile(gitignorePath) {
56076
56415
  let existing = "";
56077
56416
  let stats;
56078
56417
  try {
56079
- stats = await fsp27.lstat(gitignorePath);
56418
+ stats = await fsp28.lstat(gitignorePath);
56080
56419
  } catch (error51) {
56081
56420
  if (!(error51 instanceof Error && "code" in error51 && error51.code === "ENOENT")) {
56082
56421
  const detail = errorMessage(error51);
@@ -56093,7 +56432,7 @@ async function readGitignoreFile(gitignorePath) {
56093
56432
  throw new CodegraphLifecycleUserError(`Cannot update ${gitignorePath}: expected a regular file, but found a ${kind}. Replace it with a regular file or rerun with --no-update-gitignore.`);
56094
56433
  }
56095
56434
  try {
56096
- existing = await fsp27.readFile(gitignorePath, "utf8");
56435
+ existing = await fsp28.readFile(gitignorePath, "utf8");
56097
56436
  } catch (error51) {
56098
56437
  const detail = errorMessage(error51);
56099
56438
  throw new CodegraphLifecycleUserError(`Unable to read ${gitignorePath}: ${detail}. Check file permissions or rerun with --no-update-gitignore.`);
@@ -56115,43 +56454,38 @@ async function prepareCodegraphLifecycleGitignore(root, options = {}) {
56115
56454
  const manifestTracked = await isGitPathTracked(resolvedRoot, LIFECYCLE_MANIFEST_PATH);
56116
56455
  if (manifestTracked)
56117
56456
  return { status: "tracked", path: GITIGNORE_PATH };
56118
- const [lifecycleIgnored, diskCacheIgnored] = await Promise.all([
56119
- isGitPathIgnored(resolvedRoot, LIFECYCLE_MANIFEST_PATH),
56120
- isGitPathIgnored(resolvedRoot, DISK_CACHE_PROBE_PATH)
56121
- ]);
56122
- const missingRules = [
56123
- ...!lifecycleIgnored ? [LIFECYCLE_GITIGNORE_RULE] : [],
56124
- ...!diskCacheIgnored ? [DISK_CACHE_GITIGNORE_RULE] : []
56125
- ];
56126
- if (!missingRules.length) {
56457
+ const manifestIgnored = await isGitPathIgnored(resolvedRoot, LIFECYCLE_MANIFEST_PATH);
56458
+ const cacheIgnored = await isGitPathIgnored(resolvedRoot, LIFECYCLE_CACHE_PATH);
56459
+ if (manifestIgnored && cacheIgnored) {
56127
56460
  return { status: "already-ignored", path: GITIGNORE_PATH, rules: [...CODEGRAPH_GITIGNORE_RULES] };
56128
56461
  }
56462
+ const missingRules = [LIFECYCLE_GITIGNORE_RULE];
56129
56463
  const gitignorePath = path50.join(resolvedRoot, GITIGNORE_PATH);
56130
56464
  const { existing, newline } = await readGitignoreFile(gitignorePath);
56131
56465
  let suffix = missingRules.map((rule) => `${rule}${newline}`).join("");
56132
56466
  if (existing && !existing.endsWith("\n"))
56133
56467
  suffix = `${newline}${suffix}`;
56134
56468
  try {
56135
- await fsp27.appendFile(gitignorePath, suffix, "utf8");
56469
+ await fsp28.appendFile(gitignorePath, suffix, "utf8");
56136
56470
  } catch (error51) {
56137
56471
  const detail = errorMessage(error51);
56138
56472
  throw new CodegraphLifecycleUserError(`Unable to update ${gitignorePath}: ${detail}. Check file permissions or rerun with --no-update-gitignore.`);
56139
56473
  }
56140
56474
  return { status: "added", path: GITIGNORE_PATH, rules: missingRules };
56141
56475
  }
56142
- var LIFECYCLE_MANIFEST_PATH, DISK_CACHE_PROBE_PATH, GITIGNORE_PATH, LIFECYCLE_GITIGNORE_RULE, DISK_CACHE_GITIGNORE_RULE, CODEGRAPH_GITIGNORE_RULES;
56476
+ var LIFECYCLE_MANIFEST_PATH, LIFECYCLE_CACHE_PATH, GITIGNORE_PATH, LIFECYCLE_GITIGNORE_RULE, CODEGRAPH_GITIGNORE_RULES;
56143
56477
  var init_gitignore = __esm({
56144
56478
  "dist/lifecycle/gitignore.js"() {
56145
56479
  "use strict";
56480
+ init_location();
56146
56481
  init_git();
56147
56482
  init_errors();
56148
56483
  init_errors4();
56149
56484
  LIFECYCLE_MANIFEST_PATH = ".codegraph/manifest.json";
56150
- DISK_CACHE_PROBE_PATH = ".codegraph-cache/index-v1/manifest.json";
56485
+ LIFECYCLE_CACHE_PATH = PROJECT_CACHE_RELATIVE_PATH.replaceAll(path50.sep, "/");
56151
56486
  GITIGNORE_PATH = ".gitignore";
56152
56487
  LIFECYCLE_GITIGNORE_RULE = ".codegraph/";
56153
- DISK_CACHE_GITIGNORE_RULE = ".codegraph-cache/";
56154
- CODEGRAPH_GITIGNORE_RULES = [LIFECYCLE_GITIGNORE_RULE, DISK_CACHE_GITIGNORE_RULE];
56488
+ CODEGRAPH_GITIGNORE_RULES = [LIFECYCLE_GITIGNORE_RULE];
56155
56489
  }
56156
56490
  });
56157
56491
 
@@ -56167,7 +56501,7 @@ __export(manifest_exports, {
56167
56501
  uninitCodegraphLifecycle: () => uninitCodegraphLifecycle
56168
56502
  });
56169
56503
  import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
56170
- import fsp28 from "node:fs/promises";
56504
+ import fsp29 from "node:fs/promises";
56171
56505
  import path51 from "node:path";
56172
56506
  function codegraphLifecycleManifestPath(root) {
56173
56507
  return path51.join(root, CODEGRAPH_DIR, MANIFEST_FILE);
@@ -56268,11 +56602,11 @@ async function uninitCodegraphLifecycle(root, options = {}) {
56268
56602
  if (!entries.length) {
56269
56603
  return { schemaVersion: MANIFEST_SCHEMA_VERSION, root, removed: false, manifestPath };
56270
56604
  }
56271
- const unknownEntries = entries.filter((entry) => !KNOWN_CODEGRAPH_FILES.has(entry));
56605
+ const unknownEntries = entries.filter((entry) => !Object.hasOwn(KNOWN_CODEGRAPH_FILES, entry));
56272
56606
  if (unknownEntries.length && !options.force) {
56273
56607
  throw new CodegraphLifecycleUserError(`Refusing to remove .codegraph with unknown entries: ${unknownEntries.join(", ")}. Use --force to remove them.`);
56274
56608
  }
56275
- const removableEntries = entries.filter((entry) => entry !== SERVER_REGISTRY_FILE && entry !== SERVER_LOG_FILE);
56609
+ const removableEntries = entries.filter((entry) => entry !== SERVER_REGISTRY_FILE && entry !== SERVER_LOG_FILE && entry !== CACHE_DIRECTORY);
56276
56610
  if (options.force) {
56277
56611
  for (const entry of removableEntries) {
56278
56612
  await removeCodegraphPath(path51.join(dir, entry), { recursive: true });
@@ -56312,7 +56646,7 @@ async function readLifecycleManifest(root, options = {}) {
56312
56646
  const manifestPath = codegraphLifecycleManifestPath(root);
56313
56647
  let raw;
56314
56648
  try {
56315
- raw = await fsp28.readFile(manifestPath, "utf8");
56649
+ raw = await fsp29.readFile(manifestPath, "utf8");
56316
56650
  } catch (error51) {
56317
56651
  if (error51 instanceof Error && "code" in error51 && error51.code === "ENOENT")
56318
56652
  return null;
@@ -56336,14 +56670,14 @@ function lifecycleManifestTempFilePath(manifestPath) {
56336
56670
  }
56337
56671
  async function writeLifecycleManifest(root, manifest) {
56338
56672
  const manifestPath = codegraphLifecycleManifestPath(root);
56339
- await fsp28.mkdir(path51.dirname(manifestPath), { recursive: true });
56673
+ await fsp29.mkdir(path51.dirname(manifestPath), { recursive: true });
56340
56674
  const tempPath = lifecycleManifestTempFilePath(manifestPath);
56341
56675
  try {
56342
- await fsp28.writeFile(tempPath, `${JSON.stringify(manifest, null, 2)}
56676
+ await fsp29.writeFile(tempPath, `${JSON.stringify(manifest, null, 2)}
56343
56677
  `, "utf8");
56344
- await fsp28.rename(tempPath, manifestPath);
56678
+ await fsp29.rename(tempPath, manifestPath);
56345
56679
  } catch (error51) {
56346
- await fsp28.rm(tempPath, { force: true }).catch(() => {
56680
+ await fsp29.rm(tempPath, { force: true }).catch(() => {
56347
56681
  });
56348
56682
  throw new CodegraphLifecycleUserError(`Unable to write Codegraph lifecycle manifest at ${manifestPath}: ${stringifyError(error51)}`);
56349
56683
  }
@@ -56384,7 +56718,7 @@ async function hashDiscoveredFiles(files, root) {
56384
56718
  const signatures = /* @__PURE__ */ new Map();
56385
56719
  await mapLimit(sorted, FILE_SIGNATURE_STAT_CONCURRENCY2, async (file2) => {
56386
56720
  try {
56387
- const stat = await fsp28.stat(file2);
56721
+ const stat = await fsp29.stat(file2);
56388
56722
  signatures.set(file2, { size: stat.size, mtimeMs: stat.mtimeMs });
56389
56723
  } catch (error51) {
56390
56724
  if (isMissingStatRace2(error51))
@@ -56443,18 +56777,18 @@ function sha256(value) {
56443
56777
  }
56444
56778
  async function removeCodegraphPath(target, options) {
56445
56779
  try {
56446
- await fsp28.rm(target, { ...options.recursive ? { recursive: true } : {}, force: true });
56780
+ await fsp29.rm(target, { ...options.recursive ? { recursive: true } : {}, force: true });
56447
56781
  } catch (error51) {
56448
56782
  throw new CodegraphLifecycleUserError(`Unable to remove ${target}: ${stringifyError(error51)}`);
56449
56783
  }
56450
56784
  }
56451
56785
  async function readCodegraphDirEntries(dir) {
56452
56786
  try {
56453
- const stats = await fsp28.lstat(dir);
56787
+ const stats = await fsp29.lstat(dir);
56454
56788
  if (stats.isSymbolicLink()) {
56455
56789
  throw new CodegraphLifecycleUserError(`Refusing to traverse symbolic link as .codegraph lifecycle directory: ${dir}`);
56456
56790
  }
56457
- return await fsp28.readdir(dir);
56791
+ return await fsp29.readdir(dir);
56458
56792
  } catch (error51) {
56459
56793
  if (error51 instanceof CodegraphLifecycleUserError)
56460
56794
  throw error51;
@@ -56465,7 +56799,7 @@ async function readCodegraphDirEntries(dir) {
56465
56799
  }
56466
56800
  async function removeDirIfEmpty(dir) {
56467
56801
  try {
56468
- await fsp28.rmdir(dir);
56802
+ await fsp29.rmdir(dir);
56469
56803
  } catch (error51) {
56470
56804
  if (error51 instanceof Error && "code" in error51 && error51.code === "ENOTEMPTY")
56471
56805
  return;
@@ -56487,7 +56821,7 @@ function isOptionalStringArray(value) {
56487
56821
  return true;
56488
56822
  return Array.isArray(value) && value.every((entry) => typeof entry === "string");
56489
56823
  }
56490
- var MANIFEST_SCHEMA_VERSION, CODEGRAPH_DIR, MANIFEST_FILE, SERVER_REGISTRY_FILE, SERVER_LOG_FILE, KNOWN_CODEGRAPH_FILES, FILE_SIGNATURE_STAT_CONCURRENCY2;
56824
+ var MANIFEST_SCHEMA_VERSION, CODEGRAPH_DIR, MANIFEST_FILE, SERVER_REGISTRY_FILE, SERVER_LOG_FILE, CACHE_DIRECTORY, KNOWN_CODEGRAPH_FILES, FILE_SIGNATURE_STAT_CONCURRENCY2;
56491
56825
  var init_manifest2 = __esm({
56492
56826
  "dist/lifecycle/manifest.js"() {
56493
56827
  "use strict";
@@ -56504,7 +56838,13 @@ var init_manifest2 = __esm({
56504
56838
  MANIFEST_FILE = "manifest.json";
56505
56839
  SERVER_REGISTRY_FILE = "server.json";
56506
56840
  SERVER_LOG_FILE = "server.log";
56507
- KNOWN_CODEGRAPH_FILES = /* @__PURE__ */ new Set([MANIFEST_FILE, SERVER_REGISTRY_FILE, SERVER_LOG_FILE]);
56841
+ CACHE_DIRECTORY = "cache";
56842
+ KNOWN_CODEGRAPH_FILES = {
56843
+ [MANIFEST_FILE]: true,
56844
+ [SERVER_REGISTRY_FILE]: true,
56845
+ [SERVER_LOG_FILE]: true,
56846
+ [CACHE_DIRECTORY]: true
56847
+ };
56508
56848
  FILE_SIGNATURE_STAT_CONCURRENCY2 = 64;
56509
56849
  }
56510
56850
  });
@@ -56557,7 +56897,7 @@ function formatSyncResult(label, result) {
56557
56897
  const summary = `${label} Codegraph at ${result.root}: ${result.manifest.fileCount} files${changeLabel}. Manifest: ${result.manifestPath}`;
56558
56898
  if (result.gitignore?.status === "added") {
56559
56899
  const gitignorePath = path52.join(result.root, result.gitignore.path);
56560
- const rules = result.gitignore.rules?.length ? result.gitignore.rules.join(", ") : ".codegraph/, .codegraph-cache/";
56900
+ const rules = result.gitignore.rules?.length ? result.gitignore.rules.join(", ") : ".codegraph/";
56561
56901
  return `${summary}
56562
56902
  Updated Git ignore policy at ${gitignorePath}: added ${rules}.`;
56563
56903
  }
@@ -57350,14 +57690,14 @@ __export(skill_exports, {
57350
57690
  getSkillTargetDirForAgent: () => getSkillTargetDirForAgent,
57351
57691
  handleSkillCommand: () => handleSkillCommand
57352
57692
  });
57353
- import fsp29 from "node:fs/promises";
57354
- import os5 from "node:os";
57693
+ import fsp30 from "node:fs/promises";
57694
+ import os6 from "node:os";
57355
57695
  import path53 from "node:path";
57356
57696
  function getBundledSkillDir(packageRoot) {
57357
57697
  const candidate = path53.join(packageRoot, "codegraph-skill", "codegraph");
57358
57698
  return pathExists(path53.join(candidate, "SKILL.md")) ? candidate : null;
57359
57699
  }
57360
- function getSkillTargetDirForAgent(agent, homeDir = os5.homedir(), env = process.env) {
57700
+ function getSkillTargetDirForAgent(agent, homeDir = os6.homedir(), env = process.env) {
57361
57701
  if (agent === "agents") {
57362
57702
  return path53.join(homeDir, ".agents", "skills", "codegraph");
57363
57703
  }
@@ -57418,10 +57758,10 @@ function isCommandAvailableOnPath(command) {
57418
57758
  }
57419
57759
  async function copyDirectoryRecursive(sourceDir, targetDir, overwrite) {
57420
57760
  if (overwrite && pathExists(targetDir)) {
57421
- await fsp29.rm(targetDir, { recursive: true, force: true });
57761
+ await fsp30.rm(targetDir, { recursive: true, force: true });
57422
57762
  }
57423
- await fsp29.mkdir(targetDir, { recursive: true });
57424
- const entries = await fsp29.readdir(sourceDir, { withFileTypes: true });
57763
+ await fsp30.mkdir(targetDir, { recursive: true });
57764
+ const entries = await fsp30.readdir(sourceDir, { withFileTypes: true });
57425
57765
  for (const entry of entries) {
57426
57766
  const sourcePath = path53.join(sourceDir, entry.name);
57427
57767
  const targetPath = path53.join(targetDir, entry.name);
@@ -57432,7 +57772,7 @@ async function copyDirectoryRecursive(sourceDir, targetDir, overwrite) {
57432
57772
  if (!overwrite && pathExists(targetPath)) {
57433
57773
  throw new Error(`Target file already exists: ${normalizePathForDisplay(targetPath)}. Re-run with --force to overwrite.`);
57434
57774
  }
57435
- await fsp29.copyFile(sourcePath, targetPath);
57775
+ await fsp30.copyFile(sourcePath, targetPath);
57436
57776
  }
57437
57777
  }
57438
57778
  function normalizePathSegmentForComparison(segment) {
@@ -57575,8 +57915,8 @@ var init_skill = __esm({
57575
57915
 
57576
57916
  // dist/installer/locks.js
57577
57917
  import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
57578
- import fsp30, {} from "node:fs/promises";
57579
- import os6 from "node:os";
57918
+ import fsp31, {} from "node:fs/promises";
57919
+ import os7 from "node:os";
57580
57920
  import path54 from "node:path";
57581
57921
  function opencodeConfigHome(settings) {
57582
57922
  const configHome = settings.env.XDG_CONFIG_HOME?.trim();
@@ -57612,7 +57952,7 @@ function installerTransactionLockScopes(settings) {
57612
57952
  }
57613
57953
  function installerTransactionLockPath(scope) {
57614
57954
  const digest = createHash5("sha256").update(scope).digest("hex");
57615
- return path54.join(os6.tmpdir(), `codegraph-installer-${digest}.lock`);
57955
+ return path54.join(os7.tmpdir(), `codegraph-installer-${digest}.lock`);
57616
57956
  }
57617
57957
  async function withInstallerLeaseLock(lockPath, resourceName, operation) {
57618
57958
  const lock = await acquireInstallerLeaseLock(lockPath, resourceName);
@@ -57630,14 +57970,14 @@ async function acquireInstallerLeaseLock(lockPath, resourceName) {
57630
57970
  let file2;
57631
57971
  try {
57632
57972
  const owner = randomUUID3();
57633
- file2 = await fsp30.open(acquisitionPath, "wx", 384);
57973
+ file2 = await fsp31.open(acquisitionPath, "wx", 384);
57634
57974
  await writeInstallerLeaseMetadata(file2, owner);
57635
57975
  await file2.close();
57636
57976
  file2 = void 0;
57637
- await fsp30.link(acquisitionPath, lockPath);
57977
+ await fsp31.link(acquisitionPath, lockPath);
57638
57978
  published = true;
57639
- await fsp30.rm(acquisitionPath, { force: true });
57640
- file2 = await fsp30.open(lockPath, "r+");
57979
+ await fsp31.rm(acquisitionPath, { force: true });
57980
+ file2 = await fsp31.open(lockPath, "r+");
57641
57981
  const lockFile = file2;
57642
57982
  const renewalTimer = setInterval(() => {
57643
57983
  void writeInstallerLeaseMetadata(lockFile, owner).catch(() => void 0);
@@ -57646,9 +57986,9 @@ async function acquireInstallerLeaseLock(lockPath, resourceName) {
57646
57986
  return { file: lockFile, lockPath, metadataPath: lockPath, owner, renewalTimer };
57647
57987
  } catch (error51) {
57648
57988
  await file2?.close().catch(() => void 0);
57649
- await fsp30.rm(acquisitionPath, { force: true }).catch(() => void 0);
57989
+ await fsp31.rm(acquisitionPath, { force: true }).catch(() => void 0);
57650
57990
  if (published)
57651
- await fsp30.rm(lockPath, { force: true }).catch(() => void 0);
57991
+ await fsp31.rm(lockPath, { force: true }).catch(() => void 0);
57652
57992
  if (!isInstallerLockPublishConflict(error51, lockPath))
57653
57993
  throw error51;
57654
57994
  const conflict = await resolveInstallerLeaseConflict(lockPath);
@@ -57666,10 +58006,10 @@ async function acquireInstallerLeaseLock(lockPath, resourceName) {
57666
58006
  async function resolveInstallerLeaseConflict(lockPath) {
57667
58007
  let observed;
57668
58008
  try {
57669
- const stats = await fsp30.lstat(lockPath);
58009
+ const stats = await fsp31.lstat(lockPath);
57670
58010
  if (!stats.isFile())
57671
58011
  return "blocked";
57672
- observed = { content: await fsp30.readFile(lockPath, "utf8"), mtimeMs: stats.mtimeMs };
58012
+ observed = { content: await fsp31.readFile(lockPath, "utf8"), mtimeMs: stats.mtimeMs };
57673
58013
  } catch (error51) {
57674
58014
  if (isFileSystemErrorCode(error51, "ENOENT"))
57675
58015
  return "absent";
@@ -57691,7 +58031,7 @@ async function resolveInstallerLeaseConflict(lockPath) {
57691
58031
  async function reclaimAbandonedInstallerLease(lockPath, observed) {
57692
58032
  const reclaimPath = `${lockPath}.reclaim-${process.pid}-${randomUUID3()}`;
57693
58033
  try {
57694
- await fsp30.rename(lockPath, reclaimPath);
58034
+ await fsp31.rename(lockPath, reclaimPath);
57695
58035
  } catch (error51) {
57696
58036
  if (isFileSystemErrorCode(error51, "ENOENT"))
57697
58037
  return "absent";
@@ -57699,20 +58039,20 @@ async function reclaimAbandonedInstallerLease(lockPath, observed) {
57699
58039
  }
57700
58040
  let verified = false;
57701
58041
  try {
57702
- const stats = await fsp30.lstat(reclaimPath);
57703
- verified = stats.isFile() && stats.mtimeMs === observed.mtimeMs && await fsp30.readFile(reclaimPath, "utf8") === observed.content;
58042
+ const stats = await fsp31.lstat(reclaimPath);
58043
+ verified = stats.isFile() && stats.mtimeMs === observed.mtimeMs && await fsp31.readFile(reclaimPath, "utf8") === observed.content;
57704
58044
  } catch {
57705
58045
  verified = false;
57706
58046
  }
57707
58047
  if (verified) {
57708
- await fsp30.rm(reclaimPath, { force: true });
58048
+ await fsp31.rm(reclaimPath, { force: true });
57709
58049
  return "reclaimed";
57710
58050
  }
57711
58051
  try {
57712
- await fsp30.link(reclaimPath, lockPath);
58052
+ await fsp31.link(reclaimPath, lockPath);
57713
58053
  } catch {
57714
58054
  }
57715
- await fsp30.rm(reclaimPath, { force: true });
58055
+ await fsp31.rm(reclaimPath, { force: true });
57716
58056
  return "blocked";
57717
58057
  }
57718
58058
  function isInstallerLockPublishConflict(error51, lockPath) {
@@ -57749,9 +58089,9 @@ async function releaseInstallerLeaseLock(lock) {
57749
58089
  clearInterval(lock.renewalTimer);
57750
58090
  await lock.file.close().catch(() => void 0);
57751
58091
  try {
57752
- const metadata = parseInstallerLeaseMetadata(await fsp30.readFile(lock.metadataPath, "utf8"));
58092
+ const metadata = parseInstallerLeaseMetadata(await fsp31.readFile(lock.metadataPath, "utf8"));
57753
58093
  if (metadata?.owner === lock.owner)
57754
- await fsp30.rm(lock.lockPath, { force: true });
58094
+ await fsp31.rm(lock.lockPath, { force: true });
57755
58095
  } catch (error51) {
57756
58096
  if (!isFileSystemErrorCode(error51, "ENOENT"))
57757
58097
  throw error51;
@@ -57777,8 +58117,8 @@ var init_locks = __esm({
57777
58117
 
57778
58118
  // dist/installer/registry.js
57779
58119
  import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
57780
- import fsp31 from "node:fs/promises";
57781
- import os7 from "node:os";
58120
+ import fsp32 from "node:fs/promises";
58121
+ import os8 from "node:os";
57782
58122
  import path55 from "node:path";
57783
58123
  import { applyEdits, modify, parse as parseJsonc2 } from "jsonc-parser";
57784
58124
  function listInstallTargets() {
@@ -57905,7 +58245,7 @@ async function installDefinitions(definitions, options) {
57905
58245
  return await withInstallerTransactionLocks(settings, install);
57906
58246
  }
57907
58247
  async function prepareInstallPlan(definitions, settings, dryRun, force) {
57908
- const bundledSkill = await fsp31.readFile(bundledSkillFilePath());
58248
+ const bundledSkill = await fsp32.readFile(bundledSkillFilePath());
57909
58249
  const files = [];
57910
58250
  const conflicts = [];
57911
58251
  for (const definition of definitions) {
@@ -58033,7 +58373,7 @@ async function rollbackInstallPlan(plan) {
58033
58373
  }
58034
58374
  async function removePlannedInstallFile(filePath, root) {
58035
58375
  await assertNoSymbolicLinksWithinRoot(filePath, root);
58036
- await fsp31.rm(filePath, { force: true });
58376
+ await fsp32.rm(filePath, { force: true });
58037
58377
  }
58038
58378
  async function verifyInstallPlan(plan, verifyInstalledConfig) {
58039
58379
  for (const file2 of plan.files) {
@@ -58527,7 +58867,7 @@ function installerSettings(options) {
58527
58867
  if (env === void 0)
58528
58868
  env = options.homeDir === void 0 ? process.env : {};
58529
58869
  return {
58530
- homeDir: options.homeDir ?? os7.homedir(),
58870
+ homeDir: options.homeDir ?? os8.homedir(),
58531
58871
  env
58532
58872
  };
58533
58873
  }
@@ -58544,14 +58884,14 @@ function bundledSkillFilePath() {
58544
58884
  async function snapshotInstallFile(filePath, root) {
58545
58885
  await assertNoSymbolicLinksWithinRoot(filePath, root);
58546
58886
  try {
58547
- const stats = await fsp31.lstat(filePath);
58887
+ const stats = await fsp32.lstat(filePath);
58548
58888
  if (stats.isSymbolicLink())
58549
58889
  throw unsafeSymbolicLinkError(filePath);
58550
58890
  if (!stats.isFile()) {
58551
58891
  throw new Error(`Expected a regular file at ${normalizePathForDisplay(filePath)} before running the installer.`);
58552
58892
  }
58553
58893
  return {
58554
- bytes: await fsp31.readFile(filePath),
58894
+ bytes: await fsp32.readFile(filePath),
58555
58895
  mode: stats.mode & 4095
58556
58896
  };
58557
58897
  } catch (error51) {
@@ -58571,7 +58911,7 @@ async function assertNoSymbolicLinksWithinRoot(filePath, root) {
58571
58911
  }
58572
58912
  for (let current = resolvedFilePath; current !== resolvedRoot; current = path55.dirname(current)) {
58573
58913
  try {
58574
- const stats = await fsp31.lstat(current);
58914
+ const stats = await fsp32.lstat(current);
58575
58915
  if (stats.isSymbolicLink())
58576
58916
  throw unsafeSymbolicLinkError(current);
58577
58917
  } catch (error51) {
@@ -58586,7 +58926,7 @@ async function confirmMissingPathHasDirectoryAncestors(filePath) {
58586
58926
  let current = path55.dirname(filePath);
58587
58927
  while (true) {
58588
58928
  try {
58589
- const stats = await fsp31.lstat(current);
58929
+ const stats = await fsp32.lstat(current);
58590
58930
  if (stats.isSymbolicLink())
58591
58931
  throw unsafeSymbolicLinkError(current);
58592
58932
  if (!stats.isDirectory()) {
@@ -58625,7 +58965,7 @@ async function writeTextFileAtomic(filePath, content, options = {}) {
58625
58965
  await assertNoSymbolicLinkAtPath(filePath);
58626
58966
  else
58627
58967
  await assertNoSymbolicLinksWithinRoot(filePath, options.root);
58628
- await fsp31.mkdir(path55.dirname(filePath), { recursive: true });
58968
+ await fsp32.mkdir(path55.dirname(filePath), { recursive: true });
58629
58969
  await withInstallerLeaseLock(`${filePath}.codegraph-lock`, normalizePathForDisplay(filePath), async () => {
58630
58970
  if (options.root === void 0)
58631
58971
  await assertNoSymbolicLinkAtPath(filePath);
@@ -58637,7 +58977,7 @@ async function writeTextFileAtomic(filePath, content, options = {}) {
58637
58977
  let mode = options.mode ?? 384;
58638
58978
  if (options.mode === void 0) {
58639
58979
  try {
58640
- const existing = await fsp31.lstat(filePath);
58980
+ const existing = await fsp32.lstat(filePath);
58641
58981
  if (existing.isSymbolicLink())
58642
58982
  throw unsafeSymbolicLinkError(filePath);
58643
58983
  mode = existing.mode & 4095;
@@ -58646,8 +58986,8 @@ async function writeTextFileAtomic(filePath, content, options = {}) {
58646
58986
  throw error51;
58647
58987
  }
58648
58988
  }
58649
- await fsp31.writeFile(temporaryPath, content, { encoding: "utf8", flag: "wx", mode });
58650
- await fsp31.chmod(temporaryPath, mode);
58989
+ await fsp32.writeFile(temporaryPath, content, { encoding: "utf8", flag: "wx", mode });
58990
+ await fsp32.chmod(temporaryPath, mode);
58651
58991
  await renameTemporaryFile(temporaryPath, filePath);
58652
58992
  } catch (error51) {
58653
58993
  if (isFileSystemErrorCode(error51, "EACCES") || isFileSystemErrorCode(error51, "EPERM")) {
@@ -58655,13 +58995,13 @@ async function writeTextFileAtomic(filePath, content, options = {}) {
58655
58995
  }
58656
58996
  throw error51;
58657
58997
  } finally {
58658
- await fsp31.rm(temporaryPath, { force: true }).catch(() => void 0);
58998
+ await fsp32.rm(temporaryPath, { force: true }).catch(() => void 0);
58659
58999
  }
58660
59000
  });
58661
59001
  }
58662
59002
  async function assertNoSymbolicLinkAtPath(filePath) {
58663
59003
  try {
58664
- const stats = await fsp31.lstat(filePath);
59004
+ const stats = await fsp32.lstat(filePath);
58665
59005
  if (stats.isSymbolicLink())
58666
59006
  throw unsafeSymbolicLinkError(filePath);
58667
59007
  } catch (error51) {
@@ -58676,7 +59016,7 @@ function unsafeSymbolicLinkError(filePath) {
58676
59016
  async function renameTemporaryFile(temporaryPath, filePath) {
58677
59017
  for (let attempt = 0; attempt < 5; attempt += 1) {
58678
59018
  try {
58679
- await fsp31.rename(temporaryPath, filePath);
59019
+ await fsp32.rename(temporaryPath, filePath);
58680
59020
  return;
58681
59021
  } catch (error51) {
58682
59022
  if (!isFileSystemErrorCode(error51, "EPERM") || attempt === 4)
@@ -58864,7 +59204,7 @@ __export(doctor_exports, {
58864
59204
  formatDoctorSummary: () => formatDoctorSummary
58865
59205
  });
58866
59206
  import fs25 from "node:fs";
58867
- import os8 from "node:os";
59207
+ import os9 from "node:os";
58868
59208
  import path57 from "node:path";
58869
59209
  function statIfExists(filePath) {
58870
59210
  try {
@@ -58897,15 +59237,15 @@ function detectIndexedArtifactType(filePath, stats) {
58897
59237
  return "artifactBundle";
58898
59238
  }
58899
59239
  const normalized = normalizePathForDisplay(filePath).toLowerCase();
59240
+ if (normalized.endsWith("/.codegraph/cache") || normalized.includes("/.codegraph/cache/") || normalized.endsWith("/.codegraph-cache") || normalized.includes("/.codegraph-cache/")) {
59241
+ return "diskCache";
59242
+ }
58900
59243
  if (normalized.endsWith("/codegraph.json") || normalized.endsWith(".json")) {
58901
59244
  return "jsonGraph";
58902
59245
  }
58903
59246
  if (normalized.endsWith("/graph.sqlite") || normalized.endsWith(".sqlite")) {
58904
59247
  return "sqliteGraph";
58905
59248
  }
58906
- if (normalized.endsWith("/.codegraph-cache") || normalized.includes("/.codegraph-cache/")) {
58907
- return "diskCache";
58908
- }
58909
59249
  return "unknown";
58910
59250
  }
58911
59251
  function buildIndexedArtifactReport(indexPath) {
@@ -59092,7 +59432,7 @@ function readCacheLocationField(configPath) {
59092
59432
  }
59093
59433
  }
59094
59434
  function readUserCacheLocation() {
59095
- const configRoot = process.platform === "win32" ? process.env.APPDATA?.trim() || path57.join(os8.homedir(), "AppData", "Roaming") : process.env.XDG_CONFIG_HOME?.trim() || path57.join(os8.homedir(), ".config");
59435
+ const configRoot = process.platform === "win32" ? process.env.APPDATA?.trim() || path57.join(os9.homedir(), "AppData", "Roaming") : process.env.XDG_CONFIG_HOME?.trim() || path57.join(os9.homedir(), ".config");
59096
59436
  return readCacheLocationField(path57.join(configRoot, "codegraph", "config.json"));
59097
59437
  }
59098
59438
  function readEffectiveCacheLocation(root) {
@@ -60605,7 +60945,7 @@ var chunk_exports = {};
60605
60945
  __export(chunk_exports, {
60606
60946
  handleChunkCommand: () => handleChunkCommand
60607
60947
  });
60608
- import fsp32 from "node:fs/promises";
60948
+ import fsp33 from "node:fs/promises";
60609
60949
  import path61 from "node:path";
60610
60950
  function normalizeChunkLanguageId(languageId) {
60611
60951
  return chunkLanguageAliases2[languageId] ?? languageId;
@@ -60638,7 +60978,7 @@ async function handleChunkCommand(context) {
60638
60978
  }
60639
60979
  try {
60640
60980
  const filePath = path61.resolve(context.cwd(), parseSourceLocationInput(inputFilePath).file);
60641
- const source = await fsp32.readFile(filePath, "utf8");
60981
+ const source = await fsp33.readFile(filePath, "utf8");
60642
60982
  const ext = path61.extname(filePath).toLowerCase();
60643
60983
  let languageId = context.getOpt("--language");
60644
60984
  if (!languageId) {
@@ -61728,7 +62068,7 @@ var init_fileView = __esm({
61728
62068
  });
61729
62069
 
61730
62070
  // dist/documentLinks/check.js
61731
- import fsp33 from "node:fs/promises";
62071
+ import fsp34 from "node:fs/promises";
61732
62072
  import path64 from "node:path";
61733
62073
  async function checkMarkdownLinks(projectRoot, discovery) {
61734
62074
  const root = path64.resolve(projectRoot);
@@ -61736,14 +62076,14 @@ async function checkMarkdownLinks(projectRoot, discovery) {
61736
62076
  }
61737
62077
  async function checkMarkdownLinksInFiles(projectRoot, files) {
61738
62078
  const root = path64.resolve(projectRoot);
61739
- const realRoot = await fsp33.realpath(root);
62079
+ const realRoot = await fsp34.realpath(root);
61740
62080
  const markdownFileCandidates = await Promise.all(Array.from(files, async (file2) => {
61741
62081
  const absoluteFile = path64.resolve(root, file2);
61742
62082
  const relativeFile = toProjectRelativePath(root, absoluteFile);
61743
62083
  if (relativeFile !== null)
61744
62084
  return path64.resolve(realRoot, relativeFile);
61745
62085
  try {
61746
- return await fsp33.realpath(absoluteFile);
62086
+ return await fsp34.realpath(absoluteFile);
61747
62087
  } catch {
61748
62088
  return absoluteFile;
61749
62089
  }
@@ -61812,7 +62152,7 @@ async function recoverDisplayFileCasing(file2) {
61812
62152
  const dir = path64.dirname(file2);
61813
62153
  const base = path64.basename(file2);
61814
62154
  try {
61815
- const entries = await fsp33.readdir(dir);
62155
+ const entries = await fsp34.readdir(dir);
61816
62156
  const match = entries.find((entry) => entry.toLowerCase() === base.toLowerCase());
61817
62157
  return match ? path64.join(dir, match) : file2;
61818
62158
  } catch {
@@ -61886,8 +62226,8 @@ async function cachedTargetStatus(target, targetStatusByPath) {
61886
62226
  }
61887
62227
  async function readTargetStatus(target) {
61888
62228
  try {
61889
- const realPath = await fsp33.realpath(target);
61890
- const stats = await fsp33.stat(realPath);
62229
+ const realPath = await fsp34.realpath(target);
62230
+ const stats = await fsp34.stat(realPath);
61891
62231
  return { status: "found", isDirectory: stats.isDirectory(), realPath };
61892
62232
  } catch (error51) {
61893
62233
  if (isMissingPathError3(error51))
@@ -61908,7 +62248,7 @@ async function cachedMarkdownAnchors(target, anchorsByPath) {
61908
62248
  }
61909
62249
  async function readMarkdownFile(file2) {
61910
62250
  try {
61911
- return await fsp33.readFile(file2, "utf8");
62251
+ return await fsp34.readFile(file2, "utf8");
61912
62252
  } catch (error51) {
61913
62253
  if (error51 instanceof Error) {
61914
62254
  throw new Error(`Failed to read Markdown file ${normalizePath(file2)}: ${error51.message}`);
@@ -62959,7 +63299,7 @@ var init_reference_context = __esm({
62959
63299
 
62960
63300
  // dist/indexer/navigation-references.js
62961
63301
  function exportFromIdentifier(index, fileId, range, parsed) {
62962
- if (!isJsTsLanguage(parsed.sup.id))
63302
+ if (!parsed.sup.supportsExportFromReferences)
62963
63303
  return null;
62964
63304
  const startIndex = range.start.index;
62965
63305
  if (typeof startIndex !== "number")
@@ -63083,8 +63423,16 @@ async function collectVerifiedNamedNodeReferences(index, fileId, symbolName, exp
63083
63423
  if (maxVerified !== void 0 && maxVerified > 0 && verified.length >= maxVerified) {
63084
63424
  break;
63085
63425
  }
63086
- if (exportFromIdentifier(index, fileId, range, parsed)?.isExportFrom)
63087
- continue;
63426
+ const exportFrom = exportFromIdentifier(index, fileId, range, parsed);
63427
+ if (exportFrom?.entry) {
63428
+ const reexported = resolveExport(index, exportFrom.entry.fromModule, exportFrom.entry.sourceSpecifier);
63429
+ if (reexported?.kind === "resolved") {
63430
+ if (sameDef(reexported.def, expectedDef, index.languageExtensions)) {
63431
+ verified.push({ range, via: { reexport: true } });
63432
+ }
63433
+ continue;
63434
+ }
63435
+ }
63088
63436
  const resolved = await resolveDefinition({
63089
63437
  file: fileId,
63090
63438
  line: range.start.line,
@@ -63093,7 +63441,11 @@ async function collectVerifiedNamedNodeReferences(index, fileId, symbolName, exp
63093
63441
  if (resolved.status !== "ok" || !resolved.definition)
63094
63442
  continue;
63095
63443
  if (sameDef(resolved.definition, expectedDef, index.languageExtensions)) {
63096
- verified.push({ range, ...resolved.provenance ? { provenance: resolved.provenance } : {} });
63444
+ verified.push({
63445
+ range,
63446
+ ...exportFrom?.isExportFrom ? { via: { reexport: true } } : {},
63447
+ ...resolved.provenance ? { provenance: resolved.provenance } : {}
63448
+ });
63097
63449
  }
63098
63450
  }
63099
63451
  return verified;
@@ -63188,6 +63540,9 @@ function filesExportingDefinition(index, def, exportedNames) {
63188
63540
  break;
63189
63541
  }
63190
63542
  }
63543
+ if (moduleIndex.exports.some((entry) => entry.type === "reexport" && exportedNames.includes(entry.sourceSpecifier) && resolveExport(index, entry.fromModule, entry.sourceSpecifier)?.kind !== "resolved")) {
63544
+ files.set(fileIdentityKey(fileId), fileId);
63545
+ }
63191
63546
  }
63192
63547
  return [...files.values()];
63193
63548
  }
@@ -63227,7 +63582,13 @@ function getCachedReferenceCandidateFiles(index, def, exportedNames, hasGlobalNa
63227
63582
  return cached2;
63228
63583
  const candidates = /* @__PURE__ */ new Map();
63229
63584
  const candidateFileEntries = getIndexedReferenceCandidateFiles(index, def, exportedNames) ?? Array.from(index.byFile.values(), (module2) => module2.file);
63230
- const exportingFiles = new Set(filesExportingDefinition(index, def, exportedNames).map((file2) => fileIdentityKey(file2)));
63585
+ const exportingFileIds = filesExportingDefinition(index, def, exportedNames);
63586
+ const exportingFiles = new Set(exportingFileIds.map((file2) => fileIdentityKey(file2)));
63587
+ for (const fileId of exportingFileIds) {
63588
+ if (fileIdentityKey(fileId) !== fileIdentityKey(def.file)) {
63589
+ candidates.set(fileIdentityKey(fileId), fileId);
63590
+ }
63591
+ }
63231
63592
  for (const fileId of candidateFileEntries) {
63232
63593
  if (fileIdentityKey(fileId) === fileIdentityKey(def.file))
63233
63594
  continue;
@@ -63251,7 +63612,6 @@ var init_navigation_references = __esm({
63251
63612
  "dist/indexer/navigation-references.js"() {
63252
63613
  "use strict";
63253
63614
  init_languages();
63254
- init_js_family();
63255
63615
  init_paths();
63256
63616
  init_ast();
63257
63617
  init_parse_context();
@@ -63269,7 +63629,7 @@ var init_navigation_references = __esm({
63269
63629
  });
63270
63630
 
63271
63631
  // dist/sql/navigation.js
63272
- import fsp34 from "node:fs/promises";
63632
+ import fsp35 from "node:fs/promises";
63273
63633
  import path65 from "node:path";
63274
63634
  function isSqlFile2(filePath) {
63275
63635
  return path65.extname(filePath).toLowerCase() === ".sql";
@@ -63555,7 +63915,7 @@ async function sourceForFile(filePath, index) {
63555
63915
  if (cached2 !== void 0)
63556
63916
  return cached2;
63557
63917
  const parsed = index.parsed?.get(key);
63558
- const source = parsed ? parsed.source : await fsp34.readFile(filePath, "utf8");
63918
+ const source = parsed ? parsed.source : await fsp35.readFile(filePath, "utf8");
63559
63919
  cache.sourceByFile.set(key, source);
63560
63920
  return source;
63561
63921
  }
@@ -63713,6 +64073,30 @@ async function goToDefinition(index, req, parsedContext) {
63713
64073
  name = sliceText(declNameNode, source);
63714
64074
  }
63715
64075
  }
64076
+ if (node && sup.supportsExportFromReferences && index.projectRoot) {
64077
+ const exportFrom = exportFromIdentifier(index, file2, toRange(node), context);
64078
+ if (exportFrom?.entry) {
64079
+ let resolvedTarget;
64080
+ if (index.byFile.has(fileIdentityKey(exportFrom.entry.fromModule))) {
64081
+ resolvedTarget = exportFrom.entry.fromModule;
64082
+ } else {
64083
+ const { matchPath } = await loadNearestTsconfigFor(file2, index.projectRoot);
64084
+ resolvedTarget = await resolveImportSpecifier(index.projectRoot, file2, exportFrom.entry.moduleSpecifier ?? exportFrom.entry.fromModule, sup.id, {
64085
+ ...matchPath ? { matchPath } : {}
64086
+ });
64087
+ }
64088
+ if (typeof resolvedTarget === "string") {
64089
+ const hit = resolveExport(index, resolvedTarget, exportFrom.entry.sourceSpecifier);
64090
+ if (hit?.kind === "resolved") {
64091
+ return okGoToResult(index, hit.def, {
64092
+ via: { importedFrom: resolvedTarget, exportedName: exportFrom.entry.sourceSpecifier },
64093
+ resolution: "import",
64094
+ confidence: "high"
64095
+ });
64096
+ }
64097
+ }
64098
+ }
64099
+ }
63716
64100
  if (sup.supportsCrossModuleSymbols) {
63717
64101
  const scopeIndex = node.parent && isMemberAccessNode(sup, node.parent) ? getOrBuildScopeIndex(index, file2, source, sup, mod2, tree) : null;
63718
64102
  const memberAccessResult = await resolveMemberAccessDefinition({
@@ -63903,6 +64287,28 @@ async function findReferences(index, req, opts) {
63903
64287
  }
63904
64288
  return scopeIndex;
63905
64289
  };
64290
+ if (supportForFile(fileId, index.languageExtensions)?.supportsExportFromReferences) {
64291
+ for (const entry of module2.exports) {
64292
+ if (hasReachedMaxReferences())
64293
+ break;
64294
+ if (entry.type !== "reexport")
64295
+ continue;
64296
+ if (!exportedNameSet.has(entry.sourceSpecifier))
64297
+ continue;
64298
+ const resolved = resolveExport(index, entry.fromModule, entry.sourceSpecifier);
64299
+ if (resolved?.kind === "resolved" && !sameDef(resolved.def, def, index.languageExtensions))
64300
+ continue;
64301
+ const remainingReferences = maxReferences !== void 0 ? Math.max(0, maxReferences - refs.length) : void 0;
64302
+ const ranges = await collectVerifiedNamedNodeReferences(index, fileId, entry.sourceSpecifier, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences);
64303
+ for (const { range, provenance: provenance3, via } of ranges) {
64304
+ if (hasReachedMaxReferences())
64305
+ break;
64306
+ if (!via?.reexport)
64307
+ continue;
64308
+ pushRef({ file: fileId, range, via, ...provenance3 ? { provenance: provenance3 } : {} });
64309
+ }
64310
+ }
64311
+ }
63906
64312
  for (const imp of module2.imports) {
63907
64313
  if (hasReachedMaxReferences())
63908
64314
  break;
@@ -63938,10 +64344,15 @@ async function findReferences(index, req, opts) {
63938
64344
  }
63939
64345
  const remainingReferences = maxReferences !== void 0 ? Math.max(0, maxReferences - refs.length) : void 0;
63940
64346
  const ranges = await collectVerifiedNamedNodeReferences(index, fileId, exportedName, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences);
63941
- for (const { range, provenance: provenance3 } of ranges) {
64347
+ for (const { range, provenance: provenance3, via } of ranges) {
63942
64348
  if (hasReachedMaxReferences())
63943
64349
  break;
63944
- pushRef({ file: fileId, range, via: { import: imp }, ...provenance3 ? { provenance: provenance3 } : {} });
64350
+ pushRef({
64351
+ file: fileId,
64352
+ range,
64353
+ via: { import: imp, ...via ?? {} },
64354
+ ...provenance3 ? { provenance: provenance3 } : {}
64355
+ });
63945
64356
  }
63946
64357
  } else {
63947
64358
  let exported = exportedName;
@@ -63957,10 +64368,15 @@ async function findReferences(index, req, opts) {
63957
64368
  if (fileIdentityKey(targetFile) !== fileIdentityKey(definitionFile)) {
63958
64369
  const remainingReferences = maxReferences !== void 0 ? Math.max(0, maxReferences - refs.length) : void 0;
63959
64370
  const ranges = await collectVerifiedNamedNodeReferences(index, fileId, imp.local, def, (params, parsed2) => goToDefinition(index, params, parsed2), remainingReferences);
63960
- for (const { range, provenance: provenance3 } of ranges) {
64371
+ for (const { range, provenance: provenance3, via } of ranges) {
63961
64372
  if (hasReachedMaxReferences())
63962
64373
  break;
63963
- pushRef({ file: fileId, range, via: { import: imp }, ...provenance3 ? { provenance: provenance3 } : {} });
64374
+ pushRef({
64375
+ file: fileId,
64376
+ range,
64377
+ via: { import: imp, ...via ?? {} },
64378
+ ...provenance3 ? { provenance: provenance3 } : {}
64379
+ });
63964
64380
  }
63965
64381
  continue;
63966
64382
  }
@@ -63986,10 +64402,10 @@ async function findReferences(index, req, opts) {
63986
64402
  if (hasReachedMaxReferences())
63987
64403
  break;
63988
64404
  const ranges = await collectVerifiedNamedNodeReferences(index, fileId, candidateName, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences);
63989
- for (const { range, provenance: provenance3 } of ranges) {
64405
+ for (const { range, provenance: provenance3, via } of ranges) {
63990
64406
  if (hasReachedMaxReferences())
63991
64407
  break;
63992
- pushRef({ file: fileId, range, ...provenance3 ? { provenance: provenance3 } : {} });
64408
+ pushRef({ file: fileId, range, ...via ? { via } : {}, ...provenance3 ? { provenance: provenance3 } : {} });
63993
64409
  }
63994
64410
  }
63995
64411
  }
@@ -64004,10 +64420,10 @@ async function findReferences(index, req, opts) {
64004
64420
  continue;
64005
64421
  const remainingReferences = maxReferences !== void 0 ? Math.max(0, maxReferences - refs.length) : void 0;
64006
64422
  const ranges = await collectVerifiedNamedNodeReferences(index, fileId, def.localName, def, (params, parsed) => goToDefinition(index, params, parsed), remainingReferences);
64007
- for (const { range, provenance: provenance3 } of ranges) {
64423
+ for (const { range, provenance: provenance3, via } of ranges) {
64008
64424
  if (hasReachedMaxReferences())
64009
64425
  break;
64010
- pushRef({ file: fileId, range, ...provenance3 ? { provenance: provenance3 } : {} });
64426
+ pushRef({ file: fileId, range, ...via ? { via } : {}, ...provenance3 ? { provenance: provenance3 } : {} });
64011
64427
  }
64012
64428
  }
64013
64429
  }
@@ -65203,7 +65619,7 @@ var init_changes = __esm({
65203
65619
  });
65204
65620
 
65205
65621
  // dist/sql/review.js
65206
- import fsp35 from "node:fs/promises";
65622
+ import fsp36 from "node:fs/promises";
65207
65623
  import path67 from "node:path";
65208
65624
  function isSqlFile3(filePath) {
65209
65625
  return path67.extname(filePath).toLowerCase() === ".sql";
@@ -65218,7 +65634,7 @@ function sortEntries(left, right) {
65218
65634
  }
65219
65635
  async function readExistingFile(filePath) {
65220
65636
  try {
65221
- return await fsp35.readFile(filePath, "utf8");
65637
+ return await fsp36.readFile(filePath, "utf8");
65222
65638
  } catch {
65223
65639
  return null;
65224
65640
  }
@@ -68581,7 +68997,7 @@ var init_referenceCache = __esm({
68581
68997
  });
68582
68998
 
68583
68999
  // dist/review/summaries.js
68584
- import fsp36 from "node:fs/promises";
69000
+ import fsp37 from "node:fs/promises";
68585
69001
  import path70 from "node:path";
68586
69002
  import { performance as performance9 } from "node:perf_hooks";
68587
69003
  function relativePath2(root, file2) {
@@ -68843,7 +69259,7 @@ function sameRange(left, right) {
68843
69259
  }
68844
69260
  async function summarizeChangedFiles2(input) {
68845
69261
  const { projectRoot, index, changedFileList, diffHunksByFile, diffKindsByFile, diffChangesByFile, explicitFiles, existenceByFile, deletedSnapshots, loadSource: suppliedLoadSource, includeSymbolDetails, includeDiffContext, diffContextLines, maxCallsites, referenceConcurrency, diagnostics, reviewTimings } = input;
68846
- const readSource = suppliedLoadSource ?? (async (file2) => await fsp36.readFile(file2, "utf8"));
69262
+ const readSource = suppliedLoadSource ?? (async (file2) => await fsp37.readFile(file2, "utf8"));
68847
69263
  const sourceCache = /* @__PURE__ */ new Map();
68848
69264
  const loadSource = (file2) => {
68849
69265
  const key = fileIdentityKey(file2);
@@ -74768,9 +75184,9 @@ Usage:
74768
75184
  codegraph uninit --root <path> [--force] [--json | --pretty]
74769
75185
 
74770
75186
  State:
74771
- Lifecycle commands own only .codegraph/manifest.json metadata; a shared MCP server separately owns .codegraph/server.json and .codegraph/server.log. In a Git worktree, init and sync --init ensure the manifest is effectively ignored, appending .codegraph/ and .codegraph-cache/ to the resolved root's .gitignore only when needed; opt out with --no-update-gitignore.
74772
- A tracked manifest is left tracked with a warning. Uninit preserves server.json and server.log even with --force, so stop a shared server explicitly; it leaves the root .gitignore rule, and ordinary sync never changes ignore policy.
74773
- Init and sync may warm or update the disk cache under .codegraph-cache/index-v1/. Other commands do not depend on the manifest.
75187
+ Lifecycle commands own .codegraph/manifest.json metadata; a shared MCP server owns .codegraph/server.json and .codegraph/server.log. In a Git worktree, init and sync --init ensure the .codegraph/ directory is effectively ignored, appending .codegraph/ to the resolved root's .gitignore only when needed; opt out with --no-update-gitignore.
75188
+ A tracked manifest is left tracked with a warning. Uninit preserves server.json, server.log, and cache even with --force, so stop a shared server explicitly; it leaves the root .gitignore rule, and ordinary sync never changes ignore policy.
75189
+ Init and sync may warm or update the disk cache under .codegraph/cache/index-v1/. Existing .codegraph-cache/ directories migrate automatically on the next run. Other commands do not depend on the manifest.
74774
75190
  Positional paths and --root are alternatives for lifecycle commands; do not combine them.
74775
75191
  `;
74776
75192
  AFFECTED_HELP_TEXT = `codegraph affected - List tests likely affected by changed files
@@ -101464,7 +101880,7 @@ var init_dist4 = __esm({
101464
101880
  });
101465
101881
 
101466
101882
  // dist/mcp/http.js
101467
- import os9 from "node:os";
101883
+ import os10 from "node:os";
101468
101884
  function getRequestPath(request) {
101469
101885
  return new URL(request.url ?? "/", "http://127.0.0.1").pathname;
101470
101886
  }
@@ -101667,11 +102083,11 @@ function isWildcardBindHost(host) {
101667
102083
  }
101668
102084
  function localInterfaceHostnames() {
101669
102085
  const hosts = /* @__PURE__ */ new Set();
101670
- const hostname3 = os9.hostname().trim().toLowerCase();
102086
+ const hostname3 = os10.hostname().trim().toLowerCase();
101671
102087
  if (hostname3) {
101672
102088
  hosts.add(hostname3);
101673
102089
  }
101674
- const interfaces = os9.networkInterfaces();
102090
+ const interfaces = os10.networkInterfaces();
101675
102091
  for (const entries of Object.values(interfaces)) {
101676
102092
  for (const entry of entries ?? []) {
101677
102093
  if (entry.internal)
@@ -103790,7 +104206,7 @@ var graphDelta_exports = {};
103790
104206
  __export(graphDelta_exports, {
103791
104207
  handleGraphDeltaCommand: () => handleGraphDeltaCommand
103792
104208
  });
103793
- import fsp37 from "node:fs/promises";
104209
+ import fsp38 from "node:fs/promises";
103794
104210
  function formatGraphDeltaReport(delta) {
103795
104211
  const lines = [];
103796
104212
  lines.push("Graph delta");
@@ -103857,7 +104273,7 @@ async function handleGraphDeltaCommand(context) {
103857
104273
  const outputFile = outputArg ? normalizePath(resolveFilePathFromRoot(context.cwd(), outputArg)) : void 0;
103858
104274
  if (outputFile) {
103859
104275
  const output = context.hasFlag("--json") ? JSON.stringify(delta, null, 2) : formatGraphDeltaReport(delta);
103860
- await fsp37.writeFile(outputFile, `${output}
104276
+ await fsp38.writeFile(outputFile, `${output}
103861
104277
  `, "utf8");
103862
104278
  } else {
103863
104279
  writeCliOutput(context, delta, formatGraphDeltaReport);
@@ -104157,7 +104573,7 @@ var init_symbol_render = __esm({
104157
104573
  });
104158
104574
 
104159
104575
  // dist/sql/graph.js
104160
- import fsp38 from "node:fs/promises";
104576
+ import fsp39 from "node:fs/promises";
104161
104577
  function statementNodeId(fact) {
104162
104578
  return `sql:statement:${fact.filePath}:${fact.startLine}-${fact.endLine}`;
104163
104579
  }
@@ -104312,7 +104728,7 @@ function projectSqlFactsToGraph(facts) {
104312
104728
  };
104313
104729
  }
104314
104730
  async function buildSqlArtifactGraphFromFiles(files) {
104315
- const factGroups = await Promise.all(files.map(async (filePath) => extractSqlFactsFromSource(filePath, await fsp38.readFile(filePath, "utf8"))));
104731
+ const factGroups = await Promise.all(files.map(async (filePath) => extractSqlFactsFromSource(filePath, await fsp39.readFile(filePath, "utf8"))));
104316
104732
  return projectSqlFactsToGraph(factGroups.flat());
104317
104733
  }
104318
104734
  var init_graph = __esm({
@@ -104343,7 +104759,7 @@ __export(graph_exports, {
104343
104759
  handleGraphCommand: () => handleGraphCommand
104344
104760
  });
104345
104761
  import fs42 from "node:fs";
104346
- import fsp39 from "node:fs/promises";
104762
+ import fsp40 from "node:fs/promises";
104347
104763
  import path88 from "node:path";
104348
104764
  import { performance as performance12 } from "node:perf_hooks";
104349
104765
  function resolveGraphCacheMode(cache) {
@@ -104537,7 +104953,7 @@ async function handleGraphCommand(context) {
104537
104953
  };
104538
104954
  const writeOut = async (text) => {
104539
104955
  if (outputFile) {
104540
- await fsp39.writeFile(outputFile, `${text}
104956
+ await fsp40.writeFile(outputFile, `${text}
104541
104957
  `, "utf8");
104542
104958
  } else {
104543
104959
  context.writeStdoutLine(text);
@@ -105480,7 +105896,7 @@ var init_report3 = __esm({
105480
105896
  });
105481
105897
 
105482
105898
  // dist/drift/artifact.js
105483
- import fsp40 from "node:fs/promises";
105899
+ import fsp41 from "node:fs/promises";
105484
105900
  import path90 from "node:path";
105485
105901
  function readStringRecord2(value) {
105486
105902
  if (!isPlainRecord(value))
@@ -105504,7 +105920,7 @@ function parseManifest(value) {
105504
105920
  }
105505
105921
  async function readJson(filePath, label) {
105506
105922
  try {
105507
- return JSON.parse(await fsp40.readFile(filePath, "utf8"));
105923
+ return JSON.parse(await fsp41.readFile(filePath, "utf8"));
105508
105924
  } catch (error51) {
105509
105925
  if (error51 instanceof Error && "code" in error51 && error51.code === "ENOENT") {
105510
105926
  throw new Error(`Codegraph artifact ${label} is missing.`);
@@ -105638,8 +106054,8 @@ var init_artifact3 = __esm({
105638
106054
 
105639
106055
  // dist/drift/git.js
105640
106056
  import { execFile } from "node:child_process";
105641
- import fsp41 from "node:fs/promises";
105642
- import os10 from "node:os";
106057
+ import fsp42 from "node:fs/promises";
106058
+ import os11 from "node:os";
105643
106059
  import path91 from "node:path";
105644
106060
  import { promisify } from "node:util";
105645
106061
  function snapshotOptions(options) {
@@ -105659,7 +106075,7 @@ async function cleanupTempDir(dir) {
105659
106075
  if (!dir)
105660
106076
  return;
105661
106077
  try {
105662
- await fsp41.rm(dir, { recursive: true, force: true });
106078
+ await fsp42.rm(dir, { recursive: true, force: true });
105663
106079
  } catch {
105664
106080
  }
105665
106081
  }
@@ -105678,7 +106094,7 @@ async function materializeGitRef(root, ref, prefix) {
105678
106094
  }
105679
106095
  if (checkoutRef === void 0 || checkoutRef === "." || isGitWorktreeSentinel(checkoutRef))
105680
106096
  return { root };
105681
- const tempRoot = await fsp41.mkdtemp(path91.join(os10.tmpdir(), prefix));
106097
+ const tempRoot = await fsp42.mkdtemp(path91.join(os11.tmpdir(), prefix));
105682
106098
  try {
105683
106099
  await execFileAsync("git", ["clone", "--quiet", "--no-checkout", root, tempRoot], { env: process.env });
105684
106100
  const checkoutCommit = await resolveGitCommit(root, checkoutRef);
@@ -106697,7 +107113,7 @@ var init_navigation3 = __esm({
106697
107113
  });
106698
107114
 
106699
107115
  // dist/graphs/grep.js
106700
- import fsp42 from "node:fs/promises";
107116
+ import fsp43 from "node:fs/promises";
106701
107117
  async function* streamAstGrep(projectRoot, querySource, patterns = [
106702
107118
  "**/*.{ts,tsx,js,jsx,mts,cts,mjs,cjs,py,vue,svelte,go,java,cs,rb,rs,html,htm,css,scss,less,kt,kts,swift,zig,c,h,cc,cpp,cxx,c++,hpp,hh,hxx,ipp,tpp,inl}"
106703
107119
  ], opts) {
@@ -106755,7 +107171,7 @@ async function* streamTextGrep(projectRoot, patternSource, patterns = [
106755
107171
  break;
106756
107172
  let source;
106757
107173
  try {
106758
- source = await fsp42.readFile(file2, "utf8");
107174
+ source = await fsp43.readFile(file2, "utf8");
106759
107175
  } catch {
106760
107176
  continue;
106761
107177
  }