@lzehrung/codegraph 2.2.2 → 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.
package/dist/bin/cli.js CHANGED
@@ -4671,9 +4671,9 @@ var require_path = __commonJS({
4671
4671
  "use strict";
4672
4672
  Object.defineProperty(exports, "__esModule", { value: true });
4673
4673
  exports.convertPosixPathToPattern = exports.convertWindowsPathToPattern = exports.convertPathToPattern = exports.escapePosixPath = exports.escapeWindowsPath = exports.escape = exports.removeLeadingDotSegment = exports.makeAbsolute = exports.unixify = void 0;
4674
- var os11 = __require("os");
4674
+ var os12 = __require("os");
4675
4675
  var path99 = __require("path");
4676
- var IS_WINDOWS_PLATFORM = os11.platform() === "win32";
4676
+ var IS_WINDOWS_PLATFORM = os12.platform() === "win32";
4677
4677
  var LEADING_DOT_SEGMENT_CHARACTERS_COUNT = 2;
4678
4678
  var POSIX_UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()*?[\]{|}]|^!|[!+@](?=\()|\\(?![!()*+?@[\]{|}]))/g;
4679
4679
  var WINDOWS_UNESCAPED_GLOB_SYMBOLS_RE = /(\\?)([()[\]{}]|^!|[!+@](?=\())/g;
@@ -10191,8 +10191,8 @@ var require_settings4 = __commonJS({
10191
10191
  Object.defineProperty(exports, "__esModule", { value: true });
10192
10192
  exports.DEFAULT_FILE_SYSTEM_ADAPTER = void 0;
10193
10193
  var fs49 = __require("fs");
10194
- var os11 = __require("os");
10195
- var CPU_COUNT = Math.max(os11.cpus().length, 1);
10194
+ var os12 = __require("os");
10195
+ var CPU_COUNT = Math.max(os12.cpus().length, 1);
10196
10196
  exports.DEFAULT_FILE_SYSTEM_ADAPTER = {
10197
10197
  lstat: fs49.lstat,
10198
10198
  lstatSync: fs49.lstatSync,
@@ -13403,554 +13403,12 @@ var init_concurrency = __esm({
13403
13403
  }
13404
13404
  });
13405
13405
 
13406
- // dist/util/projectFiles.js
13407
- var projectFiles_exports = {};
13408
- __export(projectFiles_exports, {
13409
- DEFAULT_PROJECT_FILE_IGNORES: () => DEFAULT_PROJECT_FILE_IGNORES,
13410
- DEFAULT_PROJECT_MANIFESTS: () => DEFAULT_PROJECT_MANIFESTS,
13411
- DEFAULT_PROJECT_PATTERNS: () => DEFAULT_PROJECT_PATTERNS,
13412
- createDiscoveredFileMatcher: () => createDiscoveredFileMatcher,
13413
- discoverProjectFiles: () => discoverProjectFiles,
13414
- filterRealPathsWithinRoot: () => filterRealPathsWithinRoot,
13415
- isRelativePathInside: () => isRelativePathInside,
13416
- listProjectFiles: () => listProjectFiles,
13417
- matchesDiscoveryGlob: () => matchesDiscoveryGlob,
13418
- translateGlobRootIgnoreGlobsForScanRoot: () => translateGlobRootIgnoreGlobsForScanRoot
13419
- });
13420
- import fs6 from "node:fs";
13421
- import fsp4 from "node:fs/promises";
13422
- import path8 from "node:path";
13423
- function isPresent(value) {
13424
- return value !== void 0;
13425
- }
13426
- function normalizeGlobPattern(globPattern) {
13427
- return globPattern.trim().replace(/\\/g, "/");
13428
- }
13429
- function isIgnoreGlobReopenedByIncludes(ignoreGlob, includeGlobs) {
13430
- const literalSegments = ignoreGlob.split("/").filter((segment) => segment && !segment.includes("*"));
13431
- if (!literalSegments.length)
13432
- return false;
13433
- return includeGlobs.some((includeGlob) => {
13434
- const includeSegments = includeGlob.split("/");
13435
- return literalSegments.every((segment) => includeSegments.includes(segment));
13436
- });
13437
- }
13438
- function isLocationIndependentGlob(globPattern) {
13439
- return globPattern.startsWith("**/");
13440
- }
13441
- function translateGlobRootIgnoreGlobsForScanRoot(scanRoot, globRoot, ignoreGlobs) {
13442
- const relativeScanRoot = normalizePath(path8.relative(globRoot, scanRoot));
13443
- if (!relativeScanRoot || !isRelativePathInside(relativeScanRoot)) {
13444
- return ignoreGlobs.map(normalizeGlobPattern).filter(Boolean);
13445
- }
13446
- const rootPrefix = `${relativeScanRoot}/`;
13447
- return ignoreGlobs.map(normalizeGlobPattern).filter(Boolean).map((globPattern) => {
13448
- const rootRelativePattern = globPattern.startsWith("/") ? globPattern.slice(1) : globPattern;
13449
- if (isLocationIndependentGlob(rootRelativePattern))
13450
- return rootRelativePattern;
13451
- if (rootRelativePattern === relativeScanRoot || rootRelativePattern === `${relativeScanRoot}/**`)
13452
- return "**";
13453
- if (rootRelativePattern.startsWith(rootPrefix))
13454
- return rootRelativePattern.slice(rootPrefix.length) || "**";
13455
- return void 0;
13456
- }).filter(isPresent);
13457
- }
13458
- function stripGitignoreTrailingSpaces(line) {
13459
- let end = line.length;
13460
- while (end > 0 && line[end - 1] === " ") {
13461
- let slashCount = 0;
13462
- for (let i = end - 2; i >= 0 && line[i] === "\\"; i -= 1) {
13463
- slashCount += 1;
13464
- }
13465
- if (slashCount % 2 === 1) {
13466
- break;
13467
- }
13468
- end -= 1;
13469
- }
13470
- return line.slice(0, end);
13471
- }
13472
- function parseGitignoreRule(baseDir, rawLine) {
13473
- const trimmedLine = stripGitignoreTrailingSpaces(rawLine);
13474
- if (!trimmedLine)
13475
- return null;
13476
- if (trimmedLine.startsWith("#"))
13477
- return null;
13478
- const negated = trimmedLine.startsWith("!");
13479
- let pattern = negated ? trimmedLine.slice(1) : trimmedLine;
13480
- if (!pattern || pattern === "/")
13481
- return null;
13482
- const dirOnly = pattern.endsWith("/");
13483
- if (dirOnly) {
13484
- pattern = pattern.slice(0, -1);
13485
- }
13486
- if (pattern.startsWith("/")) {
13487
- pattern = pattern.slice(1);
13488
- }
13489
- pattern = pattern.replace(/^\\([#!])/, "$1").replace(/\\/g, "/");
13490
- if (!pattern)
13491
- return null;
13492
- const anchored = trimmedLine.startsWith("/") || negated && trimmedLine.startsWith("!/");
13493
- const baseMatcherPattern = anchored || pattern.includes("/") ? pattern : `**/${pattern}`;
13494
- const matcherPattern = dirOnly ? `${baseMatcherPattern}/**` : [baseMatcherPattern, `${baseMatcherPattern}/**`];
13495
- const matches = (0, import_picomatch.default)(matcherPattern, { dot: true });
13496
- return {
13497
- baseDir: normalizePath(baseDir),
13498
- negated,
13499
- dirOnly,
13500
- matches
13501
- };
13502
- }
13503
- async function loadGitignoreRules(projectRoot) {
13504
- const gitignoreFiles = await (0, import_fast_glob2.default)(["**/.gitignore"], {
13505
- cwd: projectRoot,
13506
- absolute: true,
13507
- dot: true,
13508
- followSymbolicLinks: false,
13509
- ignore: DEFAULT_PROJECT_FILE_IGNORES
13510
- });
13511
- gitignoreFiles.sort((left, right) => normalizePath(left).localeCompare(normalizePath(right)));
13512
- const rules = [];
13513
- for (const gitignoreFile of gitignoreFiles) {
13514
- if (isIgnoredByGitignore(gitignoreFile, rules)) {
13515
- continue;
13516
- }
13517
- try {
13518
- const baseDir = path8.dirname(gitignoreFile);
13519
- const raw = await fsp4.readFile(gitignoreFile, "utf8");
13520
- rules.push(...raw.split(/\r?\n/).map((line) => parseGitignoreRule(baseDir, line)).filter((rule) => !!rule));
13521
- } catch {
13522
- continue;
13523
- }
13524
- }
13525
- return rules;
13526
- }
13527
- async function loadGitignoreRulesForRootAliases(projectRoot) {
13528
- const roots = [projectRoot];
13529
- const realRoot = await fsp4.realpath(projectRoot);
13530
- if (normalizePath(realRoot) !== normalizePath(projectRoot)) {
13531
- roots.push(realRoot);
13532
- }
13533
- const rules = [];
13534
- for (const root of roots) {
13535
- rules.push(...await loadGitignoreRules(root));
13536
- }
13537
- return rules;
13538
- }
13539
- function isIgnoredByGitignore(absolutePath, rules) {
13540
- let ignored = false;
13541
- for (const rule of rules) {
13542
- const relativePath3 = path8.relative(rule.baseDir, absolutePath);
13543
- if (!isRelativePathInside(relativePath3)) {
13544
- continue;
13545
- }
13546
- const normalizedRelativePath2 = normalizePath(relativePath3);
13547
- if (rule.dirOnly && !normalizedRelativePath2.includes("/")) {
13548
- continue;
13549
- }
13550
- if (rule.matches(normalizedRelativePath2)) {
13551
- ignored = !rule.negated;
13552
- }
13553
- }
13554
- return ignored;
13555
- }
13556
- async function ensureDirectoryReadable(directoryPath, label) {
13557
- const resolvedPath = path8.resolve(directoryPath);
13558
- let stats;
13559
- try {
13560
- stats = await fsp4.stat(resolvedPath);
13561
- } catch (error51) {
13562
- throw new Error(`${label} does not exist or is not readable: ${resolvedPath} (${stringifyUnknown(error51)})`);
13563
- }
13564
- if (!stats.isDirectory()) {
13565
- throw new Error(`${label} is not a directory: ${resolvedPath}`);
13566
- }
13567
- return resolvedPath;
13568
- }
13569
- async function listProjectFiles(projectRoot, patterns = DEFAULT_PROJECT_PATTERNS, options) {
13570
- const root = await ensureDirectoryReadable(projectRoot, "Project root");
13571
- const globRoot = options?.globRoot ? await ensureDirectoryReadable(options.globRoot, "Glob root") : root;
13572
- const includeGlobs = (options?.includeGlobs ?? []).map(normalizeGlobPattern).filter(Boolean);
13573
- const includeMatchers = includeGlobs.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
13574
- const userIgnoreGlobs = (options?.ignoreGlobs ?? []).map(normalizeGlobPattern).filter(Boolean);
13575
- const userIgnoreMatchers = userIgnoreGlobs.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
13576
- const defaultIgnoreMatchers = DEFAULT_PROJECT_FILE_IGNORES.map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
13577
- const patternMatchers = patterns.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
13578
- const translatedUserIgnoreGlobs = translateGlobRootIgnoreGlobsForScanRoot(root, globRoot, userIgnoreGlobs);
13579
- const fastGlobIgnoreGlobs = [...DEFAULT_PROJECT_FILE_IGNORES, ...translatedUserIgnoreGlobs];
13580
- const symlinkProbeIgnoreGlobs = includeGlobs.length ? [
13581
- ...translatedUserIgnoreGlobs,
13582
- ...DEFAULT_PROJECT_FILE_IGNORES.filter((ignoreGlob) => !isIgnoreGlobReopenedByIncludes(ignoreGlob, includeGlobs))
13583
- ] : fastGlobIgnoreGlobs;
13584
- try {
13585
- const useGitignore = options?.useGitignore ?? true;
13586
- const realRoot = await fsp4.realpath(root);
13587
- const gitignoreRules = !useGitignore ? [] : await loadGitignoreRulesForRootAliases(options?.gitignoreRoot ? await ensureDirectoryReadable(options.gitignoreRoot, "Gitignore root") : root);
13588
- const files = await (0, import_fast_glob2.default)(patterns, {
13589
- cwd: root,
13590
- absolute: true,
13591
- dot: true,
13592
- followSymbolicLinks: false,
13593
- ignore: fastGlobIgnoreGlobs
13594
- });
13595
- const includedOverrideFiles = includeGlobs.length ? await (0, import_fast_glob2.default)(translateGlobRootIgnoreGlobsForScanRoot(root, globRoot, includeGlobs), {
13596
- cwd: root,
13597
- absolute: true,
13598
- dot: true,
13599
- followSymbolicLinks: false,
13600
- ignore: translatedUserIgnoreGlobs
13601
- }) : [];
13602
- const symlinkOptions = {
13603
- globRoot,
13604
- ...options?.knownSymlinkDirectories !== void 0 ? { knownSymlinkDirectories: options.knownSymlinkDirectories } : {},
13605
- ...options?.onSymlinkDirectoriesDiscovered ? { onSymlinkDirectoriesDiscovered: options.onSymlinkDirectoriesDiscovered } : {}
13606
- };
13607
- const safeSymlinkDirectories = await resolveSafeSymlinkDirectories(root, realRoot, symlinkProbeIgnoreGlobs, symlinkOptions);
13608
- const linkedFiles = await listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, fastGlobIgnoreGlobs, {
13609
- ...symlinkOptions,
13610
- filterIgnoreGlobs: [...DEFAULT_PROJECT_FILE_IGNORES, ...userIgnoreGlobs],
13611
- resolvedSafeSymlinkDirectories: safeSymlinkDirectories
13612
- });
13613
- const linkedOverrideFiles = includeGlobs.length === 0 ? [] : await listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, translatedUserIgnoreGlobs, {
13614
- globRoot,
13615
- filterIgnoreGlobs: userIgnoreGlobs,
13616
- resolvedSafeSymlinkDirectories: safeSymlinkDirectories
13617
- });
13618
- const rootSafeFiles = await filterRealPathsWithinRootEntries([...files, ...includedOverrideFiles, ...linkedFiles, ...linkedOverrideFiles], realRoot);
13619
- const seen = /* @__PURE__ */ new Set();
13620
- return rootSafeFiles.map(({ path: filePath, realPath }) => ({ filePath: normalizePath(filePath), realPath })).filter(({ filePath, realPath }) => {
13621
- if (seen.has(filePath))
13622
- return false;
13623
- seen.add(filePath);
13624
- const rootRelative = normalizePath(path8.relative(root, filePath));
13625
- if (!isRelativePathInside(rootRelative))
13626
- return false;
13627
- if (!patternMatchers.some((matcher) => matcher(rootRelative)))
13628
- return false;
13629
- const matchesInclude = !includeMatchers.length || includeMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher));
13630
- if (!matchesInclude)
13631
- return false;
13632
- if (userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(filePath, globRoot, matcher))) {
13633
- return false;
13634
- }
13635
- const ignoredByDefault = defaultIgnoreMatchers.some((matcher) => matcher(rootRelative));
13636
- if (ignoredByDefault && !(includeMatchers.length > 0 && matchesInclude)) {
13637
- return false;
13638
- }
13639
- return !isIgnoredByGitignore(filePath, gitignoreRules) && !isIgnoredByGitignore(realPath, gitignoreRules);
13640
- }).map(({ filePath }) => filePath);
13641
- } catch (error51) {
13642
- logWithLevel(options?.logLevel, "debug", `listProjectFiles failed for ${root}: ${stringifyUnknown(error51)}`);
13643
- throw new Error(`Failed to list files in ${root}: ${stringifyUnknown(error51)}`);
13644
- }
13645
- }
13646
- function createDiscoveredFileMatcher(root, globRoot, patterns, options) {
13647
- const patternMatchers = patterns.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
13648
- const defaultIgnoreMatchers = DEFAULT_PROJECT_FILE_IGNORES.map((pattern) => (0, import_picomatch.default)(normalizeGlobPattern(pattern), { dot: true }));
13649
- const includeMatchers = (options?.includeGlobs ?? []).map(normalizeGlobPattern).filter(Boolean).map((pattern) => (0, import_picomatch.default)(pattern, { dot: true }));
13650
- const userIgnoreMatchers = (options?.ignoreGlobs ?? []).map(normalizeGlobPattern).filter(Boolean).map((pattern) => (0, import_picomatch.default)(pattern, { dot: true }));
13651
- return (absolutePath) => {
13652
- const rootRelative = path8.relative(root, absolutePath);
13653
- if (!isRelativePathInside(rootRelative))
13654
- return false;
13655
- const normalizedRootRelative = normalizePath(rootRelative);
13656
- if (!patternMatchers.some((matcher) => matcher(normalizedRootRelative)))
13657
- return false;
13658
- const matchesInclude = !includeMatchers.length || includeMatchers.some((matcher) => matchesDiscoveryGlob(absolutePath, globRoot, matcher));
13659
- if (!matchesInclude)
13660
- return false;
13661
- if (defaultIgnoreMatchers.some((matcher) => matcher(normalizedRootRelative))) {
13662
- if (!(includeMatchers.length > 0 && matchesInclude))
13663
- return false;
13664
- }
13665
- return !userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(absolutePath, globRoot, matcher));
13666
- };
13667
- }
13668
- async function isSafeSymlinkDirectory(root, linkPath, realRoot) {
13669
- try {
13670
- if (!isRelativePathInside(path8.relative(root, linkPath)))
13671
- return false;
13672
- const [linkStats, realPath, targetStats] = await Promise.all([
13673
- fsp4.lstat(linkPath),
13674
- fsp4.realpath(linkPath),
13675
- fsp4.stat(linkPath)
13676
- ]);
13677
- if (!linkStats.isSymbolicLink())
13678
- return false;
13679
- if (!targetStats.isDirectory())
13680
- return false;
13681
- if (!isFilePathWithinRoot(realRoot, realPath))
13682
- return false;
13683
- return normalizePath(realPath) !== normalizePath(realRoot);
13684
- } catch {
13685
- return false;
13686
- }
13687
- }
13688
- async function resolveSafeSymlinkDirectories(root, realRoot, ignore, options) {
13689
- if (options.knownSymlinkDirectories !== void 0) {
13690
- const verified = await mapLimitSemaphore(Array.from(new Set(options.knownSymlinkDirectories)), REALPATH_FILTER_CONCURRENCY, async (linkPath) => await isSafeSymlinkDirectory(root, linkPath, realRoot) ? linkPath : null);
13691
- const resolved = verified.filter((entry) => entry !== null);
13692
- options.onSymlinkDirectoriesDiscovered?.(resolved);
13693
- return resolved;
13694
- }
13695
- const entries = await (0, import_fast_glob2.default)(["**/*"], {
13696
- cwd: root,
13697
- absolute: true,
13698
- dot: true,
13699
- onlyFiles: false,
13700
- followSymbolicLinks: false,
13701
- objectMode: true,
13702
- ignore
13703
- });
13704
- 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);
13705
- const discovered = candidates.filter((entry) => entry !== null);
13706
- options.onSymlinkDirectoriesDiscovered?.(discovered);
13707
- return discovered;
13708
- }
13709
- async function listEntriesFromSafeSymlinkDirectories(root, realRoot, patterns, ignore, options = {}) {
13710
- const globRoot = options.globRoot ?? root;
13711
- const filterIgnoreGlobs = options.filterIgnoreGlobs ?? ignore;
13712
- const rootRelativeIgnoreMatchers = filterIgnoreGlobs.map(normalizeGlobPattern).filter(Boolean).map((globPattern) => (0, import_picomatch.default)(globPattern, { dot: true }));
13713
- const locationIndependentIgnores = ignore.map(normalizeGlobPattern).filter(isLocationIndependentGlob);
13714
- const safeSymlinkDirectories = options.resolvedSafeSymlinkDirectories ?? await resolveSafeSymlinkDirectories(root, realRoot, ignore, options);
13715
- if (!safeSymlinkDirectories.length)
13716
- return [];
13717
- const filesByPath = /* @__PURE__ */ new Map();
13718
- const filesByDirectory = await mapLimitSemaphore(Array.from(safeSymlinkDirectories), REALPATH_FILTER_CONCURRENCY, async (directory) => (await (0, import_fast_glob2.default)(patterns, {
13719
- cwd: directory,
13720
- absolute: true,
13721
- dot: true,
13722
- followSymbolicLinks: false,
13723
- ignore: locationIndependentIgnores,
13724
- ...options.onlyFiles !== void 0 ? { onlyFiles: options.onlyFiles } : {},
13725
- ...options.markDirectories !== void 0 ? { markDirectories: options.markDirectories } : {}
13726
- })).filter((filePath) => {
13727
- const cleanPath = filePath.endsWith("/") ? filePath.slice(0, -1) : filePath;
13728
- return !rootRelativeIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(cleanPath, globRoot, matcher));
13729
- }));
13730
- for (const files of filesByDirectory) {
13731
- for (const file2 of files) {
13732
- filesByPath.set(normalizePath(file2), file2);
13733
- }
13734
- }
13735
- return [...filesByPath.values()];
13736
- }
13737
- async function filterRealPathsWithinRootEntries(paths, realRoot) {
13738
- const filtered = await mapLimitSemaphore(paths, REALPATH_FILTER_CONCURRENCY, async (filePath) => {
13739
- try {
13740
- const realPath = await fsp4.realpath(filePath);
13741
- return isFilePathWithinRoot(realRoot, realPath) ? { path: filePath, realPath } : null;
13742
- } catch {
13743
- return null;
13744
- }
13745
- });
13746
- return filtered.filter((entry) => entry !== null);
13747
- }
13748
- async function filterRealPathsWithinRoot(paths, realRoot) {
13749
- const entries = await filterRealPathsWithinRootEntries(paths, realRoot);
13750
- return entries.map((entry) => entry.path);
13751
- }
13752
- function toProjectGlob(pattern) {
13753
- return pattern.startsWith("**/") ? pattern : `**/${pattern}`;
13754
- }
13755
- async function buildProjectFileInfo(def, filePath) {
13756
- const normalizedPath = normalizePath(filePath);
13757
- const projectRoot = normalizePath(path8.dirname(filePath));
13758
- let name = null;
13759
- if (def.parseName && def.kind === "file") {
13760
- try {
13761
- const raw = await fsp4.readFile(filePath, "utf8");
13762
- name = def.parseName(raw, filePath);
13763
- } catch {
13764
- name = null;
13765
- }
13766
- }
13767
- if (!name && def.nameFromPath) {
13768
- if (def.nameFromPath === "file") {
13769
- name = trimToNull(path8.basename(filePath, path8.extname(filePath)));
13770
- } else {
13771
- name = trimToNull(path8.basename(projectRoot));
13772
- }
13773
- }
13774
- return {
13775
- path: normalizedPath,
13776
- kind: def.kind,
13777
- type: def.type,
13778
- role: def.role,
13779
- projectRoot,
13780
- ...name ? { name } : {}
13781
- };
13782
- }
13783
- async function discoverProjectFiles(projectRoot, options) {
13784
- const root = await ensureDirectoryReadable(projectRoot, "Project root");
13785
- try {
13786
- const realRoot = await fsp4.realpath(root);
13787
- const allPatterns = PROJECT_FILE_DEFINITIONS.flatMap((def) => def.patterns.map(toProjectGlob));
13788
- const matches = await (0, import_fast_glob2.default)(allPatterns, {
13789
- cwd: root,
13790
- absolute: true,
13791
- dot: true,
13792
- followSymbolicLinks: false,
13793
- ignore: DEFAULT_PROJECT_FILE_IGNORES,
13794
- markDirectories: true,
13795
- onlyFiles: false
13796
- });
13797
- const linkedMatches = await listEntriesFromSafeSymlinkDirectories(root, realRoot, allPatterns, DEFAULT_PROJECT_FILE_IGNORES, {
13798
- markDirectories: true,
13799
- onlyFiles: false,
13800
- ...options?.knownSymlinkDirectories !== void 0 ? { knownSymlinkDirectories: options.knownSymlinkDirectories } : {},
13801
- ...options?.onSymlinkDirectoriesDiscovered ? { onSymlinkDirectoriesDiscovered: options.onSymlinkDirectoriesDiscovered } : {}
13802
- });
13803
- const rootSafeMatches = await filterRealPathsWithinRoot([...matches, ...linkedMatches].map((match) => match.endsWith("/") ? match.slice(0, -1) : match), realRoot);
13804
- const projectFileDefinitionMatchers = PROJECT_FILE_DEFINITIONS.map((definition) => definition.patterns.map((pattern) => pattern.includes("*") || pattern.includes("?") ? new RegExp("^" + pattern.replace(/\./g, "\\.").replace(/\*/g, ".*") + "$") : void 0));
13805
- const entries = [];
13806
- const matchTasks = rootSafeMatches.map(async (cleanMatch) => {
13807
- const stats = await fsp4.stat(cleanMatch);
13808
- const isDir = stats.isDirectory();
13809
- const fileName = path8.basename(cleanMatch);
13810
- for (let definitionIndex = 0; definitionIndex < PROJECT_FILE_DEFINITIONS.length; definitionIndex++) {
13811
- const def = PROJECT_FILE_DEFINITIONS[definitionIndex];
13812
- if (isDir && def.kind !== "dir")
13813
- continue;
13814
- if (!isDir && def.kind !== "file")
13815
- continue;
13816
- const matchesPattern = def.patterns.some((pattern, patternIndex) => {
13817
- const matcher = projectFileDefinitionMatchers[definitionIndex][patternIndex];
13818
- return matcher ? matcher.test(fileName) : pattern === fileName;
13819
- });
13820
- if (matchesPattern) {
13821
- entries.push(await buildProjectFileInfo(def, cleanMatch));
13822
- }
13823
- }
13824
- });
13825
- await Promise.all(matchTasks);
13826
- const byKey2 = /* @__PURE__ */ new Map();
13827
- for (const entry of entries) {
13828
- const key = `${entry.path}::${entry.type}::${entry.role}`;
13829
- const existing = byKey2.get(key);
13830
- if (!existing || !existing.name && entry.name) {
13831
- byKey2.set(key, entry);
13832
- }
13833
- }
13834
- return Array.from(byKey2.values()).sort((a, b) => {
13835
- if (a.path === b.path)
13836
- return a.type.localeCompare(b.type);
13837
- return a.path.localeCompare(b.path);
13838
- });
13839
- } catch (error51) {
13840
- logWithLevel(options?.logLevel, "debug", `discoverProjectFiles failed for ${root}: ${stringifyUnknown(error51)}`);
13841
- throw new Error(`Failed to discover project files in ${root}: ${stringifyUnknown(error51)}`);
13842
- }
13843
- }
13844
- var import_fast_glob2, import_picomatch, DEFAULT_PROJECT_FILE_IGNORES, DEFAULT_PROJECT_MANIFESTS, DEFAULT_PROJECT_PATTERNS, REALPATH_FILTER_CONCURRENCY;
13845
- var init_projectFiles = __esm({
13846
- "dist/util/projectFiles.js"() {
13847
- "use strict";
13848
- import_fast_glob2 = __toESM(require_out4(), 1);
13849
- import_picomatch = __toESM(require_picomatch4(), 1);
13850
- init_logging();
13851
- init_ast();
13852
- init_paths();
13853
- init_discoveryPath();
13854
- init_discoveryPath();
13855
- init_definitions();
13856
- init_parsers();
13857
- init_concurrency();
13858
- DEFAULT_PROJECT_FILE_IGNORES = [
13859
- "**/node_modules/**",
13860
- "**/.git/**",
13861
- // Never treat Codegraph's own on-disk state as project input. `.codegraph/` is the
13862
- // lifecycle manifest root; `.codegraph-cache/` is the indexer disk cache. Either can
13863
- // contain JSON/markdown/source-looking files that would otherwise match discovery
13864
- // patterns and create perpetual "new untracked file" / reindex churn.
13865
- "**/.codegraph/**",
13866
- "**/.codegraph-cache/**",
13867
- "**/dist/**",
13868
- "**/build/**",
13869
- "**/target/**",
13870
- "**/.venv/**",
13871
- "**/venv/**",
13872
- "**/site-packages/**",
13873
- "**/__pycache__/**",
13874
- "**/vendor/bundle/**",
13875
- "**/.build/**",
13876
- "**/Pods/**"
13877
- ];
13878
- DEFAULT_PROJECT_MANIFESTS = [
13879
- "package.json",
13880
- "package-lock.json",
13881
- "pnpm-lock.yaml",
13882
- "yarn.lock",
13883
- "bun.lockb",
13884
- "pnpm-workspace.yaml",
13885
- "lerna.json",
13886
- "nx.json",
13887
- "turbo.json",
13888
- "tsconfig.json",
13889
- "jsconfig.json",
13890
- "requirements.txt",
13891
- "requirements.in",
13892
- "pyproject.toml",
13893
- "Pipfile",
13894
- "Pipfile.lock",
13895
- "poetry.lock",
13896
- "setup.py",
13897
- "setup.cfg",
13898
- "Cargo.toml",
13899
- "Cargo.lock",
13900
- "rust-toolchain",
13901
- "rust-toolchain.toml",
13902
- "go.mod",
13903
- "go.sum",
13904
- "go.work",
13905
- "Gemfile",
13906
- "Gemfile.lock",
13907
- "*.gemspec",
13908
- "pom.xml",
13909
- "mvnw",
13910
- "build.gradle",
13911
- "build.gradle.kts",
13912
- "settings.gradle",
13913
- "settings.gradle.kts",
13914
- "gradle.properties",
13915
- "gradlew",
13916
- "*.csproj",
13917
- "*.fsproj",
13918
- "*.vbproj",
13919
- "*.sln",
13920
- "Directory.Build.props",
13921
- "Directory.Build.targets",
13922
- "global.json",
13923
- "composer.json",
13924
- "composer.lock",
13925
- "Package.swift",
13926
- "Package.resolved",
13927
- "CMakeLists.txt",
13928
- "CMakePresets.json",
13929
- "CMakeUserPresets.json",
13930
- "Makefile",
13931
- "makefile",
13932
- "GNUmakefile",
13933
- "configure.ac",
13934
- "configure.in",
13935
- "meson.build",
13936
- "meson_options.txt",
13937
- "conanfile.txt",
13938
- "conanfile.py",
13939
- "vcpkg.json"
13940
- ];
13941
- DEFAULT_PROJECT_PATTERNS = [
13942
- "**/*.{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}",
13943
- ...DEFAULT_PROJECT_MANIFESTS.map((name) => `**/${name}`)
13944
- ];
13945
- REALPATH_FILTER_CONCURRENCY = 64;
13946
- }
13947
- });
13948
-
13949
13406
  // dist/util/git.js
