pi-lens 4.4.2 → 4.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +306 -0
  2. package/README.md +13 -1
  3. package/dist/{chunk-VITUCP5A.js → chunk-24AKHFGP.js} +9 -7
  4. package/dist/{chunk-JSQFTSC6.js → chunk-2FN63LPV.js} +11 -11
  5. package/dist/{chunk-CF24OIYI.js → chunk-2KXTHF3M.js} +20 -20
  6. package/dist/{chunk-ESBQHFMJ.js → chunk-3DA2JDOL.js} +543 -349
  7. package/dist/{chunk-RWWYJ623.js → chunk-3KAZU3VR.js} +1 -1
  8. package/dist/chunk-3PTHXJXR.js +28 -0
  9. package/dist/chunk-3QED2P4V.js +31 -0
  10. package/dist/{chunk-N2H3ZPZY.js → chunk-3RKP3FMS.js} +1 -0
  11. package/dist/{chunk-XVF2VVXP.js → chunk-5HSRT26B.js} +2 -2
  12. package/dist/{chunk-XKIZ2HNN.js → chunk-5IUSNGOC.js} +18 -16
  13. package/dist/{chunk-EJIUXPBX.js → chunk-5OFV52AM.js} +1 -1
  14. package/dist/{chunk-AW7R2GWG.js → chunk-5VSF4UUQ.js} +12 -7
  15. package/dist/{chunk-NW7BOILN.js → chunk-5WRPDP2A.js} +18 -18
  16. package/dist/{chunk-7ULNCDCO.js → chunk-6DLWKFEX.js} +35 -34
  17. package/dist/{chunk-WOA4LN2R.js → chunk-7D6L4MTX.js} +2 -2
  18. package/dist/{chunk-GAEGEPOC.js → chunk-7HXE67BN.js} +2 -2
  19. package/dist/chunk-AGSS7277.js +84 -0
  20. package/dist/{chunk-R3PCY3YS.js → chunk-AMERCH6V.js} +13 -13
  21. package/dist/{chunk-WU567RSC.js → chunk-ATKJFDQN.js} +14 -10
  22. package/dist/{chunk-VSSBEUFT.js → chunk-AWSCPCN2.js} +16 -16
  23. package/dist/{chunk-VCHXCQFG.js → chunk-BWD5EPKO.js} +3 -3
  24. package/dist/{chunk-ODBNXWT4.js → chunk-CT4MN5IU.js} +1271 -262
  25. package/dist/{chunk-34WSVGEM.js → chunk-CUHSHSB6.js} +83 -5
  26. package/dist/{chunk-CHOW74WE.js → chunk-CYRKQRPM.js} +7 -10
  27. package/dist/{chunk-Q7DUK4LX.js → chunk-DEL2U6CM.js} +5 -5
  28. package/dist/{chunk-6C7JWPCP.js → chunk-DGUKIYSI.js} +26 -23
  29. package/dist/{chunk-PHDMFAKM.js → chunk-DUO2TU77.js} +2 -2
  30. package/dist/{chunk-MRXKYPCS.js → chunk-EBC36LNN.js} +2 -2
  31. package/dist/chunk-ED36RF6Y.js +17 -0
  32. package/dist/{chunk-76MWC2K6.js → chunk-EUXZEFTV.js} +90 -97
  33. package/dist/{chunk-QOZXJ4NA.js → chunk-FM4RDMKT.js} +11 -8
  34. package/dist/{chunk-66VMUL7Q.js → chunk-GAQ7OQG6.js} +31 -28
  35. package/dist/{chunk-Q6ZWTCLY.js → chunk-GL5LUMZE.js} +23 -24
  36. package/dist/{chunk-BKE7E7TC.js → chunk-GMX5OS7B.js} +9 -9
  37. package/dist/{chunk-ZHFZWTZV.js → chunk-GSLNCYGG.js} +11 -11
  38. package/dist/{chunk-ARJ6PLFH.js → chunk-GSM5UU7A.js} +26 -34
  39. package/dist/{chunk-724UL2TJ.js → chunk-GXFV6MYU.js} +3 -3
  40. package/dist/chunk-HAD7QRQI.js +30 -0
  41. package/dist/{chunk-QOBUHWHX.js → chunk-HRNHZA3N.js} +6 -6
  42. package/dist/{chunk-VWCXW2E7.js → chunk-HWCCCQNX.js} +1 -1
  43. package/dist/{chunk-7DYBLXYA.js → chunk-HYGZEMX6.js} +3 -3
  44. package/dist/chunk-IJYGIDF4.js +186 -0
  45. package/dist/{chunk-4W4VLAPJ.js → chunk-IQWN5JCP.js} +11 -11
  46. package/dist/{chunk-GZYBXISR.js → chunk-IUE7SSKV.js} +9 -9
  47. package/dist/{chunk-IBGIOBEE.js → chunk-IWDLU6K3.js} +68 -5
  48. package/dist/{chunk-XCJN5IJU.js → chunk-IYNJ6YPZ.js} +4 -4
  49. package/dist/{chunk-JAU72XNY.js → chunk-J64T55EI.js} +3 -3
  50. package/dist/{chunk-2BBLFX75.js → chunk-J6MPGQTS.js} +5 -5
  51. package/dist/chunk-JORED7EX.js +44 -0
  52. package/dist/{chunk-6SDEP3AJ.js → chunk-JUKMK5Y4.js} +8 -8
  53. package/dist/{chunk-IBRALRBR.js → chunk-JW3YWBVB.js} +15 -14
  54. package/dist/{chunk-L3BLXZG7.js → chunk-KAPJINKN.js} +30 -69
  55. package/dist/{chunk-2B73ZPOK.js → chunk-KN35K52N.js} +3 -3
  56. package/dist/{chunk-C34ZXZ7O.js → chunk-KUA4OJ5Y.js} +24 -11
  57. package/dist/chunk-KYUB6QDC.js +72 -0
  58. package/dist/{chunk-67WMNWEE.js → chunk-LLJNQKQW.js} +12 -10
  59. package/dist/chunk-LZSEQW6A.js +57 -0
  60. package/dist/{chunk-ZANLILYD.js → chunk-M2IFVFWU.js} +2 -2
  61. package/dist/chunk-M2UD3LPU.js +88 -0
  62. package/dist/{chunk-WOKKRCJ5.js → chunk-MBKQHGA7.js} +15 -14
  63. package/dist/{chunk-P7266HLM.js → chunk-MPNJLASA.js} +2 -2
  64. package/dist/{chunk-6GFRNVYR.js → chunk-MZGQNL3Y.js} +10 -10
  65. package/dist/{chunk-QZVW3AAK.js → chunk-N5FLEO7W.js} +33 -31
  66. package/dist/{chunk-3JNKMP3B.js → chunk-O4NWQT6I.js} +4 -4
  67. package/dist/{chunk-PFNSLWU5.js → chunk-OHKJ7PQF.js} +33 -31
  68. package/dist/{chunk-VRZP76YM.js → chunk-P63GXC37.js} +4 -1
  69. package/dist/{chunk-XTMJ7XDT.js → chunk-POMLXPMY.js} +1 -1
  70. package/dist/{chunk-QQNQ7SJW.js → chunk-QBFBRV2B.js} +10 -4
  71. package/dist/{chunk-3UUPUE3O.js → chunk-R3T7CLQ2.js} +84 -60
  72. package/dist/{chunk-23WOO7TX.js → chunk-R7YOCSNV.js} +10 -10
  73. package/dist/{chunk-2CD5RS6M.js → chunk-RKBXZN3A.js} +23 -2
  74. package/dist/{chunk-OG5UUPN7.js → chunk-RKKOYPRA.js} +14 -14
  75. package/dist/{chunk-52FUZI6K.js → chunk-RMLC67LF.js} +1 -1
  76. package/dist/{chunk-XMSXXAHW.js → chunk-ROYSX2S5.js} +2 -2
  77. package/dist/{chunk-PV6LR5P5.js → chunk-RVK72CZX.js} +9605 -14776
  78. package/dist/{chunk-FLKWCIZH.js → chunk-S2HEXIWI.js} +5 -5
  79. package/dist/{chunk-UZ3TUTRV.js → chunk-S67KYG37.js} +1 -1
  80. package/dist/{chunk-JWA5I3SR.js → chunk-SD4I7ZO4.js} +16 -18
  81. package/dist/{chunk-Z527N6YQ.js → chunk-SKAMFC7A.js} +12 -12
  82. package/dist/{chunk-MQLUAEOC.js → chunk-SRYEJNES.js} +2 -2
  83. package/dist/{chunk-BRGDSBJE.js → chunk-SYE6T2EB.js} +12 -12
  84. package/dist/{chunk-GXW6K4YJ.js → chunk-T57LYLZT.js} +1 -1
  85. package/dist/{chunk-ZXYDQ3TZ.js → chunk-T7FEBKXN.js} +48 -15
  86. package/dist/{chunk-IXXDFY73.js → chunk-TIQ2PKDW.js} +3 -3
  87. package/dist/{chunk-QILIYSJF.js → chunk-TLX72JTG.js} +1 -1
  88. package/dist/{chunk-TYM6OUIT.js → chunk-TOY6QKOH.js} +12 -12
  89. package/dist/{chunk-IZSHAIJ7.js → chunk-TRETHTQO.js} +12 -12
  90. package/dist/{chunk-3VPVG5SU.js → chunk-U44DJXQP.js} +10 -6
  91. package/dist/{chunk-PNQDPDRC.js → chunk-UA7ZXYDR.js} +1 -1
  92. package/dist/{chunk-OXXKQCSV.js → chunk-UBOQLVT2.js} +4 -4
  93. package/dist/{chunk-QGQ5CCCG.js → chunk-UMS2MFPZ.js} +2 -2
  94. package/dist/{chunk-QHJ6DQ2R.js → chunk-US7ECLD7.js} +55 -13
  95. package/dist/{chunk-EETY4LF7.js → chunk-UZ2XCLYS.js} +8 -8
  96. package/dist/chunk-V35JG4JB.js +2667 -0
  97. package/dist/{chunk-N3X6Q4YT.js → chunk-VOOAPROD.js} +3 -3
  98. package/dist/{chunk-SAEFPSB6.js → chunk-W66O5E25.js} +21 -11
  99. package/dist/{chunk-KBG47GHP.js → chunk-WMYE35PX.js} +15 -11
  100. package/dist/{chunk-5NGR67U5.js → chunk-XBJVAEEO.js} +32 -5
  101. package/dist/chunk-XHJBS436.js +4217 -0
  102. package/dist/{chunk-GBPP5P7X.js → chunk-XTBDCR27.js} +4 -4
  103. package/dist/{chunk-4UNXBNE2.js → chunk-XTFDJI3T.js} +11 -11
  104. package/dist/{chunk-UXGETRP4.js → chunk-XYURS6EN.js} +13 -12
  105. package/dist/{chunk-RPLINGVB.js → chunk-Y6GZHWSJ.js} +6 -6
  106. package/dist/{chunk-2ZGIZPDG.js → chunk-YCWYO6TQ.js} +41 -38
  107. package/dist/{chunk-66SXUJCA.js → chunk-YH5MVY2U.js} +10 -8
  108. package/dist/{chunk-AOHZU2BM.js → chunk-YNE3RP52.js} +1 -1
  109. package/dist/{chunk-GOYRYZ2R.js → chunk-ZHH37KZL.js} +72 -20
  110. package/dist/{chunk-ZGDGW2V3.js → chunk-ZIVJBOO3.js} +2 -2
  111. package/dist/{chunk-GHB6AYCP.js → chunk-ZVUL7QYY.js} +29 -28
  112. package/dist/index.js +11559 -6213
  113. package/dist/mcp/analyze-cli.js +10 -9
  114. package/dist/mcp/cli.js +41 -38
  115. package/dist/mcp/server.js +1142 -495
  116. package/dist/mcp/worker.js +61 -57
  117. package/dist/probes/installer.js +19 -19
  118. package/dist/workers/project-snapshot-persist-worker.js +28 -9
  119. package/dist/workers/review-graph-persist-worker.js +1 -1
  120. package/docs/configuration.md +11 -0
  121. package/docs/engineering-principles.md +32 -0
  122. package/docs/lsp-capability-matrix.md +1 -1
  123. package/docs/lsp-idle-eviction.md +15 -26
  124. package/docs/pi-lens-fixer.md +117 -13
  125. package/docs/pi-lens-merge-policy.md +109 -11
  126. package/docs/pi-lens-monitor.md +8 -2
  127. package/docs/pi-lens-retro.md +4 -1
  128. package/docs/pi-lens-reviewer.md +114 -3
  129. package/docs/pi-lens-subagent.md +27 -14
  130. package/docs/release-qa-baseline.md +3 -1
  131. package/docs/servercapabilities.md +9 -19
  132. package/package.json +7 -5
  133. package/scripts/download-grammars.js +40 -7
  134. package/scripts/lib/latency-log-phases.mjs +95 -0
  135. package/scripts/rpc-load-check.mjs +570 -81
  136. package/dist/chunk-3WA4HP4I.js +0 -55
  137. package/dist/chunk-466Z26MG.js +0 -30
  138. package/dist/chunk-5JM2524W.js +0 -84
  139. package/dist/chunk-CO2G5PHH.js +0 -84
  140. package/dist/chunk-LOP6GRPH.js +0 -31
  141. package/dist/chunk-UOPVJQ4N.js +0 -103
  142. package/dist/chunk-XPZPXCBV.js +0 -28
  143. /package/dist/{chunk-FC4RP4I2.js → chunk-6HWVI6MI.js} +0 -0
@@ -1,32 +1,36 @@
1
1
  import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
