@lzehrung/codegraph 2.3.14 → 2.3.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (64) hide show
  1. package/README.md +1 -1
  2. package/codegraph-skill/codegraph/SKILL.md +11 -3
  3. package/dist/agent/query-index/workerPool.d.ts.map +1 -1
  4. package/dist/agent/query-index/workerPool.js +2 -0
  5. package/dist/agent/query-index/workerPool.js.map +1 -1
  6. package/dist/bin/cli.js +381 -174
  7. package/dist/bin/queryIndexWorker.js +30 -0
  8. package/dist/bin/rawQueryWorker.js +34 -0
  9. package/dist/cli/commandTable.d.ts.map +1 -1
  10. package/dist/cli/commandTable.js +14 -1
  11. package/dist/cli/commandTable.js.map +1 -1
  12. package/dist/cli/help.d.ts +4 -4
  13. package/dist/cli/help.d.ts.map +1 -1
  14. package/dist/cli/help.js +8 -7
  15. package/dist/cli/help.js.map +1 -1
  16. package/dist/cli/mcp.js +11 -6
  17. package/dist/cli/mcp.js.map +1 -1
  18. package/dist/cli/options.d.ts.map +1 -1
  19. package/dist/cli/options.js +3 -2
  20. package/dist/cli/options.js.map +1 -1
  21. package/dist/cli/orient.d.ts +6 -2
  22. package/dist/cli/orient.d.ts.map +1 -1
  23. package/dist/cli/orient.js +13 -1
  24. package/dist/cli/orient.js.map +1 -1
  25. package/dist/cli/progress.d.ts.map +1 -1
  26. package/dist/cli/progress.js +21 -7
  27. package/dist/cli/progress.js.map +1 -1
  28. package/dist/cli/server.d.ts.map +1 -1
  29. package/dist/cli/server.js +6 -2
  30. package/dist/cli/server.js.map +1 -1
  31. package/dist/indexer/build-index.d.ts.map +1 -1
  32. package/dist/indexer/build-index.js +148 -28
  33. package/dist/indexer/build-index.js.map +1 -1
  34. package/dist/indexer/types.d.ts +2 -0
  35. package/dist/indexer/types.d.ts.map +1 -1
  36. package/dist/mcp/handlers.d.ts +6 -1
  37. package/dist/mcp/handlers.d.ts.map +1 -1
  38. package/dist/mcp/handlers.js +13 -9
  39. package/dist/mcp/handlers.js.map +1 -1
  40. package/dist/mcp/protocol.d.ts.map +1 -1
  41. package/dist/mcp/protocol.js +1 -1
  42. package/dist/mcp/protocol.js.map +1 -1
  43. package/dist/native/runtime.d.ts.map +1 -1
  44. package/dist/native/runtime.js +2 -0
  45. package/dist/native/runtime.js.map +1 -1
  46. package/dist/sqlite/rawQueryWorkerPool.d.ts.map +1 -1
  47. package/dist/sqlite/rawQueryWorkerPool.js +10 -6
  48. package/dist/sqlite/rawQueryWorkerPool.js.map +1 -1
  49. package/dist/sqlite-driver.d.ts +1 -0
  50. package/dist/sqlite-driver.d.ts.map +1 -1
  51. package/dist/sqlite-driver.js +6 -0
  52. package/dist/sqlite-driver.js.map +1 -1
  53. package/dist/util/projectFiles.d.ts.map +1 -1
  54. package/dist/util/projectFiles.js +10 -1
  55. package/dist/util/projectFiles.js.map +1 -1
  56. package/dist/util/windowsProcessDrain.d.ts +23 -0
  57. package/dist/util/windowsProcessDrain.d.ts.map +1 -0
  58. package/dist/util/windowsProcessDrain.js +51 -0
  59. package/dist/util/windowsProcessDrain.js.map +1 -0
  60. package/dist/worker/nativeWorkerPool.d.ts.map +1 -1
  61. package/dist/worker/nativeWorkerPool.js +2 -0
  62. package/dist/worker/nativeWorkerPool.js.map +1 -1
  63. package/docs/mcp.md +9 -9
  64. package/package.json +3 -3
@@ -414,15 +414,67 @@ function emitIndexCheckActivity(opts, activity, current = 0, total = 0) {
414
414
  total,
415
415
  });
416
416
  }