13950
13407
  var git_exports = {};
13951
13408
  __export(git_exports, {
13952
13409
  DEFAULT_GIT_TIMEOUT_MS: () => DEFAULT_GIT_TIMEOUT_MS,
13953
13410
  assertSafeRevision: () => assertSafeRevision,
13411
+ clearGitDiscoveryCacheForTests: () => clearGitDiscoveryCacheForTests,
13954
13412
  clearGitRepositoryCheckCacheForTests: () => clearGitRepositoryCheckCacheForTests,
13955
13413
  decodeGitPath: () => decodeGitPath,
13956
13414
  getGitBlobHashes: () => getGitBlobHashes,
@@ -13960,16 +13418,22 @@ __export(git_exports, {
13960
13418
  isGitIndexSentinel: () => isGitIndexSentinel,
13961
13419
  isGitPathIgnored: () => isGitPathIgnored,
13962
13420
  isGitPathTracked: () => isGitPathTracked,
13421
+ isGitProjectRootIgnored: () => isGitProjectRootIgnored,
13963
13422
  isGitRepo: () => isGitRepo,
13964
13423
  isGitWorktreeSentinel: () => isGitWorktreeSentinel,
13965
13424
  listChangedFiles: () => listChangedFiles,
13425
+ listGitExcludeFiles: () => listGitExcludeFiles,
13426
+ listGitSubmoduleDirectories: () => listGitSubmoduleDirectories,
13427
+ listTrackedFiles: () => listTrackedFiles,
13966
13428
  listUntrackedFiles: () => listUntrackedFiles,
13967
13429
  runGit: () => runGit,
13968
13430
  setGitExecutableForTests: () => setGitExecutableForTests
13969
13431
  });
13970
13432
  import { spawn } from "node:child_process";
13971
13433
  import { StringDecoder } from "node:string_decoder";
13972
- import path9 from "node:path";
13434
+ import fsp4 from "node:fs/promises";
13435
+ import os2 from "node:os";
13436
+ import path8 from "node:path";
13973
13437
  function decodeGitPath(rawPath) {
13974
13438
  if (!rawPath.startsWith('"') || !rawPath.endsWith('"')) {
13975
13439
  return rawPath;
@@ -14013,6 +13477,10 @@ function setGitExecutableForTests(executable) {
14013
13477
  function clearGitRepositoryCheckCacheForTests() {
14014
13478
  gitRepositoryChecks.clear();
14015
13479
  }
13480
+ function clearGitDiscoveryCacheForTests() {
13481
+ gitDiscoveryRootIgnoredChecks.clear();
13482
+ gitExcludeFileChecks.clear();
13483
+ }
14016
13484
  function resolveGitTimeoutMs(timeoutMs) {
14017
13485
  if (typeof timeoutMs === "number" && Number.isFinite(timeoutMs) && timeoutMs > 0) {
14018
13486
  return Math.floor(timeoutMs);
@@ -14223,7 +13691,7 @@ async function getGitHead(projectRoot) {
14223
13691
  }
14224
13692
  }
14225
13693
  async function isGitRepo(projectRoot) {
14226
- const resolvedRoot = path9.resolve(projectRoot);
13694
+ const resolvedRoot = path8.resolve(projectRoot);
14227
13695
  const cached2 = gitRepositoryChecks.get(resolvedRoot);
14228
13696
  if (cached2)
14229
13697
  return await cached2;
@@ -14244,6 +13712,15 @@ async function isGitPathTracked(projectRoot, file2) {
14244
13712
  async function isGitPathIgnored(projectRoot, file2) {
14245
13713
  return await runGitPathPredicate(projectRoot, ["check-ignore", "--quiet", "--no-index", "--", normalizePath(file2)]);
14246
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
+ }
14247
13724
  async function runGitPathPredicate(projectRoot, args) {
14248
13725
  try {
14249
13726
  await runGit(projectRoot, args);
@@ -14252,180 +13729,930 @@ async function runGitPathPredicate(projectRoot, args) {
14252
13729
  const message = error51 instanceof Error ? error51.message : String(error51);
14253
13730
  if (/\bfailed \(1\):/.test(message))
14254
13731
  return false;
14255
- if (error51 instanceof Error && message.startsWith(`git ${args.join(" ")} failed in ${projectRoot}:`)) {
14256
- throw error51;
13732
+ if (error51 instanceof Error && message.startsWith(`git ${args.join(" ")} failed in ${projectRoot}:`)) {
13733
+ throw error51;
13734
+ }
13735
+ throw createGitError(projectRoot, args, error51);
13736
+ }
13737
+ }
13738
+ async function getGitBlobHashes(projectRoot, files, opts) {
13739
+ const gitAvailable = opts?.gitAvailable ?? true;
13740
+ if (!gitAvailable)
13741
+ return /* @__PURE__ */ new Map();
13742
+ const relFileSet = new Set(files.map((file2) => normalizePath(path8.relative(projectRoot, file2))).filter((rel) => rel && !rel.startsWith("..") && !path8.isAbsolute(rel) && rel !== "."));
13743
+ if (!relFileSet.size)
13744
+ return /* @__PURE__ */ new Map();
13745
+ try {
13746
+ const { stdout: trackedStdout } = await runGit(projectRoot, ["ls-files", "-z"], {
13747
+ maxBuffer: 64 * 1024 * 1024
13748
+ });
13749
+ const trackedRel = trackedStdout.split("\0").filter((rel) => rel && relFileSet.has(rel));
13750
+ if (!trackedRel.length)
13751
+ return /* @__PURE__ */ new Map();
13752
+ const hashes = await hashGitPaths(projectRoot, trackedRel);
13753
+ if (hashes.length !== trackedRel.length) {
13754
+ logWithLevel(opts?.logLevel, "warn", `Warning: git hash-object returned ${hashes.length} hash(es) for ${trackedRel.length} requested file(s); discarding Git signatures for this build.`);
13755
+ return /* @__PURE__ */ new Map();
13756
+ }
13757
+ const out = /* @__PURE__ */ new Map();
13758
+ for (let i = 0; i < trackedRel.length; i += 1) {
13759
+ const rel = trackedRel[i];
13760
+ const hash2 = hashes[i];
13761
+ if (!hash2)
13762
+ continue;
13763
+ const abs = normalizePath(path8.resolve(projectRoot, rel));
13764
+ out.set(abs, hash2);
13765
+ }
13766
+ return out;
13767
+ } catch (error51) {
13768
+ logWithLevel(opts?.logLevel, "warn", "Warning: Failed to read Git blob hashes; falling back to content hashing.", error51);
13769
+ return /* @__PURE__ */ new Map();
13770
+ }
13771
+ }
13772
+ async function hashGitPaths(projectRoot, trackedRel) {
13773
+ const batches = [];
13774
+ let currentBatch = [];
13775
+ let currentBatchBytes = 0;
13776
+ for (const rel of trackedRel) {
13777
+ const absolutePath = path8.resolve(projectRoot, rel);
13778
+ const pathBytes = Buffer.byteLength(absolutePath, "utf8") + 1;
13779
+ const wouldExceedBatchLimit = currentBatch.length && currentBatchBytes + pathBytes > MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES;
13780
+ if (wouldExceedBatchLimit) {
13781
+ batches.push(currentBatch);
13782
+ currentBatch = [];
13783
+ currentBatchBytes = 0;
13784
+ }
13785
+ currentBatch.push(absolutePath);
13786
+ currentBatchBytes += pathBytes;
13787
+ }
13788
+ if (currentBatch.length)
13789
+ batches.push(currentBatch);
13790
+ const hashes = [];
13791
+ for (const batch of batches) {
13792
+ const { stdout } = await runGit(projectRoot, ["hash-object", "--", ...batch]);
13793
+ hashes.push(...stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
13794
+ }
13795
+ return hashes;
13796
+ }
13797
+ async function listChangedFiles(projectRoot, opts) {
13798
+ let args = [
13799
+ "diff",
13800
+ "--no-ext-diff",
13801
+ "--no-textconv",
13802
+ "--find-renames",
13803
+ "--name-only",
13804
+ "-z",
13805
+ "--diff-filter=ACDMRTUXB"
13806
+ ];
13807
+ if (opts.base) {
13808
+ const head = opts.head ?? "HEAD";
13809
+ args = gitDiffArgs(opts.base, head, ["--name-only", "-z", "--diff-filter=ACDMRTUXB"]);
13810
+ } else if (opts.changedSince) {
13811
+ args.push("--end-of-options", assertSafeRevision(opts.changedSince, "changedSince"));
13812
+ } else {
13813
+ return [];
13814
+ }
13815
+ args.push("--");
13816
+ try {
13817
+ const stdout = await runGitCollectStdout(projectRoot, args);
13818
+ const relFiles = stdout.split("\0").filter(Boolean);
13819
+ const out = [];
13820
+ for (const rel of relFiles) {
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));
13935
+ if (abs)
13936
+ out.push(abs);
13937
+ }
13938
+ return Array.from(new Set(out));
13939
+ } catch (error51) {
13940
+ throw createGitError(projectRoot, args, error51);
13941
+ }
13942
+ }
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;
14257
14364
  }
14258
- throw createGitError(projectRoot, args, error51);
14259
- }
14365
+ return !userIgnoreMatchers.some((matcher) => matchesDiscoveryGlob(absolutePath, globRoot, matcher));
14366
+ };
14260
14367
  }
14261
- async function getGitBlobHashes(projectRoot, files, opts) {
14262
- const gitAvailable = opts?.gitAvailable ?? true;
14263
- if (!gitAvailable)
14264
- return /* @__PURE__ */ new Map();
14265
- const relFileSet = new Set(files.map((file2) => normalizePath(path9.relative(projectRoot, file2))).filter((rel) => rel && !rel.startsWith("..") && !path9.isAbsolute(rel) && rel !== "."));
14266
- if (!relFileSet.size)
14267
- return /* @__PURE__ */ new Map();
14368
+ async function isSafeSymlinkDirectory(root, linkPath, realRoot) {
14268
14369
  try {
14269
- const { stdout: trackedStdout } = await runGit(projectRoot, ["ls-files", "-z"], {
14270
- maxBuffer: 64 * 1024 * 1024
14271
- });
14272
- const trackedRel = trackedStdout.split("\0").filter((rel) => rel && relFileSet.has(rel));
14273
- if (!trackedRel.length)
14274
- return /* @__PURE__ */ new Map();
14275
- const hashes = await hashGitPaths(projectRoot, trackedRel);
14276
- if (hashes.length !== trackedRel.length) {
14277
- logWithLevel(opts?.logLevel, "warn", `Warning: git hash-object returned ${hashes.length} hash(es) for ${trackedRel.length} requested file(s); discarding Git signatures for this build.`);
14278
- return /* @__PURE__ */ new Map();
14279
- }
14280
- const out = /* @__PURE__ */ new Map();
14281
- for (let i = 0; i < trackedRel.length; i += 1) {
14282
- const rel = trackedRel[i];
14283
- const hash2 = hashes[i];
14284
- if (!hash2)
14285
- continue;
14286
- const abs = normalizePath(path9.resolve(projectRoot, rel));
14287
- out.set(abs, hash2);
14288
- }
14289
- return out;
14290
- } catch (error51) {
14291
- logWithLevel(opts?.logLevel, "warn", "Warning: Failed to read Git blob hashes; falling back to content hashing.", error51);
14292
- return /* @__PURE__ */ new Map();
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;
14293
14382
  }
14294
14383
  }
14295
- async function hashGitPaths(projectRoot, trackedRel) {
14296
- const batches = [];
14297
- let currentBatch = [];
14298
- let currentBatchBytes = 0;
14299
- for (const rel of trackedRel) {
14300
- const absolutePath = path9.resolve(projectRoot, rel);
14301
- const pathBytes = Buffer.byteLength(absolutePath, "utf8") + 1;
14302
- const wouldExceedBatchLimit = currentBatch.length && currentBatchBytes + pathBytes > MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES;
14303
- if (wouldExceedBatchLimit) {
14304
- batches.push(currentBatch);
14305
- currentBatch = [];
14306
- currentBatchBytes = 0;
14307
- }
14308
- currentBatch.push(absolutePath);
14309
- currentBatchBytes += pathBytes;
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;
14310
14390
  }
14311
- if (currentBatch.length)
14312
- batches.push(currentBatch);
14313
- const hashes = [];
14314
- for (const batch of batches) {
14315
- const { stdout } = await runGit(projectRoot, ["hash-object", "--", ...batch]);
14316
- hashes.push(...stdout.split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
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;
14317
14403
  }
14318
- return hashes;
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;
14319
14417
  }
14320
- async function listChangedFiles(projectRoot, opts) {
14321
- let args = [
14322
- "diff",
14323
- "--no-ext-diff",
14324
- "--no-textconv",
14325
- "--find-renames",
14326
- "--name-only",
14327
- "-z",
14328
- "--diff-filter=ACDMRTUXB"
14329
- ];
14330
- if (opts.base) {
14331
- const head = opts.head ?? "HEAD";
14332
- args = gitDiffArgs(opts.base, head, ["--name-only", "-z", "--diff-filter=ACDMRTUXB"]);
14333
- } else if (opts.changedSince) {
14334
- args.push("--end-of-options", assertSafeRevision(opts.changedSince, "changedSince"));
14335
- } else {
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)
14336
14425
  return [];
14337
- }
14338
- args.push("--");
14339
- try {
14340
- const stdout = await runGitCollectStdout(projectRoot, args);
14341
- const relFiles = stdout.split("\0").filter(Boolean);
14342
- const out = [];
14343
- for (const rel of relFiles) {
14344
- const abs = normalizePath(path9.resolve(projectRoot, rel));
14345
- if (abs)
14346
- 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);
14347
14442
  }
14348
- return Array.from(new Set(out));
14349
- } catch (error51) {
14350
- throw createGitError(projectRoot, args, error51);
14351
14443
  }
14444
+ return [...filesByPath.values()];
14352
14445
  }
14353
- async function listUntrackedFiles(projectRoot, opts) {
14354
- const gitAvailable = opts?.gitAvailable ?? true;
14355
- if (!gitAvailable)
14356
- return [];
14357
- const args = ["ls-files", "--others", "-z"];
14358
- if (opts?.respectGitignore ?? true)
14359
- args.push("--exclude-standard");
14360
- try {
14361
- const stdout = await runGitCollectStdout(projectRoot, args);
14362
- const relFiles = stdout.toString().split("\0").filter(Boolean);
14363
- const out = [];
14364
- for (const rel of relFiles) {
14365
- const abs = normalizePath(path9.resolve(projectRoot, rel));
14366
- if (abs)
14367
- out.push(abs);
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;
14368
14453
  }
14369
- return Array.from(new Set(out));
14370
- } catch (error51) {
14371
- throw createGitError(projectRoot, args, error51);
14372
- }
14454
+ });
14455
+ return filtered.filter((entry) => entry !== null);
14373
14456
  }
14374
- async function getUnifiedDiff(projectRoot, opts) {
14375
- let args = [
14376
- "diff",
14377
- "--no-ext-diff",
14378
- "--no-textconv",
14379
- "--find-renames",
14380
- "--unified=0",
14381
- "--no-color",
14382
- "--diff-filter=ACDMRTUXB"
14383
- ];
14384
- if (opts.base) {
14385
- const head = opts.head ?? "HEAD";
14386
- args = gitDiffArgs(opts.base, head, ["--unified=0", "--no-color", "--diff-filter=ACDMRTUXB"]);
14387
- } else if (opts.changedSince) {
14388
- args.push("--end-of-options", assertSafeRevision(opts.changedSince, "changedSince"));
14389
- } else {
14390
- return "";
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
+ }
14391
14475
  }
14392
- args.push("--");
14393
- try {
14394
- return await runGitCollectStdout(projectRoot, args);
14395
- } catch (error51) {
14396
- 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
+ }
14397
14482
  }
14483
+ return {
14484
+ path: normalizedPath,
14485
+ kind: def.kind,
14486
+ type: def.type,
14487
+ role: def.role,
14488
+ projectRoot,
14489
+ ...name ? { name } : {}
14490
+ };
14398
14491
  }
14399
- function createGitError(projectRoot, args, error51) {
14400
- let detail = stringifyUnknown(error51);
14401
- if (typeof error51 === "object" && error51 !== null && "stderr" in error51 && typeof error51.stderr === "string" && error51.stderr.trim()) {
14402
- detail = error51.stderr.trim();
14403
- } else if (error51 instanceof Error && error51.message) {
14404
- 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)}`);
14405
14551
  }
14406
- return new Error(`git ${args.join(" ")} failed in ${projectRoot}: ${detail}`);
14407
14552
  }
14408
- var DEFAULT_GIT_TIMEOUT_MS, gitRepositoryChecks, MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES, gitExecutableForTests, GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES, UNSAFE_REVISION_CHARACTERS;
14409
- var init_git = __esm({
14410
- "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"() {
14411
14556
  "use strict";
14557
+ import_fast_glob2 = __toESM(require_out4(), 1);
14558
+ import_picomatch = __toESM(require_picomatch4(), 1);
14559
+ init_logging();
14412
14560
  init_ast();
14413
14561
  init_paths();
14414
- init_logging();
14415
- DEFAULT_GIT_TIMEOUT_MS = 3e4;
14416
- gitRepositoryChecks = /* @__PURE__ */ new Map();
14417
- MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES = 24 * 1024;
14418
- gitExecutableForTests = null;
14419
- GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES = {
14420
- a: 7,
14421
- b: 8,
14422
- f: 12,
14423
- n: 10,
14424
- r: 13,
14425
- t: 9,
14426
- v: 11
14427
- };
14428
- 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() };
14429
14656
  }
14430
14657
  });
14431
14658
 
@@ -14523,13 +14750,13 @@ var init_projectSymbols = __esm({
14523
14750
  });
14524
14751
 
14525
14752
  // dist/util/resolution/jvm.js
14526
- import fsp5 from "node:fs/promises";
14753
+ import fsp6 from "node:fs/promises";
14527
14754
  import path10 from "node:path";
14528
14755
  async function readJvmSymbolIndex(filePath, cache, options) {
14529
14756
  const cached2 = cache.get(filePath);
14530
14757
  if (cached2)
14531
14758
  return cached2;
14532
- const source = await fsp5.readFile(filePath, "utf8");
14759
+ const source = await fsp6.readFile(filePath, "utf8");
14533
14760
  const packageName = source.match(options.packagePattern)?.[1] ?? null;
14534
14761
  const symbols = /* @__PURE__ */ new Set();
14535
14762
  for (const match of source.matchAll(options.declarationPattern)) {
@@ -14684,7 +14911,7 @@ var init_findFirstExisting = __esm({
14684
14911
  });
14685
14912
 
14686
14913
  // dist/util/resolution/files.js
14687
- import fsp6 from "node:fs/promises";
14914
+ import fsp7 from "node:fs/promises";
14688
14915
  import path12 from "node:path";
14689
14916
  async function findNearestFile(startDir, stopDir, fileName) {
14690
14917
  let dir = path12.resolve(startDir);
@@ -14704,7 +14931,7 @@ async function findNearestFile(startDir, stopDir, fileName) {
14704
14931
  }
14705
14932
  async function isDirectory(p) {
14706
14933
  try {
14707
- const st = await fsp6.stat(p);
14934
+ const st = await fsp7.stat(p);
14708
14935
  return st.isDirectory();
14709
14936
  } catch {
14710
14937
  return false;
@@ -14719,7 +14946,7 @@ var init_files = __esm({
14719
14946
 
14720
14947
  // dist/util/resolution/go.js
14721
14948
  import fs7 from "node:fs";
14722
- import fsp7 from "node:fs/promises";
14949
+ import fsp8 from "node:fs/promises";
14723
14950
  import path13 from "node:path";
14724
14951
  function stripInlineComment(line) {
14725
14952
  const idx = line.indexOf("//");
@@ -14729,7 +14956,7 @@ async function parseGoMod(moduleRoot) {
14729
14956
  const modPath = path13.join(moduleRoot, "go.mod");
14730
14957
  if (!await fileExists(modPath))
14731
14958
  return null;
14732
- const raw = await fsp7.readFile(modPath, "utf8");
14959
+ const raw = await fsp8.readFile(modPath, "utf8");
14733
14960
  const lines = raw.split(/\r?\n/);
14734
14961
  let modulePath = null;
14735
14962
  const replacements = /* @__PURE__ */ new Map();
@@ -14765,7 +14992,7 @@ async function parseGoMod(moduleRoot) {
14765
14992
  };
14766
14993
  }
14767
14994
  async function parseGoWork(goWorkPath) {
14768
- const content = await fsp7.readFile(goWorkPath, "utf8");
14995
+ const content = await fsp8.readFile(goWorkPath, "utf8");
14769
14996
  const lines = content.split(/\r?\n/);
14770
14997
  const modules = [];
14771
14998
  let inUseBlock = false;
@@ -14794,7 +15021,7 @@ async function parseGoWork(goWorkPath) {
14794
15021
  }
14795
15022
  async function findGoPackageEntry(dirPath) {
14796
15023
  try {
14797
- const stat = await fsp7.stat(dirPath);
15024
+ const stat = await fsp8.stat(dirPath);
14798
15025
  if (!stat.isDirectory())
14799
15026
  return null;
14800
15027
  } catch {
@@ -14802,7 +15029,7 @@ async function findGoPackageEntry(dirPath) {
14802
15029
  }
14803
15030
  let entries = [];
14804
15031
  try {
14805
- entries = await fsp7.readdir(dirPath, { withFileTypes: true });
15032
+ entries = await fsp8.readdir(dirPath, { withFileTypes: true });
14806
15033
  } catch {
14807
15034
  return null;
14808
15035
  }
@@ -14954,7 +15181,7 @@ var init_node = __esm({
14954
15181
  });
14955
15182
 
14956
15183
  // dist/util/resolution/phpComposer.js
14957
- import fsp8 from "node:fs/promises";
15184
+ import fsp9 from "node:fs/promises";
14958
15185
  import path15 from "node:path";
14959
15186
  function readComposerNamespaceDirs(value, composerDir) {
14960
15187
  const result = /* @__PURE__ */ new Map();
@@ -15004,7 +15231,7 @@ async function loadPhpComposerConfig(composerPath) {
15004
15231
  return await cached2;
15005
15232
  const pending = (async () => {
15006
15233
  try {
15007
- const raw = await fsp8.readFile(composerPath, "utf8");
15234
+ const raw = await fsp9.readFile(composerPath, "utf8");
15008
15235
  const parsed = JSON.parse(raw);
15009
15236
  const composerDir = path15.dirname(composerPath);
15010
15237
  const autoload = parsed.autoload && typeof parsed.autoload === "object" ? parsed.autoload : {};
@@ -15141,7 +15368,7 @@ async function getPhpComposerAutoloadFiles(composerPath, composerConfig) {
15141
15368
  }
15142
15369
  for (const root of roots) {
15143
15370
  try {
15144
- const stat = await fsp8.stat(root.path);
15371
+ const stat = await fsp9.stat(root.path);
15145
15372
  if (stat.isDirectory()) {
15146
15373
  const files = await listProjectFiles(root.path, ["**/*.php"]);
15147
15374
  for (const filePath of files) {
@@ -15181,7 +15408,7 @@ async function phpFileMatchesNamespacePrefixes(filePath, namespacePrefixes) {
15181
15408
  }
15182
15409
  let source;
15183
15410
  try {
15184
- source = await fsp8.readFile(filePath, "utf8");
15411
+ source = await fsp9.readFile(filePath, "utf8");
15185
15412
  } catch {
15186
15413
  return false;
15187
15414
  }
@@ -15217,7 +15444,7 @@ var init_phpComposer = __esm({
15217
15444
  });
15218
15445
 
15219
15446
  // dist/util/resolution/php.js
15220
- import fsp9 from "node:fs/promises";
15447
+ import fsp10 from "node:fs/promises";
15221
15448
  import path16 from "node:path";
15222
15449
  async function resolvePhpPathLikeSpecifier(projectRoot, fromFile, spec) {
15223
15450
  let base = path16.resolve(path16.dirname(fromFile), spec);
@@ -15270,7 +15497,7 @@ async function readPhpSymbolIndex(filePath) {
15270
15497
  const cached2 = phpSymbolIndexCache.get(filePath);
15271
15498
  if (cached2)
15272
15499
  return cached2;
15273
- const source = await fsp9.readFile(filePath, "utf8");
15500
+ const source = await fsp10.readFile(filePath, "utf8");
15274
15501
  const packageEntries = extractPhpTopLevelPackageEntries(source);
15275
15502
  const primaryEntry = packageEntries[0] ?? {
15276
15503
  packageName: "",
@@ -15719,7 +15946,7 @@ var init_php2 = __esm({
15719
15946
 
15720
15947
  // dist/util/resolution/python.js
15721
15948
  import fs8 from "node:fs";
15722
- import fsp10 from "node:fs/promises";
15949
+ import fsp11 from "node:fs/promises";
15723
15950
  import path17 from "node:path";
15724
15951
  function pythonResolutionCacheKey(projectRoot, fromFile, moduleName, importDotCount) {
15725
15952
  const normalizedRoot = normalizePath(path17.resolve(projectRoot));
@@ -15731,7 +15958,7 @@ async function findPythonPackageAnchor(startDir) {
15731
15958
  let topWithInit = startDir;
15732
15959
  while (true) {
15733
15960
  try {
15734
- 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);
15735
15962
  topWithInit = dir;
15736
15963
  } catch {
15737
15964
  }
@@ -15777,7 +16004,7 @@ async function resolvePythonModule(projectRoot, fromFile, moduleName, importDotC
15777
16004
  resolvePythonModuleCache.set(cacheKey3, res);
15778
16005
  return res;
15779
16006
  }
15780
- await fsp10.access(c, fs8.constants.R_OK);
16007
+ await fsp11.access(c, fs8.constants.R_OK);
15781
16008
  {
15782
16009
  const res = normalizePath(path17.resolve(c));
15783
16010
  resolvePythonModuleCache.set(cacheKey3, res);
@@ -15815,7 +16042,7 @@ async function resolvePythonModule(projectRoot, fromFile, moduleName, importDotC
15815
16042
  resolvePythonModuleCache.set(cacheKey3, res);
15816
16043
  return res;
15817
16044
  }
15818
- await fsp10.access(c, fs8.constants.R_OK);
16045
+ await fsp11.access(c, fs8.constants.R_OK);
15819
16046
  {
15820
16047
  const res = normalizePath(path17.resolve(c));
15821
16048
  resolvePythonModuleCache.set(cacheKey3, res);
@@ -17987,7 +18214,7 @@ var require_lib2 = __commonJS({
17987
18214
 
17988
18215
  // dist/util/resolution/tsconfig.js
17989
18216
  import fs9 from "node:fs";
17990
- import fsp11 from "node:fs/promises";
18217
+ import fsp12 from "node:fs/promises";
17991
18218
  import { createRequire } from "node:module";
17992
18219
  import path19 from "node:path";
17993
18220
  async function findNearestTsconfig(startFromFile, projectRoot) {
@@ -17997,7 +18224,7 @@ async function findNearestTsconfig(startFromFile, projectRoot) {
17997
18224
  while (isFilePathWithinRoot(resolvedProjectRoot, dir)) {
17998
18225
  const candidate = path19.join(dir, "tsconfig.json");
17999
18226
  try {
18000
- await fsp11.access(candidate, fs9.constants.R_OK);
18227
+ await fsp12.access(candidate, fs9.constants.R_OK);
18001
18228
  return candidate;
18002
18229
  } catch {
18003
18230
  }
@@ -18058,7 +18285,7 @@ async function loadTsconfigConfig(cfgPath, projectRoot, seen = /* @__PURE__ */ n
18058
18285
  return { baseUrl: path19.dirname(normalizedCfgPath).replace(/\\/g, "/"), paths: {} };
18059
18286
  }
18060
18287
  seen.add(cfgKey);
18061
- const raw = await fsp11.readFile(cfgPath, "utf8");
18288
+ const raw = await fsp12.readFile(cfgPath, "utf8");
18062
18289
  const json3 = parseJsonc(raw);
18063
18290
  const cfgDir = path19.dirname(cfgPath);
18064
18291
  const compilerOptions = json3.compilerOptions;
@@ -18181,7 +18408,7 @@ __export(resolution_exports, {
18181
18408
  resolveSpecifier: () => resolveSpecifier
18182
18409
  });
18183
18410
  import fs10 from "node:fs";
18184
- import fsp12 from "node:fs/promises";
18411
+ import fsp13 from "node:fs/promises";
18185
18412
  import path20 from "node:path";
18186
18413
  function getResolveSpecifierCacheEntry(key) {
18187
18414
  return lruMapGet(resolveSpecifierCache, key);
@@ -18237,7 +18464,7 @@ async function resolvePathLikeModule(projectRoot, spec, resolutionExtensions) {
18237
18464
  return path20.resolve(path20.join(p, "index" + e));
18238
18465
  }
18239
18466
  if (await fileExists(p)) {
18240
- const st = await fsp12.stat(p);
18467
+ const st = await fsp13.stat(p);
18241
18468
  if (!st.isDirectory())
18242
18469
  return path20.resolve(p);
18243
18470
  }
@@ -18584,7 +18811,7 @@ var init_confinedFile = __esm({
18584
18811
  });
18585
18812
 
18586
18813
  // dist/util/workerThreads.js
18587
- import os2 from "node:os";
18814
+ import os3 from "node:os";
18588
18815
  function resolveWorkerThreadCount(options) {
18589
18816
  const max = options?.max ?? 64;
18590
18817
  const requested = options?.requested;
@@ -18595,7 +18822,7 @@ function resolveWorkerThreadCount(options) {
18595
18822
  if (typeof defaultCount === "number" && Number.isFinite(defaultCount) && defaultCount > 0) {
18596
18823
  return Math.min(Math.max(Math.floor(defaultCount), 1), max);
18597
18824
  }
18598
- return Math.min(Math.max(os2.availableParallelism() - 1, 1), max);
18825
+ return Math.min(Math.max(os3.availableParallelism() - 1, 1), max);
18599
18826
  }
18600
18827
  var init_workerThreads = __esm({
18601
18828
  "dist/util/workerThreads.js"() {
@@ -19000,17 +19227,17 @@ var init_sfc = __esm({
19000
19227
  });
19001
19228
 
19002
19229
  // dist/languages/filePrep.js
19003
- import fsp13 from "node:fs/promises";
19230
+ import fsp14 from "node:fs/promises";
19004
19231
  async function prepareSourceInput(file2, opts) {
19005
19232
  const framework = detectSFCFramework(file2);
19006
19233
  if (framework) {
19007
- const rawSource2 = opts?.source ?? await fsp13.readFile(file2, "utf8");
19234
+ const rawSource2 = opts?.source ?? await fsp14.readFile(file2, "utf8");
19008
19235
  return prepareSFCSourceInput(rawSource2, framework);
19009
19236
  }
19010
19237
  const sup = supportForFile(file2, opts?.languageExtensions);
19011
19238
  if (!sup)
19012
19239
  throw new UnsupportedParserInputError(file2);
19013
- const rawSource = opts?.source ?? await fsp13.readFile(file2, "utf8");
19240
+ const rawSource = opts?.source ?? await fsp14.readFile(file2, "utf8");
19014
19241
  return {
19015
19242
  source: rawSource,
19016
19243
  sup
@@ -20962,7 +21189,7 @@ var init_angularjs = __esm({
20962
21189
  });
20963
21190
 
20964
21191
  // dist/graphs/angularjs.js
20965
- import fsp14 from "node:fs/promises";
21192
+ import fsp15 from "node:fs/promises";
20966
21193
  async function collectAngularJsFrameworkEdges(projectRoot, files, workspaceConfig, parsed) {
20967
21194
  const jsFiles = files.filter((file2) => file2.toLowerCase().endsWith(".js"));
20968
21195
  if (!jsFiles.length)
@@ -20975,7 +21202,7 @@ async function collectAngularJsFrameworkEdges(projectRoot, files, workspaceConfi
20975
21202
  continue;
20976
21203
  }
20977
21204
  try {
20978
- const source = await fsp14.readFile(file2, "utf8");
21205
+ const source = await fsp15.readFile(file2, "utf8");
20979
21206
  contexts.push({ file: file2, source });
20980
21207
  } catch {
20981
21208
  continue;
@@ -24725,7 +24952,7 @@ var init_extractFacts = __esm({
24725
24952
 
24726
24953
  // dist/sql/sourceGraph.js
24727
24954
  import crypto2 from "node:crypto";
24728
- import fsp15 from "node:fs/promises";
24955
+ import fsp16 from "node:fs/promises";
24729
24956
  function isSqlFile(filePath, languageExtensions) {
24730
24957
  return supportForFile(filePath, languageExtensions)?.id === "sql";
24731
24958
  }
@@ -24854,7 +25081,7 @@ function buildSqlModuleIndex(filePath, source) {
24854
25081
  };
24855
25082
  }
24856
25083
  async function readSqlFacts(filePath) {
24857
- return extractSqlFactsFromSource(filePath, await fsp15.readFile(filePath, "utf8"));
25084
+ return extractSqlFactsFromSource(filePath, await fsp16.readFile(filePath, "utf8"));
24858
25085
  }
24859
25086
  async function buildSqlFactCache(allFiles, languageExtensions) {
24860
25087
  const sqlFiles2 = Array.from(new Set(allFiles.map(normalizePath).filter((file2) => isSqlFile(file2, languageExtensions)))).sort((left, right) => left.localeCompare(right));
@@ -25290,7 +25517,7 @@ var init_adjacency = __esm({
25290
25517
  });
25291
25518
 
25292
25519
  // dist/worker/nativeExtractWorker.js
25293
- import fsp16 from "node:fs/promises";
25520
+ import fsp17 from "node:fs/promises";
25294
25521
  import { createRequire as createRequire3 } from "node:module";
25295
25522
  import path32 from "node:path";
25296
25523
  import { fileURLToPath as fileURLToPath3 } from "node:url";
@@ -25493,9 +25720,9 @@ var init_nativeExtractWorker = __esm({
25493
25720
  localNativePackageRoot2 = path32.resolve(path32.dirname(fileURLToPath3(import.meta.url)), "../../packages/codegraph-native");
25494
25721
  runExtraction = createNativeExtractor({
25495
25722
  loadBinding: loadProductionBinding,
25496
- readFile: fsp16.readFile,
25723
+ readFile: fsp17.readFile,
25497
25724
  statFile: async (filePath) => {
25498
- const stat = await fsp16.stat(filePath);
25725
+ const stat = await fsp17.stat(filePath);
25499
25726
  return { size: stat.size };
25500
25727
  }
25501
25728
  });
@@ -29805,7 +30032,7 @@ var init_reports = __esm({
29805
30032
  // dist/indexer/build-cache/location.js
29806
30033
  import crypto4 from "node:crypto";
29807
30034
  import fs16 from "node:fs";
29808
- import os3 from "node:os";
30035
+ import os4 from "node:os";
29809
30036
  import path35 from "node:path";
29810
30037
  function isWritableDirectory(candidate) {
29811
30038
  try {
@@ -29816,7 +30043,7 @@ function isWritableDirectory(candidate) {
29816
30043
  }
29817
30044
  function isForbiddenAnchor(candidate) {
29818
30045
  const resolved = path35.resolve(candidate);
29819
- const home = path35.resolve(os3.homedir());
30046
+ const home = path35.resolve(os4.homedir());
29820
30047
  const parsed = path35.parse(resolved);
29821
30048
  return fileIdentityKey(resolved) === fileIdentityKey(home) || fileIdentityKey(resolved) === fileIdentityKey(parsed.root);
29822
30049
  }
@@ -29840,7 +30067,7 @@ function findRepositoryAnchor(projectRoot) {
29840
30067
  return { anchor: start, layer: "project" };
29841
30068
  }
29842
30069
  function resolveCodegraphUserCacheRoot2() {
29843
- 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");
29844
30071
  return path35.join(base, "codegraph");
29845
30072
  }
29846
30073
  function projectCacheNamespace(projectRoot, anchor) {
@@ -29874,7 +30101,33 @@ function assertAnchorNotForbidden(resolution) {
29874
30101
  return;
29875
30102
  if (!isForbiddenAnchor(resolution.anchor))
29876
30103
  return;
29877
- 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
+ }
29878
30131
  }
29879
30132
  function resolveCacheLocation(projectRoot, opts) {
29880
30133
  const root = path35.resolve(projectRoot);
@@ -29885,34 +30138,57 @@ function resolveCacheLocation(projectRoot, opts) {
29885
30138
  const anchor = anchorWritable ? resolution.anchor : root;
29886
30139
  const effectiveLayer = anchorWritable ? resolution.layer : "project";
29887
30140
  const sameRoot2 = fileIdentityKey(anchor) === fileIdentityKey(root);
29888
- if (sameRoot2 && !opts?.cacheDir && !process.env.CODEGRAPH_CACHE_DIR?.trim() && (!opts?.cacheLocation || opts.cacheLocation === "project")) {
29889
- 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 };
29890
30151
  }
29891
30152
  const namespace = projectCacheNamespace(root, effectiveLayer === "git" || effectiveLayer === "manifest" ? anchor : void 0);
29892
- const explicitBase = opts?.cacheDir?.trim() || process.env.CODEGRAPH_CACHE_DIR?.trim();
29893
30153
  if (explicitBase) {
29894
30154
  const configured = path35.resolve(explicitBase);
29895
30155
  const configuredPath = path35.basename(configured) === namespace ? configured : path35.join(configured, namespace);
29896
30156
  return { path: configuredPath, anchor, layer: effectiveLayer };
29897
30157
  }
29898
- const base = opts?.cacheLocation === "user" ? resolveCodegraphUserCacheRoot2() : path35.join(anchor, ".codegraph-cache");
29899
- const candidate = path35.join(path35.resolve(base), "index-v1", namespace);
29900
- if (!sameRoot2 && !opts?.cacheLocation) {
29901
- const legacy = path35.join(root, ".codegraph-cache", "index-v1");
29902
- if (!fs16.existsSync(candidate) && fs16.existsSync(legacy)) {
29903
- 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")
29904
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
+ });
29905
30177
  }
30178
+ migrateLegacyProjectCache(candidate, legacyCandidates, opts?.logLevel);
29906
30179
  return { path: candidate, anchor, layer: effectiveLayer };
29907
30180
  }
29908
30181
  function cacheRoot(projectRoot, opts) {
29909
30182
  return resolveCacheLocation(projectRoot, opts).path;
29910
30183
  }
29911
- var CONFIGURED_ANCHOR_LAYER_LABELS;
30184
+ var PROJECT_CACHE_DIRECTORY, PROJECT_CACHE_RELATIVE_PATH, LEGACY_PROJECT_CACHE_RELATIVE_PATH, CONFIGURED_ANCHOR_LAYER_LABELS;
29912
30185
  var init_location = __esm({
29913
30186
  "dist/indexer/build-cache/location.js"() {
29914
30187
  "use strict";
29915
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");
29916
30192
  CONFIGURED_ANCHOR_LAYER_LABELS = {
29917
30193
  explicit: "--cache-dir (or an absolute cache.location)",
29918
30194
  environment: "CODEGRAPH_CACHE_DIR",
@@ -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";
@@ -49763,7 +50039,7 @@ function sameSnapshotFileIdentity(left, right) {
49763
50039
  return left.size === right.size && left.mtimeMs === right.mtimeMs && left.ctimeMs === right.ctimeMs;
49764
50040
  }
49765
50041
  async function snapshotFileIdentity(snapshotPath) {
49766
- const stat = await fsp21.stat(snapshotPath);
50042
+ const stat = await fsp22.stat(snapshotPath);
49767
50043
  return {
49768
50044
  size: stat.size,
49769
50045
  mtimeMs: stat.mtimeMs,
@@ -49771,7 +50047,7 @@ async function snapshotFileIdentity(snapshotPath) {
49771
50047
  };
49772
50048
  }
49773
50049
  async function fsyncSnapshotFile(snapshotPath) {
49774
- const handle = await fsp21.open(snapshotPath, "r+");
50050
+ const handle = await fsp22.open(snapshotPath, "r+");
49775
50051
  try {
49776
50052
  await handle.sync();
49777
50053
  } finally {
@@ -49788,7 +50064,7 @@ function isSnapshotTempName(name, snapshotName) {
49788
50064
  async function cleanupStaleSnapshotTemps(snapshotPath) {
49789
50065
  let entries;
49790
50066
  try {
49791
- entries = await fsp21.readdir(path43.dirname(snapshotPath), { withFileTypes: true });
50067
+ entries = await fsp22.readdir(path43.dirname(snapshotPath), { withFileTypes: true });
49792
50068
  } catch {
49793
50069
  return;
49794
50070
  }
@@ -49799,26 +50075,26 @@ async function cleanupStaleSnapshotTemps(snapshotPath) {
49799
50075
  continue;
49800
50076
  const candidate = path43.join(path43.dirname(snapshotPath), entry.name);
49801
50077
  try {
49802
- const stat = await fsp21.lstat(candidate);
50078
+ const stat = await fsp22.lstat(candidate);
49803
50079
  if (stat.mtimeMs > cutoff)
49804
50080
  continue;
49805
- await fsp21.rm(candidate, { force: true });
50081
+ await fsp22.rm(candidate, { force: true });
49806
50082
  } catch {
49807
50083
  }
49808
50084
  }
49809
50085
  }
49810
50086
  async function writeSnapshotAtomically(snapshotPath, data) {
49811
- await fsp21.mkdir(path43.dirname(snapshotPath), { recursive: true });
50087
+ await fsp22.mkdir(path43.dirname(snapshotPath), { recursive: true });
49812
50088
  await cleanupStaleSnapshotTemps(snapshotPath);
49813
50089
  let tempPath = path43.join(path43.dirname(snapshotPath), `.${path43.basename(snapshotPath)}.${process.pid}.${randomUUID()}${SNAPSHOT_TEMP_SUFFIX}`);
49814
50090
  try {
49815
- await fsp21.writeFile(tempPath, data, { flag: "wx" });
50091
+ await fsp22.writeFile(tempPath, data, { flag: "wx" });
49816
50092
  await fsyncSnapshotFile(tempPath);
49817
- await fsp21.rename(tempPath, snapshotPath);
50093
+ await fsp22.rename(tempPath, snapshotPath);
49818
50094
  tempPath = void 0;
49819
50095
  } finally {
49820
50096
  if (tempPath) {
49821
- await fsp21.rm(tempPath, { force: true }).catch(() => void 0);
50097
+ await fsp22.rm(tempPath, { force: true }).catch(() => void 0);
49822
50098
  }
49823
50099
  }
49824
50100
  }
@@ -49873,7 +50149,7 @@ async function readParsedSnapshot(snapshotPath) {
49873
50149
  setBoundedSnapshotCache(parsedSnapshotCache, snapshotPath, cached2);
49874
50150
  return { identity: cached2.identity, payload: cached2.payload };
49875
50151
  }
49876
- const compressed = Buffer.from(await fsp21.readFile(snapshotPath));
50152
+ const compressed = Buffer.from(await fsp22.readFile(snapshotPath));
49877
50153
  const payload = decodeSnapshotPayload(compressed);
49878
50154
  const after = await snapshotFileIdentity(snapshotPath);
49879
50155
  const entry = { identity: before, compressed, payload };
@@ -50735,21 +51011,21 @@ var init_build_cache = __esm({
50735
51011
 
50736
51012
  // dist/indexer/build-cache/resolver-environment.js
50737
51013
  import crypto9 from "node:crypto";
50738
- import fsp22 from "node:fs/promises";
51014
+ import fsp23 from "node:fs/promises";
50739
51015
  import path44 from "node:path";
50740
51016
  function normalizedRelativePath(projectRoot, target) {
50741
51017
  return path44.relative(projectRoot, target).replace(/\\/g, "/");
50742
51018
  }
50743
51019
  async function statInput(projectRoot, target) {
50744
51020
  try {
50745
- const stat = await fsp22.stat(target);
51021
+ const stat = await fsp23.stat(target);
50746
51022
  if (!stat.isFile())
50747
51023
  return null;
50748
51024
  const sampleBytes = Math.min(stat.size, RESOLUTION_INPUT_SAMPLE_BYTES);
50749
51025
  const hash2 = crypto9.createHash("sha256");
50750
51026
  if (sampleBytes) {
50751
51027
  const first = Buffer.allocUnsafe(sampleBytes);
50752
- const handle = await fsp22.open(target, "r");
51028
+ const handle = await fsp23.open(target, "r");
50753
51029
  try {
50754
51030
  const firstRead = await handle.read(first, 0, sampleBytes, 0);
50755
51031
  hash2.update(first.subarray(0, firstRead.bytesRead));
@@ -50797,7 +51073,7 @@ async function activeNodeModulesRoots(projectRoot, files) {
50797
51073
  return null;
50798
51074
  const roots = await Promise.all(candidates.map(async (candidate) => {
50799
51075
  try {
50800
- return (await fsp22.stat(candidate)).isDirectory() ? candidate : null;
51076
+ return (await fsp23.stat(candidate)).isDirectory() ? candidate : null;
50801
51077
  } catch {
50802
51078
  return null;
50803
51079
  }
@@ -50807,7 +51083,7 @@ async function activeNodeModulesRoots(projectRoot, files) {
50807
51083
  async function installedPackageManifests(projectRoot, root) {
50808
51084
  let entries;
50809
51085
  try {
50810
- entries = await fsp22.readdir(root, { withFileTypes: true });
51086
+ entries = await fsp23.readdir(root, { withFileTypes: true });
50811
51087
  } catch {
50812
51088
  return [];
50813
51089
  }
@@ -50818,7 +51094,7 @@ async function installedPackageManifests(projectRoot, root) {
50818
51094
  const entryPath = path44.join(root, entry.name);
50819
51095
  if (entry.name.startsWith("@")) {
50820
51096
  try {
50821
- const scopedEntries = await fsp22.readdir(entryPath, { withFileTypes: true });
51097
+ const scopedEntries = await fsp23.readdir(entryPath, { withFileTypes: true });
50822
51098
  for (const scopedEntry of scopedEntries) {
50823
51099
  if (scopedEntry.isDirectory())
50824
51100
  packageDirectories.push(path44.join(entryPath, scopedEntry.name));
@@ -50969,7 +51245,7 @@ var init_finalize = __esm({
50969
51245
  });
50970
51246
 
50971
51247
  // dist/indexer/build-manifest.js
50972
- import fsp23 from "node:fs/promises";
51248
+ import fsp24 from "node:fs/promises";
50973
51249
  import path45 from "node:path";
50974
51250
  import { performance as performance3 } from "node:perf_hooks";
50975
51251
  function toManifestFileEntry(entry) {
@@ -50989,7 +51265,7 @@ async function writeIndexManifestSnapshot(args) {
50989
51265
  const writeManifestStart = performance3.now();
50990
51266
  const lastCommit = await getGitHead(args.projectRoot);
50991
51267
  const configHashResult = await computeConfigHash(args.projectRoot, args.opts?.logLevel);
50992
- 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;
50993
51269
  const configHash = recordConfigHashResult(args.manifestReport, configHashResult, args.opts?.logLevel);
50994
51270
  const manifestData = {
50995
51271
  version: MANIFEST_VERSION,
@@ -52617,10 +52893,10 @@ var init_build_workers = __esm({
52617
52893
 
52618
52894
  // dist/indexer/incremental-plan.js
52619
52895
  import fs22 from "node:fs";
52620
- import fsp24 from "node:fs/promises";
52896
+ import fsp25 from "node:fs/promises";
52621
52897
  async function pathExists2(filePath) {
52622
52898
  try {
52623
- await fsp24.access(filePath);
52899
+ await fsp25.access(filePath);
52624
52900
  return true;
52625
52901
  } catch {
52626
52902
  return false;
@@ -52797,7 +53073,7 @@ __export(build_index_exports, {
52797
53073
  });
52798
53074
  import crypto10 from "node:crypto";
52799
53075
  import fs23 from "node:fs";
52800
- import fsp25 from "node:fs/promises";
53076
+ import fsp26 from "node:fs/promises";
52801
53077
  import path47 from "node:path";
52802
53078
  import { performance as performance5 } from "node:perf_hooks";
52803
53079
  function initParserBackendDegradationReport(report) {
@@ -53151,7 +53427,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
53151
53427
  const shouldWriteManifest = manifestMode === "read-write";
53152
53428
  const projectFiles = helperOpts?.projectFiles;
53153
53429
  initManifestReport(report, useManifest, false);
53154
- const confinedRoot = helperOpts?.confineReads ? await fsp25.realpath(projectRoot) : void 0;
53430
+ const confinedRoot = helperOpts?.confineReads ? await fsp26.realpath(projectRoot) : void 0;
53155
53431
  const [trustedSources, normalizedFiles] = [
53156
53432
  confinedRoot ? /* @__PURE__ */ new Map() : void 0,
53157
53433
  Array.from(new Set(normalizeIndexedFileInputs(projectRoot, rawFiles ?? [], "Index file")))
@@ -53529,7 +53805,7 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
53529
53805
  const useDiskCache = (opts?.cache ?? "off") === "disk";
53530
53806
  const wantsMaxSymlinkCorrectness = !!opts?.cacheStrict || !!opts?.cacheVerify;
53531
53807
  const symlinkHintManifest = helperOpts?.ignoreExistingManifest || wantsMaxSymlinkCorrectness || !useDiskCache ? null : await loadManifest(projectRoot, opts);
53532
- const rootMtime = symlinkHintManifest ? (await fsp25.stat(projectRoot)).mtimeMs : void 0;
53808
+ const rootMtime = symlinkHintManifest ? (await fsp26.stat(projectRoot)).mtimeMs : void 0;
53533
53809
  const symlinkHintIsFresh = symlinkHintManifest?.symlinkDirectoriesRootMtimeMs !== void 0 && rootMtime === symlinkHintManifest.symlinkDirectoriesRootMtimeMs;
53534
53810
  const knownSymlinkDirectories = symlinkHintIsFresh ? symlinkHintManifest?.symlinkDirectories : void 0;
53535
53811
  let discoveredSymlinkDirectories = knownSymlinkDirectories;
@@ -53886,7 +54162,7 @@ async function buildProjectIndexIncremental(projectRoot, opts) {
53886
54162
  if (!entry?.sig)
53887
54163
  return true;
53888
54164
  try {
53889
- const stat = await fsp25.stat(file2);
54165
+ const stat = await fsp26.stat(file2);
53890
54166
  const prefix = `${stat.mtimeMs}:${stat.size}`;
53891
54167
  return !(entry.sig === prefix || entry.sig.startsWith(`${prefix}:`));
53892
54168
  } catch {
@@ -55693,7 +55969,7 @@ var init_sessionLifecycle = __esm({
55693
55969
  });
55694
55970
 
55695
55971
  // dist/agent/session.js
55696
- import fsp26 from "node:fs/promises";
55972
+ import fsp27 from "node:fs/promises";
55697
55973
  import path49 from "node:path";
55698
55974
  function assertNoPrebuiltSessionWithBuildOptions(options, consumer) {
55699
55975
  if (options.session && options.buildOptions) {
@@ -55772,7 +56048,7 @@ async function collectAgentFileSignatures(files) {
55772
56048
  await mapLimit([...files], FILE_SIGNATURE_STAT_CONCURRENCY, async (file2) => {
55773
56049
  const resolvedFile = normalizePath(path49.resolve(file2));
55774
56050
  try {
55775
- const stat = await fsp26.stat(resolvedFile);
56051
+ const stat = await fsp27.stat(resolvedFile);
55776
56052
  if (!stat.isFile())
55777
56053
  return;
55778
56054
  signatures.set(resolvedFile, {
@@ -56133,13 +56409,13 @@ var init_errors4 = __esm({
56133
56409
  });
56134
56410
 
56135
56411
  // dist/lifecycle/gitignore.js
56136
- import fsp27 from "node:fs/promises";
56412
+ import fsp28 from "node:fs/promises";
56137
56413
  import path50 from "node:path";
56138
56414
  async function readGitignoreFile(gitignorePath) {
56139
56415
  let existing = "";
56140
56416
  let stats;
56141
56417
  try {
56142
- stats = await fsp27.lstat(gitignorePath);
56418
+ stats = await fsp28.lstat(gitignorePath);
56143
56419
  } catch (error51) {
56144
56420
  if (!(error51 instanceof Error && "code" in error51 && error51.code === "ENOENT")) {
56145
56421
  const detail = errorMessage(error51);
@@ -56156,7 +56432,7 @@ async function readGitignoreFile(gitignorePath) {
56156
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.`);
56157
56433
  }