+ import {
3
+ resolveOpengrepConfig
4
+ } from "./chunk-HWCCCQNX.js";
2
5
  import {
3
6
  augmentPythonEnvironment,
4
7
  detectPythonEnvironment,
5
8
  detectPythonVenv,
6
9
  pythonEnvironmentToolCandidates
7
- } from "./chunk-34WSVGEM.js";
10
+ } from "./chunk-CUHSHSB6.js";
8
11
  import {
9
- resolveOpengrepConfig
10
- } from "./chunk-VWCXW2E7.js";
12
+ resolveGitCheckout,
13
+ resolveLinkedWorktreeOwner
14
+ } from "./chunk-UA7ZXYDR.js";
15
+ import {
16
+ hasCargoWorkspaceTable,
17
+ readCargoWorkspaceExclude,
18
+ readCargoWorkspaceMembers,
19
+ readTextFileOrUndefined
20
+ } from "./chunk-MPNJLASA.js";
11
21
  import {
12
22
  extensionsForLanguage,
13
23
  getUserRuleRoot,
14
24
  ruleCorpusFingerprintForCycle
15
- } from "./chunk-724UL2TJ.js";
25
+ } from "./chunk-GXFV6MYU.js";
16
26
  import {
17
27
  load,
18
28
  resolvePackagePath
19
- } from "./chunk-QOZXJ4NA.js";
20
- import {
21
- hasCargoWorkspaceTable,
22
- readCargoWorkspaceExclude,
23
- readCargoWorkspaceMembers,
24
- readTextFileOrUndefined
25
- } from "./chunk-P7266HLM.js";
29
+ } from "./chunk-FM4RDMKT.js";
26
30
  import {
27
31
  HOOK_WALL_BUDGET_MS,
28
32
  bounded
29
- } from "./chunk-52FUZI6K.js";
33
+ } from "./chunk-RMLC67LF.js";
30
34
  import {
31
35
  ensureTool,
32
36
  findManagedToolBinary,
@@ -36,43 +40,46 @@ import {
36
40
  isZizmorAuditTarget,
37
41
  logSessionStart,
38
42
  resolveZizmorGitHubToken
39
- } from "./chunk-66VMUL7Q.js";
43
+ } from "./chunk-GAQ7OQG6.js";
40
44
  import {
41
45
  resolveToolCwd
42
- } from "./chunk-Q7DUK4LX.js";
46
+ } from "./chunk-DEL2U6CM.js";
43
47
  import {
48
+ commandSearchReachesProject,
44
49
  findGlobalBinary,
45
50
  isProjectLocalBinPath
46
- } from "./chunk-AW7R2GWG.js";
51
+ } from "./chunk-5VSF4UUQ.js";
47
52
  import {
48
53
  classifyProbeFailure,
49
54
  createAvailabilityLatch,
50
55
  describeInstallAttempt,
51
56
  logAvailabilityDecision,
52
57
  startHostStallSampler
53
- } from "./chunk-VRZP76YM.js";
58
+ } from "./chunk-P63GXC37.js";
54
59
  import {
55
60
  OWNER_TAG_ENV,
56
61
  SpawnFailureError,
57
62
  classifySpawnFailure,
58
63
  createGenerationSource,
59
64
  ephemeralStagingRoot,
65
+ findCommandAsync,
60
66
  getGlobalPiLensDir,
61
67
  getProjectIgnoreGlobs,
62
68
  hasSpawnFailureKind,
63
69
  holdOwnChildPid,
64
70
  isCommandAvailableAsync,
65
71
  isPathIgnoredByProject,
72
+ isPiLensInternalPath,
66
73
  ownerTagForChildren,
67
74
  resolveWindowsCommandForEnvironment,
68
75
  safeSpawnAsync
69
- } from "./chunk-C34ZXZ7O.js";
76
+ } from "./chunk-KUA4OJ5Y.js";
70
77
  import {
71
78
  DOTNET_CSHARP_ROOT_MARKERS,
72
79
  DOTNET_FSHARP_ROOT_MARKERS,
73
80
  KIND_EXTENSIONS,
74
81
  LEAN_ROOT_MARKERS
75
- } from "./chunk-PHDMFAKM.js";
82
+ } from "./chunk-DUO2TU77.js";
76
83
  import {
77
84
  isRepoTier,
78
85
  lspSectionOf,
@@ -82,7 +89,7 @@ import {
82
89
  reportPiLensConfigRecords,
83
90
  resolvePiLensConfig,
84
91
  summarizeConfigResolution
85
- } from "./chunk-2BBLFX75.js";
92
+ } from "./chunk-J6MPGQTS.js";
86
93
  import {
87
94
  FRESHNESS_CADENCE_MS,
88
95
  LSP_NAMESPACE_KEY,
@@ -90,30 +97,30 @@ import {
90
97
  dirMtimesStillFreshAsync,
91
98
  getPiLensGlobalConfigPath,
92
99
  unknownDirMtimeRecords
93
- } from "./chunk-XVF2VVXP.js";
100
+ } from "./chunk-5HSRT26B.js";
94
101
  import {
95
102
  getProjectTrustState
96
- } from "./chunk-ZANLILYD.js";
103
+ } from "./chunk-M2IFVFWU.js";
97
104
  import {
98
105
  getDegradationLedgerGeneration,
99
106
  incrementDegradationCount,
100
107
  recordDegradationOnce
101
- } from "./chunk-JAU72XNY.js";
108
+ } from "./chunk-J64T55EI.js";
102
109
  import {
103
110
  logExtension
104
- } from "./chunk-XTMJ7XDT.js";
111
+ } from "./chunk-POMLXPMY.js";
105
112
  import {
106
113
  claimPhaseOncePerSession,
107
114
  currentSessionRecordId,
108
115
  logLatency,
109
116
  releasePhaseClaim
110
- } from "./chunk-QQNQ7SJW.js";
117
+ } from "./chunk-QBFBRV2B.js";
111
118
  import {
112
119
  getGlobalPiLensLogDir,
113
120
  getProcessSingleton,
114
121
  isTestMode,
115
122
  redactSecrets
116
- } from "./chunk-AOHZU2BM.js";
123
+ } from "./chunk-YNE3RP52.js";
117
124
  import {
118
125
  STAGE_TMP_PATTERN
119
126
  } from "./chunk-LJDODBBZ.js";
@@ -128,6 +135,7 @@ import {
128
135
  isAtOrAboveHomeDir,
129
136
  isFullyQualified,
130
137
  isUnderDir,
138
+ isVendorPath,
131
139
  isWindowsPath,
132
140
  matchesWorkspaceMemberPattern,
133
141
  normalizeEphemeralMapKey,
@@ -135,44 +143,171 @@ import {
135
143
  normalizeMapKey,
136
144
  pathsEqual,
137
145
  toPosix
138
- } from "./chunk-N2H3ZPZY.js";
146
+ } from "./chunk-3RKP3FMS.js";
139
147
  import {
140
148
  __require
141
149
  } from "./chunk-NXL4FFQQ.js";
142
150
 
151
+ // dist/clients/analysis-root.js
152
+ import * as fs from "node:fs";
153
+ import * as os from "node:os";
154
+ import * as nodePath from "node:path";
155
+ var PROJECT_MARKERS = [
156
+ ".git",
157
+ "package.json",
158
+ "pyproject.toml",
159
+ "Cargo.toml",
160
+ "go.mod",
161
+ "pom.xml",
162
+ "build.gradle",
163
+ "Gemfile",
164
+ "mix.exs",
165
+ "composer.json"
166
+ ];
167
+ function hasProjectMarker(dir) {
168
+ return PROJECT_MARKERS.some((marker) => {
169
+ try {
170
+ const stat2 = fs.statSync(nodePath.join(dir, marker));
171
+ return stat2.isFile() || marker === ".git";
172
+ } catch {
173
+ return false;
174
+ }
175
+ });
176
+ }
177
+ function nearestProjectRoot(filePath) {
178
+ let dir = nodePath.dirname(nodePath.resolve(filePath));
179
+ const filesystemRoot = nodePath.parse(dir).root;
180
+ while (dir !== filesystemRoot) {
181
+ if (hasProjectMarker(dir))
182
+ return dir;
183
+ dir = nodePath.dirname(dir);
184
+ }
185
+ return hasProjectMarker(filesystemRoot) ? filesystemRoot : void 0;
186
+ }
187
+ function realPathOrResolved(filePath) {
188
+ const resolved = nodePath.resolve(filePath);
189
+ let current = resolved;
190
+ const missing = [];
191
+ while (true) {
192
+ try {
193
+ const real = fs.realpathSync.native(current);
194
+ return nodePath.join(real, ...missing.reverse());
195
+ } catch {
196
+ const parent = nodePath.dirname(current);
197
+ if (parent === current)
198
+ return resolved;
199
+ missing.push(nodePath.basename(current));
200
+ current = parent;
201
+ }
202
+ }
203
+ }
204
+ function resolveAnalysisRootPath(filePath, sessionRoot, homeDirOverride) {
205
+ const resolved = realPathOrResolved(filePath);
206
+ const session = realPathOrResolved(sessionRoot);
207
+ const tmp = realPathOrResolved(os.tmpdir());
208
+ const data = process.env.PI_LENS_HOME ? realPathOrResolved(process.env.PI_LENS_HOME) : void 0;
209
+ if (isVendorPath(resolved))
210
+ return void 0;
211
+ if (resolved === tmp || resolved === session || isUnderDir(session, resolved) || !isUnderDir(resolved, session) && (isPiLensInternalPath(resolved, session) || data !== void 0 && (resolved === data || isUnderDir(resolved, data))))
212
+ return void 0;
213
+ if (isUnderDir(resolved, session))
214
+ return session;
215
+ const checkout = resolveGitCheckout(session);
216
+ const linked = checkout && resolveLinkedWorktreeOwner(checkout, resolved);
217
+ if (linked && !isVendorPath(resolved))
218
+ return linked.root;
219
+ const candidate = nearestProjectRoot(resolved);
220
+ if (!candidate || candidate === nodePath.parse(candidate).root)
221
+ return void 0;
222
+ const home = realPathOrResolved(homeDirOverride ?? os.homedir());
223
+ if (isAtOrAboveHomeDir(candidate, home))
224
+ return void 0;
225
+ if (candidate === tmp || isUnderDir(session, candidate))
226
+ return void 0;
227
+ if (isVendorPath(candidate))
228
+ return void 0;
229
+ return candidate;
230
+ }
231
+ function resolveAnalysisRoot(filePath, sessionRoot, homeDirOverride) {
232
+ const resolved = realPathOrResolved(filePath);
233
+ if (isVendorPath(resolved))
234
+ return "none";
235
+ const checkout = resolveGitCheckout(sessionRoot);
236
+ if (checkout && resolveLinkedWorktreeOwner(checkout, resolved)) {
237
+ return "linked-worktree";
238
+ }
239
+ const root = resolveAnalysisRootPath(filePath, sessionRoot, homeDirOverride);
240
+ if (!root)
241
+ return "none";
242
+ if (root === nodePath.resolve(sessionRoot))
243
+ return "session";
244
+ return "adopted";
245
+ }
246
+ function canWriteAnalysisRoot(mode) {
247
+ return mode === "session" || mode === "linked-worktree";
248
+ }
249
+
250
+ // dist/clients/lsp/file-server-match.js
251
+ import path from "node:path";
252
+ function fileMatchKeys(filePath) {
253
+ return {
254
+ ext: path.extname(filePath).toLowerCase(),
255
+ base: path.basename(filePath).toLowerCase()
256
+ };
257
+ }
258
+ function fileMatchReason(server, filePath, ext, base) {
259
+ let matched = false;
260
+ for (const value of server.extensions) {
261
+ const lower = value.toLowerCase();
262
+ if (lower === ext || lower === base) {
263
+ matched = true;
264
+ break;
265
+ }
266
+ }
267
+ if (!matched)
268
+ return "extension-mismatch";
269
+ if (server.pathFilter && !server.pathFilter(filePath))
270
+ return "path-filter";
271
+ return "selected";
272
+ }
273
+ function registeredServers(config, catalog) {
274
+ const builtins = config.registeredServers.length ? config.registeredServers : catalog;
275
+ return [...builtins, ...config.customServers];
276
+ }
277
+
143
278
  // dist/clients/lsp/server.js
144
- import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync3 } from "node:fs";
145
- import { access, readFile, readdir, stat } from "node:fs/promises";
146
- import os6 from "node:os";
147
- import path6 from "node:path";
279
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync4 } from "node:fs";
280
+ import { access, readFile, readdir, realpath, stat } from "node:fs/promises";
281
+ import os7 from "node:os";
282
+ import path7 from "node:path";
148
283
 
149
284
  // dist/clients/sgconfig.js
150
285
  import { createHash } from "node:crypto";
151
- import * as fs from "node:fs";
152
- import * as os from "node:os";
153
- import * as path from "node:path";
286
+ import * as fs2 from "node:fs";
287
+ import * as os2 from "node:os";
288
+ import * as path2 from "node:path";
154
289
  var SGCONFIG_NAMES = ["sgconfig.yml", "sgconfig.yaml"];
155
290
  function findLocalSgconfig(startDir) {
156
291
  return findLocalToolConfig(startDir, SGCONFIG_NAMES);
157
292
  }
158
293
  function canonicalDir(dir) {
159
294
  try {
160
- return fs.realpathSync.native(dir);
295
+ return fs2.realpathSync.native(dir);
161
296
  } catch {
162
- return path.resolve(dir);
297
+ return path2.resolve(dir);
163
298
  }
164
299
  }
