pi-lens 4.4.2 → 4.4.4

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 (143) hide show
  1. package/CHANGELOG.md +306 -0
  2. package/README.md +13 -1
  3. package/dist/{chunk-VITUCP5A.js → chunk-24AKHFGP.js} +9 -7
  4. package/dist/{chunk-JSQFTSC6.js → chunk-2FN63LPV.js} +11 -11
  5. package/dist/{chunk-CF24OIYI.js → chunk-2KXTHF3M.js} +20 -20
  6. package/dist/{chunk-ESBQHFMJ.js → chunk-3DA2JDOL.js} +543 -349
  7. package/dist/{chunk-RWWYJ623.js → chunk-3KAZU3VR.js} +1 -1
  8. package/dist/chunk-3PTHXJXR.js +28 -0
  9. package/dist/chunk-3QED2P4V.js +31 -0
  10. package/dist/{chunk-N2H3ZPZY.js → chunk-3RKP3FMS.js} +1 -0
  11. package/dist/{chunk-XVF2VVXP.js → chunk-5HSRT26B.js} +2 -2
  12. package/dist/{chunk-XKIZ2HNN.js → chunk-5IUSNGOC.js} +18 -16
  13. package/dist/{chunk-EJIUXPBX.js → chunk-5OFV52AM.js} +1 -1
  14. package/dist/{chunk-AW7R2GWG.js → chunk-5VSF4UUQ.js} +12 -7
  15. package/dist/{chunk-NW7BOILN.js → chunk-5WRPDP2A.js} +18 -18
  16. package/dist/{chunk-7ULNCDCO.js → chunk-6DLWKFEX.js} +35 -34
  17. package/dist/{chunk-WOA4LN2R.js → chunk-7D6L4MTX.js} +2 -2
  18. package/dist/{chunk-GAEGEPOC.js → chunk-7HXE67BN.js} +2 -2
  19. package/dist/chunk-AGSS7277.js +84 -0
  20. package/dist/{chunk-R3PCY3YS.js → chunk-AMERCH6V.js} +13 -13
  21. package/dist/{chunk-WU567RSC.js → chunk-ATKJFDQN.js} +14 -10
  22. package/dist/{chunk-VSSBEUFT.js → chunk-AWSCPCN2.js} +16 -16
  23. package/dist/{chunk-VCHXCQFG.js → chunk-BWD5EPKO.js} +3 -3
  24. package/dist/{chunk-ODBNXWT4.js → chunk-CT4MN5IU.js} +1271 -262
  25. package/dist/{chunk-34WSVGEM.js → chunk-CUHSHSB6.js} +83 -5
  26. package/dist/{chunk-CHOW74WE.js → chunk-CYRKQRPM.js} +7 -10
  27. package/dist/{chunk-Q7DUK4LX.js → chunk-DEL2U6CM.js} +5 -5
  28. package/dist/{chunk-6C7JWPCP.js → chunk-DGUKIYSI.js} +26 -23
  29. package/dist/{chunk-PHDMFAKM.js → chunk-DUO2TU77.js} +2 -2
  30. package/dist/{chunk-MRXKYPCS.js → chunk-EBC36LNN.js} +2 -2
  31. package/dist/chunk-ED36RF6Y.js +17 -0
  32. package/dist/{chunk-76MWC2K6.js → chunk-EUXZEFTV.js} +90 -97
  33. package/dist/{chunk-QOZXJ4NA.js → chunk-FM4RDMKT.js} +11 -8
  34. package/dist/{chunk-66VMUL7Q.js → chunk-GAQ7OQG6.js} +31 -28
  35. package/dist/{chunk-Q6ZWTCLY.js → chunk-GL5LUMZE.js} +23 -24
  36. package/dist/{chunk-BKE7E7TC.js → chunk-GMX5OS7B.js} +9 -9
  37. package/dist/{chunk-ZHFZWTZV.js → chunk-GSLNCYGG.js} +11 -11
  38. package/dist/{chunk-ARJ6PLFH.js → chunk-GSM5UU7A.js} +26 -34
  39. package/dist/{chunk-724UL2TJ.js → chunk-GXFV6MYU.js} +3 -3
  40. package/dist/chunk-HAD7QRQI.js +30 -0
  41. package/dist/{chunk-QOBUHWHX.js → chunk-HRNHZA3N.js} +6 -6
  42. package/dist/{chunk-VWCXW2E7.js → chunk-HWCCCQNX.js} +1 -1
  43. package/dist/{chunk-7DYBLXYA.js → chunk-HYGZEMX6.js} +3 -3
  44. package/dist/chunk-IJYGIDF4.js +186 -0
  45. package/dist/{chunk-4W4VLAPJ.js → chunk-IQWN5JCP.js} +11 -11
  46. package/dist/{chunk-GZYBXISR.js → chunk-IUE7SSKV.js} +9 -9
  47. package/dist/{chunk-IBGIOBEE.js → chunk-IWDLU6K3.js} +68 -5
  48. package/dist/{chunk-XCJN5IJU.js → chunk-IYNJ6YPZ.js} +4 -4
  49. package/dist/{chunk-JAU72XNY.js → chunk-J64T55EI.js} +3 -3
  50. package/dist/{chunk-2BBLFX75.js → chunk-J6MPGQTS.js} +5 -5
  51. package/dist/chunk-JORED7EX.js +44 -0
  52. package/dist/{chunk-6SDEP3AJ.js → chunk-JUKMK5Y4.js} +8 -8
  53. package/dist/{chunk-IBRALRBR.js → chunk-JW3YWBVB.js} +15 -14
  54. package/dist/{chunk-L3BLXZG7.js → chunk-KAPJINKN.js} +30 -69
  55. package/dist/{chunk-2B73ZPOK.js → chunk-KN35K52N.js} +3 -3
  56. package/dist/{chunk-C34ZXZ7O.js → chunk-KUA4OJ5Y.js} +24 -11
  57. package/dist/chunk-KYUB6QDC.js +72 -0
  58. package/dist/{chunk-67WMNWEE.js → chunk-LLJNQKQW.js} +12 -10
  59. package/dist/chunk-LZSEQW6A.js +57 -0
  60. package/dist/{chunk-ZANLILYD.js → chunk-M2IFVFWU.js} +2 -2
  61. package/dist/chunk-M2UD3LPU.js +88 -0
  62. package/dist/{chunk-WOKKRCJ5.js → chunk-MBKQHGA7.js} +15 -14
  63. package/dist/{chunk-P7266HLM.js → chunk-MPNJLASA.js} +2 -2
  64. package/dist/{chunk-6GFRNVYR.js → chunk-MZGQNL3Y.js} +10 -10
  65. package/dist/{chunk-QZVW3AAK.js → chunk-N5FLEO7W.js} +33 -31
  66. package/dist/{chunk-3JNKMP3B.js → chunk-O4NWQT6I.js} +4 -4
  67. package/dist/{chunk-PFNSLWU5.js → chunk-OHKJ7PQF.js} +33 -31
  68. package/dist/{chunk-VRZP76YM.js → chunk-P63GXC37.js} +4 -1
  69. package/dist/{chunk-XTMJ7XDT.js → chunk-POMLXPMY.js} +1 -1
  70. package/dist/{chunk-QQNQ7SJW.js → chunk-QBFBRV2B.js} +10 -4
  71. package/dist/{chunk-3UUPUE3O.js → chunk-R3T7CLQ2.js} +84 -60
  72. package/dist/{chunk-23WOO7TX.js → chunk-R7YOCSNV.js} +10 -10
  73. package/dist/{chunk-2CD5RS6M.js → chunk-RKBXZN3A.js} +23 -2
  74. package/dist/{chunk-OG5UUPN7.js → chunk-RKKOYPRA.js} +14 -14
  75. package/dist/{chunk-52FUZI6K.js → chunk-RMLC67LF.js} +1 -1
  76. package/dist/{chunk-XMSXXAHW.js → chunk-ROYSX2S5.js} +2 -2
  77. package/dist/{chunk-PV6LR5P5.js → chunk-RVK72CZX.js} +9605 -14776
  78. package/dist/{chunk-FLKWCIZH.js → chunk-S2HEXIWI.js} +5 -5
  79. package/dist/{chunk-UZ3TUTRV.js → chunk-S67KYG37.js} +1 -1
  80. package/dist/{chunk-JWA5I3SR.js → chunk-SD4I7ZO4.js} +16 -18
  81. package/dist/{chunk-Z527N6YQ.js → chunk-SKAMFC7A.js} +12 -12
  82. package/dist/{chunk-MQLUAEOC.js → chunk-SRYEJNES.js} +2 -2
  83. package/dist/{chunk-BRGDSBJE.js → chunk-SYE6T2EB.js} +12 -12
  84. package/dist/{chunk-GXW6K4YJ.js → chunk-T57LYLZT.js} +1 -1
  85. package/dist/{chunk-ZXYDQ3TZ.js → chunk-T7FEBKXN.js} +48 -15
  86. package/dist/{chunk-IXXDFY73.js → chunk-TIQ2PKDW.js} +3 -3
  87. package/dist/{chunk-QILIYSJF.js → chunk-TLX72JTG.js} +1 -1
  88. package/dist/{chunk-TYM6OUIT.js → chunk-TOY6QKOH.js} +12 -12
  89. package/dist/{chunk-IZSHAIJ7.js → chunk-TRETHTQO.js} +12 -12
  90. package/dist/{chunk-3VPVG5SU.js → chunk-U44DJXQP.js} +10 -6
  91. package/dist/{chunk-PNQDPDRC.js → chunk-UA7ZXYDR.js} +1 -1
  92. package/dist/{chunk-OXXKQCSV.js → chunk-UBOQLVT2.js} +4 -4
  93. package/dist/{chunk-QGQ5CCCG.js → chunk-UMS2MFPZ.js} +2 -2
  94. package/dist/{chunk-QHJ6DQ2R.js → chunk-US7ECLD7.js} +55 -13
  95. package/dist/{chunk-EETY4LF7.js → chunk-UZ2XCLYS.js} +8 -8
  96. package/dist/chunk-V35JG4JB.js +2667 -0
  97. package/dist/{chunk-N3X6Q4YT.js → chunk-VOOAPROD.js} +3 -3
  98. package/dist/{chunk-SAEFPSB6.js → chunk-W66O5E25.js} +21 -11
  99. package/dist/{chunk-KBG47GHP.js → chunk-WMYE35PX.js} +15 -11
  100. package/dist/{chunk-5NGR67U5.js → chunk-XBJVAEEO.js} +32 -5
  101. package/dist/chunk-XHJBS436.js +4217 -0
  102. package/dist/{chunk-GBPP5P7X.js → chunk-XTBDCR27.js} +4 -4
  103. package/dist/{chunk-4UNXBNE2.js → chunk-XTFDJI3T.js} +11 -11
  104. package/dist/{chunk-UXGETRP4.js → chunk-XYURS6EN.js} +13 -12
  105. package/dist/{chunk-RPLINGVB.js → chunk-Y6GZHWSJ.js} +6 -6
  106. package/dist/{chunk-2ZGIZPDG.js → chunk-YCWYO6TQ.js} +41 -38
  107. package/dist/{chunk-66SXUJCA.js → chunk-YH5MVY2U.js} +10 -8
  108. package/dist/{chunk-AOHZU2BM.js → chunk-YNE3RP52.js} +1 -1
  109. package/dist/{chunk-GOYRYZ2R.js → chunk-ZHH37KZL.js} +72 -20
  110. package/dist/{chunk-ZGDGW2V3.js → chunk-ZIVJBOO3.js} +2 -2
  111. package/dist/{chunk-GHB6AYCP.js → chunk-ZVUL7QYY.js} +29 -28
  112. package/dist/index.js +11559 -6213
  113. package/dist/mcp/analyze-cli.js +10 -9
  114. package/dist/mcp/cli.js +41 -38
  115. package/dist/mcp/server.js +1142 -495
  116. package/dist/mcp/worker.js +61 -57
  117. package/dist/probes/installer.js +19 -19
  118. package/dist/workers/project-snapshot-persist-worker.js +28 -9
  119. package/dist/workers/review-graph-persist-worker.js +1 -1
  120. package/docs/configuration.md +11 -0
  121. package/docs/engineering-principles.md +32 -0
  122. package/docs/lsp-capability-matrix.md +1 -1
  123. package/docs/lsp-idle-eviction.md +15 -26
  124. package/docs/pi-lens-fixer.md +117 -13
  125. package/docs/pi-lens-merge-policy.md +109 -11
  126. package/docs/pi-lens-monitor.md +8 -2
  127. package/docs/pi-lens-retro.md +4 -1
  128. package/docs/pi-lens-reviewer.md +114 -3
  129. package/docs/pi-lens-subagent.md +27 -14
  130. package/docs/release-qa-baseline.md +3 -1
  131. package/docs/servercapabilities.md +9 -19
  132. package/package.json +7 -5
  133. package/scripts/download-grammars.js +40 -7
  134. package/scripts/lib/latency-log-phases.mjs +95 -0
  135. package/scripts/rpc-load-check.mjs +570 -81
  136. package/dist/chunk-3WA4HP4I.js +0 -55
  137. package/dist/chunk-466Z26MG.js +0 -30
  138. package/dist/chunk-5JM2524W.js +0 -84
  139. package/dist/chunk-CO2G5PHH.js +0 -84
  140. package/dist/chunk-LOP6GRPH.js +0 -31
  141. package/dist/chunk-UOPVJQ4N.js +0 -103
  142. package/dist/chunk-XPZPXCBV.js +0 -28
  143. /package/dist/{chunk-FC4RP4I2.js → chunk-6HWVI6MI.js} +0 -0