56158
56434
  try {
56159
- existing = await fsp27.readFile(gitignorePath, "utf8");
56435
+ existing = await fsp28.readFile(gitignorePath, "utf8");
56160
56436
  } catch (error51) {
56161
56437
  const detail = errorMessage(error51);
56162
56438
  throw new CodegraphLifecycleUserError(`Unable to read ${gitignorePath}: ${detail}. Check file permissions or rerun with --no-update-gitignore.`);
@@ -56178,43 +56454,38 @@ async function prepareCodegraphLifecycleGitignore(root, options = {}) {
56178
56454
  const manifestTracked = await isGitPathTracked(resolvedRoot, LIFECYCLE_MANIFEST_PATH);
56179
56455
  if (manifestTracked)
56180
56456
  return { status: "tracked", path: GITIGNORE_PATH };
56181
- const [lifecycleIgnored, diskCacheIgnored] = await Promise.all([
56182
- isGitPathIgnored(resolvedRoot, LIFECYCLE_MANIFEST_PATH),
56183
- isGitPathIgnored(resolvedRoot, DISK_CACHE_PROBE_PATH)
56184
- ]);
56185
- const missingRules = [
56186
- ...!lifecycleIgnored ? [LIFECYCLE_GITIGNORE_RULE] : [],
56187
- ...!diskCacheIgnored ? [DISK_CACHE_GITIGNORE_RULE] : []
56188
- ];
56189
- 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) {
56190
56460
  return { status: "already-ignored", path: GITIGNORE_PATH, rules: [...CODEGRAPH_GITIGNORE_RULES] };
56191
56461
  }
56462
+ const missingRules = [LIFECYCLE_GITIGNORE_RULE];
56192
56463
  const gitignorePath = path50.join(resolvedRoot, GITIGNORE_PATH);
56193
56464
  const { existing, newline } = await readGitignoreFile(gitignorePath);
56194
56465
  let suffix = missingRules.map((rule) => `${rule}${newline}`).join("");
56195
56466
  if (existing && !existing.endsWith("\n"))
56196
56467
  suffix = `${newline}${suffix}`;
56197
56468
  try {
56198
- await fsp27.appendFile(gitignorePath, suffix, "utf8");
56469
+ await fsp28.appendFile(gitignorePath, suffix, "utf8");
56199
56470
  } catch (error51) {
56200
56471
  const detail = errorMessage(error51);
56201
56472
  throw new CodegraphLifecycleUserError(`Unable to update ${gitignorePath}: ${detail}. Check file permissions or rerun with --no-update-gitignore.`);
56202
56473
  }
56203
56474
  return { status: "added", path: GITIGNORE_PATH, rules: missingRules };
56204
56475
  }
