pi-lens 4.4.0 → 4.4.2

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 (150) hide show
  1. package/CHANGELOG.md +158 -0
  2. package/README.md +1 -0
  3. package/config/dependency-cruiser-eager-allowlist.json +3 -1
  4. package/dist/{chunk-IXEBJSUE.js → chunk-23WOO7TX.js} +11 -10
  5. package/dist/{chunk-E2WFBRHM.js → chunk-2B73ZPOK.js} +3 -3
  6. package/dist/{chunk-DQ2NZ7C4.js → chunk-2BBLFX75.js} +139 -11
  7. package/dist/{chunk-ZMOL6RQC.js → chunk-2CD5RS6M.js} +2 -2
  8. package/dist/{chunk-VD4JQCQC.js → chunk-2ZGIZPDG.js} +38 -37
  9. package/dist/{chunk-DIULIWKU.js → chunk-34WSVGEM.js} +29 -3
  10. package/dist/chunk-3JNKMP3B.js +17 -0
  11. package/dist/{chunk-HVUZXSWK.js → chunk-3UUPUE3O.js} +45 -41
  12. package/dist/{chunk-5RXBNSW2.js → chunk-3VPVG5SU.js} +16 -8
  13. package/dist/chunk-3WA4HP4I.js +55 -0
  14. package/dist/chunk-466Z26MG.js +30 -0
  15. package/dist/{chunk-34U7PLGE.js → chunk-4UNXBNE2.js} +12 -11
  16. package/dist/{chunk-E4MAEDPM.js → chunk-4W4VLAPJ.js} +12 -11
  17. package/dist/{chunk-GUOGVTCQ.js → chunk-52FUZI6K.js} +4 -2
  18. package/dist/chunk-5JM2524W.js +84 -0
  19. package/dist/{chunk-VHQLPIRJ.js → chunk-5NGR67U5.js} +2 -2
  20. package/dist/{chunk-DFVMIKMQ.js → chunk-66SXUJCA.js} +45 -9
  21. package/dist/{chunk-OQGFCZJ4.js → chunk-66VMUL7Q.js} +16 -16
  22. package/dist/{chunk-HWBZIGMJ.js → chunk-67WMNWEE.js} +9 -8
  23. package/dist/{chunk-6QIUX4EP.js → chunk-6C7JWPCP.js} +7 -7
  24. package/dist/{chunk-47D2BO4S.js → chunk-6GFRNVYR.js} +11 -10
  25. package/dist/{chunk-ZAWW3AF6.js → chunk-6SDEP3AJ.js} +9 -8
  26. package/dist/{chunk-TGPXLY2J.js → chunk-724UL2TJ.js} +38 -2
  27. package/dist/{chunk-YLF3PPT5.js → chunk-76MWC2K6.js} +3 -3
  28. package/dist/{chunk-F3P7VQ7H.js → chunk-7DYBLXYA.js} +3 -3
  29. package/dist/{chunk-AJ3QXTRQ.js → chunk-7ULNCDCO.js} +37 -35
  30. package/dist/{chunk-UK3CMEAL.js → chunk-AOHZU2BM.js} +1 -1
  31. package/dist/{chunk-F3AU2TMM.js → chunk-ARJ6PLFH.js} +44 -28
  32. package/dist/{chunk-U3VGR4VU.js → chunk-AW7R2GWG.js} +65 -8
  33. package/dist/{chunk-FJNKANOS.js → chunk-BKE7E7TC.js} +10 -9
  34. package/dist/{chunk-4W2PLC4J.js → chunk-BRGDSBJE.js} +13 -12
  35. package/dist/{chunk-VN2AXLEX.js → chunk-C34ZXZ7O.js} +102 -16
  36. package/dist/{chunk-T4ERT73Y.js → chunk-CF24OIYI.js} +21 -20
  37. package/dist/{chunk-NMABWBZN.js → chunk-CHOW74WE.js} +1 -1
  38. package/dist/{chunk-ARFCXG7S.js → chunk-CO2G5PHH.js} +2 -2
  39. package/dist/{chunk-AS44CFQ4.js → chunk-EETY4LF7.js} +24 -10
  40. package/dist/{chunk-I3R7GKDR.js → chunk-EJIUXPBX.js} +1 -1
  41. package/dist/{chunk-B3I2UUT4.js → chunk-ESBQHFMJ.js} +653 -251
  42. package/dist/{chunk-O5WILGMI.js → chunk-FLKWCIZH.js} +8 -5
  43. package/dist/{chunk-LNAKEO63.js → chunk-GAEGEPOC.js} +4 -4
  44. package/dist/{chunk-V7R425XG.js → chunk-GBPP5P7X.js} +4 -4
  45. package/dist/{chunk-XK4CIILI.js → chunk-GHB6AYCP.js} +27 -26
  46. package/dist/{chunk-7FBF7NPR.js → chunk-GOYRYZ2R.js} +29 -26
  47. package/dist/{chunk-JOLITGCC.js → chunk-GXW6K4YJ.js} +1 -1
  48. package/dist/{chunk-NXPS2L6U.js → chunk-GZYBXISR.js} +9 -9
  49. package/dist/{chunk-OAXNDZJ7.js → chunk-IBGIOBEE.js} +8 -4
  50. package/dist/{chunk-IIXOAGHR.js → chunk-IBRALRBR.js} +19 -15
  51. package/dist/{chunk-7F72PXY2.js → chunk-IXXDFY73.js} +3 -3
  52. package/dist/chunk-IZSHAIJ7.js +26 -0
  53. package/dist/{chunk-N3YQJI6O.js → chunk-JAU72XNY.js} +20 -15
  54. package/dist/chunk-JMYYOTQG.js +77 -0
  55. package/dist/{chunk-NL5GLMFI.js → chunk-JSQFTSC6.js} +12 -11
  56. package/dist/{chunk-IVP5KAB7.js → chunk-JWA5I3SR.js} +35 -21
  57. package/dist/{chunk-Y4T27COV.js → chunk-KBG47GHP.js} +4 -4
  58. package/dist/{chunk-PWFHBTSF.js → chunk-L3BLXZG7.js} +2 -2
  59. package/dist/chunk-LOP6GRPH.js +31 -0
  60. package/dist/chunk-MQLUAEOC.js +162 -0
  61. package/dist/{chunk-RPD5K5NC.js → chunk-MRXKYPCS.js} +2 -2
  62. package/dist/{chunk-Q5U6FMDI.js → chunk-N2H3ZPZY.js} +59 -7
  63. package/dist/{chunk-27BBHSZC.js → chunk-N3X6Q4YT.js} +3 -3
  64. package/dist/{chunk-UY752QSN.js → chunk-NW7BOILN.js} +19 -17
  65. package/dist/{chunk-TLGFBM3F.js → chunk-ODBNXWT4.js} +302 -113
  66. package/dist/{chunk-TT22N74J.js → chunk-OG5UUPN7.js} +13 -13
  67. package/dist/{chunk-C2AUYXQH.js → chunk-OXXKQCSV.js} +6 -6
  68. package/dist/{chunk-PAK6IDME.js → chunk-P7266HLM.js} +2 -2
  69. package/dist/{chunk-52H5B5TC.js → chunk-PFNSLWU5.js} +31 -30
  70. package/dist/{chunk-5ZEJHV35.js → chunk-PHDMFAKM.js} +12 -2
  71. package/dist/{chunk-3I5QFT3N.js → chunk-PNQDPDRC.js} +6 -3
  72. package/dist/{chunk-TVN5N6GY.js → chunk-PV6LR5P5.js} +6057 -1311
  73. package/dist/{chunk-AHDW3GPE.js → chunk-Q6ZWTCLY.js} +24 -22
  74. package/dist/{chunk-RUOG5D2C.js → chunk-Q7DUK4LX.js} +5 -5
  75. package/dist/{chunk-YSNFMJKN.js → chunk-QGQ5CCCG.js} +2 -2
  76. package/dist/{chunk-LW6XKDZQ.js → chunk-QHJ6DQ2R.js} +287 -38
  77. package/dist/{chunk-GVXA6CCE.js → chunk-QILIYSJF.js} +1 -1
  78. package/dist/{chunk-22AVIGXS.js → chunk-QOBUHWHX.js} +12 -7
  79. package/dist/{chunk-5THXD6X5.js → chunk-QQNQ7SJW.js} +2 -75
  80. package/dist/{chunk-4RERDEXR.js → chunk-QZVW3AAK.js} +50 -41
  81. package/dist/{chunk-AWE5TMPO.js → chunk-R3PCY3YS.js} +14 -13
  82. package/dist/{chunk-HHYAELLJ.js → chunk-RPLINGVB.js} +7 -7
  83. package/dist/{chunk-EOKRQ3SA.js → chunk-RWWYJ623.js} +10 -1
  84. package/dist/{chunk-YGVYKNSN.js → chunk-SAEFPSB6.js} +59 -12
  85. package/dist/{chunk-K6VYLB2U.js → chunk-TYM6OUIT.js} +13 -12
  86. package/dist/{chunk-AJXODZY6.js → chunk-UXGETRP4.js} +13 -12
  87. package/dist/{chunk-I7SPGOWI.js → chunk-UZ3TUTRV.js} +1 -1
  88. package/dist/{chunk-TO6WL3GM.js → chunk-VCHXCQFG.js} +12 -2
  89. package/dist/{chunk-BRJKOBZP.js → chunk-VITUCP5A.js} +17 -6
  90. package/dist/{chunk-WONERJTP.js → chunk-VRZP76YM.js} +1 -1
  91. package/dist/{chunk-OS32RNR2.js → chunk-VSSBEUFT.js} +16 -15
  92. package/dist/{chunk-VDBPVIKD.js → chunk-VWCXW2E7.js} +1 -1
  93. package/dist/{chunk-NKOUFPQF.js → chunk-WOA4LN2R.js} +2 -2
  94. package/dist/{chunk-RIISMHKN.js → chunk-WOKKRCJ5.js} +15 -13
  95. package/dist/{chunk-XECLWQZN.js → chunk-WU567RSC.js} +13 -10
  96. package/dist/{chunk-V5PXFSZK.js → chunk-XCJN5IJU.js} +4 -4
  97. package/dist/{chunk-ORTMY4Q4.js → chunk-XKIZ2HNN.js} +22 -14
  98. package/dist/{chunk-6VCD4VKZ.js → chunk-XMSXXAHW.js} +2 -2
  99. package/dist/chunk-XPZPXCBV.js +28 -0
  100. package/dist/{chunk-O6TQT6RI.js → chunk-XTMJ7XDT.js} +1 -1
  101. package/dist/{chunk-VNMT7C64.js → chunk-XVF2VVXP.js} +5 -3
  102. package/dist/chunk-Z527N6YQ.js +28 -0
  103. package/dist/{chunk-NTUJEGDZ.js → chunk-ZANLILYD.js} +22 -2
  104. package/dist/{chunk-6P62QVEA.js → chunk-ZGDGW2V3.js} +2 -2
  105. package/dist/{chunk-ODEEOXPH.js → chunk-ZHFZWTZV.js} +12 -11
  106. package/dist/{chunk-AJVLWNFN.js → chunk-ZXYDQ3TZ.js} +5 -5
  107. package/dist/index.js +102097 -97778
  108. package/dist/mcp/analyze-cli.js +18 -8
  109. package/dist/mcp/cli.js +48 -37
  110. package/dist/mcp/server.js +695 -426
  111. package/dist/mcp/worker.js +66 -55
  112. package/dist/probes/installer.js +20 -19
  113. package/docs/ast-grep_rules_catalog.md +2 -2
  114. package/docs/astplayground.md +1 -1
  115. package/docs/configuration.md +38 -2
  116. package/docs/custom-rules.md +25 -2
  117. package/docs/dependencies.md +3 -0
  118. package/docs/environment-variables.md +34 -6
  119. package/docs/features.md +39 -3
  120. package/docs/globalconfig.md +3 -1
  121. package/docs/language-coverage.md +2 -1
  122. package/docs/lsp-capability-matrix.md +29 -6
  123. package/docs/lsp-idle-eviction.md +38 -47
  124. package/docs/pi-lens-fixer.md +13 -5
  125. package/docs/pi-lens-merge-policy.md +48 -1
  126. package/docs/pi-lens-monitor.md +3 -1
  127. package/docs/pi-lens-retro.md +2 -1
  128. package/docs/pi-lens-reviewer.md +19 -3
  129. package/docs/pi-lens-subagent.md +2 -0
  130. package/docs/real-harness.md +17 -0
  131. package/docs/schema/pi-lens-config-v1.json +565 -0
  132. package/docs/servercapabilities.md +18 -4
  133. package/docs/settings.md +2 -1
  134. package/docs/word-index.md +6 -0
  135. package/package.json +1 -1
  136. package/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  137. package/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  138. package/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  139. package/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  140. package/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  141. package/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  142. package/dist/chunk-54B42YRX.js +0 -27
  143. package/dist/chunk-CGGAIP7J.js +0 -29
  144. package/dist/chunk-D6QS3U6M.js +0 -30
  145. package/dist/chunk-HMFXILPV.js +0 -83
  146. package/dist/chunk-LSMJ6KBE.js +0 -4139
  147. package/dist/chunk-NTHH2J74.js +0 -27
  148. package/dist/chunk-RPD4ZSB7.js +0 -25
  149. package/dist/chunk-T6D4Q4AQ.js +0 -54
  150. package/dist/chunk-VCSRSYLF.js +0 -81