@@ -1,9 +1,9 @@
1
1
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
2
  import {
3
3
  createSubsystemLogger
4
- } from "./chunk-XTMJ7XDT.js";
5
- import "./chunk-AOHZU2BM.js";
6
- import "./chunk-N2H3ZPZY.js";
4
+ } from "./chunk-POMLXPMY.js";
5
+ import "./chunk-YNE3RP52.js";
6
+ import "./chunk-3RKP3FMS.js";
7
7
  import "./chunk-NXL4FFQQ.js";
8
8
 
9
9
  // dist/clients/metrics-client.js
@@ -1,7 +1,7 @@
1
1
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
2
  import {
3
3
  getWordIndexMaxFilesDerived
4
- } from "./chunk-GAEGEPOC.js";
4
+ } from "./chunk-7HXE67BN.js";
5
5
  import {
6
6
  PathKeyedMap
7
7
  } from "./chunk-Z3CCVDUB.js";
@@ -15,27 +15,27 @@ import {
15
15
  } from "./chunk-QYF5S6P6.js";
16
16
  import {
17
17
  KIND_EXTENSIONS
18
- } from "./chunk-PHDMFAKM.js";
18
+ } from "./chunk-DUO2TU77.js";
19
19
  import {
20
20
  isTestFileName
21
- } from "./chunk-2BBLFX75.js";
21
+ } from "./chunk-J6MPGQTS.js";
22
22
  import {
23
23
  incrementDegradationCount
24
- } from "./chunk-JAU72XNY.js";
24
+ } from "./chunk-J64T55EI.js";
25
25
  import {
26
26
  logLatency
27
- } from "./chunk-QQNQ7SJW.js";
27
+ } from "./chunk-QBFBRV2B.js";
28
28
  import {
29
29
  createNdjsonLogger,
30
30
  getGlobalPiLensLogDir,
31
31
  getMaxLogSizeMB,
32
32
  isTestMode
33
- } from "./chunk-AOHZU2BM.js";
33
+ } from "./chunk-YNE3RP52.js";
34
34
  import {
35
35
  isAtOrAboveHomeDir,
36
36
  normalizeEphemeralMapKey,
37
37
  normalizeFilePath
38
- } from "./chunk-N2H3ZPZY.js";
38
+ } from "./chunk-3RKP3FMS.js";
39
39
 