56205
- 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;
56206
56477
  var init_gitignore = __esm({
56207
56478
  "dist/lifecycle/gitignore.js"() {
56208
56479
  "use strict";
56480
+ init_location();
56209
56481
  init_git();
56210
56482
  init_errors();
56211
56483
  init_errors4();
56212
56484
  LIFECYCLE_MANIFEST_PATH = ".codegraph/manifest.json";
56213
- DISK_CACHE_PROBE_PATH = ".codegraph-cache/index-v1/manifest.json";
56485
+ LIFECYCLE_CACHE_PATH = PROJECT_CACHE_RELATIVE_PATH.replaceAll(path50.sep, "/");
56214
56486
  GITIGNORE_PATH = ".gitignore";
56215
56487
  LIFECYCLE_GITIGNORE_RULE = ".codegraph/";
56216
- DISK_CACHE_GITIGNORE_RULE = ".codegraph-cache/";
56217
- CODEGRAPH_GITIGNORE_RULES = [LIFECYCLE_GITIGNORE_RULE, DISK_CACHE_GITIGNORE_RULE];
56488
+ CODEGRAPH_GITIGNORE_RULES = [LIFECYCLE_GITIGNORE_RULE];
56218
56489
  }
56219
56490
  });
56220
56491
 
@@ -56230,7 +56501,7 @@ __export(manifest_exports, {
56230
56501
  uninitCodegraphLifecycle: () => uninitCodegraphLifecycle
56231
56502
  });