@@ -2,17 +2,17 @@ import { createRequire as __pilensCreateRequire } from "node:module"; const requ
2
2
  import {
3
3
  getLazyInstallAttempt,
4
4
  tryLazyInstall
5
- } from "./chunk-C2AUYXQH.js";
5
+ } from "./chunk-OXXKQCSV.js";
6
6
  import {
7
7
  stripAnsi
8
8
  } from "./chunk-FC4RP4I2.js";
9
9
  import {
10
10
  isTrivyEnabled,
11
11
  resolveSeverityFloor
12
- } from "./chunk-DFVMIKMQ.js";
12
+ } from "./chunk-66SXUJCA.js";
13
13
  import {
14
14
  rustClient
15
- } from "./chunk-RPD5K5NC.js";
15
+ } from "./chunk-MRXKYPCS.js";
16
16
  import {
17
17
  findingsResult,
18
18
  finishParsedRun,
@@ -22,10 +22,10 @@ import {
22
22
  isRunnerSkipReason,
23
23
  parseToolRun,
24
24
  skipUnlessToolRan
25
- } from "./chunk-ZMOL6RQC.js";
25
+ } from "./chunk-2CD5RS6M.js";
26
26
  import {
27
27
  spawnFailedWithNoOutput
28
- } from "./chunk-I7SPGOWI.js";
28
+ } from "./chunk-UZ3TUTRV.js";
29
29
  import {
30
30
  coveringLaneAvailable,
31
31
  createAvailabilityChecker,
@@ -40,7 +40,7 @@ import {
40
40
  resolveToolCommand,
41
41
  resolveToolCommandWithInstallFallback,
42
42
  resolveVendorToolCommand
43
- } from "./chunk-7FBF7NPR.js";
43
+ } from "./chunk-GOYRYZ2R.js";
44
44
  import {
45
45
  biomeConfigArgs,
46
46
  findCompiledClassesDir,
@@ -59,41 +59,39 @@ import {
59
59
  hasYamllintConfig,
60
60
  markdownlintConfigArgs,
61
61
  ruffConfigArgs
62
- } from "./chunk-HHYAELLJ.js";
62
+ } from "./chunk-RPLINGVB.js";
63
63
  import {
64
64
  importFactProvider
65
- } from "./chunk-V5PXFSZK.js";
65
+ } from "./chunk-XCJN5IJU.js";
66
66
  import {
67
67
  functionFactProvider
68
- } from "./chunk-YLF3PPT5.js";
68
+ } from "./chunk-76MWC2K6.js";
69
69
  import {
70
70
  extractFactsFromTree
71
- } from "./chunk-JOLITGCC.js";
71
+ } from "./chunk-GXW6K4YJ.js";
72
72
  import {
73
73
  computeImpactCascade
74
- } from "./chunk-E2WFBRHM.js";
74
+ } from "./chunk-2B73ZPOK.js";
75
75
  import {
76
76
  goClient
77
- } from "./chunk-6VCD4VKZ.js";
78
- import {
79
- buildOrUpdateGraph,
80
- clearReviewGraphWorkspaceCache,
81
- fileContentProvider
82
- } from "./chunk-LSMJ6KBE.js";
77
+ } from "./chunk-XMSXXAHW.js";
83
78
  import {
84
79
  MAX_BLOCKING_RULE_COMPLEXITY,
85
80
  RUNTIME_CONFIG,
86
81
  applyDispositions,
87
82
  applyDispositionsMultiFile,
88
83
  buildEffectiveAstGrepCatalog,
84
+ buildOrUpdateGraph,
89
85
  calculateRuleComplexity,
90
86
  classifyDefect,
91
87
  classifyDiagnostic,
92
88
  classifyDiagnosticTier,
93
89
  clearPendingAuxiliaryCoverage,
90
+ clearReviewGraphWorkspaceCache,
94
91
  enabledAuxiliaryLspServerIds,
95
92
  exceedsLspSyncLimits,
96
93
  fetchTsserverProjectIdentity,
94
+ fileContentProvider,
97
95
  getLSPService,
98
96
  getRunnerTimeoutFloorMs,
99
97
  hasAnyDispositionMarks,
@@ -109,7 +107,7 @@ import {
109
107
  touchCoverageGap,
110
108
  tryWarmAttachedCodeActions,
111
109
  tryWarmAttachedDiagnostics
112
- } from "./chunk-TVN5N6GY.js";
110
+ } from "./chunk-PV6LR5P5.js";
113
111
  import {
114
112
  mapWithConcurrency
115
113
  } from "./chunk-XIMYZ3JA.js";
