pi-lens 4.4.1 → 4.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (140) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/config/dependency-cruiser-eager-allowlist.json +3 -1
  3. package/dist/{chunk-BJF25SUG.js → chunk-23WOO7TX.js} +11 -10
  4. package/dist/{chunk-WATC3IQR.js → chunk-2B73ZPOK.js} +3 -3
  5. package/dist/{chunk-N2IRDBZI.js → chunk-2BBLFX75.js} +101 -11
  6. package/dist/{chunk-TA5L5KWQ.js → chunk-2CD5RS6M.js} +2 -2
  7. package/dist/{chunk-3TTPKMEF.js → chunk-2VUQMUMT.js} +38 -37
  8. package/dist/{chunk-5JHXUETW.js → chunk-3EFKIGZN.js} +3 -3
  9. package/dist/chunk-3JNKMP3B.js +17 -0
  10. package/dist/{chunk-6P54TMWL.js → chunk-3VPVG5SU.js} +6 -6
  11. package/dist/{chunk-ZETYB3XG.js → chunk-3W7IEVVL.js} +44 -28
  12. package/dist/chunk-3YN6V5TV.js +85 -0
  13. package/dist/chunk-466Z26MG.js +30 -0
  14. package/dist/{chunk-PCHDGUKT.js → chunk-4UNXBNE2.js} +12 -11
  15. package/dist/{chunk-J7WJZ6W7.js → chunk-4W4VLAPJ.js} +12 -11
  16. package/dist/{chunk-F5VLZHUW.js → chunk-52FUZI6K.js} +4 -2
  17. package/dist/{chunk-H5EV3VMF.js → chunk-5NGR67U5.js} +2 -2
  18. package/dist/{chunk-CBD2B5CP.js → chunk-5UCAH366.js} +21 -20
  19. package/dist/{chunk-BHWHXMQ2.js → chunk-67WMNWEE.js} +9 -8
  20. package/dist/{chunk-USHG4DLH.js → chunk-6C7JWPCP.js} +7 -7
  21. package/dist/{chunk-VPWKF3GN.js → chunk-6GFRNVYR.js} +11 -10
  22. package/dist/{chunk-3JL6IFD7.js → chunk-6SDEP3AJ.js} +9 -8
  23. package/dist/{chunk-NJF2X5XF.js → chunk-6STA6Z7I.js} +19 -17
  24. package/dist/{chunk-KJCWXUUS.js → chunk-724UL2TJ.js} +6 -4
  25. package/dist/{chunk-MQA2YBAH.js → chunk-76MWC2K6.js} +3 -3
  26. package/dist/{chunk-YLN4K6WD.js → chunk-77M37OKR.js} +10 -8
  27. package/dist/{chunk-XHSCVSOL.js → chunk-7DYBLXYA.js} +3 -3
  28. package/dist/{chunk-OC7LYAU5.js → chunk-AB3YYG6N.js} +24 -10
  29. package/dist/{chunk-LD7WFTRX.js → chunk-ACXEQZOU.js} +16 -16
  30. package/dist/{chunk-VSMJK3ZJ.js → chunk-AOHZU2BM.js} +1 -1
  31. package/dist/{chunk-T4QZ6FEH.js → chunk-AW7R2GWG.js} +65 -8
  32. package/dist/{chunk-GAYGGICG.js → chunk-BKE7E7TC.js} +10 -9
  33. package/dist/{chunk-EUT3B22Y.js → chunk-BRGDSBJE.js} +13 -12
  34. package/dist/{chunk-T54VJRAD.js → chunk-C34ZXZ7O.js} +55 -11
  35. package/dist/{chunk-UGSMCQ4D.js → chunk-CHOW74WE.js} +1 -1
  36. package/dist/{chunk-H2W2AXD3.js → chunk-CMMHNUZ2.js} +66 -58
  37. package/dist/chunk-CSHFH5IN.js +17 -0
  38. package/dist/chunk-EC47DWFA.js +56 -0
  39. package/dist/{chunk-KJN4IOEJ.js → chunk-EJIUXPBX.js} +1 -1
  40. package/dist/{chunk-IQZXOZ7S.js → chunk-EU63SDCG.js} +720 -334
  41. package/dist/{chunk-LNOMA3MP.js → chunk-FCSOVF55.js} +107 -3
  42. package/dist/{chunk-CFYISHUW.js → chunk-FLKWCIZH.js} +8 -5
  43. package/dist/{chunk-OYTMZXRF.js → chunk-FMSO6RDZ.js} +16 -14
  44. package/dist/{chunk-IUKPRZER.js → chunk-GAEGEPOC.js} +2 -2
  45. package/dist/{chunk-6XX5HCLK.js → chunk-GBPP5P7X.js} +4 -4
  46. package/dist/{chunk-TQFBZVCG.js → chunk-GXW6K4YJ.js} +1 -1
  47. package/dist/{chunk-ZZUFZUQZ.js → chunk-H6MF6EVN.js} +331 -107
  48. package/dist/{chunk-3JSEWUWI.js → chunk-IBGIOBEE.js} +3 -3
  49. package/dist/chunk-IZSHAIJ7.js +26 -0
  50. package/dist/{chunk-7ELY2CSM.js → chunk-JAU72XNY.js} +16 -14
  51. package/dist/chunk-JMYYOTQG.js +77 -0
  52. package/dist/{chunk-ELVWJYRV.js → chunk-JSQFTSC6.js} +12 -11
  53. package/dist/{chunk-J6DFZDFA.js → chunk-KBG47GHP.js} +4 -4
  54. package/dist/chunk-KDERNTCQ.js +28 -0
  55. package/dist/chunk-KSX3TXSZ.js +69 -0
  56. package/dist/{chunk-XGUL4SLQ.js → chunk-L3BLXZG7.js} +2 -2
  57. package/dist/chunk-LOP6GRPH.js +31 -0
  58. package/dist/{chunk-MTDRLSI7.js → chunk-M6HSLBWY.js} +1064 -986
  59. package/dist/chunk-MQLUAEOC.js +162 -0
  60. package/dist/{chunk-3MRXIJ6I.js → chunk-MRXKYPCS.js} +2 -2
  61. package/dist/{chunk-D65GOCJB.js → chunk-MXGY6KPT.js} +19 -16
  62. package/dist/{chunk-G3RLJYA2.js → chunk-N2H3ZPZY.js} +47 -1
  63. package/dist/{chunk-HREZQCT3.js → chunk-N3X6Q4YT.js} +3 -3
  64. package/dist/{chunk-HSIRMK2D.js → chunk-OCAGUNBC.js} +15 -15
  65. package/dist/{chunk-ZOEYVXSV.js → chunk-OXXKQCSV.js} +6 -6
  66. package/dist/{chunk-S6AMWKPC.js → chunk-P7266HLM.js} +2 -2
  67. package/dist/{chunk-64LU2OVH.js → chunk-PHDMFAKM.js} +12 -2
  68. package/dist/{chunk-IMSQR4WO.js → chunk-PNQDPDRC.js} +1 -1
  69. package/dist/{chunk-IUR76K6A.js → chunk-Q7DUK4LX.js} +5 -5
  70. package/dist/{chunk-CULQYFLU.js → chunk-QGQ5CCCG.js} +2 -2
  71. package/dist/{chunk-UEAHAMEI.js → chunk-QHJ6DQ2R.js} +11 -11
  72. package/dist/{chunk-BNKLYOOO.js → chunk-QILIYSJF.js} +1 -1
  73. package/dist/{chunk-VYKKMYVT.js → chunk-QOBUHWHX.js} +11 -6
  74. package/dist/{chunk-3PNFWVHT.js → chunk-QQNQ7SJW.js} +2 -75
  75. package/dist/{chunk-7M4T5N3V.js → chunk-R3PCY3YS.js} +14 -13
  76. package/dist/{chunk-MGIWSROG.js → chunk-RPLINGVB.js} +7 -7
  77. package/dist/{chunk-CMU4RMJB.js → chunk-RWWYJ623.js} +10 -1
  78. package/dist/{chunk-3FERWMCH.js → chunk-RXCOMYPY.js} +27 -26
  79. package/dist/{chunk-7BX5DWSA.js → chunk-SAEFPSB6.js} +11 -11
  80. package/dist/{chunk-N7YNJE5R.js → chunk-SQ4TVJBR.js} +10 -10
  81. package/dist/{chunk-DMS52BEP.js → chunk-SS36ZX4J.js} +9 -7
  82. package/dist/{chunk-JONGANYH.js → chunk-T6CPIZNT.js} +32 -30
  83. package/dist/{chunk-6UYA642J.js → chunk-TYM6OUIT.js} +13 -12
  84. package/dist/{chunk-ALQRYH6O.js → chunk-UXGETRP4.js} +13 -12
  85. package/dist/{chunk-FGALODG3.js → chunk-UZ3TUTRV.js} +1 -1
  86. package/dist/{chunk-J3FZR3YY.js → chunk-VCHXCQFG.js} +3 -3
  87. package/dist/{chunk-3P4PF2BX.js → chunk-VCO24T4L.js} +34 -22
  88. package/dist/{chunk-O7HZX7CA.js → chunk-VRZP76YM.js} +1 -1
  89. package/dist/{chunk-VZHJS6DR.js → chunk-VSSBEUFT.js} +16 -15
  90. package/dist/{chunk-U52SAGRM.js → chunk-VWCXW2E7.js} +1 -1
  91. package/dist/{chunk-WW5BLB74.js → chunk-WOA4LN2R.js} +2 -2
  92. package/dist/{chunk-ZMOMPX2Z.js → chunk-WU567RSC.js} +11 -10
  93. package/dist/{chunk-N77WFUL7.js → chunk-XAGLWMJ4.js} +23 -22
  94. package/dist/{chunk-WSEWKCXU.js → chunk-XCJN5IJU.js} +4 -4
  95. package/dist/{chunk-IR6XFA5P.js → chunk-XIJZVPD5.js} +16 -14
  96. package/dist/{chunk-OYOB3ZFG.js → chunk-XMSXXAHW.js} +2 -2
  97. package/dist/{chunk-34OCDWOS.js → chunk-XTMJ7XDT.js} +1 -1
  98. package/dist/{chunk-Z6BB4JLQ.js → chunk-XVF2VVXP.js} +5 -3
  99. package/dist/{chunk-AFZYMAIV.js → chunk-YIUAVF7E.js} +37 -35
  100. package/dist/{chunk-NFXP47RV.js → chunk-YSZNHOBY.js} +85 -31
  101. package/dist/{chunk-V5MEBST3.js → chunk-YYOIQFRS.js} +21 -21
  102. package/dist/chunk-Z527N6YQ.js +28 -0
  103. package/dist/{chunk-D6YF5HKX.js → chunk-ZANLILYD.js} +22 -2
  104. package/dist/{chunk-2UMYMRI3.js → chunk-ZGDGW2V3.js} +2 -2
  105. package/dist/{chunk-6B5I6ICN.js → chunk-ZHFZWTZV.js} +12 -11
  106. package/dist/{chunk-OEX24ECI.js → chunk-ZWFS3OEF.js} +51 -41
  107. package/dist/{chunk-AXKPQ6ZI.js → chunk-ZXYDQ3TZ.js} +5 -5
  108. package/dist/index.js +73783 -71033
  109. package/dist/mcp/analyze-cli.js +18 -8
  110. package/dist/mcp/cli.js +48 -37
  111. package/dist/mcp/server.js +568 -397
  112. package/dist/mcp/worker.js +67 -55
  113. package/dist/probes/installer.js +20 -19
  114. package/docs/astplayground.md +1 -1
  115. package/docs/configuration.md +29 -0
  116. package/docs/dependencies.md +3 -0
  117. package/docs/features.md +2 -2
  118. package/docs/globalconfig.md +2 -0
  119. package/docs/language-coverage.md +2 -1
  120. package/docs/lsp-capability-matrix.md +29 -7
  121. package/docs/lsp-idle-eviction.md +38 -47
  122. package/docs/pi-lens-fixer.md +4 -0
  123. package/docs/pi-lens-merge-policy.md +17 -4
  124. package/docs/pi-lens-retro.md +2 -1
  125. package/docs/pi-lens-reviewer.md +18 -0
  126. package/docs/pi-lens-subagent.md +7 -0
  127. package/docs/real-harness.md +17 -0
  128. package/docs/schema/pi-lens-config-v1.json +37 -2
  129. package/docs/servercapabilities.md +15 -4
  130. package/docs/settings.md +2 -1
  131. package/package.json +1 -1
  132. package/dist/chunk-2FQ62SPM.js +0 -83
  133. package/dist/chunk-7AXO3ZDF.js +0 -30
  134. package/dist/chunk-KMGPUKFD.js +0 -25
  135. package/dist/chunk-OZJINN4O.js +0 -84
  136. package/dist/chunk-RJFED73B.js +0 -79
  137. package/dist/chunk-RTQNRM5X.js +0 -27
  138. package/dist/chunk-VGEVL5EQ.js +0 -29
  139. package/dist/chunk-WPE5FPGR.js +0 -54
  140. package/dist/chunk-Z7QQA2KT.js +0 -27