40
40
  // dist/clients/word-index.js
41
41
  import * as fs from "node:fs";
@@ -861,7 +861,7 @@ async function updateWordIndexDocumentAsyncUnsafe(index, doc, shouldContinue) {
861
861
  }
862
862
  var WORD_INDEX_MAX_BYTES = 512 * 1024;
863
863
  async function collectWordIndexDocs(root, shouldContinue = () => true, preflightFiles) {
864
- const { collectSourceFilesAsync } = await import("./chunk-4W4VLAPJ.js");
864
+ const { collectSourceFilesAsync } = await import("./chunk-IQWN5JCP.js");
865
865
  const maxFiles = preflightFiles?.length ?? getWordIndexMaxFilesDerived(root);
866
866
  const files = preflightFiles ? preflightFiles.map((file) => file.path) : await collectSourceFilesAsync(root, {
867
867
  maxFiles,
@@ -910,7 +910,7 @@ async function refreshWordIndexIncrementally(index, root, shouldContinue = () =>
910
910
  timings: { sourceWalkMs: 0, statWalkMs: 0, refreshReadsMs: 0 }
911
911
  };
912
912
  }
913
- const { collectSourceFilesAsync } = await import("./chunk-4W4VLAPJ.js");
913
+ const { collectSourceFilesAsync } = await import("./chunk-IQWN5JCP.js");
914
914
  const maxFiles = getWordIndexMaxFilesDerived(root);
915
915
  const sourceWalkStartMs = Date.now();
916
916
  const walked = await collectSourceFilesAsync(root, {
@@ -1713,6 +1713,13 @@ function deserializeWordIndex(data) {
1713
1713
  }
1714
1714
  return index;
1715
1715
  }
1716
+ var wordIndexPublishedListener;
1717
+ function setWordIndexPublishedListener(listener) {
1718
+ wordIndexPublishedListener = listener;
1719
+ }
1720
+ function notifyWordIndexPublished(cwd, index, trigger) {
1721
+ wordIndexPublishedListener?.(cwd, index, trigger);
1722
+ }
1716
1723
  var buildStatuses = /* @__PURE__ */ new Map();
1717
1724
  function getWordIndexBuildStatus(cwd) {
1718
1725
  return buildStatuses.get(path2.resolve(cwd));
@@ -1743,7 +1750,7 @@ function triggerBackgroundWordIndexBuild(cwd, dbg, options = {}) {
1743
1750
  void (async () => {
1744
1751
  const startMs = Date.now();
1745
1752
  try {
1746
- const { loadProjectSnapshot, saveProjectSnapshot, PROJECT_SNAPSHOT_VERSION } = await import("./chunk-UXGETRP4.js");
1753
+ const { loadProjectSnapshot, saveProjectSnapshot, PROJECT_SNAPSHOT_VERSION } = await import("./chunk-XYURS6EN.js");
1747
1754
  const docs = await collectWordIndexDocs(key);
1748
1755
  const index = await buildWordIndexAsync(docs);
1749
1756
  const existing = loadProjectSnapshot(key);
@@ -1760,6 +1767,7 @@ function triggerBackgroundWordIndexBuild(cwd, dbg, options = {}) {
1760
1767
  snapshot.generatedAt = (/* @__PURE__ */ new Date()).toISOString();
1761
1768
  snapshot.wordIndex = serializeWordIndex(index);
1762
1769
  saveProjectSnapshot(key, snapshot);
1770
+ notifyWordIndexPublished(key, index, "cold_query");
1763
1771
  dbg?.(`word-index cold-build: ${index.docCount} files, ${index.postings.size} tokens (${Date.now() - startMs}ms)`);
1764
1772
  logWordIndex({
1765
1773
  phase: "cold_build",
@@ -1809,7 +1817,7 @@ function getWordIndexPersistScheduler() {
1809
1817
  async function writeWordIndexSnapshot(cwd, index, dbg) {
1810
1818
  const persistStartedAt = Date.now();
1811
1819
  try {
1812
- const { loadProjectSnapshot, saveProjectSnapshot, PROJECT_SNAPSHOT_VERSION } = await import("./chunk-UXGETRP4.js");
1820
+ const { loadProjectSnapshot, saveProjectSnapshot, PROJECT_SNAPSHOT_VERSION } = await import("./chunk-XYURS6EN.js");
1813
1821
  const existing = loadProjectSnapshot(cwd);
1814
1822
  const snapshot = existing ?? {
1815
1823
  version: PROJECT_SNAPSHOT_VERSION,
@@ -1899,6 +1907,8 @@ export {
1899
1907
  getLastWordIndexSerializeWork,
1900
1908
  serializeWordIndex,
1901
1909
  deserializeWordIndex,
1910
+ setWordIndexPublishedListener,
1911
+ notifyWordIndexPublished,
1902
1912
  getWordIndexBuildStatus,
1903
1913
  _resetWordIndexBuildGuardForTests,
1904
1914
  triggerBackgroundWordIndexBuild,
@@ -1,21 +1,21 @@
1
1
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
2
  import {
3
3
  freshnessFromMtime,
4
+ hashFileBytes,
4
5
  resolveRunnerPath
5
- } from "./chunk-L3BLXZG7.js";
6
+ } from "./chunk-AGSS7277.js";
6
7
  import {
7
8
  translateGuidanceToolNames
8
- } from "./chunk-JAU72XNY.js";
9
+ } from "./chunk-J64T55EI.js";
9
10
  import {
10
11
  logLatency
11
- } from "./chunk-QQNQ7SJW.js";
12
+ } from "./chunk-QBFBRV2B.js";
12
13
  import {
13
14
  normalizeMapKey,
14
15
  toProjectRelativePath
15
- } from "./chunk-N2H3ZPZY.js";
16
+ } from "./chunk-3RKP3FMS.js";
16
17
 
17
18
  // dist/clients/advisory-provenance.js
18
- import { createHash } from "node:crypto";
19
19
  import * as fs from "node:fs";
20
20
  import * as path from "node:path";
21
21
  var MAX_ADVISORY_AFFECTED_FILES = 256;
@@ -24,7 +24,7 @@ function advisoryPathKey(filePath, cwd) {
24
24
  }
25
25
  function advisoryFileHash(filePath) {
26
26
  try {
27
- return createHash("sha256").update(fs.readFileSync(filePath)).digest("hex");
27
+ return hashFileBytes(filePath);
28
28
  } catch (error) {
29
29
  const code = error.code ?? "unknown";
30
30
  return code === "ENOENT" ? "missing" : `unreadable:${code}`;
@@ -139,7 +139,7 @@ function validateAdvisoryProvenance(record, cwd, runtime) {
139
139
  changedPaths.add(advisoryPathKey(resolved, cwd));
140
140
  continue;
141
141
  }
142
- if (stat.mtimeMs !== captured.mtimeMs || stat.size !== captured.size) {
142
+ if (stat.size !== captured.size) {
143
143
  reasons.push(`metadata-changed:${advisoryPathKey(resolved, cwd)}`);
144
144
  }
145
145
  const currentHash = advisoryFileHash(resolved);
@@ -205,7 +205,7 @@ function createPathFactsMemo(args) {
205
205
  };
206
206
  }
207
207
  function partitionOneSource(args, memo) {
208
- const scannedAtMs = parseScannedAtMs(args.scannedAt);
208
+ const storeScannedAtMs = parseScannedAtMs(args.scannedAt);
209
209
  const onMissing = args.onMissing ?? "drop";
210
210
  const rawVerdicts = /* @__PURE__ */ new Map();
211
211
  const live = [];
@@ -221,7 +221,9 @@ function partitionOneSource(args, memo) {
221
221
  live.push(finding);
222
222
  continue;
223
223
  }
224
- let verdict = rawVerdicts.get(cited);
224
+ const scannedAtMs = args.scannedAtOf ? parseScannedAtMs(args.scannedAtOf(finding)) : storeScannedAtMs;
225
+ const verdictKey = args.scannedAtOf ? `${cited}\0${scannedAtMs}` : cited;
226
+ let verdict = rawVerdicts.get(verdictKey);
225
227
  if (verdict === void 0) {
226
228
  const resolved = resolveRunnerPath(args.cwd, cited);
227
229
  let facts = memo.facts.get(resolved);
@@ -245,7 +247,7 @@ function partitionOneSource(args, memo) {
245
247
  else if (verdict === "stale")
246
248
  stalePaths.push(resolved);
247
249
  }
248
- rawVerdicts.set(cited, verdict);
250
+ rawVerdicts.set(verdictKey, verdict);
249
251
  }
250
252
  if (verdict === "missing")
251
253
  dropped.push(finding);
@@ -288,7 +290,9 @@ function gateFindingsByPathFreshness(args) {
288
290
  dropped[name] = partition.dropped.length;
289
291
  if (partition.stale.length > 0)
290
292
  demoted[name] = partition.stale.length;
291
- if (source.scannedAt !== void 0) {
293
+ if (source.scannedAtOf !== void 0) {
294
+ scannedAtByStore[name] = "per-finding";
295
+ } else if (source.scannedAt !== void 0) {
292
296
  scannedAtByStore[name] = String(source.scannedAt);
293
297
  }
294
298
  merged.live.push(...partition.live);
@@ -1,14 +1,14 @@
1
1
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
2
  import {
3
3
  isExcludedDirName
4
- } from "./chunk-C34ZXZ7O.js";
4
+ } from "./chunk-KUA4OJ5Y.js";
5
5
  import {
6
6
  createDeadline,
7
7
  yieldIfOverBudget
8
8
  } from "./chunk-QYF5S6P6.js";
9
9
  import {
10
10
  isGeneratedArtifactDirectoryName
11
- } from "./chunk-2BBLFX75.js";
11
+ } from "./chunk-J6MPGQTS.js";
12
12
 
13
13
  // dist/clients/source-walker.js
14
14
  import * as fs from "node:fs";
@@ -41,6 +41,30 @@ function shouldRecurseIntoDir(entry, fullPath, policy, onGeneratedDirSkip) {
41
41
  return false;
42
42
  return true;
43
43
  }
44
+ var DirectoryListing = class {
45
+ #entries;
46
+ #foldedNames;
47
+ constructor(entries) {
48
+ this.#entries = entries;
49
+ }
50
+ /**
51
+ * False only when no entry of this directory could be the file at `target`
52
+ * on any filesystem, so a caller may treat it as absent without asking the
53
+ * filesystem. True means "possibly present": the caller still asks, which
54
+ * keeps the exact verdict for a directory-named twin, a broken symlink or a
55
+ * name that matches only case-sensitively. Names are case-folded and
56
+ * NFC-normalized so a case-insensitive filesystem (Windows, default macOS)
57
+ * or a normalization-insensitive one (macOS) never reports a real entry
58
+ * absent. `target` must lie directly in the listed directory.
59
+ */
60
+ mayContain(target) {
61
+ this.#foldedNames ??= new Set(this.#entries.map((entry) => foldEntryName(entry.name)));
62
+ return this.#foldedNames.has(foldEntryName(path.basename(target)));
63
+ }
64
+ };
65
+ function foldEntryName(name) {
66
+ return name.normalize("NFC").toLowerCase();
67
+ }
44
68
  function* walkTreeStackSteps(rootDir, visit, shouldStop) {
45
69
  const stack = [rootDir];
46
70
  while (stack.length > 0) {
@@ -50,10 +74,11 @@ function* walkTreeStackSteps(rootDir, visit, shouldStop) {
50
74
  if (dir === void 0)
51
75
  continue;
52
76
  const entries = (yield dir) ?? [];
77
+ const listing = new DirectoryListing(entries);
53
78
  const subDirs = [];
54
79
  for (const entry of entries) {
55
80
  const fullPath = path.join(dir, entry.name);
56
- const disposition = visit(entry, fullPath);
81
+ const disposition = visit(entry, fullPath, listing);
57
82
  if (disposition === "stop")
58
83
  return true;
59
84
  if (disposition === "recurse")
@@ -92,9 +117,11 @@ async function walkTreeStackAsync(rootDir, visit, opts) {
92
117
  }
93
118
  function walkTreeRecursiveSync(rootDir, visit) {
94
119
  function scan(currentDir) {
95
- for (const entry of readDirEntriesSafe(currentDir)) {
120
+ const entries = readDirEntriesSafe(currentDir);
121
+ const listing = new DirectoryListing(entries);
122
+ for (const entry of entries) {
96
123
  const fullPath = path.join(currentDir, entry.name);
97
- const disposition = visit(entry, fullPath);
124
+ const disposition = visit(entry, fullPath, listing);
98
125
  if (disposition === "stop")
99
126
  return true;
100
127
  if (disposition === "recurse" && scan(fullPath))