@@ -117,14 +115,20 @@ import {
117
115
  LSP_SERVERS,
118
116
  getAstGrepRuleSources,
119
117
  registerRunnerId
120
- } from "./chunk-B3I2UUT4.js";
118
+ } from "./chunk-ESBQHFMJ.js";
121
119
  import {
122
120
  augmentPythonEnvironment,
123
121
  detectPythonEnvironment
124
- } from "./chunk-DIULIWKU.js";
122
+ } from "./chunk-34WSVGEM.js";
123
+ import {
124
+ loadProjectSnapshot,
125
+ readJsonCache,
126
+ readJsonCacheAsync
127
+ } from "./chunk-ZXYDQ3TZ.js";
125
128
  import {
126
129
  BUNDLED_QUERIES_ROOT,
127
130
  classifyTreeSitterWasmError,
131
+ computeRuleFilesFingerprint,
128
132
  firstChildOfType,
129
133
  getBundledQueriesRootHealth,
130
134
  getSharedTreeSitterClient,
@@ -140,16 +144,11 @@ import {
140
144
  ruleFilesForLanguage,
141
145
  ruleSourceLanguages,
142
146
  walk
143
- } from "./chunk-LW6XKDZQ.js";
147
+ } from "./chunk-QHJ6DQ2R.js";
144
148
  import {
145
149
  buildModuleGraph,
146
150
  clearModuleGraphCache
147
- } from "./chunk-V7R425XG.js";
148
- import {
149
- loadProjectSnapshot,
150
- readJsonCache,
151
- readJsonCacheAsync
152
- } from "./chunk-AJVLWNFN.js";
151
+ } from "./chunk-GBPP5P7X.js";
153
152
  import {
154
153
  WORD_INDEX_MAX_BYTES,
155
154
  deserializeWordIndex,
@@ -157,7 +156,7 @@ import {
157
156
  removeWordIndexDocumentAsync,
158
157
  scheduleWordIndexPersist,
159
158
  updateWordIndexDocumentForEdit
160
- } from "./chunk-YGVYKNSN.js";
159
+ } from "./chunk-SAEFPSB6.js";
161
160
  import {
162
161
  PathKeyedMap
163
162
  } from "./chunk-Z3CCVDUB.js";
@@ -165,26 +164,24 @@ import {
165
164
  WARM_CODE_ACTION_LOOKUP_LIMIT,
166
165
  contentHash,
167
166
  resolveRunnerPath
168
- } from "./chunk-PWFHBTSF.js";
167
+ } from "./chunk-L3BLXZG7.js";
169
168
  import {
170
169
  isSpawnableCommand
171
- } from "./chunk-OQGFCZJ4.js";
170
+ } from "./chunk-66VMUL7Q.js";
172
171
  import {
173
172
  logRunnerAdvisoryOnce,
174
173
  resolveRunnerCwd,
175
174
  resolveRunnerCwdWithReason
176
- } from "./chunk-RUOG5D2C.js";
175
+ } from "./chunk-Q7DUK4LX.js";
177
176
  import {
178
- findLocalBinUpwards
179
- } from "./chunk-U3VGR4VU.js";
180
- import {
181
- getProjectTrustState,
182
- projectTrustDenialReason
183
- } from "./chunk-NTUJEGDZ.js";
177
+ findLocalBinUpwards,
178
+ localBinPath
179
+ } from "./chunk-AW7R2GWG.js";
184
180
  import {
185
181
  createAvailabilityProbeFlight,
182
+ getIsolatedNpxSpawnOptions,
186
183
  probeToolAsync
187
- } from "./chunk-EOKRQ3SA.js";
184
+ } from "./chunk-RWWYJ623.js";
188
185
  import {
189
186
  classifyProbeFailure,
190
187
  createAvailabilityLatch,
@@ -194,13 +191,14 @@ import {
194
191
  logAvailabilityDecision,
195
192
  startHostStallSampler,
196
193
  transientRetryDelayMs
197
- } from "./chunk-WONERJTP.js";
194
+ } from "./chunk-VRZP76YM.js";
198
195
  import {
199
196
  getLspCapableKinds,
200
197
  getPrimaryDispatchGroup,
201
198
  resolveLanguageRootForFile
202
- } from "./chunk-22AVIGXS.js";
199
+ } from "./chunk-QOBUHWHX.js";
203
200
  import {
201
+ getGlobalPiLensDir,
204
202
  getProjectDataDir,
205
203
  isInSpawnTimeoutCooldown,
206
204
  isTestFile,
@@ -208,48 +206,53 @@ import {
208
206
  noteSpawnTimeout,
209
207
  resolvePiLensFlag,
210
208
  safeSpawnAsync
211
- } from "./chunk-VN2AXLEX.js";
209
+ } from "./chunk-C34ZXZ7O.js";
212
210
  import {
213
211
  detectFileKind
214
- } from "./chunk-5ZEJHV35.js";
212
+ } from "./chunk-PHDMFAKM.js";
215
213
  import {
216
214
  classifyGeneratedOrArtifactDetailed,
217
215
  detectFileRole,
218
- loadPiLensProjectConfig
219
- } from "./chunk-DQ2NZ7C4.js";
216
+ loadPiLensProjectConfig,
217
+ resolvePiLensConfig
218
+ } from "./chunk-2BBLFX75.js";
220
219
  import {
221
220
  findNearestDirWithAnyBasename,
222
- findNearestDirWithMarker
223
- } from "./chunk-VNMT7C64.js";
221
+ findNearestDirWithMarker,
222
+ getPiLensGlobalConfigPath
223
+ } from "./chunk-XVF2VVXP.js";
224
+ import {
225
+ getProjectTrustState,
226
+ projectTrustDenialReason
227
+ } from "./chunk-ZANLILYD.js";
224
228
  import {
225
229
  killedForOutputCap,
226
230
  truncatedByOutputCap,
227
231
  writeFileAtomic
228
232
  } from "./chunk-2EECBNC5.js";