@@ -2,17 +2,7 @@ import { createRequire as __pilensCreateRequire } from "node:module"; const requ
2
2
  import {
3
3
  getLazyInstallAttempt,
4
4
  tryLazyInstall
5
- } from "./chunk-ZOEYVXSV.js";
6
- import {
7
- stripAnsi
8
- } from "./chunk-FC4RP4I2.js";
9
- import {
10
- isTrivyEnabled,
11
- resolveSeverityFloor
12
- } from "./chunk-YLN4K6WD.js";
13
- import {
14
- rustClient
15
- } from "./chunk-3MRXIJ6I.js";
5
+ } from "./chunk-OXXKQCSV.js";
16
6
  import {
17
7
  findingsResult,
18
8
  finishParsedRun,
@@ -22,10 +12,23 @@ import {
22
12
  isRunnerSkipReason,
23
13
  parseToolRun,
24
14
  skipUnlessToolRan
25
- } from "./chunk-TA5L5KWQ.js";
15
+ } from "./chunk-2CD5RS6M.js";
16
+ import {
17
+ stripAnsi
18
+ } from "./chunk-FC4RP4I2.js";
26
19
  import {
27
20
  spawnFailedWithNoOutput
28
- } from "./chunk-FGALODG3.js";
21
+ } from "./chunk-UZ3TUTRV.js";
22
+ import {
23
+ goClient
24
+ } from "./chunk-XMSXXAHW.js";
25
+ import {
26
+ isTrivyEnabled,
27
+ resolveSeverityFloor
28
+ } from "./chunk-77M37OKR.js";
29
+ import {
30
+ rustClient
31
+ } from "./chunk-MRXKYPCS.js";
29
32
  import {
30
33
  coveringLaneAvailable,
31
34
  createAvailabilityChecker,
@@ -33,6 +36,7 @@ import {
33
36
  getDispatchAvailabilityGeneration,
34
37
  lspPrimaryCoversFile,
35
38
  recordAvailabilityProbeOverrun,
39
+ resolveAdoptedRootCommand,
36
40
  resolveAvailableOrInstall,
37
41
  resolveCommandArgsWithInstallFallback,
38
42
  resolveLocalFirstAsync,
@@ -40,7 +44,7 @@ import {
40
44
  resolveToolCommand,
41
45
  resolveToolCommandWithInstallFallback,
42
46
  resolveVendorToolCommand
43
- } from "./chunk-NFXP47RV.js";
47
+ } from "./chunk-YSZNHOBY.js";
44
48
  import {
45
49
  biomeConfigArgs,
46
50
  findCompiledClassesDir,
@@ -59,27 +63,24 @@ import {
59
63
  hasYamllintConfig,
60
64
  markdownlintConfigArgs,
61
65
  ruffConfigArgs
62
- } from "./chunk-MGIWSROG.js";
66
+ } from "./chunk-RPLINGVB.js";
63
67
  import {
64
68
  importFactProvider
65
- } from "./chunk-WSEWKCXU.js";
69
+ } from "./chunk-XCJN5IJU.js";
66
70
  import {
67
71
  functionFactProvider
68
- } from "./chunk-MQA2YBAH.js";
72
+ } from "./chunk-76MWC2K6.js";
69
73
  import {
70
74
  extractFactsFromTree
71
- } from "./chunk-TQFBZVCG.js";
75
+ } from "./chunk-GXW6K4YJ.js";
72
76
  import {
73
77
  computeImpactCascade
74
- } from "./chunk-WATC3IQR.js";
75
- import {
76
- goClient
77
- } from "./chunk-OYOB3ZFG.js";
78
+ } from "./chunk-2B73ZPOK.js";
78
79
  import {
79
80
  buildOrUpdateGraph,
80
81
  clearReviewGraphWorkspaceCache,
81
82
  fileContentProvider
82
- } from "./chunk-V5MEBST3.js";
83
+ } from "./chunk-YYOIQFRS.js";
83
84
  import {
84
85
  MAX_BLOCKING_RULE_COMPLEXITY,
85
86
  RUNTIME_CONFIG,
@@ -109,19 +110,25 @@ import {
109
110
  touchCoverageGap,
110
111
  tryWarmAttachedCodeActions,
111
112
  tryWarmAttachedDiagnostics
112
- } from "./chunk-MTDRLSI7.js";
113
+ } from "./chunk-M6HSLBWY.js";
113
114
  import {
114
115
  mapWithConcurrency
115
116
  } from "./chunk-XIMYZ3JA.js";
