pi-lens 4.3.0 → 4.4.1

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 (659) hide show
  1. package/CHANGELOG.md +1159 -0
  2. package/README.md +116 -25
  3. package/config/dependency-cruiser-eager-allowlist.json +6 -2
  4. package/dist/chunk-2BEMIWEW.js +51 -0
  5. package/dist/chunk-2EECBNC5.js +89 -0
  6. package/dist/chunk-2FQ62SPM.js +83 -0
  7. package/dist/chunk-2UMYMRI3.js +118 -0
  8. package/dist/chunk-34OCDWOS.js +48 -0
  9. package/dist/chunk-3FERWMCH.js +144 -0
  10. package/dist/chunk-3JL6IFD7.js +97 -0
  11. package/dist/chunk-3JSEWUWI.js +327 -0
  12. package/dist/chunk-3MED75HT.js +37 -0
  13. package/dist/chunk-3MRXIJ6I.js +79 -0
  14. package/dist/chunk-3P4PF2BX.js +2250 -0
  15. package/dist/chunk-3PNFWVHT.js +201 -0
  16. package/dist/chunk-3TTPKMEF.js +162 -0
  17. package/dist/chunk-5JHXUETW.js +326 -0
  18. package/dist/chunk-5W3R6LFT.js +24 -0
  19. package/dist/chunk-64LU2OVH.js +341 -0
  20. package/dist/chunk-6B5I6ICN.js +44 -0
  21. package/dist/chunk-6P54TMWL.js +274 -0
  22. package/dist/chunk-6UYA642J.js +145 -0
  23. package/dist/chunk-6XX5HCLK.js +398 -0
  24. package/dist/chunk-7AXO3ZDF.js +30 -0
  25. package/dist/chunk-7BX5DWSA.js +1907 -0
  26. package/dist/chunk-7ELY2CSM.js +902 -0
  27. package/dist/chunk-7M4T5N3V.js +38 -0
  28. package/dist/chunk-7U2IDNXN.js +37 -0
  29. package/dist/chunk-ABBOA7UD.js +157 -0
  30. package/dist/chunk-AFZYMAIV.js +756 -0
  31. package/dist/chunk-ALQRYH6O.js +112 -0
  32. package/dist/chunk-AXKPQ6ZI.js +1610 -0
  33. package/dist/chunk-BHWHXMQ2.js +62 -0
  34. package/dist/chunk-BJF25SUG.js +22 -0
  35. package/dist/chunk-BNKLYOOO.js +18 -0
  36. package/dist/chunk-BW5KFIQC.js +59 -0
  37. package/dist/chunk-CBD2B5CP.js +599 -0
  38. package/dist/chunk-CFYISHUW.js +42 -0
  39. package/dist/chunk-CMU4RMJB.js +30 -0
  40. package/dist/chunk-CULQYFLU.js +14 -0
  41. package/dist/chunk-D65GOCJB.js +289 -0
  42. package/dist/chunk-D6YF5HKX.js +59 -0
  43. package/dist/chunk-DMS52BEP.js +342 -0
  44. package/dist/chunk-ELVWJYRV.js +211 -0
  45. package/dist/chunk-EUT3B22Y.js +27 -0
  46. package/dist/chunk-F5VLZHUW.js +146 -0
  47. package/dist/chunk-FC4RP4I2.js +12 -0
  48. package/dist/chunk-FGALODG3.js +55 -0
  49. package/dist/chunk-G3RLJYA2.js +2358 -0
  50. package/dist/chunk-GAYGGICG.js +32 -0
  51. package/dist/chunk-H2W2AXD3.js +419 -0
  52. package/dist/chunk-H5EV3VMF.js +114 -0
  53. package/dist/chunk-HREZQCT3.js +106 -0
  54. package/dist/chunk-HSIRMK2D.js +517 -0
  55. package/dist/chunk-IMSQR4WO.js +184 -0
  56. package/dist/chunk-IQZXOZ7S.js +4816 -0
  57. package/dist/chunk-IR6XFA5P.js +37 -0
  58. package/dist/chunk-IUKPRZER.js +78 -0
  59. package/dist/chunk-IUR76K6A.js +262 -0
  60. package/dist/chunk-J3FZR3YY.js +98 -0
  61. package/dist/chunk-J6DFZDFA.js +562 -0
  62. package/dist/chunk-J7WJZ6W7.js +50 -0
  63. package/dist/chunk-JONGANYH.js +202 -0
  64. package/dist/chunk-KJCWXUUS.js +425 -0
  65. package/dist/chunk-KJN4IOEJ.js +17 -0
  66. package/dist/chunk-KMGPUKFD.js +25 -0
  67. package/dist/chunk-LD7WFTRX.js +4533 -0
  68. package/dist/chunk-LJDODBBZ.js +78 -0
  69. package/dist/chunk-LNOMA3MP.js +169 -0
  70. package/dist/chunk-MGIWSROG.js +2334 -0
  71. package/dist/chunk-MQA2YBAH.js +308 -0
  72. package/dist/chunk-MTDRLSI7.js +21720 -0
  73. package/dist/chunk-N2IRDBZI.js +2355 -0
  74. package/dist/chunk-N77WFUL7.js +50 -0
  75. package/dist/chunk-N7YNJE5R.js +466 -0
  76. package/dist/chunk-NABLC7W3.js +148 -0
  77. package/dist/chunk-NFXP47RV.js +999 -0
  78. package/dist/chunk-NJF2X5XF.js +45 -0
  79. package/dist/chunk-NXL4FFQQ.js +47 -0
  80. package/dist/chunk-O7HZX7CA.js +309 -0
  81. package/dist/chunk-OC7LYAU5.js +708 -0
  82. package/dist/chunk-OEX24ECI.js +348 -0
  83. package/dist/chunk-OYOB3ZFG.js +78 -0
  84. package/dist/chunk-OYTMZXRF.js +41 -0
  85. package/dist/chunk-OZJINN4O.js +84 -0
  86. package/dist/chunk-PCHDGUKT.js +23 -0
  87. package/dist/chunk-QOZXJ4NA.js +3364 -0
  88. package/dist/chunk-QYF5S6P6.js +45 -0
  89. package/dist/chunk-RJFED73B.js +79 -0
  90. package/dist/chunk-RTQNRM5X.js +27 -0
  91. package/dist/chunk-S6AMWKPC.js +189 -0
  92. package/dist/chunk-T4QZ6FEH.js +308 -0
  93. package/dist/chunk-T54VJRAD.js +5456 -0
  94. package/dist/chunk-TA5L5KWQ.js +167 -0
  95. package/dist/chunk-TQFBZVCG.js +27 -0
  96. package/dist/chunk-U52SAGRM.js +62 -0
  97. package/dist/chunk-UEAHAMEI.js +5814 -0
  98. package/dist/chunk-UGSMCQ4D.js +327 -0
  99. package/dist/chunk-UOPVJQ4N.js +103 -0
  100. package/dist/chunk-USHG4DLH.js +459 -0
  101. package/dist/chunk-UXVNL3D7.js +23 -0
  102. package/dist/chunk-V5MEBST3.js +4139 -0
  103. package/dist/chunk-VGEVL5EQ.js +29 -0
  104. package/dist/chunk-VPWKF3GN.js +87 -0
  105. package/dist/chunk-VSMJK3ZJ.js +1250 -0
  106. package/dist/chunk-VYKKMYVT.js +560 -0
  107. package/dist/chunk-VZHJS6DR.js +442 -0
  108. package/dist/chunk-WATC3IQR.js +213 -0
  109. package/dist/chunk-WPE5FPGR.js +54 -0
  110. package/dist/chunk-WSEWKCXU.js +188 -0
  111. package/dist/chunk-WW5BLB74.js +659 -0
  112. package/dist/chunk-XGUL4SLQ.js +406 -0
  113. package/dist/chunk-XHSCVSOL.js +170 -0
  114. package/dist/chunk-XIMYZ3JA.js +27 -0
  115. package/dist/chunk-YLN4K6WD.js +385 -0
  116. package/dist/chunk-Z3CCVDUB.js +76 -0
  117. package/dist/chunk-Z54ZSOBZ.js +61 -0
  118. package/dist/chunk-Z6BB4JLQ.js +478 -0
  119. package/dist/chunk-Z7QQA2KT.js +27 -0
  120. package/dist/chunk-ZETYB3XG.js +2626 -0
  121. package/dist/chunk-ZMOMPX2Z.js +87 -0
  122. package/dist/chunk-ZOEYVXSV.js +149 -0
  123. package/dist/chunk-ZZUFZUQZ.js +12120 -0
  124. package/dist/index.js +39712 -29876
  125. package/dist/mcp/analyze-cli.js +186 -232
  126. package/dist/mcp/cli.js +137 -98
  127. package/dist/mcp/server.js +22688 -1573
  128. package/dist/mcp/worker.js +97 -38
  129. package/dist/probes/installer.js +135 -0
  130. package/dist/workers/project-snapshot-persist-worker.js +22 -0
  131. package/dist/workers/review-graph-persist-worker.js +15 -0
  132. package/docs/agent-guide.md +13 -4
  133. package/docs/agent-tools.md +42 -0
  134. package/docs/ast-grep_rules_catalog.md +5 -4
  135. package/docs/configuration.md +28 -2
  136. package/docs/custom-rules.md +25 -2
  137. package/docs/environment-variables.md +86 -8
  138. package/docs/features.md +45 -5
  139. package/docs/globalconfig.md +2 -1
  140. package/docs/io-bridge-v2-migration.md +94 -0
  141. package/docs/lsp-capability-matrix.md +37 -4
  142. package/docs/lsp-idle-eviction.md +124 -0
  143. package/docs/mcp.md +21 -5
  144. package/docs/merge-queue-rollout.md +77 -0
  145. package/docs/pi-lens-fixer.md +160 -88
  146. package/docs/pi-lens-investigator.md +74 -26
  147. package/docs/pi-lens-merge-policy.md +736 -0
  148. package/docs/pi-lens-monitor.md +75 -7
  149. package/docs/pi-lens-retro.md +6 -4
  150. package/docs/pi-lens-reviewer.md +189 -48
  151. package/docs/pi-lens-subagent.md +246 -45
  152. package/docs/pi-lens-warden.md +18 -7
  153. package/docs/public-api-stability.md +55 -0
  154. package/docs/release-qa-baseline.md +84 -17
  155. package/docs/rfcs/3654-unified-io-bridge-v2.md +316 -0
  156. package/docs/schema/pi-lens-config-v1.json +530 -0
  157. package/docs/servercapabilities.md +56 -49
  158. package/docs/settings.md +1 -0
  159. package/docs/usage.md +5 -1
  160. package/docs/word-index.md +6 -0
  161. package/grammars/tree-sitter-bash.wasm +0 -0
  162. package/grammars/tree-sitter-bash.wasm.json +3 -3
  163. package/package.json +19 -7
  164. package/rules/ast-grep-rules/rule-tests/advisory-registry-subset-test.yml +41 -0
  165. package/rules/ast-grep-rules/rule-tests/no-identity-operator-on-literals-test.yml +12 -2
  166. package/rules/ast-grep-rules/rule-tests/throw-new-error-js-test.yml +2 -0
  167. package/rules/ast-grep-rules/rule-tests/throw-new-error-test.yml +2 -0
  168. package/rules/ast-grep-rules/rules/advisory-registry-subset.yml +50 -0
  169. package/rules/ast-grep-rules/rules/no-identity-operator-on-literals.yml +20 -12
  170. package/rules/ast-grep-rules/rules/throw-new-error-js.yml +2 -2
  171. package/rules/ast-grep-rules/rules/throw-new-error.yml +2 -2
  172. package/scripts/analyze-pi-lens-logs.mjs +1275 -62
  173. package/scripts/download-grammars.js +5 -0
  174. package/scripts/grammars.lock.json +6 -1
  175. package/scripts/install-selftest.mjs +33 -34
  176. package/scripts/lib/host-provided-deps.mjs +13 -4
  177. package/scripts/lib/web-tree-sitter-dir.mjs +18 -0
  178. package/dist/clients/actionable-warnings-logger.js +0 -35
  179. package/dist/clients/actionable-warnings.js +0 -1610
  180. package/dist/clients/advisory-provenance.js +0 -593
  181. package/dist/clients/agent-behavior-client.js +0 -147
  182. package/dist/clients/agent-nudge.js +0 -325
  183. package/dist/clients/analysed-root.js +0 -1
  184. package/dist/clients/ast-grep-client.js +0 -731
  185. package/dist/clients/ast-grep-rule-manager.js +0 -146
  186. package/dist/clients/ast-grep-tool-logger.js +0 -118
  187. package/dist/clients/ast-grep-types.js +0 -10
  188. package/dist/clients/ast-grep-yaml-synth.js +0 -154
  189. package/dist/clients/atomic-write-staging.js +0 -75
  190. package/dist/clients/atomic-write.js +0 -189
  191. package/dist/clients/availability-probe-flight.js +0 -12
  192. package/dist/clients/bash-file-access.js +0 -1056
  193. package/dist/clients/biome-client.js +0 -357
  194. package/dist/clients/blocker-freshness.js +0 -837
  195. package/dist/clients/blocker-past-eof.js +0 -129
  196. package/dist/clients/bootstrap.js +0 -569
  197. package/dist/clients/bounded-cache.js +0 -190
  198. package/dist/clients/bounded-pid-file-lock.js +0 -202
  199. package/dist/clients/bounded-telemetry.js +0 -295
  200. package/dist/clients/build-identity.js +0 -95
  201. package/dist/clients/bundled-resource-health.js +0 -113
  202. package/dist/clients/bus-events-logger.js +0 -105
  203. package/dist/clients/bus-publish.js +0 -171
  204. package/dist/clients/cache/rule-cache.js +0 -190
  205. package/dist/clients/cache-manager.js +0 -524
  206. package/dist/clients/cache-observability.js +0 -1313
  207. package/dist/clients/call-graph.js +0 -852
  208. package/dist/clients/cargo-manifest.js +0 -422
  209. package/dist/clients/cascade-budget.js +0 -196
  210. package/dist/clients/cascade-format.js +0 -315
  211. package/dist/clients/cascade-logger.js +0 -31
  212. package/dist/clients/cascade-types.js +0 -9
  213. package/dist/clients/child-unref.js +0 -124
  214. package/dist/clients/code-quality-warnings.js +0 -286
  215. package/dist/clients/codebase-model.js +0 -173
  216. package/dist/clients/collateral-test-role.js +0 -40
  217. package/dist/clients/complexity-client.js +0 -462
  218. package/dist/clients/config-core/deny.js +0 -221
  219. package/dist/clients/config-core/index.js +0 -50
  220. package/dist/clients/config-core/merge.js +0 -357
  221. package/dist/clients/config-core/normalize.js +0 -340
  222. package/dist/clients/config-core/process-spec.js +0 -248
  223. package/dist/clients/config-core/provenance.js +0 -164
  224. package/dist/clients/config-core/records.js +0 -218
  225. package/dist/clients/config-core/resolve.js +0 -125
  226. package/dist/clients/config-core/safe-object.js +0 -78
  227. package/dist/clients/config-core/schema.js +0 -165
  228. package/dist/clients/config-diagnostic-codes.js +0 -285
  229. package/dist/clients/config-locations.js +0 -388
  230. package/dist/clients/config-resolve.js +0 -798
  231. package/dist/clients/config-schema.js +0 -251
  232. package/dist/clients/config-warn.js +0 -407
  233. package/dist/clients/console-guard-install.js +0 -26
  234. package/dist/clients/cooperative-budget.js +0 -42
  235. package/dist/clients/dead-code-client.js +0 -510
  236. package/dist/clients/dead-code-logger.js +0 -30
  237. package/dist/clients/deadline-utils.js +0 -280
  238. package/dist/clients/debug-handles.js +0 -211
  239. package/dist/clients/debug-heap.js +0 -174
  240. package/dist/clients/deferred-lsp-work.js +0 -106
  241. package/dist/clients/degradation-ledger.js +0 -426
  242. package/dist/clients/demoted-finding-render.js +0 -169
  243. package/dist/clients/dependency-checker.js +0 -885
  244. package/dist/clients/deps/ast-grep-napi.js +0 -22
  245. package/dist/clients/deps/js-yaml.js +0 -9
  246. package/dist/clients/deps/minimatch.js +0 -4
  247. package/dist/clients/deps/pi-tui.js +0 -14
  248. package/dist/clients/deps/typebox.js +0 -14
  249. package/dist/clients/deps/vscode-jsonrpc.js +0 -1
  250. package/dist/clients/deps/web-tree-sitter.js +0 -24
  251. package/dist/clients/diagnostic-dispositions.js +0 -630
  252. package/dist/clients/diagnostic-line-freshness.js +0 -305
  253. package/dist/clients/diagnostic-logger.js +0 -73
  254. package/dist/clients/diagnostic-tracker.js +0 -105
  255. package/dist/clients/diagnostics-publish.js +0 -230
  256. package/dist/clients/dispatch/ast-grep-catalog.js +0 -65
  257. package/dist/clients/dispatch/auxiliary-lsp.js +0 -343
  258. package/dist/clients/dispatch/collect-later-tier.js +0 -35
  259. package/dist/clients/dispatch/diagnostic-taxonomy.js +0 -66
  260. package/dist/clients/dispatch/dispatcher.js +0 -1269
  261. package/dist/clients/dispatch/fact-provider-types.js +0 -1
  262. package/dist/clients/dispatch/fact-rule-runner.js +0 -17
  263. package/dist/clients/dispatch/fact-runner.js +0 -30
  264. package/dist/clients/dispatch/fact-scheduler.js +0 -68
  265. package/dist/clients/dispatch/fact-store.js +0 -377
  266. package/dist/clients/dispatch/facts/comment-facts.js +0 -22
  267. package/dist/clients/dispatch/facts/file-content.js +0 -19
  268. package/dist/clients/dispatch/facts/function-facts.js +0 -348
  269. package/dist/clients/dispatch/facts/import-facts.js +0 -204
  270. package/dist/clients/dispatch/facts/tree-sitter-facts.js +0 -34
  271. package/dist/clients/dispatch/facts/try-catch-facts.js +0 -119
  272. package/dist/clients/dispatch/finding-policy.js +0 -339
  273. package/dist/clients/dispatch/indent-detect.js +0 -417
  274. package/dist/clients/dispatch/inline-suppressions.js +0 -89
  275. package/dist/clients/dispatch/integration.js +0 -2377
  276. package/dist/clients/dispatch/lazy.js +0 -11
  277. package/dist/clients/dispatch/pending-runner-findings.js +0 -87
  278. package/dist/clients/dispatch/plan.js +0 -278
  279. package/dist/clients/dispatch/priorities.js +0 -20
  280. package/dist/clients/dispatch/rule-id-normalize.js +0 -50
  281. package/dist/clients/dispatch/rule-ignores.js +0 -69
  282. package/dist/clients/dispatch/rule-policy.js +0 -135
  283. package/dist/clients/dispatch/rules/async-noise.js +0 -40
  284. package/dist/clients/dispatch/rules/async-unnecessary-wrapper.js +0 -27
  285. package/dist/clients/dispatch/rules/cors-wildcard.js +0 -54
  286. package/dist/clients/dispatch/rules/error-obscuring.js +0 -32
  287. package/dist/clients/dispatch/rules/error-swallowing.js +0 -31
  288. package/dist/clients/dispatch/rules/framework-call-noise.js +0 -67
  289. package/dist/clients/dispatch/rules/high-complexity.js +0 -72
  290. package/dist/clients/dispatch/rules/high-fan-out.js +0 -74
  291. package/dist/clients/dispatch/rules/high-import-coupling.js +0 -37
  292. package/dist/clients/dispatch/rules/missing-error-propagation.js +0 -62
  293. package/dist/clients/dispatch/rules/no-commented-credentials.js +0 -52
  294. package/dist/clients/dispatch/rules/pass-through-wrappers.js +0 -42
  295. package/dist/clients/dispatch/rules/placeholder-comments.js +0 -40
  296. package/dist/clients/dispatch/rules/unsafe-boundary.js +0 -126
  297. package/dist/clients/dispatch/runner-context.js +0 -52
  298. package/dist/clients/dispatch/runners/actionlint.js +0 -111
  299. package/dist/clients/dispatch/runners/ast-grep-napi.js +0 -1196
  300. package/dist/clients/dispatch/runners/biome-check.js +0 -296
  301. package/dist/clients/dispatch/runners/cpp-check.js +0 -219
  302. package/dist/clients/dispatch/runners/credo.js +0 -82
  303. package/dist/clients/dispatch/runners/cue-vet.js +0 -416
  304. package/dist/clients/dispatch/runners/dart-analyze.js +0 -183
  305. package/dist/clients/dispatch/runners/detekt.js +0 -176
  306. package/dist/clients/dispatch/runners/dotnet-build.js +0 -164
  307. package/dist/clients/dispatch/runners/elixir-check.js +0 -195
  308. package/dist/clients/dispatch/runners/eslint.js +0 -137
  309. package/dist/clients/dispatch/runners/fact-rules.js +0 -29
  310. package/dist/clients/dispatch/runners/fish-indent.js +0 -69
  311. package/dist/clients/dispatch/runners/gleam-check.js +0 -167
  312. package/dist/clients/dispatch/runners/go-vet.js +0 -104
  313. package/dist/clients/dispatch/runners/golangci-lint.js +0 -135
  314. package/dist/clients/dispatch/runners/hadolint.js +0 -91
  315. package/dist/clients/dispatch/runners/helm-lint.js +0 -171
  316. package/dist/clients/dispatch/runners/helm-render.js +0 -940
  317. package/dist/clients/dispatch/runners/htmlhint.js +0 -117
  318. package/dist/clients/dispatch/runners/index.js +0 -108
  319. package/dist/clients/dispatch/runners/javac.js +0 -99
  320. package/dist/clients/dispatch/runners/ktlint.js +0 -110
  321. package/dist/clients/dispatch/runners/lsp.js +0 -379
  322. package/dist/clients/dispatch/runners/markdownlint.js +0 -167
  323. package/dist/clients/dispatch/runners/mypy.js +0 -102
  324. package/dist/clients/dispatch/runners/oxlint.js +0 -414
  325. package/dist/clients/dispatch/runners/php-lint.js +0 -85
  326. package/dist/clients/dispatch/runners/phpstan.js +0 -120
  327. package/dist/clients/dispatch/runners/prisma-validate.js +0 -62
  328. package/dist/clients/dispatch/runners/psscriptanalyzer.js +0 -445
  329. package/dist/clients/dispatch/runners/pyright.js +0 -169
  330. package/dist/clients/dispatch/runners/rubocop.js +0 -99
  331. package/dist/clients/dispatch/runners/ruff.js +0 -88
  332. package/dist/clients/dispatch/runners/rust-clippy.js +0 -256
  333. package/dist/clients/dispatch/runners/shellcheck.js +0 -173
  334. package/dist/clients/dispatch/runners/shfmt.js +0 -102
  335. package/dist/clients/dispatch/runners/spellcheck.js +0 -128
  336. package/dist/clients/dispatch/runners/spotbugs.js +0 -259
  337. package/dist/clients/dispatch/runners/sqlfluff.js +0 -183
  338. package/dist/clients/dispatch/runners/stylelint.js +0 -166
  339. package/dist/clients/dispatch/runners/swiftlint.js +0 -163
  340. package/dist/clients/dispatch/runners/taplo.js +0 -174
  341. package/dist/clients/dispatch/runners/terragrunt.js +0 -168
  342. package/dist/clients/dispatch/runners/tflint.js +0 -125
  343. package/dist/clients/dispatch/runners/tree-sitter.js +0 -643
  344. package/dist/clients/dispatch/runners/trivy-config.js +0 -253
  345. package/dist/clients/dispatch/runners/utils/availability-policy.js +0 -496
  346. package/dist/clients/dispatch/runners/utils/candidate-probe.js +0 -90
  347. package/dist/clients/dispatch/runners/utils/diagnostic-parsers.js +0 -95
  348. package/dist/clients/dispatch/runners/utils/lazy-installer.js +0 -274
  349. package/dist/clients/dispatch/runners/utils/runner-helpers.js +0 -1701
  350. package/dist/clients/dispatch/runners/utils/spawn-outcome.js +0 -101
  351. package/dist/clients/dispatch/runners/utils/tool-failure.js +0 -235
  352. package/dist/clients/dispatch/runners/utils/toolchain-availability.js +0 -169
  353. package/dist/clients/dispatch/runners/utils.js +0 -15
  354. package/dist/clients/dispatch/runners/vale.js +0 -128
  355. package/dist/clients/dispatch/runners/yaml-rule-parser.js +0 -242
  356. package/dist/clients/dispatch/runners/yamllint.js +0 -95
  357. package/dist/clients/dispatch/runners/zig-check.js +0 -79
  358. package/dist/clients/dispatch/suppress-writer.js +0 -79
  359. package/dist/clients/dispatch/tool-profile.js +0 -30
  360. package/dist/clients/dispatch/types.js +0 -21
  361. package/dist/clients/dispatch/utils/format-utils.js +0 -45
  362. package/dist/clients/dispatch/utils/lsp-diagnostics.js +0 -35
  363. package/dist/clients/disposition-logger.js +0 -32
  364. package/dist/clients/disposition-publish.js +0 -124
  365. package/dist/clients/durable-store.js +0 -97
  366. package/dist/clients/effective-config.js +0 -415
  367. package/dist/clients/env-utils.js +0 -83
  368. package/dist/clients/error-class.js +0 -23
  369. package/dist/clients/eval-timestamp.js +0 -17
  370. package/dist/clients/event-loop-hold.js +0 -274
  371. package/dist/clients/event-loop-monitor.js +0 -250
  372. package/dist/clients/extension-log.js +0 -504
  373. package/dist/clients/extension-mode.js +0 -73
  374. package/dist/clients/feature-hints.js +0 -103
  375. package/dist/clients/file-kinds.js +0 -412
  376. package/dist/clients/file-role.js +0 -134
  377. package/dist/clients/file-time.js +0 -160
  378. package/dist/clients/file-utils.js +0 -1032
  379. package/dist/clients/finding-delivery-gate.js +0 -494
  380. package/dist/clients/finding-identity.js +0 -88
  381. package/dist/clients/fix-worklog.js +0 -103
  382. package/dist/clients/format-events-publish.js +0 -343
  383. package/dist/clients/format-service.js +0 -222
  384. package/dist/clients/formatters-lazy.js +0 -9
  385. package/dist/clients/formatters.js +0 -2032
  386. package/dist/clients/freshness-cadence.js +0 -17
  387. package/dist/clients/freshness.js +0 -38
  388. package/dist/clients/generated-artifacts.js +0 -394
  389. package/dist/clients/generation-guard.js +0 -215
  390. package/dist/clients/git-guard.js +0 -1136
  391. package/dist/clients/git-tracked-ignore.js +0 -243
  392. package/dist/clients/gitleaks-client.js +0 -509
  393. package/dist/clients/go-client.js +0 -104
  394. package/dist/clients/govulncheck-client.js +0 -605
  395. package/dist/clients/gradle-ktfmt-style.js +0 -348
  396. package/dist/clients/grammar-source.js +0 -465
  397. package/dist/clients/gzip-stage-write.js +0 -135
  398. package/dist/clients/hashline-anchor.js +0 -424
  399. package/dist/clients/hook-budgets.js +0 -76
  400. package/dist/clients/host-edit-normalize.js +0 -140
  401. package/dist/clients/host-ports.js +0 -39
  402. package/dist/clients/indent-retarget.js +0 -154
  403. package/dist/clients/inline-blocker-dispositions.js +0 -186
  404. package/dist/clients/install-diagnostics.js +0 -156
  405. package/dist/clients/installer/index.js +0 -5416
  406. package/dist/clients/installer/managed-tool-refresh-session.js +0 -58
  407. package/dist/clients/installer/managed-tool-refresh.js +0 -783
  408. package/dist/clients/instance-reaper-state.js +0 -7
  409. package/dist/clients/instance-reaper.js +0 -1137
  410. package/dist/clients/instance-registry-lock.js +0 -145
  411. package/dist/clients/instance-registry.js +0 -773
  412. package/dist/clients/jscpd-client.js +0 -378
  413. package/dist/clients/json-cache-read.js +0 -65
  414. package/dist/clients/knip-client.js +0 -788
  415. package/dist/clients/language-policy.js +0 -252
  416. package/dist/clients/language-profile.js +0 -361
  417. package/dist/clients/language-registry.js +0 -603
  418. package/dist/clients/latency-logger.js +0 -727
  419. package/dist/clients/lazy-import.js +0 -21
  420. package/dist/clients/ledger-bounds.js +0 -54
  421. package/dist/clients/lens-config.js +0 -404
  422. package/dist/clients/lens-engine.js +0 -358
  423. package/dist/clients/lens-events.js +0 -125
  424. package/dist/clients/lens-flag-registry.js +0 -467
  425. package/dist/clients/lens-map.js +0 -1156
  426. package/dist/clients/live-bus-emitter.js +0 -74
  427. package/dist/clients/log-cleanup.js +0 -222
  428. package/dist/clients/lsp/aggregation.js +0 -178
  429. package/dist/clients/lsp/cascade-tier.js +0 -421
  430. package/dist/clients/lsp/client.js +0 -4593
  431. package/dist/clients/lsp/config.js +0 -691
  432. package/dist/clients/lsp/content-limits.js +0 -41
  433. package/dist/clients/lsp/diagnostic-binding.js +0 -225
  434. package/dist/clients/lsp/document-drift.js +0 -467
  435. package/dist/clients/lsp/edits.js +0 -1219
  436. package/dist/clients/lsp/index.js +0 -8305
  437. package/dist/clients/lsp/inferred-project.js +0 -216
  438. package/dist/clients/lsp/jvm-runtime.js +0 -298
  439. package/dist/clients/lsp/language.js +0 -63
  440. package/dist/clients/lsp/launch.js +0 -746
  441. package/dist/clients/lsp/lombok.js +0 -158
  442. package/dist/clients/lsp/path-utils.js +0 -5
  443. package/dist/clients/lsp/pending-aux-coverage.js +0 -251
  444. package/dist/clients/lsp/position-encoding.js +0 -61
  445. package/dist/clients/lsp/ruby-drive-dirs.js +0 -83
  446. package/dist/clients/lsp/server.js +0 -3290
  447. package/dist/clients/lsp/session-roots.js +0 -192
  448. package/dist/clients/lsp/spawn-history.js +0 -17
  449. package/dist/clients/lsp/sync-kind.js +0 -56
  450. package/dist/clients/lsp/tsserver-sync.js +0 -332
  451. package/dist/clients/lsp/wait-policy/capability-snapshot.js +0 -1
  452. package/dist/clients/lsp/wait-policy/classification.js +0 -85
  453. package/dist/clients/lsp/wait-policy/index.js +0 -10
  454. package/dist/clients/lsp/wait-policy/strategies.js +0 -392
  455. package/dist/clients/lsp/watch-queue.js +0 -61
  456. package/dist/clients/lsp/workspace-diagnostics-cache.js +0 -639
  457. package/dist/clients/lsp/workspace-diagnostics-session.js +0 -30
  458. package/dist/clients/lsp/workspace-sweep-hold.js +0 -184
  459. package/dist/clients/lsp-budget.js +0 -186
  460. package/dist/clients/lsp-document-symbols.js +0 -110
  461. package/dist/clients/lsp-lazy.js +0 -9
  462. package/dist/clients/lsp-mutation.js +0 -396
  463. package/dist/clients/lsp-status.js +0 -59
  464. package/dist/clients/map-with-concurrency.js +0 -40
  465. package/dist/clients/mcp/analyze.js +0 -420
  466. package/dist/clients/mcp/host-shim.js +0 -31
  467. package/dist/clients/mcp/ipc.js +0 -523
  468. package/dist/clients/mcp/review.js +0 -197
  469. package/dist/clients/mcp/session.js +0 -412
  470. package/dist/clients/memory-sampler.js +0 -208
  471. package/dist/clients/message-end-attribution.js +0 -30
  472. package/dist/clients/metrics-client.js +0 -107
  473. package/dist/clients/metrics-history.js +0 -327
  474. package/dist/clients/middle-man-analysis.js +0 -263
  475. package/dist/clients/model-provider.js +0 -53
  476. package/dist/clients/module-report-lsp.js +0 -189
  477. package/dist/clients/module-report.js +0 -2150
  478. package/dist/clients/mutating-tool.js +0 -651
  479. package/dist/clients/mutation-attribution.js +0 -368
  480. package/dist/clients/mutation-bridge.js +0 -240
  481. package/dist/clients/ndjson-logger.js +0 -734
  482. package/dist/clients/observed-mutation-sources.js +0 -101
  483. package/dist/clients/observed-mutation.js +0 -1253
  484. package/dist/clients/oldtext-autopatch.js +0 -128
  485. package/dist/clients/opaque-mutation-scan.js +0 -496
  486. package/dist/clients/opengrep-client.js +0 -333
  487. package/dist/clients/opengrep-config.js +0 -76
  488. package/dist/clients/package-manager.js +0 -607
  489. package/dist/clients/package-root.js +0 -38
  490. package/dist/clients/partial-edit-apply.js +0 -439
  491. package/dist/clients/path-attribution-telemetry.js +0 -43
  492. package/dist/clients/path-keyed-map.js +0 -108
  493. package/dist/clients/path-utils.js +0 -1244
  494. package/dist/clients/performance-report.js +0 -331
  495. package/dist/clients/persist-debounce.js +0 -70
  496. package/dist/clients/persistent-reverify.js +0 -271
  497. package/dist/clients/php-cs-fixer-config.js +0 -119
  498. package/dist/clients/pipeline.js +0 -1480
  499. package/dist/clients/post-autofix-notice.js +0 -37
  500. package/dist/clients/print-mode.js +0 -21
  501. package/dist/clients/probe-home-state.js +0 -228
  502. package/dist/clients/process-bridge.js +0 -66
  503. package/dist/clients/process-singletons.js +0 -210
  504. package/dist/clients/process-snapshot.js +0 -68
  505. package/dist/clients/project-changes.js +0 -181
  506. package/dist/clients/project-conventions.js +0 -171
  507. package/dist/clients/project-diagnostics/cache.js +0 -137
  508. package/dist/clients/project-diagnostics/extractors.js +0 -109
  509. package/dist/clients/project-diagnostics/fresh-fetch.js +0 -668
  510. package/dist/clients/project-diagnostics/runner-adapters/call-graph-impact.js +0 -118
  511. package/dist/clients/project-diagnostics/runner-adapters/dead-code.js +0 -47
  512. package/dist/clients/project-diagnostics/runner-adapters/gitleaks.js +0 -40
  513. package/dist/clients/project-diagnostics/runner-adapters/govulncheck.js +0 -34
  514. package/dist/clients/project-diagnostics/runner-adapters/jscpd.js +0 -38
  515. package/dist/clients/project-diagnostics/runner-adapters/knip.js +0 -47
  516. package/dist/clients/project-diagnostics/runner-adapters/madge.js +0 -48
  517. package/dist/clients/project-diagnostics/runner-adapters/opengrep.js +0 -40
  518. package/dist/clients/project-diagnostics/runner-adapters/runner-findings.js +0 -127
  519. package/dist/clients/project-diagnostics/runner-adapters/trivy.js +0 -81
  520. package/dist/clients/project-diagnostics/scanner.js +0 -520
  521. package/dist/clients/project-diagnostics/types.js +0 -1
  522. package/dist/clients/project-lens-config.js +0 -807
  523. package/dist/clients/project-report.js +0 -701
  524. package/dist/clients/project-scale.js +0 -237
  525. package/dist/clients/project-scan-policy.js +0 -11
  526. package/dist/clients/project-snapshot-fingerprint.js +0 -48
  527. package/dist/clients/project-snapshot-persist-worker.js +0 -20
  528. package/dist/clients/project-snapshot.js +0 -1653
  529. package/dist/clients/project-trust.js +0 -181
  530. package/dist/clients/python-environment.js +0 -217
  531. package/dist/clients/python-provenance.js +0 -639
  532. package/dist/clients/quiet-window-config.js +0 -47
  533. package/dist/clients/quiet-window.js +0 -236
  534. package/dist/clients/read-bridge.js +0 -139
  535. package/dist/clients/read-expansion.js +0 -266
  536. package/dist/clients/read-guard-logger.js +0 -251
  537. package/dist/clients/read-guard-tool-lines.js +0 -1306
  538. package/dist/clients/read-guard.js +0 -1561
  539. package/dist/clients/recent-touches.js +0 -240
  540. package/dist/clients/redact/secrets.js +0 -449
  541. package/dist/clients/resource-sampler.js +0 -628
  542. package/dist/clients/reverse-deps.js +0 -270
  543. package/dist/clients/review-graph/builder.js +0 -5126
  544. package/dist/clients/review-graph/format.js +0 -33
  545. package/dist/clients/review-graph/git-identity.js +0 -163
  546. package/dist/clients/review-graph/import-resolvers.js +0 -481
  547. package/dist/clients/review-graph/persist-worker.js +0 -10
  548. package/dist/clients/review-graph/query.js +0 -244
  549. package/dist/clients/review-graph/revision-drift.js +0 -21
  550. package/dist/clients/review-graph/service.js +0 -96
  551. package/dist/clients/review-graph/shared-extraction-ir.js +0 -82
  552. package/dist/clients/review-graph/symbol-id.js +0 -103
  553. package/dist/clients/review-graph/tsconfig-paths.js +0 -299
  554. package/dist/clients/review-graph/types.js +0 -1
  555. package/dist/clients/review-graph/workspace-modules.js +0 -449
  556. package/dist/clients/review-graph-logger.js +0 -81
  557. package/dist/clients/ruff-client.js +0 -179
  558. package/dist/clients/rules-scanner.js +0 -131
  559. package/dist/clients/run-duration.js +0 -55
  560. package/dist/clients/runtime-agent-end.js +0 -746
  561. package/dist/clients/runtime-config.js +0 -77
  562. package/dist/clients/runtime-context.js +0 -211
  563. package/dist/clients/runtime-coordinator.js +0 -1324
  564. package/dist/clients/runtime-session.js +0 -2485
  565. package/dist/clients/runtime-tool-call.js +0 -1295
  566. package/dist/clients/runtime-tool-result.js +0 -2049
  567. package/dist/clients/runtime-turn.js +0 -3767
  568. package/dist/clients/rust-client.js +0 -97
  569. package/dist/clients/safe-spawn.js +0 -2357
  570. package/dist/clients/sanitize.js +0 -286
  571. package/dist/clients/scan-utils.js +0 -25
  572. package/dist/clients/scratch-tree-policy.js +0 -165
  573. package/dist/clients/search-read-registration.js +0 -94
  574. package/dist/clients/secret-findings.js +0 -114
  575. package/dist/clients/security-scan-client.js +0 -352
  576. package/dist/clients/session-event-guard.js +0 -297
  577. package/dist/clients/session-lifecycle.js +0 -484
  578. package/dist/clients/session-start-observability.js +0 -94
  579. package/dist/clients/session-state-store.js +0 -101
  580. package/dist/clients/session-summary.js +0 -15
  581. package/dist/clients/sessionstart-logger.js +0 -27
  582. package/dist/clients/sg-runner.js +0 -876
  583. package/dist/clients/sgconfig.js +0 -347
  584. package/dist/clients/shared-checkout-guard.js +0 -338
  585. package/dist/clients/single-flight.js +0 -158
  586. package/dist/clients/situational-tool-telemetry.js +0 -108
  587. package/dist/clients/skills-resolver.js +0 -141
  588. package/dist/clients/slow-fs.js +0 -138
  589. package/dist/clients/smells-rollup.js +0 -231
  590. package/dist/clients/source-filter.js +0 -596
  591. package/dist/clients/source-walker.js +0 -211
  592. package/dist/clients/spawn-output-cap.js +0 -120
  593. package/dist/clients/spawn-timeout-cooldown.js +0 -110
  594. package/dist/clients/stale-marker.js +0 -17
  595. package/dist/clients/startup-scan.js +0 -400
  596. package/dist/clients/startup-timing.js +0 -130
  597. package/dist/clients/string-utils.js +0 -33
  598. package/dist/clients/subagent-mode.js +0 -111
  599. package/dist/clients/symbol-containment.js +0 -24
  600. package/dist/clients/symbol-types.js +0 -5
  601. package/dist/clients/test-runner-client.js +0 -2507
  602. package/dist/clients/test-runner-delivery.js +0 -287
  603. package/dist/clients/todo-scanner.js +0 -229
  604. package/dist/clients/tool-agreement.js +0 -423
  605. package/dist/clients/tool-config.js +0 -249
  606. package/dist/clients/tool-cwd.js +0 -270
  607. package/dist/clients/tool-definition.js +0 -47
  608. package/dist/clients/tool-event.js +0 -105
  609. package/dist/clients/tool-policy.js +0 -2755
  610. package/dist/clients/tool-probe.js +0 -53
  611. package/dist/clients/tool-render.js +0 -156
  612. package/dist/clients/tool-set-policy.js +0 -118
  613. package/dist/clients/tree-sitter-cache.js +0 -364
  614. package/dist/clients/tree-sitter-client.js +0 -4195
  615. package/dist/clients/tree-sitter-logger.js +0 -100
  616. package/dist/clients/tree-sitter-navigator.js +0 -269
  617. package/dist/clients/tree-sitter-query-loader.js +0 -569
  618. package/dist/clients/tree-sitter-shared.js +0 -158
  619. package/dist/clients/tree-sitter-symbol-extractor.js +0 -1265
  620. package/dist/clients/trivy-client.js +0 -467
  621. package/dist/clients/tui-fit.js +0 -50
  622. package/dist/clients/turn-context.js +0 -52
  623. package/dist/clients/turn-end/lane.js +0 -51
  624. package/dist/clients/turn-end/lanes/govulncheck.js +0 -112
  625. package/dist/clients/turn-end/lanes/secrets.js +0 -204
  626. package/dist/clients/turn-summary-render.js +0 -72
  627. package/dist/clients/turn-summary.js +0 -132
  628. package/dist/clients/typos-config.js +0 -29
  629. package/dist/clients/user-notify.js +0 -51
  630. package/dist/clients/vanished-instance-marker.js +0 -57
  631. package/dist/clients/warm-attach.js +0 -305
  632. package/dist/clients/widget-state.js +0 -1808
  633. package/dist/clients/word-index-logger.js +0 -49
  634. package/dist/clients/word-index-store.js +0 -459
  635. package/dist/clients/word-index.js +0 -2080
  636. package/dist/clients/workspace-topology.js +0 -443
  637. package/dist/clients/write-ordering-guard.js +0 -65
  638. package/dist/clients/zizmor-config.js +0 -340
  639. package/dist/i18n.js +0 -69
  640. package/dist/mcp/build-staleness.js +0 -124
  641. package/dist/scripts/lib/process-scan.mjs +0 -583
  642. package/dist/scripts/lib/skills-predicate.mjs +0 -129
  643. package/dist/scripts/lib/web-tree-sitter-dir.mjs +0 -123
  644. package/dist/tools/activate-tools.js +0 -87
  645. package/dist/tools/ast-grep-outline.js +0 -137
  646. package/dist/tools/ast-grep-replace.js +0 -244
  647. package/dist/tools/ast-grep-search.js +0 -762
  648. package/dist/tools/effective-config.js +0 -90
  649. package/dist/tools/lens-diagnostic-mark.js +0 -375
  650. package/dist/tools/lens-diagnostics.js +0 -2571
  651. package/dist/tools/lsp-diagnostics.js +0 -1745
  652. package/dist/tools/lsp-navigation.js +0 -1367
  653. package/dist/tools/lsp-structured-output.js +0 -294
  654. package/dist/tools/module-report.js +0 -382
  655. package/dist/tools/project-report.js +0 -86
  656. package/dist/tools/render-compact.js +0 -374
  657. package/dist/tools/scan-progress.js +0 -53
  658. package/dist/tools/shared.js +0 -29
  659. package/dist/tools/symbol-search.js +0 -155