56232
56503
  import { createHash as createHash4, randomUUID as randomUUID2 } from "node:crypto";
56233
- import fsp28 from "node:fs/promises";
56504
+ import fsp29 from "node:fs/promises";
56234
56505
  import path51 from "node:path";
56235
56506
  function codegraphLifecycleManifestPath(root) {
56236
56507
  return path51.join(root, CODEGRAPH_DIR, MANIFEST_FILE);
@@ -56331,11 +56602,11 @@ async function uninitCodegraphLifecycle(root, options = {}) {
56331
56602
  if (!entries.length) {
56332
56603
  return { schemaVersion: MANIFEST_SCHEMA_VERSION, root, removed: false, manifestPath };
56333
56604
  }
56334
- const unknownEntries = entries.filter((entry) => !KNOWN_CODEGRAPH_FILES.has(entry));
56605
+ const unknownEntries = entries.filter((entry) => !Object.hasOwn(KNOWN_CODEGRAPH_FILES, entry));
56335
56606
  if (unknownEntries.length && !options.force) {
56336
56607
  throw new CodegraphLifecycleUserError(`Refusing to remove .codegraph with unknown entries: ${unknownEntries.join(", ")}. Use --force to remove them.`);
56337
56608
  }
56338
- 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);
56339
56610
  if (options.force) {
56340
56611
  for (const entry of removableEntries) {
56341
56612
  await removeCodegraphPath(path51.join(dir, entry), { recursive: true });
@@ -56375,7 +56646,7 @@ async function readLifecycleManifest(root, options = {}) {
56375
56646
  const manifestPath = codegraphLifecycleManifestPath(root);
56376
56647
  let raw;
56377
56648
  try {
56378
- raw = await fsp28.readFile(manifestPath, "utf8");
56649
+ raw = await fsp29.readFile(manifestPath, "utf8");
56379
56650
  } catch (error51) {
56380
56651
  if (error51 instanceof Error && "code" in error51 && error51.code === "ENOENT")
56381
56652
  return null;
@@ -56399,14 +56670,14 @@ function lifecycleManifestTempFilePath(manifestPath) {
56399
56670
  }
56400
56671
  async function writeLifecycleManifest(root, manifest) {
56401
56672
  const manifestPath = codegraphLifecycleManifestPath(root);
56402
- await fsp28.mkdir(path51.dirname(manifestPath), { recursive: true });
56673
+ await fsp29.mkdir(path51.dirname(manifestPath), { recursive: true });
56403
56674
  const tempPath = lifecycleManifestTempFilePath(manifestPath);
56404
56675
  try {
56405
- await fsp28.writeFile(tempPath, `${JSON.stringify(manifest, null, 2)}
56676
+ await fsp29.writeFile(tempPath, `${JSON.stringify(manifest, null, 2)}
56406
56677
  `, "utf8");
56407
- await fsp28.rename(tempPath, manifestPath);
56678
+ await fsp29.rename(tempPath, manifestPath);
56408
56679
  } catch (error51) {
56409
- await fsp28.rm(tempPath, { force: true }).catch(() => {
56680
+ await fsp29.rm(tempPath, { force: true }).catch(() => {
56410
56681
  });
56411
56682
  throw new CodegraphLifecycleUserError(`Unable to write Codegraph lifecycle manifest at ${manifestPath}: ${stringifyError(error51)}`);
56412
56683
  }
@@ -56447,7 +56718,7 @@ async function hashDiscoveredFiles(files, root) {
56447
56718
  const signatures = /* @__PURE__ */ new Map();
56448
56719
  await mapLimit(sorted, FILE_SIGNATURE_STAT_CONCURRENCY2, async (file2) => {
56449
56720
  try {
56450
- const stat = await fsp28.stat(file2);
56721
+ const stat = await fsp29.stat(file2);
56451
56722
  signatures.set(file2, { size: stat.size, mtimeMs: stat.mtimeMs });
56452
56723
  } catch (error51) {
56453
56724
  if (isMissingStatRace2(error51))
@@ -56506,18 +56777,18 @@ function sha256(value) {
56506
56777
  }
56507
56778
  async function removeCodegraphPath(target, options) {
56508
56779
  try {
56509
- await fsp28.rm(target, { ...options.recursive ? { recursive: true } : {}, force: true });
56780
+ await fsp29.rm(target, { ...options.recursive ? { recursive: true } : {}, force: true });
56510
56781
  } catch (error51) {
56511
56782
  throw new CodegraphLifecycleUserError(`Unable to remove ${target}: ${stringifyError(error51)}`);
56512
56783
  }
56513
56784
  }
56514
56785
  async function readCodegraphDirEntries(dir) {
56515
56786
  try {
56516
- const stats = await fsp28.lstat(dir);
56787
+ const stats = await fsp29.lstat(dir);
56517
56788
  if (stats.isSymbolicLink()) {
56518
56789
  throw new CodegraphLifecycleUserError(`Refusing to traverse symbolic link as .codegraph lifecycle directory: ${dir}`);
56519
56790
  }
56520
- return await fsp28.readdir(dir);
56791
+ return await fsp29.readdir(dir);
56521
56792
  } catch (error51) {
56522
56793
  if (error51 instanceof CodegraphLifecycleUserError)
56523
56794
  throw error51;
@@ -56528,7 +56799,7 @@ async function readCodegraphDirEntries(dir) {
56528
56799
  }
56529
56800
  async function removeDirIfEmpty(dir) {
56530
56801
  try {
56531
- await fsp28.rmdir(dir);
56802
+ await fsp29.rmdir(dir);
56532
56803
  } catch (error51) {
56533
56804
  if (error51 instanceof Error && "code" in error51 && error51.code === "ENOTEMPTY")
56534
56805
  return;
@@ -56550,7 +56821,7 @@ function isOptionalStringArray(value) {
56550
56821
  return true;
56551
56822
  return Array.isArray(value) && value.every((entry) => typeof entry === "string");
56552
56823
  }
56553
- 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;
56554
56825
  var init_manifest2 = __esm({
56555
56826
  "dist/lifecycle/manifest.js"() {
56556
56827
  "use strict";
@@ -56567,7 +56838,13 @@ var init_manifest2 = __esm({
56567
56838
  MANIFEST_FILE = "manifest.json";
56568
56839
  SERVER_REGISTRY_FILE = "server.json";
56569
56840
  SERVER_LOG_FILE = "server.log";
56570
- 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
+ };
56571
56848
  FILE_SIGNATURE_STAT_CONCURRENCY2 = 64;
56572
56849
  }
56573
56850
  });
@@ -56620,7 +56897,7 @@ function formatSyncResult(label, result) {
56620
56897
  const summary = `${label} Codegraph at ${result.root}: ${result.manifest.fileCount} files${changeLabel}. Manifest: ${result.manifestPath}`;
56621
56898
  if (result.gitignore?.status === "added") {
56622
56899
  const gitignorePath = path52.join(result.root, result.gitignore.path);
56623
- 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/";
56624
56901
  return `${summary}
56625
56902
  Updated Git ignore policy at ${gitignorePath}: added ${rules}.`;
56626
56903
  }
@@ -57413,14 +57690,14 @@ __export(skill_exports, {
57413
57690
  getSkillTargetDirForAgent: () => getSkillTargetDirForAgent,
57414
57691
  handleSkillCommand: () => handleSkillCommand
57415
57692
  });
57416
- import fsp29 from "node:fs/promises";
57417
- import os5 from "node:os";
57693
+ import fsp30 from "node:fs/promises";
57694
+ import os6 from "node:os";
57418
57695
  import path53 from "node:path";
57419
57696
  function getBundledSkillDir(packageRoot) {
57420
57697
  const candidate = path53.join(packageRoot, "codegraph-skill", "codegraph");
57421
57698
  return pathExists(path53.join(candidate, "SKILL.md")) ? candidate : null;
57422
57699
  }
57423
- function getSkillTargetDirForAgent(agent, homeDir = os5.homedir(), env = process.env) {
57700
+ function getSkillTargetDirForAgent(agent, homeDir = os6.homedir(), env = process.env) {
57424
57701
  if (agent === "agents") {
57425
57702
  return path53.join(homeDir, ".agents", "skills", "codegraph");
57426
57703
  }
@@ -57481,10 +57758,10 @@ function isCommandAvailableOnPath(command) {
57481
57758
  }
57482
57759
  async function copyDirectoryRecursive(sourceDir, targetDir, overwrite) {
57483
57760
  if (overwrite && pathExists(targetDir)) {
57484
- await fsp29.rm(targetDir, { recursive: true, force: true });
57761
+ await fsp30.rm(targetDir, { recursive: true, force: true });
57485
57762
  }
57486
- await fsp29.mkdir(targetDir, { recursive: true });
57487
- const entries = await fsp29.readdir(sourceDir, { withFileTypes: true });
57763
+ await fsp30.mkdir(targetDir, { recursive: true });
57764
+ const entries = await fsp30.readdir(sourceDir, { withFileTypes: true });
57488
57765
  for (const entry of entries) {
57489
57766
  const sourcePath = path53.join(sourceDir, entry.name);
57490
57767
  const targetPath = path53.join(targetDir, entry.name);
@@ -57495,7 +57772,7 @@ async function copyDirectoryRecursive(sourceDir, targetDir, overwrite) {
57495
57772
  if (!overwrite && pathExists(targetPath)) {
57496
57773
  throw new Error(`Target file already exists: ${normalizePathForDisplay(targetPath)}. Re-run with --force to overwrite.`);
57497
57774
  }
57498
- await fsp29.copyFile(sourcePath, targetPath);
57775
+ await fsp30.copyFile(sourcePath, targetPath);
57499
57776
  }
57500
57777
  }
57501
57778
  function normalizePathSegmentForComparison(segment) {
@@ -57638,8 +57915,8 @@ var init_skill = __esm({
57638
57915
 
57639
57916
  // dist/installer/locks.js
57640
57917
  import { createHash as createHash5, randomUUID as randomUUID3 } from "node:crypto";
57641
- import fsp30, {} from "node:fs/promises";
57642
- import os6 from "node:os";
57918
+ import fsp31, {} from "node:fs/promises";
57919
+ import os7 from "node:os";
57643
57920
  import path54 from "node:path";
57644
57921
  function opencodeConfigHome(settings) {
57645
57922
  const configHome = settings.env.XDG_CONFIG_HOME?.trim();
@@ -57675,7 +57952,7 @@ function installerTransactionLockScopes(settings) {
57675
57952
  }
57676
57953
  function installerTransactionLockPath(scope) {
57677
57954
  const digest = createHash5("sha256").update(scope).digest("hex");
57678
- return path54.join(os6.tmpdir(), `codegraph-installer-${digest}.lock`);
57955
+ return path54.join(os7.tmpdir(), `codegraph-installer-${digest}.lock`);
57679
57956
  }
57680
57957
  async function withInstallerLeaseLock(lockPath, resourceName, operation) {
57681
57958
  const lock = await acquireInstallerLeaseLock(lockPath, resourceName);
@@ -57693,14 +57970,14 @@ async function acquireInstallerLeaseLock(lockPath, resourceName) {
57693
57970
  let file2;
57694
57971
  try {
57695
57972
  const owner = randomUUID3();
57696
- file2 = await fsp30.open(acquisitionPath, "wx", 384);
57973
+ file2 = await fsp31.open(acquisitionPath, "wx", 384);
57697
57974
  await writeInstallerLeaseMetadata(file2, owner);
57698
57975
  await file2.close();
57699
57976
  file2 = void 0;
57700
- await fsp30.link(acquisitionPath, lockPath);
57977
+ await fsp31.link(acquisitionPath, lockPath);
57701
57978
  published = true;
57702
- await fsp30.rm(acquisitionPath, { force: true });
57703
- file2 = await fsp30.open(lockPath, "r+");
57979
+ await fsp31.rm(acquisitionPath, { force: true });
57980
+ file2 = await fsp31.open(lockPath, "r+");
57704
57981
  const lockFile = file2;
57705
57982
  const renewalTimer = setInterval(() => {
57706
57983
  void writeInstallerLeaseMetadata(lockFile, owner).catch(() => void 0);
@@ -57709,9 +57986,9 @@ async function acquireInstallerLeaseLock(lockPath, resourceName) {
57709
57986
  return { file: lockFile, lockPath, metadataPath: lockPath, owner, renewalTimer };
57710
57987
  } catch (error51) {
57711
57988
  await file2?.close().catch(() => void 0);
57712
- await fsp30.rm(acquisitionPath, { force: true }).catch(() => void 0);
57989
+ await fsp31.rm(acquisitionPath, { force: true }).catch(() => void 0);
57713
57990
  if (published)
57714
- await fsp30.rm(lockPath, { force: true }).catch(() => void 0);
57991
+ await fsp31.rm(lockPath, { force: true }).catch(() => void 0);
57715
57992
  if (!isInstallerLockPublishConflict(error51, lockPath))
57716
57993
  throw error51;
57717
57994
  const conflict = await resolveInstallerLeaseConflict(lockPath);
@@ -57729,10 +58006,10 @@ async function acquireInstallerLeaseLock(lockPath, resourceName) {
57729
58006
  async function resolveInstallerLeaseConflict(lockPath) {
57730
58007
  let observed;
57731
58008
  try {
57732
- const stats = await fsp30.lstat(lockPath);
58009
+ const stats = await fsp31.lstat(lockPath);
57733
58010
  if (!stats.isFile())
57734
58011
  return "blocked";
57735
- observed = { content: await fsp30.readFile(lockPath, "utf8"), mtimeMs: stats.mtimeMs };
58012
+ observed = { content: await fsp31.readFile(lockPath, "utf8"), mtimeMs: stats.mtimeMs };
57736
58013
  } catch (error51) {
57737
58014
  if (isFileSystemErrorCode(error51, "ENOENT"))
57738
58015
  return "absent";
@@ -57754,7 +58031,7 @@ async function resolveInstallerLeaseConflict(lockPath) {
57754
58031
  async function reclaimAbandonedInstallerLease(lockPath, observed) {
57755
58032
  const reclaimPath = `${lockPath}.reclaim-${process.pid}-${randomUUID3()}`;
57756
58033
  try {
57757
- await fsp30.rename(lockPath, reclaimPath);
58034
+ await fsp31.rename(lockPath, reclaimPath);
57758
58035
  } catch (error51) {
57759
58036
  if (isFileSystemErrorCode(error51, "ENOENT"))
57760
58037
  return "absent";
@@ -57762,20 +58039,20 @@ async function reclaimAbandonedInstallerLease(lockPath, observed) {
57762
58039
  }
57763
58040
  let verified = false;
57764
58041
  try {
57765
- const stats = await fsp30.lstat(reclaimPath);
57766
- 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;
57767
58044
  } catch {
57768
58045
  verified = false;
57769
58046
  }
57770
58047
  if (verified) {
57771
- await fsp30.rm(reclaimPath, { force: true });
58048
+ await fsp31.rm(reclaimPath, { force: true });
57772
58049
  return "reclaimed";
57773
58050
  }
57774
58051
  try {
57775
- await fsp30.link(reclaimPath, lockPath);
58052
+ await fsp31.link(reclaimPath, lockPath);
57776
58053
  } catch {
57777
58054
  }
57778
- await fsp30.rm(reclaimPath, { force: true });
58055
+ await fsp31.rm(reclaimPath, { force: true });
57779
58056
  return "blocked";
57780
58057
  }
57781
58058
  function isInstallerLockPublishConflict(error51, lockPath) {
@@ -57812,9 +58089,9 @@ async function releaseInstallerLeaseLock(lock) {
57812
58089
  clearInterval(lock.renewalTimer);
57813
58090
  await lock.file.close().catch(() => void 0);
57814
58091
  try {
57815
- const metadata = parseInstallerLeaseMetadata(await fsp30.readFile(lock.metadataPath, "utf8"));
58092
+ const metadata = parseInstallerLeaseMetadata(await fsp31.readFile(lock.metadataPath, "utf8"));
57816
58093
  if (metadata?.owner === lock.owner)
57817
- await fsp30.rm(lock.lockPath, { force: true });
58094
+ await fsp31.rm(lock.lockPath, { force: true });
57818
58095
  } catch (error51) {
57819
58096
  if (!isFileSystemErrorCode(error51, "ENOENT"))
57820
58097
  throw error51;
@@ -57840,8 +58117,8 @@ var init_locks = __esm({
57840
58117
 
57841
58118
  // dist/installer/registry.js
57842
58119
  import { createHash as createHash6, randomUUID as randomUUID4 } from "node:crypto";
57843
- import fsp31 from "node:fs/promises";
57844
- import os7 from "node:os";
58120
+ import fsp32 from "node:fs/promises";
58121
+ import os8 from "node:os";
57845
58122
  import path55 from "node:path";
57846
58123
  import { applyEdits, modify, parse as parseJsonc2 } from "jsonc-parser";
57847
58124
  function listInstallTargets() {
@@ -57968,7 +58245,7 @@ async function installDefinitions(definitions, options) {
57968
58245
  return await withInstallerTransactionLocks(settings, install);
57969
58246
  }
57970
58247
  async function prepareInstallPlan(definitions, settings, dryRun, force) {
57971
- const bundledSkill = await fsp31.readFile(bundledSkillFilePath());
58248
+ const bundledSkill = await fsp32.readFile(bundledSkillFilePath());
57972
58249
  const files = [];
57973
58250
  const conflicts = [];
57974
58251
  for (const definition of definitions) {
@@ -58096,7 +58373,7 @@ async function rollbackInstallPlan(plan) {
58096
58373
  }
58097
58374
  async function removePlannedInstallFile(filePath, root) {
58098
58375
  await assertNoSymbolicLinksWithinRoot(filePath, root);
58099
- await fsp31.rm(filePath, { force: true });
58376
+ await fsp32.rm(filePath, { force: true });
58100
58377
  }
58101
58378
  async function verifyInstallPlan(plan, verifyInstalledConfig) {
58102
58379
  for (const file2 of plan.files) {
@@ -58590,7 +58867,7 @@ function installerSettings(options) {
58590
58867
  if (env === void 0)
58591
58868
  env = options.homeDir === void 0 ? process.env : {};
58592
58869
  return {
58593
- homeDir: options.homeDir ?? os7.homedir(),
58870
+ homeDir: options.homeDir ?? os8.homedir(),
58594
58871
  env
58595
58872
  };
58596
58873
  }
@@ -58607,14 +58884,14 @@ function bundledSkillFilePath() {
58607
58884
  async function snapshotInstallFile(filePath, root) {
58608
58885
  await assertNoSymbolicLinksWithinRoot(filePath, root);
58609
58886
  try {
58610
- const stats = await fsp31.lstat(filePath);
58887
+ const stats = await fsp32.lstat(filePath);
58611
58888
  if (stats.isSymbolicLink())
58612
58889
  throw unsafeSymbolicLinkError(filePath);
58613
58890
  if (!stats.isFile()) {
58614
58891
  throw new Error(`Expected a regular file at ${normalizePathForDisplay(filePath)} before running the installer.`);
58615
58892
  }
58616
58893
  return {
58617
- bytes: await fsp31.readFile(filePath),
58894
+ bytes: await fsp32.readFile(filePath),
58618
58895
  mode: stats.mode & 4095
58619
58896
  };
58620
58897
  } catch (error51) {
@@ -58634,7 +58911,7 @@ async function assertNoSymbolicLinksWithinRoot(filePath, root) {
58634
58911
  }
58635
58912
  for (let current = resolvedFilePath; current !== resolvedRoot; current = path55.dirname(current)) {
58636
58913
  try {
58637
- const stats = await fsp31.lstat(current);
58914
+ const stats = await fsp32.lstat(current);
58638
58915
  if (stats.isSymbolicLink())
58639
58916
  throw unsafeSymbolicLinkError(current);
58640
58917
  } catch (error51) {
@@ -58649,7 +58926,7 @@ async function confirmMissingPathHasDirectoryAncestors(filePath) {
58649
58926
  let current = path55.dirname(filePath);
58650
58927
  while (true) {
58651
58928
  try {
58652
- const stats = await fsp31.lstat(current);
58929
+ const stats = await fsp32.lstat(current);
58653
58930
  if (stats.isSymbolicLink())
58654
58931
  throw unsafeSymbolicLinkError(current);
58655
58932
  if (!stats.isDirectory()) {
@@ -58688,7 +58965,7 @@ async function writeTextFileAtomic(filePath, content, options = {}) {
58688
58965
  await assertNoSymbolicLinkAtPath(filePath);
58689
58966
  else
58690
58967
  await assertNoSymbolicLinksWithinRoot(filePath, options.root);
58691
- await fsp31.mkdir(path55.dirname(filePath), { recursive: true });
58968
+ await fsp32.mkdir(path55.dirname(filePath), { recursive: true });
58692
58969
  await withInstallerLeaseLock(`${filePath}.codegraph-lock`, normalizePathForDisplay(filePath), async () => {
58693
58970
  if (options.root === void 0)
58694
58971
  await assertNoSymbolicLinkAtPath(filePath);
@@ -58700,7 +58977,7 @@ async function writeTextFileAtomic(filePath, content, options = {}) {
58700
58977
  let mode = options.mode ?? 384;
58701
58978
  if (options.mode === void 0) {
58702
58979
  try {
58703
- const existing = await fsp31.lstat(filePath);
58980
+ const existing = await fsp32.lstat(filePath);
58704
58981
  if (existing.isSymbolicLink())
58705
58982
  throw unsafeSymbolicLinkError(filePath);
58706
58983
  mode = existing.mode & 4095;
@@ -58709,8 +58986,8 @@ async function writeTextFileAtomic(filePath, content, options = {}) {
58709
58986
  throw error51;
58710
58987
  }
58711
58988
  }
58712
- await fsp31.writeFile(temporaryPath, content, { encoding: "utf8", flag: "wx", mode });
58713
- await fsp31.chmod(temporaryPath, mode);
58989
+ await fsp32.writeFile(temporaryPath, content, { encoding: "utf8", flag: "wx", mode });
58990
+ await fsp32.chmod(temporaryPath, mode);
58714
58991
  await renameTemporaryFile(temporaryPath, filePath);
58715
58992
  } catch (error51) {
58716
58993
  if (isFileSystemErrorCode(error51, "EACCES") || isFileSystemErrorCode(error51, "EPERM")) {
@@ -58718,13 +58995,13 @@ async function writeTextFileAtomic(filePath, content, options = {}) {
58718
58995
  }
58719
58996
  throw error51;
58720
58997
  } finally {
58721
- await fsp31.rm(temporaryPath, { force: true }).catch(() => void 0);
58998
+ await fsp32.rm(temporaryPath, { force: true }).catch(() => void 0);
58722
58999
  }
58723
59000
  });
58724
59001
  }
58725
59002
  async function assertNoSymbolicLinkAtPath(filePath) {
58726
59003
  try {
58727
- const stats = await fsp31.lstat(filePath);
59004
+ const stats = await fsp32.lstat(filePath);
58728
59005
  if (stats.isSymbolicLink())
58729
59006
  throw unsafeSymbolicLinkError(filePath);
58730
59007
  } catch (error51) {
@@ -58739,7 +59016,7 @@ function unsafeSymbolicLinkError(filePath) {
58739
59016
  async function renameTemporaryFile(temporaryPath, filePath) {
58740
59017
  for (let attempt = 0; attempt < 5; attempt += 1) {
58741
59018
  try {
58742
- await fsp31.rename(temporaryPath, filePath);
59019
+ await fsp32.rename(temporaryPath, filePath);
58743
59020
  return;
58744
59021
  } catch (error51) {
58745
59022
  if (!isFileSystemErrorCode(error51, "EPERM") || attempt === 4)
@@ -58927,7 +59204,7 @@ __export(doctor_exports, {
58927
59204
  formatDoctorSummary: () => formatDoctorSummary
58928
59205
  });
58929
59206
  import fs25 from "node:fs";
58930
- import os8 from "node:os";
59207
+ import os9 from "node:os";
58931
59208
  import path57 from "node:path";
58932
59209
  function statIfExists(filePath) {
58933
59210
  try {
@@ -58960,15 +59237,15 @@ function detectIndexedArtifactType(filePath, stats) {
58960
59237
  return "artifactBundle";
58961
59238
  }
58962
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
+ }
58963
59243
  if (normalized.endsWith("/codegraph.json") || normalized.endsWith(".json")) {
58964
59244
  return "jsonGraph";
58965
59245
  }
58966
59246
  if (normalized.endsWith("/graph.sqlite") || normalized.endsWith(".sqlite")) {
58967
59247
  return "sqliteGraph";
58968
59248
  }
58969
- if (normalized.endsWith("/.codegraph-cache") || normalized.includes("/.codegraph-cache/")) {
58970
- return "diskCache";
58971
- }
58972
59249
  return "unknown";
58973
59250
  }
58974
59251
  function buildIndexedArtifactReport(indexPath) {
@@ -59155,7 +59432,7 @@ function readCacheLocationField(configPath) {
59155
59432
  }
59156
59433
  }
59157
59434
  function readUserCacheLocation() {
59158
- 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");
59159
59436
  return readCacheLocationField(path57.join(configRoot, "codegraph", "config.json"));
59160
59437
  }
59161
59438
  function readEffectiveCacheLocation(root) {
@@ -60668,7 +60945,7 @@ var chunk_exports = {};
60668
60945
  __export(chunk_exports, {
60669
60946
  handleChunkCommand: () => handleChunkCommand
60670
60947
  });
60671
- import fsp32 from "node:fs/promises";
60948
+ import fsp33 from "node:fs/promises";
60672
60949
  import path61 from "node:path";
60673
60950
  function normalizeChunkLanguageId(languageId) {
60674
60951
  return chunkLanguageAliases2[languageId] ?? languageId;
@@ -60701,7 +60978,7 @@ async function handleChunkCommand(context) {
60701
60978
  }
60702
60979
  try {
60703
60980
  const filePath = path61.resolve(context.cwd(), parseSourceLocationInput(inputFilePath).file);
60704
- const source = await fsp32.readFile(filePath, "utf8");
60981
+ const source = await fsp33.readFile(filePath, "utf8");
60705
60982
  const ext = path61.extname(filePath).toLowerCase();
60706
60983
  let languageId = context.getOpt("--language");
60707
60984
  if (!languageId) {
@@ -61791,7 +62068,7 @@ var init_fileView = __esm({
61791
62068
  });
61792
62069
 
61793
62070
  // dist/documentLinks/check.js
61794
- import fsp33 from "node:fs/promises";
62071
+ import fsp34 from "node:fs/promises";
61795
62072
  import path64 from "node:path";
61796
62073
  async function checkMarkdownLinks(projectRoot, discovery) {
61797
62074
  const root = path64.resolve(projectRoot);
@@ -61799,14 +62076,14 @@ async function checkMarkdownLinks(projectRoot, discovery) {
61799
62076
  }
61800
62077
  async function checkMarkdownLinksInFiles(projectRoot, files) {
61801
62078
  const root = path64.resolve(projectRoot);
61802
- const realRoot = await fsp33.realpath(root);
62079
+ const realRoot = await fsp34.realpath(root);
61803
62080
  const markdownFileCandidates = await Promise.all(Array.from(files, async (file2) => {
61804
62081
  const absoluteFile = path64.resolve(root, file2);
61805
62082
  const relativeFile = toProjectRelativePath(root, absoluteFile);
61806
62083
  if (relativeFile !== null)
61807
62084
  return path64.resolve(realRoot, relativeFile);
61808
62085
  try {
61809
- return await fsp33.realpath(absoluteFile);
62086
+ return await fsp34.realpath(absoluteFile);
61810
62087
  } catch {
61811
62088
  return absoluteFile;
61812
62089
  }
@@ -61875,7 +62152,7 @@ async function recoverDisplayFileCasing(file2) {
61875
62152
  const dir = path64.dirname(file2);
61876
62153
  const base = path64.basename(file2);
61877
62154
  try {
61878
- const entries = await fsp33.readdir(dir);
62155
+ const entries = await fsp34.readdir(dir);
61879
62156
  const match = entries.find((entry) => entry.toLowerCase() === base.toLowerCase());
61880
62157
  return match ? path64.join(dir, match) : file2;
61881
62158
  } catch {
@@ -61949,8 +62226,8 @@ async function cachedTargetStatus(target, targetStatusByPath) {
61949
62226
  }
61950
62227
  async function readTargetStatus(target) {
61951
62228
  try {
61952
- const realPath = await fsp33.realpath(target);
61953
- const stats = await fsp33.stat(realPath);
62229
+ const realPath = await fsp34.realpath(target);
62230
+ const stats = await fsp34.stat(realPath);
61954
62231
  return { status: "found", isDirectory: stats.isDirectory(), realPath };
61955
62232
  } catch (error51) {
61956
62233
  if (isMissingPathError3(error51))
@@ -61971,7 +62248,7 @@ async function cachedMarkdownAnchors(target, anchorsByPath) {
61971
62248
  }
61972
62249
  async function readMarkdownFile(file2) {
61973
62250
  try {
61974
- return await fsp33.readFile(file2, "utf8");
62251
+ return await fsp34.readFile(file2, "utf8");
61975
62252
  } catch (error51) {
61976
62253
  if (error51 instanceof Error) {
61977
62254
  throw new Error(`Failed to read Markdown file ${normalizePath(file2)}: ${error51.message}`);
@@ -63352,7 +63629,7 @@ var init_navigation_references = __esm({
63352
63629
  });
63353
63630
 
63354
63631
  // dist/sql/navigation.js
63355
- import fsp34 from "node:fs/promises";
63632
+ import fsp35 from "node:fs/promises";
63356
63633
  import path65 from "node:path";
63357
63634
  function isSqlFile2(filePath) {
63358
63635
  return path65.extname(filePath).toLowerCase() === ".sql";
@@ -63638,7 +63915,7 @@ async function sourceForFile(filePath, index) {
63638
63915
  if (cached2 !== void 0)
63639
63916
  return cached2;
63640
63917
  const parsed = index.parsed?.get(key);
63641
- const source = parsed ? parsed.source : await fsp34.readFile(filePath, "utf8");
63918
+ const source = parsed ? parsed.source : await fsp35.readFile(filePath, "utf8");
63642
63919
  cache.sourceByFile.set(key, source);
63643
63920
  return source;
63644
63921
  }
@@ -65342,7 +65619,7 @@ var init_changes = __esm({
65342
65619
  });
65343
65620
 
65344
65621
  // dist/sql/review.js
65345
- import fsp35 from "node:fs/promises";
65622
+ import fsp36 from "node:fs/promises";
65346
65623
  import path67 from "node:path";
65347
65624
  function isSqlFile3(filePath) {
65348
65625
  return path67.extname(filePath).toLowerCase() === ".sql";
@@ -65357,7 +65634,7 @@ function sortEntries(left, right) {
65357
65634
  }
65358
65635
  async function readExistingFile(filePath) {
65359
65636
  try {
65360
- return await fsp35.readFile(filePath, "utf8");
65637
+ return await fsp36.readFile(filePath, "utf8");
65361
65638
  } catch {
65362
65639
  return null;
65363
65640
  }
@@ -68720,7 +68997,7 @@ var init_referenceCache = __esm({
68720
68997
  });
68721
68998
 
68722
68999
  // dist/review/summaries.js
68723
- import fsp36 from "node:fs/promises";
69000
+ import fsp37 from "node:fs/promises";
68724
69001
  import path70 from "node:path";
68725
69002
  import { performance as performance9 } from "node:perf_hooks";
68726
69003
  function relativePath2(root, file2) {
@@ -68982,7 +69259,7 @@ function sameRange(left, right) {
68982
69259
  }
68983
69260
  async function summarizeChangedFiles2(input) {
68984
69261
  const { projectRoot, index, changedFileList, diffHunksByFile, diffKindsByFile, diffChangesByFile, explicitFiles, existenceByFile, deletedSnapshots, loadSource: suppliedLoadSource, includeSymbolDetails, includeDiffContext, diffContextLines, maxCallsites, referenceConcurrency, diagnostics, reviewTimings } = input;
68985
- const readSource = suppliedLoadSource ?? (async (file2) => await fsp36.readFile(file2, "utf8"));
69262
+ const readSource = suppliedLoadSource ?? (async (file2) => await fsp37.readFile(file2, "utf8"));
68986
69263
  const sourceCache = /* @__PURE__ */ new Map();
68987
69264
  const loadSource = (file2) => {
68988
69265
  const key = fileIdentityKey(file2);
@@ -74907,9 +75184,9 @@ Usage:
74907
75184
  codegraph uninit --root <path> [--force] [--json | --pretty]
74908
75185
 
74909
75186
  State:
74910
- 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.
74911
- 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.
74912
- 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.
74913
75190
  Positional paths and --root are alternatives for lifecycle commands; do not combine them.
74914
75191
  `;
74915
75192
  AFFECTED_HELP_TEXT = `codegraph affected - List tests likely affected by changed files
@@ -101603,7 +101880,7 @@ var init_dist4 = __esm({
101603
101880
  });
101604
101881
 
101605
101882
  // dist/mcp/http.js
101606
- import os9 from "node:os";
101883
+ import os10 from "node:os";
101607
101884
  function getRequestPath(request) {
101608
101885
  return new URL(request.url ?? "/", "http://127.0.0.1").pathname;
101609
101886
  }
@@ -101806,11 +102083,11 @@ function isWildcardBindHost(host) {
101806
102083
  }
101807
102084
  function localInterfaceHostnames() {
101808
102085
  const hosts = /* @__PURE__ */ new Set();
101809
- const hostname3 = os9.hostname().trim().toLowerCase();
102086
+ const hostname3 = os10.hostname().trim().toLowerCase();
101810
102087
  if (hostname3) {
101811
102088
  hosts.add(hostname3);
101812
102089
  }
101813
- const interfaces = os9.networkInterfaces();
102090
+ const interfaces = os10.networkInterfaces();
101814
102091
  for (const entries of Object.values(interfaces)) {
101815
102092
  for (const entry of entries ?? []) {
101816
102093
  if (entry.internal)
@@ -103929,7 +104206,7 @@ var graphDelta_exports = {};
103929
104206
  __export(graphDelta_exports, {
103930
104207
  handleGraphDeltaCommand: () => handleGraphDeltaCommand
103931
104208
  });
103932
- import fsp37 from "node:fs/promises";
104209
+ import fsp38 from "node:fs/promises";
103933
104210
  function formatGraphDeltaReport(delta) {
103934
104211
  const lines = [];
103935
104212
  lines.push("Graph delta");
@@ -103996,7 +104273,7 @@ async function handleGraphDeltaCommand(context) {
103996
104273
  const outputFile = outputArg ? normalizePath(resolveFilePathFromRoot(context.cwd(), outputArg)) : void 0;
103997
104274
  if (outputFile) {
103998
104275
  const output = context.hasFlag("--json") ? JSON.stringify(delta, null, 2) : formatGraphDeltaReport(delta);
103999
- await fsp37.writeFile(outputFile, `${output}
104276
+ await fsp38.writeFile(outputFile, `${output}
104000
104277
  `, "utf8");
104001
104278
  } else {
104002
104279
  writeCliOutput(context, delta, formatGraphDeltaReport);
@@ -104296,7 +104573,7 @@ var init_symbol_render = __esm({
104296
104573
  });
104297
104574
 
104298
104575
  // dist/sql/graph.js
104299
- import fsp38 from "node:fs/promises";
104576
+ import fsp39 from "node:fs/promises";
104300
104577
  function statementNodeId(fact) {
104301
104578
  return `sql:statement:${fact.filePath}:${fact.startLine}-${fact.endLine}`;
104302
104579
  }
@@ -104451,7 +104728,7 @@ function projectSqlFactsToGraph(facts) {
104451
104728
  };
104452
104729
  }
104453
104730
  async function buildSqlArtifactGraphFromFiles(files) {
104454
- 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"))));
104455
104732
  return projectSqlFactsToGraph(factGroups.flat());
104456
104733
  }
104457
104734
  var init_graph = __esm({
@@ -104482,7 +104759,7 @@ __export(graph_exports, {
104482
104759
  handleGraphCommand: () => handleGraphCommand
104483
104760
  });
104484
104761
  import fs42 from "node:fs";
104485
- import fsp39 from "node:fs/promises";
104762
+ import fsp40 from "node:fs/promises";
104486
104763
  import path88 from "node:path";
104487
104764
  import { performance as performance12 } from "node:perf_hooks";
104488
104765
  function resolveGraphCacheMode(cache) {
@@ -104676,7 +104953,7 @@ async function handleGraphCommand(context) {
104676
104953
  };
104677
104954
  const writeOut = async (text) => {
104678
104955
  if (outputFile) {
104679
- await fsp39.writeFile(outputFile, `${text}
104956
+ await fsp40.writeFile(outputFile, `${text}
104680
104957
  `, "utf8");
104681
104958
  } else {
104682
104959
  context.writeStdoutLine(text);
@@ -105619,7 +105896,7 @@ var init_report3 = __esm({
105619
105896
  });
105620
105897
 
105621
105898
  // dist/drift/artifact.js
105622
- import fsp40 from "node:fs/promises";
105899
+ import fsp41 from "node:fs/promises";
105623
105900
  import path90 from "node:path";
105624
105901
  function readStringRecord2(value) {
105625
105902
  if (!isPlainRecord(value))
@@ -105643,7 +105920,7 @@ function parseManifest(value) {
105643
105920
  }
105644
105921
  async function readJson(filePath, label) {
105645
105922
  try {
105646
- return JSON.parse(await fsp40.readFile(filePath, "utf8"));
105923
+ return JSON.parse(await fsp41.readFile(filePath, "utf8"));
105647
105924
  } catch (error51) {
105648
105925
  if (error51 instanceof Error && "code" in error51 && error51.code === "ENOENT") {
105649
105926
  throw new Error(`Codegraph artifact ${label} is missing.`);
@@ -105777,8 +106054,8 @@ var init_artifact3 = __esm({
105777
106054
 
105778
106055
  // dist/drift/git.js
105779
106056
  import { execFile } from "node:child_process";
105780
- import fsp41 from "node:fs/promises";
105781
- import os10 from "node:os";
106057
+ import fsp42 from "node:fs/promises";
106058
+ import os11 from "node:os";
105782
106059
  import path91 from "node:path";
105783
106060
  import { promisify } from "node:util";
105784
106061
  function snapshotOptions(options) {
@@ -105798,7 +106075,7 @@ async function cleanupTempDir(dir) {
105798
106075
  if (!dir)
105799
106076
  return;
105800
106077
  try {
105801
- await fsp41.rm(dir, { recursive: true, force: true });
106078
+ await fsp42.rm(dir, { recursive: true, force: true });
105802
106079
  } catch {
105803
106080
  }
105804
106081
  }
@@ -105817,7 +106094,7 @@ async function materializeGitRef(root, ref, prefix) {
105817
106094
  }
105818
106095
  if (checkoutRef === void 0 || checkoutRef === "." || isGitWorktreeSentinel(checkoutRef))
105819
106096
  return { root };
105820
- const tempRoot = await fsp41.mkdtemp(path91.join(os10.tmpdir(), prefix));
106097
+ const tempRoot = await fsp42.mkdtemp(path91.join(os11.tmpdir(), prefix));
105821
106098
  try {
105822
106099
  await execFileAsync("git", ["clone", "--quiet", "--no-checkout", root, tempRoot], { env: process.env });
105823
106100
  const checkoutCommit = await resolveGitCommit(root, checkoutRef);
@@ -106836,7 +107113,7 @@ var init_navigation3 = __esm({
106836
107113
  });
106837
107114
 
106838
107115
  // dist/graphs/grep.js
106839
- import fsp42 from "node:fs/promises";
107116
+ import fsp43 from "node:fs/promises";
106840
107117
  async function* streamAstGrep(projectRoot, querySource, patterns = [
106841
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}"
106842
107119
  ], opts) {
@@ -106894,7 +107171,7 @@ async function* streamTextGrep(projectRoot, patternSource, patterns = [
106894
107171
  break;
106895
107172
  let source;
106896
107173
  try {
106897
- source = await fsp42.readFile(file2, "utf8");
107174
+ source = await fsp43.readFile(file2, "utf8");
106898
107175
  } catch {
106899
107176
  continue;
106900
107177
  }