116
117
  import {
117
118
  LSP_SERVERS,
118
119
  getAstGrepRuleSources,
119
- registerRunnerId
120
- } from "./chunk-IQZXOZ7S.js";
120
+ registerRunnerId,
121
+ resolveAnalysisRoot
122
+ } from "./chunk-EU63SDCG.js";
121
123
  import {
122
124
  augmentPythonEnvironment,
123
125
  detectPythonEnvironment
124
- } from "./chunk-LNOMA3MP.js";
126
+ } from "./chunk-FCSOVF55.js";
127
+ import {
128
+ loadProjectSnapshot,
129
+ readJsonCache,
130
+ readJsonCacheAsync
131
+ } from "./chunk-ZXYDQ3TZ.js";
125
132
  import {
126
133
  BUNDLED_QUERIES_ROOT,
127
134
  classifyTreeSitterWasmError,
@@ -141,16 +148,11 @@ import {
141
148
  ruleFilesForLanguage,
142
149
  ruleSourceLanguages,
143
150
  walk
144
- } from "./chunk-UEAHAMEI.js";
151
+ } from "./chunk-QHJ6DQ2R.js";
145
152
  import {
146
153
  buildModuleGraph,
147
154
  clearModuleGraphCache
148
- } from "./chunk-6XX5HCLK.js";
149
- import {
150
- loadProjectSnapshot,
151
- readJsonCache,
152
- readJsonCacheAsync
153
- } from "./chunk-AXKPQ6ZI.js";
155
+ } from "./chunk-GBPP5P7X.js";
154
156
  import {
155
157
  WORD_INDEX_MAX_BYTES,
156
158
  deserializeWordIndex,
@@ -158,7 +160,7 @@ import {
158
160
  removeWordIndexDocumentAsync,
159
161
  scheduleWordIndexPersist,
160
162
  updateWordIndexDocumentForEdit
161
- } from "./chunk-7BX5DWSA.js";
163
+ } from "./chunk-SAEFPSB6.js";
162
164
  import {
163
165
  PathKeyedMap
164
166
  } from "./chunk-Z3CCVDUB.js";
@@ -166,26 +168,24 @@ import {
166
168
  WARM_CODE_ACTION_LOOKUP_LIMIT,
167
169
  contentHash,
168
170
  resolveRunnerPath
169
- } from "./chunk-XGUL4SLQ.js";
171
+ } from "./chunk-L3BLXZG7.js";
170
172
  import {
171
173
  isSpawnableCommand
172
- } from "./chunk-LD7WFTRX.js";
174
+ } from "./chunk-ACXEQZOU.js";
173
175
  import {
174
176
  logRunnerAdvisoryOnce,
175
177
  resolveRunnerCwd,
176
178
  resolveRunnerCwdWithReason
177
- } from "./chunk-IUR76K6A.js";
178
- import {
179
- findLocalBinUpwards
180
- } from "./chunk-T4QZ6FEH.js";
179
+ } from "./chunk-Q7DUK4LX.js";
181
180
  import {
182
- getProjectTrustState,
183
- projectTrustDenialReason
184
- } from "./chunk-D6YF5HKX.js";
181
+ findLocalBinUpwards,
182
+ localBinPath
183
+ } from "./chunk-AW7R2GWG.js";
185
184
  import {
186
185
  createAvailabilityProbeFlight,
186
+ getIsolatedNpxSpawnOptions,
187
187
  probeToolAsync
188
- } from "./chunk-CMU4RMJB.js";
188
+ } from "./chunk-RWWYJ623.js";
189
189
  import {
190
190
  classifyProbeFailure,
191
191
  createAvailabilityLatch,
@@ -195,13 +195,14 @@ import {
195
195
  logAvailabilityDecision,
196
196
  startHostStallSampler,
197
197
  transientRetryDelayMs
198
- } from "./chunk-O7HZX7CA.js";
198
+ } from "./chunk-VRZP76YM.js";
199
199
  import {
200
200
  getLspCapableKinds,
201
201
  getPrimaryDispatchGroup,
202
202
  resolveLanguageRootForFile
203
- } from "./chunk-VYKKMYVT.js";
203
+ } from "./chunk-QOBUHWHX.js";
204
204
  import {
205
+ getGlobalPiLensDir,
205
206
  getProjectDataDir,
206
207
  isInSpawnTimeoutCooldown,
207
208
  isTestFile,
@@ -209,19 +210,25 @@ import {
209
210
  noteSpawnTimeout,
210
211
  resolvePiLensFlag,
211
212
  safeSpawnAsync
212
- } from "./chunk-T54VJRAD.js";
213
+ } from "./chunk-C34ZXZ7O.js";
213
214
  import {
214
215
  detectFileKind
215
- } from "./chunk-64LU2OVH.js";
216
+ } from "./chunk-PHDMFAKM.js";
216
217
  import {
217
218
  classifyGeneratedOrArtifactDetailed,
218
219
  detectFileRole,
219
- loadPiLensProjectConfig
220
- } from "./chunk-N2IRDBZI.js";
220
+ loadPiLensProjectConfig,
221
+ resolvePiLensConfig
222
+ } from "./chunk-2BBLFX75.js";
221
223
  import {
222
224
  findNearestDirWithAnyBasename,
223
- findNearestDirWithMarker
224
- } from "./chunk-Z6BB4JLQ.js";
225
+ findNearestDirWithMarker,
226
+ getPiLensGlobalConfigPath
227
+ } from "./chunk-XVF2VVXP.js";
228
+ import {
229
+ getProjectTrustState,
230
+ projectTrustDenialReason
231
+ } from "./chunk-ZANLILYD.js";
225
232
  import {
226
233
  killedForOutputCap,
227
234
  truncatedByOutputCap,
@@ -230,26 +237,26 @@ import {
230
237
  import {
231
238
  incrementDegradationCount,
232
239
  recordDegradationOnce
233
- } from "./chunk-7ELY2CSM.js";
240
+ } from "./chunk-JAU72XNY.js";
234
241
  import {
235
242
  createSubsystemLogger,
236
243
  logExtension
237
- } from "./chunk-34OCDWOS.js";
244
+ } from "./chunk-XTMJ7XDT.js";
238
245
  import {
239
246
  logLatency,
240
247
  phaseFinished,
241
248
  phaseStarted
242
- } from "./chunk-3PNFWVHT.js";
249
+ } from "./chunk-QQNQ7SJW.js";
243
250
  import {
244
251
  createNdjsonLogger,
245
252
  getGlobalPiLensLogDir,
246
253
  getMaxLogSizeMB,
247
254
  isTestMode
248
- } from "./chunk-VSMJK3ZJ.js";
255
+ } from "./chunk-AOHZU2BM.js";
249
256
  import {
250
257
  FactStore,
251
258
  setFactStoreEvictionReporter
252
- } from "./chunk-UGSMCQ4D.js";
259
+ } from "./chunk-CHOW74WE.js";
253
260
  import {
254
261
  BoundedLruCache
255
262
  } from "./chunk-ABBOA7UD.js";