165
300
  function getAstGrepRuleSources(projectRoot = process.cwd()) {
166
- const root = path.resolve(projectRoot || process.cwd());
301
+ const root = path2.resolve(projectRoot || process.cwd());
167
302
  const packageRoot = resolvePackagePath(import.meta.url);
168
303
  const candidates = [];
169
304
  if (canonicalDir(root) !== canonicalDir(packageRoot)) {
170
305
  candidates.push({
171
- dir: path.join(root, "rules", "ast-grep-rules", "rules"),
306
+ dir: path2.join(root, "rules", "ast-grep-rules", "rules"),
172
307
  origin: "project",
173
308
  tier: "primary"
174
309
  }, {
175
- dir: path.join(root, "rules", "ast-grep-rules", "coderabbit", "rules"),
310
+ dir: path2.join(root, "rules", "ast-grep-rules", "coderabbit", "rules"),
176
311
  origin: "project",
177
312
  tier: "secondary"
178
313
  });
@@ -180,27 +315,27 @@ function getAstGrepRuleSources(projectRoot = process.cwd()) {
180
315
  const userRoot = getUserRuleRoot();
181
316
  if (canonicalDir(userRoot) !== canonicalDir(root)) {
182
317
  candidates.push({
183
- dir: path.join(userRoot, "ast-grep-rules", "rules"),
318
+ dir: path2.join(userRoot, "ast-grep-rules", "rules"),
184
319
  origin: "user",
185
320
  tier: "primary"
186
321
  }, {
187
- dir: path.join(userRoot, "ast-grep-rules", "coderabbit", "rules"),
322
+ dir: path2.join(userRoot, "ast-grep-rules", "coderabbit", "rules"),
188
323
  origin: "user",
189
324
  tier: "secondary"
190
325
  });
191
326
  }
192
327
  candidates.push({
193
- dir: path.join(packageRoot, "rules", "ast-grep-rules", "rules"),
328
+ dir: path2.join(packageRoot, "rules", "ast-grep-rules", "rules"),
194
329
  origin: "bundled",
195
330
  tier: "primary"
196
331
  }, {
197
- dir: path.join(packageRoot, "rules", "ast-grep-rules", "coderabbit", "rules"),
332
+ dir: path2.join(packageRoot, "rules", "ast-grep-rules", "coderabbit", "rules"),
198
333
  origin: "bundled",
199
334
  tier: "secondary"
200
335
  });
201
336
  const seen = /* @__PURE__ */ new Set();
202
337
  return candidates.filter((source) => {
203
- if (!fs.existsSync(source.dir))
338
+ if (!fs2.existsSync(source.dir))
204
339
  return false;
205
340
  const key = canonicalDir(source.dir);
206
341
  if (seen.has(key))
@@ -213,12 +348,12 @@ function findYamlFiles(dir) {
213
348
  const files = [];
214
349
  let entries;
215
350
  try {
216
- entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
351
+ entries = fs2.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
217
352
  } catch {
218
353
  return files;
219
354
  }
220
355
  for (const entry of entries) {
221
- const full = path.join(dir, entry.name);
356
+ const full = path2.join(dir, entry.name);
222
357
  if (entry.isDirectory()) {
223
358
  files.push(...findYamlFiles(full));
224
359
  } else if (entry.isFile() && /\.ya?ml$/i.test(entry.name)) {
@@ -260,12 +395,12 @@ function snapshotRuleSource(source) {
260
395
  const hash = createHash("sha256");
261
396
  const files = [];
262
397
  for (const file of findYamlFiles(source.dir)) {
263
- const relativePath = path.relative(source.dir, file);
398
+ const relativePath = path2.relative(source.dir, file);
264
399
  hash.update(relativePath);
265
400
  hash.update("\0");
266
401
  let content;
267
402
  try {
268
- content = fs.readFileSync(file, "utf8");
403
+ content = fs2.readFileSync(file, "utf8");
269
404
  } catch {
270
405
  hash.update("missing");
271
406
  hash.update("\0");
@@ -303,12 +438,12 @@ function sourceFingerprint(sources) {
303
438
  return hash.digest("hex");
304
439
  }
305
440
  function getAstGrepRuleFingerprint(projectRoot = process.cwd(), options = {}) {
306
- const root = path.resolve(projectRoot || process.cwd());
441
+ const root = path2.resolve(projectRoot || process.cwd());
307
442
  return ruleCorpusFingerprintForCycle("ast-grep", root, (fingerprintRoot) => sourceFingerprint(getAstGrepRuleSources(fingerprintRoot).map(snapshotRuleSource)), options.force);
308
443
  }
309
444
  function materializeMergedRuleDir(sources, mergedDir) {
310
- fs.rmSync(mergedDir, { recursive: true, force: true });
311
- fs.mkdirSync(mergedDir, { recursive: true });
445
+ fs2.rmSync(mergedDir, { recursive: true, force: true });
446
+ fs2.mkdirSync(mergedDir, { recursive: true });
312
447
  const claimedByEarlierSource = /* @__PURE__ */ new Set();
313
448
  for (const [sourceIndex, source] of sources.entries()) {
314
449
  const claimedThisSource = /* @__PURE__ */ new Set();
@@ -323,11 +458,11 @@ function materializeMergedRuleDir(sources, mergedDir) {
323
458
  });
324
459
  if (survivingDocuments.length === 0)
325
460
  continue;
326
- const destination = path.join(mergedDir, `source${sourceIndex}-${String(fileIndex).padStart(5, "0")}.yml`);
461
+ const destination = path2.join(mergedDir, `source${sourceIndex}-${String(fileIndex).padStart(5, "0")}.yml`);
327
462
  if (survivingDocuments.length === file.documents.length) {
328
- fs.copyFileSync(file.file, destination);
463
+ fs2.copyFileSync(file.file, destination);
329
464
  } else {
330
- fs.writeFileSync(destination, `${survivingDocuments.map((document) => document.raw.trim()).join("\n---\n")}
465
+ fs2.writeFileSync(destination, `${survivingDocuments.map((document) => document.raw.trim()).join("\n---\n")}
331
466
  `);
332
467
  }
333
468
  }
@@ -340,20 +475,20 @@ function rootArtifactKey(root) {
340
475
  return createHash("sha256").update(root).digest("hex").slice(0, 12);
341
476
  }
342
477
  function resolveBaselineSgconfig(projectRoot = process.cwd()) {
343
- const root = canonicalDir(path.resolve(projectRoot || process.cwd()));
478
+ const root = canonicalDir(path2.resolve(projectRoot || process.cwd()));
344
479
  const fingerprint = getAstGrepRuleFingerprint(root, { force: true });
345
480
  const cached = cachedBaselines.get(root);
346
- if (cached?.fingerprint === fingerprint && fs.existsSync(cached.path) && fs.existsSync(cached.mergedDir)) {
481
+ if (cached?.fingerprint === fingerprint && fs2.existsSync(cached.path) && fs2.existsSync(cached.mergedDir)) {
347
482
  return cached.path;
348
483
  }
349
484
  const sources = getAstGrepRuleSources(root).map(snapshotRuleSource);
350
485
  if (sources.length === 0)
351
486
  return void 0;
352
- const dir = path.join(os.tmpdir(), "pi-lens-ast-grep");
353
- fs.mkdirSync(dir, { recursive: true });
487
+ const dir = path2.join(os2.tmpdir(), "pi-lens-ast-grep");
488
+ fs2.mkdirSync(dir, { recursive: true });
354
489
  const artifactKey = rootArtifactKey(root);
355
- const file = path.join(dir, `baseline-${process.pid}-${artifactKey}.sgconfig.yml`);
356
- const mergedDir = path.join(dir, `baseline-${process.pid}-${artifactKey}.rules`);
490
+ const file = path2.join(dir, `baseline-${process.pid}-${artifactKey}.sgconfig.yml`);
491
+ const mergedDir = path2.join(dir, `baseline-${process.pid}-${artifactKey}.rules`);
357
492
  const protectedArtifacts = /* @__PURE__ */ new Set([file, mergedDir]);
358
493
  for (const baseline of cachedBaselines.values()) {
359
494
  protectedArtifacts.add(baseline.path);
@@ -361,8 +496,8 @@ function resolveBaselineSgconfig(projectRoot = process.cwd()) {
361
496
  }
362
497
  cleanupStaleBaselines(dir, protectedArtifacts);
363
498
  materializeMergedRuleDir(sources, mergedDir);
364
- const ruleDirForYaml = mergedDir.split(path.sep).join("/");
365
- fs.writeFileSync(file, `ruleDirs:
499
+ const ruleDirForYaml = mergedDir.split(path2.sep).join("/");
500
+ fs2.writeFileSync(file, `ruleDirs:
366
501
  - ${JSON.stringify(ruleDirForYaml)}
367
502
  `);
368
503
  cachedBaselines.set(root, { fingerprint, path: file, mergedDir });
@@ -373,7 +508,7 @@ var SG_BASELINE_RETAINED_ENTRIES = 24;
373
508
  function enforceBaselineEntryCap(dir, keep) {
374
509
  let names;
375
510
  try {
376
- names = fs.readdirSync(dir);
511
+ names = fs2.readdirSync(dir);
377
512
  } catch {
378
513
  return;
379
514
  }
@@ -383,13 +518,13 @@ function enforceBaselineEntryCap(dir, keep) {
383
518
  const isMergedDir = /^baseline-\d+(?:-[a-f0-9]+)?\.rules$/.test(name);
384
519
  if (!isConfig && !isMergedDir)
385
520
  continue;
386
- const full = path.join(dir, name);
521
+ const full = path2.join(dir, name);
387
522
  if (keep.has(full))
388
523
  continue;
389
524
  try {
390
525
  candidates.push({
391
526
  full,
392
- mtimeMs: fs.statSync(full).mtimeMs,
527
+ mtimeMs: fs2.statSync(full).mtimeMs,
393
528
  recursive: isMergedDir
394
529
  });
395
530
  } catch {
@@ -402,7 +537,7 @@ function enforceBaselineEntryCap(dir, keep) {
402
537
  let evicted = 0;
403
538
  for (const candidate of candidates.slice(0, over)) {
404
539
  try {
405
- fs.rmSync(candidate.full, {
540
+ fs2.rmSync(candidate.full, {
406
541
  recursive: candidate.recursive,
407
542
  force: true
408
543
  });
@@ -426,18 +561,18 @@ function enforceBaselineEntryCap(dir, keep) {
426
561
  function cleanupStaleBaselines(dir, keep) {
427
562
  try {
428
563
  const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
429
- for (const name of fs.readdirSync(dir)) {
564
+ for (const name of fs2.readdirSync(dir)) {
430
565
  const isConfig = /^baseline(?:-\d+(?:-[a-f0-9]+)?)?\.sgconfig\.yml$/.test(name);
431
566
  const isMergedDir = /^baseline-\d+(?:-[a-f0-9]+)?\.rules$/.test(name);
432
567
  if (!isConfig && !isMergedDir)
433
568
  continue;
434
- const full = path.join(dir, name);
569
+ const full = path2.join(dir, name);
435
570
  if (keep.has(full))
436
571
  continue;
437
572
  try {
438
- const stat2 = fs.statSync(full);
573
+ const stat2 = fs2.statSync(full);
439
574
  if (stat2.mtimeMs < cutoff) {
440
- fs.rmSync(full, { recursive: stat2.isDirectory(), force: true });
575
+ fs2.rmSync(full, { recursive: stat2.isDirectory(), force: true });
441
576
  }
442
577
  } catch {
443
578
  }
@@ -492,8 +627,8 @@ function serverTraits(declaration) {
492
627
  }
493
628
 
494
629
  // dist/clients/lsp/config.js
495
- import * as os3 from "node:os";
496
- import path3 from "node:path";
630
+ import * as os4 from "node:os";
631
+ import path4 from "node:path";
497
632
 
498
633
  // dist/clients/dispatch/known-runner-ids.js
499
634
  var knownRunnerIds = /* @__PURE__ */ new Set();
@@ -510,12 +645,12 @@ function isKnownRunnerId(id) {
510
645
  // dist/clients/lsp/launch.js
511
646
  import { AsyncLocalStorage } from "node:async_hooks";
512
647
  import { execFileSync, spawn as nodeSpawn } from "node:child_process";
513
- import fs3 from "node:fs";
514
- import os2 from "node:os";
515
- import path2 from "node:path";
648
+ import fs4 from "node:fs";
649
+ import os3 from "node:os";
650
+ import path3 from "node:path";
516
651
 
517
652
  // dist/clients/lsp/ruby-drive-dirs.js
518
- import * as fs2 from "node:fs";
653
+ import * as fs3 from "node:fs";
519
654
  var RUBY_VERSION_DIR = /^ruby\d/i;
520
655
  var rubyDirNamesCache = new BoundedLruCache(16);
521
656
  function rubyDriveKey(driveRoot) {
@@ -531,7 +666,7 @@ function getRubyVersionDirNamesSync(driveRoot) {
531
666
  return cached;
532
667
  let names;
533
668
  try {
534
- names = filterRubyDirNames(fs2.readdirSync(driveRoot));
669
+ names = filterRubyDirNames(fs3.readdirSync(driveRoot));
535
670
  } catch {
536
671
  names = [];
537
672
  }
@@ -545,7 +680,7 @@ async function getRubyVersionDirNamesAsync(driveRoot) {
545
680
  return cached;
546
681
  let names;
547
682
  try {
548
- names = filterRubyDirNames(await fs2.promises.readdir(driveRoot));
683
+ names = filterRubyDirNames(await fs3.promises.readdir(driveRoot));
549
684
  } catch {
550
685
  names = [];
551
686
  }
@@ -562,9 +697,9 @@ function computeNeedsShell(resolvedCommand) {
562
697
  var DEFAULT_STARTUP_FAILURE_WINDOW_MS = 50;
563
698
  var WINDOWS_NAV_STARTUP_FAILURE_WINDOW_MS = 500;
564
699
  var SESSIONSTART_LOG_DIR = getGlobalPiLensLogDir();
565
- var SESSIONSTART_LOG = path2.join(SESSIONSTART_LOG_DIR, "sessionstart.log");
566
- var PI_LENS_BIN_DIR = path2.join(getGlobalPiLensDir(), "bin");
567
- var PI_LENS_TOOLS_BIN_DIR = path2.join(getGlobalPiLensDir(), "tools", "node_modules", ".bin");
700
+ var SESSIONSTART_LOG = path3.join(SESSIONSTART_LOG_DIR, "sessionstart.log");
701
+ var PI_LENS_BIN_DIR = path3.join(getGlobalPiLensDir(), "bin");
702
+ var PI_LENS_TOOLS_BIN_DIR = path3.join(getGlobalPiLensDir(), "tools", "node_modules", ".bin");
568
703
  function logSessionStart2(msg) {
569
704
  if (isTestMode()) {
570
705
  return;
@@ -572,8 +707,8 @@ function logSessionStart2(msg) {
572
707
  const line = redactSecrets(`[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
573
708
  `);
574
709
  try {
575
- fs3.mkdirSync(SESSIONSTART_LOG_DIR, { recursive: true });
576
- fs3.appendFileSync(SESSIONSTART_LOG, line);
710
+ fs4.mkdirSync(SESSIONSTART_LOG_DIR, { recursive: true });
711
+ fs4.appendFileSync(SESSIONSTART_LOG, line);
577
712
  } catch {
578
713
  }
579
714
  }
@@ -593,8 +728,8 @@ function splitPathEntries(value, delimiter) {
593
728
  }
594
729
  function normalizePathEntry(entry, platform) {
595
730
  if (platform !== "win32")
596
- return path2.posix.normalize(entry);
597
- return path2.win32.normalize(entry).toLowerCase();
731
+ return path3.posix.normalize(entry);
732
+ return path3.win32.normalize(entry).toLowerCase();
598
733
  }
599
734
  function combinePathValuesForPlatform(values, platform = process.platform) {
600
735
  const unique = [];
@@ -649,34 +784,34 @@ function getLiveWindowsPath() {
649
784
  }
650
785
  async function buildAugmentedPath(basePath) {
651
786
  const candidates = [];
652
- const nodeDir = path2.dirname(process.execPath);
787
+ const nodeDir = path3.dirname(process.execPath);
653
788
  if (nodeDir) {
654
789
  candidates.push(nodeDir);
655
790
  }
656
791
  if (isWindows) {
657
- const home = os2.homedir();
658
- const driveRoot = path2.parse(home).root;
659
- candidates.push(path2.join(home, ".cargo", "bin"));
660
- candidates.push(path2.join(home, "go", "bin"));
661
- candidates.push(path2.join(home, ".dotnet", "tools"));
792
+ const home = os3.homedir();
793
+ const driveRoot = path3.parse(home).root;
794
+ candidates.push(path3.join(home, ".cargo", "bin"));
795
+ candidates.push(path3.join(home, "go", "bin"));
796
+ candidates.push(path3.join(home, ".dotnet", "tools"));
662
797
  candidates.push(PI_LENS_BIN_DIR);
663
798
  candidates.push(PI_LENS_TOOLS_BIN_DIR);
664
- candidates.push(path2.join(driveRoot, "Program Files", "Go", "bin"));
665
- candidates.push(path2.join(driveRoot, "Go", "bin"));
799
+ candidates.push(path3.join(driveRoot, "Program Files", "Go", "bin"));
800
+ candidates.push(path3.join(driveRoot, "Go", "bin"));
666
801
  for (const entry of await getRubyVersionDirNamesAsync(driveRoot)) {
667
- candidates.push(path2.join(driveRoot, entry, "bin"));
802
+ candidates.push(path3.join(driveRoot, entry, "bin"));
668
803
  }
669
804
  }
670
805
  const effectiveBase = isWindows ? combinePathValuesForPlatform([basePath, getLiveWindowsPath()]) : basePath;
671
806
  const existing = /* @__PURE__ */ new Set();
672
- for (const entry of splitPathEntries(effectiveBase, path2.delimiter)) {
807
+ for (const entry of splitPathEntries(effectiveBase, path3.delimiter)) {
673
808
  if (!entry)
674
809
  continue;
675
810
  existing.add(normalizePathEntry(entry, process.platform));
676
811
  }
677
812
  const toAppend = [];
678
813
  for (const candidate of candidates) {
679
- if (!candidate || !fs3.existsSync(candidate))
814
+ if (!candidate || !fs4.existsSync(candidate))
680
815
  continue;
681
816
  const normalized = normalizePathEntry(candidate, process.platform);
682
817
  if (existing.has(normalized))
@@ -687,18 +822,18 @@ async function buildAugmentedPath(basePath) {
687
822
  if (toAppend.length === 0)
688
823
  return basePath ?? "";
689
824
  if (!basePath)
690
- return toAppend.join(path2.delimiter);
691
- return `${basePath}${path2.delimiter}${toAppend.join(path2.delimiter)}`;
825
+ return toAppend.join(path3.delimiter);
826
+ return `${basePath}${path3.delimiter}${toAppend.join(path3.delimiter)}`;
692
827
  }
693
828
  function isCmdShimValid(cmdPath) {
694
829
  try {
695
- const content = fs3.readFileSync(cmdPath, "utf-8");
830
+ const content = fs4.readFileSync(cmdPath, "utf-8");
696
831
  const match = content.match(/"%~dp0[/\\]\.\.[/\\]((?:[\w./@-]|\\)+\.(?:mjs|cjs|js))"/i);
697
832
  if (!match)
698
833
  return true;
699
- const relPath = match[1].replace(/[/\\]/g, path2.sep);
700
- const target = path2.resolve(path2.dirname(cmdPath), "..", relPath);
701
- return fs3.existsSync(target);
834
+ const relPath = match[1].replace(/[/\\]/g, path3.sep);
835
+ const target = path3.resolve(path3.dirname(cmdPath), "..", relPath);
836
+ return fs4.existsSync(target);
702
837
  } catch {
703
838
  return true;
704
839
  }
@@ -715,16 +850,16 @@ function isMissingWindowsStartupCommand(command, spawnCommand, cwd, env) {
715
850
  }
716
851
  function bypassPs1OnWindows(ps1Path, args) {
717
852
  const cmdPath = `${ps1Path.slice(0, -4)}.cmd`;
718
- if (fs3.existsSync(cmdPath)) {
853
+ if (fs4.existsSync(cmdPath)) {
719
854
  return { command: cmdPath, args, needsShell: true };
720
855
  }
721
856
  try {
722
- const content = fs3.readFileSync(ps1Path, "utf-8");
857
+ const content = fs4.readFileSync(ps1Path, "utf-8");
723
858
  const match = content.match(/"\$basedir[/\\]\.\.[/\\]((?:[\w./@-]|\\)+\.(?:mjs|cjs|js))"/i);
724
859
  if (match) {
725
- const relPath = match[1].replace(/[/\\]/g, path2.sep);
726
- const jsPath = path2.resolve(path2.dirname(ps1Path), "..", relPath);
727
- if (fs3.existsSync(jsPath)) {
860
+ const relPath = match[1].replace(/[/\\]/g, path3.sep);
861
+ const jsPath = path3.resolve(path3.dirname(ps1Path), "..", relPath);
862
+ if (fs4.existsSync(jsPath)) {
728
863
  return {
729
864
  command: process.execPath,
730
865
  args: [jsPath, ...args],
@@ -752,7 +887,7 @@ function findBinaryOnPath(command, env) {
752
887
  timeout: 5e3
753
888
  }).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
754
889
  for (const candidate of result) {
755
- if (fs3.existsSync(candidate)) {
890
+ if (fs4.existsSync(candidate)) {
756
891
  return candidate;
757
892
  }
758
893
  }
@@ -767,7 +902,7 @@ function withLspProjectCodePermission(allowed, spawn) {
767
902
  var unknownProjectLocalNoticeGeneration = -1;
768
903
  function recordUntrustedLspExecution(request, trust) {
769
904
  const generation = getDegradationLedgerGeneration();
770
- const subject = request.kind === "project-code-server" ? `project-code-server:g${generation}:${request.serverId}:${request.root}` : `project-local-binary:g${generation}:${path2.basename(request.command)}`;
905
+ const subject = request.kind === "project-code-server" ? `project-code-server:g${generation}:${request.serverId}:${request.root}` : `project-local-binary:g${generation}:${path3.basename(request.command)}`;
771
906
  const metadata = request.kind === "project-code-server" ? {
772
907
  field: request.kind,
773
908
  serverId: request.serverId,
@@ -801,21 +936,43 @@ function refuseUntrustedLspExecution(request) {
801
936
  return;
802
937
  if (request.kind === "project-code-server")
803
938
  recordUntrustedLspExecution(request, trust);
804
- throw new SpawnFailureError("spawn-failed", `LSP ${request.kind === "project-code-server" ? "project-code server" : "project-local binary"} refused: project trust is ${trust}`, new Error(`project trust is ${trust}`));
939
+ throw new SpawnFailureError(request.kind === "project-local-binary" ? "trust-refused" : "spawn-failed", `LSP ${request.kind === "project-code-server" ? "project-code server" : "project-local binary"} refused: project trust is ${trust}`, new Error(`project trust is ${trust}`));
805
940
  }
806
941
  function admitProjectSuppliedTsserver(tsserverPath, cwd, projectSupplied, projectCodeAllowed = lspProjectCodePermission.getStore() ?? false) {
807
942
  const trust = getProjectTrustState();
808
943
  if (tsserverPath && (!projectSupplied || projectCodeAllowed && trust === "trusted"))
809
944
  return tsserverPath;
945
+ if (!tsserverPath) {
946
+ recordUnresolvedTypeScriptCompiler(cwd, trust);
947
+ return void 0;
948
+ }
810
949
  recordUntrustedLspExecution({
811
950
  kind: "project-local-binary",
812
- command: tsserverPath ?? "tsserver.js",
813
- resolvedCommand: tsserverPath ?? "tsserver.js",
951
+ command: tsserverPath,
952
+ resolvedCommand: tsserverPath,
814
953
  cwd,
815
- reason: tsserverPath ? "LSP execution refused: project compiler requires trusted session ownership" : "LSP execution refused: no admitted absolute TypeScript compiler"
954
+ reason: "LSP execution refused: project compiler requires trusted session ownership"
816
955
  }, trust);
817
956
  return void 0;
818
957
  }
958
+ function recordUnresolvedTypeScriptCompiler(cwd, trust) {
959
+ const generation = getDegradationLedgerGeneration();
960
+ recordDegradationOnce({
961
+ kind: "lsp-registry-decision",
962
+ subject: `typescript-compiler:g${generation}:${cwd}`,
963
+ reason: "LSP not started: no absolute TypeScript compiler (tsserver.js) was resolved",
964
+ metadata: { field: "typescript-compiler", trust }
965
+ });
966
+ if (unknownProjectLocalNoticeGeneration === generation)
967
+ return;
968
+ unknownProjectLocalNoticeGeneration = generation;
969
+ logExtension({
970
+ subsystem: "lsp-registry",
971
+ level: "warn",
972
+ message: "TypeScript language server not started: no absolute TypeScript compiler (tsserver.js) could be resolved; install typescript in the project or let pi-lens install it",
973
+ metadata: { field: "typescript-compiler" }
974
+ });
975
+ }
819
976
  function isProjectLocalLspBinary(resolvedCommand, cwd) {
820
977
  return isProjectLocalBinPath(resolvedCommand, cwd);
821
978
  }
@@ -910,12 +1067,12 @@ async function launchLSP(command, args = [], options = {}) {
910
1067
  delete env[OWNER_TAG_ENV];
911
1068
  else
912
1069
  env[OWNER_TAG_ENV] = ownerTag;
913
- const isRelativePath = !isFullyQualified(command) && (command.includes(path2.sep) || command.includes("/"));
914
- const explicitCommand = isRelativePath ? path2.resolve(cwd, command) : command;
915
- const resolvedCommand = !isFullyQualified(command) && !command.includes(path2.sep) && !command.includes("/") ? findBinaryOnPath(command, env) ?? explicitCommand : explicitCommand;
1070
+ const isRelativePath = !isFullyQualified(command) && (command.includes(path3.sep) || command.includes("/"));
1071
+ const explicitCommand = isRelativePath ? path3.resolve(cwd, command) : command;
1072
+ const resolvedCommand = !isFullyQualified(command) && !command.includes(path3.sep) && !command.includes("/") ? findBinaryOnPath(command, env) ?? explicitCommand : explicitCommand;
916
1073
  let needsShell = computeNeedsShell(resolvedCommand);
917
1074
  let spawnCommand = resolvedCommand;
918
- if (!isFullyQualified(command) && !command.includes(path2.sep) && !command.includes("/")) {
1075
+ if (!isFullyQualified(command) && !command.includes(path3.sep) && !command.includes("/")) {
919
1076
  const globalBinPath = await findGlobalBinary(command);
920
1077
  if (globalBinPath) {
921
1078
  spawnCommand = globalBinPath;
@@ -950,7 +1107,7 @@ async function launchLSP(command, args = [], options = {}) {
950
1107
  try {
951
1108
  proc = trySpawn(spawnCommand, args, cwd, env, needsShell);
952
1109
  } catch (err) {
953
- if (!isFullyQualified(command) && !command.includes(path2.sep) && !command.includes("/")) {
1110
+ if (!isFullyQualified(command) && !command.includes(path3.sep) && !command.includes("/")) {
954
1111
  const globalBinPath = await findGlobalBinary(command);
955
1112
  if (globalBinPath && globalBinPath !== spawnCommand) {
956
1113
  const needsShellGlobal = computeNeedsShell(globalBinPath);
@@ -988,7 +1145,7 @@ async function launchLSP(command, args = [], options = {}) {
988
1145
  };
989
1146
  proc.stderr?.on("data", onStartupStderr);
990
1147
  try {
991
- await new Promise((resolve3, reject) => {
1148
+ await new Promise((resolve4, reject) => {
992
1149
  let settled = false;
993
1150
  proc.on("error", (err) => {
994
1151
  if (!settled) {
@@ -1025,7 +1182,7 @@ async function launchLSP(command, args = [], options = {}) {
1025
1182
  setTimeout(() => {
1026
1183
  if (!settled) {
1027
1184
  settled = true;
1028
- resolve3();
1185
+ resolve4();
1029
1186
  }
1030
1187
  }, startupFailureWindowMs);
1031
1188
  });
@@ -1056,7 +1213,7 @@ function asRecord(value) {
1056
1213
  }
1057
1214
  var CONFIG_RESOLVED_PHASE = "config_resolved";
1058
1215
  function configResolutionKey(cwd) {
1059
- return normalizeFilePath(path3.resolve(normalizeFilePath(cwd)));
1216
+ return normalizeFilePath(path4.resolve(normalizeFilePath(cwd)));
1060
1217
  }
1061
1218
  function publishConfigResolutionPending(cwd) {
1062
1219
  logSessionStart(`session_start config_resolution_pending session=${currentSessionRecordId()} root=${configResolutionKey(cwd)}`);
@@ -1086,7 +1243,7 @@ function recordConfigResolved(cwd, resolution, lspConfig, homeDir, resolveMs) {
1086
1243
  });
1087
1244
  logSessionStart(`config resolved documents=${summary.documents.length} legacy=${legacyDocuments} records=${summary.recordCount} deniedServers=${deniedServers} resolveMs=${resolveMs} session=${sessionId} root=${root}`);
1088
1245
  }
1089
- async function loadLSPConfig(cwd, homeDir = os3.homedir(), options = {}) {
1246
+ async function loadLSPConfig(cwd, homeDir = os4.homedir(), options = {}) {
1090
1247
  const reporting = options.report !== false;
1091
1248
  publishConfigResolutionPending(cwd);
1092
1249
  const resolveStartedAt = Date.now();
@@ -1287,7 +1444,7 @@ var EMPTY_CONFIG = {
1287
1444
  };
1288
1445
  var configInFlight = /* @__PURE__ */ new Map();
1289
1446
  function normalizeWorkspacePath(cwd) {
1290
- return path3.resolve(cwd);
1447
+ return path4.resolve(cwd);
1291
1448
  }
1292
1449
  var unknownTrustNoticeGeneration = -1;
1293
1450
  function sourceFor(provenance, key) {
@@ -1331,24 +1488,8 @@ function registryDecision(id, field, provenance, trust, allowed) {
1331
1488
  }
1332
1489
  }
1333
1490
  }
1334
- function isSameOrChildPath(filePath, candidateRoot) {
1335
- if (filePath === candidateRoot)
1336
- return true;
1337
- return filePath.startsWith(`${candidateRoot}${path3.sep}`);
1338
- }
1339
1491
  function getConfigForFile(filePath) {
1340
- const resolvedFilePath = path3.resolve(filePath);
1341
- let bestMatch;
1342
- for (const [root, config] of sessionRootConfigEntries()) {
1343
- if (config === void 0)
1344
- continue;
1345
- if (!isSameOrChildPath(resolvedFilePath, root))
1346
- continue;
1347
- if (!bestMatch || root.length > bestMatch.root.length) {
1348
- bestMatch = { root, config };
1349
- }
1350
- }
1351
- return bestMatch?.config ?? EMPTY_CONFIG;
1492
+ return sessionRootConfigForFile(filePath) ?? EMPTY_CONFIG;
1352
1493
  }
1353
1494
  function registerLSPConfig(config) {
1354
1495
  return compileLspRegistry(config, config.__provenance);
@@ -1447,7 +1588,7 @@ async function initLSPConfig(cwd) {
1447
1588
  if (existing)
1448
1589
  return existing;
1449
1590
  const promise = (async () => {
1450
- const config = registerLSPConfig(await loadLSPConfig(cwd, os3.homedir()));
1591
+ const config = registerLSPConfig(await loadLSPConfig(cwd, os4.homedir()));
1451
1592
  forgetConfigResolvedClaims(setSessionRootConfig(normalizedCwd, config));
1452
1593
  })();
1453
1594
  configInFlight.set(normalizedCwd, promise);
@@ -1459,40 +1600,22 @@ async function initLSPConfig(cwd) {
1459
1600
  }
1460
1601
  }
1461
1602
  }
1462
- function registeredServers(config) {
1463
- const builtins = config.registeredServers.length ? config.registeredServers : LSP_SERVERS;
1464
- return [...builtins, ...config.customServers];
1465
- }
1466
1603
  function selectionReason(server, config, filePath, ext, base) {
1467
1604
  if (config.disabledServerIds.has(server.id))
1468
1605
  return "disabled-by-config";
1469
- let matched = false;
1470
- for (const value of server.extensions) {
1471
- const lower = value.toLowerCase();
1472
- if (lower === ext || lower === base) {
1473
- matched = true;
1474
- break;
1475
- }
1476
- }
1477
- if (!matched)
1478
- return "extension-mismatch";
1479
- if (server.pathFilter && !server.pathFilter(filePath))
1480
- return "path-filter";
1481
- return "selected";
1606
+ return fileMatchReason(server, filePath, ext, base);
1482
1607
  }
1483
1608
  function explainServersForFile(filePath, config = getConfigForFile(filePath)) {
1484
- const ext = path3.extname(filePath).toLowerCase();
1485
- const base = path3.basename(filePath).toLowerCase();
1486
- return registeredServers(config).map((server) => {
1609
+ const { ext, base } = fileMatchKeys(filePath);
1610
+ return registeredServers(config, LSP_SERVERS).map((server) => {
1487
1611
  const reason = selectionReason(server, config, filePath, ext, base);
1488
1612
  return { server, selected: reason === "selected", reason };
1489
1613
  });
1490
1614
  }
1491
1615
  function getServersForFileWithConfig(filePath) {
1492
1616
  const config = getConfigForFile(filePath);
1493
- const ext = path3.extname(filePath).toLowerCase();
1494
- const base = path3.basename(filePath).toLowerCase();
1495
- return registeredServers(config).filter((server) => selectionReason(server, config, filePath, ext, base) === "selected");
1617
+ const { ext, base } = fileMatchKeys(filePath);
1618
+ return registeredServers(config, LSP_SERVERS).filter((server) => selectionReason(server, config, filePath, ext, base) === "selected");
1496
1619
  }
1497
1620
  function primaryServerEntry(filePath) {
1498
1621
  return getServersForFileWithConfig(filePath).find((s) => !isAuxiliary(s));
@@ -1960,32 +2083,32 @@ function classifyCascadeWaitTier(lspService, filePath, snapshots) {
1960
2083
  }
1961
2084
 
1962
2085
  // dist/clients/lsp/lombok.js
1963
- import * as fs4 from "node:fs";
1964
- import * as os4 from "node:os";
1965
- import * as path4 from "node:path";
2086
+ import * as fs5 from "node:fs";
2087
+ import * as os5 from "node:os";
2088
+ import * as path5 from "node:path";
1966
2089
  var LOMBOK_ENV_KEYS = ["PI_LENS_LOMBOK_JAR", "LOMBOK_JAR"];
1967
2090
  var LOMBOK_PROJECT_FILES = [
1968
2091
  "lombok.config",
1969
2092
  "pom.xml",
1970
2093
  "build.gradle",
1971
2094
  "build.gradle.kts",
1972
- path4.join("gradle", "libs.versions.toml")
2095
+ path5.join("gradle", "libs.versions.toml")
1973
2096
  ];
1974
2097
  function fileExists(filePath) {
1975
2098
  if (!filePath)
1976
2099
  return false;
1977
2100
  try {
1978
- return fs4.statSync(filePath).isFile();
2101
+ return fs5.statSync(filePath).isFile();
1979
2102
  } catch {
1980
2103
  return false;
1981
2104
  }
1982
2105
  }
1983
2106
  function readTextIfSmall(filePath, maxBytes = 512 * 1024) {
1984
2107
  try {
1985
- const stat2 = fs4.statSync(filePath);
2108
+ const stat2 = fs5.statSync(filePath);
1986
2109
  if (!stat2.isFile() || stat2.size > maxBytes)
1987
2110
  return "";
1988
- return fs4.readFileSync(filePath, "utf-8");
2111
+ return fs5.readFileSync(filePath, "utf-8");
1989
2112
  } catch {
1990
2113
  return "";
1991
2114
  }
@@ -1994,13 +2117,13 @@ function containsLombokDependency(text) {
1994
2117
  return /\borg\.projectlombok\b/i.test(text) || /<artifactId>\s*lombok\s*<\/artifactId>/i.test(text) || /\b(annotationProcessor|compileOnly|testCompileOnly|testAnnotationProcessor)\b[^\n]*\blombok\b/i.test(text);
1995
2118
  }
1996
2119
  function hasLombokProject(root) {
1997
- const lombokConfig = path4.join(root, "lombok.config");
2120
+ const lombokConfig = path5.join(root, "lombok.config");
1998
2121
  if (fileExists(lombokConfig))
1999
2122
  return true;
2000
2123
  for (const name of LOMBOK_PROJECT_FILES) {
2001
2124
  if (name === "lombok.config")
2002
2125
  continue;
2003
- const text = readTextIfSmall(path4.join(root, name));
2126
+ const text = readTextIfSmall(path5.join(root, name));
2004
2127
  if (text && containsLombokDependency(text))
2005
2128
  return true;
2006
2129
  }
@@ -2010,23 +2133,23 @@ function envJarCandidate(env) {
2010
2133
  for (const key of LOMBOK_ENV_KEYS) {
2011
2134
  const value = env[key]?.trim();
2012
2135
  if (fileExists(value))
2013
- return path4.resolve(value);
2136
+ return path5.resolve(value);
2014
2137
  }
2015
2138
  return void 0;
2016
2139
  }
2017
2140
  function localJarCandidates(root) {
2018
2141
  return [
2019
- path4.join(root, "lombok.jar"),
2020
- path4.join(root, ".lombok", "lombok.jar"),
2021
- path4.join(root, "lib", "lombok.jar"),
2022
- path4.join(root, "libs", "lombok.jar")
2142
+ path5.join(root, "lombok.jar"),
2143
+ path5.join(root, ".lombok", "lombok.jar"),
2144
+ path5.join(root, "lib", "lombok.jar"),
2145
+ path5.join(root, "libs", "lombok.jar")
2023
2146
  ];
2024
2147
  }
2025
2148
  function newestExistingJar(candidates) {
2026
2149
  let best;
2027
2150
  for (const candidate of candidates) {
2028
2151
  try {
2029
- const stat2 = fs4.statSync(candidate);
2152
+ const stat2 = fs5.statSync(candidate);
2030
2153
  if (!stat2.isFile())
2031
2154
  continue;
2032
2155
  if (!best || stat2.mtimeMs > best.mtimeMs) {
@@ -2037,35 +2160,35 @@ function newestExistingJar(candidates) {
2037
2160
  }
2038
2161
  return best?.filePath;
2039
2162
  }
2040
- function mavenLocalLombokJar(home = os4.homedir()) {
2041
- const base = path4.join(home, ".m2", "repository", "org", "projectlombok", "lombok");
2163
+ function mavenLocalLombokJar(home = os5.homedir()) {
2164
+ const base = path5.join(home, ".m2", "repository", "org", "projectlombok", "lombok");
2042
2165
  let versions = [];
2043
2166
  try {
2044
- versions = fs4.readdirSync(base);
2167
+ versions = fs5.readdirSync(base);
2045
2168
  } catch {
2046
2169
  return void 0;
2047
2170
  }
2048
- return newestExistingJar(versions.map((version) => path4.join(base, version, `lombok-${version}.jar`)));
2171
+ return newestExistingJar(versions.map((version) => path5.join(base, version, `lombok-${version}.jar`)));
2049
2172
  }
2050
- function gradleCacheLombokJar(home = os4.homedir()) {
2051
- const base = path4.join(home, ".gradle", "caches", "modules-2", "files-2.1", "org.projectlombok", "lombok");
2173
+ function gradleCacheLombokJar(home = os5.homedir()) {
2174
+ const base = path5.join(home, ".gradle", "caches", "modules-2", "files-2.1", "org.projectlombok", "lombok");
2052
2175
  const candidates = [];
2053
2176
  let versions = [];
2054
2177
  try {
2055
- versions = fs4.readdirSync(base);
2178
+ versions = fs5.readdirSync(base);
2056
2179
  } catch {
2057
2180
  return void 0;
2058
2181
  }
2059
2182
  for (const version of versions) {
2060
- const versionDir = path4.join(base, version);
2183
+ const versionDir = path5.join(base, version);
2061
2184
  let hashes = [];
2062
2185
  try {
2063
- hashes = fs4.readdirSync(versionDir);
2186
+ hashes = fs5.readdirSync(versionDir);
2064
2187
  } catch {
2065
2188
  continue;
2066
2189
  }
2067
2190
  for (const hash of hashes) {
2068
- candidates.push(path4.join(versionDir, hash, `lombok-${version}.jar`));
2191
+ candidates.push(path5.join(versionDir, hash, `lombok-${version}.jar`));
2069
2192
  }
2070
2193
  }
2071
2194
  return newestExistingJar(candidates);
@@ -2097,16 +2220,16 @@ function createLombokJdtlsArgs(root, baseEnv = process.env) {
2097
2220
 
2098
2221
  // dist/clients/lsp/jvm-runtime.js
2099
2222
  import { existsSync as existsSync2, readdirSync as readdirSync4 } from "node:fs";
2100
- import os5 from "node:os";
2101
- import path5 from "node:path";
2223
+ import os6 from "node:os";
2224
+ import path6 from "node:path";
2102
2225
  var JAVA_EXE = process.platform === "win32" ? "java.exe" : "java";
2103
2226
  var JAVA_PROBE_TIMEOUT_MS = 5e3;
2104
2227
  var MIN_JAVA_MAJOR = 17;
2105
2228
  function jdkHomeFrom(dir) {
2106
- if (existsSync2(path5.join(dir, "bin", JAVA_EXE)))
2229
+ if (existsSync2(path6.join(dir, "bin", JAVA_EXE)))
2107
2230
  return dir;
2108
- const macHome = path5.join(dir, "Contents", "Home");
2109
- if (existsSync2(path5.join(macHome, "bin", JAVA_EXE)))
2231
+ const macHome = path6.join(dir, "Contents", "Home");
2232
+ if (existsSync2(path6.join(macHome, "bin", JAVA_EXE)))
2110
2233
  return macHome;
2111
2234
  return void 0;
2112
2235
  }
@@ -2119,34 +2242,34 @@ function parseMajorVersion(name) {
2119
2242
  }
2120
2243
  function childDirs(base) {
2121
2244
  try {
2122
- return readdirSync4(base, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => path5.join(base, e.name));
2245
+ return readdirSync4(base, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => path6.join(base, e.name));
2123
2246
  } catch {
2124
2247
  return [];
2125
2248
  }
2126
2249
  }
2127
2250
  function candidateRoots() {
2128
- const home = os5.homedir();
2251
+ const home = os6.homedir();
2129
2252
  const roots = [];
2130
- roots.push(path5.join(home, ".jdks"));
2131
- roots.push(path5.join(getGlobalPiLensDir(), "tools"));
2253
+ roots.push(path6.join(home, ".jdks"));
2254
+ roots.push(path6.join(getGlobalPiLensDir(), "tools"));
2132
2255
  if (process.platform === "win32") {
2133
2256
  const progFiles = [
2134
2257
  process.env.ProgramFiles,
2135
2258
  process.env["ProgramFiles(x86)"],
2136
2259
  process.env.ProgramW6432,
2137
- path5.join(process.env.LOCALAPPDATA ?? path5.join(home, "AppData", "Local"), "Programs")
2260
+ path6.join(process.env.LOCALAPPDATA ?? path6.join(home, "AppData", "Local"), "Programs")
2138
2261
  ].filter((p) => Boolean(p));
2139
2262
  for (const base of progFiles) {
2140
- roots.push(path5.join(base, "Eclipse Adoptium"));
2141
- roots.push(path5.join(base, "Java"));
2142
- roots.push(path5.join(base, "Microsoft"));
2143
- roots.push(path5.join(base, "Zulu"));
2144
- roots.push(path5.join(base, "Amazon Corretto"));
2145
- roots.push(path5.join(base, "BellSoft"));
2263
+ roots.push(path6.join(base, "Eclipse Adoptium"));
2264
+ roots.push(path6.join(base, "Java"));
2265
+ roots.push(path6.join(base, "Microsoft"));
2266
+ roots.push(path6.join(base, "Zulu"));
2267
+ roots.push(path6.join(base, "Amazon Corretto"));
2268
+ roots.push(path6.join(base, "BellSoft"));
2146
2269
  }
2147
2270
  } else if (process.platform === "darwin") {
2148
2271
  roots.push("/Library/Java/JavaVirtualMachines");
2149
- roots.push(path5.join(home, "Library", "Java", "JavaVirtualMachines"));
2272
+ roots.push(path6.join(home, "Library", "Java", "JavaVirtualMachines"));
2150
2273
  roots.push("/opt/homebrew/opt");
2151
2274
  roots.push("/usr/local/opt");
2152
2275
  } else {
@@ -2170,7 +2293,7 @@ function discoverJdkHome(roots = candidateRoots(), javaHome = process.env.JAVA_H
2170
2293
  continue;
2171
2294
  found.push({
2172
2295
  home: resolved,
2173
- major: parseMajorVersion(path5.basename(dir))
2296
+ major: parseMajorVersion(path6.basename(dir))
2174
2297
  });
2175
2298
  }
2176
2299
  }
@@ -2269,12 +2392,12 @@ async function resolveJavaRuntimeEnv() {
2269
2392
  }
2270
2393
  const home = discoverJdkHome();
2271
2394
  if (home) {
2272
- const binDir = path5.join(home, "bin");
2395
+ const binDir = path6.join(home, "bin");
2273
2396
  const currentPath = process.env.PATH ?? process.env.Path ?? "";
2274
2397
  _cachedEnv = {
2275
2398
  value: {
2276
2399
  JAVA_HOME: home,
2277
- PATH: binDir + path5.delimiter + currentPath
2400
+ PATH: binDir + path6.delimiter + currentPath
2278
2401
  }
2279
2402
  };
2280
2403
  return _cachedEnv.value;
@@ -2330,7 +2453,7 @@ async function resolveLspServerCwd(server, filePath, sessionCwd, onRootFailure,
2330
2453
  rootFailed = true;
2331
2454
  onRootFailure?.(reason);
2332
2455
  }
2333
- const isFileDirFallback = serverRoot !== void 0 && rootMarkers?.length && path6.resolve(serverRoot) === path6.resolve(path6.dirname(filePath));
2456
+ const isFileDirFallback = serverRoot !== void 0 && rootMarkers?.length && path7.resolve(serverRoot) === path7.resolve(path7.dirname(filePath));
2334
2457
  if (!serverRoot || isFileDirFallback) {
2335
2458
  const probedMarker = onRootFallback && !rootFailed && rootMarkers?.length ? resolveToolCwd("lsp", server.id, filePath, {
2336
2459
  cwd: sessionCwd,
@@ -2339,7 +2462,7 @@ async function resolveLspServerCwd(server, filePath, sessionCwd, onRootFailure,
2339
2462
  }).marker : void 0;
2340
2463
  const refusedMarker = probedMarker !== void 0 && rootMarkers?.includes(probedMarker) ? probedMarker : void 0;
2341
2464
  const fallbackCwd = rootMarkers?.length ? resolveToolCwd("lsp", server.id, filePath, {
2342
- cwd: path6.dirname(path6.resolve(filePath)),
2465
+ cwd: path7.dirname(path7.resolve(filePath)),
2343
2466
  rootMarkers
2344
2467
  }) : void 0;
2345
2468
  if (!serverRoot) {
@@ -2393,6 +2516,9 @@ var PROJECT_BOUNDARY_MARKERS = [
2393
2516
  "Cargo.toml",
2394
2517
  "Cargo.lock",
2395
2518
  "pyproject.toml",
2519
+ "setup.py",
2520
+ "setup.cfg",
2521
+ "requirements.txt",
2396
2522
  "uv.lock",
2397
2523
  "poetry.lock",
2398
2524
  "Pipfile",
@@ -2406,24 +2532,30 @@ var PROJECT_BOUNDARY_MARKERS = [
2406
2532
  "pubspec.yaml",
2407
2533
  "Package.swift"
2408
2534
  ];
2535
+ var PYTHON_BOUNDARY_MARKERS = /* @__PURE__ */ new Set([
2536
+ "setup.py",
2537
+ "setup.cfg",
2538
+ "requirements.txt"
2539
+ ]);
2540
+ var PYTHON_EXTENSIONS = new Set(extensionsForLanguage("python"));
2409
2541
  var loggedRootCeilingClamps = /* @__PURE__ */ new Set();
2410
2542
  var posixCaseInsensitiveByPath = /* @__PURE__ */ new Map();
2411
2543
  function posixFilesystemIsCaseInsensitive(root) {
2412
- const resolved = path6.resolve(root);
2544
+ const resolved = path7.resolve(root);
2413
2545
  const cached = posixCaseInsensitiveByPath.get(resolved);
2414
2546
  if (cached !== void 0)
2415
2547
  return cached;
2416
2548
  if (!existsSync3(resolved)) {
2417
2549
  return false;
2418
2550
  }
2419
- const name = path6.basename(resolved);
2551
+ const name = path7.basename(resolved);
2420
2552
  const alternate = name.toLowerCase() === name ? name.toUpperCase() : name.toLowerCase();
2421
2553
  let insensitive = false;
2422
- const alternatePath = path6.join(path6.dirname(resolved), alternate);
2554
+ const alternatePath = path7.join(path7.dirname(resolved), alternate);
2423
2555
  if (alternate !== name && existsSync3(alternatePath)) {
2424
2556
  try {
2425
- const actual = statSync3(resolved);
2426
- const alternateStat = statSync3(alternatePath);
2557
+ const actual = statSync4(resolved);
2558
+ const alternateStat = statSync4(alternatePath);
2427
2559
  insensitive = actual.dev === alternateStat.dev && actual.ino === alternateStat.ino;
2428
2560
  } catch {
2429
2561
  insensitive = false;
@@ -2437,7 +2569,7 @@ function resetLSPCaseSensitivityState() {
2437
2569
  }
2438
2570
  function isSameOrWithin(ancestor, candidate, deps = {}) {
2439
2571
  const windowsShaped = isWindowsPath(ancestor) || isWindowsPath(candidate);
2440
- const pathApi = windowsShaped ? path6.win32 : path6;
2572
+ const pathApi = windowsShaped ? path7.win32 : path7;
2441
2573
  const resolvedAncestor = pathApi.resolve(ancestor);
2442
2574
  const resolvedCandidate = pathApi.resolve(candidate);
2443
2575
  let caseInsensitive = false;
@@ -2457,7 +2589,7 @@ function isSameOrWithin(ancestor, candidate, deps = {}) {
2457
2589
  }
2458
2590
  function enforceLspRootCeiling(root, sessionCwd, filePath) {
2459
2591
  const windowsShaped = isWindowsPath(root) || isWindowsPath(sessionCwd);
2460
- const pathApi = windowsShaped ? path6.win32 : path6;
2592
+ const pathApi = windowsShaped ? path7.win32 : path7;
2461
2593
  const resolvedRoot = pathApi.resolve(root);
2462
2594
  const resolvedCwd = pathApi.resolve(sessionCwd);
2463
2595
  if (filePath && !isSameOrWithin(resolvedCwd, pathApi.resolve(filePath))) {
@@ -2472,18 +2604,20 @@ function enforceLspRootCeiling(root, sessionCwd, filePath) {
2472
2604
  }
2473
2605
  return resolvedCwd;
2474
2606
  }
2475
- async function hasProjectBoundaryMarker(dir) {
2607
+ async function hasProjectBoundaryMarker(dir, server) {
2476
2608
  if (await markerExists(dir, ".git"))
2477
2609
  return true;
2478
2610
  for (const marker of PROJECT_BOUNDARY_MARKERS) {
2611
+ if (PYTHON_BOUNDARY_MARKERS.has(marker) && !server.extensions.some((extension) => PYTHON_EXTENSIONS.has(extension)))
2612
+ continue;
2479
2613
  if (await markerExists(dir, marker))
2480
2614
  return true;
2481
2615
  }
2482
2616
  return false;
2483
2617
  }
2484
2618
  function pathSegments(dir) {
2485
- const parsed = path6.parse(path6.resolve(dir));
2486
- return path6.relative(parsed.root, path6.resolve(dir)).split(path6.sep).filter(Boolean).map((segment) => segment.toLowerCase());
2619
+ const parsed = path7.parse(path7.resolve(dir));
2620
+ return path7.relative(parsed.root, path7.resolve(dir)).split(path7.sep).filter(Boolean).map((segment) => segment.toLowerCase());
2487
2621
  }
2488
2622
  function hasFixtureConvention(dir) {
2489
2623
  const segments = pathSegments(dir);
@@ -2495,18 +2629,18 @@ function hasAtomicStageSegment(dir) {
2495
2629
  return pathSegments(dir).some((segment) => STAGE_TMP_PATTERN.test(segment));
2496
2630
  }
2497
2631
  async function findGitBoundary(dir) {
2498
- let current = path6.resolve(dir);
2499
- const fsRoot = path6.parse(current).root;
2632
+ let current = path7.resolve(dir);
2633
+ const fsRoot = path7.parse(current).root;
2500
2634
  while (true) {
2501
2635
  if (await markerExists(current, ".git"))
2502
2636
  return current;
2503
2637
  if (current === fsRoot)
2504
2638
  return void 0;
2505
- current = path6.dirname(current);
2639
+ current = path7.dirname(current);
2506
2640
  }
2507
2641
  }
2508
2642
  async function isExcludedLspRoot(dir) {
2509
- const candidate = path6.resolve(dir);
2643
+ const candidate = path7.resolve(dir);
2510
2644
  if (hasFixtureConvention(candidate) || hasAtomicStageSegment(candidate))
2511
2645
  return true;
2512
2646
  const gitRoot = await findGitBoundary(candidate);
@@ -2518,9 +2652,9 @@ async function isExcludedLspRoot(dir) {
2518
2652
  }
2519
2653
  async function nearestNonExcludedFallbackRoot(candidate) {
2520
2654
  if (!await isExcludedLspRoot(candidate))
2521
- return path6.resolve(candidate);
2522
- let current = path6.dirname(path6.resolve(candidate));
2523
- const fsRoot = path6.parse(current).root;
2655
+ return path7.resolve(candidate);
2656
+ let current = path7.dirname(path7.resolve(candidate));
2657
+ const fsRoot = path7.parse(current).root;
2524
2658
  let nearestAllowed;
2525
2659
  while (true) {
2526
2660
  if (!await isExcludedLspRoot(current)) {
@@ -2532,7 +2666,7 @@ async function nearestNonExcludedFallbackRoot(candidate) {
2532
2666
  }
2533
2667
  if (current === fsRoot)
2534
2668
  break;
2535
- current = path6.dirname(current);
2669
+ current = path7.dirname(current);
2536
2670
  }
2537
2671
  return nearestAllowed ?? fsRoot;
2538
2672
  }
@@ -2573,7 +2707,7 @@ async function installEvidenceForLaunch(toolId, installed, attempt) {
2573
2707
  const confirmedManagedPath = await findManagedToolBinary(toolId);
2574
2708
  return {
2575
2709
  ...evidence,
2576
- binary: path6.basename(installed),
2710
+ binary: path7.basename(installed),
2577
2711
  ...confirmedManagedPath !== void 0 && pathsEqual(confirmedManagedPath, installed) && {
2578
2712
  source: "managed-dir"
2579
2713
  }
@@ -2626,7 +2760,7 @@ function markDirectLspCommandUnavailable(command) {
2626
2760
  directLspCommandUnavailableUntil.set(command, Date.now() + DIRECT_LSP_NEGATIVE_TTL_MS);
2627
2761
  directLspCommandSkipLoggedUntil.delete(command);
2628
2762
  }
2629
- var PI_LENS_BIN_DIR2 = path6.join(getGlobalPiLensDir(), "bin");
2763
+ var PI_LENS_BIN_DIR2 = path7.join(getGlobalPiLensDir(), "bin");
2630
2764
  async function resolveAndLaunch(spec, allowInstall) {
2631
2765
  const generation = lspLaunchAvailabilityGeneration.capture();
2632
2766
  const toolLabel = spec.managedToolId ?? spec.candidates[spec.candidates.length - 1] ?? "unknown";
@@ -2691,7 +2825,7 @@ async function resolveAndLaunch(spec, allowInstall) {
2691
2825
  producer: "lsp-launch",
2692
2826
  classifiedBy: "probe",
2693
2827
  evidence: {
2694
- binary: path6.basename(managedCandidate),
2828
+ binary: path7.basename(managedCandidate),
2695
2829
  source: "managed-dir",
2696
2830
  correctsLatchedRow: false
2697
2831
  }
@@ -2722,7 +2856,7 @@ async function resolveAndLaunch(spec, allowInstall) {
2722
2856
  logSessionStart(`lsp launch candidate failed tool=${toolLabel} idx=${failure.index} command=${failure.command} error=${failure.message}`);
2723
2857
  trackRuntimeFailure(failure.err);
2724
2858
  }
2725
- const hasOnlyRepairableCandidateFailures = candidateFailures.every((failure) => hasSpawnFailureKind(failure.err, "tool-not-found"));
2859
+ const hasOnlyRepairableCandidateFailures = candidateFailures.every((failure) => hasSpawnFailureKind(failure.err, "tool-not-found") || hasSpawnFailureKind(failure.err, "trust-refused"));
2726
2860
  if (!hasOnlyRepairableCandidateFailures) {
2727
2861
  if (lastRuntimeFailure)
2728
2862
  throw lastRuntimeFailure;
@@ -2741,14 +2875,19 @@ async function resolveAndLaunch(spec, allowInstall) {
2741
2875
  globalInstallDisabled: isLspInstallDisabled()
2742
2876
  }
2743
2877
  });
2878
+ if (lastRuntimeFailure)
2879
+ throw lastRuntimeFailure;
2744
2880
  return void 0;
2745
2881
  }
2882
+ const isRefusal = (failure) => hasSpawnFailureKind(failure.err, "trust-refused");
2883
+ const trustRefused = candidateFailures.some(isRefusal);
2884
+ const storeOnly = trustRefused && (candidateFailures.some((failure) => isRefusal(failure) && isSimpleCommand(failure.command)) || commandSearchReachesProject(spec.cwd));
2746
2885
  if (spec.managedToolId) {
2747
2886
  const failedCandidate = candidateFailures.at(-1);
2748
2887
  const classifiedFailure = classifyProbeFailure({
2749
2888
  error: failedCandidate?.err instanceof Error ? failedCandidate.err : void 0,
2750
2889
  spawnFailure: {
2751
- kind: hasSpawnFailureKind(failedCandidate?.err, "tool-not-found") ? "tool-not-found" : void 0
2890
+ kind: ["tool-not-found", "trust-refused"].find((kind) => hasSpawnFailureKind(failedCandidate?.err, kind))
2752
2891
  }
2753
2892
  }, { command: failedCandidate?.command });
2754
2893
  logAvailabilityDecision({
@@ -2762,10 +2901,18 @@ async function resolveAndLaunch(spec, allowInstall) {
2762
2901
  producer: "lsp-launch",
2763
2902
  evidence: classifiedFailure.evidence
2764
2903
  });
2765
- logSessionStart(`lsp launch ensure-tool start tool=${spec.managedToolId} cwd=${spec.cwd}`);
2904
+ if (storeOnly) {
2905
+ recordDegradationOnce({
2906
+ kind: "lsp-registry-decision",
2907
+ subject: `store-only-repair:${spec.managedToolId}`,
2908
+ reason: "LSP repair read only the pi-lens tool store: after a trust refusal a bare-name lookup reaches the project, so nothing was probed on PATH or installed",
2909
+ metadata: { field: "store-only-repair", tool: spec.managedToolId }
2910
+ });
2911
+ }
2912
+ logSessionStart(`lsp launch ensure-tool start tool=${spec.managedToolId} cwd=${spec.cwd} storeOnly=${storeOnly}`);
2766
2913
  const installStartedAt = Date.now();
2767
- const installed = await ensureTool(spec.managedToolId);
2768
- const installAttempt = captureInstallAttempt(spec.managedToolId);
2914
+ const installed = await (storeOnly ? getToolPath(spec.managedToolId, { storeOnly: true }) : ensureTool(spec.managedToolId));
2915
+ const installAttempt = storeOnly ? void 0 : captureInstallAttempt(spec.managedToolId);
2769
2916
  if (staleLaunch(generation, `${toolLabel}:ensureTool`)) {
2770
2917
  return void 0;
2771
2918
  }
@@ -2895,7 +3042,7 @@ async function resolveAndLaunch(spec, allowInstall) {
2895
3042
  }
2896
3043
  }
2897
3044
  }
2898
- const runtimeInstall = spec.runtimeInstall;
3045
+ const runtimeInstall = trustRefused ? void 0 : spec.runtimeInstall;
2899
3046
  const runtimeAvailable = runtimeInstall ? await isOnPath(runtimeInstall.runtimeCommand) : false;
2900
3047
  if (staleLaunch(generation, `${toolLabel}:runtimeAvailability`)) {
2901
3048
  return void 0;
@@ -2984,7 +3131,7 @@ async function resolveAndLaunchTreeBinary(spec, allowInstall) {
2984
3131
  return void 0;
2985
3132
  }
2986
3133
  const suffix = process.platform === "win32" ? ".exe" : "";
2987
- const binPath = path6.join(bundleDir, ...spec.binRelPath.split("/")) + suffix;
3134
+ const binPath = path7.join(bundleDir, ...spec.binRelPath.split("/")) + suffix;
2988
3135
  try {
2989
3136
  const proc = await launchLSP(binPath, spec.args, {
2990
3137
  cwd: spec.cwd,
@@ -2999,7 +3146,7 @@ async function resolveAndLaunchTreeBinary(spec, allowInstall) {
2999
3146
  }
3000
3147
  }
3001
3148
  function nodeBinLocalCandidates(root, baseName) {
3002
- const localBase = path6.join(root, "node_modules", ".bin", baseName);
3149
+ const localBase = path7.join(root, "node_modules", ".bin", baseName);
3003
3150
  if (process.platform === "win32") {
3004
3151
  return [`${localBase}.cmd`, `${localBase}.exe`];
3005
3152
  }
@@ -3009,7 +3156,7 @@ function nodeBinCandidates(root, baseName) {
3009
3156
  return [...nodeBinLocalCandidates(root, baseName), baseName];
3010
3157
  }
3011
3158
  function piAgentExtensionsRootKey(file) {
3012
- const dirKey = normalizeEphemeralMapKey(path6.dirname(path6.resolve(file)));
3159
+ const dirKey = normalizeEphemeralMapKey(path7.dirname(path7.resolve(file)));
3013
3160
  const marker = "/.pi/agent/extensions";
3014
3161
  const index = dirKey.indexOf(marker);
3015
3162
  if (index === -1)
@@ -3017,26 +3164,26 @@ function piAgentExtensionsRootKey(file) {
3017
3164
  return dirKey.slice(0, index + marker.length);
3018
3165
  }
3019
3166
  function IgnoreHomeRoot(primary) {
3020
- const homeKey = normalizeEphemeralMapKey(path6.resolve(os6.homedir()));
3167
+ const homeKey = normalizeEphemeralMapKey(path7.resolve(os7.homedir()));
3021
3168
  return withRootMarkers(async (file) => {
3022
3169
  const root = await primary(file);
3023
3170
  if (!root)
3024
3171
  return void 0;
3025
- return normalizeEphemeralMapKey(path6.resolve(root)) === homeKey ? void 0 : root;
3172
+ return normalizeEphemeralMapKey(path7.resolve(root)) === homeKey ? void 0 : root;
3026
3173
  }, primary.rootMarkers ?? []);
3027
3174
  }
3028
3175
  function rubyBinCandidates(baseName) {
3029
3176
  const candidates = [];
3030
- const home = os6.homedir();
3177
+ const home = os7.homedir();
3031
3178
  const isWin = process.platform === "win32";
3032
3179
  const ext = isWin ? ".bat" : "";
3033
- candidates.push(path6.join(home, ".local", "share", "mise", "installs", "ruby", "bin", `${baseName}${ext}`));
3034
- candidates.push(path6.join(home, ".asdf", "installs", "ruby", "bin", `${baseName}${ext}`));
3180
+ candidates.push(path7.join(home, ".local", "share", "mise", "installs", "ruby", "bin", `${baseName}${ext}`));
3181
+ candidates.push(path7.join(home, ".asdf", "installs", "ruby", "bin", `${baseName}${ext}`));
3035
3182
  if (isWin) {
3036
- const driveRoot = path6.parse(home).root;
3183
+ const driveRoot = path7.parse(home).root;
3037
3184
  for (const entry of getRubyVersionDirNamesSync(driveRoot)) {
3038
- candidates.push(path6.join(driveRoot, entry, "bin", `${baseName}.bat`));
3039
- candidates.push(path6.join(driveRoot, entry, "bin", baseName));
3185
+ candidates.push(path7.join(driveRoot, entry, "bin", `${baseName}.bat`));
3186
+ candidates.push(path7.join(driveRoot, entry, "bin", baseName));
3040
3187
  }
3041
3188
  }
3042
3189
  return candidates;
@@ -3084,8 +3231,8 @@ function createInteractiveServer(spec) {
3084
3231
  function PriorityRoot(markerGroups, excludePatterns, stopDir) {
3085
3232
  const resolvers = markerGroups.map((markers) => NearestRoot(markers, excludePatterns, stopDir));
3086
3233
  return withRootMarkers(async (file) => {
3087
- for (const resolve3 of resolvers) {
3088
- const root = await resolve3(file);
3234
+ for (const resolve4 of resolvers) {
3235
+ const root = await resolve4(file);
3089
3236
  if (root)
3090
3237
  return root;
3091
3238
  }
@@ -3093,7 +3240,7 @@ function PriorityRoot(markerGroups, excludePatterns, stopDir) {
3093
3240
  }, markerGroups.flat());
3094
3241
  }
3095
3242
  var FileDirRoot = async (file) => {
3096
- const candidate = path6.resolve(path6.dirname(file));
3243
+ const candidate = path7.resolve(path7.dirname(file));
3097
3244
  return nearestNonExcludedFallbackRoot(candidate);
3098
3245
  };
3099
3246
  function RootWithFallback(primary, fallback = FileDirRoot) {
@@ -3115,16 +3262,16 @@ function markerProbeDir(dir, pattern) {
3115
3262
  const normalized = toPosix(pattern);
3116
3263
  const slash = normalized.lastIndexOf("/");
3117
3264
  const parentPattern = slash >= 0 ? normalized.slice(0, slash) : "";
3118
- return parentPattern ? path6.join(dir, ...parentPattern.split("/").filter(Boolean)) : dir;
3265
+ return parentPattern ? path7.join(dir, ...parentPattern.split("/").filter(Boolean)) : dir;
3119
3266
  }
3120
3267
  async function markerExists(dir, pattern) {
3121
3268
  if (!pattern.includes("*")) {
3122
3269
  try {
3123
- await stat(path6.join(dir, pattern));
3270
+ await stat(path7.join(dir, pattern));
3124
3271
  return true;
3125
3272
  } catch (err) {
3126
3273
  if (isPermissionFsError(err)) {
3127
- logSessionStart(`lsp root marker skipped: permission error stat ${path6.join(dir, pattern)}`);
3274
+ logSessionStart(`lsp root marker skipped: permission error stat ${path7.join(dir, pattern)}`);
3128
3275
  }
3129
3276
  return false;
3130
3277
  }
@@ -3160,7 +3307,7 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
3160
3307
  subdirPatterns.push(pattern);
3161
3308
  }
3162
3309
  return withRootMarkers(async (file) => {
3163
- const startDir = path6.resolve(path6.dirname(file));
3310
+ const startDir = path7.resolve(path7.dirname(file));
3164
3311
  const dirKey = normalizeMapKey(startDir);
3165
3312
  const cached = cache.get(dirKey);
3166
3313
  if (cached !== void 0 && Date.now() - cached.walkedAt < FRESHNESS_CADENCE_MS && await bounded(dirMtimesStillFreshAsync(cached.probedDirs), rootMtimeBound("lsp-root-memo-freshness")) === true)
@@ -3172,10 +3319,10 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
3172
3319
  const walkedAt = Date.now();
3173
3320
  const promise = (async () => {
3174
3321
  let currentDir = startDir;
3175
- const fsRoot = path6.parse(currentDir).root;
3176
- const stop = stopDir ? path6.resolve(stopDir) : fsRoot;
3322
+ const fsRoot = path7.parse(currentDir).root;
3323
+ const stop = stopDir ? path7.resolve(stopDir) : fsRoot;
3177
3324
  while (true) {
3178
- if (stop !== fsRoot && currentDir.startsWith(stop + path6.sep) === false && currentDir !== stop) {
3325
+ if (stop !== fsRoot && currentDir.startsWith(stop + path7.sep) === false && currentDir !== stop) {
3179
3326
  break;
3180
3327
  }
3181
3328
  const stepDirs = [
@@ -3193,19 +3340,19 @@ function NearestRoot(includePatterns, excludePatterns, stopDir) {
3193
3340
  }
3194
3341
  }
3195
3342
  if (excluded) {
3196
- currentDir = path6.dirname(currentDir);
3343
+ currentDir = path7.dirname(currentDir);
3197
3344
  continue;
3198
3345
  }
3199
3346
  }
3200
3347
  for (const pattern of includePatterns) {
3201
- if (await markerExists(currentDir, pattern) && (pattern !== ".git" || currentDir !== os6.tmpdir()) && !await isExcludedLspRoot(currentDir)) {
3348
+ if (await markerExists(currentDir, pattern) && (pattern !== ".git" || currentDir !== os7.tmpdir()) && !await isExcludedLspRoot(currentDir)) {
3202
3349
  return enforceLspRootCeiling(currentDir, process.cwd(), file);
3203
3350
  }
3204
3351
  }
3205
3352
  if (currentDir === stop || currentDir === fsRoot) {
3206
3353
  break;
3207
3354
  }
3208
- currentDir = path6.dirname(currentDir);
3355
+ currentDir = path7.dirname(currentDir);
3209
3356
  }
3210
3357
  return void 0;
3211
3358
  })();
@@ -3246,18 +3393,18 @@ async function findAncestorFile(startDir, ...relativeSegments) {
3246
3393
  return findAncestorFileAmong(startDir, [relativeSegments]);
3247
3394
  }
3248
3395
  async function findAncestorFileAmong(startDir, candidateSegmentLists) {
3249
- const fs5 = await import("node:fs/promises");
3250
- let currentDir = path6.resolve(startDir);
3396
+ const fs6 = await import("node:fs/promises");
3397
+ let currentDir = path7.resolve(startDir);
3251
3398
  while (!isAtOrAboveHomeDir(currentDir)) {
3252
3399
  for (const segments of candidateSegmentLists) {
3253
- const candidate = path6.join(currentDir, ...segments);
3400
+ const candidate = path7.join(currentDir, ...segments);
3254
3401
  try {
3255
- await fs5.access(candidate);
3402
+ await fs6.access(candidate);
3256
3403
  return candidate;
3257
3404
  } catch {
3258
3405
  }
3259
3406
  }
3260
- const parent = path6.dirname(currentDir);
3407
+ const parent = path7.dirname(currentDir);
3261
3408
  if (parent === currentDir)
3262
3409
  break;
3263
3410
  currentDir = parent;
@@ -3273,19 +3420,21 @@ function resetClassicTsRepairGuard() {
3273
3420
  classicTsRepairState().attempted = false;
3274
3421
  }
3275
3422
  function typescriptDirsForTsc(tscPath) {
3276
- const binDir = path6.dirname(tscPath);
3423
+ const binDir = path7.dirname(tscPath);
3277
3424
  return [
3278
- path6.join(binDir, "..", "typescript"),
3279
- path6.join(binDir, "..", "..", "typescript")
3425
+ path7.join(binDir, "..", "typescript"),
3426
+ path7.join(binDir, "..", "..", "typescript"),
3427
+ // Windows npm-global: `<prefix>/tsc.cmd` beside `<prefix>/node_modules`.
3428
+ path7.join(binDir, "node_modules", "typescript")
3280
3429
  ];
3281
3430
  }
3282
3431
  async function typescriptVersionForTsc(tscPath) {
3283
- if (!path6.isAbsolute(tscPath))
3432
+ if (!path7.isAbsolute(tscPath))
3284
3433
  return void 0;
3285
3434
  for (const dir of typescriptDirsForTsc(tscPath)) {
3286
3435
  let manifest;
3287
3436
  try {
3288
- manifest = await readFile(path6.join(dir, "package.json"), "utf8");
3437
+ manifest = await readFile(path7.join(dir, "package.json"), "utf8");
3289
3438
  } catch {
3290
3439
  continue;
3291
3440
  }
@@ -3307,15 +3456,28 @@ async function typescriptVersionForTsc(tscPath) {
3307
3456
  }
3308
3457
  return void 0;
3309
3458
  }
3459
+ async function hasTypescriptPackageDir(tscPath) {
3460
+ for (const dir of typescriptDirsForTsc(tscPath)) {
3461
+ if ((await stat(dir).catch(() => void 0))?.isDirectory())
3462
+ return true;
3463
+ }
3464
+ return false;
3465
+ }
3466
+ async function absoluteCommandPath(command) {
3467
+ if (!command || path7.isAbsolute(command))
3468
+ return command;
3469
+ const found = await findCommandAsync(command);
3470
+ return found ? realpath(found).catch(() => void 0) : void 0;
3471
+ }
3310
3472
  async function findTsserverPath(root, allowInstall, options) {
3311
- const fs5 = await import("node:fs/promises");
3312
- const projectRoots = await Promise.all([root, options?.sessionRoot ?? process.cwd(), process.cwd()].map((dir) => fs5.realpath(dir).catch(() => path6.resolve(dir))));
3473
+ const fs6 = await import("node:fs/promises");
3474
+ const projectRoots = await Promise.all([root, options?.sessionRoot ?? process.cwd(), process.cwd()].map((dir) => fs6.realpath(dir).catch(() => path7.resolve(dir))));
3313
3475
  const admitCandidate = async (candidate, projectSupplied) => {
3314
3476
  try {
3315
- const real = await fs5.realpath(candidate);
3316
- if (path6.basename(real) !== "tsserver.js" || !(await fs5.stat(real)).isFile())
3477
+ const real = await fs6.realpath(candidate);
3478
+ if (path7.basename(real) !== "tsserver.js" || !(await fs6.stat(real)).isFile())
3317
3479
  return void 0;
3318
- const manifest = JSON.parse(await fs5.readFile(path6.join(path6.dirname(real), "..", "package.json"), "utf8"));
3480
+ const manifest = JSON.parse(await fs6.readFile(path7.join(path7.dirname(real), "..", "package.json"), "utf8"));
3319
3481
  if (typeof manifest?.version !== "string" || !manifest.version)
3320
3482
  return void 0;
3321
3483
  return admitProjectSuppliedTsserver(real, options?.sessionRoot ?? process.cwd(), projectSupplied || projectRoots.some((dir) => isUnderDir(real, dir)), options?.projectCodeAllowed);
@@ -3329,19 +3491,19 @@ async function findTsserverPath(root, allowInstall, options) {
3329
3491
  if (admittedAncestor)
3330
3492
  return admittedAncestor;
3331
3493
  }
3332
- const cwdCandidate = path6.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
3494
+ const cwdCandidate = path7.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
3333
3495
  try {
3334
- await fs5.access(cwdCandidate);
3496
+ await fs6.access(cwdCandidate);
3335
3497
  const admittedCwd = await admitCandidate(cwdCandidate, false);
3336
3498
  if (admittedCwd)
3337
3499
  return admittedCwd;
3338
3500
  } catch {
3339
3501
  }
3340
3502
  const tsserverForTsc = async (tscPath) => {
3341
- if (!tscPath || !path6.isAbsolute(tscPath))
3503
+ if (!tscPath)
3342
3504
  return void 0;
3343
3505
  for (const dir of typescriptDirsForTsc(tscPath)) {
3344
- const candidate = path6.join(dir, "lib", "tsserver.js");
3506
+ const candidate = path7.join(dir, "lib", "tsserver.js");
3345
3507
  try {
3346
3508
  const admitted = await admitCandidate(candidate, false);
3347
3509
  if (admitted)
@@ -3352,29 +3514,28 @@ async function findTsserverPath(root, allowInstall, options) {
3352
3514
  return void 0;
3353
3515
  };
3354
3516
  const installAllowed = canInstall(allowInstall);
3355
- const discoveredTsc = await ensureTool("typescript", {
3356
- allowInstall: installAllowed
3357
- });
3517
+ const discoveredTsc = await absoluteCommandPath(await ensureTool("typescript", { allowInstall: installAllowed }));
3358
3518
  const discoveredTsserver = await tsserverForTsc(discoveredTsc);
3359
3519
  if (discoveredTsserver || !discoveredTsc || !installAllowed || classicTsRepairState().attempted) {
3360
3520
  return discoveredTsserver;
3361
3521
  }
3362
3522
  const discoveredVersion = await typescriptVersionForTsc(discoveredTsc);
3363
- if (!discoveredVersion || discoveredVersion.major < 7)
3523
+ const shim = !await hasTypescriptPackageDir(discoveredTsc);
3524
+ if (!shim && (!discoveredVersion || discoveredVersion.major < 7))
3364
3525
  return void 0;
3365
3526
  classicTsRepairState().attempted = true;
3366
- logSessionStart(`lsp typescript: managed compiler resolved to TypeScript ${discoveredVersion.version}, which ships no tsserver.js; reinstalling pinned classic fallback`);
3367
- const repairedTsc = await ensureTool("typescript", {
3527
+ logSessionStart(shim ? `lsp typescript: ${discoveredTsc} has no TypeScript package beside it; installing pinned classic fallback` : `lsp typescript: managed compiler resolved to TypeScript ${discoveredVersion?.version}, which ships no tsserver.js; reinstalling pinned classic fallback`);
3528
+ const repairedTsc = await absoluteCommandPath(await ensureTool("typescript", {
3368
3529
  allowInstall: true,
3369
3530
  forceReinstall: true
3370
- });
3531
+ }));
3371
3532
  return tsserverForTsc(repairedTsc);
3372
3533
  }
3373
3534
  async function findNativeTypeScriptLsp(root) {
3374
- let currentDir = path6.resolve(root);
3535
+ let currentDir = path7.resolve(root);
3375
3536
  while (!isAtOrAboveHomeDir(currentDir)) {
3376
- const typescriptDir = path6.join(currentDir, "node_modules", "typescript");
3377
- const packageJsonPath = path6.join(typescriptDir, "package.json");
3537
+ const typescriptDir = path7.join(currentDir, "node_modules", "typescript");
3538
+ const packageJsonPath = path7.join(typescriptDir, "package.json");
3378
3539
  let packageJsonText;
3379
3540
  try {
3380
3541
  packageJsonText = await readFile(packageJsonPath, "utf8");
@@ -3388,7 +3549,7 @@ async function findNativeTypeScriptLsp(root) {
3388
3549
  return void 0;
3389
3550
  } catch {
3390
3551
  }
3391
- const parent = path6.dirname(currentDir);
3552
+ const parent = path7.dirname(currentDir);
3392
3553
  if (parent === currentDir)
3393
3554
  return void 0;
3394
3555
  currentDir = parent;
@@ -3408,7 +3569,7 @@ async function findNativeTypeScriptLsp(root) {
3408
3569
  const major = /^\d+$/.test(majorText) ? Number(majorText) : Number.NaN;
3409
3570
  if (!Number.isFinite(major) || major < 7)
3410
3571
  return void 0;
3411
- const localTsc = path6.join(currentDir, "node_modules", ".bin", "tsc");
3572
+ const localTsc = path7.join(currentDir, "node_modules", ".bin", "tsc");
3412
3573
  const candidates = process.platform === "win32" ? [`${localTsc}.cmd`, `${localTsc}.exe`, localTsc] : [localTsc];
3413
3574
  for (const command of candidates) {
3414
3575
  try {
@@ -3422,29 +3583,29 @@ async function findNativeTypeScriptLsp(root) {
3422
3583
  return void 0;
3423
3584
  }
3424
3585
  function dotnetToolCandidates(tool) {
3425
- const home = os6.homedir();
3586
+ const home = os7.homedir();
3426
3587
  return [
3427
- path6.join(PI_LENS_BIN_DIR2, `${tool}.exe`),
3428
- path6.join(PI_LENS_BIN_DIR2, tool),
3429
- path6.join(home, ".dotnet", "tools", `${tool}.exe`),
3430
- path6.join(home, ".dotnet", "tools", tool),
3588
+ path7.join(PI_LENS_BIN_DIR2, `${tool}.exe`),
3589
+ path7.join(PI_LENS_BIN_DIR2, tool),
3590
+ path7.join(home, ".dotnet", "tools", `${tool}.exe`),
3591
+ path7.join(home, ".dotnet", "tools", tool),
3431
3592
  tool
3432
3593
  ].filter(Boolean);
3433
3594
  }
3434
3595
  function binExeVariants(dir, tool) {
3435
- return process.platform === "win32" ? [path6.join(dir, `${tool}.exe`), path6.join(dir, tool)] : [path6.join(dir, tool)];
3596
+ return process.platform === "win32" ? [path7.join(dir, `${tool}.exe`), path7.join(dir, tool)] : [path7.join(dir, tool)];
3436
3597
  }
3437
3598
  function goBinCandidates(tool) {
3438
- const gopath = process.env.GOPATH?.split(path6.delimiter)[0] || path6.join(os6.homedir(), "go");
3439
- return [tool, ...binExeVariants(path6.join(gopath, "bin"), tool)];
3599
+ const gopath = process.env.GOPATH?.split(path7.delimiter)[0] || path7.join(os7.homedir(), "go");
3600
+ return [tool, ...binExeVariants(path7.join(gopath, "bin"), tool)];
3440
3601
  }
3441
3602
  function cargoBinCandidates(tool) {
3442
- const cargoHome = process.env.CARGO_HOME || path6.join(os6.homedir(), ".cargo");
3443
- return [tool, ...binExeVariants(path6.join(cargoHome, "bin"), tool)];
3603
+ const cargoHome = process.env.CARGO_HOME || path7.join(os7.homedir(), ".cargo");
3604
+ return [tool, ...binExeVariants(path7.join(cargoHome, "bin"), tool)];
3444
3605
  }
3445
3606
  async function tryGemInstall(gem) {
3446
- const { join: join3 } = await import("node:path");
3447
- const binDir = join3(getGlobalPiLensDir(), "bin");
3607
+ const { join: join4 } = await import("node:path");
3608
+ const binDir = join4(getGlobalPiLensDir(), "bin");
3448
3609
  const { mkdir } = await import("node:fs/promises");
3449
3610
  await mkdir(binDir, { recursive: true });
3450
3611
  const result = await safeSpawnAsync("gem", ["install", gem, "--bindir", binDir, "--no-document"], { timeout: 18e4, ignoreAmbientSignal: true });
@@ -3478,7 +3639,7 @@ var TS_TOOLING_MARKERS = [
3478
3639
  ];
3479
3640
  var TS_FAMILY_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
3480
3641
  function isTsFamilyFile(file) {
3481
- return TS_FAMILY_EXTENSIONS.has(path6.extname(file).toLowerCase());
3642
+ return TS_FAMILY_EXTENSIONS.has(path7.extname(file).toLowerCase());
3482
3643
  }
3483
3644
  function tsConfigMarkersForFile(file) {
3484
3645
  return isTsFamilyFile(file) ? ["tsconfig.json"] : TS_CONFIG_MARKERS;
@@ -3499,40 +3660,40 @@ async function findTypeScriptProjectRoot(file) {
3499
3660
  return isSameOrWithin(toolingRoot, configRoot) ? configRoot : toolingRoot;
3500
3661
  }
3501
3662
  async function findExtensionBoundedRoot(file, extensionRootKey) {
3502
- const startDir = path6.resolve(path6.dirname(file));
3663
+ const startDir = path7.resolve(path7.dirname(file));
3503
3664
  let currentDir = startDir;
3504
3665
  const configMarkers = tsConfigMarkersForFile(file);
3505
3666
  while (true) {
3506
3667
  for (const pattern of configMarkers) {
3507
3668
  try {
3508
- await stat(path6.join(currentDir, pattern));
3669
+ await stat(path7.join(currentDir, pattern));
3509
3670
  return currentDir;
3510
3671
  } catch {
3511
3672
  }
3512
3673
  }
3513
3674
  for (const pattern of TS_TOOLING_MARKERS) {
3514
3675
  try {
3515
- await stat(path6.join(currentDir, pattern));
3676
+ await stat(path7.join(currentDir, pattern));
3516
3677
  return currentDir;
3517
3678
  } catch {
3518
3679
  }
3519
3680
  }
3520
- const currentKey = normalizeEphemeralMapKey(path6.resolve(currentDir));
3681
+ const currentKey = normalizeEphemeralMapKey(path7.resolve(currentDir));
3521
3682
  if (currentKey === extensionRootKey)
3522
3683
  return void 0;
3523
- const parent = path6.dirname(currentDir);
3684
+ const parent = path7.dirname(currentDir);
3524
3685
  if (parent === currentDir)
3525
3686
  return void 0;
3526
3687
  currentDir = parent;
3527
3688
  }
3528
3689
  }
3529
3690
  async function hasAgentLevelProjectMarker(extensionRootKey) {
3530
- const agentDir = path6.dirname(extensionRootKey);
3691
+ const agentDir = path7.dirname(extensionRootKey);
3531
3692
  if (!agentDir || agentDir === extensionRootKey)
3532
3693
  return false;
3533
3694
  for (const pattern of [...TS_CONFIG_MARKERS, ...TS_TOOLING_MARKERS]) {
3534
3695
  try {
3535
- await stat(path6.join(agentDir, pattern));
3696
+ await stat(path7.join(agentDir, pattern));
3536
3697
  return true;
3537
3698
  } catch {
3538
3699
  }
@@ -3710,13 +3871,13 @@ var PythonServer = {
3710
3871
  if (!pyrightPath)
3711
3872
  return void 0;
3712
3873
  source = "managed";
3713
- const binDir = path6.dirname(pyrightPath);
3874
+ const binDir = path7.dirname(pyrightPath);
3714
3875
  const isWindows2 = process.platform === "win32";
3715
3876
  const managedCandidates = isWindows2 ? [
3716
- path6.join(binDir, "pyright-langserver.cmd"),
3717
- path6.join(binDir, "pyright-langserver"),
3877
+ path7.join(binDir, "pyright-langserver.cmd"),
3878
+ path7.join(binDir, "pyright-langserver"),
3718
3879
  "pyright-langserver"
3719
- ] : [path6.join(binDir, "pyright-langserver"), "pyright-langserver"];
3880
+ ] : [path7.join(binDir, "pyright-langserver"), "pyright-langserver"];
3720
3881
  const resolved = await resolveAndLaunch({ candidates: managedCandidates, args: ["--stdio"], cwd: root, env }, false);
3721
3882
  if (!resolved)
3722
3883
  return void 0;
@@ -3785,7 +3946,7 @@ var GoServer = {
3785
3946
  }
3786
3947
  };
3787
3948
  function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
3788
- const relativePath = path6.relative(parentDir, childDir).split(path6.sep).join("/");
3949
+ const relativePath = path7.relative(parentDir, childDir).split(path7.sep).join("/");
3789
3950
  if (relativePath === "" || relativePath.startsWith(".."))
3790
3951
  return false;
3791
3952
  const matches = (pattern) => matchesWorkspaceMemberPattern(pattern, relativePath, CARGO_WORKSPACE_MEMBER_DIALECT, () => recordDegradationOnce({
@@ -3799,7 +3960,7 @@ function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
3799
3960
  return readCargoWorkspaceMembers(workspaceContent).some(matches);
3800
3961
  }
3801
3962
  function sessionHoistCeiling(sessionCwd, file) {
3802
- return isSameOrWithin(path6.resolve(sessionCwd), path6.resolve(file)) ? path6.resolve(sessionCwd) : void 0;
3963
+ return isSameOrWithin(path7.resolve(sessionCwd), path7.resolve(file)) ? path7.resolve(sessionCwd) : void 0;
3803
3964
  }
3804
3965
  function RustWorkspaceRoot() {
3805
3966
  const crateRoot = createRootDetector(["Cargo.toml", "Cargo.lock"]);
@@ -3810,14 +3971,14 @@ function RustWorkspaceRoot() {
3810
3971
  const sessionCwd = process.cwd();
3811
3972
  const stop = sessionHoistCeiling(sessionCwd, file);
3812
3973
  let current = root;
3813
- const fsRoot = path6.parse(current).root;
3974
+ const fsRoot = path7.parse(current).root;
3814
3975
  while (true) {
3815
3976
  if (stop !== void 0 && current === stop)
3816
3977
  break;
3817
- const parent = path6.dirname(current);
3978
+ const parent = path7.dirname(current);
3818
3979
  if (parent === current || parent === fsRoot)
3819
3980
  break;
3820
- const parentCargoPath = path6.join(parent, "Cargo.toml");
3981
+ const parentCargoPath = path7.join(parent, "Cargo.toml");
3821
3982
  const parentCargoContent = await readTextFileOrUndefined(parentCargoPath);
3822
3983
  if (parentCargoContent !== void 0 && hasCargoWorkspaceTable(parentCargoContent)) {
3823
3984
  if (cargoWorkspaceDeclaresMember(parentCargoContent, parent, root)) {
@@ -3845,9 +4006,9 @@ async function declaresMavenModule(parentDir, parentPomPath, childDir) {
3845
4006
  const modulesBlock = content.match(/<modules>([\s\S]*?)<\/modules>/);
3846
4007
  if (!modulesBlock)
3847
4008
  return false;
3848
- const resolvedChild = path6.resolve(childDir);
4009
+ const resolvedChild = path7.resolve(childDir);
3849
4010
  for (const match of modulesBlock[1].matchAll(/<module>\s*([^<]+?)\s*<\/module>/g)) {
3850
- if (pathsEqual(path6.resolve(parentDir, match[1]), resolvedChild))
4011
+ if (pathsEqual(path7.resolve(parentDir, match[1]), resolvedChild))
3851
4012
  return true;
3852
4013
  }
3853
4014
  return false;
@@ -3871,15 +4032,15 @@ function JavaWorkspaceRoot() {
3871
4032
  const stop = sessionHoistCeiling(sessionCwd, file);
3872
4033
  let current = root;
3873
4034
  let lastHop = root;
3874
- const fsRoot = path6.parse(current).root;
4035
+ const fsRoot = path7.parse(current).root;
3875
4036
  while (true) {
3876
4037
  if (stop !== void 0 && current === stop)
3877
4038
  break;
3878
- const parent = path6.dirname(current);
4039
+ const parent = path7.dirname(current);
3879
4040
  if (parent === current || parent === fsRoot)
3880
4041
  break;
3881
4042
  if (await markerExists(parent, "pom.xml")) {
3882
- const parentPom = path6.join(parent, "pom.xml");
4043
+ const parentPom = path7.join(parent, "pom.xml");
3883
4044
  if (!await declaresMavenModule(parent, parentPom, lastHop))
3884
4045
  break;
3885
4046
  current = parent;
@@ -3985,17 +4146,17 @@ var PHPServer = {
3985
4146
  return {
3986
4147
  ...result,
3987
4148
  initialization: {
3988
- storagePath: path6.join(getGlobalPiLensDir(), "intelephense")
4149
+ storagePath: path7.join(getGlobalPiLensDir(), "intelephense")
3989
4150
  }
3990
4151
  };
3991
4152
  }
3992
4153
  };
3993
4154
  function buildPsesArgs(bundleDir) {
3994
- const script = path6.join(bundleDir, "PowerShellEditorServices", "Start-EditorServices.ps1");
3995
- const sessionDir = path6.join(getGlobalPiLensDir(), "pses", `${process.pid}-${Date.now()}`);
4155
+ const script = path7.join(bundleDir, "PowerShellEditorServices", "Start-EditorServices.ps1");
4156
+ const sessionDir = path7.join(getGlobalPiLensDir(), "pses", `${process.pid}-${Date.now()}`);
3996
4157
  mkdirSync2(sessionDir, { recursive: true });
3997
- const logPath = path6.join(sessionDir, "pses.log");
3998
- const sessionDetailsPath = path6.join(sessionDir, "session.json");
4158
+ const logPath = path7.join(sessionDir, "pses.log");
4159
+ const sessionDetailsPath = path7.join(sessionDir, "session.json");
3999
4160
  return [
4000
4161
  "-NoLogo",
4001
4162
  "-NoProfile",
@@ -4653,8 +4814,8 @@ var VueServer = {
4653
4814
  initializeTimeoutMs: 3e4,
4654
4815
  async spawn(root, options) {
4655
4816
  const tsserverPath = await findTsserverPath(root, options?.allowInstall, options);
4656
- const hasPackageJson = existsSync3(path6.join(root, "package.json"));
4657
- const hasNodeModules = existsSync3(path6.join(root, "node_modules"));
4817
+ const hasPackageJson = existsSync3(path7.join(root, "package.json"));
4818
+ const hasNodeModules = existsSync3(path7.join(root, "node_modules"));
4658
4819
  if (hasPackageJson && !hasNodeModules) {
4659
4820
  logSessionStart(`lsp vue: node_modules missing in ${root} \u2014 Vue navigation may be limited. Run: npm install (or pnpm/yarn install) in this project.`);
4660
4821
  }
@@ -4669,7 +4830,7 @@ var VueServer = {
4669
4830
  return {
4670
4831
  process: proc.process,
4671
4832
  source: proc.source,
4672
- initialization: tsserverPath ? { typescript: { tsdk: path6.dirname(tsserverPath) } } : void 0
4833
+ initialization: tsserverPath ? { typescript: { tsdk: path7.dirname(tsserverPath) } } : void 0
4673
4834
  };
4674
4835
  }
4675
4836
  };
@@ -4711,7 +4872,7 @@ var SvelteServer = {
4711
4872
  return {
4712
4873
  process: proc.process,
4713
4874
  source: proc.source,
4714
- initialization: tsserverPath ? { typescript: { tsdk: path6.dirname(tsserverPath) } } : void 0
4875
+ initialization: tsserverPath ? { typescript: { tsdk: path7.dirname(tsserverPath) } } : void 0
4715
4876
  };
4716
4877
  }
4717
4878
  };
@@ -5006,7 +5167,7 @@ function getServerById(id) {
5006
5167
  }
5007
5168
 
5008
5169
  // dist/clients/lsp/session-roots.js
5009
- import path7 from "node:path";
5170
+ import path8 from "node:path";
5010
5171
  var SESSION_ROOT_CAP = 128;
5011
5172
  var sessionRoots = new BoundedFifoMap(SESSION_ROOT_CAP);
5012
5173
  function noteEvictedRoots(evicted) {
@@ -5021,28 +5182,57 @@ function noteEvictedRoots(evicted) {
5021
5182
  return roots;
5022
5183
  }
5023
5184
  function registerSessionRoot(cwd) {
5024
- const root = path7.resolve(cwd);
5185
+ const root = path8.resolve(cwd);
5025
5186
  if (sessionRoots.has(root))
5026
5187
  return [];
5027
5188
  return noteEvictedRoots(sessionRoots.set(root, void 0));
5028
5189
  }
5029
5190
  function setSessionRootConfig(cwd, config) {
5030
- return noteEvictedRoots(sessionRoots.set(path7.resolve(cwd), config));
5191
+ return noteEvictedRoots(sessionRoots.set(path8.resolve(cwd), config));
5192
+ }
5193
+ function isSameOrChildPath(filePath, candidateRoot) {
5194
+ if (filePath === candidateRoot)
5195
+ return true;
5196
+ return filePath.startsWith(`${candidateRoot}${path8.sep}`);
5031
5197
  }
5032
- function sessionRootConfigEntries() {
5033
- return sessionRoots;
5198
+ function sessionRootConfigForFile(filePath) {
5199
+ const resolvedFilePath = path8.resolve(filePath);
5200
+ let bestMatch;
5201
+ for (const [root, config] of sessionRoots) {
5202
+ if (config === void 0)
5203
+ continue;
5204
+ if (!isSameOrChildPath(resolvedFilePath, root))
5205
+ continue;
5206
+ if (!bestMatch || root.length > bestMatch.root.length) {
5207
+ bestMatch = { root, config };
5208
+ }
5209
+ }
5210
+ return bestMatch?.config;
5211
+ }
5212
+ function disabledPrimaries(filePath) {
5213
+ const config = sessionRootConfigForFile(filePath);
5214
+ if (!config || config.disabledServerIds.size === 0)
5215
+ return { disabledServerIds: [], fallbackServerIds: [] };
5216
+ const { ext, base } = fileMatchKeys(filePath);
5217
+ const off = config.disabledServerIds;
5218
+ const primaries = registeredServers(config, LSP_SERVERS).filter((server) => !isAuxiliary(server) && fileMatchReason(server, filePath, ext, base) === "selected");
5219
+ const ownerOff = (server) => server.fallbackFor === void 0 || off.has(server.fallbackFor);
5220
+ return {
5221
+ disabledServerIds: primaries.filter((server) => off.has(server.id) && ownerOff(server)).map((server) => server.id),
5222
+ fallbackServerIds: primaries.filter((server) => server.fallbackFor !== void 0 && off.has(server.fallbackFor)).map((server) => server.id)
5223
+ };
5034
5224
  }
5035
5225
  function isSessionRootRegistered(cwd) {
5036
- return sessionRoots.has(path7.resolve(cwd));
5226
+ return sessionRoots.has(path8.resolve(cwd));
5037
5227
  }
5038
5228
  function shouldInitializeSessionRoot(cwd, readyRoots) {
5039
- const normalized = path7.resolve(cwd);
5229
+ const normalized = path8.resolve(cwd);
5040
5230
  return !readyRoots.has(normalized) || !isSessionRootRegistered(normalized);
5041
5231
  }
5042
5232
  function isOutsideAllSessionRoots(filePath) {
5043
5233
  if (sessionRoots.size === 0)
5044
5234
  return false;
5045
- const resolved = path7.resolve(filePath);
5235
+ const resolved = path8.resolve(filePath);
5046
5236
  for (const root of sessionRoots.keys()) {
5047
5237
  if (isSameOrWithin(root, resolved))
5048
5238
  return false;
@@ -5057,12 +5247,18 @@ export {
5057
5247
  findLocalTyposConfig,
5058
5248
  getAstGrepRuleSources,
5059
5249
  getAstGrepRuleFingerprint,
5060
- isAuxiliary,
5061
- serverTraits,
5062
5250
  registerRunnerId,
5063
5251
  withLspProjectCodePermission,
5064
5252
  refuseUntrustedLspExecution,
5253
+ resolveAnalysisRootPath,
5254
+ resolveAnalysisRoot,
5255
+ canWriteAnalysisRoot,
5065
5256
  BUILTIN_SERVER_RUNNER_COVERS,
5257
+ isAuxiliary,
5258
+ serverTraits,
5259
+ getStrategy,
5260
+ classifyServerWaitTier,
5261
+ classifyCascadeWaitTier,
5066
5262
  describeRootFallback,
5067
5263
  resolveLspServerCwd,
5068
5264
  resetLSPCaseSensitivityState,
@@ -5075,6 +5271,7 @@ export {
5075
5271
  resetClassicTsRepairGuard,
5076
5272
  LSP_SERVERS,
5077
5273
  getServerById,
5274
+ disabledPrimaries,
5078
5275
  shouldInitializeSessionRoot,
5079
5276
  isOutsideAllSessionRoots,
5080
5277
  getSessionRootsForTelemetry,
@@ -5087,8 +5284,5 @@ export {
5087
5284
  getServersForFileWithConfig,
5088
5285
  primaryServerId,
5089
5286
  resolveLspCwdForFile,
5090
- getServerInitOverride,
5091
- getStrategy,
5092
- classifyServerWaitTier,
5093
- classifyCascadeWaitTier
5287
+ getServerInitOverride
5094
5288
  };