@lzehrung/codegraph 2.3.12 → 2.3.14

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
@@ -13552,6 +13552,7 @@ __export(git_exports, {
13552
13552
  isGitPathTracked: () => isGitPathTracked,
13553
13553
  isGitProjectRootIgnored: () => isGitProjectRootIgnored,
13554
13554
  isGitRepo: () => isGitRepo,
13555
+ isGitTimeoutError: () => isGitTimeoutError,
13555
13556
  isGitWorktreeSentinel: () => isGitWorktreeSentinel,
13556
13557
  listChangedFiles: () => listChangedFiles,
13557
13558
  listGitExcludeFiles: () => listGitExcludeFiles,
@@ -13605,8 +13606,9 @@ function decodeGitPath(rawPath) {
13605
13606
  }
13606
13607
  return Buffer.from(bytes).toString("utf8");
13607
13608
  }
13608
- function setGitExecutableForTests(executable) {
13609
+ function setGitExecutableForTests(executable, prefixArgs = []) {
13609
13610
  gitExecutableForTests = executable;
13611
+ gitExecutablePrefixArgsForTests = executable ? [...prefixArgs] : [];
13610
13612
  }
13611
13613
  function clearGitRepositoryCheckCacheForTests() {
13612
13614
  gitRepositoryChecks.clear();
@@ -13673,12 +13675,13 @@ async function runGit(projectRoot, args, options) {
13673
13675
  const timeoutMs = resolveGitTimeoutMs(options?.timeoutMs);
13674
13676
  const maxBuffer = options?.maxBuffer ?? 64 * 1024 * 1024;
13675
13677
  const executable = gitExecutableForTests ?? "git";
13678
+ const childArgs = gitExecutablePrefixArgsForTests.length ? [...gitExecutablePrefixArgsForTests, ...args] : args;
13676
13679
  const signal = options?.signal;
13677
13680
  if (signal?.aborted) {
13678
13681
  throw createGitError(projectRoot, args, new Error("aborted before start"));
13679
13682
  }
13680
13683
  return await new Promise((resolve, reject) => {
13681
- const child = spawn(executable, args, {
13684
+ const child = spawn(executable, childArgs, {
13682
13685
  cwd: projectRoot,
13683
13686
  env: process.env,
13684
13687
  stdio: [options?.input !== void 0 ? "pipe" : "ignore", "pipe", "pipe"],
@@ -14159,6 +14162,10 @@ async function getUnifiedDiff(projectRoot, opts) {
14159
14162
  throw createGitError(projectRoot, args, error51);
14160
14163
  }
14161
14164
  }
14165
+ function isGitTimeoutError(error51) {
14166
+ const detail = error51 instanceof Error ? error51.message : stringifyUnknown(error51);
14167
+ return /timed out after \d+ms/i.test(detail);
14168
+ }
14162
14169
  function createGitError(projectRoot, args, error51) {
14163
14170
  let detail = stringifyUnknown(error51);
14164
14171
  if (typeof error51 === "object" && error51 !== null && "stderr" in error51 && typeof error51.stderr === "string" && error51.stderr.trim()) {
@@ -14168,7 +14175,7 @@ function createGitError(projectRoot, args, error51) {
14168
14175
  }
14169
14176
  return new Error(`git ${args.join(" ")} failed in ${projectRoot}: ${detail}`);
14170
14177
  }
14171
- var DEFAULT_GIT_TIMEOUT_MS, gitRepositoryChecks, gitDiscoveryRootIgnoredChecks, gitExcludeFileChecks, MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES, gitExecutableForTests, GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES, UNSAFE_REVISION_CHARACTERS;
14178
+ var DEFAULT_GIT_TIMEOUT_MS, gitRepositoryChecks, gitDiscoveryRootIgnoredChecks, gitExcludeFileChecks, MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES, gitExecutableForTests, gitExecutablePrefixArgsForTests, GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES, UNSAFE_REVISION_CHARACTERS;
14172
14179
  var init_git = __esm({
14173
14180
  "dist/util/git.js"() {
14174
14181
  "use strict";
@@ -14181,6 +14188,7 @@ var init_git = __esm({
14181
14188
  gitExcludeFileChecks = /* @__PURE__ */ new Map();
14182
14189
  MAX_GIT_HASH_OBJECT_ARGUMENT_BYTES = 24 * 1024;
14183
14190
  gitExecutableForTests = null;
14191
+ gitExecutablePrefixArgsForTests = [];
14184
14192
  GIT_QUOTED_PATH_SINGLE_BYTE_ESCAPES = {
14185
14193
  a: 7,
14186
14194
  b: 8,
@@ -14213,6 +14221,13 @@ __export(projectFiles_exports, {
14213
14221
  import fs6 from "node:fs";
14214
14222
  import fsp5 from "node:fs/promises";
14215
14223
  import path9 from "node:path";
14224
+ import { performance as performance2 } from "node:perf_hooks";
14225
+ function emitDiscoveryActivity(report, activity, current = 0, total = 0) {
14226
+ report?.({ activity, current, total });
14227
+ }
14228
+ function emitDiscoveryTiming(record2, name, startedAt) {
14229
+ record2?.({ name, ms: Math.round(performance2.now() - startedAt) });
14230
+ }
14216
14231
  function createPathCheckProgressReporter(total, onProgress) {
14217
14232
  onProgress?.(0, total);
14218
14233
  let completed = 0;
@@ -14228,10 +14243,6 @@ function isPresent(value) {
14228
14243
  function normalizeGlobPattern(globPattern) {
14229
14244
  return globPattern.trim().replace(/\\/g, "/");
14230
14245
  }
14231
- function defaultIgnoreGitExcludePathspecs(includeGlobs = []) {
14232
- const activeIgnores = includeGlobs.length ? DEFAULT_PROJECT_FILE_IGNORES.filter((ignoreGlob) => !isIgnoreGlobReopenedByIncludes(ignoreGlob, includeGlobs)) : DEFAULT_PROJECT_FILE_IGNORES;
14233
- return activeIgnores.map((globPattern) => `:(exclude,glob)${normalizeGlobPattern(globPattern)}`);
14234
- }
14235
14246
  function couldBeDirectorySymlink(filePath) {
14236
14247
  const basename2 = path9.posix.basename(normalizePath(filePath));
14237
14248
  if (DIRECTORY_METADATA_BASENAMES.has(basename2.toLowerCase()))
@@ -14483,31 +14494,85 @@ function collectCandidateAncestorDirectories(root, files) {
14483
14494
  }
14484
14495
  return directories;
14485
14496
  }
14486
- async function findGitIgnoreSources(sourceRoots, excludePathspecs = defaultIgnoreGitExcludePathspecs()) {
14497
+ function owningGitIgnoreSourceRoot(file2, roots) {
14498
+ const normalized = normalizePath(file2);
14499
+ let owning;
14500
+ for (const root of roots) {
14501
+ if (!isFilePathWithinRoot(root.path, normalized) && !isFilePathWithinRoot(root.repositoryRoot, normalized)) {
14502
+ continue;
14503
+ }
14504
+ if (!owning || root.repositoryRoot.length > owning.repositoryRoot.length) {
14505
+ owning = root;
14506
+ }
14507
+ }
14508
+ return owning;
14509
+ }
14510
+ async function gitignoreFileIfPresent(directory) {
14511
+ const file2 = normalizePath(path9.join(directory, ".gitignore"));
14512
+ try {
14513
+ const stat = await fsp5.stat(file2);
14514
+ return stat.isFile() ? file2 : void 0;
14515
+ } catch {
14516
+ return void 0;
14517
+ }
14518
+ }
14519
+ async function findGitIgnoreSources(sourceRoots, candidateFiles) {
14487
14520
  const roots = Array.from(new Map(sourceRoots.map(({ path: sourceRoot, repositoryRoot }) => [
14488
14521
  fileIdentityKey(sourceRoot),
14489
14522
  { path: normalizePath(sourceRoot), repositoryRoot: normalizePath(repositoryRoot) }
14490
14523
  ])).values());
14491
- const sources = [];
14524
+ const directories = /* @__PURE__ */ new Map();
14525
+ const walkedDirectoryKeys = /* @__PURE__ */ new Set();
14526
+ const addDirectory = (directory, repositoryRoot) => {
14527
+ const dir = normalizePath(directory);
14528
+ const key = fileIdentityKey(dir);
14529
+ if (!directories.has(key))
14530
+ directories.set(key, { dir, repositoryRoot: normalizePath(repositoryRoot) });
14531
+ };
14492
14532
  for (const { path: sourceRoot, repositoryRoot } of roots) {
14493
- const [gitignoreFiles, excludeFiles] = await Promise.all([
14494
- listGitIgnoreFiles(sourceRoot, { excludePathspecs }),
14495
- listGitExcludeFiles(sourceRoot)
14496
- ]);
14497
- sources.push(...gitignoreFiles.map((file2) => ({
14498
- file: file2,
14499
- baseDir: path9.dirname(file2),
14500
- repositoryRoot,
14501
- priority: 2
14502
- })));
14503
- sources.push(...excludeFiles.map((source) => ({
14533
+ addDirectory(sourceRoot, repositoryRoot);
14534
+ }
14535
+ for (const file2 of candidateFiles) {
14536
+ const owning = owningGitIgnoreSourceRoot(file2, roots);
14537
+ if (!owning)
14538
+ continue;
14539
+ let dir = normalizePath(path9.dirname(file2));
14540
+ const stopKey = fileIdentityKey(owning.repositoryRoot);
14541
+ for (; ; ) {
14542
+ const dirKey = fileIdentityKey(dir);
14543
+ const walkKey = `${dirKey}\0${stopKey}`;
14544
+ if (walkedDirectoryKeys.has(walkKey))
14545
+ break;
14546
+ addDirectory(dir, owning.repositoryRoot);
14547
+ walkedDirectoryKeys.add(walkKey);
14548
+ if (dirKey === stopKey)
14549
+ break;
14550
+ const parent = normalizePath(path9.dirname(dir));
14551
+ if (parent === dir || !isFilePathWithinRoot(owning.path, parent) && !isFilePathWithinRoot(owning.repositoryRoot, parent)) {
14552
+ break;
14553
+ }
14554
+ dir = parent;
14555
+ }
14556
+ }
14557
+ const uniqueRepositoryRoots = Array.from(new Set(roots.map((root) => root.repositoryRoot)));
14558
+ const [gitignoreHits, excludeLists] = await Promise.all([
14559
+ mapLimitSemaphore(Array.from(directories.values()), REALPATH_FILTER_CONCURRENCY, async ({ dir, repositoryRoot }) => {
14560
+ const file2 = await gitignoreFileIfPresent(dir);
14561
+ return file2 ? [{ file: file2, baseDir: dir, repositoryRoot, priority: 2 }] : [];
14562
+ }),
14563
+ Promise.all(uniqueRepositoryRoots.map((repositoryRoot) => listGitExcludeFiles(repositoryRoot)))
14564
+ ]);
14565
+ return [
14566
+ ...gitignoreHits.flat(),
14567
+ ...excludeLists.flat().map((source) => ({
14504
14568
  ...source,
14505
14569
  repositoryRoot: source.baseDir
14506
- })));
14507
- }
14508
- return sources;
14570
+ }))
14571
+ ];
14509
14572
  }
14510
14573
  async function listGitCandidateFiles(root, logLevel, opts) {
14574
+ let gitListStart;
14575
+ let gitIgnoreStart;
14511
14576
  try {
14512
14577
  const realRoot = normalizePath(await fsp5.realpath(root));
14513
14578
  const gitRoot = await isGitRepo(root) ? root : realRoot;
@@ -14518,6 +14583,9 @@ async function listGitCandidateFiles(root, logLevel, opts) {
14518
14583
  const gitRepositoryRoot = await getGitRepositoryRoot(gitRoot);
14519
14584
  if (!gitRepositoryRoot)
14520
14585
  return null;
14586
+ emitDiscoveryActivity(opts?.onDiscoveryProgress, "Listing Git files");
14587
+ const listStartedAt = performance2.now();
14588
+ gitListStart = listStartedAt;
14521
14589
  const [tracked, untracked, stageSpecial] = await Promise.all([
14522
14590
  listTrackedFiles(gitRoot, { recurseSubmodules: true, ...logLevel === void 0 ? {} : { logLevel } }),
14523
14591
  listUntrackedFiles(gitRoot, { respectGitignore: true }),
@@ -14544,15 +14612,35 @@ async function listGitCandidateFiles(root, logLevel, opts) {
14544
14612
  { path: physicalPath, repositoryRoot: physicalPath }
14545
14613
  ])
14546
14614
  ];
14615
+ emitDiscoveryTiming(opts?.onDiscoveryTiming, "git-list", listStartedAt);
14616
+ gitListStart = void 0;
14617
+ emitDiscoveryActivity(opts?.onDiscoveryProgress, "Listing Git files", files.length, files.length);
14618
+ emitDiscoveryActivity(opts?.onDiscoveryProgress, "Listing Git ignore files");
14619
+ const ignoreStartedAt = performance2.now();
14620
+ gitIgnoreStart = ignoreStartedAt;
14621
+ const gitignoreFiles = await findGitIgnoreSources(sourceRoots, files);
14622
+ emitDiscoveryTiming(opts?.onDiscoveryTiming, "git-ignore", ignoreStartedAt);
14623
+ gitIgnoreStart = void 0;
14624
+ emitDiscoveryActivity(opts?.onDiscoveryProgress, "Listing Git ignore files", gitignoreFiles.length, gitignoreFiles.length);
14547
14625
  return {
14548
14626
  files,
14549
14627
  symlinkCandidatePaths,
14550
- gitignoreFiles: await findGitIgnoreSources(sourceRoots, defaultIgnoreGitExcludePathspecs(opts?.includeGlobs ?? [])),
14628
+ gitignoreFiles,
14551
14629
  gitignoreRoots,
14552
14630
  gitignoreAliases: sourceRoots
14553
14631
  };
14554
14632
  } catch (error51) {
14555
- logWithLevel(logLevel, "debug", `Git discovery unavailable for ${root}: ${stringifyUnknown(error51)}`);
14633
+ const listingTimedOut = isGitTimeoutError(error51) && (gitIgnoreStart !== void 0 || gitListStart !== void 0);
14634
+ if (gitIgnoreStart !== void 0) {
14635
+ emitDiscoveryTiming(opts?.onDiscoveryTiming, "git-ignore", gitIgnoreStart);
14636
+ } else if (gitListStart !== void 0) {
14637
+ emitDiscoveryTiming(opts?.onDiscoveryTiming, "git-list", gitListStart);
14638
+ }
14639
+ if (listingTimedOut) {
14640
+ logWithLevel(logLevel, "warn", "Warning: Git listing timed out; scanning the filesystem instead. Ignore large untracked trees in .gitignore to keep discovery fast.");
14641
+ } else {
14642
+ logWithLevel(logLevel, "debug", `Git discovery unavailable for ${root}: ${stringifyUnknown(error51)}`);
14643
+ }
14556
14644
  return null;
14557
14645
  }
14558
14646
  }
@@ -14581,7 +14669,11 @@ async function listProjectFilesInternal(projectRoot, patterns = DEFAULT_PROJECT_
14581
14669
  const useGitignore = options?.useGitignore ?? true;
14582
14670
  const realRoot = await fsp5.realpath(root);
14583
14671
  const attemptedGitCandidates = useGitignore && options?.gitignoreRoot === void 0 && normalizePath(realRoot) === normalizePath(root);
14584
- const gitCandidates = attemptedGitCandidates ? await listGitCandidateFiles(root, options?.logLevel, { includeGlobs }) : null;
14672
+ const gitDiscoveryCallbacks = {
14673
+ ...options?.onDiscoveryProgress ? { onDiscoveryProgress: options.onDiscoveryProgress } : {},
14674
+ ...options?.onDiscoveryTiming ? { onDiscoveryTiming: options.onDiscoveryTiming } : {}
14675
+ };
14676
+ const gitCandidates = attemptedGitCandidates ? await listGitCandidateFiles(root, options?.logLevel, gitDiscoveryCallbacks) : null;
14585
14677
  if (attemptedGitCandidates) {
14586
14678
  options?.onGitCandidatesDiscovered?.(gitCandidates);
14587
14679
  }
@@ -14592,13 +14684,22 @@ async function listProjectFilesInternal(projectRoot, patterns = DEFAULT_PROJECT_
14592
14684
  const gitignoreRoot = options?.gitignoreRoot ? await ensureDirectoryReadable(options.gitignoreRoot, "Gitignore root") : root;
14593
14685
  gitignoreIndex = await loadGitignoreIndexForRootAliases(gitignoreRoot);
14594
14686
  }
14595
- const files = gitCandidates ? gitCandidates.files : await (0, import_fast_glob2.default)(patterns, {
14596
- cwd: root,
14597
- absolute: true,
14598
- dot: true,
14599
- followSymbolicLinks: false,
14600
- ignore: fastGlobIgnoreGlobs
14601
- });
14687
+ let files;
14688
+ if (gitCandidates) {
14689
+ files = gitCandidates.files;
14690
+ } else {
14691
+ emitDiscoveryActivity(options?.onDiscoveryProgress, "Scanning project files");
14692
+ const scanStart = performance2.now();
14693
+ files = await (0, import_fast_glob2.default)(patterns, {
14694
+ cwd: root,
14695
+ absolute: true,
14696
+ dot: true,
14697
+ followSymbolicLinks: false,
14698
+ ignore: fastGlobIgnoreGlobs
14699
+ });
14700
+ emitDiscoveryTiming(options?.onDiscoveryTiming, "filesystem-scan", scanStart);
14701
+ emitDiscoveryActivity(options?.onDiscoveryProgress, "Scanning project files", files.length, files.length);
14702
+ }
14602
14703
  const includedOverrideFiles = includeGlobs.length && !gitCandidates ? await (0, import_fast_glob2.default)(translateGlobRootIgnoreGlobsForScanRoot(root, globRoot, includeGlobs), {
14603
14704
  cwd: root,
14604
14705
  absolute: true,
@@ -14842,7 +14943,10 @@ async function discoverProjectFilesInternal(projectRoot, options) {
14842
14943
  if (options?.knownGitCandidates !== void 0) {
14843
14944
  gitCandidates = options.knownGitCandidates;
14844
14945
  } else {
14845
- gitCandidates = await listGitCandidateFiles(root, options?.logLevel);
14946
+ gitCandidates = await listGitCandidateFiles(root, options?.logLevel, {
14947
+ ...options?.onDiscoveryProgress ? { onDiscoveryProgress: options.onDiscoveryProgress } : {},
14948
+ ...options?.onDiscoveryTiming ? { onDiscoveryTiming: options.onDiscoveryTiming } : {}
14949
+ });
14846
14950
  }
14847
14951
  options?.onGitCandidatesDiscovered?.(gitCandidates);
14848
14952
  const reportMetadataSymlinkChecks = options?.onDiscoveryProgress ? (current, total) => options.onDiscoveryProgress?.({ activity: "Checking project metadata symlinks", current, total }) : void 0;
@@ -53010,6 +53114,23 @@ function isSymbolEdge(value) {
53010
53114
  const edge = value;
53011
53115
  return typeof edge.from === "string" && typeof edge.to === "string" && (edge.label === void 0 || typeof edge.label === "string") && (edge.site === void 0 || !!edge.site && typeof edge.site === "object" && typeof edge.site.file === "string" && !!edge.site.file && isDetailedRange(edge.site.range));
53012
53116
  }
53117
+ function sameSymbolNodeSemantics(actual, expected) {
53118
+ return actual.file === expected.file && actual.name === expected.name && actual.kind === expected.kind && actual.docstring === expected.docstring && actual.lineSpan === expected.lineSpan && actual.complexity === expected.complexity;
53119
+ }
53120
+ function indexDefinesSymbolNode(index, node) {
53121
+ const moduleEntry = index.byFile.get(fileIdentityKey(node.file));
53122
+ if (!moduleEntry)
53123
+ return false;
53124
+ if (moduleEntry.locals.some((local) => defNodeId(local) === node.id))
53125
+ return true;
53126
+ const filePrefix = `${normalizePath(moduleEntry.file)}::`;
53127
+ return moduleEntry.imports.some((imp) => {
53128
+ if (imp.kind === "star")
53129
+ return false;
53130
+ const local = imp.kind === "namespace" ? imp.localNS : imp.local;
53131
+ return node.id === `${filePrefix}${local}::import`;
53132
+ });
53133
+ }
53013
53134
  async function isDetailedSymbolGraphCompatibleWithProject(projectRoot, index, graph) {
53014
53135
  const normalizedRoot = path43.resolve(projectRoot);
53015
53136
  const indexedFiles = /* @__PURE__ */ new Set([...index.byFile.keys()]);
@@ -53030,25 +53151,26 @@ async function isDetailedSymbolGraphCompatibleWithProject(projectRoot, index, gr
53030
53151
  }
53031
53152
  }
53032
53153
  const base = await buildSymbolGraph(index);
53033
- if (graph.nodes.size !== base.nodes.size)
53034
- return false;
53035
53154
  for (const [id, expected] of base.nodes) {
53036
53155
  const actual = graph.nodes.get(id);
53037
- if (!actual || actual.file !== expected.file || actual.name !== expected.name || actual.kind !== expected.kind || actual.docstring !== expected.docstring || actual.lineSpan !== expected.lineSpan || actual.complexity !== expected.complexity) {
53156
+ if (!actual || !sameSymbolNodeSemantics(actual, expected))
53157
+ return false;
53158
+ }
53159
+ for (const node of graph.nodes.values()) {
53160
+ if (base.nodes.has(node.id))
53161
+ continue;
53162
+ if (!indexDefinesSymbolNode(index, node))
53038
53163
  return false;
53039
- }
53040
53164
  }
53041
- return sameSymbolEdgeMultiset(base.edges, graph.edges);
53165
+ return symbolEdgeMultisetContains(graph.edges, base.edges);
53042
53166
  }
53043
- function sameSymbolEdgeMultiset(expected, actual) {
53044
- if (expected.length !== actual.length)
53045
- return false;
53167
+ function symbolEdgeMultisetContains(haystack, needles) {
53046
53168
  const counts = /* @__PURE__ */ new Map();
53047
- for (const edge of expected) {
53169
+ for (const edge of haystack) {
53048
53170
  const key = symbolEdgeKey(edge);
53049
53171
  counts.set(key, (counts.get(key) ?? 0) + 1);
53050
53172
  }
53051
- for (const edge of actual) {
53173
+ for (const edge of needles) {
53052
53174
  const key = symbolEdgeKey(edge);
53053
53175
  const remaining = counts.get(key);
53054
53176
  if (remaining === void 0)
@@ -53058,7 +53180,7 @@ function sameSymbolEdgeMultiset(expected, actual) {
53058
53180
  else
53059
53181
  counts.set(key, remaining - 1);
53060
53182
  }
53061
- return !counts.size;
53183
+ return true;
53062
53184
  }
53063
53185
  function symbolEdgeKey(edge) {
53064
53186
  const from = lengthPrefixedSymbolEdgeField(edge.from);
@@ -53582,19 +53704,19 @@ var init_parsed_cache = __esm({
53582
53704
  });
53583
53705
 
53584
53706
  // dist/indexer/finalize.js
53585
- import { performance as performance2 } from "node:perf_hooks";
53707
+ import { performance as performance3 } from "node:perf_hooks";
53586
53708
  async function finalizeProjectIndex(args) {
53587
53709
  const discoveryTimings = args.opts?.report ? args.opts.report.timings ??= {} : void 0;
53588
- const metadataDiscoveryStart = args.projectFiles === void 0 ? performance2.now() : void 0;
53710
+ const metadataDiscoveryStart = args.projectFiles === void 0 ? performance3.now() : void 0;
53589
53711
  const projectFiles = await (args.projectFiles ?? discoverProjectFilesWithGitCandidates(args.projectRoot, {
53590
53712
  ...args.opts?.logLevel ? { logLevel: args.opts.logLevel } : {},
53591
53713
  ...args.knownGitCandidates !== void 0 ? { knownGitCandidates: args.knownGitCandidates } : {}
53592
53714
  }));
53593
53715
  if (metadataDiscoveryStart !== void 0 && discoveryTimings) {
53594
- discoveryTimings.metadataDiscoveryMs = Math.round(performance2.now() - metadataDiscoveryStart);
53716
+ discoveryTimings.metadataDiscoveryMs = Math.round(performance3.now() - metadataDiscoveryStart);
53595
53717
  }
53596
53718
  if (args.timings)
53597
- args.timings.totalMs = Math.round(performance2.now() - args.totalStart);
53719
+ args.timings.totalMs = Math.round(performance3.now() - args.totalStart);
53598
53720
  const languageExtensions = normalizeLanguageExtensions(args.opts?.languageExtensions);
53599
53721
  const parsed = retainedParsedCache(args.parsedMap, args.opts);
53600
53722
  return {
@@ -53632,7 +53754,7 @@ var init_finalize = __esm({
53632
53754
  // dist/indexer/build-manifest.js
53633
53755
  import fsp24 from "node:fs/promises";
53634
53756
  import path45 from "node:path";
53635
- import { performance as performance3 } from "node:perf_hooks";
53757
+ import { performance as performance4 } from "node:perf_hooks";
53636
53758
  function toManifestFileEntry(entry) {
53637
53759
  if (!entry.sig)
53638
53760
  return void 0;
@@ -53647,7 +53769,7 @@ async function writeIndexManifestSnapshot(args) {
53647
53769
  const files = args.files instanceof Map ? Object.fromEntries(args.files) : args.files;
53648
53770
  if (!Object.keys(files).length && !args.allowEmpty)
53649
53771
  return;
53650
- const writeManifestStart = performance3.now();
53772
+ const writeManifestStart = performance4.now();
53651
53773
  const lastCommit = await getGitHead(args.projectRoot);
53652
53774
  const configHashResult = await computeConfigHash(args.projectRoot, args.opts?.logLevel);
53653
53775
  const projectRootMtimeMs = args.symlinkDirectories !== void 0 ? (await fsp24.stat(args.projectRoot)).mtimeMs : void 0;
@@ -53672,7 +53794,7 @@ async function writeIndexManifestSnapshot(args) {
53672
53794
  if (manifestWritten)
53673
53795
  pruneDiskModuleCache(args.projectRoot, Object.keys(files), args.opts);
53674
53796
  if (args.timings) {
53675
- args.timings.writeManifestMs = Math.round(performance3.now() - writeManifestStart);
53797
+ args.timings.writeManifestMs = Math.round(performance4.now() - writeManifestStart);
53676
53798
  }
53677
53799
  }
53678
53800
  var init_build_manifest = __esm({
@@ -55107,7 +55229,7 @@ var init_nativeWorkerPool = __esm({
55107
55229
  });
55108
55230
 
55109
55231
  // dist/indexer/build-workers.js
55110
- import { performance as performance4 } from "node:perf_hooks";
55232
+ import { performance as performance5 } from "node:perf_hooks";
55111
55233
  function shouldEnableNativeWorkers(opts, fileCount) {
55112
55234
  if (opts?.native === "off")
55113
55235
  return false;
@@ -55201,14 +55323,14 @@ async function setupWorkerPool(opts, fileCount) {
55201
55323
  return {
55202
55324
  pool,
55203
55325
  report,
55204
- startTime: pool ? performance4.now() : 0,
55326
+ startTime: pool ? performance5.now() : 0,
55205
55327
  batchSize: NATIVE_WORKER_BATCH_SIZE
55206
55328
  };
55207
55329
  }
55208
55330
  async function teardownWorkerPool(setup, buildReport2) {
55209
55331
  if (setup.pool) {
55210
55332
  if (setup.report) {
55211
- setup.report.wallClockMs = Math.round(performance4.now() - setup.startTime);
55333
+ setup.report.wallClockMs = Math.round(performance5.now() - setup.startTime);
55212
55334
  }
55213
55335
  try {
55214
55336
  await setup.pool.destroy();
@@ -55476,7 +55598,7 @@ import crypto10 from "node:crypto";
55476
55598
  import fs23 from "node:fs";
55477
55599
  import fsp26 from "node:fs/promises";
55478
55600
  import path47 from "node:path";
55479
- import { performance as performance5 } from "node:perf_hooks";
55601
+ import { performance as performance6 } from "node:perf_hooks";
55480
55602
  function initParserBackendDegradationReport(report) {
55481
55603
  if (!report)
55482
55604
  return void 0;
@@ -55775,7 +55897,7 @@ function createIndexBuildRunState(projectRoot, opts, graphOptions = normalizeGra
55775
55897
  normalizedProjectRoot: normalizePath(path47.resolve(projectRoot)),
55776
55898
  report,
55777
55899
  timings: report?.timings,
55778
- totalStart: performance5.now(),
55900
+ totalStart: performance6.now(),
55779
55901
  cacheMode,
55780
55902
  cacheEnabled: cacheMode !== "off",
55781
55903
  graphOptions,
@@ -55820,6 +55942,31 @@ function emitIndexCheckActivity(opts, activity, current = 0, total = 0) {
55820
55942
  total
55821
55943
  });
55822
55944
  }
55945
+ function resetDiscoveryTimingSteps(timings) {
55946
+ if (!timings)
55947
+ return;
55948
+ delete timings.gitListMs;
55949
+ delete timings.filesystemScanMs;
55950
+ delete timings.sourceDiscoveryMs;
55951
+ delete timings.metadataDiscoveryMs;
55952
+ const remaining = (timings.steps ?? []).filter((step) => !DISCOVERY_TIMING_STEP_NAMES.has(step.name));
55953
+ if (remaining.length) {
55954
+ timings.steps = remaining;
55955
+ return;
55956
+ }
55957
+ delete timings.steps;
55958
+ }
55959
+ function recordBuildTimingStep(timings, step) {
55960
+ if (!timings)
55961
+ return;
55962
+ (timings.steps ??= []).push(step);
55963
+ if (step.name === "git-list") {
55964
+ timings.gitListMs = step.ms;
55965
+ return;
55966
+ }
55967
+ if (step.name === "filesystem-scan")
55968
+ timings.filesystemScanMs = step.ms;
55969
+ }
55823
55970
  function buildConcurrency(opts) {
55824
55971
  return resolveWorkerThreadCount({ requested: Number(opts?.threads || 0), defaultCount: 8, max: 64 });
55825
55972
  }
@@ -55861,7 +56008,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
55861
56008
  const fileReport = initFileReport(report);
55862
56009
  if (fileReport)
55863
56010
  fileReport.total = normalizedFiles.length;
55864
- const manifestStart = performance5.now();
56011
+ const manifestStart = performance6.now();
55865
56012
  const manifest = useManifest && !helperOpts?.ignoreExistingManifest ? await loadManifest(projectRoot, opts, report) : null;
55866
56013
  const manifestFiles = sanitizeManifestEntriesForRoot(projectRoot, manifest?.files);
55867
56014
  const manifestOptionDiffs = manifest ? diffBuildOptions(manifest.buildOptions, opts) : [];
@@ -55876,7 +56023,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
55876
56023
  }
55877
56024
  }
55878
56025
  if (timings && useManifest) {
55879
- timings.manifestMs = Math.round(performance5.now() - manifestStart);
56026
+ timings.manifestMs = Math.round(performance6.now() - manifestStart);
55880
56027
  }
55881
56028
  const edgeProbeConcurrency = buildConcurrency(opts);
55882
56029
  const staleCachedEdgeFiles = /* @__PURE__ */ new Set();
@@ -55990,7 +56137,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
55990
56137
  }
55991
56138
  return matchPath;
55992
56139
  };
55993
- const parseStart = performance5.now();
56140
+ const parseStart = performance6.now();
55994
56141
  const graph = { nodes: new Set(normalizedFiles), edges: [] };
55995
56142
  const onFileEdges = manifestEntries ? (file2, entry) => {
55996
56143
  const manifestEntry = toManifestFileEntry(entry);
@@ -56004,7 +56151,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
56004
56151
  const ensureBuildProgressStarted = () => {
56005
56152
  if (buildStartedAt !== void 0)
56006
56153
  return;
56007
- buildStartedAt = performance5.now();
56154
+ buildStartedAt = performance6.now();
56008
56155
  emitIndexLifecycleProgress(opts, "start", "build", totalFiles);
56009
56156
  };
56010
56157
  const fileResults = await mapLimit(normalizedFiles, conc, async (file2) => {
@@ -56136,8 +56283,8 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
56136
56283
  }
56137
56284
  });
56138
56285
  if (timings)
56139
- timings.parseMs = Math.round(performance5.now() - parseStart);
56140
- const graphStart = performance5.now();
56286
+ timings.parseMs = Math.round(performance6.now() - parseStart);
56287
+ const graphStart = performance6.now();
56141
56288
  const seenGraphEdges = /* @__PURE__ */ new Set();
56142
56289
  const appendUniqueGraphEdges = (edges) => {
56143
56290
  if (!edges.length)
@@ -56165,7 +56312,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
56165
56312
  const workspaceManifestEdges = await collectWorkspaceManifestDependencyEdges(projectRoot, normalizedFiles.filter((file2) => path47.basename(file2) === "package.json"), opts?.discovery, opts?.logLevel);
56166
56313
  appendUniqueGraphEdges(workspaceManifestEdges);
56167
56314
  if (timings)
56168
- timings.graphMs = Math.round(performance5.now() - graphStart);
56315
+ timings.graphMs = Math.round(performance6.now() - graphStart);
56169
56316
  for (const jsonPath of jsonDependencies.values()) {
56170
56317
  ensureJsonModule(modules, jsonPath);
56171
56318
  }
@@ -56217,7 +56364,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
56217
56364
  }
56218
56365
  if (buildStartedAt !== void 0) {
56219
56366
  const progressStartedAt = opts?.progressStartedAt ?? buildStartedAt;
56220
- emitIndexLifecycleProgress(opts, "complete", "build", index.byFile.size, performance5.now() - progressStartedAt);
56367
+ emitIndexLifecycleProgress(opts, "complete", "build", index.byFile.size, performance6.now() - progressStartedAt);
56221
56368
  }
56222
56369
  return index;
56223
56370
  } finally {
@@ -56226,6 +56373,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
56226
56373
  }
56227
56374
  async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpts) {
56228
56375
  const timings = opts?.report ? opts.report.timings ??= {} : void 0;
56376
+ resetDiscoveryTimingSteps(timings);
56229
56377
  await initializeFileIdentityCaseSensitivity(projectRoot);
56230
56378
  try {
56231
56379
  const useDiskCache = (opts?.cache ?? "off") === "disk";
@@ -56246,19 +56394,21 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
56246
56394
  discoveredGitCandidates = candidates;
56247
56395
  };
56248
56396
  const onDiscoveryProgress = helperOpts?.reportDiscoveryProgress ? (progress) => emitIndexCheckActivity(opts, progress.activity, progress.current, progress.total) : void 0;
56397
+ const onDiscoveryTiming = timings ? (step) => recordBuildTimingStep(timings, step) : void 0;
56249
56398
  if (helperOpts?.reportDiscoveryProgress)
56250
56399
  emitIndexCheckActivity(opts, "Discovering source files");
56251
- const sourceDiscoveryStart = performance5.now();
56400
+ const sourceDiscoveryStart = performance6.now();
56252
56401
  const discoveredFiles = await listProjectFilesWithGitCandidates(projectRoot, projectPatternsForLanguageExtensions(opts), {
56253
56402
  ...opts?.discovery,
56254
56403
  ...opts?.logLevel ? { logLevel: opts.logLevel } : {},
56255
56404
  ...knownSymlinkDirectories !== void 0 ? { knownSymlinkDirectories } : {},
56256
56405
  onSymlinkDirectoriesDiscovered,
56257
56406
  onGitCandidatesDiscovered,
56258
- ...onDiscoveryProgress ? { onDiscoveryProgress } : {}
56407
+ ...onDiscoveryProgress ? { onDiscoveryProgress } : {},
56408
+ ...onDiscoveryTiming ? { onDiscoveryTiming } : {}
56259
56409
  });
56260
56410
  if (timings)
56261
- timings.sourceDiscoveryMs = Math.round(performance5.now() - sourceDiscoveryStart);
56411
+ timings.sourceDiscoveryMs = Math.round(performance6.now() - sourceDiscoveryStart);
56262
56412
  const additionalFileCandidates = await normalizeIndexedFileInputsWithinRoot(projectRoot, opts?.additionalFiles ?? [], "Additional index file");
56263
56413
  const additionalFileExistence = await probePathExistence(additionalFileCandidates, buildConcurrency(opts));
56264
56414
  const additionalFiles = additionalFileCandidates.filter((file2) => additionalFileExistence.get(file2));
@@ -56267,15 +56417,16 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
56267
56417
  const transientFiles = additionalFiles.filter((file2) => !discoveredFileSet.has(file2));
56268
56418
  if (helperOpts?.reportDiscoveryProgress)
56269
56419
  emitIndexCheckActivity(opts, "Discovering project metadata");
56270
- const metadataDiscoveryStart = performance5.now();
56420
+ const metadataDiscoveryStart = performance6.now();
56271
56421
  const projectFiles = await discoverProjectFilesWithGitCandidates(projectRoot, {
56272
56422
  ...opts?.logLevel ? { logLevel: opts.logLevel } : {},
56273
56423
  ...discoveredSymlinkDirectories !== void 0 ? { knownSymlinkDirectories: discoveredSymlinkDirectories } : {},
56274
56424
  ...discoveredGitCandidates !== void 0 ? { knownGitCandidates: discoveredGitCandidates } : {},
56275
- ...onDiscoveryProgress ? { onDiscoveryProgress } : {}
56425
+ ...onDiscoveryProgress ? { onDiscoveryProgress } : {},
56426
+ ...onDiscoveryTiming ? { onDiscoveryTiming } : {}
56276
56427
  });
56277
56428
  if (timings)
56278
- timings.metadataDiscoveryMs = Math.round(performance5.now() - metadataDiscoveryStart);
56429
+ timings.metadataDiscoveryMs = Math.round(performance6.now() - metadataDiscoveryStart);
56279
56430
  return await buildIndexFromFileListShared(projectRoot, files, opts, {
56280
56431
  manifestMode: useDiskCache ? "read-write" : "off",
56281
56432
  warnNoFilesMessage: `Warning: No files found in project root: ${projectRoot}. Check codegraph.config.json globs and CLI --include-glob/--ignore-glob filters. Diagnostic: codegraph doctor`,
@@ -56294,11 +56445,11 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
56294
56445
  async function buildProjectIndex(projectRoot, opts) {
56295
56446
  return buildProjectIndexWithManifestOptions(projectRoot, {
56296
56447
  ...opts,
56297
- progressStartedAt: opts?.progressStartedAt ?? performance5.now()
56448
+ progressStartedAt: opts?.progressStartedAt ?? performance6.now()
56298
56449
  });
56299
56450
  }
56300
56451
  async function buildProjectIndexFromFiles(projectRoot, inputFiles, rawOpts) {
56301
- const opts = { ...rawOpts, progressStartedAt: rawOpts?.progressStartedAt ?? performance5.now() };
56452
+ const opts = { ...rawOpts, progressStartedAt: rawOpts?.progressStartedAt ?? performance6.now() };
56302
56453
  try {
56303
56454
  const useDiskCache = (opts?.cache ?? "off") === "disk";
56304
56455
  const normalizedInputFiles = await normalizeIndexedFileInputsWithinRoot(projectRoot, inputFiles, "Index file");
@@ -56330,7 +56481,7 @@ async function buildDeclaredScopeIndex(projectRoot, scopeFiles, opts) {
56330
56481
  async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56331
56482
  const opts = {
56332
56483
  ...rawOpts,
56333
- progressStartedAt: rawOpts?.progressStartedAt ?? performance5.now()
56484
+ progressStartedAt: rawOpts?.progressStartedAt ?? performance6.now()
56334
56485
  };
56335
56486
  await initializeFileIdentityCaseSensitivity(projectRoot);
56336
56487
  clearResolutionCaches();
@@ -56340,6 +56491,7 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56340
56491
  graphOptions.fast = false;
56341
56492
  const { normalizedProjectRoot, report, timings, totalStart, cacheMode, cacheEnabled, onFallbackImportExtraction } = createIndexBuildRunState(projectRoot, opts, graphOptions);
56342
56493
  const discoveryTimings = opts?.report ? opts.report.timings ??= {} : void 0;
56494
+ resetDiscoveryTimingSteps(discoveryTimings);
56343
56495
  let checkProgressActive = false;
56344
56496
  const startCheckProgress = () => {
56345
56497
  if (checkProgressActive)
@@ -56365,7 +56517,7 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56365
56517
  message: "Checked project index",
56366
56518
  current: total,
56367
56519
  total,
56368
- elapsedMs: performance5.now() - totalStart
56520
+ elapsedMs: performance6.now() - totalStart
56369
56521
  });
56370
56522
  };
56371
56523
  try {
@@ -56380,10 +56532,10 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56380
56532
  return await buildProjectIndexFromExport(projectRoot, opts, { ignoreExistingManifest: true });
56381
56533
  }
56382
56534
  startCheckProgress();
56383
- const manifestStart = performance5.now();
56535
+ const manifestStart = performance6.now();
56384
56536
  const manifest = await loadManifest(projectRoot, opts, report);
56385
56537
  if (timings)
56386
- timings.manifestMs = Math.round(performance5.now() - manifestStart);
56538
+ timings.manifestMs = Math.round(performance6.now() - manifestStart);
56387
56539
  const manifestUsed = !!manifest;
56388
56540
  const manifestReport = initManifestReport(report, manifestUsed, false);
56389
56541
  if (manifestReport && !manifestUsed)
@@ -56438,11 +56590,11 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56438
56590
  if (!discoveryTimings || opts?.filesAreProjectScope)
56439
56591
  return await operation();
56440
56592
  sourceDiscoveryPerformed = true;
56441
- const start = performance5.now();
56593
+ const start = performance6.now();
56442
56594
  try {
56443
56595
  return await operation();
56444
56596
  } finally {
56445
- sourceDiscoveryMs += performance5.now() - start;
56597
+ sourceDiscoveryMs += performance6.now() - start;
56446
56598
  }
56447
56599
  };
56448
56600
  let manifestDiffFiles = [];
@@ -56534,7 +56686,10 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56534
56686
  ...!opts?.cacheStrict && manifest.symlinkDirectories !== void 0 ? { knownSymlinkDirectories: manifest.symlinkDirectories } : {},
56535
56687
  onGitCandidatesDiscovered: (candidates) => {
56536
56688
  discoveredGitCandidates = candidates;
56537
- }
56689
+ },
56690
+ ...discoveryTimings ? {
56691
+ onDiscoveryTiming: (step) => recordBuildTimingStep(discoveryTimings, step)
56692
+ } : {}
56538
56693
  }));
56539
56694
  }
56540
56695
  if (discoveryTimings && sourceDiscoveryPerformed) {
@@ -56639,13 +56794,13 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56639
56794
  const snapshot = snapshotLoad.index;
56640
56795
  snapshot.manifestSignaturesFresh = false;
56641
56796
  if (snapshot.projectFiles === void 0) {
56642
- const metadataDiscoveryStart = performance5.now();
56797
+ const metadataDiscoveryStart = performance6.now();
56643
56798
  snapshot.projectFiles = await discoverProjectFilesWithGitCandidates(projectRoot, {
56644
56799
  ...opts?.logLevel ? { logLevel: opts.logLevel } : {},
56645
56800
  ...discoveredGitCandidates !== void 0 ? { knownGitCandidates: discoveredGitCandidates } : {}
56646
56801
  });
56647
56802
  if (discoveryTimings)
56648
- discoveryTimings.metadataDiscoveryMs = Math.round(performance5.now() - metadataDiscoveryStart);
56803
+ discoveryTimings.metadataDiscoveryMs = Math.round(performance6.now() - metadataDiscoveryStart);
56649
56804
  }
56650
56805
  if (opts?.cache) {
56651
56806
  snapshot.cacheMode = opts.cache;
@@ -56683,7 +56838,7 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56683
56838
  const snapshot = await reuseUnchangedSnapshot();
56684
56839
  if (snapshot) {
56685
56840
  if (timings)
56686
- timings.totalMs = Math.round(performance5.now() - totalStart);
56841
+ timings.totalMs = Math.round(performance6.now() - totalStart);
56687
56842
  completeCheckProgress(allFiles.size);
56688
56843
  return snapshot;
56689
56844
  }
@@ -56753,7 +56908,7 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56753
56908
  unchangedSnapshot.manifestEntries = new Map(Array.from(fileSignatures, ([file2, signature]) => [file2, toProjectIndexManifestEntry(signature)]));
56754
56909
  unchangedSnapshot.manifestSignaturesFresh = true;
56755
56910
  if (timings)
56756
- timings.totalMs = Math.round(performance5.now() - totalStart);
56911
+ timings.totalMs = Math.round(performance6.now() - totalStart);
56757
56912
  completeCheckProgress(allFiles.size);
56758
56913
  return unchangedSnapshot;
56759
56914
  }
@@ -56795,13 +56950,13 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56795
56950
  workerSetup = await setupWorkerPool(opts, countNativeWorkerEligibleFiles(changedList, opts?.languageExtensions));
56796
56951
  let updateStartedAt;
56797
56952
  if (changedList.length || deletedTrackedFiles.size) {
56798
- updateStartedAt = performance5.now();
56953
+ updateStartedAt = performance6.now();
56799
56954
  emitIndexLifecycleProgress(opts, "start", "update", changedList.length);
56800
56955
  }
56801
56956
  if (fileReport)
56802
56957
  fileReport.changed = changedList.length;
56803
56958
  if (changedList.length) {
56804
- const parseStart = performance5.now();
56959
+ const parseStart = performance6.now();
56805
56960
  let processedFiles = 0;
56806
56961
  const totalFiles = changedList.length;
56807
56962
  const fileResults = await mapLimit(changedList, conc, async (file2) => {
@@ -56864,7 +57019,7 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56864
57019
  writeModulesToCache(projectRoot, pendingIncrementalCacheWrites, opts);
56865
57020
  }
56866
57021
  if (timings)
56867
- timings.parseMs = Math.round(performance5.now() - parseStart);
57022
+ timings.parseMs = Math.round(performance6.now() - parseStart);
56868
57023
  }
56869
57024
  for (const jsonPath of jsonDependencies.values()) {
56870
57025
  ensureJsonModule(modules, jsonPath);
@@ -56894,7 +57049,7 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56894
57049
  }
56895
57050
  }
56896
57051
  const filesList = Array.from(changedFiles);
56897
- const graphStart = performance5.now();
57052
+ const graphStart = performance6.now();
56898
57053
  const graph = !filesList.length && baseGraph ? { nodes: new Set(baseGraph.nodes), edges: [...baseGraph.edges] } : await collectGraph(projectRoot, filesList, {
56899
57054
  parsed: parsedMap,
56900
57055
  fast: !!graphOptions.fast,
@@ -56920,7 +57075,7 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56920
57075
  }
56921
57076
  });
56922
57077
  if (timings)
56923
- timings.graphMs = Math.round(performance5.now() - graphStart);
57078
+ timings.graphMs = Math.round(performance6.now() - graphStart);
56924
57079
  for (const file2 of /* @__PURE__ */ new Set([...changedFiles, ...transientFiles])) {
56925
57080
  const signature = fileSignatures.get(file2);
56926
57081
  if (!signature || manifestEntries.has(file2))
@@ -56980,7 +57135,7 @@ async function buildProjectIndexIncremental(projectRoot, rawOpts) {
56980
57135
  });
56981
57136
  await writeProjectIndexSnapshot(projectRoot, opts, index, projectSnapshotFilesSignature(manifestEntries, projectRoot));
56982
57137
  if (updateStartedAt !== void 0) {
56983
- emitIndexLifecycleProgress(opts, "complete", "update", index.byFile.size, performance5.now() - updateStartedAt);
57138
+ emitIndexLifecycleProgress(opts, "complete", "update", index.byFile.size, performance6.now() - updateStartedAt);
56984
57139
  } else {
56985
57140
  completeCheckProgress(allFiles.size);
56986
57141
  }
@@ -57126,6 +57281,7 @@ async function buildGraphDelta(projectRoot, opts) {
57126
57281
  removed: removedRelative.sort(compareEdges)
57127
57282
  };
57128
57283
  }
57284
+ var DISCOVERY_TIMING_STEP_NAMES;
57129
57285
  var init_build_index = __esm({
57130
57286
  "dist/indexer/build-index.js"() {
57131
57287
  "use strict";
@@ -57165,6 +57321,7 @@ var init_build_index = __esm({
57165
57321
  init_build_workers();
57166
57322
  init_incremental_plan();
57167
57323
  init_parsed_cache();
57324
+ DISCOVERY_TIMING_STEP_NAMES = /* @__PURE__ */ new Set(["git-list", "git-ignore", "filesystem-scan"]);
57168
57325
  }
57169
57326
  });
57170
57327
 
@@ -67798,7 +67955,7 @@ var init_deleted = __esm({
67798
67955
  });
67799
67956
 
67800
67957
  // dist/review/candidates.js
67801
- import { performance as performance6 } from "node:perf_hooks";
67958
+ import { performance as performance7 } from "node:perf_hooks";
67802
67959
  function relativePath(root, file2) {
67803
67960
  return toProjectDisplayPath(root, file2);
67804
67961
  }
@@ -67831,7 +67988,7 @@ function mergeCandidateTestEntries(baseCandidates, additionalCandidates) {
67831
67988
  return Array.from(merged.values());
67832
67989
  }
67833
67990
  async function collectReviewCandidateTests(input) {
67834
- const candidateStart = performance6.now();
67991
+ const candidateStart = performance7.now();
67835
67992
  const configuredMaxCandidates = input.appliedOptions.maxCandidates;
67836
67993
  const maxCandidates = typeof configuredMaxCandidates === "number" && Number.isFinite(configuredMaxCandidates) ? Math.max(0, Math.floor(configuredMaxCandidates)) : REVIEW_DEFAULT_CANDIDATE_TEST_LIMIT;
67837
67994
  const candidateTests = mergeCandidateTestEntries(listCandidateTestFiles(input.index, input.changedFileList, input.changedSymbolIds, {
@@ -67851,7 +68008,7 @@ async function collectReviewCandidateTests(input) {
67851
68008
  return left.reason.localeCompare(right.reason);
67852
68009
  }).slice(0, maxCandidates);
67853
68010
  if (input.reviewTimings) {
67854
- input.reviewTimings.candidatesMs = Math.round(performance6.now() - candidateStart);
68011
+ input.reviewTimings.candidatesMs = Math.round(performance7.now() - candidateStart);
67855
68012
  }
67856
68013
  return candidateTests;
67857
68014
  }
@@ -68147,7 +68304,7 @@ var init_parse4 = __esm({
68147
68304
  });
68148
68305
 
68149
68306
  // dist/review/changes.js
68150
- import { performance as performance7 } from "node:perf_hooks";
68307
+ import { performance as performance8 } from "node:perf_hooks";
68151
68308
  function deletedPathsForChange(change2) {
68152
68309
  if (change2.kind === "deleted") {
68153
68310
  return [change2.path];
@@ -68165,16 +68322,16 @@ async function collectReviewChanges(projectRoot, appliedOptions, reviewTimings)
68165
68322
  const isIncludedReviewFile = createImpactIncludeMatcher(discoveryGlobRoot, appliedOptions.discovery?.includeGlobs ?? []);
68166
68323
  const changedFiles = /* @__PURE__ */ new Set();
68167
68324
  const explicitFiles = /* @__PURE__ */ new Set();
68168
- const changesStart = performance7.now();
68325
+ const changesStart = performance8.now();
68169
68326
  for (const file2 of appliedOptions.files ?? []) {
68170
68327
  const normalized = normalizeFile(file2, "Review file");
68171
68328
  changedFiles.add(normalized);
68172
68329
  explicitFiles.add(normalized);
68173
68330
  }
68174
68331
  if (reviewTimings) {
68175
- reviewTimings.changesMs = Math.round(performance7.now() - changesStart);
68332
+ reviewTimings.changesMs = Math.round(performance8.now() - changesStart);
68176
68333
  }
68177
- const diffStart = performance7.now();
68334
+ const diffStart = performance8.now();
68178
68335
  const shouldLoadGitDiff = Boolean(appliedOptions.gitBase || appliedOptions.changedSince);
68179
68336
  const diffText = appliedOptions.diffText ?? (shouldLoadGitDiff ? await getUnifiedDiff(projectRoot, {
68180
68337
  base: appliedOptions.gitBase,
@@ -68183,7 +68340,7 @@ async function collectReviewChanges(projectRoot, appliedOptions, reviewTimings)
68183
68340
  }) : "");
68184
68341
  const diff = diffText ? parseUnifiedDiff(diffText) : null;
68185
68342
  if (reviewTimings) {
68186
- reviewTimings.diffMs = Math.round(performance7.now() - diffStart);
68343
+ reviewTimings.diffMs = Math.round(performance8.now() - diffStart);
68187
68344
  }
68188
68345
  const diffHunksByFile = /* @__PURE__ */ new Map();
68189
68346
  const diffKindsByFile = /* @__PURE__ */ new Map();
@@ -69557,10 +69714,10 @@ var init_severity = __esm({
69557
69714
  });
69558
69715
 
69559
69716
  // dist/impact/budgets.js
69560
- import { performance as performance8 } from "node:perf_hooks";
69717
+ import { performance as performance9 } from "node:perf_hooks";
69561
69718
  function createImpactWorkBudget(options) {
69562
69719
  return {
69563
- ...typeof options.timeBudgetMs === "number" && options.timeBudgetMs >= 0 ? { deadlineAt: performance8.now() + options.timeBudgetMs } : {},
69720
+ ...typeof options.timeBudgetMs === "number" && options.timeBudgetMs >= 0 ? { deadlineAt: performance9.now() + options.timeBudgetMs } : {},
69564
69721
  ...typeof options.maxReferenceLookups === "number" && options.maxReferenceLookups >= 0 ? { maxReferenceLookups: options.maxReferenceLookups } : {},
69565
69722
  ...typeof options.maxTotalReferences === "number" && options.maxTotalReferences >= 0 ? { maxTotalReferences: options.maxTotalReferences } : {},
69566
69723
  referenceLookupsStarted: 0,
@@ -69575,7 +69732,7 @@ function isImpactDeadlineExceeded(budget) {
69575
69732
  return true;
69576
69733
  if (budget.deadlineAt === void 0)
69577
69734
  return false;
69578
- if (performance8.now() >= budget.deadlineAt) {
69735
+ if (performance9.now() >= budget.deadlineAt) {
69579
69736
  budget.deadlineExceeded = true;
69580
69737
  return true;
69581
69738
  }
@@ -71629,7 +71786,7 @@ var init_referenceCache = __esm({
71629
71786
  // dist/review/summaries.js
71630
71787
  import fsp37 from "node:fs/promises";
71631
71788
  import path70 from "node:path";
71632
- import { performance as performance9 } from "node:perf_hooks";
71789
+ import { performance as performance10 } from "node:perf_hooks";
71633
71790
  function relativePath2(root, file2) {
71634
71791
  return toProjectDisplayPath(root, file2);
71635
71792
  }
@@ -71995,7 +72152,7 @@ async function summarizeChangedFiles2(input) {
71995
72152
  }
71996
72153
  }
71997
72154
  const defsToResolve = fileEntries.flatMap((entry) => entry.locals);
71998
- const referencesStart = performance9.now();
72155
+ const referencesStart = performance10.now();
71999
72156
  const referenceResults = includeSymbolDetails && maxCallsites > 0 ? await mapLimit(defsToResolve, referenceConcurrency, async (def) => {
72000
72157
  const refs = await referenceCache.get(index, def, {
72001
72158
  maxReferences: maxCallsites + 1
@@ -72003,7 +72160,7 @@ async function summarizeChangedFiles2(input) {
72003
72160
  return { def, refs };
72004
72161
  }) : [];
72005
72162
  if (reviewTimings) {
72006
- reviewTimings.referencesMs = Math.round(performance9.now() - referencesStart);
72163
+ reviewTimings.referencesMs = Math.round(performance10.now() - referencesStart);
72007
72164
  }
72008
72165
  const referencesByHandle = /* @__PURE__ */ new Map();
72009
72166
  for (const entry of referenceResults) {
@@ -72183,7 +72340,7 @@ var init_summaries = __esm({
72183
72340
  });
72184
72341
 
72185
72342
  // dist/review.js
72186
- import { performance as performance10 } from "node:perf_hooks";
72343
+ import { performance as performance11 } from "node:perf_hooks";
72187
72344
  function mergeGraphOptions2(base, override) {
72188
72345
  if (!base)
72189
72346
  return override;
@@ -72227,7 +72384,7 @@ async function buildReviewIndex(input) {
72227
72384
  diffChangesByFile,
72228
72385
  graphOptions
72229
72386
  });
72230
- const indexStart = performance10.now();
72387
+ const indexStart = performance11.now();
72231
72388
  const indexReport = reviewReport?.indexReport ?? (reviewReport ? { timings: {} } : void 0);
72232
72389
  if (reviewReport && !reviewReport.indexReport && indexReport) {
72233
72390
  reviewReport.indexReport = indexReport;
@@ -72260,7 +72417,7 @@ async function buildReviewIndex(input) {
72260
72417
  });
72261
72418
  }
72262
72419
  if (reviewTimings) {
72263
- reviewTimings.indexMs = Math.round(performance10.now() - indexStart);
72420
+ reviewTimings.indexMs = Math.round(performance11.now() - indexStart);
72264
72421
  }
72265
72422
  return {
72266
72423
  index,
@@ -72279,7 +72436,7 @@ async function buildReviewReport(projectRoot, opts = {}, cached2) {
72279
72436
  if (reviewReport && appliedOptions.duplicateTasks === false) {
72280
72437
  delete reviewReport.duplicateAnalysis;
72281
72438
  }
72282
- const totalStart = performance10.now();
72439
+ const totalStart = performance11.now();
72283
72440
  const { changedFiles, explicitFiles, diffHunksByFile, diffKindsByFile, diffChangesByFile } = await collectReviewChanges(projectRoot, appliedOptions, reviewTimings);
72284
72441
  if (changedFiles.size === 0) {
72285
72442
  const riskSummary = computeRiskSummary({
@@ -72313,7 +72470,7 @@ async function buildReviewReport(projectRoot, opts = {}, cached2) {
72313
72470
  report2.base = appliedOptions.gitBase;
72314
72471
  report2.head = appliedOptions.gitHead ?? "HEAD";
72315
72472
  if (reviewTimings)
72316
- reviewTimings.totalMs = Math.round(performance10.now() - totalStart);
72473
+ reviewTimings.totalMs = Math.round(performance11.now() - totalStart);
72317
72474
  return report2;
72318
72475
  }
72319
72476
  const changedFileList = Array.from(changedFiles).sort(comparePaths2);
@@ -72391,7 +72548,7 @@ async function buildReviewReport(projectRoot, opts = {}, cached2) {
72391
72548
  riskRelevantParseFailures,
72392
72549
  exportedChangedCount
72393
72550
  });
72394
- const duplicateAnalysisStart = performance10.now();
72551
+ const duplicateAnalysisStart = performance11.now();
72395
72552
  if (appliedOptions.duplicateTasks !== false) {
72396
72553
  const duplicateReview = await collectReviewDuplicateTasks({
72397
72554
  projectRoot,
@@ -72412,10 +72569,10 @@ async function buildReviewReport(projectRoot, opts = {}, cached2) {
72412
72569
  }
72413
72570
  }
72414
72571
  if (reviewTimings && appliedOptions.duplicateTasks !== false) {
72415
- reviewTimings.duplicateAnalysisMs = Math.round(performance10.now() - duplicateAnalysisStart);
72572
+ reviewTimings.duplicateAnalysisMs = Math.round(performance11.now() - duplicateAnalysisStart);
72416
72573
  }
72417
72574
  if (reviewTimings)
72418
- reviewTimings.totalMs = Math.round(performance10.now() - totalStart);
72575
+ reviewTimings.totalMs = Math.round(performance11.now() - totalStart);
72419
72576
  return report;
72420
72577
  }
72421
72578
  function buildSymbolDefLookup(index) {
@@ -78501,7 +78658,7 @@ var search_exports = {};
78501
78658
  __export(search_exports, {
78502
78659
  handleSearchCommand: () => handleSearchCommand
78503
78660
  });
78504
- import { performance as performance11 } from "node:perf_hooks";
78661
+ import { performance as performance12 } from "node:perf_hooks";
78505
78662
  function parseAgentSearchMode(rawValue) {
78506
78663
  if (rawValue === void 0)
78507
78664
  return "hybrid";
@@ -78511,7 +78668,7 @@ function parseAgentSearchMode(rawValue) {
78511
78668
  throw new Error(`Invalid --mode value "${rawValue}". Expected hybrid, symbol, path, text, graph, or sql.`);
78512
78669
  }
78513
78670
  async function handleSearchCommand(context) {
78514
- const commandStart = performance11.now();
78671
+ const commandStart = performance12.now();
78515
78672
  const query = context.positionals.join(" ").trim();
78516
78673
  if (!query) {
78517
78674
  context.writeStderrLine(SEARCH_HELP_TEXT.trimEnd());
@@ -78535,7 +78692,7 @@ async function handleSearchCommand(context) {
78535
78692
  context.writeStdoutLine(formatAgentSearchResponse(response));
78536
78693
  }
78537
78694
  if (context.commandReport && context.writeCommandReport) {
78538
- context.commandReport.timings.commandMs = Math.round(performance11.now() - commandStart);
78695
+ context.commandReport.timings.commandMs = Math.round(performance12.now() - commandStart);
78539
78696
  context.commandReport.timings.totalMs = context.commandReport.timings.commandMs;
78540
78697
  await context.writeCommandReport(context.commandReport, context.reportFile);
78541
78698
  }
@@ -107516,7 +107673,7 @@ __export(graph_exports, {
107516
107673
  import fs42 from "node:fs";
107517
107674
  import fsp40 from "node:fs/promises";
107518
107675
  import path88 from "node:path";
107519
- import { performance as performance12 } from "node:perf_hooks";
107676
+ import { performance as performance13 } from "node:perf_hooks";
107520
107677
  function resolveGraphCacheMode(cache) {
107521
107678
  return cache ?? "disk";
107522
107679
  }
@@ -107656,11 +107813,11 @@ function stabilizeSymbolGraph(graph) {
107656
107813
  }
107657
107814
  async function handleGraphCommand(context) {
107658
107815
  const commandReport = context.reportEnabled ? { command: "graph", timings: {} } : void 0;
107659
- const commandStart = performance12.now();
107660
- const resolveStart = performance12.now();
107816
+ const commandStart = performance13.now();
107817
+ const resolveStart = performance13.now();
107661
107818
  const files = await context.resolveFiles();
107662
107819
  if (commandReport) {
107663
- commandReport.timings.resolveFilesMs = Math.round(performance12.now() - resolveStart);
107820
+ commandReport.timings.resolveFilesMs = Math.round(performance13.now() - resolveStart);
107664
107821
  }
107665
107822
  const hasExplicitSymbolFlag = context.hasFlag("--symbols") || context.hasFlag("--symbols-only") || context.hasFlag("--symbols-detailed");
107666
107823
  const outputArg = context.getOpt("--output") ?? context.getOpt("--out");
@@ -107702,7 +107859,7 @@ async function handleGraphCommand(context) {
107702
107859
  const finalizeReport = async () => {
107703
107860
  if (!commandReport)
107704
107861
  return;
107705
- commandReport.timings.commandMs = Math.round(performance12.now() - commandStart);
107862
+ commandReport.timings.commandMs = Math.round(performance13.now() - commandStart);
107706
107863
  commandReport.timings.totalMs = commandReport.timings.commandMs;
107707
107864
  await context.writeCommandReport(commandReport, context.reportFile);
107708
107865
  };
@@ -107922,7 +108079,7 @@ var cli_exports = {};
107922
108079
  __export(cli_exports, {
107923
108080
  handleIndexCommand: () => handleIndexCommand
107924
108081
  });
107925
- import { performance as performance13 } from "node:perf_hooks";
108082
+ import { performance as performance14 } from "node:perf_hooks";
107926
108083
  function formatIndexOutput(output) {
107927
108084
  const lines = [`Indexed ${output.files} file(s) with ${output.edges} edge(s).`];
107928
108085
  if (output.analysis.mode !== "semantic") {
@@ -107940,11 +108097,11 @@ function formatIndexOutput(output) {
107940
108097
  async function handleIndexCommand(context) {
107941
108098
  const verbose = context.hasFlag("--verbose");
107942
108099
  const commandReport = context.reportEnabled ? { command: "index", timings: {} } : void 0;
107943
- const commandStart = performance13.now();
107944
- const resolveStart = performance13.now();
108100
+ const commandStart = performance14.now();
108101
+ const resolveStart = performance14.now();
107945
108102
  const files = await context.resolveFiles();
107946
108103
  if (commandReport) {
107947
- commandReport.timings.resolveFilesMs = Math.round(performance13.now() - resolveStart);
108104
+ commandReport.timings.resolveFilesMs = Math.round(performance14.now() - resolveStart);
107948
108105
  }
107949
108106
  const threads = parseNonNegativeIntegerOption(context.getOpt("--threads"), "--threads", 0);
107950
108107
  const cache = parseCacheModeOption(context.getOpt("--cache"));
@@ -108010,7 +108167,7 @@ async function handleIndexCommand(context) {
108010
108167
  }
108011
108168
  }
108012
108169
  if (commandReport) {
108013
- commandReport.timings.commandMs = Math.round(performance13.now() - commandStart);
108170
+ commandReport.timings.commandMs = Math.round(performance14.now() - commandStart);
108014
108171
  commandReport.timings.totalMs = commandReport.timings.commandMs;
108015
108172
  await context.writeCommandReport(commandReport, context.reportFile);
108016
108173
  }
@@ -111003,7 +111160,7 @@ var review_exports = {};
111003
111160
  __export(review_exports, {
111004
111161
  handleReviewCommand: () => handleReviewCommand
111005
111162
  });
111006
- import { performance as performance14 } from "node:perf_hooks";
111163
+ import { performance as performance15 } from "node:perf_hooks";
111007
111164
  import path92 from "node:path";
111008
111165
  function parseReviewDepth(value) {
111009
111166
  if (value === "minimal" || value === "standard" || value === "deep") {
@@ -111195,7 +111352,7 @@ async function collectReviewDuplicateSummary(input) {
111195
111352
  });
111196
111353
  }
111197
111354
  async function handleReviewCommand(context) {
111198
- const commandStart = performance14.now();
111355
+ const commandStart = performance15.now();
111199
111356
  const changedSince = context.getOpt("--changed-since");
111200
111357
  const base = context.getOpt("--base") ?? (changedSince === void 0 ? "HEAD" : void 0);
111201
111358
  const head = context.getOpt("--head") ?? (changedSince === void 0 ? "WORKTREE" : void 0);
@@ -111290,7 +111447,7 @@ async function handleReviewCommand(context) {
111290
111447
  context.writeJSONLine(report);
111291
111448
  }
111292
111449
  if (context.commandReport) {
111293
- context.commandReport.timings.commandMs = Math.round(performance14.now() - commandStart);
111450
+ context.commandReport.timings.commandMs = Math.round(performance15.now() - commandStart);
111294
111451
  context.commandReport.timings.totalMs = context.commandReport.timings.commandMs;
111295
111452
  await context.writeCommandReport(context.commandReport, context.reportFile);
111296
111453
  }
@@ -111579,7 +111736,7 @@ __export(inspect_exports, {
111579
111736
  handleHotspotsCommand: () => handleHotspotsCommand,
111580
111737
  handleInspectCommand: () => handleInspectCommand
111581
111738
  });
111582
- import { performance as performance15 } from "node:perf_hooks";
111739
+ import { performance as performance16 } from "node:perf_hooks";
111583
111740
  import fs44 from "node:fs";
111584
111741
  import path93 from "node:path";
111585
111742
  function defaultCacheIndexPath(projectRoot, cacheDir, cacheLocation) {
@@ -111841,18 +111998,18 @@ async function buildInspectReport(projectRoot, includeRoots, files, discovery, g
111841
111998
  };
111842
111999
  }
111843
112000
  async function handleInspectCommand(context) {
111844
- const commandStart = performance15.now();
112001
+ const commandStart = performance16.now();
111845
112002
  const cache = parseCacheModeOption(context.getOpt("--cache"));
111846
112003
  const limit = parsePositiveIntegerOption(context.getOpt("--limit"), "--limit", 20);
111847
- const resolveStart = performance15.now();
112004
+ const resolveStart = performance16.now();
111848
112005
  const files = await context.resolveFilesFromRoots();
111849
112006
  if (context.commandReport) {
111850
- context.commandReport.timings.resolveFilesMs = Math.round(performance15.now() - resolveStart);
112007
+ context.commandReport.timings.resolveFilesMs = Math.round(performance16.now() - resolveStart);
111851
112008
  }
111852
112009
  const report = await buildInspectReport(context.projectRootFs, context.includeRootsAbs, files, context.discoveryOptions, context.graphOptions, context.languageExtensions, cache, context.getOpt("--cache-dir"), context.cacheLocation, context.nativeMode, context.workerOpts, context.progressHandler, context.commandReport?.index, limit, context.hasFlag("--duplicates"), context.writeStderrLine);
111853
112010
  writeCliOutput(context, report, formatInspectReport);
111854
112011
  if (context.commandReport && context.writeCommandReport) {
111855
- context.commandReport.timings.commandMs = Math.round(performance15.now() - commandStart);
112012
+ context.commandReport.timings.commandMs = Math.round(performance16.now() - commandStart);
111856
112013
  context.commandReport.timings.totalMs = context.commandReport.timings.commandMs;
111857
112014
  await context.writeCommandReport(context.commandReport, context.reportFile);
111858
112015
  }