@@ -263,7 +270,7 @@ import {
263
270
  normalizeMapKey,
264
271
  pathsEqual,
265
272
  toProjectRelativePath
266
- } from "./chunk-G3RLJYA2.js";
273
+ } from "./chunk-N2H3ZPZY.js";
267
274
 
268
275
  // dist/clients/mcp/analyze.js
269
276
  import * as fs19 from "node:fs";
@@ -281,6 +288,8 @@ var DEFAULT_TURN_STATE = {
281
288
  };
282
289
  var MCP_TURN_STATE_OWNER_ID = `mcp-${process.pid}`;
283
290
  var TURN_OWNER_STALE_MS = 30 * 60 * 1e3;
291
+ var TURN_SESSION_PARTITION_TTL_MS = 30 * 60 * 1e3;
292
+ var MAX_TURN_SESSION_PARTITIONS = 16;
284
293
  function getLensDir(cwd) {
285
294
  return getProjectDataDir(cwd);
286
295
  }
@@ -521,7 +530,68 @@ var CacheManager = class {
521
530
  /**
522
531
  * Read turn state. Returns default if not found.
523
532
  */
524
- readTurnState(cwd) {
533
+ readTurnState(cwd, sessionId) {
534
+ return this.resolveTurnStatePartition(cwd, sessionId).state;
535
+ }
536
+ /**
537
+ * Reserve a secondary's durable partition at turn start. The legacy
538
+ * envelope may still be ownerless before the first write, so the write
539
+ * itself cannot infer that this session is concurrent from persisted state.
540
+ */
541
+ ensureSecondaryTurnStatePartition(cwd, sessionId) {
542
+ const envelope = this.readTurnStateEnvelope(cwd);
543
+ if (envelope.sessions?.[sessionId])
544
+ return;
545
+ const secondary = {
546
+ ...DEFAULT_TURN_STATE,
547
+ files: {},
548
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
549
+ sessionId,
550
+ owner: {
551
+ kind: "pi",
552
+ id: sessionId,
553
+ pid: process.pid,
554
+ lastSeen: (/* @__PURE__ */ new Date()).toISOString()
555
+ }
556
+ };
557
+ this.writeTurnState({
558
+ ...envelope,
559
+ sessions: { ...envelope.sessions, [sessionId]: secondary }
560
+ }, cwd);
561
+ }
562
+ /** Resolve the persisted primary/secondary turn-state partition. */
563
+ resolveTurnStatePartition(cwd, sessionId) {
564
+ const envelope = this.readTurnStateEnvelope(cwd);
565
+ const persistedSecondary = sessionId === void 0 ? void 0 : envelope.sessions?.[sessionId];
566
+ const primary2 = sessionId === void 0 || envelope.owner === void 0 && persistedSecondary === void 0 || envelope.owner?.kind === "pi" && envelope.owner.id === sessionId;
567
+ const isSecondary = !primary2 && (persistedSecondary !== void 0 || envelope.owner?.kind === "pi" && envelope.owner.pid === process.pid);
568
+ const state = primary2 ? envelope : persistedSecondary ?? {
569
+ ...DEFAULT_TURN_STATE,
570
+ files: {},
571
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
572
+ };
573
+ return {
574
+ envelope,
575
+ state,
576
+ isSecondary,
577
+ persist: (next) => {
578
+ if (isSecondary && sessionId !== void 0) {
579
+ this.writeTurnState({
580
+ ...envelope,
581
+ sessions: { ...envelope.sessions, [sessionId]: next }
582
+ }, cwd);
583
+ } else
584
+ this.writeTurnState(next, cwd);
585
+ },
586
+ remove: () => {
587
+ if (!isSecondary || sessionId === void 0)
588
+ return;
589
+ const { [sessionId]: _retired, ...remaining } = envelope.sessions ?? {};
590
+ this.writeTurnState({ ...envelope, sessions: remaining }, cwd);
591
+ }
592
+ };
593
+ }
594
+ readTurnStateEnvelope(cwd) {
525
595
  const statePath = getTurnStatePath(cwd);
526
596
  if (!fs.existsSync(statePath)) {
527
597
  return {
@@ -531,11 +601,26 @@ var CacheManager = class {
531
601
  };
532
602
  }
533
603
  const state = readJsonCache(statePath, (parsed) => parsed);
534
- return state ?? {
604
+ const envelope = state ?? {
535
605
  ...DEFAULT_TURN_STATE,
536
606
  files: {},
537
607
  lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
538
608
  };
609
+ if (!envelope.sessions)
610
+ return envelope;
611
+ const now = Date.now();
612
+ const liveEntries = Object.entries(envelope.sessions).filter(([, partition]) => {
613
+ const updated = Date.parse(partition.lastUpdated ?? "");
614
+ return Number.isFinite(updated) && now - updated <= TURN_SESSION_PARTITION_TTL_MS;
615
+ });
616
+ liveEntries.sort(([, left], [, right]) => Date.parse(right.lastUpdated) - Date.parse(left.lastUpdated));
617
+ const sessions = Object.fromEntries(liveEntries.slice(0, MAX_TURN_SESSION_PARTITIONS));
618
+ if (Object.keys(sessions).length !== Object.keys(envelope.sessions).length) {
619
+ const pruned = { ...envelope, sessions };
620
+ writeFileAtomic(statePath, JSON.stringify(pruned, null, 2));
621
+ return pruned;
622
+ }
623
+ return envelope;
539
624
  }
540
625
  /**
541
626
  * Write turn state.
@@ -602,13 +687,17 @@ var CacheManager = class {
602
687
  * current owner; callers must provide an explicit owner id to claim a stale
603
688
  * worklist.
604
689
  */
605
- addModifiedRange(filePath, range, importsChanged, cwd, sessionId, ownerKind = "pi", projectRoot) {
690
+ addModifiedRange(filePath, range, importsChanged, cwd, sessionId, ownerKind = "pi", projectRoot, analysisRootMode) {
691
+ if (analysisRootMode !== void 0 && analysisRootMode !== "session" && analysisRootMode !== "linked-worktree")
692
+ return this.readTurnState(cwd);
606
693
  const containmentRoot = projectRoot ?? cwd;
607
694
  if (!this.isTurnStatePathWithinRoot(filePath, containmentRoot)) {
608
695
  this.log(`turn-state: rejected out-of-project path ${filePath} (project root ${containmentRoot})`);
609
696
  return this.readTurnState(cwd);
610
697
  }
611
- const state = this.readTurnState(cwd);
698
+ const partition = this.resolveTurnStatePartition(cwd, sessionId !== null ? sessionId : void 0);
699
+ const useSessionPartition = partition.isSecondary && ownerKind === "pi" && sessionId != null;
700
+ const state = useSessionPartition ? partition.state : partition.envelope;
612
701
  if (sessionId) {
613
702
  const owner = {
614
703
  kind: ownerKind,
@@ -616,7 +705,7 @@ var CacheManager = class {
616
705
  pid: process.pid,
617
706
  lastSeen: (/* @__PURE__ */ new Date()).toISOString()
618
707
  };
619
- if (this.getTurnStateAccess(cwd, owner) === "foreign-live")
708
+ if (!useSessionPartition && this.getTurnStateAccess(cwd, owner) === "foreign-live")
620
709
  return state;
621
710
  state.sessionId = sessionId;
622
711
  state.owner = owner;
@@ -637,14 +726,23 @@ var CacheManager = class {
637
726
  lastEdit: (/* @__PURE__ */ new Date()).toISOString()
638
727
  };
639
728
  }
640
- this.writeTurnState(state, cwd);
729
+ if (useSessionPartition)
730
+ partition.persist(state);
731
+ else
732
+ this.writeTurnState(state, cwd);
641
733
  return state;
642
734
  }
643
735
  /**
644
736
  * Clear turn state (after turn_end processes it).
645
737
  */
646
- clearTurnState(cwd, owner) {
647
- const currentState = this.readTurnState(cwd);
738
+ clearTurnState(cwd, owner, sessionId) {
739
+ const partition = this.resolveTurnStatePartition(cwd, sessionId);
740
+ if (sessionId !== void 0 && partition.isSecondary) {
741
+ if (partition.envelope.sessions?.[sessionId])
742
+ partition.remove();
743
+ return true;
744
+ }
745
+ const currentState = partition.envelope;
648
746
  const isCurrentOwner = this.getTurnStateAccess(cwd, owner) !== "foreign-live";
649
747
  if (!isCurrentOwner && process.pid !== currentState.owner?.pid)
650
748
  return false;
@@ -668,35 +766,38 @@ var CacheManager = class {
668
766
  /**
669
767
  * Increment turn cycle counter.
670
768
  */
671
- incrementTurnCycle(cwd, owner) {
672
- const state = this.readTurnState(cwd);
769
+ incrementTurnCycle(cwd, owner, sessionId) {
770
+ const partition = this.resolveTurnStatePartition(cwd, sessionId);
771
+ const state = partition.state;
772
+ if (sessionId !== void 0 && state.owner?.id !== sessionId)
773
+ return state;
673
774
  const isCurrentOwner = this.getTurnStateAccess(cwd, owner) !== "foreign-live";
674
775
  if (!isCurrentOwner && process.pid !== state.owner?.pid)
675
776
  return state;
676
777
  state.turnCycles++;
677
- this.writeTurnState(state, cwd);
778
+ partition.persist(state);
678
779
  return state;
679
780
  }
680
781
  /**
681
782
  * Check if max cycles exceeded.
682
783
  */
683
- isMaxCyclesExceeded(cwd) {
684
- const state = this.readTurnState(cwd);
784
+ isMaxCyclesExceeded(cwd, sessionId) {
785
+ const state = this.readTurnState(cwd, sessionId);
685
786
  return state.turnCycles >= state.maxCycles;
686
787
  }
687
788
  /**
688
789
  * Get files that need jscpd re-scan (any edit).
689
790
  * Only returns source code files jscpd can meaningfully analyse.
690
791
  */
691
- getFilesForJscpd(cwd) {
692
- const state = this.readTurnState(cwd);
792
+ getFilesForJscpd(cwd, sessionId) {
793
+ const state = this.readTurnState(cwd, sessionId);
693
794
  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
795
  }
695
796
  /**
696
797
  * Get files that need madge re-scan (imports changed).
697
798
  */
698
- getFilesForMadge(cwd) {
699
- const state = this.readTurnState(cwd);
799
+ getFilesForMadge(cwd, sessionId) {
800
+ const state = this.readTurnState(cwd, sessionId);
700
801
  return Object.entries(state.files).filter(([, f]) => f.importsChanged).map(([p]) => p);
701
802
  }
702
803
  // ---- Utilities ----
@@ -1084,6 +1185,11 @@ var LANGUAGE_CAPABILITY_MATRIX = {
1084
1185
  { mode: "fallback", runnerIds: ["detekt"], filterKinds: ["kotlin"] }
1085
1186
  ]
1086
1187
  },
1188
+ lean: {
1189
+ name: "Lean 4 Diagnostics",
1190
+ capabilities: ["types", "lint"],
1191
+ writeGroups: [primary("lean")]
1192
+ },
1087
1193
  swift: {
1088
1194
  name: "Swift Linting",
1089
1195
  capabilities: ["types", "lint"],
@@ -1209,6 +1315,7 @@ function settledSnapshot(entry) {
1209
1315
  // #3758/#3813: carry the producer's handle so a requeued snapshot keeps
1210
1316
  // the scope that owned it, and the gate's peek can fence with it.
1211
1317
  session: entry.session,
1318
+ sessionId: entry.sessionId,
1212
1319
  result: entry.result
1213
1320
  };
1214
1321
  }
@@ -1219,12 +1326,17 @@ function ownedByLiveSession(entry, site) {
1219
1326
  return entry.session.guardedWrite(`${site}:${entry.runnerId}:${entry.filePath}`, () => true) !== void 0;
1220
1327
  }
1221
1328
  function deferRunnerFindings(entry) {
1222
- const { session, ...owned } = entry;
1329
+ const { session, sessionId, ...owned } = entry;
1223
1330
  if (session !== void 0 && session.guardedWrite(`${entry.runnerId}:${entry.filePath}`, () => true) === void 0) {
1224
1331
  void entry.promise.catch(() => void 0);
1225
1332
  return;
1226
1333
  }
1227
- const tracked = { ...owned, session, settled: false };
1334
+ const tracked = {
1335
+ ...owned,
1336
+ session,
1337
+ sessionId,
1338
+ settled: false
1339
+ };
1228
1340
  void tracked.promise.then((result) => {
1229
1341
  tracked.result = result;
1230
1342
  tracked.settled = true;
@@ -1245,6 +1357,7 @@ function requeueRunnerFindings(entry) {
1245
1357
  track({
1246
1358
  ...owned,
1247
1359
  session: entry.session,
1360
+ sessionId: entry.sessionId,
1248
1361
  promise: Promise.resolve(result),
1249
1362
  settled: true,
1250
1363
  result
@@ -1263,7 +1376,7 @@ function track(tracked) {
1263
1376
  }
1264
1377
  }
1265
1378
  }
1266
- async function drainPendingRunnerFindings(maxWaitMs = 2e3) {
1379
+ async function drainPendingRunnerFindings(maxWaitMs = 2e3, currentSession, currentSessionId) {
1267
1380
  if (pending.length === 0)
1268
1381
  return [];
1269
1382
  const current = pending.splice(0, pending.length);
@@ -1279,6 +1392,14 @@ async function drainPendingRunnerFindings(maxWaitMs = 2e3) {
1279
1392
  for (const entry of current) {
1280
1393
  if (!ownedByLiveSession(entry, "turn-end"))
1281
1394
  continue;
1395
+ if (currentSessionId !== void 0 && entry.sessionId !== void 0 && entry.sessionId !== currentSessionId) {
1396
+ pending.push(entry);
1397
+ continue;
1398
+ }
1399
+ if (currentSession !== void 0 && entry.session !== void 0 && entry.session.generation !== currentSession.generation) {
1400
+ pending.push(entry);
1401
+ continue;
1402
+ }
1282
1403
  if (entry.settled && entry.result) {
1283
1404
  results.push(settledSnapshot(entry));
1284
1405
  } else {
@@ -1303,7 +1424,51 @@ function pendingRunnerFindingsSize() {
1303
1424
  return pending.length;
1304
1425
  }
1305
1426
 
1427
+ // dist/clients/dispatch/adopted-root-runners.js
1428
+ var ADOPTED_ROOT_RUNNER_ALLOWLIST = [
1429
+ "fish-indent",
1430
+ "php-lint"
1431
+ ];
1432
+ var ADMITTED = new Set(ADOPTED_ROOT_RUNNER_ALLOWLIST);
1433
+ function isAdoptedRootRunnerAdmitted(runnerId) {
1434
+ return ADMITTED.has(runnerId);
1435
+ }
1436
+ function filterGroupsForAdoptedRoot(groups) {
1437
+ const filtered = [];
1438
+ for (const group of groups) {
1439
+ const runnerIds = [];
1440
+ for (const runnerId of group.runnerIds) {
1441
+ if (isAdoptedRootRunnerAdmitted(runnerId))
1442
+ runnerIds.push(runnerId);
1443
+ }
1444
+ if (runnerIds.length === 0)
1445
+ continue;
1446
+ filtered.push(runnerIds.length === group.runnerIds.length ? group : { ...group, runnerIds });
1447
+ }
1448
+ return filtered;
1449
+ }
1450
+
1306
1451
  // dist/clients/dispatch/rule-policy.js
1452
+ function resolvedRulePolicyMap(root) {
1453
+ const resolved = resolvePiLensConfig({
1454
+ cwd: root,
1455
+ globalDir: getGlobalPiLensDir(),
1456
+ globalConfigPath: getPiLensGlobalConfigPath()
1457
+ });
1458
+ return rulePolicyMapFromConfig(resolved.value.rules);
1459
+ }
1460
+ function ruleIgnoredForPath(ruleId, filePath, root, policyMap) {
1461
+ const normalized = normalizeRuleId(ruleId);
1462
+ const patterns = [];
1463
+ for (const [configuredId, entry] of Object.entries(policyMap ?? {})) {
1464
+ if (normalizeRuleId(configuredId) !== normalized || !entry?.ignorePaths)
1465
+ continue;
1466
+ patterns.push(...entry.ignorePaths);
1467
+ }
1468
+ if (patterns.length === 0)
1469
+ return false;
1470
+ return isRuleIgnoredForPath(filePath, root, patterns);
1471
+ }
1307
1472
  function evaluateRulePolicy(ruleId, policyMap) {
1308
1473
  if (!policyMap)
1309
1474
  return { dropped: false };
@@ -1331,7 +1496,7 @@ function matchesRule(entry, raw, normalized) {
1331
1496
  return true;
1332
1497
  return normalizeRuleId(entry) === normalized;
1333
1498
  }
1334
- function applyRulePolicy(diagnostics, policyMap) {
1499
+ function applyRulePolicy(diagnostics, policyMap, options) {
1335
1500
  if (!policyMap)
1336
1501
  return diagnostics;
1337
1502
  let hasFilter = false;
@@ -1340,7 +1505,7 @@ function applyRulePolicy(diagnostics, policyMap) {
1340
1505
  continue;
1341
1506
  }
1342
1507
  const entry = rawEntry;
1343
- if ((entry.disable?.length ?? 0) > 0 || (entry.select?.length ?? 0) > 0) {
1508
+ if ((entry.disable?.length ?? 0) > 0 || (entry.select?.length ?? 0) > 0 || (entry.ignorePaths?.length ?? 0) > 0) {
1344
1509
  hasFilter = true;
1345
1510
  break;
1346
1511
  }
@@ -1351,6 +1516,9 @@ function applyRulePolicy(diagnostics, policyMap) {
1351
1516
  const ruleId = d.rule ?? d.code;
1352
1517
  if (!ruleId)
1353
1518
  return true;
1519
+ const filePath = d.filePath ?? options.filePath;
1520
+ if (filePath && ruleIgnoredForPath(ruleId, filePath, options.root, policyMap))
1521
+ return false;
1354
1522
  const { dropped } = evaluateRulePolicy(ruleId, policyMap);
1355
1523
  return !dropped;
1356
1524
  });
@@ -1366,12 +1534,14 @@ function rulePolicyMapFromConfig(rules2) {
1366
1534
  const entry = rawEntry;
1367
1535
  const hasDisable = (entry.disable?.length ?? 0) > 0;
1368
1536
  const hasSelect = (entry.select?.length ?? 0) > 0;
1369
- if (!hasDisable && !hasSelect)
1537
+ const hasIgnorePaths = (entry.ignorePaths?.length ?? 0) > 0;
1538
+ if (!hasDisable && !hasSelect && !hasIgnorePaths)
1370
1539
  continue;
1371
1540
  built ??= {};
1372
1541
  built[key2] = {
1373
1542
  ...hasDisable ? { disable: entry.disable } : {},
1374
- ...hasSelect ? { select: entry.select } : {}
1543
+ ...hasSelect ? { select: entry.select } : {},
1544
+ ...hasIgnorePaths ? { ignorePaths: entry.ignorePaths } : {}
1375
1545
  };
1376
1546
  }
1377
1547
  return built;
@@ -1611,7 +1781,7 @@ function readFilePrefix(filePath, maxBytes = 4096) {
1611
1781
  }
1612
1782
  }
1613
1783
  }
1614
- function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, modifiedRanges, projectRoot, writeIndex, telemetryModel, telemetryProvider, sessionGeneration) {
1784
+ function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, modifiedRanges, projectRoot, writeIndex, telemetryModel, telemetryProvider, sessionGeneration, sessionId, analysisRootMode) {
1615
1785
  const absoluteFilePath = resolveRunnerPath(cwd, filePath);
1616
1786
  const normalizedProjectRoot = normalizeMapKey(path4.resolve(projectRoot ?? cwd));
1617
1787
  const normalizedCwd = normalizeMapKey(resolveLanguageRootForFile(absoluteFilePath, cwd));
@@ -1625,12 +1795,14 @@ function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, modifiedR
1625
1795
  includeDeclarations: false
1626
1796
  }) : void 0;
1627
1797
  const projectConfig = loadPiLensProjectConfig(normalizedCwd);
1798
+ const rulePolicy = resolvedRulePolicyMap(normalizedProjectRoot);
1628
1799
  return {
1629
1800
  filePath: normalizedFilePath,
1630
1801
  projectRoot: normalizedProjectRoot,
1631
1802
  cwd: normalizedCwd,
1632
1803
  kind,
1633
1804
  fileRole,
1805
+ analysisRootMode,
1634
1806
  generatedEvidence: generatedDetail?.evidence,
1635
1807
  generatedLineShapeMean: generatedDetail?.lineShapeMean,
1636
1808
  pi,
@@ -1638,10 +1810,12 @@ function createDispatchContext(filePath, cwd, pi, facts, blockingOnly, modifiedR
1638
1810
  deltaMode: !pi.getFlag("no-delta"),
1639
1811
  facts,
1640
1812
  projectConfig,
1813
+ rulePolicy,
1641
1814
  blockingOnly,
1642
1815
  modifiedRanges,
1643
1816
  writeIndex,
1644
1817
  sessionGeneration,
1818
+ sessionId,
1645
1819
  telemetryModel,
1646
1820
  telemetryProvider,
1647
1821
  toolCwdMemo: {},
@@ -2028,7 +2202,8 @@ async function runGroup(ctx, group, registry, onRunnerResult) {
2028
2202
  markedAtMs,
2029
2203
  writeIndex: ctx.writeIndex,
2030
2204
  promise: deferred,
2031
- session: ctx.sessionGeneration
2205
+ session: ctx.sessionGeneration,
2206
+ sessionId: ctx.sessionId
2032
2207
  });
2033
2208
  latencies.push({
2034
2209
  runnerId: runner.id,
@@ -2140,6 +2315,36 @@ async function runGroup(ctx, group, registry, onRunnerResult) {
2140
2315
  }
2141
2316
  async function dispatchForFile(ctx, groups, registry, onRunnerResult, options) {
2142
2317
  const _overallStart = Date.now();
2318
+ if (ctx.analysisRootMode === "adopted") {
2319
+ const planned = groups.flatMap((g) => g.runnerIds);
2320
+ groups = filterGroupsForAdoptedRoot(groups);
2321
+ const admitted = new Set(groups.flatMap((g) => g.runnerIds));
2322
+ logLatency({
2323
+ type: "phase",
2324
+ filePath: ctx.filePath,
2325
+ phase: "dispatch_adopted_root_allowlist",
2326
+ durationMs: 0,
2327
+ metadata: {
2328
+ admissionList: ADOPTED_ROOT_RUNNER_ALLOWLIST.join(","),
2329
+ admitted: groups.flatMap((g) => g.runnerIds).join(","),
2330
+ refused: planned.flatMap((id) => admitted.has(id) ? [] : [id]).join(","),
2331
+ noAdmittedRunner: groups.length === 0
2332
+ }
2333
+ });
2334
+ if (groups.length === 0) {
2335
+ return {
2336
+ diagnostics: [],
2337
+ blockers: [],
2338
+ warnings: [],
2339
+ baselineWarningCount: 0,
2340
+ fixed: [],
2341
+ resolvedCount: 0,
2342
+ output: "",
2343
+ blockerOutput: "",
2344
+ hasBlockers: false
2345
+ };
2346
+ }
2347
+ }
2143
2348
  if (ctx.fileRole === "generated") {
2144
2349
  const evidence = ctx.generatedEvidence;
2145
2350
  const lineShapeMean = ctx.generatedLineShapeMean;
@@ -2204,13 +2409,16 @@ async function dispatchForFile(ctx, groups, registry, onRunnerResult, options) {
2204
2409
  }
2205
2410
  const dedupedDiagnostics = dedupeOverlappingDiagnostics(allDiagnostics);
2206
2411
  const fileContent = ctx.facts.getFileFact(ctx.filePath, "file.content") ?? "";
2207
- const rulePolicy = rulePolicyMapFromConfig(loadPiLensProjectConfig(ctx.projectRoot ?? ctx.cwd).rules);
2412
+ const rulePolicy = ctx.rulePolicy ?? rulePolicyMapFromConfig(loadPiLensProjectConfig(ctx.projectRoot ?? ctx.cwd).rules);
2208
2413
  const applyOutputFilters = (diags) => {
2209
2414
  const dockerOverlap = suppressTrivyConfigDockerOverlap(diags);
2210
2415
  const overlap = suppressLintOverlapsWithLsp(dockerOverlap);
2211
2416
  const inline = applyInlineSuppressions(overlap, fileContent);
2212
2417
  const disposition = applyDispositions(inline, ctx.projectRoot ?? ctx.cwd, ctx.filePath, fileContent);
2213
- return applyRulePolicy(disposition, rulePolicy);
2418
+ return applyRulePolicy(disposition, rulePolicy, {
2419
+ root: ctx.projectRoot ?? ctx.cwd,
2420
+ filePath: ctx.filePath
2421
+ });
2214
2422
  };
2215
2423
  let visibleDiagnostics = applyOutputFilters(dedupedDiagnostics);
2216
2424
  let resolvedCount = 0;
@@ -2230,14 +2438,14 @@ async function dispatchForFile(ctx, groups, registry, onRunnerResult, options) {
2230
2438
  provider: ctx.telemetryProvider
2231
2439
  };
2232
2440
  if (fixedItems.length > 0) {
2233
- import("./chunk-3JL6IFD7.js").then(({ appendToWorklog }) => {
2441
+ import("./chunk-6SDEP3AJ.js").then(({ appendToWorklog }) => {
2234
2442
  appendToWorklog(ctx.cwd, fixedItems, true, worklogIdentity);
2235
2443
  }).catch(() => {
2236
2444
  });
2237
2445
  }
2238
2446
  const fixableWarnings = warnings.filter((d) => d.fixable);
2239
2447
  if (fixableWarnings.length > 0) {
2240
- import("./chunk-3JL6IFD7.js").then(({ appendToWorklog }) => {
2448
+ import("./chunk-6SDEP3AJ.js").then(({ appendToWorklog }) => {
2241
2449
  appendToWorklog(ctx.cwd, fixableWarnings, false, worklogIdentity);
2242
2450
  }).catch(() => {
2243
2451
  });
@@ -2431,10 +2639,16 @@ function convertLspDiagnostics(diags, filePath, options = {}) {
2431
2639
  function applyFindingPolicy(diagnostics, options) {
2432
2640
  const inlineKept = applyInlineSuppressions(diagnostics, options.content);
2433
2641
  const disposed = filterDisposed(inlineKept, options);
2434
- return { kept: applyRulePolicy(disposed, options.policyMap), inlineKept };
2642
+ return {
2643
+ kept: applyRulePolicy(disposed, options.policyMap, {
2644
+ root: options.cwd,
2645
+ filePath: options.filePath
2646
+ }),
2647
+ inlineKept
2648
+ };
2435
2649
  }
2436
2650
  function loadProjectRulePolicyMap(cwd) {
2437
- return rulePolicyMapFromConfig(loadPiLensProjectConfig(cwd).rules);
2651
+ return resolvedRulePolicyMap(cwd);
2438
2652
  }
2439
2653
  function filterDisposed(diagnostics, options) {
2440
2654
  const identities = options.identities;
@@ -2605,6 +2819,9 @@ function formatCacheAgeLabel(scannedAt, nowMs = Date.now()) {
2605
2819
  const remMinutes = ageMinutes % 60;
2606
2820
  return remMinutes === 0 ? `scanned ${ageHours}h ago` : `scanned ${ageHours}h ${remMinutes}m ago`;
2607
2821
  }
2822
+ function admitsProjectWideFindings(sessionRole) {
2823
+ return sessionRole === "primary";
2824
+ }
2608
2825
  function gated(file, description, gates, evidence, extra = {}) {
2609
2826
  return { mode: "gated", file, description, gates, evidence, ...extra };
2610
2827
  }
@@ -3443,6 +3660,8 @@ function evaluateAstGrepRules(filePath, rootNode, cwd, kind, options = {}) {
3443
3660
  continue;
3444
3661
  if (catalog.effectiveRules.get(rule.id)?.rule !== rule)
3445
3662
  continue;
3663
+ if (ruleIgnoredForPath(rule.id, filePath, ignoreRoot, options.rulePolicy))
3664
+ continue;
3446
3665
  if (blockingOnly && rule.severity !== "error")
3447
3666
  continue;
3448
3667
  if (isRuleIgnoredForPath(filePath, ignoreRoot, rule.ignores))
@@ -3632,6 +3851,7 @@ var astGrepNapiRunner = {
3632
3851
  const diagnostics = evaluateAstGrepRules(ctx.filePath, rootNode, ctx.cwd, ctx.kind, {
3633
3852
  blockingOnly: runningAsLspSubstitute ? false : ctx.blockingOnly,
3634
3853
  projectRoot: ctx.projectRoot,
3854
+ rulePolicy: ctx.rulePolicy,
3635
3855
  log: (message) => ctx.log(message),
3636
3856
  content,
3637
3857
  sgModule
@@ -7293,7 +7513,7 @@ var OXLINT_NO_FILES_REPORT_KEYS = /* @__PURE__ */ new Set([
7293
7513
  "start_time"
7294
7514
  ]);
7295
7515
  function resolveLocalVp(cwd) {
7296
- return findLocalBinUpwards("vp", cwd) ?? null;
7516
+ return localBinPath(findLocalBinUpwards("vp", cwd)) ?? null;
7297
7517
  }
7298
7518
  async function resolveVitePlusCommand(cwd) {
7299
7519
  const local = resolveLocalVp(cwd);
@@ -7616,13 +7836,9 @@ var phpLintRunner = {
7616
7836
  skipTestFiles: false,
7617
7837
  async run(ctx) {
7618
7838
  const cwd = resolveRunnerCwd(ctx, "php-lint");
7619
- if (!await php.isAvailableAsync(cwd)) {
7620
- return { status: "skipped", diagnostics: [], semantic: "none" };
7621
- }
7622
- const cmd = php.getCommand(cwd);
7623
- if (!cmd) {
7839
+ const cmd = ctx.analysisRootMode === "adopted" ? resolveAdoptedRootCommand("php", ctx, cwd) : await php.isAvailableAsync(cwd) ? php.getCommand(cwd) : null;
7840
+ if (!cmd)
7624
7841
  return { status: "skipped", diagnostics: [], semantic: "none" };
7625
- }
7626
7842
  const absPath = path30.resolve(cwd, ctx.filePath);
7627
7843
  const result = await safeSpawnAsync(cmd, ["-l", absPath], {
7628
7844
  timeout: 15e3,
@@ -7765,7 +7981,13 @@ var prismaValidateRunner = {
7765
7981
  const cwd = resolveRunnerCwd(ctx, "prisma-validate");
7766
7982
  const resolved = await resolveLocalFirstAsync("prisma", cwd);
7767
7983
  const absPath = path32.resolve(cwd, ctx.filePath);
7768
- const result = await safeSpawnAsync(resolved.cmd, [...resolved.args, "validate", "--schema", absPath], { timeout: 2e4, cwd });
7984
+ const result = await safeSpawnAsync(resolved.cmd, [...resolved.args, "validate", "--schema", absPath], {
7985
+ timeout: 2e4,
7986
+ // ONE neutral-cwd seam for npx fallbacks (#4193, #4268 acceptance 3),
7987
+ // an override of the resolver's project cwd that the scan can still see.
7988
+ cwd,
7989
+ ...resolved.cmd === "npx" ? getIsolatedNpxSpawnOptions() : {}
7990
+ });
7769
7991
  if (result.error && !result.stdout && !result.stderr) {
7770
7992
  return { status: "skipped", diagnostics: [], semantic: "none" };
7771
7993
  }
@@ -8120,7 +8342,7 @@ var pyrightRunner = {
8120
8342
  cmd = installedPath;
8121
8343
  }
8122
8344
  if (!cmd) {
8123
- const { findCommandAsync } = await import("./chunk-BHWHXMQ2.js");
8345
+ const { findCommandAsync } = await import("./chunk-67WMNWEE.js");
8124
8346
  const foundCmd = await findCommandAsync("pyright");
8125
8347
  if (foundCmd)
8126
8348
  cmd = foundCmd;
@@ -8902,10 +9124,7 @@ var fishIndentRunner = {
8902
9124
  skipTestFiles: false,
8903
9125
  async run(ctx) {
8904
9126
  const cwd = resolveRunnerCwd(ctx, "fish-indent");
8905
- const available = await fishIndent.isAvailableAsync(cwd);
8906
- if (!available)
8907
- return { status: "skipped", diagnostics: [], semantic: "none" };
8908
- const cmd = fishIndent.getCommand(cwd);
9127
+ const cmd = ctx.analysisRootMode === "adopted" ? resolveAdoptedRootCommand("fish_indent", ctx, cwd) : await fishIndent.isAvailableAsync(cwd) ? fishIndent.getCommand(cwd) : null;
8909
9128
  if (!cmd)
8910
9129
  return { status: "skipped", diagnostics: [], semantic: "none" };
8911
9130
  const result = await safeSpawnAsync(cmd, ["--check", ctx.filePath], {
@@ -9570,7 +9789,7 @@ var taploRunner = {
9570
9789
  claimSource: cover.claimSource
9571
9790
  };
9572
9791
  }
9573
- let cmd = findLocalBinUpwards("taplo", cwd) ?? null;
9792
+ let cmd = localBinPath(findLocalBinUpwards("taplo", cwd)) ?? null;
9574
9793
  if (!cmd) {
9575
9794
  if (await taplo.isAvailableAsync(cwd)) {
9576
9795
  cmd = taplo.getCommand(cwd);
@@ -10528,7 +10747,7 @@ var treeSitterRunner = {
10528
10747
  }
10529
10748
  const fileIsTest = isTestFile(filePath);
10530
10749
  const ignoreRoot = ctx.projectRoot ?? ctx.cwd;
10531
- 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));
10750
+ 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));
10532
10751
  logTreeSitter({
10533
10752
  phase: "queries_loaded",
10534
10753
  filePath,
@@ -10538,6 +10757,9 @@ var treeSitterRunner = {
10538
10757
  cacheHit,
10539
10758
  metadata: { blockingOnly: !!ctx.blockingOnly }
10540
10759
  });
10760
+ if (effectiveQueries.length === 0) {
10761
+ return { status: "succeeded", diagnostics: [], semantic: "none" };
10762
+ }
10541
10763
  const contentFromFacts = ctx.facts.getFileFact(filePath, "file.content");
10542
10764
  const contentOverride = contentFromFacts !== void 0 && contentFromFacts !== null ? contentFromFacts : void 0;
10543
10765
  const diagnostics = [];
@@ -11727,7 +11949,7 @@ var CASCADE_GRAPH_KINDS = /* @__PURE__ */ new Set([
11727
11949
  "cxx"
11728
11950
  ]);
11729
11951
  async function dispatchLintWithResult(filePath, cwd, pi, modifiedRanges, logContext, options) {
11730
- const ctx = createDispatchContext(filePath, cwd, pi, sessionFacts, options?.blockingOnly ?? true, modifiedRanges, options?.projectRoot, options?.writeIndex, options?.telemetryModel, options?.telemetryProvider, options?.sessionGeneration);
11952
+ 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);
11731
11953
  sessionFacts.clearFileFactsFor(ctx.filePath);
11732
11954
  try {
11733
11955
  const kind = ctx.kind;
@@ -11970,6 +12192,7 @@ async function analyzeFile(filePath, cwd, options = {}) {
11970
12192
  const start = Date.now();
11971
12193
  const result = await dispatchLintWithResult(absPath, cwd, host, void 0, void 0, {
11972
12194
  blockingOnly: options.blockingOnly ?? false,
12195
+ analysisRootMode: resolveAnalysisRoot(absPath, cwd),
11973
12196
  // #3791: this is a pull surface. Each call is an independent question,
11974
12197
  // so the synthetic coverage notice must come back on every call rather
11975
12198
  // than being latched once per session by the dispatcher — otherwise a
@@ -12097,6 +12320,7 @@ export {
12097
12320
  filterFindingsByDisposition,
12098
12321
  markUnreconciledFindings,
12099
12322
  formatCacheAgeLabel,
12323
+ admitsProjectWideFindings,
12100
12324
  cascadeCarrySuffix,
12101
12325
  logCascade,
12102
12326
  resetCascadeTierSessionState,