229
233
  import {
230
- compareOrdinal,
231
234
  incrementDegradationCount,
232
235
  recordDegradationOnce
233
- } from "./chunk-N3YQJI6O.js";
236
+ } from "./chunk-JAU72XNY.js";
234
237
  import {
235
238
  createSubsystemLogger,
236
239
  logExtension
237
- } from "./chunk-O6TQT6RI.js";
240
+ } from "./chunk-XTMJ7XDT.js";
238
241
  import {
239
242
  logLatency,
240
243
  phaseFinished,
241
244
  phaseStarted
242
- } from "./chunk-5THXD6X5.js";
245
+ } from "./chunk-QQNQ7SJW.js";
243
246
  import {
244
247
  createNdjsonLogger,
245
248
  getGlobalPiLensLogDir,
246
249
  getMaxLogSizeMB,
247
250
  isTestMode
248
- } from "./chunk-UK3CMEAL.js";
251
+ } from "./chunk-AOHZU2BM.js";
249
252
  import {
250
253
  FactStore,
251
254
  setFactStoreEvictionReporter
252
- } from "./chunk-NMABWBZN.js";
255
+ } from "./chunk-CHOW74WE.js";
253
256
  import {
254
257
  BoundedLruCache
255
258
  } from "./chunk-ABBOA7UD.js";
@@ -263,7 +266,7 @@ import {
263
266
  normalizeMapKey,
264
267
  pathsEqual,
265
268
  toProjectRelativePath
266
- } from "./chunk-Q5U6FMDI.js";
269
+ } from "./chunk-N2H3ZPZY.js";
267
270
 
268
271
  // dist/clients/mcp/analyze.js
269
272
  import * as fs19 from "node:fs";
@@ -281,6 +284,8 @@ var DEFAULT_TURN_STATE = {
281
284
  };
282
285
  var MCP_TURN_STATE_OWNER_ID = `mcp-${process.pid}`;
283
286
  var TURN_OWNER_STALE_MS = 30 * 60 * 1e3;
287
+ var TURN_SESSION_PARTITION_TTL_MS = 30 * 60 * 1e3;
288
+ var MAX_TURN_SESSION_PARTITIONS = 16;
284
289
  function getLensDir(cwd) {
285
290
  return getProjectDataDir(cwd);
286
291
  }