417
- const DISCOVERY_TIMING_STEP_NAMES = new Set(["git-list", "git-ignore", "filesystem-scan"]);
418
- function resetDiscoveryTimingSteps(timings) {
417
+ function emitIndexBuildActivity(opts, activity, current, total) {
418
+ opts?.onProgress?.({
419
+ type: "progress",
420
+ phase: "update",
421
+ mode: "build",
422
+ message: activity,
423
+ activity,
424
+ current,
425
+ total,
426
+ });
427
+ }
428
+ /**
429
+ * Names and times a post-parse persistence phase (cache write, manifest, graph finalize,
430
+ * snapshot write) so the interactive spinner shows what is happening instead of freezing at
431
+ * the last "Indexed N/N files" count, and so `--report` exposes each phase's cost.
432
+ */
433
+ async function timeIndexBuildPhase(args) {
434
+ if (args.buildStartedAt !== undefined) {
435
+ emitIndexBuildActivity(args.opts, args.activity, args.current, args.total);
436
+ }
437
+ const startedAt = performance.now();
438
+ const result = await args.fn();
439
+ recordBuildTimingStep(args.timings, { name: args.stepName, ms: Math.round(performance.now() - startedAt) });
440
+ return result;
441
+ }
442
+ function createCountedCheckProgress(opts, activity, total) {
443
+ emitIndexCheckActivity(opts, activity, 0, total);
444
+ let completed = 0;
445
+ return () => {
446
+ completed += 1;
447
+ if (completed === total || completed % 100 === 0) {
448
+ emitIndexCheckActivity(opts, activity, completed, total);
449
+ }
450
+ };
451
+ }
452
+ const TRANSIENT_BUILD_TIMING_STEP_NAMES = {
453
+ "git-list": true,
454
+ "git-ignore": true,
455
+ "filesystem-scan": true,
456
+ "cache-probe": true,
457
+ "persist-cache": true,
458
+ "workspace-manifests": true,
459
+ "index-manifest": true,
460
+ finalize: true,
461
+ "snapshot-write": true,
462
+ };
463
+ /**
464
+ * Clears named step timings and coarse discovery fields left over from a prior build that
465
+ * reused the same `BuildReport` object (for example an MCP session's `buildOptions.report`
466
+ * across `refresh_index` calls), so a reused report never mixes this build's steps with a
467
+ * previous one's.
468
+ */
469
+ function resetTransientBuildTimings(timings) {
419
470
  if (!timings)
420
471
  return;
421
472
  delete timings.gitListMs;
422
473
  delete timings.filesystemScanMs;
423
474
  delete timings.sourceDiscoveryMs;
424
475
  delete timings.metadataDiscoveryMs;
425
- const remaining = (timings.steps ?? []).filter((step) => !DISCOVERY_TIMING_STEP_NAMES.has(step.name));
476
+ delete timings.cacheProbeMs;
477
+ const remaining = (timings.steps ?? []).filter((step) => !Object.hasOwn(TRANSIENT_BUILD_TIMING_STEP_NAMES, step.name));
426
478
  if (remaining.length) {
427
479
  timings.steps = remaining;
428
480
  return;
@@ -534,6 +586,12 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
534
586
  ? projectIndexManifestEntries(cachedGraphEntries ?? [])
535
587
  : new Map();
536
588
  const modules = new Map();
589
+ const moduleCacheAvailable = opts?.cache !== "disk" || diskModuleCacheExists(projectRoot, opts);
590
+ const shouldReportFileCacheCheck = Boolean(helperOpts?.reportDiscoveryProgress);
591
+ const reportFileCacheCheck = shouldReportFileCacheCheck
592
+ ? createCountedCheckProgress(opts, "Checking file cache", normalizedFiles.length)
593
+ : () => undefined;
594
+ const cacheProbeStart = performance.now();
537
595
  const gitAvailable = await isGitRepo(projectRoot);
538
596
  const needsPersistentSignatures = cacheEnabled || useManifest;
539
597
  const useGitSignatures = gitAvailable && needsPersistentSignatures;
@@ -580,7 +638,6 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
580
638
  // Cache lookup determines the work a full build will actually parse. Complete that cheap phase
581
639
  // before starting Piscina, so a warm build does not bootstrap workers and partial hits bound
582
640
  // the pool to real parse misses rather than the input file list.
583
- const moduleCacheAvailable = opts?.cache !== "disk" || diskModuleCacheExists(projectRoot, opts);
584
641
  const cacheProbes = new Map(await mapLimit(normalizedFiles, conc, async (file) => {
585
642
  try {
586
643
  let sigInfo = fileSignatures.get(file);
@@ -616,7 +673,15 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
616
673
  catch (error) {
617
674
  return [file, { error }];
618
675
  }
676
+ finally {
677
+ reportFileCacheCheck();
678
+ }
619
679
  }));
680
+ if (timings) {
681
+ const cacheProbeMs = Math.round(performance.now() - cacheProbeStart);
682
+ timings.cacheProbeMs = cacheProbeMs;
683
+ recordBuildTimingStep(timings, { name: "cache-probe", ms: cacheProbeMs });
684
+ }
620
685
  const cacheMisses = Array.from(cacheProbes, ([file, probe]) => !("error" in probe) && !probe.mod ? file : null).filter((file) => file !== null);
621
686
  const workerSetup = await setupWorkerPool(opts, countNativeWorkerEligibleFiles(cacheMisses, opts?.languageExtensions));
622
687
  try {
@@ -658,6 +723,8 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
658
723
  buildStartedAt = performance.now();
659
724
  emitIndexLifecycleProgress(opts, "start", "build", totalFiles);
660
725
  };
726
+ if (cacheMisses.length)
727
+ ensureBuildProgressStarted();
661
728
  const fileResults = await mapLimit(normalizedFiles, conc, async (file) => {
662
729
  try {
663
730
  const cacheProbe = cacheProbes.get(file);
@@ -823,9 +890,32 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
823
890
  pendingCacheWrites.push(cacheWrite);
824
891
  }
825
892
  if (pendingCacheWrites.length) {
826
- writeModulesToCache(projectRoot, pendingCacheWrites, opts);
893
+ if (opts?.cache === "disk") {
894
+ await timeIndexBuildPhase({
895
+ opts,
896
+ timings,
897
+ buildStartedAt,
898
+ stepName: "persist-cache",
899
+ activity: "Writing disk cache",
900
+ current: totalFiles,
901
+ total: totalFiles,
902
+ fn: () => writeModulesToCache(projectRoot, pendingCacheWrites, opts),
903
+ });
904
+ }
905
+ else {
906
+ writeModulesToCache(projectRoot, pendingCacheWrites, opts);
907
+ }
827
908
  }
828
- const workspaceManifestEdges = await collectWorkspaceManifestDependencyEdges(projectRoot, normalizedFiles.filter((file) => path.basename(file) === "package.json"), opts?.discovery, opts?.logLevel);
909
+ const workspaceManifestEdges = await timeIndexBuildPhase({
910
+ opts,
911
+ timings,
912
+ buildStartedAt,
913
+ stepName: "workspace-manifests",
914
+ activity: "Resolving workspace manifests",
915
+ current: totalFiles,
916
+ total: totalFiles,
917
+ fn: () => collectWorkspaceManifestDependencyEdges(projectRoot, normalizedFiles.filter((file) => path.basename(file) === "package.json"), opts?.discovery, opts?.logLevel),
918
+ });
829
919
  appendUniqueGraphEdges(workspaceManifestEdges);
830
920
  if (timings)
831
921
  timings.graphMs = Math.round(performance.now() - graphStart);
@@ -845,16 +935,27 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
845
935
  }
846
936
  }
847
937
  if (manifestEntries) {
848
- await writeIndexManifestSnapshot({
849
- projectRoot,
938
+ await timeIndexBuildPhase({
850
939
  opts,
851
- graphOptions,
852
- ...(resolverEnvironmentFingerprint ? { resolverEnvironmentFingerprint } : {}),
853
- files: manifestEntries,
854
940
  timings,
855
- manifestReport: report?.manifest,
856
- ...(helperOpts?.transientFiles !== undefined ? { transientFiles: helperOpts.transientFiles } : {}),
857
- ...(helperOpts?.symlinkDirectories !== undefined ? { symlinkDirectories: helperOpts.symlinkDirectories } : {}),
941
+ buildStartedAt,
942
+ stepName: "index-manifest",
943
+ activity: "Writing index manifest",
944
+ current: totalFiles,
945
+ total: totalFiles,
946
+ fn: () => writeIndexManifestSnapshot({
947
+ projectRoot,
948
+ opts,
949
+ graphOptions,
950
+ ...(resolverEnvironmentFingerprint ? { resolverEnvironmentFingerprint } : {}),
951
+ files: manifestEntries,
952
+ timings,
953
+ manifestReport: report?.manifest,
954
+ ...(helperOpts?.transientFiles !== undefined ? { transientFiles: helperOpts.transientFiles } : {}),
955
+ ...(helperOpts?.symlinkDirectories !== undefined
956
+ ? { symlinkDirectories: helperOpts.symlinkDirectories }
957
+ : {}),
958
+ }),
858
959
  });
859
960
  }
860
961
  const indexManifestEntries = manifestEntries
@@ -863,22 +964,40 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
863
964
  return [file, { ...entry, ...(cacheSig ? { cacheSig } : {}) }];
864
965
  }))
865
966
  : manifestEntriesForIndex;
866
- const index = await finalizeProjectIndex({
867
- projectRoot,
868
- normalizedProjectRoot,
967
+ const index = await timeIndexBuildPhase({
869
968
  opts,
870
969
  timings,
871
- totalStart,
872
- graph,
873
- modules,
874
- parsedMap,
875
- bloomFilterCache,
876
- ...(projectFiles !== undefined ? { projectFiles } : {}),
877
- buildReport: report,
878
- manifestEntries: indexManifestEntries,
970
+ buildStartedAt,
971
+ stepName: "finalize",
972
+ activity: "Finalizing project graph",
973
+ current: totalFiles,
974
+ total: totalFiles,
975
+ fn: () => finalizeProjectIndex({
976
+ projectRoot,
977
+ normalizedProjectRoot,
978
+ opts,
979
+ timings,
980
+ totalStart,
981
+ graph,
982
+ modules,
983
+ parsedMap,
984
+ bloomFilterCache,
985
+ ...(projectFiles !== undefined ? { projectFiles } : {}),
986
+ buildReport: report,
987
+ manifestEntries: indexManifestEntries,
988
+ }),
879
989
  });
880
990
  if (manifestEntries) {
881
- await writeProjectIndexSnapshot(projectRoot, opts, index, projectSnapshotFilesSignature(manifestEntries, projectRoot));
991
+ await timeIndexBuildPhase({
992
+ opts,
993
+ timings,
994
+ buildStartedAt,
995
+ stepName: "snapshot-write",
996
+ activity: "Writing project snapshot",
997
+ current: totalFiles,
998
+ total: totalFiles,
999
+ fn: () => writeProjectIndexSnapshot(projectRoot, opts, index, projectSnapshotFilesSignature(manifestEntries, projectRoot)),
1000
+ });
882
1001
  }
883
1002
  if (buildStartedAt !== undefined) {
884
1003
  // Report the whole operation the caller waited on. Discovery runs before the first
@@ -896,7 +1015,7 @@ async function buildIndexFromFileListShared(projectRoot, rawFiles, opts, helperO
896
1015
  }
897
1016
  async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpts) {
898
1017
  const timings = opts?.report ? (opts.report.timings ??= {}) : undefined;
899
- resetDiscoveryTimingSteps(timings);
1018
+ resetTransientBuildTimings(timings);
900
1019
  await initializeFileIdentityCaseSensitivity(projectRoot);
901
1020
  try {
902
1021
  const useDiskCache = (opts?.cache ?? "off") === "disk";
@@ -972,6 +1091,7 @@ async function buildProjectIndexWithManifestOptions(projectRoot, opts, helperOpt
972
1091
  manifestMode: useDiskCache ? "read-write" : "off",
973
1092
  warnNoFilesMessage: `Warning: No files found in project root: ${projectRoot}. Check codegraph.config.json globs and CLI --include-glob/--ignore-glob filters. Diagnostic: codegraph doctor`,
974
1093
  ...(helperOpts?.ignoreExistingManifest ? { ignoreExistingManifest: true } : {}),
1094
+ ...(helperOpts?.reportDiscoveryProgress ? { reportDiscoveryProgress: true } : {}),
975
1095
  projectFiles,
976
1096
  transientFiles,
977
1097
  ...(discoveredSymlinkDirectories !== undefined ? { symlinkDirectories: discoveredSymlinkDirectories } : {}),
@@ -1070,7 +1190,7 @@ export async function buildProjectIndexIncremental(projectRoot, rawOpts) {
1070
1190
  graphOptions.fast = false;
1071
1191
  const { normalizedProjectRoot, report, timings, totalStart, cacheMode, cacheEnabled, onFallbackImportExtraction } = createIndexBuildRunState(projectRoot, opts, graphOptions);
1072
1192
  const discoveryTimings = opts?.report ? (opts.report.timings ??= {}) : undefined;
1073
- resetDiscoveryTimingSteps(discoveryTimings);
1193
+ resetTransientBuildTimings(discoveryTimings);
1074
1194
  let checkProgressActive = false;
1075
1195
  const startCheckProgress = () => {
1076
1196
  if (checkProgressActive)