@@ -0,0 +1,4816 @@
1
+ import { createRequire as __pilensCreateRequire } from "node:module"; const require = __pilensCreateRequire(import.meta.url);
2
+ import {
3
+ augmentPythonEnvironment,
4
+ detectPythonEnvironment,
5
+ detectPythonVenv,
6
+ pythonEnvironmentToolCandidates
7
+ } from "./chunk-LNOMA3MP.js";
8
+ import {
9
+ resolveOpengrepConfig
10
+ } from "./chunk-U52SAGRM.js";
11
+ import {
12
+ extensionsForLanguage,
13
+ getUserRuleRoot,
14
+ ruleCorpusFingerprintForCycle
15
+ } from "./chunk-KJCWXUUS.js";
16
+ import {
17
+ load,
18
+ resolvePackagePath
19
+ } from "./chunk-QOZXJ4NA.js";
20
+ import {
21
+ hasCargoWorkspaceTable,
22
+ readCargoWorkspaceExclude,
23
+ readCargoWorkspaceMembers,
24
+ readTextFileOrUndefined
25
+ } from "./chunk-S6AMWKPC.js";
26
+ import {
27
+ HOOK_WALL_BUDGET_MS,
28
+ bounded
29
+ } from "./chunk-F5VLZHUW.js";
30
+ import {
31
+ ensureTool,
32
+ findManagedToolBinary,
33
+ getInstallAttempt,
34
+ getToolEnvironment,
35
+ getToolPath,
36
+ isZizmorAuditTarget,
37
+ logSessionStart,
38
+ resolveZizmorGitHubToken
39
+ } from "./chunk-LD7WFTRX.js";
40
+ import {
41
+ resolveToolCwd
42
+ } from "./chunk-IUR76K6A.js";
43
+ import {
44
+ findGlobalBinary
45
+ } from "./chunk-T4QZ6FEH.js";
46
+ import {
47
+ classifyProbeFailure,
48
+ createAvailabilityLatch,
49
+ describeInstallAttempt,
50
+ logAvailabilityDecision,
51
+ startHostStallSampler
52
+ } from "./chunk-O7HZX7CA.js";
53
+ import {
54
+ OWNER_TAG_ENV,
55
+ SpawnFailureError,
56
+ classifySpawnFailure,
57
+ createGenerationSource,
58
+ ephemeralStagingRoot,
59
+ getGlobalPiLensDir,
60
+ getProjectIgnoreGlobs,
61
+ hasSpawnFailureKind,
62
+ holdOwnChildPid,
63
+ isCommandAvailableAsync,
64
+ isPathIgnoredByProject,
65
+ ownerTagForChildren,
66
+ safeSpawnAsync
67
+ } from "./chunk-T54VJRAD.js";
68
+ import {
69
+ DOTNET_CSHARP_ROOT_MARKERS,
70
+ DOTNET_FSHARP_ROOT_MARKERS,
71
+ KIND_EXTENSIONS
72
+ } from "./chunk-64LU2OVH.js";
73
+ import {
74
+ lspSectionOf,
75
+ migrationSubject,
76
+ reportConfigReadFailure,
77
+ reportPiLensConfigRecords,
78
+ resolvePiLensConfig,
79
+ summarizeConfigResolution
80
+ } from "./chunk-N2IRDBZI.js";
81
+ import {
82
+ FRESHNESS_CADENCE_MS,
83
+ LSP_NAMESPACE_KEY,
84
+ dirMtimeRecordsAsync,
85
+ dirMtimesStillFreshAsync,
86
+ getPiLensGlobalConfigPath,
87
+ unknownDirMtimeRecords
88
+ } from "./chunk-Z6BB4JLQ.js";
89
+ import {
90
+ incrementDegradationCount,
91
+ recordDegradationOnce
92
+ } from "./chunk-7ELY2CSM.js";
93
+ import {
94
+ claimPhaseOncePerSession,
95
+ currentSessionRecordId,
96
+ logLatency,
97
+ releasePhaseClaim
98
+ } from "./chunk-3PNFWVHT.js";
99
+ import {
100
+ getGlobalPiLensLogDir,
101
+ getProcessSingleton,
102
+ isTestMode,
103
+ redactSecrets
104
+ } from "./chunk-VSMJK3ZJ.js";
105
+ import {
106
+ STAGE_TMP_PATTERN
107
+ } from "./chunk-LJDODBBZ.js";
108
+ import {
109
+ BoundedFifoMap,
110
+ BoundedLruCache
111
+ } from "./chunk-ABBOA7UD.js";
112
+ import {
113
+ CARGO_WORKSPACE_MEMBER_DIALECT,
114
+ direntsHaveMarkerGlobMatch,
115
+ findLocalToolConfig,
116
+ isAtOrAboveHomeDir,
117
+ isFullyQualified,
118
+ isWindowsPath,
119
+ matchesWorkspaceMemberPattern,
120
+ normalizeEphemeralMapKey,
121
+ normalizeFilePath,
122
+ normalizeMapKey,
123
+ pathsEqual,
124
+ toPosix
125
+ } from "./chunk-G3RLJYA2.js";
126
+ import {
127
+ __require
128
+ } from "./chunk-NXL4FFQQ.js";
129
+
130
+ // dist/clients/lsp/server-traits.js
131
+ var ROLE_MEMBERS = {
132
+ language: "language",
133
+ auxiliary: "auxiliary"
134
+ };
135
+ var DEFAULT_LSP_SERVER_ROLE = "language";
136
+ function isAuxiliary(bearer) {
137
+ const declared = bearer?.role;
138
+ const role = typeof declared === "string" && Object.hasOwn(ROLE_MEMBERS, declared) ? ROLE_MEMBERS[declared] : DEFAULT_LSP_SERVER_ROLE;
139
+ switch (role) {
140
+ case "auxiliary":
141
+ return true;
142
+ case "language":
143
+ return false;
144
+ }
145
+ }
146
+ var DEFAULT_NOTIFY_INFLIGHT_LIMIT = 8;
147
+ var DEFAULT_REPLY_ORDERING = "unmeasured";
148
+ function serverTraits(declaration) {
149
+ const declaredLimit = declaration.notifyInflightLimit;
150
+ if (typeof declaredLimit === "number" && Number.isFinite(declaredLimit) && declaredLimit > 0) {
151
+ return {
152
+ role: declaration.role,
153
+ notifyInflightLimit: Math.floor(declaredLimit),
154
+ replyOrdering: declaration.replyOrdering ?? DEFAULT_REPLY_ORDERING
155
+ };
156
+ }
157
+ const envLimit = Number(process.env.PI_LENS_LSP_AUX_NOTIFY_INFLIGHT);
158
+ return {
159
+ role: declaration.role,
160
+ notifyInflightLimit: Number.isFinite(envLimit) && envLimit > 0 ? Math.floor(envLimit) : DEFAULT_NOTIFY_INFLIGHT_LIMIT,
161
+ replyOrdering: declaration.replyOrdering ?? DEFAULT_REPLY_ORDERING
162
+ };
163
+ }
164
+
165
+ // dist/clients/lsp/server.js
166
+ import { existsSync as existsSync3, mkdirSync as mkdirSync2, statSync as statSync3 } from "node:fs";
167
+ import { access, readFile, readdir, stat } from "node:fs/promises";
168
+ import os6 from "node:os";
169
+ import path7 from "node:path";
170
+
171
+ // dist/clients/sgconfig.js
172
+ import { createHash } from "node:crypto";
173
+ import * as fs from "node:fs";
174
+ import * as os from "node:os";
175
+ import * as path from "node:path";
176
+ var SGCONFIG_NAMES = ["sgconfig.yml", "sgconfig.yaml"];
177
+ function findLocalSgconfig(startDir) {
178
+ return findLocalToolConfig(startDir, SGCONFIG_NAMES);
179
+ }
180
+ function canonicalDir(dir) {
181
+ try {
182
+ return fs.realpathSync.native(dir);
183
+ } catch {
184
+ return path.resolve(dir);
185
+ }
186
+ }
187
+ function getAstGrepRuleSources(projectRoot = process.cwd()) {
188
+ const root = path.resolve(projectRoot || process.cwd());
189
+ const packageRoot = resolvePackagePath(import.meta.url);
190
+ const candidates = [];
191
+ if (canonicalDir(root) !== canonicalDir(packageRoot)) {
192
+ candidates.push({
193
+ dir: path.join(root, "rules", "ast-grep-rules", "rules"),
194
+ origin: "project",
195
+ tier: "primary"
196
+ }, {
197
+ dir: path.join(root, "rules", "ast-grep-rules", "coderabbit", "rules"),
198
+ origin: "project",
199
+ tier: "secondary"
200
+ });
201
+ }
202
+ const userRoot = getUserRuleRoot();
203
+ if (canonicalDir(userRoot) !== canonicalDir(root)) {
204
+ candidates.push({
205
+ dir: path.join(userRoot, "ast-grep-rules", "rules"),
206
+ origin: "user",
207
+ tier: "primary"
208
+ }, {
209
+ dir: path.join(userRoot, "ast-grep-rules", "coderabbit", "rules"),
210
+ origin: "user",
211
+ tier: "secondary"
212
+ });
213
+ }
214
+ candidates.push({
215
+ dir: path.join(packageRoot, "rules", "ast-grep-rules", "rules"),
216
+ origin: "bundled",
217
+ tier: "primary"
218
+ }, {
219
+ dir: path.join(packageRoot, "rules", "ast-grep-rules", "coderabbit", "rules"),
220
+ origin: "bundled",
221
+ tier: "secondary"
222
+ });
223
+ const seen = /* @__PURE__ */ new Set();
224
+ return candidates.filter((source) => {
225
+ if (!fs.existsSync(source.dir))
226
+ return false;
227
+ const key = canonicalDir(source.dir);
228
+ if (seen.has(key))
229
+ return false;
230
+ seen.add(key);
231
+ return true;
232
+ });
233
+ }
234
+ function findYamlFiles(dir) {
235
+ const files = [];
236
+ let entries;
237
+ try {
238
+ entries = fs.readdirSync(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
239
+ } catch {
240
+ return files;
241
+ }
242
+ for (const entry of entries) {
243
+ const full = path.join(dir, entry.name);
244
+ if (entry.isDirectory()) {
245
+ files.push(...findYamlFiles(full));
246
+ } else if (entry.isFile() && /\.ya?ml$/i.test(entry.name)) {
247
+ files.push(full);
248
+ }
249
+ }
250
+ return files;
251
+ }
252
+ function parseRuleId(raw) {
253
+ const scalar = raw.match(/^id:\s*(.*?)\s*$/m)?.[1];
254
+ if (!scalar)
255
+ return void 0;
256
+ const withoutComment = scalar.replace(/\s+#.*$/, "").trim();
257
+ const unquoted = withoutComment.startsWith('"') && withoutComment.endsWith('"') || withoutComment.startsWith("'") && withoutComment.endsWith("'") ? withoutComment.slice(1, -1) : withoutComment;
258
+ if (/^[A-Za-z0-9_.:-]+$/.test(unquoted))
259
+ return unquoted;
260
+ try {
261
+ const parsed = load(raw);
262
+ if (!parsed || typeof parsed !== "object" || !("id" in parsed)) {
263
+ return void 0;
264
+ }
265
+ const id = parsed.id;
266
+ return typeof id === "string" ? id : void 0;
267
+ } catch {
268
+ return void 0;
269
+ }
270
+ }
271
+ function parseRuleDocuments(content) {
272
+ return content.split(/^---\s*$/m).filter((document) => document.trim()).map((raw) => ({ raw, id: parseRuleId(raw) }));
273
+ }
274
+ var bundledSnapshots = /* @__PURE__ */ new Map();
275
+ function snapshotRuleSource(source) {
276
+ const cacheKey = canonicalDir(source.dir);
277
+ if (source.origin === "bundled") {
278
+ const cached = bundledSnapshots.get(cacheKey);
279
+ if (cached)
280
+ return cached;
281
+ }
282
+ const hash = createHash("sha256");
283
+ const files = [];
284
+ for (const file of findYamlFiles(source.dir)) {
285
+ const relativePath = path.relative(source.dir, file);
286
+ hash.update(relativePath);
287
+ hash.update("\0");
288
+ let content;
289
+ try {
290
+ content = fs.readFileSync(file, "utf8");
291
+ } catch {
292
+ hash.update("missing");
293
+ hash.update("\0");
294
+ continue;
295
+ }
296
+ hash.update(content);
297
+ hash.update("\0");
298
+ const documents = parseRuleDocuments(content);
299
+ if (source.origin === "user" && documents.some((document) => !document.id)) {
300
+ recordDegradationOnce({
301
+ kind: "ast-grep-rule-invalid",
302
+ subject: file,
303
+ reason: "user ast-grep rule has no valid rule id"
304
+ });
305
+ }
306
+ files.push({ file, relativePath, documents });
307
+ }
308
+ const snapshot = { ...source, files, digest: hash.digest("hex") };
309
+ if (source.origin === "bundled")
310
+ bundledSnapshots.set(cacheKey, snapshot);
311
+ return snapshot;
312
+ }
313
+ function sourceFingerprint(sources) {
314
+ const hash = createHash("sha256");
315
+ for (const source of sources) {
316
+ hash.update(source.origin);
317
+ hash.update("\0");
318
+ hash.update(source.tier);
319
+ hash.update("\0");
320
+ hash.update(canonicalDir(source.dir));
321
+ hash.update("\0");
322
+ hash.update(source.digest);
323
+ hash.update("\0");
324
+ }
325
+ return hash.digest("hex");
326
+ }
327
+ function getAstGrepRuleFingerprint(projectRoot = process.cwd(), options = {}) {
328
+ const root = path.resolve(projectRoot || process.cwd());
329
+ return ruleCorpusFingerprintForCycle("ast-grep", root, (fingerprintRoot) => sourceFingerprint(getAstGrepRuleSources(fingerprintRoot).map(snapshotRuleSource)), options.force);
330
+ }
331
+ function materializeMergedRuleDir(sources, mergedDir) {
332
+ fs.rmSync(mergedDir, { recursive: true, force: true });
333
+ fs.mkdirSync(mergedDir, { recursive: true });
334
+ const claimedByEarlierSource = /* @__PURE__ */ new Set();
335
+ for (const [sourceIndex, source] of sources.entries()) {
336
+ const claimedThisSource = /* @__PURE__ */ new Set();
337
+ for (const [fileIndex, file] of source.files.entries()) {
338
+ const survivingDocuments = file.documents.filter((document) => {
339
+ if (!document.id)
340
+ return true;
341
+ if (claimedByEarlierSource.has(document.id))
342
+ return false;
343
+ claimedThisSource.add(document.id);
344
+ return true;
345
+ });
346
+ if (survivingDocuments.length === 0)
347
+ continue;
348
+ const destination = path.join(mergedDir, `source${sourceIndex}-${String(fileIndex).padStart(5, "0")}.yml`);
349
+ if (survivingDocuments.length === file.documents.length) {
350
+ fs.copyFileSync(file.file, destination);
351
+ } else {
352
+ fs.writeFileSync(destination, `${survivingDocuments.map((document) => document.raw.trim()).join("\n---\n")}
353
+ `);
354
+ }
355
+ }
356
+ for (const id of claimedThisSource)
357
+ claimedByEarlierSource.add(id);
358
+ }
359
+ }
360
+ var cachedBaselines = /* @__PURE__ */ new Map();
361
+ function rootArtifactKey(root) {
362
+ return createHash("sha256").update(root).digest("hex").slice(0, 12);
363
+ }
364
+ function resolveBaselineSgconfig(projectRoot = process.cwd()) {
365
+ const root = canonicalDir(path.resolve(projectRoot || process.cwd()));
366
+ const fingerprint = getAstGrepRuleFingerprint(root, { force: true });
367
+ const cached = cachedBaselines.get(root);
368
+ if (cached?.fingerprint === fingerprint && fs.existsSync(cached.path) && fs.existsSync(cached.mergedDir)) {
369
+ return cached.path;
370
+ }
371
+ const sources = getAstGrepRuleSources(root).map(snapshotRuleSource);
372
+ if (sources.length === 0)
373
+ return void 0;
374
+ const dir = path.join(os.tmpdir(), "pi-lens-ast-grep");
375
+ fs.mkdirSync(dir, { recursive: true });
376
+ const artifactKey = rootArtifactKey(root);
377
+ const file = path.join(dir, `baseline-${process.pid}-${artifactKey}.sgconfig.yml`);
378
+ const mergedDir = path.join(dir, `baseline-${process.pid}-${artifactKey}.rules`);
379
+ const protectedArtifacts = /* @__PURE__ */ new Set([file, mergedDir]);
380
+ for (const baseline of cachedBaselines.values()) {
381
+ protectedArtifacts.add(baseline.path);
382
+ protectedArtifacts.add(baseline.mergedDir);
383
+ }
384
+ cleanupStaleBaselines(dir, protectedArtifacts);
385
+ materializeMergedRuleDir(sources, mergedDir);
386
+ const ruleDirForYaml = mergedDir.split(path.sep).join("/");
387
+ fs.writeFileSync(file, `ruleDirs:
388
+ - ${JSON.stringify(ruleDirForYaml)}
389
+ `);
390
+ cachedBaselines.set(root, { fingerprint, path: file, mergedDir });
391
+ enforceBaselineEntryCap(dir, /* @__PURE__ */ new Set([file, mergedDir]));
392
+ return file;
393
+ }
394
+ var SG_BASELINE_RETAINED_ENTRIES = 24;
395
+ function enforceBaselineEntryCap(dir, keep) {
396
+ let names;
397
+ try {
398
+ names = fs.readdirSync(dir);
399
+ } catch {
400
+ return;
401
+ }
402
+ const candidates = [];
403
+ for (const name of names) {
404
+ const isConfig = /^baseline(?:-\d+(?:-[a-f0-9]+)?)?\.sgconfig\.yml$/.test(name);
405
+ const isMergedDir = /^baseline-\d+(?:-[a-f0-9]+)?\.rules$/.test(name);
406
+ if (!isConfig && !isMergedDir)
407
+ continue;
408
+ const full = path.join(dir, name);
409
+ if (keep.has(full))
410
+ continue;
411
+ try {
412
+ candidates.push({
413
+ full,
414
+ mtimeMs: fs.statSync(full).mtimeMs,
415
+ recursive: isMergedDir
416
+ });
417
+ } catch {
418
+ }
419
+ }
420
+ const over = candidates.length + keep.size - SG_BASELINE_RETAINED_ENTRIES;
421
+ if (over <= 0)
422
+ return;
423
+ candidates.sort((a, b) => a.mtimeMs - b.mtimeMs);
424
+ let evicted = 0;
425
+ for (const candidate of candidates.slice(0, over)) {
426
+ try {
427
+ fs.rmSync(candidate.full, {
428
+ recursive: candidate.recursive,
429
+ force: true
430
+ });
431
+ evicted += 1;
432
+ for (const [root, baseline] of cachedBaselines) {
433
+ if (baseline.path === candidate.full || baseline.mergedDir === candidate.full) {
434
+ cachedBaselines.delete(root);
435
+ }
436
+ }
437
+ } catch {
438
+ }
439
+ }
440
+ if (evicted > 0) {
441
+ recordDegradationOnce({
442
+ kind: "sgconfig-baseline-cap-evict",
443
+ subject: dir,
444
+ reason: `evicted ${evicted} oldest baseline entries over the ${SG_BASELINE_RETAINED_ENTRIES}-entry cap`
445
+ });
446
+ }
447
+ }
448
+ function cleanupStaleBaselines(dir, keep) {
449
+ try {
450
+ const cutoff = Date.now() - 7 * 24 * 60 * 60 * 1e3;
451
+ for (const name of fs.readdirSync(dir)) {
452
+ const isConfig = /^baseline(?:-\d+(?:-[a-f0-9]+)?)?\.sgconfig\.yml$/.test(name);
453
+ const isMergedDir = /^baseline-\d+(?:-[a-f0-9]+)?\.rules$/.test(name);
454
+ if (!isConfig && !isMergedDir)
455
+ continue;
456
+ const full = path.join(dir, name);
457
+ if (keep.has(full))
458
+ continue;
459
+ try {
460
+ const stat2 = fs.statSync(full);
461
+ if (stat2.mtimeMs < cutoff) {
462
+ fs.rmSync(full, { recursive: stat2.isDirectory(), force: true });
463
+ }
464
+ } catch {
465
+ }
466
+ }
467
+ } catch {
468
+ }
469
+ }
470
+
471
+ // dist/clients/typos-config.js
472
+ var LOCAL_TYPOS_CONFIG_NAMES = [
473
+ "typos.toml",
474
+ "_typos.toml",
475
+ ".typos.toml"
476
+ ];
477
+ function findLocalTyposConfig(startDir) {
478
+ return findLocalToolConfig(startDir, LOCAL_TYPOS_CONFIG_NAMES);
479
+ }
480
+
481
+ // dist/clients/lsp/config.js
482
+ import * as os3 from "node:os";
483
+ import path4 from "node:path";
484
+
485
+ // dist/clients/dispatch/known-runner-ids.js
486
+ var knownRunnerIds = /* @__PURE__ */ new Set();
487
+ function registerRunnerId(id) {
488
+ knownRunnerIds.add(id);
489
+ }
490
+ function runnerIdentityPopulated() {
491
+ return knownRunnerIds.size > 0;
492
+ }
493
+ function isKnownRunnerId(id) {
494
+ return knownRunnerIds.has(id);
495
+ }
496
+
497
+ // dist/clients/lsp/launch.js
498
+ import { execFileSync, spawn as nodeSpawn } from "node:child_process";
499
+ import fs3 from "node:fs";
500
+ import os2 from "node:os";
501
+ import path2 from "node:path";
502
+
503
+ // dist/clients/lsp/ruby-drive-dirs.js
504
+ import * as fs2 from "node:fs";
505
+ var RUBY_VERSION_DIR = /^ruby\d/i;
506
+ var rubyDirNamesCache = new BoundedLruCache(16);
507
+ function rubyDriveKey(driveRoot) {
508
+ return normalizeMapKey(driveRoot);
509
+ }
510
+ function filterRubyDirNames(entries) {
511
+ return entries.filter((name) => RUBY_VERSION_DIR.test(name));
512
+ }
513
+ function getRubyVersionDirNamesSync(driveRoot) {
514
+ const key = rubyDriveKey(driveRoot);
515
+ const cached = rubyDirNamesCache.get(key);
516
+ if (cached !== void 0)
517
+ return cached;
518
+ let names;
519
+ try {
520
+ names = filterRubyDirNames(fs2.readdirSync(driveRoot));
521
+ } catch {
522
+ names = [];
523
+ }
524
+ rubyDirNamesCache.set(key, names);
525
+ return names;
526
+ }
527
+ async function getRubyVersionDirNamesAsync(driveRoot) {
528
+ const key = rubyDriveKey(driveRoot);
529
+ const cached = rubyDirNamesCache.get(key);
530
+ if (cached !== void 0)
531
+ return cached;
532
+ let names;
533
+ try {
534
+ names = filterRubyDirNames(await fs2.promises.readdir(driveRoot));
535
+ } catch {
536
+ names = [];
537
+ }
538
+ rubyDirNamesCache.set(key, names);
539
+ return names;
540
+ }
541
+
542
+ // dist/clients/lsp/launch.js
543
+ var isWindows = process.platform === "win32";
544
+ var OWNER_TAG_READ_MS = 5e3;
545
+ function computeNeedsShell(resolvedCommand) {
546
+ return isWindows && (resolvedCommand.includes(" ") || /\.(cmd|bat)$/i.test(resolvedCommand) || !/\.(exe|cmd|bat)$/i.test(resolvedCommand));
547
+ }
548
+ var DEFAULT_STARTUP_FAILURE_WINDOW_MS = 50;
549
+ var WINDOWS_NAV_STARTUP_FAILURE_WINDOW_MS = 500;
550
+ var SESSIONSTART_LOG_DIR = getGlobalPiLensLogDir();
551
+ var SESSIONSTART_LOG = path2.join(SESSIONSTART_LOG_DIR, "sessionstart.log");
552
+ var PI_LENS_BIN_DIR = path2.join(getGlobalPiLensDir(), "bin");
553
+ var PI_LENS_TOOLS_BIN_DIR = path2.join(getGlobalPiLensDir(), "tools", "node_modules", ".bin");
554
+ function logSessionStart2(msg) {
555
+ if (isTestMode()) {
556
+ return;
557
+ }
558
+ const line = redactSecrets(`[${(/* @__PURE__ */ new Date()).toISOString()}] ${msg}
559
+ `);
560
+ try {
561
+ fs3.mkdirSync(SESSIONSTART_LOG_DIR, { recursive: true });
562
+ fs3.appendFileSync(SESSIONSTART_LOG, line);
563
+ } catch {
564
+ }
565
+ }
566
+ function compactLogValue(value, max = 280) {
567
+ const normalized = value.replace(/\s+/g, " ").trim();
568
+ if (!normalized)
569
+ return "";
570
+ return normalized.length > max ? `${normalized.slice(0, max)}...` : normalized;
571
+ }
572
+ function delimiterForPlatform(platform) {
573
+ return platform === "win32" ? ";" : ":";
574
+ }
575
+ function splitPathEntries(value, delimiter) {
576
+ if (!value)
577
+ return [];
578
+ return value.split(delimiter).map((entry) => entry.trim()).filter((entry) => entry.length > 0);
579
+ }
580
+ function normalizePathEntry(entry, platform) {
581
+ if (platform !== "win32")
582
+ return path2.posix.normalize(entry);
583
+ return path2.win32.normalize(entry).toLowerCase();
584
+ }
585
+ function combinePathValuesForPlatform(values, platform = process.platform) {
586
+ const unique = [];
587
+ const seen = /* @__PURE__ */ new Set();
588
+ const delimiter = delimiterForPlatform(platform);
589
+ for (const value of values) {
590
+ for (const entry of splitPathEntries(value, delimiter)) {
591
+ const key = normalizePathEntry(entry, platform);
592
+ if (seen.has(key))
593
+ continue;
594
+ seen.add(key);
595
+ unique.push(entry);
596
+ }
597
+ }
598
+ return unique.join(delimiter);
599
+ }
600
+ function resolvePathValue(env) {
601
+ return combinePathValuesForPlatform([env.PATH, env.Path, env.path]);
602
+ }
603
+ function runStderrGuardedProbe(command, args) {
604
+ try {
605
+ const { execFileSync: execFileSync2 } = __require("node:child_process");
606
+ const out = execFileSync2(command, args, {
607
+ timeout: 3e3,
608
+ encoding: "utf8",
609
+ // #2095 sibling: execFileSync inherits stderr to the parent by
610
+ // default (only stdout is captured into `out`), so a failed
611
+ // powershell.exe launch would print raw diagnostics into the pi
612
+ // TUI even though the surrounding catch already returns "" (read as
613
+ // "unknown" PATH by every caller). Pipe stderr instead of inheriting it.
614
+ stdio: ["ignore", "pipe", "ignore"]
615
+ });
616
+ return out.trim();
617
+ } catch {
618
+ return "";
619
+ }
620
+ }
621
+ function readWindowsRegistryPath() {
622
+ return runStderrGuardedProbe("powershell.exe", [
623
+ "-NoProfile",
624
+ "-NonInteractive",
625
+ "-Command",
626
+ "[System.Environment]::GetEnvironmentVariable('Path','Machine') + ';' + [System.Environment]::GetEnvironmentVariable('Path','User')"
627
+ ]);
628
+ }
629
+ var _liveWindowsPath = null;
630
+ function getLiveWindowsPath() {
631
+ if (_liveWindowsPath === null) {
632
+ _liveWindowsPath = readWindowsRegistryPath();
633
+ }
634
+ return _liveWindowsPath;
635
+ }
636
+ async function buildAugmentedPath(basePath) {
637
+ const candidates = [];
638
+ const nodeDir = path2.dirname(process.execPath);
639
+ if (nodeDir) {
640
+ candidates.push(nodeDir);
641
+ }
642
+ if (isWindows) {
643
+ const home = os2.homedir();
644
+ const driveRoot = path2.parse(home).root;
645
+ candidates.push(path2.join(home, ".cargo", "bin"));
646
+ candidates.push(path2.join(home, "go", "bin"));
647
+ candidates.push(path2.join(home, ".dotnet", "tools"));
648
+ candidates.push(PI_LENS_BIN_DIR);
649
+ candidates.push(PI_LENS_TOOLS_BIN_DIR);
650
+ candidates.push(path2.join(driveRoot, "Program Files", "Go", "bin"));
651
+ candidates.push(path2.join(driveRoot, "Go", "bin"));
652
+ for (const entry of await getRubyVersionDirNamesAsync(driveRoot)) {
653
+ candidates.push(path2.join(driveRoot, entry, "bin"));
654
+ }
655
+ }
656
+ const effectiveBase = isWindows ? combinePathValuesForPlatform([basePath, getLiveWindowsPath()]) : basePath;
657
+ const existing = /* @__PURE__ */ new Set();
658
+ for (const entry of splitPathEntries(effectiveBase, path2.delimiter)) {
659
+ if (!entry)
660
+ continue;
661
+ existing.add(normalizePathEntry(entry, process.platform));
662
+ }
663
+ const toAppend = [];
664
+ for (const candidate of candidates) {
665
+ if (!candidate || !fs3.existsSync(candidate))
666
+ continue;
667
+ const normalized = normalizePathEntry(candidate, process.platform);
668
+ if (existing.has(normalized))
669
+ continue;
670
+ toAppend.push(candidate);
671
+ existing.add(normalized);
672
+ }
673
+ if (toAppend.length === 0)
674
+ return basePath ?? "";
675
+ if (!basePath)
676
+ return toAppend.join(path2.delimiter);
677
+ return `${basePath}${path2.delimiter}${toAppend.join(path2.delimiter)}`;
678
+ }
679
+ function isCmdShimValid(cmdPath) {
680
+ try {
681
+ const content = fs3.readFileSync(cmdPath, "utf-8");
682
+ const match = content.match(/"%~dp0[/\\]\.\.[/\\]((?:[\w./@-]|\\)+\.(?:mjs|cjs|js))"/i);
683
+ if (!match)
684
+ return true;
685
+ const relPath = match[1].replace(/[/\\]/g, path2.sep);
686
+ const target = path2.resolve(path2.dirname(cmdPath), "..", relPath);
687
+ return fs3.existsSync(target);
688
+ } catch {
689
+ return true;
690
+ }
691
+ }
692
+ function bypassPs1OnWindows(ps1Path, args) {
693
+ const cmdPath = `${ps1Path.slice(0, -4)}.cmd`;
694
+ if (fs3.existsSync(cmdPath)) {
695
+ return { command: cmdPath, args, needsShell: true };
696
+ }
697
+ try {
698
+ const content = fs3.readFileSync(ps1Path, "utf-8");
699
+ const match = content.match(/"\$basedir[/\\]\.\.[/\\]((?:[\w./@-]|\\)+\.(?:mjs|cjs|js))"/i);
700
+ if (match) {
701
+ const relPath = match[1].replace(/[/\\]/g, path2.sep);
702
+ const jsPath = path2.resolve(path2.dirname(ps1Path), "..", relPath);
703
+ if (fs3.existsSync(jsPath)) {
704
+ return {
705
+ command: process.execPath,
706
+ args: [jsPath, ...args],
707
+ needsShell: false
708
+ };
709
+ }
710
+ }
711
+ } catch {
712
+ }
713
+ return void 0;
714
+ }
715
+ function findBinaryOnPath(command, env) {
716
+ try {
717
+ const result = execFileSync(isWindows ? "where" : "which", [command], {
718
+ encoding: "utf-8",
719
+ stdio: ["ignore", "pipe", "ignore"],
720
+ env,
721
+ // #1980 population sweep: this was the one synchronous
722
+ // child-process site in the repo with no bound at all. It runs on
723
+ // the LSP server spawn path, so an unresponsive `where`/`which` — a
724
+ // stale network PATH entry is the classic cause — parked the event
725
+ // loop with no ceiling, and read as ordinary compute in
726
+ // `loop_block`. 5s matches `isCommandAvailable`/`findCommand` in
727
+ // clients/safe-spawn.ts, which run these same two binaries.
728
+ timeout: 5e3
729
+ }).split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
730
+ for (const candidate of result) {
731
+ if (fs3.existsSync(candidate)) {
732
+ return candidate;
733
+ }
734
+ }
735
+ } catch {
736
+ }
737
+ return void 0;
738
+ }
739
+ function trySpawn(command, args, cwd, env, needsShell) {
740
+ let proc;
741
+ if (needsShell) {
742
+ const escapeCmdArg = (s) => {
743
+ const escaped = s.replace(/([&|<>^()!])/g, "^$1");
744
+ return /[\s"]/.test(escaped) ? `"${escaped.replace(/"/g, '""')}"` : escaped;
745
+ };
746
+ const shellCommand = `"${command}" ${args.map(escapeCmdArg).join(" ")}`;
747
+ proc = nodeSpawn(shellCommand, [], {
748
+ cwd,
749
+ env,
750
+ stdio: ["pipe", "pipe", "pipe"],
751
+ detached: !isWindows,
752
+ windowsHide: true,
753
+ shell: true
754
+ });
755
+ } else {
756
+ proc = nodeSpawn(command, args, {
757
+ cwd,
758
+ env,
759
+ stdio: ["pipe", "pipe", "pipe"],
760
+ detached: !isWindows,
761
+ windowsHide: isWindows
762
+ });
763
+ }
764
+ if (!proc.stdin || !proc.stdout || !proc.stderr) {
765
+ throw new Error(`Failed to spawn LSP server: ${command}`);
766
+ }
767
+ if (proc.exitCode !== null || proc.killed) {
768
+ throw new Error(`LSP server ${command} exited immediately (code: ${proc.exitCode}). The binary may be missing or corrupted.`);
769
+ }
770
+ holdOwnChildPid(proc.pid, "lsp-spawn");
771
+ return proc;
772
+ }
773
+ function unrefLspProcessHandles(proc) {
774
+ try {
775
+ proc.unref();
776
+ } catch {
777
+ }
778
+ for (const stream of [proc.stdin, proc.stdout, proc.stderr]) {
779
+ try {
780
+ stream?.unref?.();
781
+ } catch {
782
+ }
783
+ }
784
+ }
785
+ function _attachErrorHandler(proc, context, logContext, rejectOnImmediateError) {
786
+ let stderrPreview = "";
787
+ let closeLogged = false;
788
+ const onStderr = (chunk) => {
789
+ if (stderrPreview.length >= 4e3)
790
+ return;
791
+ stderrPreview += chunk.toString();
792
+ };
793
+ proc.stderr?.on("data", onStderr);
794
+ proc.on("error", (err) => {
795
+ if (logContext) {
796
+ logSessionStart2("lsp process " + context + ": spawn-error command=" + logContext.command + " args=" + JSON.stringify(logContext.args) + " cwd=" + logContext.cwd + " pid=" + (logContext.pid ?? 0) + " error=" + err.message + (stderrPreview ? " stderr=" + compactLogValue(stderrPreview) : ""));
797
+ }
798
+ if (rejectOnImmediateError && logContext) {
799
+ void classifySpawnFailure(err, {
800
+ command: logContext.command,
801
+ cwd: logContext.cwd
802
+ }).then(rejectOnImmediateError);
803
+ }
804
+ });
805
+ proc.on("close", (code, signal) => {
806
+ if (closeLogged)
807
+ return;
808
+ closeLogged = true;
809
+ proc.stderr?.off("data", onStderr);
810
+ if (code !== 0 && code !== null) {
811
+ if (logContext) {
812
+ logSessionStart2("lsp process " + context + ": closed code=" + code + (signal ? " signal=" + signal : "") + " command=" + logContext.command + " args=" + JSON.stringify(logContext.args) + " cwd=" + logContext.cwd + " pid=" + (logContext.pid ?? 0) + (stderrPreview ? " stderr=" + compactLogValue(stderrPreview) : ""));
813
+ }
814
+ } else if (signal && logContext) {
815
+ logSessionStart2("lsp process " + context + ": closed signal=" + signal + " command=" + logContext.command + " args=" + JSON.stringify(logContext.args) + " cwd=" + logContext.cwd + " pid=" + (logContext.pid ?? 0) + (stderrPreview ? " stderr=" + compactLogValue(stderrPreview) : ""));
816
+ }
817
+ });
818
+ }
819
+ async function launchLSP(command, args = [], options = {}) {
820
+ const cwd = String(options.cwd ?? process.cwd());
821
+ const mergedEnv = { ...process.env, ...options.env };
822
+ const augmentedPath = await buildAugmentedPath(resolvePathValue(mergedEnv));
823
+ const ownerTag = await ownerTagForChildren({ timeoutMs: OWNER_TAG_READ_MS });
824
+ const env = {
825
+ ...mergedEnv,
826
+ PATH: augmentedPath,
827
+ ...isWindows ? { Path: augmentedPath } : {}
828
+ };
829
+ if (ownerTag === void 0)
830
+ delete env[OWNER_TAG_ENV];
831
+ else
832
+ env[OWNER_TAG_ENV] = ownerTag;
833
+ const isRelativePath = !isFullyQualified(command) && (command.includes(path2.sep) || command.includes("/"));
834
+ const explicitCommand = isRelativePath ? path2.resolve(cwd, command) : command;
835
+ const resolvedCommand = !isFullyQualified(command) && !command.includes(path2.sep) && !command.includes("/") ? findBinaryOnPath(command, env) ?? explicitCommand : explicitCommand;
836
+ let needsShell = computeNeedsShell(resolvedCommand);
837
+ let spawnCommand = resolvedCommand;
838
+ if (!isFullyQualified(command) && !command.includes(path2.sep) && !command.includes("/")) {
839
+ const globalBinPath = await findGlobalBinary(command);
840
+ if (globalBinPath) {
841
+ spawnCommand = globalBinPath;
842
+ needsShell = computeNeedsShell(spawnCommand);
843
+ }
844
+ }
845
+ if (isWindows && /\.(cmd|bat)$/i.test(spawnCommand) && !isCmdShimValid(spawnCommand)) {
846
+ logSessionStart2(`lsp cmd-shim-invalid: ${spawnCommand} target missing \u2014 skipping candidate`);
847
+ const cause = Object.assign(new Error(`LSP .cmd shim target not found: ${spawnCommand}`), { code: "ENOENT" });
848
+ throw await classifySpawnFailure(cause, { command, cwd });
849
+ }
850
+ if (isWindows && /\.ps1$/i.test(spawnCommand)) {
851
+ const bypass = bypassPs1OnWindows(spawnCommand, args);
852
+ if (bypass) {
853
+ logSessionStart2(`lsp ps1-bypass: ${spawnCommand} \u2192 ${bypass.command} shell=${bypass.needsShell}`);
854
+ spawnCommand = bypass.command;
855
+ args = bypass.args;
856
+ needsShell = bypass.needsShell;
857
+ } else {
858
+ logSessionStart2(`lsp ps1-bypass: no .cmd or JS entry found for ${spawnCommand}, spawn may hang`);
859
+ }
860
+ }
861
+ let proc;
862
+ try {
863
+ proc = trySpawn(spawnCommand, args, cwd, env, needsShell);
864
+ } catch (err) {
865
+ if (!isFullyQualified(command) && !command.includes(path2.sep) && !command.includes("/")) {
866
+ const globalBinPath = await findGlobalBinary(command);
867
+ if (globalBinPath && globalBinPath !== spawnCommand) {
868
+ const needsShellGlobal = computeNeedsShell(globalBinPath);
869
+ proc = trySpawn(globalBinPath, args, cwd, env, needsShellGlobal);
870
+ } else {
871
+ throw await classifySpawnFailure(err, { command, cwd });
872
+ }
873
+ } else {
874
+ throw await classifySpawnFailure(err, { command, cwd });
875
+ }
876
+ }
877
+ if (!proc.stdin || !proc.stdout || !proc.stderr) {
878
+ throw new Error(`Failed to spawn LSP server: ${command}`);
879
+ }
880
+ if (proc.exitCode !== null || proc.killed) {
881
+ throw new Error(`LSP server ${command} exited immediately (code: ${proc.exitCode}). The binary may be missing or corrupted.`);
882
+ }
883
+ logSessionStart2(`lsp launch: command=${command} resolved=${spawnCommand} args=${JSON.stringify(args)} cwd=${cwd} shell=${needsShell ? "true" : "false"} pid=${proc.pid ?? 0}`);
884
+ const formatStartupStderr = (stderr) => {
885
+ const normalized = compactLogValue(stderr);
886
+ if (!normalized)
887
+ return "";
888
+ return ` stderr=${normalized}`;
889
+ };
890
+ let startupStderr = "";
891
+ const onStartupStderr = (chunk) => {
892
+ if (startupStderr.length >= 4e3)
893
+ return;
894
+ startupStderr += chunk.toString();
895
+ };
896
+ proc.stderr?.on("data", onStartupStderr);
897
+ try {
898
+ await new Promise((resolve3, reject) => {
899
+ let settled = false;
900
+ proc.on("error", (err) => {
901
+ if (!settled) {
902
+ settled = true;
903
+ void classifySpawnFailure(err, { command, cwd }).then((failure) => {
904
+ const detail = formatStartupStderr(startupStderr);
905
+ reject(detail ? new SpawnFailureError(failure.kind, `${failure.message}${detail}`, failure.cause) : failure);
906
+ });
907
+ }
908
+ });
909
+ proc.on("exit", (code) => {
910
+ if (!settled && code !== null) {
911
+ settled = true;
912
+ const isWindowsCmd = isWindows && command.endsWith(".cmd");
913
+ const errorMsg = isWindowsCmd && code === 1 ? `npm .cmd shim failed (underlying binary not installed). Run 'npm install' in this project or use a global installation.` : `The binary may be missing or corrupted.`;
914
+ reject(new Error(`LSP server ${command} exited immediately with code ${code}. ${errorMsg}${formatStartupStderr(startupStderr)}`));
915
+ }
916
+ });
917
+ const startupFailureWindowMs = (() => {
918
+ if (options?.startupFailureWindowMs) {
919
+ return options.startupFailureWindowMs;
920
+ }
921
+ if (isWindows && needsShell) {
922
+ return WINDOWS_NAV_STARTUP_FAILURE_WINDOW_MS;
923
+ }
924
+ return DEFAULT_STARTUP_FAILURE_WINDOW_MS;
925
+ })();
926
+ setTimeout(() => {
927
+ if (!settled) {
928
+ settled = true;
929
+ resolve3();
930
+ }
931
+ }, startupFailureWindowMs);
932
+ });
933
+ } finally {
934
+ proc.stderr?.off("data", onStartupStderr);
935
+ }
936
+ _attachErrorHandler(proc, command, {
937
+ command: spawnCommand,
938
+ args,
939
+ cwd,
940
+ pid: proc.pid ?? 0
941
+ });
942
+ unrefLspProcessHandles(proc);
943
+ return {
944
+ process: proc,
945
+ stdin: proc.stdin,
946
+ stdout: proc.stdout,
947
+ stderr: proc.stderr,
948
+ pid: proc.pid ?? 0,
949
+ command: spawnCommand,
950
+ args
951
+ };
952
+ }
953
+
954
+ // dist/clients/lsp/session-roots.js
955
+ import path3 from "node:path";
956
+ var SESSION_ROOT_CAP = 128;
957
+ var sessionRoots = new BoundedFifoMap(SESSION_ROOT_CAP);
958
+ function noteEvictedRoots(evicted) {
959
+ const roots = evicted.map(([root]) => root);
960
+ if (roots.length > 0) {
961
+ incrementDegradationCount({
962
+ kind: "lsp-session-root-evicted",
963
+ subject: `cap=${SESSION_ROOT_CAP}`,
964
+ reason: `session root registry at capacity; will load the dropped root again on the next session start or tool call naming it (dropped: ${roots[0]})`
965
+ });
966
+ }
967
+ return roots;
968
+ }
969
+ function registerSessionRoot(cwd) {
970
+ const root = path3.resolve(cwd);
971
+ if (sessionRoots.has(root))
972
+ return [];
973
+ return noteEvictedRoots(sessionRoots.set(root, void 0));
974
+ }
975
+ function setSessionRootConfig(cwd, config) {
976
+ return noteEvictedRoots(sessionRoots.set(path3.resolve(cwd), config));
977
+ }
978
+ function sessionRootConfigEntries() {
979
+ return sessionRoots;
980
+ }
981
+ function isSessionRootRegistered(cwd) {
982
+ return sessionRoots.has(path3.resolve(cwd));
983
+ }
984
+ function shouldInitializeSessionRoot(cwd, readyRoots) {
985
+ const normalized = path3.resolve(cwd);
986
+ return !readyRoots.has(normalized) || !isSessionRootRegistered(normalized);
987
+ }
988
+ function isOutsideAllSessionRoots(filePath) {
989
+ if (sessionRoots.size === 0)
990
+ return false;
991
+ const resolved = path3.resolve(filePath);
992
+ for (const root of sessionRoots.keys()) {
993
+ if (isSameOrWithin(root, resolved))
994
+ return false;
995
+ }
996
+ return true;
997
+ }
998
+ function getSessionRootsForTelemetry(limit = 4) {
999
+ return [...sessionRoots.keys()].sort((a, b) => Number(a > b) - Number(a < b)).slice(0, limit);
1000
+ }
1001
+
1002
+ // dist/clients/lsp/config.js
1003
+ function asRecord(value) {
1004
+ return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
1005
+ }
1006
+ var CONFIG_RESOLVED_PHASE = "config_resolved";
1007
+ function configResolutionKey(cwd) {
1008
+ return normalizeFilePath(path4.resolve(normalizeFilePath(cwd)));
1009
+ }
1010
+ function publishConfigResolutionPending(cwd) {
1011
+ logSessionStart(`session_start config_resolution_pending session=${currentSessionRecordId()} root=${configResolutionKey(cwd)}`);
1012
+ }
1013
+ function recordConfigResolved(cwd, resolution, lspConfig, homeDir, resolveMs) {
1014
+ const root = configResolutionKey(cwd);
1015
+ if (!claimPhaseOncePerSession(CONFIG_RESOLVED_PHASE, root)) {
1016
+ return;
1017
+ }
1018
+ const summary = summarizeConfigResolution(resolution, homeDir);
1019
+ const sessionId = currentSessionRecordId();
1020
+ const deniedServers = lspConfig.disabledServers?.length ?? 0;
1021
+ const legacyDocuments = summary.documents.filter((document) => document.legacy).length;
1022
+ logLatency({
1023
+ type: "phase",
1024
+ phase: CONFIG_RESOLVED_PHASE,
1025
+ filePath: root,
1026
+ durationMs: resolveMs,
1027
+ metadata: {
1028
+ sessionId,
1029
+ documents: summary.documents,
1030
+ countsByTier: summary.countsByTier,
1031
+ recordCount: summary.recordCount,
1032
+ deniedServers,
1033
+ resolveMs
1034
+ }
1035
+ });
1036
+ logSessionStart(`config resolved documents=${summary.documents.length} legacy=${legacyDocuments} records=${summary.recordCount} deniedServers=${deniedServers} resolveMs=${resolveMs} session=${sessionId} root=${root}`);
1037
+ }
1038
+ async function loadLSPConfig(cwd, homeDir = os3.homedir(), options = {}) {
1039
+ const reporting = options.report !== false;
1040
+ publishConfigResolutionPending(cwd);
1041
+ const resolveStartedAt = Date.now();
1042
+ const resolution = resolvePiLensConfig({
1043
+ cwd,
1044
+ globalDir: getGlobalPiLensDir(),
1045
+ // The global tier reads the PRODUCTION resolution (global-config-location
1046
+ // PR, refs #2457): which file supplies it is an env fact (PI_LENS_CONFIG_PATH,
1047
+ // then a legacy default, then PI_CODING_AGENT_DIR, then the
1048
+ // PI_LENS_HOME-relocated canonical default), not a property of the
1049
+ // `$HOME` this call's project walk is ceiling-bounded by. `homeDir`
1050
+ // stays threaded below for the walk and `globalDir`; a test redirecting
1051
+ // the global tier does it through the env, the same way production is
1052
+ // controlled.
1053
+ globalConfigPath: getPiLensGlobalConfigPath(),
1054
+ homeDir,
1055
+ // The subsystem comes from the failing DOCUMENT, not from this loader
1056
+ // (#2445). This resolution opens `~/.pi-lens/config.json` and
1057
+ // `.pi-lens.json` as well as the LSP-scoped files, and reporting all of
1058
+ // them as `lsp-config` announced an "invalid LSP config" for a file whose
1059
+ // contents are pi-lens settings. An LSP-scoped file still reports here.
1060
+ ...reporting ? { onReadError: reportConfigReadFailure } : {}
1061
+ });
1062
+ const coversProblems = newCoversClaimProblems();
1063
+ const config = lspConfigOf(resolution.value, coversProblems);
1064
+ if (reporting) {
1065
+ reportPiLensConfigRecords([
1066
+ ...resolution.records,
1067
+ ...coversClaimRecords(resolution, coversProblems)
1068
+ ]);
1069
+ }
1070
+ recordCoversUnvalidated(resolution, coversProblems);
1071
+ recordConfigResolved(cwd, resolution, config, homeDir, Date.now() - resolveStartedAt);
1072
+ return config;
1073
+ }
1074
+ function newCoversClaimProblems() {
1075
+ return { refusals: [], unvalidated: [] };
1076
+ }
1077
+ function coversPointer(serverId) {
1078
+ return `/${LSP_NAMESPACE_KEY}/servers/${serverId}/covers`;
1079
+ }
1080
+ function coversClaimSource(resolution, pointer) {
1081
+ let candidate = pointer;
1082
+ while (candidate !== void 0) {
1083
+ const entry = resolution.provenance.get(candidate);
1084
+ if (entry) {
1085
+ return {
1086
+ file: entry.file ?? "",
1087
+ ...entry.tier ? { tier: entry.tier } : {}
1088
+ };
1089
+ }
1090
+ const cut = candidate.lastIndexOf("/");
1091
+ candidate = cut > 0 ? candidate.slice(0, cut) : void 0;
1092
+ }
1093
+ return { file: resolution.documents.at(-1)?.file ?? "" };
1094
+ }
1095
+ function coversClaimRecords(resolution, problems) {
1096
+ return problems.refusals.map((refusal) => {
1097
+ const pointer = coversPointer(refusal.serverId);
1098
+ const { file, tier } = coversClaimSource(resolution, pointer);
1099
+ const dropped = refusal.declaredCount - refusal.keptCount;
1100
+ return {
1101
+ code: "PILENS_CFG_0005",
1102
+ file,
1103
+ key: pointer,
1104
+ subject: migrationSubject(file, pointer),
1105
+ reason: `lsp.servers.${refusal.serverId}.covers declares member(s) that are not recognized dispatch runner ids; ${dropped} of ${refusal.declaredCount} member(s) dropped, the server still ` + keptTail(refusal.keptCount),
1106
+ ...tier ? { tier } : {}
1107
+ };
1108
+ });
1109
+ }
1110
+ function keptTail(keptCount) {
1111
+ return keptCount > 0 ? "registers with the remaining covers claim" : "registers with no covers claim";
1112
+ }
1113
+ function recordCoversUnvalidated(resolution, problems) {
1114
+ for (const unvalidated of problems.unvalidated) {
1115
+ const pointer = coversPointer(unvalidated.serverId);
1116
+ const { file } = coversClaimSource(resolution, pointer);
1117
+ recordDegradationOnce({
1118
+ kind: "lsp-covers-unvalidated",
1119
+ subject: `${file}\0${pointer}`,
1120
+ reason: "no runner registry has populated this process yet; the covers claim was accepted without runner-id validation and the next config load validates it",
1121
+ metadata: { pointer }
1122
+ });
1123
+ }
1124
+ }
1125
+ function coversValidatedServers(servers, problems = void 0) {
1126
+ const out = {};
1127
+ for (const [id, raw] of Object.entries(servers)) {
1128
+ const entry = raw;
1129
+ if (!entry.covers?.length) {
1130
+ out[id] = entry;
1131
+ continue;
1132
+ }
1133
+ if (!runnerIdentityPopulated()) {
1134
+ problems?.unvalidated.push({ serverId: id });
1135
+ out[id] = entry;
1136
+ continue;
1137
+ }
1138
+ const kept = [];
1139
+ for (const member of entry.covers) {
1140
+ if (isKnownRunnerId(member))
1141
+ kept.push(member);
1142
+ }
1143
+ if (kept.length === entry.covers.length) {
1144
+ out[id] = entry;
1145
+ continue;
1146
+ }
1147
+ problems?.refusals.push({
1148
+ serverId: id,
1149
+ declaredCount: entry.covers.length,
1150
+ keptCount: kept.length
1151
+ });
1152
+ const { covers: _dropped, ...rest } = entry;
1153
+ out[id] = kept.length > 0 ? { ...rest, covers: kept } : rest;
1154
+ }
1155
+ return out;
1156
+ }
1157
+ function customServerSpecsOf(value) {
1158
+ const servers = asRecord(lspSectionOf(value).servers);
1159
+ return servers ? coversValidatedServers(servers) : {};
1160
+ }
1161
+ function lspConfigOf(value, problems) {
1162
+ const section = lspSectionOf(value);
1163
+ const config = {};
1164
+ const servers = asRecord(section.servers);
1165
+ if (servers)
1166
+ config.servers = coversValidatedServers(servers, problems);
1167
+ const serverOverrides = asRecord(section.serverOverrides);
1168
+ if (serverOverrides) {
1169
+ config.serverOverrides = serverOverrides;
1170
+ }
1171
+ if (Array.isArray(section.disabledServers)) {
1172
+ config.disabledServers = section.disabledServers;
1173
+ }
1174
+ if (Array.isArray(section.warmFiles)) {
1175
+ config.warmFiles = section.warmFiles;
1176
+ }
1177
+ return config;
1178
+ }
1179
+ function createCustomServer(config, id) {
1180
+ return {
1181
+ id,
1182
+ name: config.name,
1183
+ custom: true,
1184
+ // The own-command token the runner-coverage seam gates this row's
1185
+ // covers claim on (#3968 F2): the declared covering lane's binary must
1186
+ // be probeable before the claim can defer a CLI runner.
1187
+ command: config.command,
1188
+ extensions: config.extensions,
1189
+ // A config-declared server is a language server. The public
1190
+ // `lsp.servers.<id>.role` field is validated and projected by
1191
+ // `ResolvedLspConfig` (clients/lsp/resolved-config.ts) but stays
1192
+ // RESERVED and inert here: honouring it would change which servers a
1193
+ // file selects as primary, and that is the catalog slice's call, not
1194
+ // this loader's (#2416 slice 1). The stated default is applied rather
1195
+ // than left absent (#1488).
1196
+ role: DEFAULT_LSP_SERVER_ROLE,
1197
+ idleEviction: "unmeasured",
1198
+ // The config-declared covers channel (#3968): the claim the loader
1199
+ // validated (`lspConfigOf`'s projection drops unknown runner ids) rides
1200
+ // the server entry into the runner-coverage seam.
1201
+ ...config.covers ? { covers: config.covers } : {},
1202
+ ...config.rootMarkers ? { rootMarkers: config.rootMarkers } : {},
1203
+ root: config.rootMarkers ? async (file) => resolveToolCwd("lsp", id, file, {
1204
+ cwd: process.cwd(),
1205
+ ...config.rootMarkers ? { rootMarkers: config.rootMarkers } : {}
1206
+ }).cwd : async (file) => resolveToolCwd("lsp", id, file, { cwd: process.cwd() }).cwd,
1207
+ async spawn(root) {
1208
+ const proc = await launchLSP(config.command, config.args ?? ["--stdio"], {
1209
+ cwd: root,
1210
+ env: config.env ? { ...process.env, ...config.env } : process.env
1211
+ });
1212
+ return { process: proc };
1213
+ }
1214
+ };
1215
+ }
1216
+ var EMPTY_CONFIG = {
1217
+ customServers: [],
1218
+ disabledServerIds: /* @__PURE__ */ new Set(),
1219
+ serverOverrides: /* @__PURE__ */ new Map()
1220
+ };
1221
+ var configInFlight = /* @__PURE__ */ new Map();
1222
+ function normalizeWorkspacePath(cwd) {
1223
+ return path4.resolve(cwd);
1224
+ }
1225
+ function isSameOrChildPath(filePath, candidateRoot) {
1226
+ if (filePath === candidateRoot)
1227
+ return true;
1228
+ return filePath.startsWith(`${candidateRoot}${path4.sep}`);
1229
+ }
1230
+ function getConfigForFile(filePath) {
1231
+ const resolvedFilePath = path4.resolve(filePath);
1232
+ let bestMatch;
1233
+ for (const [root, config] of sessionRootConfigEntries()) {
1234
+ if (config === void 0)
1235
+ continue;
1236
+ if (!isSameOrChildPath(resolvedFilePath, root))
1237
+ continue;
1238
+ if (!bestMatch || root.length > bestMatch.root.length) {
1239
+ bestMatch = { root, config };
1240
+ }
1241
+ }
1242
+ return bestMatch?.config ?? EMPTY_CONFIG;
1243
+ }
1244
+ function registerLSPConfig(config) {
1245
+ const customServers = [];
1246
+ const disabledServerIds = new Set(config.disabledServers ?? []);
1247
+ if (config.servers) {
1248
+ for (const [id, serverConfig] of Object.entries(config.servers)) {
1249
+ try {
1250
+ const server = createCustomServer(serverConfig, id);
1251
+ customServers.push(server);
1252
+ } catch {
1253
+ }
1254
+ }
1255
+ }
1256
+ const serverOverrides = /* @__PURE__ */ new Map();
1257
+ if (config.serverOverrides) {
1258
+ for (const [id, entry] of Object.entries(config.serverOverrides)) {
1259
+ if (entry && typeof entry === "object" && !Array.isArray(entry)) {
1260
+ const initOpts = entry.initializationOptions;
1261
+ if (initOpts !== void 0 && typeof initOpts === "object" && initOpts !== null && !Array.isArray(initOpts)) {
1262
+ serverOverrides.set(id, {
1263
+ initializationOptions: initOpts
1264
+ });
1265
+ }
1266
+ }
1267
+ }
1268
+ }
1269
+ return { customServers, disabledServerIds, serverOverrides };
1270
+ }
1271
+ function forgetConfigResolvedClaims(evictedRoots) {
1272
+ for (const root of evictedRoots) {
1273
+ releasePhaseClaim(CONFIG_RESOLVED_PHASE, configResolutionKey(root));
1274
+ }
1275
+ }
1276
+ async function initLSPConfig(cwd) {
1277
+ const normalizedCwd = normalizeWorkspacePath(cwd);
1278
+ forgetConfigResolvedClaims(registerSessionRoot(normalizedCwd));
1279
+ const existing = configInFlight.get(normalizedCwd);
1280
+ if (existing)
1281
+ return existing;
1282
+ const promise = (async () => {
1283
+ const config = registerLSPConfig(await loadLSPConfig(cwd, os3.homedir()));
1284
+ forgetConfigResolvedClaims(setSessionRootConfig(normalizedCwd, config));
1285
+ })();
1286
+ configInFlight.set(normalizedCwd, promise);
1287
+ try {
1288
+ await promise;
1289
+ } finally {
1290
+ if (configInFlight.get(normalizedCwd) === promise) {
1291
+ configInFlight.delete(normalizedCwd);
1292
+ }
1293
+ }
1294
+ }
1295
+ function registeredServers(config) {
1296
+ return [...LSP_SERVERS, ...config.customServers];
1297
+ }
1298
+ function selectionReason(server, config, filePath, ext, base) {
1299
+ if (config.disabledServerIds.has(server.id))
1300
+ return "disabled-by-config";
1301
+ let matched = false;
1302
+ for (const value of server.extensions) {
1303
+ const lower = value.toLowerCase();
1304
+ if (lower === ext || lower === base) {
1305
+ matched = true;
1306
+ break;
1307
+ }
1308
+ }
1309
+ if (!matched)
1310
+ return "extension-mismatch";
1311
+ if (server.pathFilter && !server.pathFilter(filePath))
1312
+ return "path-filter";
1313
+ return "selected";
1314
+ }
1315
+ function explainServersForFile(filePath, config = getConfigForFile(filePath)) {
1316
+ const ext = path4.extname(filePath).toLowerCase();
1317
+ const base = path4.basename(filePath).toLowerCase();
1318
+ return registeredServers(config).map((server) => {
1319
+ const reason = selectionReason(server, config, filePath, ext, base);
1320
+ return { server, selected: reason === "selected", reason };
1321
+ });
1322
+ }
1323
+ function getServersForFileWithConfig(filePath) {
1324
+ const config = getConfigForFile(filePath);
1325
+ const ext = path4.extname(filePath).toLowerCase();
1326
+ const base = path4.basename(filePath).toLowerCase();
1327
+ return registeredServers(config).filter((server) => selectionReason(server, config, filePath, ext, base) === "selected");
1328
+ }
1329
+ function primaryServerEntry(filePath) {
1330
+ return getServersForFileWithConfig(filePath).find((s) => !isAuxiliary(s));
1331
+ }
1332
+ function primaryServerId(filePath) {
1333
+ return primaryServerEntry(filePath)?.id;
1334
+ }
1335
+ async function resolveLspCwdForFile(filePath, sessionCwd, onRootFallback) {
1336
+ const primary = primaryServerEntry(filePath);
1337
+ if (!primary)
1338
+ return void 0;
1339
+ return resolveLspServerCwd(primary, filePath, sessionCwd, void 0, onRootFallback);
1340
+ }
1341
+ function getServerInitOverride(serverId, filePath) {
1342
+ return getConfigForFile(filePath).serverOverrides.get(serverId);
1343
+ }
1344
+
1345
+ // dist/clients/lsp/wait-policy/strategies.js
1346
+ import { createRequire } from "node:module";
1347
+ var _require = createRequire(import.meta.url);
1348
+ function astGrepNativePackageName(platform, arch) {
1349
+ switch (platform) {
1350
+ case "win32":
1351
+ if (arch === "x64")
1352
+ return "@ast-grep/cli-win32-x64-msvc";
1353
+ if (arch === "arm64")
1354
+ return "@ast-grep/cli-win32-arm64-msvc";
1355
+ if (arch === "ia32")
1356
+ return "@ast-grep/cli-win32-ia32-msvc";
1357
+ return void 0;
1358
+ case "darwin":
1359
+ if (arch === "arm64")
1360
+ return "@ast-grep/cli-darwin-arm64";
1361
+ if (arch === "x64")
1362
+ return "@ast-grep/cli-darwin-x64";
1363
+ return void 0;
1364
+ case "linux":
1365
+ if (arch === "x64")
1366
+ return "@ast-grep/cli-linux-x64-gnu";
1367
+ if (arch === "arm64")
1368
+ return "@ast-grep/cli-linux-arm64-gnu";
1369
+ return void 0;
1370
+ default:
1371
+ return void 0;
1372
+ }
1373
+ }
1374
+ function resolveAstGrepNativeExe(platform = process.platform, arch = process.arch) {
1375
+ const pkgName = astGrepNativePackageName(platform, arch);
1376
+ if (!pkgName)
1377
+ return void 0;
1378
+ const binaryName = platform === "win32" ? "ast-grep.exe" : "ast-grep";
1379
+ try {
1380
+ return _require.resolve(`${pkgName}/${binaryName}`);
1381
+ } catch {
1382
+ return void 0;
1383
+ }
1384
+ }
1385
+ var SERVER_DIAGNOSTIC_STRATEGIES = {
1386
+ typescript: {
1387
+ seedFirstPush: true,
1388
+ pullRetryBudgetMs: 0,
1389
+ debounceMs: 50,
1390
+ aggregateWaitMs: 1e3,
1391
+ expectSemanticSecondPush: false,
1392
+ // Tier 3 (#458): typescript-language-server publishes nothing on a
1393
+ // clean→clean edit (docs/lsp-capability-matrix.md, re-confirmed
1394
+ // 2026-07-12). It's the lone core-set tier-3 server, which is exactly
1395
+ // why the cascade lane's in-lane wait is worth skipping for it
1396
+ // specifically. Applies to the CLASSIC server only (#524/#529/#558)
1397
+ // — TS7's native `tsc --lsp --stdio` variant shares this
1398
+ // "typescript" server id but the 2026-07-12 dual-environment
1399
+ // re-measurement found it now publishes on clean (a drift from the
1400
+ // #541 measurement); wait-policy/classification.ts's classifier checks the live
1401
+ // snapshot's launchVariant and never applies this flag to a
1402
+ // native-ts7 instance.
1403
+ silentOnClean: true
1404
+ },
1405
+ "rust-analyzer": {
1406
+ seedFirstPush: false,
1407
+ pullRetryBudgetMs: 500,
1408
+ debounceMs: 150,
1409
+ aggregateWaitMs: 3e3,
1410
+ expectSemanticSecondPush: true
1411
+ },
1412
+ // PythonServer (pyright / basedpyright) — openFilesOnly mode: lazy per-file
1413
+ // analysis, startup similar to jedi. seedFirstPush: true because pyright's
1414
+ // first publishDiagnostics after didOpen is the complete result for that file.
1415
+ python: {
1416
+ seedFirstPush: true,
1417
+ pullRetryBudgetMs: 0,
1418
+ debounceMs: 100,
1419
+ aggregateWaitMs: 1500,
1420
+ expectSemanticSecondPush: false
1421
+ },
1422
+ // jedi-language-server is push-only (no pull diagnostics) and its FIRST
1423
+ // publishDiagnostics is the complete result (seedFirstPush). But that first
1424
+ // push lands just after didOpen+~1s on cold start (Python/parso import) —
1425
+ // measured ~1011ms — so a 1000ms aggregate budget misses it by a hair and
1426
+ // returns zero. 3000ms gives cold-start headroom without stalling the warm path.
1427
+ "python-jedi": {
1428
+ seedFirstPush: true,
1429
+ pullRetryBudgetMs: 0,
1430
+ debounceMs: 100,
1431
+ aggregateWaitMs: 3e3,
1432
+ expectSemanticSecondPush: false
1433
+ },
1434
+ eslint: {
1435
+ seedFirstPush: true,
1436
+ pullRetryBudgetMs: 0,
1437
+ debounceMs: 200,
1438
+ aggregateWaitMs: 2e3,
1439
+ expectSemanticSecondPush: false
1440
+ },
1441
+ // Opengrep security scanner (cross-language LSP). It pushes an EMPTY result
1442
+ // during the one-time rule-load window at startup, then the real scan after
1443
+ // `semgrep/rulesRefreshed` — so never seed the first push. Push-only (no pull
1444
+ // diagnostics). Warm per-file scan ~1.3s; the first touch in a session may
1445
+ // also pay rule-load (~3.5s cold). aggregateWaitMs is 3500: it's this
1446
+ // server's OWN deadline, bounded by the per-edit caller cap as a ceiling
1447
+ // (#242), so on a 2500ms-capped edit opengrep waits min(2500, 3500)=2500
1448
+ // and on uncapped paths it gets its full 3500. 3500 covers warm and most
1449
+ // cold; a cold scan that overruns isn't lost — late diagnostics are cached
1450
+ // and surface on the next unchanged-content read through the content-bound
1451
+ // auxiliary carry-over path.
1452
+ opengrep: {
1453
+ seedFirstPush: false,
1454
+ pullRetryBudgetMs: 0,
1455
+ debounceMs: 250,
1456
+ aggregateWaitMs: 3500,
1457
+ expectSemanticSecondPush: false,
1458
+ // Opengrep re-scans only on a fresh didOpen — didChange is a no-op for it.
1459
+ reopenOnResync: true,
1460
+ rescansOnSave: true
1461
+ },
1462
+ // ast-grep structural linter (sgconfig-gated auxiliary LSP). Push-only,
1463
+ // compiles the project rules on the first scan of a session, and — like
1464
+ // Opengrep — is re-synced via didClose+didOpen so edits trigger a re-scan.
1465
+ // Conservative budget until measured against real projects (#239).
1466
+ // ast-grep re-scans on didChange (verified: toggling the violation count
1467
+ // 3→1→4→2 returns the correct fresh count each touch, with matching doc
1468
+ // versions), so reopen isn't needed and didChange is the lighter path.
1469
+ // aggregateWaitMs is 1800: its warm scan is ~1.3s, so 1000 was under-budgeted
1470
+ // (only masked before because the old with-auxiliary deadline was a global
1471
+ // max() floor; now each server has its own caller-cap-bounded deadline, #242,
1472
+ // so the budget must actually cover the scan).
1473
+ "ast-grep": {
1474
+ seedFirstPush: false,
1475
+ pullRetryBudgetMs: 0,
1476
+ debounceMs: 150,
1477
+ aggregateWaitMs: 1800,
1478
+ expectSemanticSecondPush: false,
1479
+ reopenOnResync: false
1480
+ },
1481
+ // zizmor (GitHub Actions security scanner, auxiliary LSP #272). Push-only
1482
+ // (no pull diagnostics). Its audit set is compiled-in — there's no rule-load
1483
+ // window like Opengrep — so the FIRST publishDiagnostics after didOpen IS the
1484
+ // complete result: seedFirstPush. It re-scans on didChange (FULL sync), so no
1485
+ // reopen-on-resync. A native single-workflow audit is sub-second offline;
1486
+ // online mode may add a GitHub-API round-trip, so 2000ms gives headroom
1487
+ // (bounded by the per-edit caller cap as a ceiling, #242), and any late online
1488
+ // finding can surface on the next unchanged-content read through the
1489
+ // content-bound auxiliary carry-over path.
1490
+ zizmor: {
1491
+ seedFirstPush: true,
1492
+ pullRetryBudgetMs: 0,
1493
+ debounceMs: 150,
1494
+ aggregateWaitMs: 2e3,
1495
+ expectSemanticSecondPush: false,
1496
+ reopenOnResync: false,
1497
+ // #3548: zizmor@main `crates/zizmor/src/lsp.rs` `initialized()`
1498
+ // registers `textDocument/didSave` DYNAMICALLY (`include_text: true`),
1499
+ // and `did_save` re-audits and republishes with `version: None` — the
1500
+ // same surplus shape as opengrep's static save rescan (#3482). This is
1501
+ // presently INERT: `applyDynamicCapabilities` (client.ts) does not map
1502
+ // a dynamic didSave registration to anything, and `negotiateSaveOptions`
1503
+ // (sync-kind.ts) reads only the static `initialize` reply, so
1504
+ // `state.saveOptions` never gets set for zizmor and `sendDidSave` never
1505
+ // fires (its `if (!save) return;` guard). Set now anyway, measured
1506
+ // fact style (like `silentOnClean`'s launch-variant scoping above): the
1507
+ // day dynamic registration is honoured, this is the correct value with
1508
+ // no further plumbing, and `expectSaveRescan`/`rescansOnSave`'s
1509
+ // counting logic does not care how `saveOptions` got set — proven by
1510
+ // `ZIZMOR-SAVE-RESCAN-EXPECTED` in
1511
+ // tests/clients/lsp/late-auxiliary-findings.test.ts, which sets
1512
+ // `state.saveOptions` directly (the same shape as the pre-existing
1513
+ // `SAVE-RESCAN-EXPECTED` opengrep test) rather than through the
1514
+ // unbuilt dynamic-capability path.
1515
+ rescansOnSave: true
1516
+ },
1517
+ // typos (source-code spell checker, auxiliary LSP #283). Push-only (no pull
1518
+ // diagnostics). Its dictionary is compiled in — there's NO rule-load window
1519
+ // like Opengrep — so the FIRST publishDiagnostics after didOpen IS the
1520
+ // complete result: seedFirstPush. It re-scans on didChange (FULL sync), so no
1521
+ // reopen-on-resync. A native single-file spell scan is sub-100ms, so the seed
1522
+ // arrives near-instantly; aggregateWaitMs is a generous ceiling (bounded by
1523
+ // the per-edit caller cap, #242) that the early seed resolves well under.
1524
+ typos: {
1525
+ seedFirstPush: true,
1526
+ pullRetryBudgetMs: 0,
1527
+ debounceMs: 150,
1528
+ aggregateWaitMs: 1500,
1529
+ expectSemanticSecondPush: false,
1530
+ reopenOnResync: false,
1531
+ // #3548: see `publishesOnClose`'s doc comment above — typos'
1532
+ // `did_close` always publishes an empty, version-less set.
1533
+ publishesOnClose: true
1534
+ },
1535
+ // marksman (Markdown LSP, #274). Push-based; native binary so the per-file
1536
+ // parse is fast, but its value is CROSS-file (broken intra-repo links,
1537
+ // missing anchors/heading refs) which needs the workspace index — so the
1538
+ // first push after didOpen can be empty before indexing completes. Don't
1539
+ // seed it (like opengrep/rust-analyzer); a modest 1500ms aggregate covers
1540
+ // warm edits, and any late cross-file finding surfaces on the next touch.
1541
+ marksman: {
1542
+ seedFirstPush: false,
1543
+ pullRetryBudgetMs: 0,
1544
+ debounceMs: 150,
1545
+ aggregateWaitMs: 1500,
1546
+ expectSemanticSecondPush: false,
1547
+ // #645: a full-tree sweep opens every markdown file, all racing the
1548
+ // SAME one-time workspace index build — pay the 1500ms budget once
1549
+ // per sweep (the first markdown file touched), not once per file.
1550
+ // 250ms covers a warm per-file publish (native binary, fast parse)
1551
+ // once the index has already had a full aggregateWaitMs window.
1552
+ workspaceIndexing: true,
1553
+ workspaceIndexingWarmWaitMs: 250,
1554
+ // #799 / run 35914033696: marksman is push-only and publishes NOTHING on
1555
+ // a clean transition — there is no pull fallback and no sync-confirm protocol (unlike
1556
+ // typescript's tsserver commands), so a clean markdown file's touch
1557
+ // waits its full budget with zero signal either way. Marking it
1558
+ // `silentOnClean` lets the generic push-only clean-confirm gate
1559
+ // (`touchFile`, the `classifyCascadeWaitTier`-driven fallback next to
1560
+ // the tsserver sync-confirm block) treat "no publish within budget,
1561
+ // notify succeeded" as CONFIRMED clean instead of inconclusive/timed
1562
+ // out — closing the observed 2x20s-per-sweep burn on clean markdown
1563
+ // (evidence: agents.md timed out at 20009ms/20014ms on both the
1564
+ // warm-up attempt and its retry, with zero diagnostics either time).
1565
+ silentOnClean: true
1566
+ },
1567
+ // cue lsp serve (#1522, #1519, #1520). Measured directly against the real
1568
+ // v0.17.1 binary (`getWorkspaceDiagnosticsSupport` reports
1569
+ // `mode: "push-only"`; no pull support advertised).
1570
+ //
1571
+ // It reports load/parse (syntax) errors as you type, but leaves
1572
+ // conflicting-value/constraint-failure (evaluation) errors to `cue vet`
1573
+ // — the #1522 cue-vet auxiliary runner covers that gap. So the syntax
1574
+ // diagnostics this strategy governs are a REAL but PARTIAL signal, not
1575
+ // full CUE validation.
1576
+ //
1577
+ // The repaired clean-signal probe measured cue as publishing a versioned
1578
+ // clean-transition set (run 35914033696), so this marker stays absent:
1579
+ // the cascade must retain its normal early-publish path.
1580
+ //
1581
+ // seedFirstPush: true — the one cold-open case that DOES publish (a
1582
+ // file that is already broken) sends the complete single diagnostic on
1583
+ // its first push; there is no observed partial-then-complete sequence to
1584
+ // wait out.
1585
+ //
1586
+ // aggregateWaitMs: 2000 covers the measured warm edit-to-publish latency
1587
+ // (~930ms, syntax reparse) with margin; a cold spawn's first push (~2.1s
1588
+ // end-to-end including process start) is bounded separately by
1589
+ // maxClientWaitMs, not this per-diagnostics budget.
1590
+ // intelephense (php, #3310). Measured directly against the real v1.18.5
1591
+ // binary over a raw JSON-RPC session answering `workspace/configuration`
1592
+ // the way pi-lens does (2026-09-23, linux):
1593
+ //
1594
+ // +284ms initialize response — NO diagnosticProvider (push-only)
1595
+ // +295ms publishDiagnostics diags=0 version=undefined <- pre-index
1596
+ // +301ms indexingStarted
1597
+ // +689ms indexingEnded
1598
+ // +696ms publishDiagnostics diags=2 version=undefined <- the answer
1599
+ //
1600
+ // so `emptyFirstPublish: "indexing"`: the first publish is an artifact of
1601
+ // the cold index, not an answer. The same session on a genuinely CLEAN
1602
+ // file publishes `[]` at +304ms and `[]` AGAIN at +663ms (right after
1603
+ // `indexingEnded`), which is what lets the held publish be released by
1604
+ // the server's own second publish instead of by a timer — and a WARM
1605
+ // touch (a second file, index already built) publishes the real set
1606
+ // FIRST at +5ms, so the hold costs nothing once the session is warm.
1607
+ //
1608
+ // seedFirstPush: false — the measurement above is the direct refutation
1609
+ // of "the first push is complete" for this server.
1610
+ // pullRetryBudgetMs: 0 — no pull provider is advertised at all.
1611
+ // aggregateWaitMs: 8000 — the budget must cover the cold index window,
1612
+ // which is what the second (real) publish waits on: ~0.4s for a 139-file
1613
+ // index on the dev box, ~5s on the nightly ubuntu runner (#3217's
1614
+ // transcript). Per-edit callers cap this as a CEILING (#242), so an
1615
+ // interactive edit still waits only its own cap; the uncapped paths (the
1616
+ // tool-smoke LSP gate, which itself caps at 8000ms) get the full budget,
1617
+ // and the cold index is paid once per session, not once per touch.
1618
+ php: {
1619
+ seedFirstPush: false,
1620
+ pullRetryBudgetMs: 0,
1621
+ debounceMs: 150,
1622
+ aggregateWaitMs: 8e3,
1623
+ expectSemanticSecondPush: false,
1624
+ emptyFirstPublish: "indexing",
1625
+ // #3484: the fence reply (+2..4 ms) precedes the publish (~+1 s).
1626
+ diagnosticsFence: "reply-first"
1627
+ },
1628
+ // terraform-ls (#3310). The nightly tool-smoke measurement was identical
1629
+ // on 2026-09-27, 2026-09-28, and 2026-09-29:
1630
+ //
1631
+ // [terraform] clean-behavior=publishes-unversioned (tier 2*)
1632
+ // dirtyPubs=2(v:0) cleanPubs=2(v:0) first-publish=empty-first
1633
+ // provisional pre-index publish
1634
+ //
1635
+ // The first empty, unversioned publish is the cold-index artifact, so hold
1636
+ // it once and let terraform-ls's next publish resolve the wait. A held
1637
+ // clean-only trace therefore reaches the caller as unanswered/indeterminate
1638
+ // at the wait ceiling rather than a false clean, by design (#3310). The
1639
+ // marker is measured policy, not a blanket rule for Tier 2* push servers.
1640
+ terraform: {
1641
+ seedFirstPush: false,
1642
+ pullRetryBudgetMs: 0,
1643
+ debounceMs: 150,
1644
+ aggregateWaitMs: 1500,
1645
+ expectSemanticSecondPush: false,
1646
+ emptyFirstPublish: "indexing"
1647
+ },
1648
+ // yaml-language-server (#3484): the default strategy plus the measured
1649
+ // fence order — reply +3..6 ms, publish +207..242 ms after didChange.
1650
+ yaml: {
1651
+ seedFirstPush: false,
1652
+ pullRetryBudgetMs: 250,
1653
+ debounceMs: 150,
1654
+ aggregateWaitMs: 1500,
1655
+ expectSemanticSecondPush: false,
1656
+ diagnosticsFence: "reply-first"
1657
+ },
1658
+ // Svelte's pull diagnostics settle after the default 1500ms budget on a
1659
+ // cold server. The tool-smoke gate caps waits at 8000ms, so give this
1660
+ // server enough aggregate budget to return its seeded findings (#3311).
1661
+ svelte: {
1662
+ seedFirstPush: false,
1663
+ pullRetryBudgetMs: 0,
1664
+ debounceMs: 150,
1665
+ aggregateWaitMs: 4e3,
1666
+ expectSemanticSecondPush: false
1667
+ },
1668
+ // csharp-ls loads (design-time-builds) the restored project AFTER
1669
+ // `initialize` returns, so the publish for a seeded CS0029 lands inside
1670
+ // `waitForDiagnostics`, not inside the client wait. MEASURED A/B over two
1671
+ // nightly Tool-smoke runs of the same fixture with `dotnet restore` already
1672
+ // done — the ONLY difference between them was this budget:
1673
+ // * `aggregateWaitMs` 1500 (the default): run 36054901266 →
1674
+ // `[csharp] touched=0`, 4.05s after `Restored …/toolsmoke.csproj`.
1675
+ // A real compiler error read as clean.
1676
+ // * 8000: run 36058292424 → `[csharp] touched=1`, 6.49s after the same
1677
+ // line; gate run 36059988117 → `lsp_diagnostics returned 1 primary
1678
+ // finding`, 4.86s after it.
1679
+ // Both windows INCLUDE workspace bootstrap + spawn + initialize, so they
1680
+ // bound the wait from above: the required budget is in (1500, 4860] ms.
1681
+ // 6000 covers the measured gate window with margin and stays under the
1682
+ // tool-smoke gate's own 8000ms ceiling (that relation is pinned by
1683
+ // tests/config/lsp-gate-population.test.ts), so the gate can still witness
1684
+ // this budget. CONFIRMED at 6000 by run 36064829436: `✓ csharp csharp-ls 1
1685
+ // lsp_diagnostics returned 1 primary finding`, 4.78s after `Restored
1686
+ // …csproj`, census unchanged at gated 31 / handshake-only 8 / unavailable 6
1687
+ // — so 6000 is measured as sufficient, not merely inferred from the 8000
1688
+ // runs above. It is deliberately NOT the ceiling: an `lsp_diagnostics` call
1689
+ // that passes no `waitMs` pays this budget in full on a file the server never
1690
+ // publishes for (`tools/lsp-diagnostics.ts` leaves `maxClientWaitMs`
1691
+ // undefined → `perServerTimeout` has no caller cap), so every 1000ms here
1692
+ // is 1000ms of turn latency on the no-publication path (#3402 review r2).
1693
+ // Every other field is DEFAULT_STRATEGY's value on purpose: csharp-ls is
1694
+ // `mode=pull`/tier-1 authoritative-clean (#3311 investigator table), and
1695
+ // nothing has measured its pull retry, so this entry moves the one field
1696
+ // that was measured and no other (pinned in
1697
+ // tests/clients/lsp/server-strategies.test.ts).
1698
+ // fsharp/expert/vue deliberately have NO entry: the same two runs show
1699
+ // `touched=0` at BOTH 1500 and 8000, so the budget is not what stops them
1700
+ // publishing and an 8000 entry would buy nothing while costing every
1701
+ // uncapped production call 6.5 extra seconds. Their fixture rows carry the
1702
+ // observed-behavior exemption instead (`scripts/smoke-tools.mjs`), and a
1703
+ // probe that wants a long window sets `PI_LENS_LSP_DIAGNOSTICS_MAX_WAIT_MS`
1704
+ // — a flat harness override that needs no production budget.
1705
+ csharp: {
1706
+ seedFirstPush: false,
1707
+ pullRetryBudgetMs: 250,
1708
+ debounceMs: 150,
1709
+ aggregateWaitMs: 6e3,
1710
+ expectSemanticSecondPush: false
1711
+ },
1712
+ cue: {
1713
+ seedFirstPush: true,
1714
+ pullRetryBudgetMs: 0,
1715
+ debounceMs: 150,
1716
+ aggregateWaitMs: 2e3,
1717
+ expectSemanticSecondPush: false,
1718
+ reopenOnResync: false
1719
+ },
1720
+ // lua-language-server is push-only and was silent on clean transitions in
1721
+ // the repaired probe (run 35914033696). Keep the marker on the measured
1722
+ // server id so the cascade can skip its in-lane wait safely.
1723
+ lua: {
1724
+ seedFirstPush: true,
1725
+ pullRetryBudgetMs: 0,
1726
+ debounceMs: 150,
1727
+ aggregateWaitMs: 2e3,
1728
+ expectSemanticSecondPush: false,
1729
+ silentOnClean: true
1730
+ }
1731
+ };
1732
+ var NATIVE_TS7_DIAGNOSTIC_STRATEGY = {
1733
+ ...SERVER_DIAGNOSTIC_STRATEGIES.typescript,
1734
+ seedFirstPush: false,
1735
+ // Native TS7 demonstrably publishes on clean, so the classic-only marker
1736
+ // must not leak through the shared server id.
1737
+ silentOnClean: false
1738
+ };
1739
+ var DEFAULT_STRATEGY = {
1740
+ seedFirstPush: false,
1741
+ pullRetryBudgetMs: 250,
1742
+ debounceMs: 150,
1743
+ aggregateWaitMs: 1500,
1744
+ expectSemanticSecondPush: false
1745
+ };
1746
+ function getStrategy(serverId, launchVariant) {
1747
+ if (serverId === "typescript" && launchVariant === "native-ts7") {
1748
+ return NATIVE_TS7_DIAGNOSTIC_STRATEGY;
1749
+ }
1750
+ return SERVER_DIAGNOSTIC_STRATEGIES[serverId] ?? DEFAULT_STRATEGY;
1751
+ }
1752
+
1753
+ // dist/clients/lsp/wait-policy/classification.js
1754
+ function classifyServerWaitTier(serverId, snapshot) {
1755
+ if (!snapshot)
1756
+ return "waits";
1757
+ const mode = snapshot.workspaceDiagnosticsSupport?.mode;
1758
+ if (mode === "pull")
1759
+ return "pull-capable";
1760
+ if (snapshot.customServer === true && mode === "push-only" && snapshot.diagnosticsUnsupported === true && snapshot.diagnosticsPublished !== true) {
1761
+ return "diagnostics-unsupported";
1762
+ }
1763
+ if (mode !== "push-only")
1764
+ return "waits";
1765
+ const strategy = getStrategy(serverId, snapshot.launchVariant);
1766
+ if (strategy.silentOnClean !== true)
1767
+ return "waits";
1768
+ if (snapshot.launchVariant === "native-ts7")
1769
+ return "waits";
1770
+ return "tier3-silent";
1771
+ }
1772
+ function resolvePrimaryServerForWaitPolicy(filePath, snapshots) {
1773
+ const servers = getServersForFileWithConfig(filePath).filter((s) => !isAuxiliary(s));
1774
+ const primary = servers[0];
1775
+ if (!primary)
1776
+ return void 0;
1777
+ return {
1778
+ serverId: primary.id,
1779
+ snapshot: snapshots.find((s) => s.serverId === primary.id)
1780
+ };
1781
+ }
1782
+ function classifyCascadeWaitTier(lspService, filePath, snapshots) {
1783
+ void lspService;
1784
+ const primary = resolvePrimaryServerForWaitPolicy(filePath, snapshots);
1785
+ if (!primary)
1786
+ return "waits";
1787
+ return classifyServerWaitTier(primary.serverId, primary.snapshot);
1788
+ }
1789
+
1790
+ // dist/clients/lsp/lombok.js
1791
+ import * as fs4 from "node:fs";
1792
+ import * as os4 from "node:os";
1793
+ import * as path5 from "node:path";
1794
+ var LOMBOK_ENV_KEYS = ["PI_LENS_LOMBOK_JAR", "LOMBOK_JAR"];
1795
+ var LOMBOK_PROJECT_FILES = [
1796
+ "lombok.config",
1797
+ "pom.xml",
1798
+ "build.gradle",
1799
+ "build.gradle.kts",
1800
+ path5.join("gradle", "libs.versions.toml")
1801
+ ];
1802
+ function fileExists(filePath) {
1803
+ if (!filePath)
1804
+ return false;
1805
+ try {
1806
+ return fs4.statSync(filePath).isFile();
1807
+ } catch {
1808
+ return false;
1809
+ }
1810
+ }
1811
+ function readTextIfSmall(filePath, maxBytes = 512 * 1024) {
1812
+ try {
1813
+ const stat2 = fs4.statSync(filePath);
1814
+ if (!stat2.isFile() || stat2.size > maxBytes)
1815
+ return "";
1816
+ return fs4.readFileSync(filePath, "utf-8");
1817
+ } catch {
1818
+ return "";
1819
+ }
1820
+ }
1821
+ function containsLombokDependency(text) {
1822
+ 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);
1823
+ }
1824
+ function hasLombokProject(root) {
1825
+ const lombokConfig = path5.join(root, "lombok.config");
1826
+ if (fileExists(lombokConfig))
1827
+ return true;
1828
+ for (const name of LOMBOK_PROJECT_FILES) {
1829
+ if (name === "lombok.config")
1830
+ continue;
1831
+ const text = readTextIfSmall(path5.join(root, name));
1832
+ if (text && containsLombokDependency(text))
1833
+ return true;
1834
+ }
1835
+ return false;
1836
+ }
1837
+ function envJarCandidate(env) {
1838
+ for (const key of LOMBOK_ENV_KEYS) {
1839
+ const value = env[key]?.trim();
1840
+ if (fileExists(value))
1841
+ return path5.resolve(value);
1842
+ }
1843
+ return void 0;
1844
+ }
1845
+ function localJarCandidates(root) {
1846
+ return [
1847
+ path5.join(root, "lombok.jar"),
1848
+ path5.join(root, ".lombok", "lombok.jar"),
1849
+ path5.join(root, "lib", "lombok.jar"),
1850
+ path5.join(root, "libs", "lombok.jar")
1851
+ ];
1852
+ }
1853
+ function newestExistingJar(candidates) {
1854
+ let best;
1855
+ for (const candidate of candidates) {
1856
+ try {
1857
+ const stat2 = fs4.statSync(candidate);
1858
+ if (!stat2.isFile())
1859
+ continue;
1860
+ if (!best || stat2.mtimeMs > best.mtimeMs) {
1861
+ best = { filePath: candidate, mtimeMs: stat2.mtimeMs };
1862
+ }
1863
+ } catch {
1864
+ }
1865
+ }
1866
+ return best?.filePath;
1867
+ }
1868
+ function mavenLocalLombokJar(home = os4.homedir()) {
1869
+ const base = path5.join(home, ".m2", "repository", "org", "projectlombok", "lombok");
1870
+ let versions = [];
1871
+ try {
1872
+ versions = fs4.readdirSync(base);
1873
+ } catch {
1874
+ return void 0;
1875
+ }
1876
+ return newestExistingJar(versions.map((version) => path5.join(base, version, `lombok-${version}.jar`)));
1877
+ }
1878
+ function gradleCacheLombokJar(home = os4.homedir()) {
1879
+ const base = path5.join(home, ".gradle", "caches", "modules-2", "files-2.1", "org.projectlombok", "lombok");
1880
+ const candidates = [];
1881
+ let versions = [];
1882
+ try {
1883
+ versions = fs4.readdirSync(base);
1884
+ } catch {
1885
+ return void 0;
1886
+ }
1887
+ for (const version of versions) {
1888
+ const versionDir = path5.join(base, version);
1889
+ let hashes = [];
1890
+ try {
1891
+ hashes = fs4.readdirSync(versionDir);
1892
+ } catch {
1893
+ continue;
1894
+ }
1895
+ for (const hash of hashes) {
1896
+ candidates.push(path5.join(versionDir, hash, `lombok-${version}.jar`));
1897
+ }
1898
+ }
1899
+ return newestExistingJar(candidates);
1900
+ }
1901
+ function resolveLombokJar(root, env = process.env) {
1902
+ const explicit = envJarCandidate(env);
1903
+ if (explicit)
1904
+ return explicit;
1905
+ return newestExistingJar(localJarCandidates(root)) ?? mavenLocalLombokJar() ?? gradleCacheLombokJar();
1906
+ }
1907
+ function hasLombokJavaAgent(jvmArgs) {
1908
+ return /(^|\s)-javaagent:(?:"[^"]*lombok[^"]*\.jar"|\S*lombok\S*\.jar)/i.test(jvmArgs ?? "");
1909
+ }
1910
+ function resolveLombokJavaAgentArg(root, baseEnv = process.env) {
1911
+ if (baseEnv.PI_LENS_JAVA_LOMBOK === "0")
1912
+ return void 0;
1913
+ if (hasLombokJavaAgent(baseEnv.JDTLS_JVM_ARGS))
1914
+ return void 0;
1915
+ const explicitJar = envJarCandidate(baseEnv);
1916
+ if (!explicitJar && !hasLombokProject(root))
1917
+ return void 0;
1918
+ const jar = explicitJar ?? resolveLombokJar(root, baseEnv);
1919
+ return jar ? `-javaagent:${jar}` : void 0;
1920
+ }
1921
+ function createLombokJdtlsArgs(root, baseEnv = process.env) {
1922
+ const javaAgent = resolveLombokJavaAgentArg(root, baseEnv);
1923
+ return javaAgent ? [`--jvm-arg=${javaAgent}`] : [];
1924
+ }
1925
+
1926
+ // dist/clients/lsp/jvm-runtime.js
1927
+ import { existsSync as existsSync2, readdirSync as readdirSync4 } from "node:fs";
1928
+ import os5 from "node:os";
1929
+ import path6 from "node:path";
1930
+ var JAVA_EXE = process.platform === "win32" ? "java.exe" : "java";
1931
+ var JAVA_PROBE_TIMEOUT_MS = 5e3;
1932
+ var MIN_JAVA_MAJOR = 17;
1933
+ function jdkHomeFrom(dir) {
1934
+ if (existsSync2(path6.join(dir, "bin", JAVA_EXE)))
1935
+ return dir;
1936
+ const macHome = path6.join(dir, "Contents", "Home");
1937
+ if (existsSync2(path6.join(macHome, "bin", JAVA_EXE)))
1938
+ return macHome;
1939
+ return void 0;
1940
+ }
1941
+ function parseMajorVersion(name) {
1942
+ const legacy = name.match(/(?<!\d)1\.(\d+)/);
1943
+ if (legacy)
1944
+ return Number.parseInt(legacy[1], 10);
1945
+ const modern = name.match(/(?<!\d)(\d{1,2})/);
1946
+ return modern ? Number.parseInt(modern[1], 10) : 0;
1947
+ }
1948
+ function childDirs(base) {
1949
+ try {
1950
+ return readdirSync4(base, { withFileTypes: true }).filter((e) => e.isDirectory() || e.isSymbolicLink()).map((e) => path6.join(base, e.name));
1951
+ } catch {
1952
+ return [];
1953
+ }
1954
+ }
1955
+ function candidateRoots() {
1956
+ const home = os5.homedir();
1957
+ const roots = [];
1958
+ roots.push(path6.join(home, ".jdks"));
1959
+ roots.push(path6.join(getGlobalPiLensDir(), "tools"));
1960
+ if (process.platform === "win32") {
1961
+ const progFiles = [
1962
+ process.env.ProgramFiles,
1963
+ process.env["ProgramFiles(x86)"],
1964
+ process.env.ProgramW6432,
1965
+ path6.join(process.env.LOCALAPPDATA ?? path6.join(home, "AppData", "Local"), "Programs")
1966
+ ].filter((p) => Boolean(p));
1967
+ for (const base of progFiles) {
1968
+ roots.push(path6.join(base, "Eclipse Adoptium"));
1969
+ roots.push(path6.join(base, "Java"));
1970
+ roots.push(path6.join(base, "Microsoft"));
1971
+ roots.push(path6.join(base, "Zulu"));
1972
+ roots.push(path6.join(base, "Amazon Corretto"));
1973
+ roots.push(path6.join(base, "BellSoft"));
1974
+ }
1975
+ } else if (process.platform === "darwin") {
1976
+ roots.push("/Library/Java/JavaVirtualMachines");
1977
+ roots.push(path6.join(home, "Library", "Java", "JavaVirtualMachines"));
1978
+ roots.push("/opt/homebrew/opt");
1979
+ roots.push("/usr/local/opt");
1980
+ } else {
1981
+ roots.push("/usr/lib/jvm");
1982
+ roots.push("/usr/java");
1983
+ roots.push("/opt/java");
1984
+ }
1985
+ return roots;
1986
+ }
1987
+ function discoverJdkHome(roots = candidateRoots(), javaHome = process.env.JAVA_HOME) {
1988
+ const found = [];
1989
+ if (javaHome) {
1990
+ const resolved = jdkHomeFrom(javaHome);
1991
+ if (resolved)
1992
+ found.push({ home: resolved, major: Number.MAX_SAFE_INTEGER });
1993
+ }
1994
+ for (const root of roots) {
1995
+ for (const dir of childDirs(root)) {
1996
+ const resolved = jdkHomeFrom(dir);
1997
+ if (!resolved)
1998
+ continue;
1999
+ found.push({
2000
+ home: resolved,
2001
+ major: parseMajorVersion(path6.basename(dir))
2002
+ });
2003
+ }
2004
+ }
2005
+ const usable = found.filter((c) => c.major >= MIN_JAVA_MAJOR);
2006
+ if (usable.length === 0)
2007
+ return void 0;
2008
+ usable.sort((a, b) => b.major - a.major);
2009
+ return usable[0].home;
2010
+ }
2011
+ var _cachedEnv;
2012
+ var javaAvailabilityLatch = createAvailabilityLatch();
2013
+ var inFlightJavaProbe = null;
2014
+ function probeJavaOnPath() {
2015
+ const memo = javaAvailabilityLatch.read();
2016
+ if (memo !== null) {
2017
+ if (javaAvailabilityLatch.getOutcome() === "transient") {
2018
+ logAvailabilityDecision({
2019
+ tool: "java",
2020
+ verdict: "unavailable",
2021
+ outcome: "transient",
2022
+ cause: javaAvailabilityLatch.getCause() ?? "probe-timeout",
2023
+ elapsedMs: 0,
2024
+ latched: false,
2025
+ retryAfterMs: Math.max(0, javaAvailabilityLatch.getRetryAtMs() - Date.now()),
2026
+ budgetMs: JAVA_PROBE_TIMEOUT_MS,
2027
+ // No probe ran here: the latch's own remembered cause is replayed
2028
+ // as-is, so the call site is the one asserting it (#2209).
2029
+ classifiedBy: "caller"
2030
+ });
2031
+ return Promise.resolve(void 0);
2032
+ }
2033
+ return Promise.resolve(memo);
2034
+ }
2035
+ if (inFlightJavaProbe)
2036
+ return inFlightJavaProbe;
2037
+ inFlightJavaProbe = runJavaProbe().finally(() => {
2038
+ inFlightJavaProbe = null;
2039
+ });
2040
+ return inFlightJavaProbe;
2041
+ }
2042
+ async function runJavaProbe() {
2043
+ const finder = process.platform === "win32" ? "where" : "which";
2044
+ const startedAt = Date.now();
2045
+ const sampler = startHostStallSampler();
2046
+ let result;
2047
+ let hostStallMs;
2048
+ try {
2049
+ result = await safeSpawnAsync(finder, ["java"], {
2050
+ timeout: JAVA_PROBE_TIMEOUT_MS
2051
+ });
2052
+ } finally {
2053
+ hostStallMs = sampler.stop();
2054
+ }
2055
+ const elapsedMs = Date.now() - startedAt;
2056
+ if (result.status === 0 && !result.error) {
2057
+ javaAvailabilityLatch.noteAvailable();
2058
+ logAvailabilityDecision({
2059
+ tool: "java",
2060
+ verdict: "available",
2061
+ outcome: "success",
2062
+ cause: "ok",
2063
+ elapsedMs,
2064
+ latched: true,
2065
+ hostStallMs,
2066
+ budgetMs: JAVA_PROBE_TIMEOUT_MS,
2067
+ classifiedBy: "probe"
2068
+ });
2069
+ return true;
2070
+ }
2071
+ const { outcome, cause } = classifyProbeFailure(result, {
2072
+ hostStallMs,
2073
+ unclassifiedFailureOutcome: "missing"
2074
+ });
2075
+ const retryAfterMs = javaAvailabilityLatch.noteUnavailable(outcome, cause);
2076
+ logAvailabilityDecision({
2077
+ tool: "java",
2078
+ verdict: "unavailable",
2079
+ outcome,
2080
+ cause,
2081
+ elapsedMs,
2082
+ latched: outcome !== "transient",
2083
+ hostStallMs,
2084
+ ...retryAfterMs > 0 && { retryAfterMs },
2085
+ budgetMs: JAVA_PROBE_TIMEOUT_MS,
2086
+ classifiedBy: "probe"
2087
+ });
2088
+ return outcome === "transient" ? void 0 : false;
2089
+ }
2090
+ async function resolveJavaRuntimeEnv() {
2091
+ if (_cachedEnv)
2092
+ return _cachedEnv.value;
2093
+ const javaOnPath = await probeJavaOnPath();
2094
+ if (javaOnPath === true) {
2095
+ _cachedEnv = { value: void 0 };
2096
+ return void 0;
2097
+ }
2098
+ const home = discoverJdkHome();
2099
+ if (home) {
2100
+ const binDir = path6.join(home, "bin");
2101
+ const currentPath = process.env.PATH ?? process.env.Path ?? "";
2102
+ _cachedEnv = {
2103
+ value: {
2104
+ JAVA_HOME: home,
2105
+ PATH: binDir + path6.delimiter + currentPath
2106
+ }
2107
+ };
2108
+ return _cachedEnv.value;
2109
+ }
2110
+ if (javaOnPath === false) {
2111
+ _cachedEnv = { value: void 0 };
2112
+ }
2113
+ return void 0;
2114
+ }
2115
+
2116
+ // dist/clients/lsp/server-covers.js
2117
+ var BUILTIN_SERVER_RUNNER_COVERS = /* @__PURE__ */ new Map([
2118
+ [
2119
+ "bash",
2120
+ { runnerIds: ["shellcheck"], gateCommands: ["bash-language-server"] }
2121
+ ],
2122
+ ["shuck", { runnerIds: ["shellcheck"], gateCommands: ["shuck"] }],
2123
+ ["toml", { runnerIds: ["taplo"], gateCommands: ["taplo"] }]
2124
+ ]);
2125
+
2126
+ // dist/clients/lsp/server.js
2127
+ function withRootMarkers(root, markers) {
2128
+ root.rootMarkers = markers;
2129
+ return root;
2130
+ }
2131
+ function describeRootFallback(fallback) {
2132
+ const tail = "so pi-lens cannot confirm the server analysed the file";
2133
+ if (fallback.cause === "failed") {
2134
+ return `${fallback.serverName}: resolving the project root for this file failed, ${tail}`;
2135
+ }
2136
+ if (fallback.cause === "unselected") {
2137
+ return `${fallback.serverName}: found ${fallback.marker} for this file but pi-lens did not select it as the project root (a test-fixture or git-ignored directory is never used as one), ${tail}`;
2138
+ }
2139
+ return `${fallback.serverName}: no project root found for this file (looked for ${fallback.rootMarkers.join(" / ")}); the server was started at the file's directory and may not have analysed it`;
2140
+ }
2141
+ async function resolveLspServerCwd(server, filePath, sessionCwd, onRootFailure, onRootFallback) {
2142
+ const stagingRoot = ephemeralStagingRoot(filePath);
2143
+ if (stagingRoot) {
2144
+ recordDegradationOnce({
2145
+ kind: "lsp-root-declined",
2146
+ subject: stagingRoot,
2147
+ reason: "LSP root declined for host pi-agent staging directory"
2148
+ });
2149
+ return void 0;
2150
+ }
2151
+ const rootMarkers = server.rootMarkers ?? server.root.rootMarkers;
2152
+ let serverRoot;
2153
+ let rootFailed = false;
2154
+ try {
2155
+ serverRoot = await server.root(filePath);
2156
+ } catch (error) {
2157
+ const reason = error instanceof Error ? error.message : String(error);
2158
+ rootFailed = true;
2159
+ onRootFailure?.(reason);
2160
+ }
2161
+ const isFileDirFallback = serverRoot !== void 0 && rootMarkers?.length && path7.resolve(serverRoot) === path7.resolve(path7.dirname(filePath));
2162
+ if (!serverRoot || isFileDirFallback) {
2163
+ const probedMarker = onRootFallback && !rootFailed && rootMarkers?.length ? resolveToolCwd("lsp", server.id, filePath, {
2164
+ cwd: sessionCwd,
2165
+ rootMarkers,
2166
+ suppressTelemetry: true
2167
+ }).marker : void 0;
2168
+ const refusedMarker = probedMarker !== void 0 && rootMarkers?.includes(probedMarker) ? probedMarker : void 0;
2169
+ const fallbackCwd = rootMarkers?.length ? resolveToolCwd("lsp", server.id, filePath, {
2170
+ cwd: path7.dirname(path7.resolve(filePath)),
2171
+ rootMarkers
2172
+ }) : void 0;
2173
+ if (!serverRoot) {
2174
+ recordDegradationOnce({
2175
+ kind: "tool-cwd-resolution",
2176
+ subject: server.id,
2177
+ reason: `lsp:server-root-${rootFailed ? "failed" : "fallback"}:${filePath}`
2178
+ });
2179
+ onRootFallback?.({
2180
+ serverId: server.id,
2181
+ serverName: server.name ?? server.id,
2182
+ rootMarkers: rootMarkers ?? [],
2183
+ requiresProjectRoot: server.requiresProjectRoot === true,
2184
+ cause: rootFailed ? "failed" : refusedMarker !== void 0 ? "unselected" : "none",
2185
+ ...refusedMarker !== void 0 && { marker: refusedMarker }
2186
+ });
2187
+ }
2188
+ return fallbackCwd?.cwd;
2189
+ }
2190
+ const boundedServerRoot = enforceLspRootCeiling(serverRoot, sessionCwd, filePath);
2191
+ return resolveToolCwd("lsp", server.id, filePath, {
2192
+ cwd: sessionCwd,
2193
+ rootMarkers,
2194
+ serverRoot: boundedServerRoot
2195
+ }).cwd;
2196
+ }
2197
+ var FIXTURE_ROOT_SEGMENTS = /* @__PURE__ */ new Set(["__fixtures__", "testdata"]);
2198
+ var FALLBACK_PROJECT_MARKERS = [
2199
+ ".git",
2200
+ "package.json",
2201
+ "go.work",
2202
+ "go.mod",
2203
+ "Cargo.toml",
2204
+ "pyproject.toml",
2205
+ "pom.xml",
2206
+ "build.gradle",
2207
+ "build.gradle.kts",
2208
+ "mix.exs",
2209
+ "pubspec.yaml",
2210
+ "Package.swift"
2211
+ ];
2212
+ var PROJECT_BOUNDARY_MARKERS = [
2213
+ "package.json",
2214
+ "package-lock.json",
2215
+ "pnpm-lock.yaml",
2216
+ "yarn.lock",
2217
+ "bun.lock",
2218
+ "bun.lockb",
2219
+ "go.work",
2220
+ "go.mod",
2221
+ "Cargo.toml",
2222
+ "Cargo.lock",
2223
+ "pyproject.toml",
2224
+ "uv.lock",
2225
+ "poetry.lock",
2226
+ "Pipfile",
2227
+ "Pipfile.lock",
2228
+ "pom.xml",
2229
+ "build.gradle",
2230
+ "build.gradle.kts",
2231
+ "settings.gradle",
2232
+ "settings.gradle.kts",
2233
+ "mix.exs",
2234
+ "pubspec.yaml",
2235
+ "Package.swift"
2236
+ ];
2237
+ var loggedRootCeilingClamps = /* @__PURE__ */ new Set();
2238
+ var posixCaseInsensitiveByPath = /* @__PURE__ */ new Map();
2239
+ function posixFilesystemIsCaseInsensitive(root) {
2240
+ const resolved = path7.resolve(root);
2241
+ const cached = posixCaseInsensitiveByPath.get(resolved);
2242
+ if (cached !== void 0)
2243
+ return cached;
2244
+ if (!existsSync3(resolved)) {
2245
+ return false;
2246
+ }
2247
+ const name = path7.basename(resolved);
2248
+ const alternate = name.toLowerCase() === name ? name.toUpperCase() : name.toLowerCase();
2249
+ let insensitive = false;
2250
+ const alternatePath = path7.join(path7.dirname(resolved), alternate);
2251
+ if (alternate !== name && existsSync3(alternatePath)) {
2252
+ try {
2253
+ const actual = statSync3(resolved);
2254
+ const alternateStat = statSync3(alternatePath);
2255
+ insensitive = actual.dev === alternateStat.dev && actual.ino === alternateStat.ino;
2256
+ } catch {
2257
+ insensitive = false;
2258
+ }
2259
+ }
2260
+ posixCaseInsensitiveByPath.set(resolved, insensitive);
2261
+ return insensitive;
2262
+ }
2263
+ function resetLSPCaseSensitivityState() {
2264
+ posixCaseInsensitiveByPath.clear();
2265
+ }
2266
+ function isSameOrWithin(ancestor, candidate, deps = {}) {
2267
+ const windowsShaped = isWindowsPath(ancestor) || isWindowsPath(candidate);
2268
+ const pathApi = windowsShaped ? path7.win32 : path7;
2269
+ const resolvedAncestor = pathApi.resolve(ancestor);
2270
+ const resolvedCandidate = pathApi.resolve(candidate);
2271
+ let caseInsensitive = false;
2272
+ if (!windowsShaped) {
2273
+ if (deps.caseInsensitiveProbe) {
2274
+ const cached = posixCaseInsensitiveByPath.get(resolvedAncestor);
2275
+ caseInsensitive = cached ?? deps.caseInsensitiveProbe(resolvedAncestor);
2276
+ if (cached === void 0) {
2277
+ posixCaseInsensitiveByPath.set(resolvedAncestor, caseInsensitive);
2278
+ }
2279
+ } else {
2280
+ caseInsensitive = posixFilesystemIsCaseInsensitive(resolvedAncestor);
2281
+ }
2282
+ }
2283
+ const relative2 = pathApi.relative(caseInsensitive ? resolvedAncestor.toLowerCase() : resolvedAncestor, caseInsensitive ? resolvedCandidate.toLowerCase() : resolvedCandidate);
2284
+ return relative2 === "" || !relative2.startsWith("..") && !pathApi.isAbsolute(relative2);
2285
+ }
2286
+ function enforceLspRootCeiling(root, sessionCwd, filePath) {
2287
+ const windowsShaped = isWindowsPath(root) || isWindowsPath(sessionCwd);
2288
+ const pathApi = windowsShaped ? path7.win32 : path7;
2289
+ const resolvedRoot = pathApi.resolve(root);
2290
+ const resolvedCwd = pathApi.resolve(sessionCwd);
2291
+ if (filePath && !isSameOrWithin(resolvedCwd, pathApi.resolve(filePath))) {
2292
+ return resolvedRoot;
2293
+ }
2294
+ if (isSameOrWithin(resolvedCwd, resolvedRoot))
2295
+ return resolvedRoot;
2296
+ const logKey = `${normalizeMapKey(resolvedCwd)}:${normalizeMapKey(resolvedRoot)}`;
2297
+ if (!loggedRootCeilingClamps.has(logKey)) {
2298
+ loggedRootCeilingClamps.add(logKey);
2299
+ logSessionStart(`lsp root clamped to session cwd: candidate=${resolvedRoot} cwd=${resolvedCwd}`);
2300
+ }
2301
+ return resolvedCwd;
2302
+ }
2303
+ async function hasProjectBoundaryMarker(dir) {
2304
+ if (await markerExists(dir, ".git"))
2305
+ return true;
2306
+ for (const marker of PROJECT_BOUNDARY_MARKERS) {
2307
+ if (await markerExists(dir, marker))
2308
+ return true;
2309
+ }
2310
+ return false;
2311
+ }
2312
+ function pathSegments(dir) {
2313
+ const parsed = path7.parse(path7.resolve(dir));
2314
+ return path7.relative(parsed.root, path7.resolve(dir)).split(path7.sep).filter(Boolean).map((segment) => segment.toLowerCase());
2315
+ }
2316
+ function hasFixtureConvention(dir) {
2317
+ const segments = pathSegments(dir);
2318
+ if (segments.some((segment) => FIXTURE_ROOT_SEGMENTS.has(segment)))
2319
+ return true;
2320
+ return segments.some((segment, index) => segment === "tests" && segments[index + 1] === "fixtures");
2321
+ }
2322
+ function hasAtomicStageSegment(dir) {
2323
+ return pathSegments(dir).some((segment) => STAGE_TMP_PATTERN.test(segment));
2324
+ }
2325
+ async function findGitBoundary(dir) {
2326
+ let current = path7.resolve(dir);
2327
+ const fsRoot = path7.parse(current).root;
2328
+ while (true) {
2329
+ if (await markerExists(current, ".git"))
2330
+ return current;
2331
+ if (current === fsRoot)
2332
+ return void 0;
2333
+ current = path7.dirname(current);
2334
+ }
2335
+ }
2336
+ async function isExcludedLspRoot(dir) {
2337
+ const candidate = path7.resolve(dir);
2338
+ if (hasFixtureConvention(candidate) || hasAtomicStageSegment(candidate))
2339
+ return true;
2340
+ const gitRoot = await findGitBoundary(candidate);
2341
+ if (!gitRoot || gitRoot === candidate)
2342
+ return false;
2343
+ if (getProjectIgnoreGlobs(gitRoot).length === 0)
2344
+ return false;
2345
+ return isPathIgnoredByProject(candidate, gitRoot, true);
2346
+ }
2347
+ async function nearestNonExcludedFallbackRoot(candidate) {
2348
+ if (!await isExcludedLspRoot(candidate))
2349
+ return path7.resolve(candidate);
2350
+ let current = path7.dirname(path7.resolve(candidate));
2351
+ const fsRoot = path7.parse(current).root;
2352
+ let nearestAllowed;
2353
+ while (true) {
2354
+ if (!await isExcludedLspRoot(current)) {
2355
+ nearestAllowed ??= current;
2356
+ for (const marker of FALLBACK_PROJECT_MARKERS) {
2357
+ if (await markerExists(current, marker))
2358
+ return current;
2359
+ }
2360
+ }
2361
+ if (current === fsRoot)
2362
+ break;
2363
+ current = path7.dirname(current);
2364
+ }
2365
+ return nearestAllowed ?? fsRoot;
2366
+ }
2367
+ function isLspInstallDisabled() {
2368
+ return process.env.PI_LENS_DISABLE_LSP_INSTALL === "1";
2369
+ }
2370
+ function canInstall(allowInstall) {
2371
+ return allowInstall !== false && !isLspInstallDisabled();
2372
+ }
2373
+ var DIRECT_LSP_NEGATIVE_TTL_MS = Math.max(3e4, Number.parseInt(process.env.PI_LENS_DIRECT_LSP_NEGATIVE_TTL_MS ?? "600000", 10) || 6e5);
2374
+ var directLspCommandUnavailableUntil = /* @__PURE__ */ new Map();
2375
+ var directLspCommandSkipLoggedUntil = /* @__PURE__ */ new Map();
2376
+ var LSP_SERVICE_GENERATION_FAMILY = "lsp.service.generation";
2377
+ var LSP_SERVICE_GENERATION_VERSION = 1;
2378
+ function lspServiceGenerationCell() {
2379
+ let seed = 0;
2380
+ return getProcessSingleton(LSP_SERVICE_GENERATION_FAMILY, LSP_SERVICE_GENERATION_VERSION, () => ({ generation: seed }), (previous) => {
2381
+ const value = previous?.generation;
2382
+ seed = typeof value === "number" && Number.isSafeInteger(value) && value > 0 ? value : 0;
2383
+ });
2384
+ }
2385
+ var lspLaunchAvailabilityGeneration = createGenerationSource("lsp-launch-availability", lspServiceGenerationCell);
2386
+ function resetLspLaunchAvailabilityGeneration() {
2387
+ lspLaunchAvailabilityGeneration.bump();
2388
+ }
2389
+ function staleLaunch(generation, subject, proc) {
2390
+ if (generation.isCurrent())
2391
+ return false;
2392
+ try {
2393
+ proc?.process?.kill();
2394
+ } catch {
2395
+ }
2396
+ generation.guardedWrite(subject, () => void 0);
2397
+ return true;
2398
+ }
2399
+ async function installEvidenceForLaunch(toolId, installed, attempt) {
2400
+ const evidence = describeInstallAttempt(attempt);
2401
+ const confirmedManagedPath = await findManagedToolBinary(toolId);
2402
+ return {
2403
+ ...evidence,
2404
+ binary: path7.basename(installed),
2405
+ ...confirmedManagedPath !== void 0 && pathsEqual(confirmedManagedPath, installed) && {
2406
+ source: "managed-dir"
2407
+ }
2408
+ };
2409
+ }
2410
+ function captureInstallAttempt(toolId) {
2411
+ try {
2412
+ return getInstallAttempt?.(toolId);
2413
+ } catch {
2414
+ }
2415
+ return void 0;
2416
+ }
2417
+ function resetDirectLspCommandAvailability() {
2418
+ directLspCommandUnavailableUntil.clear();
2419
+ directLspCommandSkipLoggedUntil.clear();
2420
+ }
2421
+ function pruneExpiredDirectLspNegativeEntries(now = Date.now()) {
2422
+ for (const [command, until] of directLspCommandUnavailableUntil) {
2423
+ if (until <= now) {
2424
+ directLspCommandUnavailableUntil.delete(command);
2425
+ directLspCommandSkipLoggedUntil.delete(command);
2426
+ }
2427
+ }
2428
+ for (const [command, until] of directLspCommandSkipLoggedUntil) {
2429
+ if (until <= now)
2430
+ directLspCommandSkipLoggedUntil.delete(command);
2431
+ }
2432
+ }
2433
+ function isSimpleCommand(command) {
2434
+ return !isFullyQualified(command) && !command.includes("/") && !command.includes("\\");
2435
+ }
2436
+ function isDirectLspCommandTemporarilyUnavailable(command) {
2437
+ const now = Date.now();
2438
+ pruneExpiredDirectLspNegativeEntries(now);
2439
+ const until = directLspCommandUnavailableUntil.get(command);
2440
+ if (!until || until <= now) {
2441
+ directLspCommandUnavailableUntil.delete(command);
2442
+ return false;
2443
+ }
2444
+ const loggedUntil = directLspCommandSkipLoggedUntil.get(command) ?? 0;
2445
+ if (loggedUntil <= now) {
2446
+ logSessionStart(`lsp direct command ${command}: skipped by negative availability cache (${Math.max(0, until - now)}ms remaining)`);
2447
+ directLspCommandSkipLoggedUntil.set(command, until);
2448
+ }
2449
+ return true;
2450
+ }
2451
+ function markDirectLspCommandUnavailable(command) {
2452
+ if (!isSimpleCommand(command))
2453
+ return;
2454
+ directLspCommandUnavailableUntil.set(command, Date.now() + DIRECT_LSP_NEGATIVE_TTL_MS);
2455
+ directLspCommandSkipLoggedUntil.delete(command);
2456
+ }
2457
+ var PI_LENS_BIN_DIR2 = path7.join(getGlobalPiLensDir(), "bin");
2458
+ async function resolveAndLaunch(spec, allowInstall) {
2459
+ const generation = lspLaunchAvailabilityGeneration.capture();
2460
+ const toolLabel = spec.managedToolId ?? spec.candidates[spec.candidates.length - 1] ?? "unknown";
2461
+ let lastRuntimeFailure;
2462
+ const trackRuntimeFailure = (err) => {
2463
+ const message = err instanceof Error ? err.message : String(err);
2464
+ if (!hasSpawnFailureKind(err, "tool-not-found")) {
2465
+ lastRuntimeFailure = err instanceof Error ? err : new Error(message);
2466
+ }
2467
+ };
2468
+ const managedProbeStartedAt = Date.now();
2469
+ const managedCandidate = spec.managedToolId ? await findManagedToolBinary(spec.managedToolId) : void 0;
2470
+ if (staleLaunch(generation, `${toolLabel}:findManagedToolBinary`)) {
2471
+ return void 0;
2472
+ }
2473
+ const candidates = managedCandidate && !spec.candidates.includes(managedCandidate) ? [managedCandidate, ...spec.candidates] : spec.candidates;
2474
+ const candidateFailures = [];
2475
+ for (const [index, command] of candidates.entries()) {
2476
+ logLatency({
2477
+ type: "phase",
2478
+ phase: "lsp_launch_candidate_attempt",
2479
+ filePath: spec.cwd,
2480
+ durationMs: 0,
2481
+ metadata: {
2482
+ tool: toolLabel,
2483
+ command,
2484
+ index,
2485
+ totalCandidates: candidates.length,
2486
+ allowInstall: canInstall(allowInstall)
2487
+ }
2488
+ });
2489
+ logSessionStart(`lsp launch candidate attempt tool=${toolLabel} idx=${index}/${candidates.length - 1} command=${command} cwd=${spec.cwd}`);
2490
+ try {
2491
+ const proc = await launchLSP(command, spec.args, {
2492
+ cwd: spec.cwd,
2493
+ env: spec.env
2494
+ });
2495
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:${command}`, proc)) {
2496
+ return void 0;
2497
+ }
2498
+ logLatency({
2499
+ type: "phase",
2500
+ phase: "lsp_launch_candidate_success",
2501
+ filePath: spec.cwd,
2502
+ durationMs: 0,
2503
+ metadata: {
2504
+ tool: toolLabel,
2505
+ command,
2506
+ index,
2507
+ source: "direct"
2508
+ }
2509
+ });
2510
+ logSessionStart(`lsp launch candidate success tool=${toolLabel} idx=${index} command=${command} source=direct`);
2511
+ if (managedCandidate !== void 0 && command === managedCandidate) {
2512
+ logAvailabilityDecision({
2513
+ tool: toolLabel,
2514
+ verdict: "available",
2515
+ outcome: "success",
2516
+ cause: "ok",
2517
+ elapsedMs: Date.now() - managedProbeStartedAt,
2518
+ latched: false,
2519
+ producer: "lsp-launch",
2520
+ classifiedBy: "probe",
2521
+ evidence: {
2522
+ binary: path7.basename(managedCandidate),
2523
+ source: "managed-dir",
2524
+ correctsLatchedRow: false
2525
+ }
2526
+ });
2527
+ }
2528
+ return { process: proc, source: "direct" };
2529
+ } catch (err) {
2530
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:${command}`)) {
2531
+ return void 0;
2532
+ }
2533
+ const message = err instanceof Error ? err.message : String(err);
2534
+ candidateFailures.push({ index, command, message, err });
2535
+ }
2536
+ }
2537
+ for (const failure of candidateFailures) {
2538
+ logLatency({
2539
+ type: "phase",
2540
+ phase: "lsp_launch_candidate_failed",
2541
+ filePath: spec.cwd,
2542
+ durationMs: 0,
2543
+ metadata: {
2544
+ tool: toolLabel,
2545
+ command: failure.command,
2546
+ index: failure.index,
2547
+ error: failure.message
2548
+ }
2549
+ });
2550
+ logSessionStart(`lsp launch candidate failed tool=${toolLabel} idx=${failure.index} command=${failure.command} error=${failure.message}`);
2551
+ trackRuntimeFailure(failure.err);
2552
+ }
2553
+ const hasOnlyRepairableCandidateFailures = candidateFailures.every((failure) => hasSpawnFailureKind(failure.err, "tool-not-found"));
2554
+ if (!hasOnlyRepairableCandidateFailures) {
2555
+ if (lastRuntimeFailure)
2556
+ throw lastRuntimeFailure;
2557
+ return void 0;
2558
+ }
2559
+ if (!canInstall(allowInstall)) {
2560
+ logSessionStart(`lsp launch install blocked tool=${toolLabel} cwd=${spec.cwd} allowInstall=${allowInstall !== false} globalDisabled=${isLspInstallDisabled()}`);
2561
+ logLatency({
2562
+ type: "phase",
2563
+ phase: "lsp_launch_install_blocked",
2564
+ filePath: spec.cwd,
2565
+ durationMs: 0,
2566
+ metadata: {
2567
+ tool: toolLabel,
2568
+ allowInstall,
2569
+ globalInstallDisabled: isLspInstallDisabled()
2570
+ }
2571
+ });
2572
+ return void 0;
2573
+ }
2574
+ if (spec.managedToolId) {
2575
+ const failedCandidate = candidateFailures.at(-1);
2576
+ const classifiedFailure = classifyProbeFailure({
2577
+ error: failedCandidate?.err instanceof Error ? failedCandidate.err : void 0,
2578
+ spawnFailure: {
2579
+ kind: hasSpawnFailureKind(failedCandidate?.err, "tool-not-found") ? "tool-not-found" : void 0
2580
+ }
2581
+ }, { command: failedCandidate?.command });
2582
+ logAvailabilityDecision({
2583
+ tool: spec.managedToolId,
2584
+ verdict: "unavailable",
2585
+ outcome: classifiedFailure.outcome,
2586
+ cause: classifiedFailure.cause,
2587
+ elapsedMs: Date.now() - managedProbeStartedAt,
2588
+ latched: false,
2589
+ classifiedBy: "probe",
2590
+ producer: "lsp-launch",
2591
+ evidence: classifiedFailure.evidence
2592
+ });
2593
+ logSessionStart(`lsp launch ensure-tool start tool=${spec.managedToolId} cwd=${spec.cwd}`);
2594
+ const installStartedAt = Date.now();
2595
+ const installed = await ensureTool(spec.managedToolId);
2596
+ const installAttempt = captureInstallAttempt(spec.managedToolId);
2597
+ if (staleLaunch(generation, `${toolLabel}:ensureTool`)) {
2598
+ return void 0;
2599
+ }
2600
+ logSessionStart(`lsp launch ensure-tool result tool=${spec.managedToolId} installed=${installed ? "yes" : "no"} path=${installed ?? ""}`);
2601
+ logLatency({
2602
+ type: "phase",
2603
+ phase: "lsp_launch_ensure_tool_result",
2604
+ filePath: spec.cwd,
2605
+ durationMs: 0,
2606
+ metadata: {
2607
+ tool: spec.managedToolId,
2608
+ installed: Boolean(installed),
2609
+ path: installed
2610
+ }
2611
+ });
2612
+ if (installed) {
2613
+ try {
2614
+ const proc = await launchLSP(installed, spec.args, {
2615
+ cwd: spec.cwd,
2616
+ env: spec.env
2617
+ });
2618
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:managed`, proc)) {
2619
+ return void 0;
2620
+ }
2621
+ logSessionStart(`lsp launch managed success tool=${spec.managedToolId} command=${installed} source=managed`);
2622
+ logLatency({
2623
+ type: "phase",
2624
+ phase: "lsp_launch_managed_success",
2625
+ filePath: spec.cwd,
2626
+ durationMs: 0,
2627
+ metadata: {
2628
+ tool: spec.managedToolId,
2629
+ command: installed
2630
+ }
2631
+ });
2632
+ const evidence = await installEvidenceForLaunch(spec.managedToolId, installed, installAttempt);
2633
+ if (staleLaunch(generation, `${toolLabel}:managed-evidence`, proc)) {
2634
+ return void 0;
2635
+ }
2636
+ logAvailabilityDecision({
2637
+ tool: spec.managedToolId,
2638
+ verdict: "available",
2639
+ outcome: "success",
2640
+ cause: "ok",
2641
+ elapsedMs: Date.now() - installStartedAt,
2642
+ latched: false,
2643
+ classifiedBy: "caller",
2644
+ producer: "lsp-launch",
2645
+ evidence: {
2646
+ ...evidence,
2647
+ correctsLatchedRow: false
2648
+ }
2649
+ });
2650
+ return { process: proc, source: "managed" };
2651
+ } catch (err) {
2652
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:managed-rejection`)) {
2653
+ return void 0;
2654
+ }
2655
+ const message = err instanceof Error ? err.message : String(err);
2656
+ logSessionStart(`lsp launch managed failed tool=${spec.managedToolId} command=${installed} error=${message}`);
2657
+ logLatency({
2658
+ type: "phase",
2659
+ phase: "lsp_launch_managed_failed",
2660
+ filePath: spec.cwd,
2661
+ durationMs: 0,
2662
+ metadata: {
2663
+ tool: spec.managedToolId,
2664
+ command: installed,
2665
+ error: message
2666
+ }
2667
+ });
2668
+ trackRuntimeFailure(err);
2669
+ const looksPathResolved = !installed.includes("/") && !installed.includes("\\");
2670
+ if (looksPathResolved && hasSpawnFailureKind(err, "tool-not-found")) {
2671
+ logSessionStart(`lsp launch managed retry force-reinstall tool=${spec.managedToolId}`);
2672
+ const reinstalled = await ensureTool(spec.managedToolId, {
2673
+ forceReinstall: true
2674
+ });
2675
+ const reinstallAttempt = captureInstallAttempt(spec.managedToolId);
2676
+ if (staleLaunch(generation, `${toolLabel}:forceReinstall`)) {
2677
+ return void 0;
2678
+ }
2679
+ if (reinstalled) {
2680
+ try {
2681
+ const proc = await launchLSP(reinstalled, spec.args, {
2682
+ cwd: spec.cwd,
2683
+ env: spec.env
2684
+ });
2685
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:forceReinstall`, proc)) {
2686
+ return void 0;
2687
+ }
2688
+ logSessionStart(`lsp launch managed force-reinstall success tool=${spec.managedToolId} command=${reinstalled}`);
2689
+ logLatency({
2690
+ type: "phase",
2691
+ phase: "lsp_launch_managed_force_reinstall_success",
2692
+ filePath: spec.cwd,
2693
+ durationMs: 0,
2694
+ metadata: {
2695
+ tool: spec.managedToolId,
2696
+ command: reinstalled
2697
+ }
2698
+ });
2699
+ const evidence = await installEvidenceForLaunch(spec.managedToolId, reinstalled, reinstallAttempt);
2700
+ if (staleLaunch(generation, `${toolLabel}:forceReinstall-evidence`, proc)) {
2701
+ return void 0;
2702
+ }
2703
+ logAvailabilityDecision({
2704
+ tool: spec.managedToolId,
2705
+ verdict: "available",
2706
+ outcome: "success",
2707
+ cause: "ok",
2708
+ elapsedMs: Date.now() - installStartedAt,
2709
+ latched: false,
2710
+ classifiedBy: "caller",
2711
+ producer: "lsp-launch",
2712
+ evidence: {
2713
+ ...evidence,
2714
+ correctsLatchedRow: false
2715
+ }
2716
+ });
2717
+ return { process: proc, source: "managed" };
2718
+ } catch (retryErr) {
2719
+ logSessionStart(`lsp launch managed force-reinstall failed tool=${spec.managedToolId} error=${retryErr instanceof Error ? retryErr.message : String(retryErr)}`);
2720
+ }
2721
+ }
2722
+ }
2723
+ }
2724
+ }
2725
+ }
2726
+ const runtimeInstall = spec.runtimeInstall;
2727
+ const runtimeAvailable = runtimeInstall ? await isOnPath(runtimeInstall.runtimeCommand) : false;
2728
+ if (staleLaunch(generation, `${toolLabel}:runtimeAvailability`)) {
2729
+ return void 0;
2730
+ }
2731
+ if (runtimeInstall && runtimeAvailable) {
2732
+ const ok = await runtimeInstall.install();
2733
+ if (staleLaunch(generation, `${toolLabel}:runtimeInstall`)) {
2734
+ return void 0;
2735
+ }
2736
+ if (ok) {
2737
+ const retry = runtimeInstall.retryCandidates ?? spec.candidates;
2738
+ for (const command of retry) {
2739
+ try {
2740
+ const proc = await launchLSP(command, spec.args, {
2741
+ cwd: spec.cwd,
2742
+ env: spec.env
2743
+ });
2744
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:runtime`, proc)) {
2745
+ return void 0;
2746
+ }
2747
+ return { process: proc, source: "managed" };
2748
+ } catch (err) {
2749
+ if (staleLaunch(generation, `${toolLabel}:launchLSP:runtime`)) {
2750
+ return void 0;
2751
+ }
2752
+ trackRuntimeFailure(err);
2753
+ }
2754
+ }
2755
+ }
2756
+ }
2757
+ if (lastRuntimeFailure) {
2758
+ throw lastRuntimeFailure;
2759
+ }
2760
+ return void 0;
2761
+ }
2762
+ async function resolveAndLaunchBundle(spec, allowInstall) {
2763
+ let runtime;
2764
+ for (const candidate of spec.runtimeCandidates) {
2765
+ if (await isOnPath(candidate)) {
2766
+ runtime = candidate;
2767
+ break;
2768
+ }
2769
+ }
2770
+ if (!runtime) {
2771
+ logSessionStart(`lsp launch bundle skip tool=${spec.bundleToolId}: no runtime on PATH (tried ${spec.runtimeCandidates.join(", ")})`);
2772
+ return void 0;
2773
+ }
2774
+ let bundleDir = await getToolPath(spec.bundleToolId);
2775
+ if (!bundleDir && canInstall(allowInstall)) {
2776
+ bundleDir = await ensureTool(spec.bundleToolId);
2777
+ }
2778
+ if (!bundleDir) {
2779
+ logSessionStart(`lsp launch bundle skip tool=${spec.bundleToolId}: bundle not installed (allowInstall=${allowInstall !== false})`);
2780
+ return void 0;
2781
+ }
2782
+ try {
2783
+ const proc = await launchLSP(runtime, spec.args(bundleDir), {
2784
+ cwd: spec.cwd,
2785
+ env: spec.env
2786
+ });
2787
+ logSessionStart(`lsp launch bundle success tool=${spec.bundleToolId} runtime=${runtime} bundle=${bundleDir}`);
2788
+ return { process: proc, source: "managed" };
2789
+ } catch (err) {
2790
+ const message = err instanceof Error ? err.message : String(err);
2791
+ logSessionStart(`lsp launch bundle failed tool=${spec.bundleToolId} runtime=${runtime} error=${message}`);
2792
+ return void 0;
2793
+ }
2794
+ }
2795
+ async function resolveAndLaunchTreeBinary(spec, allowInstall) {
2796
+ for (const command of spec.candidates) {
2797
+ try {
2798
+ const proc = await launchLSP(command, spec.args, {
2799
+ cwd: spec.cwd,
2800
+ env: spec.env
2801
+ });
2802
+ return { process: proc, source: "direct" };
2803
+ } catch {
2804
+ }
2805
+ }
2806
+ let bundleDir = await getToolPath(spec.bundleToolId);
2807
+ if (!bundleDir && canInstall(allowInstall)) {
2808
+ bundleDir = await ensureTool(spec.bundleToolId);
2809
+ }
2810
+ if (!bundleDir) {
2811
+ logSessionStart(`lsp launch tree-bin skip tool=${spec.bundleToolId}: not on PATH and bundle not installed (allowInstall=${allowInstall !== false})`);
2812
+ return void 0;
2813
+ }
2814
+ const suffix = process.platform === "win32" ? ".exe" : "";
2815
+ const binPath = path7.join(bundleDir, ...spec.binRelPath.split("/")) + suffix;
2816
+ try {
2817
+ const proc = await launchLSP(binPath, spec.args, {
2818
+ cwd: spec.cwd,
2819
+ env: spec.env
2820
+ });
2821
+ logSessionStart(`lsp launch tree-bin success tool=${spec.bundleToolId} bin=${binPath}`);
2822
+ return { process: proc, source: "managed" };
2823
+ } catch (err) {
2824
+ const message = err instanceof Error ? err.message : String(err);
2825
+ logSessionStart(`lsp launch tree-bin failed tool=${spec.bundleToolId} bin=${binPath} error=${message}`);
2826
+ return void 0;
2827
+ }
2828
+ }
2829
+ function nodeBinLocalCandidates(root, baseName) {
2830
+ const localBase = path7.join(root, "node_modules", ".bin", baseName);
2831
+ if (process.platform === "win32") {
2832
+ return [`${localBase}.cmd`, `${localBase}.exe`];
2833
+ }
2834
+ return [localBase];
2835
+ }
2836
+ function nodeBinCandidates(root, baseName) {
2837
+ return [...nodeBinLocalCandidates(root, baseName), baseName];
2838
+ }
2839
+ function piAgentExtensionsRootKey(file) {
2840
+ const dirKey = normalizeEphemeralMapKey(path7.dirname(path7.resolve(file)));
2841
+ const marker = "/.pi/agent/extensions";
2842
+ const index = dirKey.indexOf(marker);
2843
+ if (index === -1)
2844
+ return void 0;
2845
+ return dirKey.slice(0, index + marker.length);
2846
+ }
2847
+ function IgnoreHomeRoot(primary) {
2848
+ const homeKey = normalizeEphemeralMapKey(path7.resolve(os6.homedir()));
2849
+ return withRootMarkers(async (file) => {
2850
+ const root = await primary(file);
2851
+ if (!root)
2852
+ return void 0;
2853
+ return normalizeEphemeralMapKey(path7.resolve(root)) === homeKey ? void 0 : root;
2854
+ }, primary.rootMarkers ?? []);
2855
+ }
2856
+ function rubyBinCandidates(baseName) {
2857
+ const candidates = [];
2858
+ const home = os6.homedir();
2859
+ const isWin = process.platform === "win32";
2860
+ const ext = isWin ? ".bat" : "";
2861
+ candidates.push(path7.join(home, ".local", "share", "mise", "installs", "ruby", "bin", `${baseName}${ext}`));
2862
+ candidates.push(path7.join(home, ".asdf", "installs", "ruby", "bin", `${baseName}${ext}`));
2863
+ if (isWin) {
2864
+ const driveRoot = path7.parse(home).root;
2865
+ for (const entry of getRubyVersionDirNamesSync(driveRoot)) {
2866
+ candidates.push(path7.join(driveRoot, entry, "bin", `${baseName}.bat`));
2867
+ candidates.push(path7.join(driveRoot, entry, "bin", baseName));
2868
+ }
2869
+ }
2870
+ return candidates;
2871
+ }
2872
+ function createInteractiveServer(spec) {
2873
+ return {
2874
+ id: spec.id,
2875
+ name: spec.name,
2876
+ extensions: spec.extensions,
2877
+ idleEviction: "unmeasured",
2878
+ // `InteractiveServerSpec` declares no role, so the factory applies the
2879
+ // stated default rather than leaving `role` absent for each consumer to
2880
+ // read by negation (#1488). An auxiliary would declare its role on a
2881
+ // row of its own, the way the four scanner rows do.
2882
+ role: DEFAULT_LSP_SERVER_ROLE,
2883
+ root: spec.root,
2884
+ rootMarkers: spec.root.rootMarkers,
2885
+ fallbackFor: spec.fallbackFor,
2886
+ requiresProjectRoot: spec.requiresProjectRoot,
2887
+ availabilityKey: typeof spec.command === "string" && isSimpleCommand(spec.command) ? spec.command : void 0,
2888
+ async spawn(root) {
2889
+ const command = typeof spec.command === "function" ? spec.command(root) : spec.command;
2890
+ const args = typeof spec.args === "function" ? spec.args(root) : spec.args || [];
2891
+ if (isSimpleCommand(command) && isDirectLspCommandTemporarilyUnavailable(command)) {
2892
+ return void 0;
2893
+ }
2894
+ const runtimeEnv = spec.runtime === "java" ? await resolveJavaRuntimeEnv() : void 0;
2895
+ try {
2896
+ const proc = await launchLSP(command, args, {
2897
+ cwd: root,
2898
+ ...runtimeEnv ? { env: runtimeEnv } : {}
2899
+ });
2900
+ const initialization = typeof spec.initialization === "function" ? spec.initialization(root) : spec.initialization;
2901
+ return { process: proc, source: "direct", initialization };
2902
+ } catch (err) {
2903
+ if (hasSpawnFailureKind(err, "tool-not-found")) {
2904
+ markDirectLspCommandUnavailable(command);
2905
+ }
2906
+ return void 0;
2907
+ }
2908
+ }
2909
+ };
2910
+ }
2911
+ function PriorityRoot(markerGroups, excludePatterns, stopDir) {
2912
+ const resolvers = markerGroups.map((markers) => NearestRoot(markers, excludePatterns, stopDir));
2913
+ return withRootMarkers(async (file) => {
2914
+ for (const resolve3 of resolvers) {
2915
+ const root = await resolve3(file);
2916
+ if (root)
2917
+ return root;
2918
+ }
2919
+ return void 0;
2920
+ }, markerGroups.flat());
2921
+ }
2922
+ var FileDirRoot = async (file) => {
2923
+ const candidate = path7.resolve(path7.dirname(file));
2924
+ return nearestNonExcludedFallbackRoot(candidate);
2925
+ };
2926
+ function RootWithFallback(primary, fallback = FileDirRoot) {
2927
+ return withRootMarkers(async (file) => {
2928
+ const primaryRoot = await primary(file);
2929
+ if (primaryRoot)
2930
+ return primaryRoot;
2931
+ return fallback(file);
2932
+ }, primary.rootMarkers ?? []);
2933
+ }
2934
+ function WorkspacePriorityRoot(markerGroups, excludePatterns) {
2935
+ return withRootMarkers(async (file) => PriorityRoot(markerGroups, excludePatterns, process.cwd())(file), markerGroups.flat());
2936
+ }
2937
+ function isPermissionFsError(err) {
2938
+ const code = err?.code;
2939
+ return code === "EACCES" || code === "EPERM";
2940
+ }
2941
+ function markerProbeDir(dir, pattern) {
2942
+ const normalized = toPosix(pattern);
2943
+ const slash = normalized.lastIndexOf("/");
2944
+ const parentPattern = slash >= 0 ? normalized.slice(0, slash) : "";
2945
+ return parentPattern ? path7.join(dir, ...parentPattern.split("/").filter(Boolean)) : dir;
2946
+ }
2947
+ async function markerExists(dir, pattern) {
2948
+ if (!pattern.includes("*")) {
2949
+ try {
2950
+ await stat(path7.join(dir, pattern));
2951
+ return true;
2952
+ } catch (err) {
2953
+ if (isPermissionFsError(err)) {
2954
+ logSessionStart(`lsp root marker skipped: permission error stat ${path7.join(dir, pattern)}`);
2955
+ }
2956
+ return false;
2957
+ }
2958
+ }
2959
+ const normalized = pattern.replace(/\\/g, "/");
2960
+ const slash = normalized.lastIndexOf("/");
2961
+ const basenamePattern = slash >= 0 ? normalized.slice(slash + 1) : normalized;
2962
+ if (!basenamePattern)
2963
+ return false;
2964
+ const targetDir = markerProbeDir(dir, normalized);
2965
+ try {
2966
+ const entries = await readdir(targetDir, { withFileTypes: true });
2967
+ return direntsHaveMarkerGlobMatch(entries, basenamePattern);
2968
+ } catch (err) {
2969
+ if (isPermissionFsError(err)) {
2970
+ logSessionStart(`lsp root marker skipped: permission error read ${targetDir}`);
2971
+ }
2972
+ return false;
2973
+ }
2974
+ }
2975
+ var rootMtimeBound = (label) => ({
2976
+ ms: HOOK_WALL_BUDGET_MS.tool_result_edit,
2977
+ signal: void 0,
2978
+ hook: "tool_result_edit",
2979
+ label
2980
+ });
2981
+ function NearestRoot(includePatterns, excludePatterns, stopDir) {
2982
+ const cache = /* @__PURE__ */ new Map();
2983
+ const inFlight = /* @__PURE__ */ new Map();
2984
+ const subdirPatterns = [];
2985
+ for (const pattern of [...includePatterns, ...excludePatterns ?? []]) {
2986
+ if (markerProbeDir("", pattern))
2987
+ subdirPatterns.push(pattern);
2988
+ }
2989
+ return withRootMarkers(async (file) => {
2990
+ const startDir = path7.resolve(path7.dirname(file));
2991
+ const dirKey = normalizeMapKey(startDir);
2992
+ const cached = cache.get(dirKey);
2993
+ if (cached !== void 0 && Date.now() - cached.walkedAt < FRESHNESS_CADENCE_MS && await bounded(dirMtimesStillFreshAsync(cached.probedDirs), rootMtimeBound("lsp-root-memo-freshness")) === true)
2994
+ return cached.root;
2995
+ const flying = inFlight.get(dirKey);
2996
+ if (flying)
2997
+ return flying;
2998
+ const probedDirs = [];
2999
+ const walkedAt = Date.now();
3000
+ const promise = (async () => {
3001
+ let currentDir = startDir;
3002
+ const fsRoot = path7.parse(currentDir).root;
3003
+ const stop = stopDir ? path7.resolve(stopDir) : fsRoot;
3004
+ while (true) {
3005
+ if (stop !== fsRoot && currentDir.startsWith(stop + path7.sep) === false && currentDir !== stop) {
3006
+ break;
3007
+ }
3008
+ const stepDirs = [
3009
+ currentDir,
3010
+ ...subdirPatterns.map((pattern) => markerProbeDir(currentDir, pattern))
3011
+ ];
3012
+ const stepRecords = await bounded(dirMtimeRecordsAsync(stepDirs), rootMtimeBound("lsp-root-walk-dir-mtimes"));
3013
+ probedDirs.push(...stepRecords ?? unknownDirMtimeRecords(stepDirs));
3014
+ if (excludePatterns) {
3015
+ let excluded = false;
3016
+ for (const pattern of excludePatterns) {
3017
+ if (await markerExists(currentDir, pattern)) {
3018
+ excluded = true;
3019
+ break;
3020
+ }
3021
+ }
3022
+ if (excluded) {
3023
+ currentDir = path7.dirname(currentDir);
3024
+ continue;
3025
+ }
3026
+ }
3027
+ for (const pattern of includePatterns) {
3028
+ if (await markerExists(currentDir, pattern) && (pattern !== ".git" || currentDir !== os6.tmpdir()) && !await isExcludedLspRoot(currentDir)) {
3029
+ return enforceLspRootCeiling(currentDir, process.cwd(), file);
3030
+ }
3031
+ }
3032
+ if (currentDir === stop || currentDir === fsRoot) {
3033
+ break;
3034
+ }
3035
+ currentDir = path7.dirname(currentDir);
3036
+ }
3037
+ return void 0;
3038
+ })();
3039
+ inFlight.set(dirKey, promise);
3040
+ try {
3041
+ const result = await promise;
3042
+ if (result !== void 0)
3043
+ cache.set(dirKey, { root: result, probedDirs, walkedAt });
3044
+ return result;
3045
+ } finally {
3046
+ inFlight.delete(dirKey);
3047
+ }
3048
+ }, includePatterns);
3049
+ }
3050
+ var createRootDetector = NearestRoot;
3051
+ function isOnPath(command) {
3052
+ return isCommandAvailableAsync(command);
3053
+ }
3054
+ async function tryGoInstallGopls() {
3055
+ const isWindows2 = process.platform === "win32";
3056
+ const result = await safeSpawnAsync(isWindows2 ? "go.exe" : "go", ["install", "golang.org/x/tools/gopls@latest"], { timeout: 18e4, ignoreAmbientSignal: true });
3057
+ return !result.error && result.status === 0;
3058
+ }
3059
+ async function tryDotnetToolInstall(tool) {
3060
+ mkdirSync2(PI_LENS_BIN_DIR2, { recursive: true });
3061
+ const result = await safeSpawnAsync("dotnet", ["tool", "install", "--tool-path", PI_LENS_BIN_DIR2, tool], { timeout: 18e4, ignoreAmbientSignal: true });
3062
+ if (!result.error && result.status === 0)
3063
+ return true;
3064
+ const stderr = result.stderr ?? "";
3065
+ if (stderr.includes("No NuGet sources are defined or enabled")) {
3066
+ logSessionStart(`lsp dotnet-install: NuGet sources missing \u2014 cannot install ${tool}. Run: dotnet nuget add source https://api.nuget.org/v3/index.json -n nuget.org`);
3067
+ return false;
3068
+ }
3069
+ const updateResult = await safeSpawnAsync("dotnet", ["tool", "update", "--tool-path", PI_LENS_BIN_DIR2, tool], { timeout: 18e4, ignoreAmbientSignal: true });
3070
+ return !updateResult.error && updateResult.status === 0;
3071
+ }
3072
+ async function findAncestorFile(startDir, ...relativeSegments) {
3073
+ return findAncestorFileAmong(startDir, [relativeSegments]);
3074
+ }
3075
+ async function findAncestorFileAmong(startDir, candidateSegmentLists) {
3076
+ const fs5 = await import("node:fs/promises");
3077
+ let currentDir = path7.resolve(startDir);
3078
+ while (!isAtOrAboveHomeDir(currentDir)) {
3079
+ for (const segments of candidateSegmentLists) {
3080
+ const candidate = path7.join(currentDir, ...segments);
3081
+ try {
3082
+ await fs5.access(candidate);
3083
+ return candidate;
3084
+ } catch {
3085
+ }
3086
+ }
3087
+ const parent = path7.dirname(currentDir);
3088
+ if (parent === currentDir)
3089
+ break;
3090
+ currentDir = parent;
3091
+ }
3092
+ return void 0;
3093
+ }
3094
+ var CLASSIC_TS_REPAIR_FAMILY = "lsp.classic-ts-repair-guard";
3095
+ var CLASSIC_TS_REPAIR_VERSION = 1;
3096
+ function classicTsRepairState() {
3097
+ return getProcessSingleton(CLASSIC_TS_REPAIR_FAMILY, CLASSIC_TS_REPAIR_VERSION, () => ({ attempted: false }));
3098
+ }
3099
+ function resetClassicTsRepairGuard() {
3100
+ classicTsRepairState().attempted = false;
3101
+ }
3102
+ function typescriptDirsForTsc(tscPath) {
3103
+ const binDir = path7.dirname(tscPath);
3104
+ return [
3105
+ path7.join(binDir, "..", "typescript"),
3106
+ path7.join(binDir, "..", "..", "typescript")
3107
+ ];
3108
+ }
3109
+ async function typescriptVersionForTsc(tscPath) {
3110
+ if (!path7.isAbsolute(tscPath))
3111
+ return void 0;
3112
+ for (const dir of typescriptDirsForTsc(tscPath)) {
3113
+ let manifest;
3114
+ try {
3115
+ manifest = await readFile(path7.join(dir, "package.json"), "utf8");
3116
+ } catch {
3117
+ continue;
3118
+ }
3119
+ let version;
3120
+ try {
3121
+ const parsed = JSON.parse(manifest);
3122
+ if (typeof parsed !== "object" || parsed === null || !("version" in parsed) || typeof parsed.version !== "string") {
3123
+ return void 0;
3124
+ }
3125
+ version = parsed.version;
3126
+ } catch {
3127
+ return void 0;
3128
+ }
3129
+ const majorText = version.split(".", 1)[0] ?? "";
3130
+ const major = /^\d+$/.test(majorText) ? Number(majorText) : Number.NaN;
3131
+ if (!Number.isFinite(major))
3132
+ return void 0;
3133
+ return { version, major };
3134
+ }
3135
+ return void 0;
3136
+ }
3137
+ async function findTsserverPath(root, allowInstall) {
3138
+ const fs5 = await import("node:fs/promises");
3139
+ const ancestorHit = await findAncestorFile(root, "node_modules", "typescript", "lib", "tsserver.js");
3140
+ if (ancestorHit)
3141
+ return ancestorHit;
3142
+ const cwdCandidate = path7.join(process.cwd(), "node_modules", "typescript", "lib", "tsserver.js");
3143
+ try {
3144
+ await fs5.access(cwdCandidate);
3145
+ return cwdCandidate;
3146
+ } catch {
3147
+ }
3148
+ const tsserverForTsc = async (tscPath) => {
3149
+ if (!tscPath)
3150
+ return void 0;
3151
+ for (const dir of typescriptDirsForTsc(tscPath)) {
3152
+ const candidate = path7.join(dir, "lib", "tsserver.js");
3153
+ try {
3154
+ await fs5.access(candidate);
3155
+ return candidate;
3156
+ } catch {
3157
+ }
3158
+ }
3159
+ return void 0;
3160
+ };
3161
+ const installAllowed = canInstall(allowInstall);
3162
+ const discoveredTsc = await ensureTool("typescript", {
3163
+ allowInstall: installAllowed
3164
+ });
3165
+ const discoveredTsserver = await tsserverForTsc(discoveredTsc);
3166
+ if (discoveredTsserver || !discoveredTsc || !installAllowed || classicTsRepairState().attempted) {
3167
+ return discoveredTsserver;
3168
+ }
3169
+ const discoveredVersion = await typescriptVersionForTsc(discoveredTsc);
3170
+ if (!discoveredVersion || discoveredVersion.major < 7)
3171
+ return void 0;
3172
+ classicTsRepairState().attempted = true;
3173
+ logSessionStart(`lsp typescript: managed compiler resolved to TypeScript ${discoveredVersion.version}, which ships no tsserver.js; reinstalling pinned classic fallback`);
3174
+ const repairedTsc = await ensureTool("typescript", {
3175
+ allowInstall: true,
3176
+ forceReinstall: true
3177
+ });
3178
+ return tsserverForTsc(repairedTsc);
3179
+ }
3180
+ async function findNativeTypeScriptLsp(root) {
3181
+ let currentDir = path7.resolve(root);
3182
+ while (!isAtOrAboveHomeDir(currentDir)) {
3183
+ const typescriptDir = path7.join(currentDir, "node_modules", "typescript");
3184
+ const packageJsonPath = path7.join(typescriptDir, "package.json");
3185
+ let packageJsonText;
3186
+ try {
3187
+ packageJsonText = await readFile(packageJsonPath, "utf8");
3188
+ } catch (error) {
3189
+ if (error.code !== "ENOENT") {
3190
+ return void 0;
3191
+ }
3192
+ try {
3193
+ const dirStat = await stat(typescriptDir);
3194
+ if (dirStat.isDirectory())
3195
+ return void 0;
3196
+ } catch {
3197
+ }
3198
+ const parent = path7.dirname(currentDir);
3199
+ if (parent === currentDir)
3200
+ return void 0;
3201
+ currentDir = parent;
3202
+ continue;
3203
+ }
3204
+ let version;
3205
+ try {
3206
+ const parsed = JSON.parse(packageJsonText);
3207
+ if (typeof parsed !== "object" || parsed === null || !("version" in parsed) || typeof parsed.version !== "string") {
3208
+ return void 0;
3209
+ }
3210
+ version = parsed.version;
3211
+ } catch {
3212
+ return void 0;
3213
+ }
3214
+ const majorText = version.split(".", 1)[0] ?? "";
3215
+ const major = /^\d+$/.test(majorText) ? Number(majorText) : Number.NaN;
3216
+ if (!Number.isFinite(major) || major < 7)
3217
+ return void 0;
3218
+ const localTsc = path7.join(currentDir, "node_modules", ".bin", "tsc");
3219
+ const candidates = process.platform === "win32" ? [`${localTsc}.cmd`, `${localTsc}.exe`, localTsc] : [localTsc];
3220
+ for (const command of candidates) {
3221
+ try {
3222
+ await access(command);
3223
+ return { command, version };
3224
+ } catch {
3225
+ }
3226
+ }
3227
+ return void 0;
3228
+ }
3229
+ return void 0;
3230
+ }
3231
+ function dotnetToolCandidates(tool) {
3232
+ const home = os6.homedir();
3233
+ return [
3234
+ path7.join(PI_LENS_BIN_DIR2, `${tool}.exe`),
3235
+ path7.join(PI_LENS_BIN_DIR2, tool),
3236
+ path7.join(home, ".dotnet", "tools", `${tool}.exe`),
3237
+ path7.join(home, ".dotnet", "tools", tool),
3238
+ tool
3239
+ ].filter(Boolean);
3240
+ }
3241
+ function binExeVariants(dir, tool) {
3242
+ return process.platform === "win32" ? [path7.join(dir, `${tool}.exe`), path7.join(dir, tool)] : [path7.join(dir, tool)];
3243
+ }
3244
+ function goBinCandidates(tool) {
3245
+ const gopath = process.env.GOPATH?.split(path7.delimiter)[0] || path7.join(os6.homedir(), "go");
3246
+ return [tool, ...binExeVariants(path7.join(gopath, "bin"), tool)];
3247
+ }
3248
+ function cargoBinCandidates(tool) {
3249
+ const cargoHome = process.env.CARGO_HOME || path7.join(os6.homedir(), ".cargo");
3250
+ return [tool, ...binExeVariants(path7.join(cargoHome, "bin"), tool)];
3251
+ }
3252
+ async function tryGemInstall(gem) {
3253
+ const { join: join3 } = await import("node:path");
3254
+ const binDir = join3(getGlobalPiLensDir(), "bin");
3255
+ const { mkdir } = await import("node:fs/promises");
3256
+ await mkdir(binDir, { recursive: true });
3257
+ const result = await safeSpawnAsync("gem", ["install", gem, "--bindir", binDir, "--no-document"], { timeout: 18e4, ignoreAmbientSignal: true });
3258
+ const ok = !result.error && result.status === 0;
3259
+ if (ok) {
3260
+ const sep2 = process.platform === "win32" ? ";" : ":";
3261
+ if (!process.env.PATH?.includes(binDir)) {
3262
+ process.env.PATH = `${binDir}${sep2}${process.env.PATH ?? ""}`;
3263
+ }
3264
+ }
3265
+ return ok;
3266
+ }
3267
+ function DenoExcludeRoot(primary) {
3268
+ const denoDetector = createRootDetector(["deno.json", "deno.jsonc"]);
3269
+ return async (file) => {
3270
+ const denoRoot = await denoDetector(file);
3271
+ if (denoRoot)
3272
+ return void 0;
3273
+ return primary(file);
3274
+ };
3275
+ }
3276
+ var JS_TS_LSP_EXTENSIONS = KIND_EXTENSIONS["jsts"].filter((ext) => ext !== ".svelte" && ext !== ".vue");
3277
+ var TS_CONFIG_MARKERS = ["tsconfig.json", "jsconfig.json"];
3278
+ var TS_TOOLING_MARKERS = [
3279
+ "package-lock.json",
3280
+ "bun.lockb",
3281
+ "bun.lock",
3282
+ "pnpm-lock.yaml",
3283
+ "yarn.lock",
3284
+ "package.json"
3285
+ ];
3286
+ var TS_FAMILY_EXTENSIONS = /* @__PURE__ */ new Set([".ts", ".tsx", ".mts", ".cts"]);
3287
+ function isTsFamilyFile(file) {
3288
+ return TS_FAMILY_EXTENSIONS.has(path7.extname(file).toLowerCase());
3289
+ }
3290
+ function tsConfigMarkersForFile(file) {
3291
+ return isTsFamilyFile(file) ? ["tsconfig.json"] : TS_CONFIG_MARKERS;
3292
+ }
3293
+ var TypeScriptConfigRootTsOnly = IgnoreHomeRoot(createRootDetector(["tsconfig.json"]));
3294
+ var TypeScriptConfigRootEither = IgnoreHomeRoot(createRootDetector([...TS_CONFIG_MARKERS]));
3295
+ var TypeScriptToolingRoot = IgnoreHomeRoot(createRootDetector([...TS_TOOLING_MARKERS]));
3296
+ async function findTypeScriptProjectRoot(file) {
3297
+ const configDetector = isTsFamilyFile(file) ? TypeScriptConfigRootTsOnly : TypeScriptConfigRootEither;
3298
+ const [configRoot, toolingRoot] = await Promise.all([
3299
+ configDetector(file),
3300
+ TypeScriptToolingRoot(file)
3301
+ ]);
3302
+ if (!configRoot)
3303
+ return toolingRoot;
3304
+ if (!toolingRoot)
3305
+ return configRoot;
3306
+ return isSameOrWithin(toolingRoot, configRoot) ? configRoot : toolingRoot;
3307
+ }
3308
+ async function findExtensionBoundedRoot(file, extensionRootKey) {
3309
+ const startDir = path7.resolve(path7.dirname(file));
3310
+ let currentDir = startDir;
3311
+ const configMarkers = tsConfigMarkersForFile(file);
3312
+ while (true) {
3313
+ for (const pattern of configMarkers) {
3314
+ try {
3315
+ await stat(path7.join(currentDir, pattern));
3316
+ return currentDir;
3317
+ } catch {
3318
+ }
3319
+ }
3320
+ for (const pattern of TS_TOOLING_MARKERS) {
3321
+ try {
3322
+ await stat(path7.join(currentDir, pattern));
3323
+ return currentDir;
3324
+ } catch {
3325
+ }
3326
+ }
3327
+ const currentKey = normalizeEphemeralMapKey(path7.resolve(currentDir));
3328
+ if (currentKey === extensionRootKey)
3329
+ return void 0;
3330
+ const parent = path7.dirname(currentDir);
3331
+ if (parent === currentDir)
3332
+ return void 0;
3333
+ currentDir = parent;
3334
+ }
3335
+ }
3336
+ async function hasAgentLevelProjectMarker(extensionRootKey) {
3337
+ const agentDir = path7.dirname(extensionRootKey);
3338
+ if (!agentDir || agentDir === extensionRootKey)
3339
+ return false;
3340
+ for (const pattern of [...TS_CONFIG_MARKERS, ...TS_TOOLING_MARKERS]) {
3341
+ try {
3342
+ await stat(path7.join(agentDir, pattern));
3343
+ return true;
3344
+ } catch {
3345
+ }
3346
+ }
3347
+ return false;
3348
+ }
3349
+ var TypeScriptRoot = withRootMarkers(DenoExcludeRoot(async (file) => {
3350
+ const extensionRootKey = piAgentExtensionsRootKey(file);
3351
+ if (extensionRootKey) {
3352
+ const bounded2 = await findExtensionBoundedRoot(file, extensionRootKey);
3353
+ if (bounded2)
3354
+ return bounded2;
3355
+ if (await hasAgentLevelProjectMarker(extensionRootKey)) {
3356
+ return FileDirRoot(file);
3357
+ }
3358
+ return void 0;
3359
+ }
3360
+ const projectRoot = await findTypeScriptProjectRoot(file);
3361
+ if (projectRoot)
3362
+ return projectRoot;
3363
+ return FileDirRoot(file);
3364
+ }), [...TS_CONFIG_MARKERS, ...TS_TOOLING_MARKERS]);
3365
+ var TypeScriptServer = {
3366
+ id: "typescript",
3367
+ idleEviction: "transparent",
3368
+ role: "language",
3369
+ name: "TypeScript Language Server",
3370
+ extensions: JS_TS_LSP_EXTENSIONS,
3371
+ autoPropagateDiagnostics: true,
3372
+ root: TypeScriptRoot,
3373
+ rootMarkers: TypeScriptRoot.rootMarkers,
3374
+ async spawn(root, options) {
3375
+ const fs5 = await import("node:fs/promises");
3376
+ const nativeLsp = await findNativeTypeScriptLsp(root);
3377
+ if (nativeLsp) {
3378
+ const env2 = await getToolEnvironment();
3379
+ logSessionStart(`lsp typescript-native: version=${nativeLsp.version} command=${nativeLsp.command}`);
3380
+ const proc2 = await launchLSP(nativeLsp.command, ["--lsp", "--stdio"], {
3381
+ cwd: root,
3382
+ env: env2
3383
+ });
3384
+ return { process: proc2, source: "direct", launchVariant: "native-ts7" };
3385
+ }
3386
+ let source = "direct";
3387
+ let lspPath = await findAncestorFileAmong(root, [
3388
+ ["node_modules", ".bin", "typescript-language-server.cmd"],
3389
+ ["node_modules", ".bin", "typescript-language-server"]
3390
+ ]);
3391
+ if (!lspPath) {
3392
+ lspPath = await ensureTool("typescript-language-server", {
3393
+ allowInstall: canInstall(options?.allowInstall)
3394
+ });
3395
+ if (lspPath)
3396
+ source = "managed";
3397
+ if (!lspPath) {
3398
+ return void 0;
3399
+ }
3400
+ }
3401
+ let tsserverPath = await findTsserverPath(root, options?.allowInstall);
3402
+ if (!tsserverPath) {
3403
+ const localCandidate = path7.join(path7.dirname(lspPath), "..", "typescript", "lib", "tsserver.js");
3404
+ try {
3405
+ await fs5.access(localCandidate);
3406
+ tsserverPath = localCandidate;
3407
+ } catch {
3408
+ }
3409
+ }
3410
+ if (tsserverPath)
3411
+ source = "managed";
3412
+ const env = await getToolEnvironment();
3413
+ const proc = await launchLSP(lspPath, ["--stdio"], {
3414
+ cwd: root,
3415
+ env: {
3416
+ ...env,
3417
+ TSSERVER_PATH: tsserverPath
3418
+ }
3419
+ });
3420
+ return {
3421
+ process: proc,
3422
+ source,
3423
+ initialization: tsserverPath ? { tsserver: { path: tsserverPath } } : void 0,
3424
+ launchVariant: "classic"
3425
+ };
3426
+ }
3427
+ };
3428
+ var DenoServer = {
3429
+ id: "deno",
3430
+ idleEviction: "transparent",
3431
+ role: "language",
3432
+ name: "Deno Language Server",
3433
+ fallbackFor: "typescript",
3434
+ extensions: JS_TS_LSP_EXTENSIONS,
3435
+ autoPropagateDiagnostics: true,
3436
+ root: createRootDetector(["deno.json", "deno.jsonc"]),
3437
+ async spawn(root, options) {
3438
+ return resolveAndLaunch({
3439
+ candidates: ["deno"],
3440
+ args: ["lsp"],
3441
+ cwd: root,
3442
+ managedToolId: "deno"
3443
+ }, options?.allowInstall);
3444
+ }
3445
+ };
3446
+ var PythonServer = {
3447
+ id: "python",
3448
+ idleEviction: "transparent",
3449
+ role: "language",
3450
+ name: "Pyright Language Server",
3451
+ extensions: KIND_EXTENSIONS["python"],
3452
+ root: RootWithFallback(createRootDetector([
3453
+ ".git",
3454
+ "pyproject.toml",
3455
+ "setup.py",
3456
+ "setup.cfg",
3457
+ "requirements.txt",
3458
+ "Pipfile",
3459
+ "poetry.lock"
3460
+ ])),
3461
+ async spawn(root, options) {
3462
+ const pythonEnvironment = await detectPythonEnvironment(root);
3463
+ const env = augmentPythonEnvironment(await getToolEnvironment(), pythonEnvironment);
3464
+ let source = "direct";
3465
+ const pyrightInit = (pythonPath) => ({
3466
+ ...pythonPath ? { pythonPath } : {},
3467
+ openFilesOnly: true
3468
+ });
3469
+ const projectPyright = await resolveAndLaunch({
3470
+ candidates: [
3471
+ ...pythonEnvironmentToolCandidates(pythonEnvironment, "pyright-langserver"),
3472
+ ...nodeBinLocalCandidates(root, "pyright-langserver"),
3473
+ ...pythonEnvironmentToolCandidates(pythonEnvironment, "basedpyright-langserver"),
3474
+ ...nodeBinLocalCandidates(root, "basedpyright-langserver")
3475
+ ],
3476
+ args: ["--stdio"],
3477
+ cwd: root,
3478
+ env
3479
+ }, false);
3480
+ if (projectPyright) {
3481
+ return {
3482
+ process: projectPyright.process,
3483
+ source: projectPyright.source,
3484
+ initialization: pyrightInit(pythonEnvironment?.pythonPath)
3485
+ };
3486
+ }
3487
+ const projectTy = await resolveAndLaunch({
3488
+ candidates: pythonEnvironmentToolCandidates(pythonEnvironment, "ty"),
3489
+ args: ["server"],
3490
+ cwd: root,
3491
+ env
3492
+ }, false);
3493
+ if (projectTy) {
3494
+ return { process: projectTy.process, source: projectTy.source };
3495
+ }
3496
+ const pathPyright = await resolveAndLaunch({
3497
+ candidates: ["pyright-langserver", "basedpyright-langserver"],
3498
+ args: ["--stdio"],
3499
+ cwd: root,
3500
+ env
3501
+ }, false);
3502
+ if (pathPyright) {
3503
+ return {
3504
+ process: pathPyright.process,
3505
+ source: pathPyright.source,
3506
+ initialization: pyrightInit(pythonEnvironment?.pythonPath)
3507
+ };
3508
+ }
3509
+ const pathTy = await resolveAndLaunch({
3510
+ candidates: ["ty"],
3511
+ args: ["server"],
3512
+ cwd: root,
3513
+ env
3514
+ }, false);
3515
+ if (pathTy) {
3516
+ return { process: pathTy.process, source: pathTy.source };
3517
+ }
3518
+ const pyrightPath = await ensureTool("pyright", {
3519
+ allowInstall: canInstall(options?.allowInstall)
3520
+ });
3521
+ if (!pyrightPath)
3522
+ return void 0;
3523
+ source = "managed";
3524
+ const binDir = path7.dirname(pyrightPath);
3525
+ const isWindows2 = process.platform === "win32";
3526
+ const managedCandidates = isWindows2 ? [
3527
+ path7.join(binDir, "pyright-langserver.cmd"),
3528
+ path7.join(binDir, "pyright-langserver"),
3529
+ "pyright-langserver"
3530
+ ] : [path7.join(binDir, "pyright-langserver"), "pyright-langserver"];
3531
+ const resolved = await resolveAndLaunch({ candidates: managedCandidates, args: ["--stdio"], cwd: root, env }, false);
3532
+ if (!resolved)
3533
+ return void 0;
3534
+ return {
3535
+ process: resolved.process,
3536
+ source,
3537
+ initialization: pyrightInit(pythonEnvironment?.pythonPath)
3538
+ };
3539
+ }
3540
+ };
3541
+ var PythonJediServer = {
3542
+ id: "python-jedi",
3543
+ idleEviction: "transparent",
3544
+ role: "language",
3545
+ name: "Jedi Language Server",
3546
+ fallbackFor: "python",
3547
+ extensions: KIND_EXTENSIONS["python"],
3548
+ root: RootWithFallback(createRootDetector([
3549
+ ".git",
3550
+ "pyproject.toml",
3551
+ "setup.py",
3552
+ "setup.cfg",
3553
+ "requirements.txt",
3554
+ "Pipfile",
3555
+ "poetry.lock"
3556
+ ])),
3557
+ async spawn(root, options) {
3558
+ const launched = await resolveAndLaunch({
3559
+ candidates: ["jedi-language-server"],
3560
+ args: [],
3561
+ cwd: root,
3562
+ managedToolId: "jedi-language-server"
3563
+ }, options?.allowInstall);
3564
+ if (!launched)
3565
+ return void 0;
3566
+ const pythonPath = await detectPythonVenv(root);
3567
+ return {
3568
+ ...launched,
3569
+ initialization: pythonPath ? { workspace: { environmentPath: pythonPath } } : {}
3570
+ };
3571
+ }
3572
+ };
3573
+ var GoServer = {
3574
+ id: "go",
3575
+ idleEviction: "unmeasured",
3576
+ role: "language",
3577
+ name: "gopls",
3578
+ extensions: KIND_EXTENSIONS["go"],
3579
+ root: RootWithFallback(WorkspacePriorityRoot([["go.work"], ["go.mod", "go.sum"], [".git"]])),
3580
+ async spawn(root, options) {
3581
+ const result = await resolveAndLaunch({
3582
+ // Canonical-bin discovery (#241): include $GOPATH/bin so a gopls that
3583
+ // `go install` dropped there resolves even when it isn't on PATH —
3584
+ // which is also the retry target after the runtimeInstall below.
3585
+ candidates: goBinCandidates("gopls"),
3586
+ args: [],
3587
+ cwd: root,
3588
+ runtimeInstall: {
3589
+ runtimeCommand: "go",
3590
+ install: tryGoInstallGopls
3591
+ }
3592
+ }, options?.allowInstall);
3593
+ if (!result)
3594
+ return void 0;
3595
+ return { ...result, initialization: { ui: { semanticTokens: true } } };
3596
+ }
3597
+ };
3598
+ function cargoWorkspaceDeclaresMember(workspaceContent, parentDir, childDir) {
3599
+ const relativePath = path7.relative(parentDir, childDir).split(path7.sep).join("/");
3600
+ if (relativePath === "" || relativePath.startsWith(".."))
3601
+ return false;
3602
+ const matches = (pattern) => matchesWorkspaceMemberPattern(pattern, relativePath, CARGO_WORKSPACE_MEMBER_DIALECT, () => recordDegradationOnce({
3603
+ kind: "workspace-glob-cap",
3604
+ subject: String(pattern.length),
3605
+ reason: "workspace glob matcher exceeded its memo-table cell cap"
3606
+ }));
3607
+ const excluded = readCargoWorkspaceExclude(workspaceContent);
3608
+ if (excluded.some(matches))
3609
+ return false;
3610
+ return readCargoWorkspaceMembers(workspaceContent).some(matches);
3611
+ }
3612
+ function sessionHoistCeiling(sessionCwd, file) {
3613
+ return isSameOrWithin(path7.resolve(sessionCwd), path7.resolve(file)) ? path7.resolve(sessionCwd) : void 0;
3614
+ }
3615
+ function RustWorkspaceRoot() {
3616
+ const crateRoot = createRootDetector(["Cargo.toml", "Cargo.lock"]);
3617
+ return withRootMarkers(async (file) => {
3618
+ const root = await crateRoot(file);
3619
+ if (!root)
3620
+ return void 0;
3621
+ const sessionCwd = process.cwd();
3622
+ const stop = sessionHoistCeiling(sessionCwd, file);
3623
+ let current = root;
3624
+ const fsRoot = path7.parse(current).root;
3625
+ while (true) {
3626
+ if (stop !== void 0 && current === stop)
3627
+ break;
3628
+ const parent = path7.dirname(current);
3629
+ if (parent === current || parent === fsRoot)
3630
+ break;
3631
+ const parentCargoPath = path7.join(parent, "Cargo.toml");
3632
+ const parentCargoContent = await readTextFileOrUndefined(parentCargoPath);
3633
+ if (parentCargoContent !== void 0 && hasCargoWorkspaceTable(parentCargoContent)) {
3634
+ if (cargoWorkspaceDeclaresMember(parentCargoContent, parent, root)) {
3635
+ return enforceLspRootCeiling(parent, sessionCwd, file);
3636
+ }
3637
+ break;
3638
+ }
3639
+ current = parent;
3640
+ }
3641
+ return root;
3642
+ }, crateRoot.rootMarkers ?? []);
3643
+ }
3644
+ function stripXmlComments(content) {
3645
+ let result = content;
3646
+ let previous;
3647
+ do {
3648
+ previous = result;
3649
+ result = result.replace(/<!--[\s\S]*?-->/g, "");
3650
+ } while (result !== previous);
3651
+ return result;
3652
+ }
3653
+ async function declaresMavenModule(parentDir, parentPomPath, childDir) {
3654
+ try {
3655
+ const content = stripXmlComments(await readFile(parentPomPath, "utf-8"));
3656
+ const modulesBlock = content.match(/<modules>([\s\S]*?)<\/modules>/);
3657
+ if (!modulesBlock)
3658
+ return false;
3659
+ const resolvedChild = path7.resolve(childDir);
3660
+ for (const match of modulesBlock[1].matchAll(/<module>\s*([^<]+?)\s*<\/module>/g)) {
3661
+ if (pathsEqual(path7.resolve(parentDir, match[1]), resolvedChild))
3662
+ return true;
3663
+ }
3664
+ return false;
3665
+ } catch {
3666
+ return false;
3667
+ }
3668
+ }
3669
+ function JavaWorkspaceRoot() {
3670
+ const moduleRoot = createRootDetector([
3671
+ "pom.xml",
3672
+ "build.gradle",
3673
+ ".classpath"
3674
+ ]);
3675
+ return withRootMarkers(async (file) => {
3676
+ const root = await moduleRoot(file);
3677
+ if (!root)
3678
+ return void 0;
3679
+ if (!await markerExists(root, "pom.xml"))
3680
+ return root;
3681
+ const sessionCwd = process.cwd();
3682
+ const stop = sessionHoistCeiling(sessionCwd, file);
3683
+ let current = root;
3684
+ let lastHop = root;
3685
+ const fsRoot = path7.parse(current).root;
3686
+ while (true) {
3687
+ if (stop !== void 0 && current === stop)
3688
+ break;
3689
+ const parent = path7.dirname(current);
3690
+ if (parent === current || parent === fsRoot)
3691
+ break;
3692
+ if (await markerExists(parent, "pom.xml")) {
3693
+ const parentPom = path7.join(parent, "pom.xml");
3694
+ if (!await declaresMavenModule(parent, parentPom, lastHop))
3695
+ break;
3696
+ current = parent;
3697
+ lastHop = parent;
3698
+ continue;
3699
+ }
3700
+ current = parent;
3701
+ }
3702
+ return enforceLspRootCeiling(lastHop, sessionCwd, file);
3703
+ }, moduleRoot.rootMarkers ?? []);
3704
+ }
3705
+ var RustServer = {
3706
+ id: "rust",
3707
+ idleEviction: "unmeasured",
3708
+ role: "language",
3709
+ // Measured (#3750): rust-analyzer answers an empty result for a detached file.
3710
+ requiresProjectRoot: true,
3711
+ name: "rust-analyzer",
3712
+ extensions: KIND_EXTENSIONS["rust"],
3713
+ // No FileDirRoot fallback (#201): rust-analyzer is a heavy workspace server
3714
+ // that is useless without a Cargo manifest. With the fallback, every .rs file
3715
+ // written before a Cargo.toml exists resolved to its OWN directory as the
3716
+ // root, and since clients dedup by `${serverId}:${root}`, each directory
3717
+ // spawned a separate rust-analyzer (one per file/dir during scaffolding).
3718
+ // Returning undefined here skips the spawn until a Cargo.toml gives a stable,
3719
+ // shared crate root — then all files share one server.
3720
+ root: RustWorkspaceRoot(),
3721
+ async spawn(root, options) {
3722
+ const result = await resolveAndLaunch({
3723
+ candidates: cargoBinCandidates("rust-analyzer"),
3724
+ args: [],
3725
+ cwd: root,
3726
+ managedToolId: "rust-analyzer"
3727
+ }, options?.allowInstall);
3728
+ if (!result)
3729
+ return void 0;
3730
+ return {
3731
+ ...result,
3732
+ initialization: {
3733
+ cargo: { buildScripts: { enable: true } },
3734
+ procMacro: { enable: true },
3735
+ diagnostics: { enable: true }
3736
+ }
3737
+ };
3738
+ }
3739
+ };
3740
+ var RubyServer = {
3741
+ id: "ruby",
3742
+ idleEviction: "unmeasured",
3743
+ role: "language",
3744
+ name: "Ruby LSP",
3745
+ extensions: KIND_EXTENSIONS["ruby"],
3746
+ root: RootWithFallback(PriorityRoot([["Gemfile", ".ruby-version"], [".git"]])),
3747
+ // Ruby LSP may need extra time to finish composed-bundle setup before it can
3748
+ // answer initialize/documentSymbol on cold start.
3749
+ initializeTimeoutMs: 3e4,
3750
+ clientWaitTimeoutMs: 3e4,
3751
+ async spawn(root, options) {
3752
+ const rubylsp = await resolveAndLaunch({
3753
+ candidates: ["ruby-lsp", ...rubyBinCandidates("ruby-lsp")],
3754
+ args: [],
3755
+ cwd: root,
3756
+ runtimeInstall: {
3757
+ runtimeCommand: "gem",
3758
+ install: () => tryGemInstall("ruby-lsp"),
3759
+ retryCandidates: ["ruby-lsp", ...rubyBinCandidates("ruby-lsp")]
3760
+ }
3761
+ }, options?.allowInstall);
3762
+ if (rubylsp)
3763
+ return rubylsp;
3764
+ const solargraph = await resolveAndLaunch({
3765
+ candidates: ["solargraph", ...rubyBinCandidates("solargraph")],
3766
+ args: ["stdio"],
3767
+ cwd: root
3768
+ }, false);
3769
+ if (solargraph)
3770
+ return solargraph;
3771
+ return resolveAndLaunch({
3772
+ candidates: ["rubocop", ...rubyBinCandidates("rubocop")],
3773
+ args: ["--lsp"],
3774
+ cwd: root
3775
+ }, false);
3776
+ }
3777
+ };
3778
+ var PHPServer = {
3779
+ id: "php",
3780
+ idleEviction: "transparent",
3781
+ role: "language",
3782
+ name: "Intelephense",
3783
+ extensions: KIND_EXTENSIONS["php"],
3784
+ root: RootWithFallback(createRootDetector(["composer.json", "composer.lock"])),
3785
+ async spawn(root, options) {
3786
+ const result = await resolveAndLaunch({
3787
+ candidates: nodeBinCandidates(root, "intelephense"),
3788
+ args: ["--stdio"],
3789
+ cwd: root,
3790
+ managedToolId: "intelephense"
3791
+ }, options?.allowInstall);
3792
+ if (!result)
3793
+ return void 0;
3794
+ return {
3795
+ ...result,
3796
+ initialization: {
3797
+ storagePath: path7.join(getGlobalPiLensDir(), "intelephense")
3798
+ }
3799
+ };
3800
+ }
3801
+ };
3802
+ function buildPsesArgs(bundleDir) {
3803
+ const script = path7.join(bundleDir, "PowerShellEditorServices", "Start-EditorServices.ps1");
3804
+ const sessionDir = path7.join(getGlobalPiLensDir(), "pses", `${process.pid}-${Date.now()}`);
3805
+ mkdirSync2(sessionDir, { recursive: true });
3806
+ const logPath = path7.join(sessionDir, "pses.log");
3807
+ const sessionDetailsPath = path7.join(sessionDir, "session.json");
3808
+ return [
3809
+ "-NoLogo",
3810
+ "-NoProfile",
3811
+ "-NonInteractive",
3812
+ // Unsigned bundled script + mark-of-the-web on Windows — Bypass so it runs;
3813
+ // ignored by non-Windows pwsh.
3814
+ "-ExecutionPolicy",
3815
+ "Bypass",
3816
+ "-File",
3817
+ script,
3818
+ "-HostName",
3819
+ "pi-lens",
3820
+ "-HostProfileId",
3821
+ "pi-lens",
3822
+ "-HostVersion",
3823
+ "1.0.0",
3824
+ "-BundledModulesPath",
3825
+ bundleDir,
3826
+ "-LogPath",
3827
+ logPath,
3828
+ "-LogLevel",
3829
+ "Warning",
3830
+ "-SessionDetailsPath",
3831
+ sessionDetailsPath,
3832
+ "-Stdio",
3833
+ "-LanguageServiceOnly"
3834
+ ];
3835
+ }
3836
+ var PowerShellServer = {
3837
+ id: "powershell",
3838
+ idleEviction: "unmeasured",
3839
+ role: "language",
3840
+ name: "PowerShell Editor Services",
3841
+ extensions: KIND_EXTENSIONS["powershell"],
3842
+ // Index at the workspace (script modules reference siblings); fall back to the
3843
+ // file dir.
3844
+ root: RootWithFallback(createRootDetector([".git"])),
3845
+ spawn(root, options) {
3846
+ return resolveAndLaunchBundle({
3847
+ runtimeCandidates: ["pwsh", "powershell"],
3848
+ bundleToolId: "powershell-editor-services",
3849
+ cwd: root,
3850
+ args: buildPsesArgs
3851
+ }, options?.allowInstall);
3852
+ }
3853
+ };
3854
+ var CSharpServer = {
3855
+ id: "csharp",
3856
+ idleEviction: "unmeasured",
3857
+ role: "language",
3858
+ // Documented (#3750): csharp-ls needs a solution or project.
3859
+ requiresProjectRoot: true,
3860
+ name: "csharp-ls",
3861
+ extensions: KIND_EXTENSIONS["csharp"],
3862
+ // No FileDirRoot fallback (#201): csharp-ls is a workspace server and should
3863
+ // not spawn once per source directory before a .sln/.csproj exists. Glob root
3864
+ // markers match real project filenames such as `App.csproj` / `App.sln`
3865
+ // (shared marker list — see file-kinds.ts, refs #895).
3866
+ root: createRootDetector([...DOTNET_CSHARP_ROOT_MARKERS]),
3867
+ async spawn(root, options) {
3868
+ const candidates = dotnetToolCandidates("csharp-ls");
3869
+ return resolveAndLaunch({
3870
+ candidates,
3871
+ args: [],
3872
+ cwd: root,
3873
+ runtimeInstall: {
3874
+ runtimeCommand: "dotnet",
3875
+ install: () => tryDotnetToolInstall("csharp-ls"),
3876
+ retryCandidates: candidates
3877
+ }
3878
+ }, options?.allowInstall);
3879
+ }
3880
+ };
3881
+ var OmniSharpServer = createInteractiveServer({
3882
+ id: "omnisharp",
3883
+ name: "OmniSharp",
3884
+ // Documented (#3750): OmniSharp needs a solution or project.
3885
+ requiresProjectRoot: true,
3886
+ fallbackFor: "csharp",
3887
+ extensions: KIND_EXTENSIONS["csharp"],
3888
+ root: createRootDetector([...DOTNET_CSHARP_ROOT_MARKERS]),
3889
+ language: "csharp",
3890
+ command: "OmniSharp",
3891
+ args: ["--languageserver"]
3892
+ });
3893
+ var FSharpServer = {
3894
+ id: "fsharp",
3895
+ idleEviction: "unmeasured",
3896
+ role: "language",
3897
+ // Documented (#3750): FSAutocomplete needs a project.
3898
+ requiresProjectRoot: true,
3899
+ name: "FSAutocomplete",
3900
+ extensions: KIND_EXTENSIONS["fsharp"],
3901
+ root: createRootDetector([...DOTNET_FSHARP_ROOT_MARKERS]),
3902
+ async spawn(root, options) {
3903
+ const candidates = dotnetToolCandidates("fsautocomplete");
3904
+ return resolveAndLaunch({
3905
+ candidates,
3906
+ args: [],
3907
+ cwd: root,
3908
+ runtimeInstall: {
3909
+ runtimeCommand: "dotnet",
3910
+ install: () => tryDotnetToolInstall("fsautocomplete"),
3911
+ retryCandidates: candidates
3912
+ }
3913
+ }, options?.allowInstall);
3914
+ }
3915
+ };
3916
+ var JavaServer = createInteractiveServer({
3917
+ id: "java",
3918
+ name: "JDT Language Server",
3919
+ extensions: KIND_EXTENSIONS["java"],
3920
+ root: RootWithFallback(JavaWorkspaceRoot()),
3921
+ language: "java",
3922
+ command: () => process.env.JDTLS_PATH || "jdtls",
3923
+ args: (root) => createLombokJdtlsArgs(root),
3924
+ runtime: "java"
3925
+ });
3926
+ var KotlinServer = {
3927
+ id: "kotlin",
3928
+ idleEviction: "unmeasured",
3929
+ role: "language",
3930
+ name: "Kotlin Language Server",
3931
+ extensions: KIND_EXTENSIONS["kotlin"],
3932
+ root: RootWithFallback(createRootDetector(["build.gradle.kts", "build.gradle", "pom.xml"])),
3933
+ async spawn(root, options) {
3934
+ return resolveAndLaunch({
3935
+ candidates: ["kotlin-lsp", "kotlin-language-server"],
3936
+ args: [],
3937
+ cwd: root,
3938
+ // #3400: a managed fwcd install when no PATH candidate launches. The
3939
+ // managed shim is consulted FIRST, but only exists once no PATH
3940
+ // candidate answered, so a PATH `kotlin-lsp` wins on a box that had
3941
+ // it before the managed install; one added afterwards does not.
3942
+ managedToolId: "kotlin-language-server"
3943
+ }, options?.allowInstall);
3944
+ }
3945
+ };
3946
+ var SwiftServer = createInteractiveServer({
3947
+ id: "swift",
3948
+ name: "SourceKit-LSP",
3949
+ extensions: KIND_EXTENSIONS["swift"],
3950
+ root: createRootDetector(["Package.swift"]),
3951
+ language: "swift",
3952
+ command: "sourcekit-lsp"
3953
+ });
3954
+ var DartServer = createInteractiveServer({
3955
+ id: "dart",
3956
+ name: "Dart Analysis Server",
3957
+ extensions: KIND_EXTENSIONS["dart"],
3958
+ root: RootWithFallback(createRootDetector(["pubspec.yaml"])),
3959
+ language: "dart",
3960
+ command: "dart",
3961
+ args: ["language-server", "--protocol=lsp"]
3962
+ });
3963
+ function createTreeBinaryServer(spec) {
3964
+ return {
3965
+ id: spec.id,
3966
+ name: spec.name,
3967
+ extensions: spec.extensions,
3968
+ idleEviction: spec.idleEviction ?? "unmeasured",
3969
+ // The spec declares no role, so the factory applies the stated default
3970
+ // (#1488) — see `createInteractiveServer`.
3971
+ role: DEFAULT_LSP_SERVER_ROLE,
3972
+ root: spec.root,
3973
+ spawn(root, options) {
3974
+ return resolveAndLaunchTreeBinary({
3975
+ candidates: [spec.binaryName],
3976
+ bundleToolId: spec.binaryName,
3977
+ binRelPath: spec.binRelPath,
3978
+ cwd: root,
3979
+ args: spec.args ?? []
3980
+ }, options?.allowInstall);
3981
+ }
3982
+ };
3983
+ }
3984
+ var LuaServer = createTreeBinaryServer({
3985
+ id: "lua",
3986
+ name: "Lua Language Server",
3987
+ extensions: KIND_EXTENSIONS["lua"],
3988
+ root: createRootDetector([".luarc.json", ".luacheckrc"]),
3989
+ binaryName: "lua-language-server",
3990
+ binRelPath: "bin/lua-language-server"
3991
+ });
3992
+ var CppServer = createTreeBinaryServer({
3993
+ id: "cpp",
3994
+ idleEviction: "transparent",
3995
+ name: "clangd",
3996
+ extensions: KIND_EXTENSIONS["cxx"],
3997
+ root: RootWithFallback(createRootDetector([
3998
+ "compile_commands.json",
3999
+ ".clangd",
4000
+ "CMakeLists.txt",
4001
+ "Makefile"
4002
+ ])),
4003
+ binaryName: "clangd",
4004
+ binRelPath: "bin/clangd",
4005
+ args: ["--background-index"]
4006
+ });
4007
+ var ZigServer = {
4008
+ id: "zig",
4009
+ idleEviction: "unmeasured",
4010
+ role: "language",
4011
+ name: "ZLS",
4012
+ extensions: KIND_EXTENSIONS["zig"],
4013
+ root: RootWithFallback(createRootDetector(["build.zig"])),
4014
+ spawn(root, options) {
4015
+ return resolveAndLaunch({
4016
+ candidates: ["zls"],
4017
+ args: [],
4018
+ cwd: root,
4019
+ managedToolId: "zls"
4020
+ }, options?.allowInstall);
4021
+ }
4022
+ };
4023
+ var HaskellServer = createInteractiveServer({
4024
+ id: "haskell",
4025
+ name: "Haskell Language Server",
4026
+ extensions: KIND_EXTENSIONS["haskell"],
4027
+ root: createRootDetector(["stack.yaml", "cabal.project", "*.cabal"]),
4028
+ language: "haskell",
4029
+ command: "haskell-language-server-wrapper",
4030
+ args: ["--lsp"]
4031
+ });
4032
+ var ElixirServer = createInteractiveServer({
4033
+ id: "elixir",
4034
+ name: "ElixirLS",
4035
+ extensions: KIND_EXTENSIONS["elixir"],
4036
+ root: RootWithFallback(createRootDetector(["mix.exs"])),
4037
+ language: "elixir",
4038
+ command: "elixir-ls"
4039
+ });
4040
+ var ElixirExpertServer = {
4041
+ id: "expert",
4042
+ idleEviction: "unmeasured",
4043
+ role: "language",
4044
+ name: "Expert",
4045
+ fallbackFor: "elixir",
4046
+ extensions: KIND_EXTENSIONS["elixir"],
4047
+ root: RootWithFallback(createRootDetector(["mix.exs"])),
4048
+ availabilityKey: "expert",
4049
+ async spawn(root, options) {
4050
+ return resolveAndLaunch({
4051
+ candidates: ["expert"],
4052
+ args: ["--stdio"],
4053
+ cwd: root,
4054
+ managedToolId: "expert"
4055
+ }, options?.allowInstall);
4056
+ },
4057
+ autoInstall: async () => Boolean(await ensureTool("expert"))
4058
+ };
4059
+ var GleamServer = {
4060
+ id: "gleam",
4061
+ idleEviction: "unmeasured",
4062
+ role: "language",
4063
+ name: "Gleam LSP",
4064
+ extensions: KIND_EXTENSIONS["gleam"],
4065
+ root: RootWithFallback(createRootDetector(["gleam.toml"])),
4066
+ async spawn(root, options) {
4067
+ return resolveAndLaunch({
4068
+ candidates: ["gleam"],
4069
+ args: ["lsp"],
4070
+ cwd: root,
4071
+ managedToolId: "gleam"
4072
+ }, options?.allowInstall);
4073
+ }
4074
+ };
4075
+ var TinymistServer = {
4076
+ id: "tinymist",
4077
+ idleEviction: "unmeasured",
4078
+ role: "language",
4079
+ name: "Tinymist",
4080
+ extensions: extensionsForLanguage("typst"),
4081
+ root: RootWithFallback(createRootDetector(["typst.toml", ".git"])),
4082
+ availabilityKey: "tinymist",
4083
+ async spawn(root, options) {
4084
+ return resolveAndLaunch({
4085
+ candidates: ["tinymist"],
4086
+ args: ["lsp"],
4087
+ cwd: root,
4088
+ managedToolId: "tinymist"
4089
+ }, options?.allowInstall);
4090
+ }
4091
+ };
4092
+ var MarksmanServer = {
4093
+ id: "marksman",
4094
+ idleEviction: "transparent",
4095
+ role: "language",
4096
+ name: "Marksman",
4097
+ extensions: KIND_EXTENSIONS["markdown"],
4098
+ // Index at the workspace root so cross-file checks (broken intra-repo links,
4099
+ // missing/renamed anchors, heading refs) see the whole tree; fall back to the
4100
+ // file's directory when there's no project marker.
4101
+ root: RootWithFallback(createRootDetector([".marksman.toml", ".git"])),
4102
+ spawn(root, options) {
4103
+ return resolveAndLaunch({
4104
+ candidates: ["marksman"],
4105
+ args: ["server"],
4106
+ cwd: root,
4107
+ managedToolId: "marksman"
4108
+ }, options?.allowInstall);
4109
+ }
4110
+ };
4111
+ var OCamlServer = createInteractiveServer({
4112
+ id: "ocaml",
4113
+ name: "ocamllsp",
4114
+ extensions: KIND_EXTENSIONS["ocaml"],
4115
+ root: createRootDetector(["dune-project", "opam"]),
4116
+ language: "ocaml",
4117
+ command: "ocamllsp"
4118
+ });
4119
+ var ClojureServer = {
4120
+ id: "clojure",
4121
+ idleEviction: "transparent",
4122
+ role: "language",
4123
+ name: "Clojure LSP",
4124
+ extensions: KIND_EXTENSIONS["clojure"],
4125
+ root: createRootDetector(["deps.edn", "project.clj"]),
4126
+ async spawn(root, options) {
4127
+ return resolveAndLaunch({
4128
+ candidates: ["clojure-lsp"],
4129
+ args: [],
4130
+ cwd: root,
4131
+ managedToolId: "clojure-lsp"
4132
+ }, options?.allowInstall);
4133
+ }
4134
+ };
4135
+ var CueServer = {
4136
+ id: "cue",
4137
+ idleEviction: "unmeasured",
4138
+ role: "language",
4139
+ name: "CUE Language Server",
4140
+ extensions: KIND_EXTENSIONS["cue"],
4141
+ root: RootWithFallback(createRootDetector(["cue.mod", ".git"])),
4142
+ async spawn(root, options) {
4143
+ return resolveAndLaunch({
4144
+ candidates: ["cue"],
4145
+ args: ["lsp", "serve"],
4146
+ cwd: root,
4147
+ managedToolId: "cue"
4148
+ }, options?.allowInstall);
4149
+ }
4150
+ };
4151
+ var TerraformServer = {
4152
+ id: "terraform",
4153
+ idleEviction: "unmeasured",
4154
+ role: "language",
4155
+ name: "Terraform LSP",
4156
+ extensions: KIND_EXTENSIONS["terraform"],
4157
+ root: RootWithFallback(createRootDetector([".terraform.lock.hcl", ".terraform"])),
4158
+ spawn(root, options) {
4159
+ return resolveAndLaunch({
4160
+ candidates: ["terraform-ls"],
4161
+ args: ["serve"],
4162
+ cwd: root,
4163
+ managedToolId: "terraform-ls"
4164
+ }, options?.allowInstall);
4165
+ }
4166
+ };
4167
+ var NixServer = createInteractiveServer({
4168
+ id: "nix",
4169
+ name: "nixd",
4170
+ extensions: KIND_EXTENSIONS["nix"],
4171
+ root: createRootDetector(["flake.nix"]),
4172
+ language: "nix",
4173
+ command: "nixd"
4174
+ });
4175
+ var BashServer = {
4176
+ id: "bash",
4177
+ idleEviction: "transparent",
4178
+ role: "language",
4179
+ name: "Bash Language Server",
4180
+ // #3968: narrowed from [".bash", ".sh", ".zsh"] — bash-language-server does
4181
+ // not support zsh (its own analyzer refuses the dialect), and its ≥5.7.0
4182
+ // lint flow even feeds shebang'd zsh to its embedded shellcheck, importing
4183
+ // SC1071-class noise through the LSP lane. `shuck` owns `.zsh` when
4184
+ // installed; when it is not, the shellcheck runner's dialect gate keeps
4185
+ // zsh quiet instead of falling back to a zsh-illiterate analyzer.
4186
+ extensions: [".bash", ".sh"],
4187
+ root: FileDirRoot,
4188
+ // #2194 bounded the installer's own verification at 20s; the dispatch
4189
+ // touch's client-wait floor stayed at the shared 5s default, so a cold
4190
+ // spawn could still race that budget and read as unavailable even after
4191
+ // installer verification cleared it. Match the installer bound (#2169).
4192
+ // `initializeTimeoutMs` matches it too: the measured cold start
4193
+ // (9,667ms, #2194) has headroom under the unraised 15s default, but
4194
+ // leaving the two fields mismatched is the same latent shape that made
4195
+ // the Prisma/Vue raise a no-op (fix-round F1, #2233) — a slower host
4196
+ // than the one measured would still hit the 15s inner kill despite the
4197
+ // caller being willing to wait 20s. Equalizing costs nothing on the
4198
+ // success path and removes the mismatch outright.
4199
+ clientWaitTimeoutMs: 2e4,
4200
+ initializeTimeoutMs: 2e4,
4201
+ spawn(root, options) {
4202
+ return resolveAndLaunch({
4203
+ candidates: nodeBinCandidates(root, "bash-language-server"),
4204
+ args: ["start"],
4205
+ cwd: root,
4206
+ managedToolId: "bash-language-server"
4207
+ }, options?.allowInstall);
4208
+ }
4209
+ };
4210
+ var ShuckServer = createInteractiveServer({
4211
+ id: "shuck",
4212
+ name: "Shuck",
4213
+ extensions: [".zsh"],
4214
+ root: FileDirRoot,
4215
+ language: "shell",
4216
+ command: "shuck",
4217
+ args: ["server"]
4218
+ });
4219
+ var FishServer = {
4220
+ id: "fish",
4221
+ idleEviction: "transparent",
4222
+ role: "language",
4223
+ name: "Fish Language Server",
4224
+ extensions: KIND_EXTENSIONS["fish"],
4225
+ root: RootWithFallback(createRootDetector([".git"])),
4226
+ spawn(root, options) {
4227
+ return resolveAndLaunch({
4228
+ candidates: nodeBinCandidates(root, "fish-lsp"),
4229
+ args: ["start", "--stdio"],
4230
+ cwd: root,
4231
+ managedToolId: "fish-lsp"
4232
+ }, options?.allowInstall);
4233
+ }
4234
+ };
4235
+ var CMakeServer = {
4236
+ id: "cmake",
4237
+ idleEviction: "unmeasured",
4238
+ role: "language",
4239
+ name: "CMake Language Server",
4240
+ // CMake's canonical project file has no .cmake suffix. The configured-server
4241
+ // matcher supports exact basenames as well as extensions.
4242
+ extensions: [...KIND_EXTENSIONS["cmake"], "CMakeLists.txt"],
4243
+ root: RootWithFallback(createRootDetector(["CMakeLists.txt", ".git"])),
4244
+ spawn(root, options) {
4245
+ return resolveAndLaunch({
4246
+ candidates: ["cmake-language-server"],
4247
+ args: [],
4248
+ cwd: root,
4249
+ managedToolId: "cmake-language-server"
4250
+ }, options?.allowInstall);
4251
+ }
4252
+ };
4253
+ var DockerServer = {
4254
+ id: "docker",
4255
+ idleEviction: "transparent",
4256
+ role: "language",
4257
+ name: "Dockerfile Language Server",
4258
+ extensions: [".dockerfile", "Dockerfile"],
4259
+ root: RootWithFallback(PriorityRoot([
4260
+ [
4261
+ "docker-compose.yml",
4262
+ "docker-compose.yaml",
4263
+ "compose.yml",
4264
+ "compose.yaml"
4265
+ ],
4266
+ [".git"]
4267
+ ])),
4268
+ spawn(root, options) {
4269
+ return resolveAndLaunch({
4270
+ candidates: nodeBinCandidates(root, "docker-langserver"),
4271
+ args: ["--stdio"],
4272
+ cwd: root,
4273
+ managedToolId: "dockerfile-language-server-nodejs"
4274
+ }, options?.allowInstall);
4275
+ }
4276
+ };
4277
+ var DockerOfficialServer = {
4278
+ id: "docker-official",
4279
+ idleEviction: "unmeasured",
4280
+ role: "language",
4281
+ name: "Docker Language Server (official)",
4282
+ fallbackFor: "docker",
4283
+ extensions: [".dockerfile", "Dockerfile"],
4284
+ root: RootWithFallback(PriorityRoot([
4285
+ [
4286
+ "docker-compose.yml",
4287
+ "docker-compose.yaml",
4288
+ "compose.yml",
4289
+ "compose.yaml"
4290
+ ],
4291
+ [".git"]
4292
+ ])),
4293
+ spawn(root, options) {
4294
+ return resolveAndLaunch({
4295
+ candidates: nodeBinCandidates(root, "docker-language-server"),
4296
+ args: ["start", "--stdio"],
4297
+ cwd: root
4298
+ }, options?.allowInstall);
4299
+ }
4300
+ };
4301
+ var YamlServer = {
4302
+ id: "yaml",
4303
+ idleEviction: "transparent",
4304
+ role: "language",
4305
+ name: "YAML Language Server",
4306
+ extensions: KIND_EXTENSIONS["yaml"],
4307
+ root: RootWithFallback(PriorityRoot([
4308
+ [".yamllint", "yamllint.yml", "yamllint.yaml", "pyproject.toml"],
4309
+ [".git"]
4310
+ ])),
4311
+ spawn(root, options) {
4312
+ return resolveAndLaunch({
4313
+ candidates: nodeBinCandidates(root, "yaml-language-server"),
4314
+ args: ["--stdio"],
4315
+ cwd: root,
4316
+ managedToolId: "yaml-language-server"
4317
+ }, options?.allowInstall);
4318
+ }
4319
+ };
4320
+ var JsonServer = {
4321
+ id: "json",
4322
+ idleEviction: "transparent",
4323
+ role: "language",
4324
+ name: "VSCode JSON Language Server",
4325
+ extensions: KIND_EXTENSIONS["json"],
4326
+ root: RootWithFallback(WorkspacePriorityRoot([
4327
+ ["package.json", "tsconfig.json", "jsconfig.json"],
4328
+ [".git"]
4329
+ ])),
4330
+ // See BashServer above: the installer's 20s verification bound (#2194)
4331
+ // does not, on its own, raise the dispatch touch's cold-spawn wait floor.
4332
+ // Mirror it here so a cold spawn cannot lose that race either (#2169).
4333
+ // `initializeTimeoutMs` matches it for the same reason as BashServer
4334
+ // above (fix-round F1, #2233).
4335
+ clientWaitTimeoutMs: 2e4,
4336
+ initializeTimeoutMs: 2e4,
4337
+ spawn(root, options) {
4338
+ return resolveAndLaunch({
4339
+ candidates: ["vscode-json-language-server"],
4340
+ args: ["--stdio"],
4341
+ cwd: root,
4342
+ managedToolId: "vscode-json-language-server"
4343
+ }, options?.allowInstall);
4344
+ }
4345
+ };
4346
+ var HtmlServer = {
4347
+ id: "html",
4348
+ idleEviction: "transparent",
4349
+ role: "language",
4350
+ name: "VSCode HTML Language Server",
4351
+ extensions: KIND_EXTENSIONS["html"],
4352
+ root: RootWithFallback(IgnoreHomeRoot(PriorityRoot([["package.json", "index.html", "vite.config.ts"]]))),
4353
+ spawn(root, options) {
4354
+ return resolveAndLaunch({
4355
+ candidates: nodeBinCandidates(root, "vscode-html-language-server"),
4356
+ args: ["--stdio"],
4357
+ cwd: root,
4358
+ managedToolId: "vscode-html-languageserver-bin"
4359
+ }, options?.allowInstall);
4360
+ }
4361
+ };
4362
+ var TomlServer = {
4363
+ id: "toml",
4364
+ idleEviction: "unmeasured",
4365
+ role: "language",
4366
+ name: "Taplo",
4367
+ extensions: KIND_EXTENSIONS["toml"],
4368
+ root: RootWithFallback(PriorityRoot([["pyproject.toml", "Cargo.toml", "taplo.toml"], [".git"]])),
4369
+ spawn(root, options) {
4370
+ return resolveAndLaunch({
4371
+ candidates: ["taplo"],
4372
+ args: ["lsp", "stdio"],
4373
+ cwd: root,
4374
+ managedToolId: "taplo"
4375
+ }, options?.allowInstall);
4376
+ }
4377
+ };
4378
+ var PrismaServer = {
4379
+ id: "prisma",
4380
+ idleEviction: "transparent",
4381
+ role: "language",
4382
+ name: "Prisma Language Server",
4383
+ extensions: KIND_EXTENSIONS["prisma"],
4384
+ root: RootWithFallback(createRootDetector(["prisma/schema.prisma", "schema.prisma"])),
4385
+ // Matches the installer's 40s verification bound above (#2169): the
4386
+ // dispatch touch's cold-spawn wait floor defaults to 5s and would
4387
+ // otherwise time the client out well before the binary answers.
4388
+ // `initializeTimeoutMs` MUST be set alongside it (RubyServer's own
4389
+ // precedent below): unset, it falls back to the 15s
4390
+ // `INITIALIZE_TIMEOUT_MS` default in `clients/lsp/client.ts`, which
4391
+ // hard-kills the child mid-handshake — a real cold Prisma run measured
4392
+ // up to 27.3s, so the spawn would die there before this wait ever gets
4393
+ // a chance to matter (fix-round F1, #2233).
4394
+ clientWaitTimeoutMs: 4e4,
4395
+ initializeTimeoutMs: 4e4,
4396
+ spawn(root, options) {
4397
+ return resolveAndLaunch({
4398
+ candidates: nodeBinCandidates(root, "prisma-language-server"),
4399
+ args: ["--stdio"],
4400
+ cwd: root,
4401
+ managedToolId: "@prisma/language-server"
4402
+ }, options?.allowInstall);
4403
+ }
4404
+ };
4405
+ var VueServer = {
4406
+ id: "vue",
4407
+ idleEviction: "unmeasured",
4408
+ role: "language",
4409
+ name: "Vue Language Server",
4410
+ extensions: [".vue"],
4411
+ root: RootWithFallback(IgnoreHomeRoot(createRootDetector([
4412
+ "package.json",
4413
+ "package-lock.json",
4414
+ "bun.lockb",
4415
+ "bun.lock",
4416
+ "pnpm-lock.yaml",
4417
+ "yarn.lock"
4418
+ ]))),
4419
+ // Vue's launcher loads the full language-service bundle before answering,
4420
+ // matching the installer's 30s verification bound (#2176). The dispatch
4421
+ // touch's cold-spawn wait floor defaults to 5s and needs the same raise so
4422
+ // a cold spawn cannot time out there instead. `initializeTimeoutMs` MUST
4423
+ // match: unset, it falls back to the 15s default in `clients/lsp/client.ts`
4424
+ // and hard-kills a cold spawn (measured up to 22.6s in #2188, 16.2s here)
4425
+ // mid-handshake before this wait can matter (fix-round F1, #2233).
4426
+ clientWaitTimeoutMs: 3e4,
4427
+ initializeTimeoutMs: 3e4,
4428
+ async spawn(root, options) {
4429
+ const tsserverPath = await findTsserverPath(root, options?.allowInstall);
4430
+ const hasPackageJson = existsSync3(path7.join(root, "package.json"));
4431
+ const hasNodeModules = existsSync3(path7.join(root, "node_modules"));
4432
+ if (hasPackageJson && !hasNodeModules) {
4433
+ logSessionStart(`lsp vue: node_modules missing in ${root} \u2014 Vue navigation may be limited. Run: npm install (or pnpm/yarn install) in this project.`);
4434
+ }
4435
+ const proc = await resolveAndLaunch({
4436
+ candidates: nodeBinCandidates(root, "vue-language-server"),
4437
+ args: ["--stdio"],
4438
+ cwd: root,
4439
+ managedToolId: "@vue/language-server"
4440
+ }, options?.allowInstall);
4441
+ if (!proc)
4442
+ return void 0;
4443
+ return {
4444
+ process: proc.process,
4445
+ source: proc.source,
4446
+ initialization: tsserverPath ? { typescript: { tsdk: path7.dirname(tsserverPath) } } : void 0
4447
+ };
4448
+ }
4449
+ };
4450
+ var SvelteServer = {
4451
+ id: "svelte",
4452
+ idleEviction: "unmeasured",
4453
+ role: "language",
4454
+ name: "Svelte Language Server",
4455
+ extensions: [".svelte"],
4456
+ root: RootWithFallback(IgnoreHomeRoot(createRootDetector([
4457
+ "package.json",
4458
+ "package-lock.json",
4459
+ "bun.lockb",
4460
+ "bun.lock",
4461
+ "pnpm-lock.yaml",
4462
+ "yarn.lock"
4463
+ ]))),
4464
+ // Matches the installer's 20s verification bound above (#2169): the
4465
+ // dispatch touch's cold-spawn wait floor defaults to 5s and would
4466
+ // otherwise time the client out well before the binary answers.
4467
+ // `initializeTimeoutMs` matches it for the same reason as BashServer
4468
+ // above (fix-round F1, #2233).
4469
+ clientWaitTimeoutMs: 2e4,
4470
+ initializeTimeoutMs: 2e4,
4471
+ async spawn(root, options) {
4472
+ const tsserverPath = await findTsserverPath(root, options?.allowInstall);
4473
+ const proc = await resolveAndLaunch({
4474
+ candidates: [
4475
+ ...nodeBinCandidates(root, "svelteserver"),
4476
+ ...nodeBinCandidates(root, "svelte-language-server")
4477
+ ],
4478
+ args: ["--stdio"],
4479
+ cwd: root,
4480
+ managedToolId: "svelte-language-server"
4481
+ }, options?.allowInstall);
4482
+ if (!proc)
4483
+ return void 0;
4484
+ return {
4485
+ process: proc.process,
4486
+ source: proc.source,
4487
+ initialization: tsserverPath ? { typescript: { tsdk: path7.dirname(tsserverPath) } } : void 0
4488
+ };
4489
+ }
4490
+ };
4491
+ var CssServer = {
4492
+ id: "css",
4493
+ idleEviction: "transparent",
4494
+ role: "language",
4495
+ name: "CSS Language Server",
4496
+ extensions: KIND_EXTENSIONS["css"],
4497
+ root: RootWithFallback(IgnoreHomeRoot(PriorityRoot([
4498
+ [
4499
+ "package.json",
4500
+ "postcss.config.js",
4501
+ "tailwind.config.js",
4502
+ "vite.config.ts"
4503
+ ]
4504
+ ]))),
4505
+ spawn(root, options) {
4506
+ return resolveAndLaunch({
4507
+ candidates: nodeBinCandidates(root, "vscode-css-language-server"),
4508
+ args: ["--stdio"],
4509
+ cwd: root,
4510
+ managedToolId: "vscode-css-languageserver"
4511
+ }, options?.allowInstall);
4512
+ }
4513
+ };
4514
+ var OPENGREP_KINDS = [
4515
+ "csharp",
4516
+ "css",
4517
+ "cxx",
4518
+ "dart",
4519
+ "docker",
4520
+ "go",
4521
+ "html",
4522
+ "java",
4523
+ "json",
4524
+ "jsts",
4525
+ "kotlin",
4526
+ "lua",
4527
+ "php",
4528
+ "python",
4529
+ "ruby",
4530
+ "rust",
4531
+ "shell",
4532
+ "swift",
4533
+ "terraform",
4534
+ "yaml"
4535
+ ];
4536
+ var OPENGREP_EXTENSIONS = Array.from(new Set(OPENGREP_KINDS.flatMap((k) => KIND_EXTENSIONS[k] ?? [])));
4537
+ function opengrepInitialization(root) {
4538
+ const resolved = resolveOpengrepConfig(root, { enabled: true });
4539
+ return {
4540
+ scan: {
4541
+ configuration: [resolved.configArg ?? "auto"],
4542
+ onlyGitDirty: false,
4543
+ jobs: 16
4544
+ },
4545
+ metrics: { enabled: false },
4546
+ doHover: false
4547
+ };
4548
+ }
4549
+ var OpengrepServer = {
4550
+ id: "opengrep",
4551
+ idleEviction: "transparent",
4552
+ role: "auxiliary",
4553
+ name: "Opengrep Security Scanner",
4554
+ extensions: OPENGREP_EXTENSIONS,
4555
+ // Stable per-repo root so ONE warm server serves the whole project (a
4556
+ // per-directory root would spawn a fresh server — and re-pay rule load —
4557
+ // for every folder).
4558
+ root: RootWithFallback(NearestRoot([".git"]), async () => process.cwd()),
4559
+ availabilityKey: "opengrep",
4560
+ // Rule compilation can take a few seconds on the first scan of a session.
4561
+ initializeTimeoutMs: 15e3,
4562
+ async spawn(root, options) {
4563
+ const launched = await resolveAndLaunch({
4564
+ candidates: ["opengrep"],
4565
+ args: ["lsp", "--experimental"],
4566
+ cwd: root,
4567
+ managedToolId: "opengrep"
4568
+ }, options?.allowInstall);
4569
+ if (!launched)
4570
+ return void 0;
4571
+ return { ...launched, initialization: opengrepInitialization(root) };
4572
+ },
4573
+ autoInstall: async () => Boolean(await ensureTool("opengrep"))
4574
+ };
4575
+ var AST_GREP_KINDS = [
4576
+ "csharp",
4577
+ "cxx",
4578
+ "css",
4579
+ "elixir",
4580
+ "go",
4581
+ "haskell",
4582
+ "html",
4583
+ "java",
4584
+ "json",
4585
+ "jsts",
4586
+ "kotlin",
4587
+ "lua",
4588
+ "nix",
4589
+ "php",
4590
+ "python",
4591
+ "ruby",
4592
+ "rust",
4593
+ "scala",
4594
+ "shell",
4595
+ "swift",
4596
+ "yaml"
4597
+ ];
4598
+ var AST_GREP_EXTENSIONS = Array.from(new Set(AST_GREP_KINDS.flatMap((k) => KIND_EXTENSIONS[k] ?? [])));
4599
+ var AstGrepServer = {
4600
+ id: "ast-grep",
4601
+ idleEviction: "unmeasured",
4602
+ role: "auxiliary",
4603
+ name: "ast-grep structural linter",
4604
+ extensions: AST_GREP_EXTENSIONS,
4605
+ // Attaches everywhere (#239 Phase 2): prefer a project `sgconfig.y[a]ml` root,
4606
+ // else the repo root (.git) or cwd — like Opengrep. When there's no project
4607
+ // sgconfig the spawn launches with `--config <shipped baseline>` so the team's
4608
+ // rules still run; the napi runner steps aside when this server is available
4609
+ // (it falls back to napi when the ast-grep binary is absent — Gate B).
4610
+ root: RootWithFallback(createRootDetector(["sgconfig.yml", "sgconfig.yaml"]), RootWithFallback(NearestRoot([".git"]), async () => process.cwd())),
4611
+ availabilityKey: "ast-grep",
4612
+ // #1714: the one auxiliary with a measured wedge ceiling. A `lens_diagnostics
4613
+ // mode=full` sweep of 225 files drove this server into an unrecoverable stall
4614
+ // twice in two exposures (writes outstanding 5.9 s and 9.4 s, then a forced
4615
+ // shutdown that timed out both the request and the exit notify). It re-parses
4616
+ // the whole file on every didOpen, so it absorbs a sweep more slowly than the
4617
+ // other scanners — hold it to half the shared default.
4618
+ notifyInflightLimit: 4,
4619
+ // #1722, measured against the real binary over 30 repository files rather
4620
+ // than assumed: ast-grep-lsp is tower-lsp-server and drains its message
4621
+ // stream in order on one task, so the notify barrier's round-trip proves
4622
+ // the backlog was scanned. Declared here because the barrier's soundness
4623
+ // rests on it (#1756 stage 1); the evidence table is on
4624
+ // `LspReplyOrdering`.
4625
+ replyOrdering: "single-task-ordered",
4626
+ // First scan of a session compiles the rules.
4627
+ initializeTimeoutMs: 15e3,
4628
+ async spawn(root, options) {
4629
+ const projectSgconfig = findLocalSgconfig(root);
4630
+ let args = ["lsp"];
4631
+ if (!projectSgconfig) {
4632
+ const baseline = resolveBaselineSgconfig(root);
4633
+ if (baseline)
4634
+ args = ["lsp", "--config", baseline];
4635
+ }
4636
+ const nativeExe = resolveAstGrepNativeExe();
4637
+ const candidates = nativeExe ? [nativeExe, "ast-grep"] : ["ast-grep"];
4638
+ return resolveAndLaunch({
4639
+ candidates,
4640
+ args,
4641
+ cwd: root,
4642
+ managedToolId: "ast-grep"
4643
+ }, options?.allowInstall);
4644
+ },
4645
+ autoInstall: async () => Boolean(await ensureTool("ast-grep"))
4646
+ };
4647
+ var ZIZMOR_EXTENSIONS = KIND_EXTENSIONS["yaml"];
4648
+ var ZizmorServer = {
4649
+ id: "zizmor",
4650
+ idleEviction: "unmeasured",
4651
+ role: "auxiliary",
4652
+ name: "zizmor Actions Security Scanner",
4653
+ extensions: ZIZMOR_EXTENSIONS,
4654
+ pathFilter: isZizmorAuditTarget,
4655
+ // Stable per-repo root so ONE warm server serves the whole project (like
4656
+ // Opengrep) — config + workflow discovery is repo-relative.
4657
+ root: RootWithFallback(NearestRoot([".git"]), async () => process.cwd()),
4658
+ availabilityKey: "zizmor",
4659
+ async spawn(root, options) {
4660
+ const ghToken = await resolveZizmorGitHubToken();
4661
+ return resolveAndLaunch({
4662
+ candidates: ["zizmor"],
4663
+ args: ["--lsp"],
4664
+ cwd: root,
4665
+ managedToolId: "zizmor",
4666
+ ...ghToken ? { env: { GH_TOKEN: ghToken } } : {}
4667
+ }, options?.allowInstall);
4668
+ },
4669
+ autoInstall: async () => Boolean(await ensureTool("zizmor"))
4670
+ };
4671
+ var TYPOS_EXTENSIONS = Array.from(/* @__PURE__ */ new Set([...OPENGREP_EXTENSIONS, ...KIND_EXTENSIONS["markdown"]]));
4672
+ function typosInitialization(root) {
4673
+ const localConfig = findLocalTyposConfig(root);
4674
+ if (localConfig) {
4675
+ logLatency({
4676
+ type: "phase",
4677
+ phase: "typos_config_resolved",
4678
+ filePath: root,
4679
+ durationMs: 0,
4680
+ metadata: { mode: "project_config", configPath: localConfig }
4681
+ });
4682
+ logSessionStart(`typos config resolved mode=project_config configPath=${localConfig}`);
4683
+ return void 0;
4684
+ }
4685
+ const configPath = resolvePackagePath(import.meta.url, "rules", "typos", "_typos.toml");
4686
+ logLatency({
4687
+ type: "phase",
4688
+ phase: "typos_config_resolved",
4689
+ filePath: root,
4690
+ durationMs: 0,
4691
+ metadata: { mode: "injected_default", configPath }
4692
+ });
4693
+ logSessionStart(`typos config resolved mode=injected_default configPath=${configPath}`);
4694
+ return { config: configPath };
4695
+ }
4696
+ var TyposServer = {
4697
+ id: "typos",
4698
+ idleEviction: "unmeasured",
4699
+ role: "auxiliary",
4700
+ name: "typos Spell Checker",
4701
+ extensions: TYPOS_EXTENSIONS,
4702
+ // Stable per-repo root so ONE warm server serves the whole project (like the
4703
+ // other auxiliaries) — typos.toml discovery is repo-relative.
4704
+ root: RootWithFallback(NearestRoot([".git"]), async () => process.cwd()),
4705
+ availabilityKey: "typos-lsp",
4706
+ async spawn(root, options) {
4707
+ const launched = await resolveAndLaunch({
4708
+ candidates: ["typos-lsp"],
4709
+ args: [],
4710
+ cwd: root,
4711
+ managedToolId: "typos-lsp"
4712
+ }, options?.allowInstall);
4713
+ if (!launched)
4714
+ return void 0;
4715
+ const initialization = typosInitialization(root);
4716
+ return initialization ? { ...launched, initialization } : launched;
4717
+ },
4718
+ autoInstall: async () => Boolean(await ensureTool("typos-lsp"))
4719
+ };
4720
+ var LSP_SERVERS = [
4721
+ TypeScriptServer,
4722
+ DenoServer,
4723
+ PythonServer,
4724
+ // pyright / basedpyright — preferred; openFilesOnly avoids cold-start; ty (#717) is a local-only opt-in fallback
4725
+ PythonJediServer,
4726
+ // fallback when neither pyright nor basedpyright is available
4727
+ GoServer,
4728
+ RustServer,
4729
+ RubyServer,
4730
+ PHPServer,
4731
+ PowerShellServer,
4732
+ // PowerShell Editor Services — pwsh-bootstrapped module bundle (#278)
4733
+ CSharpServer,
4734
+ OmniSharpServer,
4735
+ FSharpServer,
4736
+ JavaServer,
4737
+ KotlinServer,
4738
+ SwiftServer,
4739
+ DartServer,
4740
+ LuaServer,
4741
+ CppServer,
4742
+ ZigServer,
4743
+ HaskellServer,
4744
+ ElixirServer,
4745
+ ElixirExpertServer,
4746
+ GleamServer,
4747
+ TinymistServer,
4748
+ MarksmanServer,
4749
+ OCamlServer,
4750
+ ClojureServer,
4751
+ CueServer,
4752
+ TerraformServer,
4753
+ NixServer,
4754
+ BashServer,
4755
+ ShuckServer,
4756
+ FishServer,
4757
+ CMakeServer,
4758
+ DockerServer,
4759
+ DockerOfficialServer,
4760
+ // alternate of docker; acquired only when docker declines (#3939)
4761
+ YamlServer,
4762
+ JsonServer,
4763
+ HtmlServer,
4764
+ TomlServer,
4765
+ PrismaServer,
4766
+ // Web frameworks & styling
4767
+ VueServer,
4768
+ SvelteServer,
4769
+ CssServer,
4770
+ // Auxiliary (cross-cutting, diagnostic-only) servers go last — never primary.
4771
+ OpengrepServer,
4772
+ AstGrepServer,
4773
+ ZizmorServer,
4774
+ TyposServer
4775
+ ];
4776
+ function getServerById(id) {
4777
+ return LSP_SERVERS.find((server) => server.id === id);
4778
+ }
4779
+
4780
+ export {
4781
+ findLocalTyposConfig,
4782
+ getAstGrepRuleSources,
4783
+ getAstGrepRuleFingerprint,
4784
+ isAuxiliary,
4785
+ serverTraits,
4786
+ registerRunnerId,
4787
+ BUILTIN_SERVER_RUNNER_COVERS,
4788
+ describeRootFallback,
4789
+ resolveLspServerCwd,
4790
+ resetLSPCaseSensitivityState,
4791
+ isSameOrWithin,
4792
+ enforceLspRootCeiling,
4793
+ hasProjectBoundaryMarker,
4794
+ resetLspLaunchAvailabilityGeneration,
4795
+ resetDirectLspCommandAvailability,
4796
+ isDirectLspCommandTemporarilyUnavailable,
4797
+ resetClassicTsRepairGuard,
4798
+ LSP_SERVERS,
4799
+ getServerById,
4800
+ shouldInitializeSessionRoot,
4801
+ isOutsideAllSessionRoots,
4802
+ getSessionRootsForTelemetry,
4803
+ loadLSPConfig,
4804
+ customServerSpecsOf,
4805
+ lspConfigOf,
4806
+ registerLSPConfig,
4807
+ initLSPConfig,
4808
+ explainServersForFile,
4809
+ getServersForFileWithConfig,
4810
+ primaryServerId,
4811
+ resolveLspCwdForFile,
4812
+ getServerInitOverride,
4813
+ getStrategy,
4814
+ classifyServerWaitTier,
4815
+ classifyCascadeWaitTier
4816
+ };