@@ -521,7 +526,68 @@ var CacheManager = class {
521
526
  /**
522
527
  * Read turn state. Returns default if not found.
523
528
  */
524
- readTurnState(cwd) {
529
+ readTurnState(cwd, sessionId) {
530
+ return this.resolveTurnStatePartition(cwd, sessionId).state;
531
+ }
532
+ /**
533
+ * Reserve a secondary's durable partition at turn start. The legacy
534
+ * envelope may still be ownerless before the first write, so the write
535
+ * itself cannot infer that this session is concurrent from persisted state.
536
+ */
537
+ ensureSecondaryTurnStatePartition(cwd, sessionId) {
538
+ const envelope = this.readTurnStateEnvelope(cwd);
539
+ if (envelope.sessions?.[sessionId])
540
+ return;
541
+ const secondary = {
542
+ ...DEFAULT_TURN_STATE,
543
+ files: {},
544
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
545
+ sessionId,
546
+ owner: {
547
+ kind: "pi",
548
+ id: sessionId,
549
+ pid: process.pid,
550
+ lastSeen: (/* @__PURE__ */ new Date()).toISOString()
551
+ }
552
+ };
553
+ this.writeTurnState({
554
+ ...envelope,
555
+ sessions: { ...envelope.sessions, [sessionId]: secondary }
556
+ }, cwd);
557
+ }
558
+ /** Resolve the persisted primary/secondary turn-state partition. */
559
+ resolveTurnStatePartition(cwd, sessionId) {
560
+ const envelope = this.readTurnStateEnvelope(cwd);
561
+ const persistedSecondary = sessionId === void 0 ? void 0 : envelope.sessions?.[sessionId];
562
+ const primary2 = sessionId === void 0 || envelope.owner === void 0 && persistedSecondary === void 0 || envelope.owner?.kind === "pi" && envelope.owner.id === sessionId;
563
+ const isSecondary = !primary2 && (persistedSecondary !== void 0 || envelope.owner?.kind === "pi" && envelope.owner.pid === process.pid);
564
+ const state = primary2 ? envelope : persistedSecondary ?? {
565
+ ...DEFAULT_TURN_STATE,
566
+ files: {},
567
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
568
+ };
569
+ return {
570
+ envelope,
571
+ state,
572
+ isSecondary,
573
+ persist: (next) => {
574
+ if (isSecondary && sessionId !== void 0) {
575
+ this.writeTurnState({
576
+ ...envelope,
577
+ sessions: { ...envelope.sessions, [sessionId]: next }
578
+ }, cwd);
579
+ } else
580
+ this.writeTurnState(next, cwd);
581
+ },
582
+ remove: () => {
583
+ if (!isSecondary || sessionId === void 0)
584
+ return;
585
+ const { [sessionId]: _retired, ...remaining } = envelope.sessions ?? {};
586
+ this.writeTurnState({ ...envelope, sessions: remaining }, cwd);
587
+ }
588
+ };
589
+ }
590
+ readTurnStateEnvelope(cwd) {
525
591
  const statePath = getTurnStatePath(cwd);
526
592
  if (!fs.existsSync(statePath)) {
527
593
  return {
@@ -531,11 +597,26 @@ var CacheManager = class {
531
597
  };
532
598
  }
533
599
  const state = readJsonCache(statePath, (parsed) => parsed);
534
- return state ?? {
600
+ const envelope = state ?? {
535
601
  ...DEFAULT_TURN_STATE,
536
602
  files: {},
537
603
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
538
604
  };
605
+ if (!envelope.sessions)
606
+ return envelope;
607
+ const now = Date.now();
608
+ const liveEntries = Object.entries(envelope.sessions).filter(([, partition]) => {
609
+ const updated = Date.parse(partition.lastUpdated ?? "");
610
+ return Number.isFinite(updated) && now - updated <= TURN_SESSION_PARTITION_TTL_MS;
611
+ });
612
+ liveEntries.sort(([, left], [, right]) => Date.parse(right.lastUpdated) - Date.parse(left.lastUpdated));
613
+ const sessions = Object.fromEntries(liveEntries.slice(0, MAX_TURN_SESSION_PARTITIONS));
614
+ if (Object.keys(sessions).length !== Object.keys(envelope.sessions).length) {
615
+ const pruned = { ...envelope, sessions };
616
+ writeFileAtomic(statePath, JSON.stringify(pruned, null, 2));
617
+ return pruned;
618
+ }
619
+ return envelope;
539
620
  }
540
621
  /**
541
622
  * Write turn state.
@@ -602,13 +683,17 @@ var CacheManager = class {
602
683
  * current owner; callers must provide an explicit owner id to claim a stale
603
684
  * worklist.
604
685
  */
605
- addModifiedRange(filePath, range, importsChanged, cwd, sessionId, ownerKind = "pi", projectRoot) {
686
+ addModifiedRange(filePath, range, importsChanged, cwd, sessionId, ownerKind = "pi", projectRoot, analysisRootMode) {
687
+ if (analysisRootMode !== void 0 && analysisRootMode !== "session" && analysisRootMode !== "linked-worktree")
688
+ return this.readTurnState(cwd);
606
689
  const containmentRoot = projectRoot ?? cwd;
607
690
  if (!this.isTurnStatePathWithinRoot(filePath, containmentRoot)) {
608
691
  this.log(`turn-state: rejected out-of-project path ${filePath} (project root ${containmentRoot})`);
609
692
  return this.readTurnState(cwd);
610
693
  }
611
- const state = this.readTurnState(cwd);
694
+ const partition = this.resolveTurnStatePartition(cwd, sessionId !== null ? sessionId : void 0);
695
+ const useSessionPartition = partition.isSecondary && ownerKind === "pi" && sessionId != null;
696
+ const state = useSessionPartition ? partition.state : partition.envelope;
612
697
  if (sessionId) {
613
698
  const owner = {
614
699
  kind: ownerKind,
@@ -616,7 +701,7 @@ var CacheManager = class {
616
701
  pid: process.pid,
617
702
  lastSeen: (/* @__PURE__ */ new Date()).toISOString()
618
703
  };
619
- if (this.getTurnStateAccess(cwd, owner) === "foreign-live")
704
+ if (!useSessionPartition && this.getTurnStateAccess(cwd, owner) === "foreign-live")
620
705
  return state;
621
706
  state.sessionId = sessionId;
622
707
  state.owner = owner;
@@ -637,14 +722,23 @@ var CacheManager = class {
637
722
  lastEdit: (/* @__PURE__ */ new Date()).toISOString()
638
723
  };
639
724
  }
640
- this.writeTurnState(state, cwd);
725
+ if (useSessionPartition)
726
+ partition.persist(state);
727
+ else
728
+ this.writeTurnState(state, cwd);
641
729
  return state;
642
730
  }
643
731
  /**
644
732
  * Clear turn state (after turn_end processes it).
645
733
  */
646
- clearTurnState(cwd, owner) {
647
- const currentState = this.readTurnState(cwd);
734
+ clearTurnState(cwd, owner, sessionId) {
735
+ const partition = this.resolveTurnStatePartition(cwd, sessionId);
736
+ if (sessionId !== void 0 && partition.isSecondary) {
737
+ if (partition.envelope.sessions?.[sessionId])
738
+ partition.remove();
739
+ return true;
740
+ }
741
+ const currentState = partition.envelope;
648
742
  const isCurrentOwner = this.getTurnStateAccess(cwd, owner) !== "foreign-live";
649
743
  if (!isCurrentOwner && process.pid !== currentState.owner?.pid)
650
744
  return false;
@@ -668,35 +762,38 @@ var CacheManager = class {
668
762
  /**
669
763
  * Increment turn cycle counter.
670
764
  */
671
- incrementTurnCycle(cwd, owner) {
672
- const state = this.readTurnState(cwd);
765
+ incrementTurnCycle(cwd, owner, sessionId) {
766
+ const partition = this.resolveTurnStatePartition(cwd, sessionId);
767
+ const state = partition.state;
768
+ if (sessionId !== void 0 && state.owner?.id !== sessionId)
769
+ return state;
673
770
  const isCurrentOwner = this.getTurnStateAccess(cwd, owner) !== "foreign-live";
674
771
  if (!isCurrentOwner && process.pid !== state.owner?.pid)
675
772
  return state;
676
773
  state.turnCycles++;
677
- this.writeTurnState(state, cwd);
774
+ partition.persist(state);
678
775
  return state;
679
776
  }
680
777
  /**
681
778
  * Check if max cycles exceeded.
682
779
  */
683
- isMaxCyclesExceeded(cwd) {
684
- const state = this.readTurnState(cwd);
780
+ isMaxCyclesExceeded(cwd, sessionId) {
781
+ const state = this.readTurnState(cwd, sessionId);
685
782
  return state.turnCycles >= state.maxCycles;
686
783
  }
687
784
  /**
688
785
  * Get files that need jscpd re-scan (any edit).
689
786
  * Only returns source code files jscpd can meaningfully analyse.
690
787
  */
691
- getFilesForJscpd(cwd) {
692
- const state = this.readTurnState(cwd);
788
+ getFilesForJscpd(cwd, sessionId) {
789
+ const state = this.readTurnState(cwd, sessionId);
693
790
  return Object.keys(state.files).filter((f) => /\.(ts|tsx|js|jsx|mjs|cjs|py|go|rs|rb|java|cs|php|cpp|c|h|hpp|swift|kt)$/.test(f));
694
791
  }
695
792
  /**
696
793
  * Get files that need madge re-scan (imports changed).
697
794
  */
698
- getFilesForMadge(cwd) {
699
- const state = this.readTurnState(cwd);
795
+ getFilesForMadge(cwd, sessionId) {
796
+ const state = this.readTurnState(cwd, sessionId);
700
797
  return Object.entries(state.files).filter(([, f]) => f.importsChanged).map(([p]) => p);
701
798
  }
702
799
  // ---- Utilities ----
@@ -1084,6 +1181,11 @@ var LANGUAGE_CAPABILITY_MATRIX = {
1084
1181
  { mode: "fallback", runnerIds: ["detekt"], filterKinds: ["kotlin"] }
1085
1182
  ]
1086
1183
  },
1184
+ lean: {
1185
+ name: "Lean 4 Diagnostics",
1186
+ capabilities: ["types", "lint"],
1187
+ writeGroups: [primary("lean")]
1188
+ },
1087
1189
  swift: {
1088
1190
  name: "Swift Linting",
1089
1191
  capabilities: ["types", "lint"],
@@ -1158,6 +1260,7 @@ var LANGUAGE_CAPABILITY_MATRIX = {
1158
1260
  function toWritePlan(entry) {
1159
1261
  return {
1160
1262
  name: entry.name,
1263
+ capabilities: entry.capabilities,
1161
1264
  groups: [...entry.writeGroups]
1162
1265
  };
1163
1266
  }
@@ -1208,6 +1311,7 @@ function settledSnapshot(entry) {
1208
1311
  // #3758/#3813: carry the producer's handle so a requeued snapshot keeps
1209
1312
  // the scope that owned it, and the gate's peek can fence with it.
1210
1313
  session: entry.session,
1314
+ sessionId: entry.sessionId,
1211
1315
  result: entry.result
1212
1316
  };
1213
1317
  }
@@ -1218,12 +1322,17 @@ function ownedByLiveSession(entry, site) {
1218
1322
  return entry.session.guardedWrite(`${site}:${entry.runnerId}:${entry.filePath}`, () => true) !== void 0;
1219
1323
  }
1220
1324
  function deferRunnerFindings(entry) {
1221
- const { session, ...owned } = entry;
1325
+ const { session, sessionId, ...owned } = entry;
1222
1326
  if (session !== void 0 && session.guardedWrite(`${entry.runnerId}:${entry.filePath}`, () => true) === void 0) {
1223
1327
  void entry.promise.catch(() => void 0);
1224
1328
  return;
1225
1329
  }
1226
- const tracked = { ...owned, session, settled: false };
1330
+ const tracked = {
1331
+ ...owned,
1332
+ session,
1333
+ sessionId,
1334
+ settled: false
1335
+ };
1227
1336
  void tracked.promise.then((result) => {
1228
1337
  tracked.result = result;
1229
1338
  tracked.settled = true;
@@ -1244,6 +1353,7 @@ function requeueRunnerFindings(entry) {
1244
1353
  track({
1245
1354
  ...owned,
1246
1355
  session: entry.session,
1356
+ sessionId: entry.sessionId,
1247
1357
  promise: Promise.resolve(result),
1248
1358
  settled: true,
1249
1359
  result
@@ -1262,7 +1372,7 @@ function track(tracked) {
1262
1372
  }
1263
1373
  }
1264
1374
  }
1265
- async function drainPendingRunnerFindings(maxWaitMs = 2e3) {
1375
+ async function drainPendingRunnerFindings(maxWaitMs = 2e3, currentSession, currentSessionId) {
1266
1376
  if (pending.length === 0)
1267
1377
  return [];
1268
1378
  const current = pending.splice(0, pending.length);
@@ -1270,14 +1380,22 @@ async function drainPendingRunnerFindings(maxWaitMs = 2e3) {
1270
1380
  if (maxWaitMs === 0)
1271
1381
  await Promise.resolve();
1272
1382
  if (maxWaitMs > 0 && current.some((entry) => !entry.settled)) {
1273
- await new Promise((resolve45) => {
1274
- const timer = setTimeout(resolve45, maxWaitMs);
1383
+ await new Promise((resolve44) => {
1384
+ const timer = setTimeout(resolve44, maxWaitMs);
1275
1385
  timer.unref?.();
1276
1386
  });
1277
1387
  }
1278
1388
  for (const entry of current) {
1279
1389
  if (!ownedByLiveSession(entry, "turn-end"))
1280
1390
  continue;
1391
+ if (currentSessionId !== void 0 && entry.sessionId !== void 0 && entry.sessionId !== currentSessionId) {
1392
+ pending.push(entry);
1393
+ continue;
1394
+ }
1395
+ if (currentSession !== void 0 && entry.session !== void 0 && entry.session.generation !== currentSession.generation) {
1396
+ pending.push(entry);
1397
+ continue;
1398
+ }
1281
1399
  if (entry.settled && entry.result) {
1282
1400
  results.push(settledSnapshot(entry));
1283
1401
  } else {
@@ -1303,6 +1421,26 @@ function pendingRunnerFindingsSize() {
1303
1421
  }
1304
1422
 
1305
1423
  // dist/clients/dispatch/rule-policy.js
1424
+ function resolvedRulePolicyMap(root) {
1425
+ const resolved = resolvePiLensConfig({
1426
+ cwd: root,
1427
+ globalDir: getGlobalPiLensDir(),
1428
+ globalConfigPath: getPiLensGlobalConfigPath()
1429
+ });
1430
+ return rulePolicyMapFromConfig(resolved.value.rules);
1431
+ }
1432
+ function ruleIgnoredForPath(ruleId, filePath, root, policyMap) {
1433
+ const normalized = normalizeRuleId(ruleId);
1434
+ const patterns = [];
1435
+ for (const [configuredId, entry] of Object.entries(policyMap ?? {})) {
1436
+ if (normalizeRuleId(configuredId) !== normalized || !entry?.ignorePaths)
1437
+ continue;
1438
+ patterns.push(...entry.ignorePaths);
1439
+ }
1440
+ if (patterns.length === 0)
1441
+ return false;
1442
+ return isRuleIgnoredForPath(filePath, root, patterns);
1443
+ }
1306
1444
  function evaluateRulePolicy(ruleId, policyMap) {
1307
1445
  if (!policyMap)
1308
1446
  return { dropped: false };
@@ -1330,7 +1468,7 @@ function matchesRule(entry, raw, normalized) {
1330
1468
  return true;
1331
1469
  return normalizeRuleId(entry) === normalized;
1332
1470
  }
1333
- function applyRulePolicy(diagnostics, policyMap) {
1471
+ function applyRulePolicy(diagnostics, policyMap, options) {
1334
1472
  if (!policyMap)
1335
1473
  return diagnostics;
1336
1474
  let hasFilter = false;
@@ -1339,7 +1477,7 @@ function applyRulePolicy(diagnostics, policyMap) {
1339
1477
  continue;
1340
1478
  }
1341
1479
  const entry = rawEntry;
1342
- if ((entry.disable?.length ?? 0) > 0 || (entry.select?.length ?? 0) > 0) {
1480
+ if ((entry.disable?.length ?? 0) > 0 || (entry.select?.length ?? 0) > 0 || (entry.ignorePaths?.length ?? 0) > 0) {
1343
1481
  hasFilter = true;
1344
1482
  break;
1345
1483
  }
@@ -1350,6 +1488,9 @@ function applyRulePolicy(diagnostics, policyMap) {
1350
1488
  const ruleId = d.rule ?? d.code;
1351
1489
  if (!ruleId)
1352
1490
  return true;
1491
+ const filePath = d.filePath ?? options.filePath;
1492
+ if (filePath && ruleIgnoredForPath(ruleId, filePath, options.root, policyMap))
1493
+ return false;
1353
1494
  const { dropped } = evaluateRulePolicy(ruleId, policyMap);
1354
1495
  return !dropped;
1355
1496
  });
@@ -1365,12 +1506,14 @@ function rulePolicyMapFromConfig(rules2) {
1365
1506
  const entry = rawEntry;
1366
1507
  const hasDisable = (entry.disable?.length ?? 0) > 0;
1367
1508
  const hasSelect = (entry.select?.length ?? 0) > 0;
1368
- if (!hasDisable && !hasSelect)
1509
+ const hasIgnorePaths = (entry.ignorePaths?.length ?? 0) > 0;
1510
+ if (!hasDisable && !hasSelect && !hasIgnorePaths)
1369
1511
  continue;
1370
1512
  built ??= {};
1371
1513
  built[key2] = {
1372
1514
  ...hasDisable ? { disable: entry.disable } : {},
1373
- ...hasSelect ? { select: entry.select } : {}
1515
+ ...hasSelect ? { select: entry.select } : {},
1516
+ ...hasIgnorePaths ? { ignorePaths: entry.ignorePaths } : {}
1374
1517
  };
1375
1518
  }
1376
1519
  return built;
@@ -1610,7 +1753,7 @@ function readFilePrefix(filePath, maxBytes = 4096) {
1610
1753
  }
1611
1754
  }
1612
1755
  }
1613
- function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, modifiedRanges, projectRoot, writeIndex, telemetryModel, telemetryProvider, sessionGeneration) {
1756
+ function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, modifiedRanges, projectRoot, writeIndex, telemetryModel, telemetryProvider, sessionGeneration, sessionId, analysisRootMode) {
1614
1757
  const absoluteFilePath = resolveRunnerPath(cwd, filePath);
1615
1758
  const normalizedProjectRoot = normalizeMapKey(path4.resolve(projectRoot ?? cwd));
1616
1759
  const normalizedCwd = normalizeMapKey(resolveLanguageRootForFile(absoluteFilePath, cwd));
@@ -1624,12 +1767,14 @@ function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, modifiedR
1624
1767
  includeDeclarations: false
1625
1768
  }) : void 0;
1626
1769
  const projectConfig = loadPiLensProjectConfig(normalizedCwd);
1770
+ const rulePolicy = resolvedRulePolicyMap(normalizedProjectRoot);
1627
1771
  return {
1628
1772
  filePath: normalizedFilePath,
1629
1773
  projectRoot: normalizedProjectRoot,
1630
1774
  cwd: normalizedCwd,
1631
1775
  kind,
1632
1776
  fileRole,
1777
+ analysisRootMode,
1633
1778
  generatedEvidence: generatedDetail?.evidence,
1634
1779
  generatedLineShapeMean: generatedDetail?.lineShapeMean,
1635
1780
  pi,
@@ -1637,10 +1782,12 @@ function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, modifiedR
1637
1782
  deltaMode: !pi.getFlag("no-delta"),
1638
1783
  facts,
1639
1784
  projectConfig,
1785
+ rulePolicy,
1640
1786
  blockingOnly,
1641
1787
  modifiedRanges,
1642
1788
  writeIndex,
1643
1789
  sessionGeneration,
1790
+ sessionId,
1644
1791
  telemetryModel,
1645
1792
  telemetryProvider,
1646
1793
  toolCwdMemo: {},
@@ -1832,8 +1979,9 @@ function buildCoverageNotice(ctx, runnerLatencies, dedupe) {
1832
1979
  "fact-rules",
1833
1980
  "opengrep"
1834
1981
  ]);
1835
- const anyLinterHasCoverage = runnerLatencies.some((r) => fallbackRunnerIds.has(r.runnerId) && !STRUCTURAL_RUNNERS.has(r.runnerId) && hasUsableResult(r));
1836
- if (anyLinterHasCoverage)
1982
+ const TYPE_CAPABLE_FALLBACK_RUNNERS = /* @__PURE__ */ new Set(["mypy"]);
1983
+ const anyFallbackHasCoverage = runnerLatencies.some((r) => fallbackRunnerIds.has(r.runnerId) && !STRUCTURAL_RUNNERS.has(r.runnerId) && hasUsableResult(r) && (!plan?.capabilities.includes("types") || TYPE_CAPABLE_FALLBACK_RUNNERS.has(r.runnerId)));
1984
+ if (anyFallbackHasCoverage)
1837
1985
  return void 0;
1838
1986
  const onceKey = `${ctx.kind}:${ctx.filePath}`;
1839
1987
  if (dedupe) {
@@ -2026,7 +2174,8 @@ async function runGroup(ctx, group, registry, onRunnerResult) {
2026
2174
  markedAtMs,
2027
2175
  writeIndex: ctx.writeIndex,
2028
2176
  promise: deferred,
2029
- session: ctx.sessionGeneration
2177
+ session: ctx.sessionGeneration,
2178
+ sessionId: ctx.sessionId
2030
2179
  });
2031
2180
  latencies.push({
2032
2181
  runnerId: runner.id,
@@ -2138,6 +2287,19 @@ async function runGroup(ctx, group, registry, onRunnerResult) {
2138
2287
  }
2139
2288
  async function dispatchForFile(ctx, groups, registry, onRunnerResult, options) {
2140
2289
  const _overallStart = Date.now();
2290
+ if (ctx.analysisRootMode === "adopted") {
2291
+ return {
2292
+ diagnostics: [],
2293
+ blockers: [],
2294
+ warnings: [],
2295
+ baselineWarningCount: 0,
2296
+ fixed: [],
2297
+ resolvedCount: 0,
2298
+ output: "",
2299
+ blockerOutput: "",
2300
+ hasBlockers: false
2301
+ };
2302
+ }
2141
2303
  if (ctx.fileRole === "generated") {
2142
2304
  const evidence = ctx.generatedEvidence;
2143
2305
  const lineShapeMean = ctx.generatedLineShapeMean;
@@ -2202,13 +2364,16 @@ async function dispatchForFile(ctx, groups, registry, onRunnerResult, options) {
2202
2364
  }
2203
2365
  const dedupedDiagnostics = dedupeOverlappingDiagnostics(allDiagnostics);
2204
2366
  const fileContent = ctx.facts.getFileFact(ctx.filePath, "file.content") ?? "";
2205
- const rulePolicy = rulePolicyMapFromConfig(loadPiLensProjectConfig(ctx.projectRoot ?? ctx.cwd).rules);
2367
+ const rulePolicy = ctx.rulePolicy ?? rulePolicyMapFromConfig(loadPiLensProjectConfig(ctx.projectRoot ?? ctx.cwd).rules);
2206
2368
  const applyOutputFilters = (diags) => {
2207
2369
  const dockerOverlap = suppressTrivyConfigDockerOverlap(diags);
2208
2370
  const overlap = suppressLintOverlapsWithLsp(dockerOverlap);
2209
2371
  const inline = applyInlineSuppressions(overlap, fileContent);
2210
2372
  const disposition = applyDispositions(inline, ctx.projectRoot ?? ctx.cwd, ctx.filePath, fileContent);
2211
- return applyRulePolicy(disposition, rulePolicy);
2373
+ return applyRulePolicy(disposition, rulePolicy, {
2374
+ root: ctx.projectRoot ?? ctx.cwd,
2375
+ filePath: ctx.filePath
2376
+ });
2212
2377
  };
2213
2378
  let visibleDiagnostics = applyOutputFilters(dedupedDiagnostics);
2214
2379
  let resolvedCount = 0;
@@ -2228,14 +2393,14 @@ async function dispatchForFile(ctx, groups, registry, onRunnerResult, options) {
2228
2393
  provider: ctx.telemetryProvider
2229
2394
  };
2230
2395
  if (fixedItems.length > 0) {
2231
- import("./chunk-ZAWW3AF6.js").then(({ appendToWorklog }) => {
2396
+ import("./chunk-6SDEP3AJ.js").then(({ appendToWorklog }) => {
2232
2397
  appendToWorklog(ctx.cwd, fixedItems, true, worklogIdentity);
2233
2398
  }).catch(() => {
2234
2399
  });
2235
2400
  }
2236
2401
  const fixableWarnings = warnings.filter((d) => d.fixable);
2237
2402
  if (fixableWarnings.length > 0) {
2238
- import("./chunk-ZAWW3AF6.js").then(({ appendToWorklog }) => {
2403
+ import("./chunk-6SDEP3AJ.js").then(({ appendToWorklog }) => {
2239
2404
  appendToWorklog(ctx.cwd, fixableWarnings, false, worklogIdentity);
2240
2405
  }).catch(() => {
2241
2406
  });
@@ -2429,10 +2594,16 @@ function convertLspDiagnostics(diags, filePath, options = {}) {
2429
2594
  function applyFindingPolicy(diagnostics, options) {
2430
2595
  const inlineKept = applyInlineSuppressions(diagnostics, options.content);
2431
2596
  const disposed = filterDisposed(inlineKept, options);
2432
- return { kept: applyRulePolicy(disposed, options.policyMap), inlineKept };
2597
+ return {
2598
+ kept: applyRulePolicy(disposed, options.policyMap, {
2599
+ root: options.cwd,
2600
+ filePath: options.filePath
2601
+ }),
2602
+ inlineKept
2603
+ };
2433
2604
  }
2434
2605
  function loadProjectRulePolicyMap(cwd) {
2435
- return rulePolicyMapFromConfig(loadPiLensProjectConfig(cwd).rules);
2606
+ return resolvedRulePolicyMap(cwd);
2436
2607
  }
2437
2608
  function filterDisposed(diagnostics, options) {
2438
2609
  const identities = options.identities;
@@ -2603,6 +2774,9 @@ function formatCacheAgeLabel(scannedAt, nowMs = Date.now()) {
2603
2774
  const remMinutes = ageMinutes % 60;
2604
2775
  return remMinutes === 0 ? `scanned ${ageHours}h ago` : `scanned ${ageHours}h ${remMinutes}m ago`;
2605
2776
  }
2777
+ function admitsProjectWideFindings(sessionRole) {
2778
+ return sessionRole === "primary";
2779
+ }
2606
2780
  function gated(file, description, gates, evidence, extra = {}) {
2607
2781
  return { mode: "gated", file, description, gates, evidence, ...extra };
2608
2782
  }
@@ -3441,6 +3615,8 @@ function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}) {
3441
3615
  continue;
3442
3616
  if (catalog.effectiveRules.get(rule.id)?.rule !== rule)
3443
3617
  continue;
3618
+ if (ruleIgnoredForPath(rule.id, filePath, ignoreRoot, options.rulePolicy))
3619
+ continue;
3444
3620
  if (blockingOnly && rule.severity !== "error")
3445
3621
  continue;
3446
3622
  if (isRuleIgnoredForPath(filePath, ignoreRoot, rule.ignores))
@@ -3630,6 +3806,7 @@ var astGrepNapiRunner = {
3630
3806
  const diagnostics = evaluateAstGrepRules(ctx.filePath, rootNode, ctx.cwd, ctx.kind, {
3631
3807
  blockingOnly: runningAsLspSubstitute ? false : ctx.blockingOnly,
3632
3808
  projectRoot: ctx.projectRoot,
3809
+ rulePolicy: ctx.rulePolicy,
3633
3810
  log: (message) => ctx.log(message),
3634
3811
  content,
3635
3812
  sgModule
@@ -6912,6 +7089,7 @@ var lspRunner = {
6912
7089
  let serverFailed = false;
6913
7090
  let lspClientReady = true;
6914
7091
  let diagnosticsInconclusive = false;
7092
+ let inconclusiveServerIds = [];
6915
7093
  let unconfirmedServerIds = [];
6916
7094
  let deferredServerIds = [];
6917
7095
  let usedWarmAttach = false;
@@ -6948,8 +7126,24 @@ var lspRunner = {
6948
7126
  } else {
6949
7127
  lspDiags = touched.diags;
6950
7128
  diagnosticsInconclusive = touched.inconclusive === true;
6951
- unconfirmedServerIds = touchCoverageGap(touched);
7129
+ inconclusiveServerIds = touched.inconclusiveServerIds ?? [];
7130
+ unconfirmedServerIds = [
7131
+ .../* @__PURE__ */ new Set([
7132
+ ...touchCoverageGap(touched),
7133
+ ...diagnosticsInconclusive ? inconclusiveServerIds : []
7134
+ ])
7135
+ ];
6952
7136
  deferredServerIds = touched.deferredServerIds ?? [];
7137
+ if (diagnosticsInconclusive && touched.binding?.boundToCurrentDisk === false) {
7138
+ const unconfirmed = new Set(unconfirmedServerIds);
7139
+ const currentContentDiags = [];
7140
+ for (const diagnostic of lspDiags) {
7141
+ if (diagnostic.serverId !== void 0 && !unconfirmed.has(diagnostic.serverId)) {
7142
+ currentContentDiags.push(diagnostic);
7143
+ }
7144
+ }
7145
+ lspDiags = currentContentDiags;
7146
+ }
6953
7147
  }
6954
7148
  } catch (err) {
6955
7149
  serverFailed = true;
@@ -6986,7 +7180,7 @@ var lspRunner = {
6986
7180
  if (!lspClientReady) {
6987
7181
  return { status: "skipped", diagnostics: [], semantic: "none" };
6988
7182
  }
6989
- if (diagnosticsInconclusive) {
7183
+ if (diagnosticsInconclusive && lspDiags.length === 0) {
6990
7184
  return { status: "skipped", diagnostics: [], semantic: "none" };
6991
7185
  }
6992
7186
  if (lspDiags.length === 0) {
@@ -7274,7 +7468,7 @@ var OXLINT_NO_FILES_REPORT_KEYS = /* @__PURE__ */ new Set([
7274
7468
  "start_time"
7275
7469
  ]);
7276
7470
  function resolveLocalVp(cwd) {
7277
- return findLocalBinUpwards("vp", cwd) ?? null;
7471
+ return localBinPath(findLocalBinUpwards("vp", cwd)) ?? null;
7278
7472
  }
7279
7473
  async function resolveVitePlusCommand(cwd) {
7280
7474
  const local = resolveLocalVp(cwd);
@@ -7746,7 +7940,13 @@ var prismaValidateRunner = {
7746
7940
  const cwd = resolveRunnerCwd(ctx, "prisma-validate");
7747
7941
  const resolved = await resolveLocalFirstAsync("prisma", cwd);
7748
7942
  const absPath = path32.resolve(cwd, ctx.filePath);
7749
- const result = await safeSpawnAsync(resolved.cmd, [...resolved.args, "validate", "--schema", absPath], { timeout: 2e4, cwd });
7943
+ const result = await safeSpawnAsync(resolved.cmd, [...resolved.args, "validate", "--schema", absPath], {
7944
+ timeout: 2e4,
7945
+ // ONE neutral-cwd seam for npx fallbacks (#4193, #4268 acceptance 3),
7946
+ // an override of the resolver's project cwd that the scan can still see.
7947
+ cwd,
7948
+ ...resolved.cmd === "npx" ? getIsolatedNpxSpawnOptions() : {}
7949
+ });
7750
7950
  if (result.error && !result.stdout && !result.stderr) {
7751
7951
  return { status: "skipped", diagnostics: [], semantic: "none" };
7752
7952
  }
@@ -8101,7 +8301,7 @@ var pyrightRunner = {
8101
8301
  cmd = installedPath;
8102
8302
  }
8103
8303
  if (!cmd) {
8104
- const { findCommandAsync } = await import("./chunk-HWBZIGMJ.js");
8304
+ const { findCommandAsync } = await import("./chunk-67WMNWEE.js");
8105
8305
  const foundCmd = await findCommandAsync("pyright");
8106
8306
  if (foundCmd)
8107
8307
  cmd = foundCmd;
@@ -9551,7 +9751,7 @@ var taploRunner = {
9551
9751
  claimSource: cover.claimSource
9552
9752
  };
9553
9753
  }
9554
- let cmd = findLocalBinUpwards("taplo", cwd) ?? null;
9754
+ let cmd = localBinPath(findLocalBinUpwards("taplo", cwd)) ?? null;
9555
9755
  if (!cmd) {
9556
9756
  if (await taplo.isAvailableAsync(cwd)) {
9557
9757
  cmd = taplo.getCommand(cwd);
@@ -10046,13 +10246,9 @@ var vale_default = valeRunner;
10046
10246
  import * as path47 from "node:path";
10047
10247
 
10048
10248
  // dist/clients/cache/rule-cache.js
10049
- import * as crypto from "node:crypto";
10050
10249
  import * as fs18 from "node:fs";
10051
10250
  import * as path46 from "node:path";
10052
10251
  var CACHE_VERSION = "v7";
10053
- function isBundledRuleFile(resolvedFile) {
10054
- return resolvedFile === BUNDLED_QUERIES_ROOT || resolvedFile.startsWith(BUNDLED_QUERIES_ROOT + path46.sep);
10055
- }
10056
10252
  function reportBundledRulesRootHealth() {
10057
10253
  reportBundledResourceDirHealth("tree-sitter-queries-dir-missing", BUNDLED_QUERIES_ROOT, getBundledQueriesRootHealth(), "bundled tree-sitter query rules");
10058
10254
  }
@@ -10072,18 +10268,7 @@ var RuleCache = class {
10072
10268
  }
10073
10269
  }
10074
10270
  computeRuleHash(ruleFiles) {
10075
- const hash = crypto.createHash("sha256");
10076
- for (const file of ruleFiles.sort(compareOrdinal)) {
10077
- const resolved = path46.resolve(file);
10078
- if (!fs18.existsSync(resolved))
10079
- continue;
10080
- const stat = fs18.statSync(resolved);
10081
- hash.update(`${file}:${stat.mtimeMs}:${stat.size}`);
10082
- if (!isBundledRuleFile(resolved)) {
10083
- hash.update(fs18.readFileSync(resolved));
10084
- }
10085
- }
10086
- return hash.digest("hex").slice(0, 16);
10271
+ return computeRuleFilesFingerprint(ruleFiles);
10087
10272
  }
10088
10273
  get(ruleFiles) {
10089
10274
  try {
@@ -10524,7 +10709,7 @@ var treeSitterRunner = {
10524
10709
  }
10525
10710
  const fileIsTest = isTestFile(filePath);
10526
10711
  const ignoreRoot = ctx.projectRoot ?? ctx.cwd;
10527
- const effectiveQueries = (ctx.blockingOnly ? languageQueries.filter((q) => q.inline_tier !== "review") : languageQueries).filter((q) => !(fileIsTest && q.skip_test_files) && !isRuleIgnoredForPath(filePath, ignoreRoot, q.ignore_paths));
10712
+ const effectiveQueries = (ctx.blockingOnly ? languageQueries.filter((q) => q.inline_tier !== "review") : languageQueries).filter((q) => !(fileIsTest && q.skip_test_files) && !isRuleIgnoredForPath(filePath, ignoreRoot, q.ignore_paths) && !ruleIgnoredForPath(q.id, filePath, ignoreRoot, ctx.rulePolicy));
10528
10713
  logTreeSitter({
10529
10714
  phase: "queries_loaded",
10530
10715
  filePath,
@@ -10534,6 +10719,9 @@ var treeSitterRunner = {
10534
10719
  cacheHit,
10535
10720
  metadata: { blockingOnly: !!ctx.blockingOnly }
10536
10721
  });
10722
+ if (effectiveQueries.length === 0) {
10723
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
10724
+ }
10537
10725
  const contentFromFacts = ctx.facts.getFileFact(filePath, "file.content");
10538
10726
  const contentOverride = contentFromFacts !== void 0 && contentFromFacts !== null ? contentFromFacts : void 0;
10539
10727
  const diagnostics = [];
@@ -11723,7 +11911,7 @@ var CASCADE_GRAPH_KINDS = /* @__PURE__ */ new Set([
11723
11911
  "cxx"
11724
11912
  ]);
11725
11913
  async function dispatchLintWithResult(filePath, cwd, pi, modifiedRanges, logContext, options) {
11726
- const ctx = createDispatchContext(filePath, cwd, pi, sessionFacts, options?.blockingOnly ?? true, modifiedRanges, options?.projectRoot, options?.writeIndex, options?.telemetryModel, options?.telemetryProvider, options?.sessionGeneration);
11914
+ const ctx = createDispatchContext(filePath, cwd, pi, sessionFacts, options?.blockingOnly ?? true, modifiedRanges, options?.projectRoot, options?.writeIndex, options?.telemetryModel, options?.telemetryProvider, options?.sessionGeneration, options?.sessionId, options?.analysisRootMode);
11727
11915
  sessionFacts.clearFileFactsFor(ctx.filePath);
11728
11916
  try {
11729
11917
  const kind = ctx.kind;
@@ -12093,6 +12281,7 @@ export {
12093
12281
  filterFindingsByDisposition,
12094
12282
  markUnreconciledFindings,
12095
12283
  formatCacheAgeLabel,
12284
+ admitsProjectWideFindings,
12096
12285
  cascadeCarrySuffix,
12097
12286
  logCascade,
12098
12287
  resetCascadeTierSessionState,