pybao-cli 1.3.87 → 1.3.89

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 (148) hide show
  1. package/dist/REPL-6C4OPI2C.js +47 -0
  2. package/dist/{acp-NMISJFTQ.js → acp-RGMIE4DU.js} +29 -29
  3. package/dist/{agentsValidate-G7H7CUZU.js → agentsValidate-MIP3HSNM.js} +7 -7
  4. package/dist/{ask-E7XSHQAN.js → ask-WWDKFT4S.js} +28 -28
  5. package/dist/{autoUpdater-EAH2H7AP.js → autoUpdater-HNFOZCBL.js} +3 -3
  6. package/dist/{chunk-N6ETO4PE.js → chunk-3DJZ2FP3.js} +4 -4
  7. package/dist/{chunk-BHBHS3MY.js → chunk-4IUTKGZ3.js} +3 -3
  8. package/dist/{chunk-6XDESUHK.js → chunk-52XBAAOD.js} +1 -1
  9. package/dist/{chunk-L62OCVZH.js → chunk-5P7OEKKE.js} +16 -16
  10. package/dist/{chunk-BBZ2VZ5J.js → chunk-5VYUNKPN.js} +3 -3
  11. package/dist/{chunk-VAR6IG72.js → chunk-7ZRJIA2T.js} +1 -1
  12. package/dist/{chunk-WCX3ELKY.js → chunk-AWJL67ZK.js} +89 -40
  13. package/dist/{chunk-WCX3ELKY.js.map → chunk-AWJL67ZK.js.map} +2 -2
  14. package/dist/{chunk-3RXA6IWG.js → chunk-BBZOZE5U.js} +1 -1
  15. package/dist/{chunk-BPN5QY2K.js → chunk-CU7LUROX.js} +2 -2
  16. package/dist/{chunk-GEHCREEY.js → chunk-CUICIV5I.js} +3 -3
  17. package/dist/{chunk-KEALJIDY.js → chunk-HENGZ5QD.js} +208 -16
  18. package/dist/chunk-HENGZ5QD.js.map +7 -0
  19. package/dist/{chunk-HWIU6J2T.js → chunk-K427K545.js} +1035 -87
  20. package/dist/chunk-K427K545.js.map +7 -0
  21. package/dist/{chunk-T2HQBH46.js → chunk-L7H4HULV.js} +3 -3
  22. package/dist/{chunk-7IKX4543.js → chunk-LHVJ4DCY.js} +2 -2
  23. package/dist/{chunk-KUIDN7FP.js → chunk-LNJEAGUM.js} +1 -1
  24. package/dist/{chunk-UF2ARFEB.js → chunk-N32NFSDQ.js} +2 -2
  25. package/dist/{chunk-XD5KXOSG.js → chunk-NE7ZMP3U.js} +2 -2
  26. package/dist/{chunk-SBF7BOS4.js → chunk-NSNUA3AI.js} +3 -3
  27. package/dist/{chunk-Q7W7PGSO.js → chunk-P4BU7OLK.js} +1 -1
  28. package/dist/{chunk-MRU4VN5R.js → chunk-PC4T3EHY.js} +2 -2
  29. package/dist/{chunk-Q32HEIH2.js → chunk-QNTNP3TF.js} +4 -4
  30. package/dist/{chunk-4GB3OWJY.js → chunk-QQP7RZHT.js} +3 -3
  31. package/dist/{chunk-VBJP55XA.js → chunk-RHMAO6OV.js} +1 -1
  32. package/dist/{chunk-H6NBGOQG.js → chunk-RVS72LGR.js} +1 -1
  33. package/dist/{chunk-H6NBGOQG.js.map → chunk-RVS72LGR.js.map} +1 -1
  34. package/dist/{chunk-LSJILN2S.js → chunk-RWT3O44A.js} +2 -2
  35. package/dist/{chunk-KTLUYKAC.js → chunk-SG7SPYM4.js} +1 -1
  36. package/dist/{chunk-EQMKAITH.js → chunk-SNRPFDBW.js} +1 -1
  37. package/dist/{chunk-HVMJPWC6.js → chunk-XKJGQBIN.js} +3 -3
  38. package/dist/{chunk-HHTFRFKN.js → chunk-YI4WTP42.js} +2 -2
  39. package/dist/{chunk-F47KO7J2.js → chunk-ZMKBSLLD.js} +1 -1
  40. package/dist/{cli-VTNXK3N3.js → cli-DBIRYO2K.js} +87 -87
  41. package/dist/commands-CYQHNNSX.js +51 -0
  42. package/dist/{config-FBT5ABGI.js → config-WF57SRJH.js} +4 -4
  43. package/dist/{context-WBZK3AZX.js → context-KZT5TVOO.js} +5 -5
  44. package/dist/{customCommands-IFWVN654.js → customCommands-RROXEXFI.js} +4 -4
  45. package/dist/{env-5O5DDVID.js → env-V3EUI52X.js} +2 -2
  46. package/dist/{file-K3P2ZXOY.js → file-EY5S5X2I.js} +4 -4
  47. package/dist/index.js +3 -3
  48. package/dist/{llm-XZHOT5QG.js → llm-HRDLIEIJ.js} +29 -29
  49. package/dist/{llmLazy-VVQLL2O4.js → llmLazy-5JLFDV6C.js} +1 -1
  50. package/dist/{loader-JC5XSGNC.js → loader-F2SDAB3W.js} +4 -4
  51. package/dist/lsp-IEUZLTEJ.js +33 -0
  52. package/dist/{lspAnchor-3AQPL2ZZ.js → lspAnchor-UT54IBRE.js} +6 -6
  53. package/dist/{mcp-DTKD53FS.js → mcp-MJTCLGYY.js} +7 -7
  54. package/dist/{mentionProcessor-JS7EY2LD.js → mentionProcessor-H7F7SZ5E.js} +5 -5
  55. package/dist/{messages-GQMW2ANU.js → messages-5PHNMZRG.js} +1 -1
  56. package/dist/{model-VGKT6DJE.js → model-L7BXTDSH.js} +5 -5
  57. package/dist/{openai-TSJZC7MB.js → openai-5VV7N26J.js} +5 -5
  58. package/dist/{outputStyles-5VLEN2UN.js → outputStyles-GTAHP23K.js} +4 -4
  59. package/dist/{pluginRuntime-XKPZ3BX4.js → pluginRuntime-23TPWKF3.js} +6 -6
  60. package/dist/{pluginValidation-ZHCDH3MZ.js → pluginValidation-6VPGAZPM.js} +6 -6
  61. package/dist/prompts-D3KBVMOD.js +53 -0
  62. package/dist/{pybAgentSessionLoad-5CPNQIXV.js → pybAgentSessionLoad-H5RDHP7Z.js} +4 -4
  63. package/dist/{pybAgentSessionResume-VCYFMJCA.js → pybAgentSessionResume-Z735IT3K.js} +4 -4
  64. package/dist/{pybAgentStreamJsonSession-DOTUU74W.js → pybAgentStreamJsonSession-YI2CEMWO.js} +1 -1
  65. package/dist/{pybHooks-W2EX2SUW.js → pybHooks-5MA4UT4U.js} +4 -4
  66. package/dist/query-NJIFF75Q.js +55 -0
  67. package/dist/{registry-OUOC3F5A.js → registry-G36Y7TJR.js} +11 -5
  68. package/dist/{ripgrep-MEONNP4W.js → ripgrep-BYLSY4AL.js} +3 -3
  69. package/dist/{skillMarketplace-34N7IKSR.js → skillMarketplace-XN5XMW6F.js} +3 -3
  70. package/dist/{state-6NDQ3LIC.js → state-ZZAYLXSR.js} +2 -2
  71. package/dist/{theme-7K5257BK.js → theme-HHHWSTRG.js} +5 -5
  72. package/dist/{toolPermissionSettings-2QVLET4R.js → toolPermissionSettings-F5675VV2.js} +6 -6
  73. package/dist/tools-BJAC444Q.js +52 -0
  74. package/dist/{userInput-MF4TLCQC.js → userInput-VPY4CM4B.js} +30 -30
  75. package/package.json +1 -1
  76. package/dist/REPL-J7EKVUMW.js +0 -47
  77. package/dist/chunk-HWIU6J2T.js.map +0 -7
  78. package/dist/chunk-KEALJIDY.js.map +0 -7
  79. package/dist/commands-SNKMFZOR.js +0 -51
  80. package/dist/lsp-NJNGVY3E.js +0 -17
  81. package/dist/prompts-MQWEK5T7.js +0 -53
  82. package/dist/query-OH75KHOB.js +0 -55
  83. package/dist/tools-6MGNB35T.js +0 -52
  84. /package/dist/{REPL-J7EKVUMW.js.map → REPL-6C4OPI2C.js.map} +0 -0
  85. /package/dist/{acp-NMISJFTQ.js.map → acp-RGMIE4DU.js.map} +0 -0
  86. /package/dist/{agentsValidate-G7H7CUZU.js.map → agentsValidate-MIP3HSNM.js.map} +0 -0
  87. /package/dist/{ask-E7XSHQAN.js.map → ask-WWDKFT4S.js.map} +0 -0
  88. /package/dist/{autoUpdater-EAH2H7AP.js.map → autoUpdater-HNFOZCBL.js.map} +0 -0
  89. /package/dist/{chunk-N6ETO4PE.js.map → chunk-3DJZ2FP3.js.map} +0 -0
  90. /package/dist/{chunk-BHBHS3MY.js.map → chunk-4IUTKGZ3.js.map} +0 -0
  91. /package/dist/{chunk-6XDESUHK.js.map → chunk-52XBAAOD.js.map} +0 -0
  92. /package/dist/{chunk-L62OCVZH.js.map → chunk-5P7OEKKE.js.map} +0 -0
  93. /package/dist/{chunk-BBZ2VZ5J.js.map → chunk-5VYUNKPN.js.map} +0 -0
  94. /package/dist/{chunk-VAR6IG72.js.map → chunk-7ZRJIA2T.js.map} +0 -0
  95. /package/dist/{chunk-3RXA6IWG.js.map → chunk-BBZOZE5U.js.map} +0 -0
  96. /package/dist/{chunk-BPN5QY2K.js.map → chunk-CU7LUROX.js.map} +0 -0
  97. /package/dist/{chunk-GEHCREEY.js.map → chunk-CUICIV5I.js.map} +0 -0
  98. /package/dist/{chunk-T2HQBH46.js.map → chunk-L7H4HULV.js.map} +0 -0
  99. /package/dist/{chunk-7IKX4543.js.map → chunk-LHVJ4DCY.js.map} +0 -0
  100. /package/dist/{chunk-KUIDN7FP.js.map → chunk-LNJEAGUM.js.map} +0 -0
  101. /package/dist/{chunk-UF2ARFEB.js.map → chunk-N32NFSDQ.js.map} +0 -0
  102. /package/dist/{chunk-XD5KXOSG.js.map → chunk-NE7ZMP3U.js.map} +0 -0
  103. /package/dist/{chunk-SBF7BOS4.js.map → chunk-NSNUA3AI.js.map} +0 -0
  104. /package/dist/{chunk-Q7W7PGSO.js.map → chunk-P4BU7OLK.js.map} +0 -0
  105. /package/dist/{chunk-MRU4VN5R.js.map → chunk-PC4T3EHY.js.map} +0 -0
  106. /package/dist/{chunk-Q32HEIH2.js.map → chunk-QNTNP3TF.js.map} +0 -0
  107. /package/dist/{chunk-4GB3OWJY.js.map → chunk-QQP7RZHT.js.map} +0 -0
  108. /package/dist/{chunk-VBJP55XA.js.map → chunk-RHMAO6OV.js.map} +0 -0
  109. /package/dist/{chunk-LSJILN2S.js.map → chunk-RWT3O44A.js.map} +0 -0
  110. /package/dist/{chunk-KTLUYKAC.js.map → chunk-SG7SPYM4.js.map} +0 -0
  111. /package/dist/{chunk-EQMKAITH.js.map → chunk-SNRPFDBW.js.map} +0 -0
  112. /package/dist/{chunk-HVMJPWC6.js.map → chunk-XKJGQBIN.js.map} +0 -0
  113. /package/dist/{chunk-HHTFRFKN.js.map → chunk-YI4WTP42.js.map} +0 -0
  114. /package/dist/{chunk-F47KO7J2.js.map → chunk-ZMKBSLLD.js.map} +0 -0
  115. /package/dist/{cli-VTNXK3N3.js.map → cli-DBIRYO2K.js.map} +0 -0
  116. /package/dist/{commands-SNKMFZOR.js.map → commands-CYQHNNSX.js.map} +0 -0
  117. /package/dist/{config-FBT5ABGI.js.map → config-WF57SRJH.js.map} +0 -0
  118. /package/dist/{context-WBZK3AZX.js.map → context-KZT5TVOO.js.map} +0 -0
  119. /package/dist/{customCommands-IFWVN654.js.map → customCommands-RROXEXFI.js.map} +0 -0
  120. /package/dist/{env-5O5DDVID.js.map → env-V3EUI52X.js.map} +0 -0
  121. /package/dist/{file-K3P2ZXOY.js.map → file-EY5S5X2I.js.map} +0 -0
  122. /package/dist/{llm-XZHOT5QG.js.map → llm-HRDLIEIJ.js.map} +0 -0
  123. /package/dist/{llmLazy-VVQLL2O4.js.map → llmLazy-5JLFDV6C.js.map} +0 -0
  124. /package/dist/{loader-JC5XSGNC.js.map → loader-F2SDAB3W.js.map} +0 -0
  125. /package/dist/{lsp-NJNGVY3E.js.map → lsp-IEUZLTEJ.js.map} +0 -0
  126. /package/dist/{lspAnchor-3AQPL2ZZ.js.map → lspAnchor-UT54IBRE.js.map} +0 -0
  127. /package/dist/{mcp-DTKD53FS.js.map → mcp-MJTCLGYY.js.map} +0 -0
  128. /package/dist/{mentionProcessor-JS7EY2LD.js.map → mentionProcessor-H7F7SZ5E.js.map} +0 -0
  129. /package/dist/{messages-GQMW2ANU.js.map → messages-5PHNMZRG.js.map} +0 -0
  130. /package/dist/{model-VGKT6DJE.js.map → model-L7BXTDSH.js.map} +0 -0
  131. /package/dist/{openai-TSJZC7MB.js.map → openai-5VV7N26J.js.map} +0 -0
  132. /package/dist/{outputStyles-5VLEN2UN.js.map → outputStyles-GTAHP23K.js.map} +0 -0
  133. /package/dist/{pluginRuntime-XKPZ3BX4.js.map → pluginRuntime-23TPWKF3.js.map} +0 -0
  134. /package/dist/{pluginValidation-ZHCDH3MZ.js.map → pluginValidation-6VPGAZPM.js.map} +0 -0
  135. /package/dist/{prompts-MQWEK5T7.js.map → prompts-D3KBVMOD.js.map} +0 -0
  136. /package/dist/{pybAgentSessionLoad-5CPNQIXV.js.map → pybAgentSessionLoad-H5RDHP7Z.js.map} +0 -0
  137. /package/dist/{pybAgentSessionResume-VCYFMJCA.js.map → pybAgentSessionResume-Z735IT3K.js.map} +0 -0
  138. /package/dist/{pybAgentStreamJsonSession-DOTUU74W.js.map → pybAgentStreamJsonSession-YI2CEMWO.js.map} +0 -0
  139. /package/dist/{pybHooks-W2EX2SUW.js.map → pybHooks-5MA4UT4U.js.map} +0 -0
  140. /package/dist/{query-OH75KHOB.js.map → query-NJIFF75Q.js.map} +0 -0
  141. /package/dist/{registry-OUOC3F5A.js.map → registry-G36Y7TJR.js.map} +0 -0
  142. /package/dist/{ripgrep-MEONNP4W.js.map → ripgrep-BYLSY4AL.js.map} +0 -0
  143. /package/dist/{skillMarketplace-34N7IKSR.js.map → skillMarketplace-XN5XMW6F.js.map} +0 -0
  144. /package/dist/{state-6NDQ3LIC.js.map → state-ZZAYLXSR.js.map} +0 -0
  145. /package/dist/{theme-7K5257BK.js.map → theme-HHHWSTRG.js.map} +0 -0
  146. /package/dist/{toolPermissionSettings-2QVLET4R.js.map → toolPermissionSettings-F5675VV2.js.map} +0 -0
  147. /package/dist/{tools-6MGNB35T.js.map → tools-BJAC444Q.js.map} +0 -0
  148. /package/dist/{userInput-MF4TLCQC.js.map → userInput-VPY4CM4B.js.map} +0 -0
@@ -3,7 +3,7 @@ const require = __pybCreateRequire(import.meta.url);
3
3
  import {
4
4
  SESSION_ID,
5
5
  resolveXdgCachePath
6
- } from "./chunk-KUIDN7FP.js";
6
+ } from "./chunk-LNJEAGUM.js";
7
7
 
8
8
  // src/utils/log/debugLogger.ts
9
9
  import { existsSync, mkdirSync, appendFileSync } from "fs";
@@ -2,11 +2,11 @@ import { createRequire as __pybCreateRequire } from "node:module";
2
2
  const require = __pybCreateRequire(import.meta.url);
3
3
  import {
4
4
  listAllContentFiles
5
- } from "./chunk-6XDESUHK.js";
5
+ } from "./chunk-52XBAAOD.js";
6
6
  import {
7
7
  getCwd,
8
8
  logError
9
- } from "./chunk-KUIDN7FP.js";
9
+ } from "./chunk-LNJEAGUM.js";
10
10
 
11
11
  // src/utils/fs/file.ts
12
12
  import {
@@ -8,14 +8,14 @@ import {
8
8
  getSettingsFileCandidates,
9
9
  loadSettingsWithLegacyFallback,
10
10
  saveSettingsToPrimaryAndSyncLegacy
11
- } from "./chunk-F47KO7J2.js";
11
+ } from "./chunk-ZMKBSLLD.js";
12
12
  import {
13
13
  getCurrentProjectConfig
14
- } from "./chunk-4GB3OWJY.js";
14
+ } from "./chunk-QQP7RZHT.js";
15
15
  import {
16
16
  getCwd,
17
17
  logError
18
- } from "./chunk-KUIDN7FP.js";
18
+ } from "./chunk-LNJEAGUM.js";
19
19
 
20
20
  // src/utils/permissions/toolPermissionSettings.ts
21
21
  function uniqueStrings(value) {
@@ -2,15 +2,15 @@ import { createRequire as __pybCreateRequire } from "node:module";
2
2
  const require = __pybCreateRequire(import.meta.url);
3
3
  import {
4
4
  loadSettingsWithLegacyFallback
5
- } from "./chunk-F47KO7J2.js";
5
+ } from "./chunk-ZMKBSLLD.js";
6
6
  import {
7
7
  debug
8
- } from "./chunk-3RXA6IWG.js";
8
+ } from "./chunk-BBZOZE5U.js";
9
9
  import {
10
10
  env,
11
11
  getCwd,
12
12
  resolveXdgDataPath
13
- } from "./chunk-KUIDN7FP.js";
13
+ } from "./chunk-LNJEAGUM.js";
14
14
  import {
15
15
  __require
16
16
  } from "./chunk-I3J4JYES.js";
@@ -76,6 +76,29 @@ async function findNearestRoot(startDir, markers, options) {
76
76
  var __filename = fileURLToPath(import.meta.url);
77
77
  var __dirname = dirname(__filename);
78
78
  var lspInstallDir = getLspBinDir();
79
+ var installNotices = [];
80
+ var HEAVY_LSP_SERVERS = /* @__PURE__ */ new Set([
81
+ "eslint",
82
+ "vue-language-server",
83
+ "jdtls",
84
+ "metals",
85
+ "elixir-ls",
86
+ "haskell-language-server",
87
+ "zls",
88
+ "lua-language-server"
89
+ ]);
90
+ function addInstallNotice(notice) {
91
+ installNotices.push(notice);
92
+ if (installNotices.length > 50) {
93
+ installNotices.shift();
94
+ }
95
+ }
96
+ function getInstallNotices() {
97
+ return [...installNotices];
98
+ }
99
+ function __resetInstallNoticesForTests() {
100
+ installNotices.length = 0;
101
+ }
79
102
  function parseBoolean(value) {
80
103
  if (value === void 0) return void 0;
81
104
  const normalized = value.trim().toLowerCase();
@@ -95,6 +118,81 @@ async function isAutoInstallEnabled() {
95
118
  if (enabled === false) return false;
96
119
  return env.hasInternetAccess();
97
120
  }
121
+ function getInstallTimeoutMs() {
122
+ const override = Number.parseInt(process.env.PYB_LSP_INSTALL_TIMEOUT_MS ?? "", 10);
123
+ if (Number.isFinite(override)) {
124
+ if (override <= 0) return null;
125
+ return override;
126
+ }
127
+ return 12e4;
128
+ }
129
+ async function runInstallWithPolicy(serverId, install) {
130
+ const isHeavy = HEAVY_LSP_SERVERS.has(serverId);
131
+ const startAt = Date.now();
132
+ if (isHeavy) {
133
+ addInstallNotice({
134
+ server: serverId,
135
+ stage: "start",
136
+ message: `${serverId} install started`,
137
+ at: startAt
138
+ });
139
+ console.log(`[LSP] ${serverId} install started`);
140
+ }
141
+ const timeoutMs = getInstallTimeoutMs();
142
+ let timeoutId = null;
143
+ const timeoutPromise = timeoutMs === null ? null : new Promise((resolve) => {
144
+ timeoutId = setTimeout(() => resolve({ timeout: true }), timeoutMs);
145
+ });
146
+ const installPromise = install().then((ok) => ({ ok })).catch((error) => ({ error }));
147
+ const result = timeoutPromise ? await Promise.race([installPromise, timeoutPromise]) : await installPromise;
148
+ if (timeoutId) clearTimeout(timeoutId);
149
+ if ("timeout" in result) {
150
+ addInstallNotice({
151
+ server: serverId,
152
+ stage: "timeout",
153
+ message: `${serverId} install timed out`,
154
+ at: Date.now()
155
+ });
156
+ if (isHeavy) {
157
+ console.log(`[LSP] ${serverId} install timed out`);
158
+ }
159
+ return false;
160
+ }
161
+ if ("error" in result) {
162
+ addInstallNotice({
163
+ server: serverId,
164
+ stage: "failed",
165
+ message: `${serverId} install failed: ${String(result.error)}`,
166
+ at: Date.now()
167
+ });
168
+ if (isHeavy) {
169
+ console.log(`[LSP] ${serverId} install failed: ${String(result.error)}`);
170
+ }
171
+ return false;
172
+ }
173
+ if (!result.ok) {
174
+ addInstallNotice({
175
+ server: serverId,
176
+ stage: "failed",
177
+ message: `${serverId} install failed`,
178
+ at: Date.now()
179
+ });
180
+ if (isHeavy) {
181
+ console.log(`[LSP] ${serverId} install failed`);
182
+ }
183
+ return false;
184
+ }
185
+ if (isHeavy) {
186
+ addInstallNotice({
187
+ server: serverId,
188
+ stage: "success",
189
+ message: `${serverId} install completed`,
190
+ at: Date.now()
191
+ });
192
+ console.log(`[LSP] ${serverId} install completed`);
193
+ }
194
+ return true;
195
+ }
98
196
  function ensureInstallDir() {
99
197
  if (!existsSync2(lspInstallDir)) {
100
198
  mkdirSync(lspInstallDir, { recursive: true });
@@ -151,6 +249,21 @@ async function runBinary(cmd, extraEnv, cwd = lspInstallDir) {
151
249
  }
152
250
  return { ok: false, error: stderr || stdout };
153
251
  }
252
+ async function runBinaryWithTimeout(cmd, extraEnv, cwd, timeoutMs) {
253
+ if (timeoutMs === null) {
254
+ return runBinary(cmd, extraEnv, cwd);
255
+ }
256
+ let timeoutId = null;
257
+ const timeoutPromise = new Promise((resolve) => {
258
+ timeoutId = setTimeout(() => resolve({ timeout: true }), timeoutMs);
259
+ });
260
+ const result = await Promise.race([runBinary(cmd, extraEnv, cwd), timeoutPromise]);
261
+ if (timeoutId) clearTimeout(timeoutId);
262
+ if ("timeout" in result) {
263
+ return { ok: false, timeout: true, error: "timeout" };
264
+ }
265
+ return result;
266
+ }
154
267
  async function getJavaMajorVersion() {
155
268
  const proc = Bun.spawn({
156
269
  cmd: ["java", "-version"],
@@ -209,12 +322,13 @@ async function installEslintServer(installDir = lspInstallDir) {
209
322
  debug.error("LSP_MISSING_DEP", { server: "eslint", error: `${npmCmd} not found` });
210
323
  return false;
211
324
  }
212
- const installResult = await runBinary([npmCmd, "install"], void 0, finalPath);
325
+ const timeoutMs = getInstallTimeoutMs();
326
+ const installResult = await runBinaryWithTimeout([npmCmd, "install"], void 0, finalPath, timeoutMs);
213
327
  if (!installResult.ok) {
214
328
  debug.error("LSP_INSTALL_FAILED", { server: "eslint", error: installResult.error });
215
329
  return false;
216
330
  }
217
- const compileResult = await runBinary([npmCmd, "run", "compile"], void 0, finalPath);
331
+ const compileResult = await runBinaryWithTimeout([npmCmd, "run", "compile"], void 0, finalPath, timeoutMs);
218
332
  if (!compileResult.ok) {
219
333
  debug.error("LSP_INSTALL_FAILED", { server: "eslint", error: compileResult.error });
220
334
  return false;
@@ -376,6 +490,17 @@ var DEFAULT_LANGUAGE_CATEGORIES = {
376
490
  var EXPERIMENTAL_SWITCHES = {
377
491
  ty: { enable: ["ty"], disable: ["pyright"] }
378
492
  };
493
+ function readExperimentalEnvToggles() {
494
+ const out = {};
495
+ for (const [key, value] of Object.entries(process.env)) {
496
+ if (!key.startsWith("PYB_LSP_EXPERIMENTAL_")) continue;
497
+ const name = key.substring("PYB_LSP_EXPERIMENTAL_".length).toLowerCase();
498
+ const v = String(value ?? "").trim().toLowerCase();
499
+ const enabled = v === "1" || v === "true" || v === "on" || v === "yes";
500
+ out[name] = enabled;
501
+ }
502
+ return Object.keys(out).length ? out : void 0;
503
+ }
379
504
  function isRecord(value) {
380
505
  return Boolean(value && typeof value === "object" && !Array.isArray(value));
381
506
  }
@@ -521,9 +646,15 @@ function hasTyBinary(rootPath) {
521
646
  }
522
647
  function applyExperimentalSwitches(settings) {
523
648
  if (!settings) return void 0;
524
- if (!settings.experimental) return settings;
649
+ const envToggles = readExperimentalEnvToggles();
650
+ const experimental = settings.experimental || envToggles;
651
+ if (!experimental) return settings;
525
652
  const disabled = new Set(settings.disabledServers ?? []);
526
- for (const [key, enabled] of Object.entries(settings.experimental)) {
653
+ const merged = {
654
+ ...settings.experimental ?? {},
655
+ ...envToggles ?? {}
656
+ };
657
+ for (const [key, enabled] of Object.entries(merged)) {
527
658
  if (!enabled) continue;
528
659
  const rule = EXPERIMENTAL_SWITCHES[key];
529
660
  if (!rule) continue;
@@ -539,7 +670,8 @@ function applyExperimentalSwitches(settings) {
539
670
  }
540
671
  return {
541
672
  ...settings,
542
- disabledServers: disabled.size ? Array.from(disabled) : void 0
673
+ disabledServers: disabled.size ? Array.from(disabled) : void 0,
674
+ experimental
543
675
  };
544
676
  }
545
677
  function normalizeLspSettings(value) {
@@ -600,6 +732,8 @@ function detectPythonVenv(rootPath) {
600
732
  async function npmInstallIfMissing(packages, cwd = lspInstallDir) {
601
733
  const missing = packages.filter((pkg) => !existsSync2(join2(cwd, "node_modules", pkg, "package.json")));
602
734
  if (missing.length === 0) return true;
735
+ const canInstall = await isAutoInstallEnabled();
736
+ if (!canInstall) return false;
603
737
  return bunInstall(missing, cwd);
604
738
  }
605
739
  function multiResolveBinary(options) {
@@ -782,6 +916,43 @@ async function installJdtls(installDir) {
782
916
  }
783
917
  return true;
784
918
  }
919
+ function resolveMetalsBinary(installDir) {
920
+ const direct = Bun.which("metals");
921
+ if (direct) return direct;
922
+ const local = resolveBinInDir(installDir, "metals");
923
+ return local || null;
924
+ }
925
+ async function installMetals(installDir) {
926
+ if (!Bun.which("java")) {
927
+ throw new Error("java not found");
928
+ }
929
+ const javaMajor = await getJavaMajorVersion();
930
+ if (!javaMajor || javaMajor < 17) {
931
+ throw new Error("java 17 or newer required");
932
+ }
933
+ if (resolveMetalsBinary(installDir)) return true;
934
+ const installer = Bun.which("cs") ?? Bun.which("coursier");
935
+ if (!installer) {
936
+ throw new Error("coursier not found");
937
+ }
938
+ if (!existsSync2(installDir)) {
939
+ mkdirSync(installDir, { recursive: true });
940
+ }
941
+ const version = process.env.PYB_METALS_VERSION?.trim();
942
+ const coord = version ? `metals:${version}` : "metals";
943
+ const result = await runBinary([installer, "install", coord, "--install-dir", installDir], void 0, installDir);
944
+ if (!result.ok) {
945
+ throw new Error(result.error || "metals install failed");
946
+ }
947
+ const binary = resolveBinInDir(installDir, "metals");
948
+ if (!binary) {
949
+ throw new Error("metals binary not found after install");
950
+ }
951
+ if (process.platform !== "win32") {
952
+ chmodSync(binary, 493);
953
+ }
954
+ return true;
955
+ }
785
956
  function getJdtlsLauncherJar(installDir) {
786
957
  const pluginsDir = join2(installDir, "plugins");
787
958
  if (!existsSync2(pluginsDir)) return null;
@@ -873,7 +1044,7 @@ var ServerDefinition = class extends BaseLspServer {
873
1044
  }
874
1045
  async prepare() {
875
1046
  if (this.installStrategy) {
876
- return this.installStrategy();
1047
+ return runInstallWithPolicy(this.id, this.installStrategy);
877
1048
  }
878
1049
  return true;
879
1050
  }
@@ -1659,13 +1830,31 @@ var LspServerRegistry = class _LspServerRegistry {
1659
1830
  "tinymist",
1660
1831
  []
1661
1832
  ));
1662
- this.register(new GenericBinaryServer(
1663
- "metals",
1664
- [".scala", ".sbt", ".sc"],
1665
- ["build.sbt"],
1666
- "metals",
1667
- []
1668
- ));
1833
+ this.register(new ServerDefinition({
1834
+ id: "metals",
1835
+ extensions: [".scala", ".sbt", ".sc"],
1836
+ rootMarkers: ["build.sbt", "build.sc", ".bsp", ".metals"],
1837
+ install: async () => {
1838
+ const installDir = join2(lspInstallDir, "metals");
1839
+ if (resolveMetalsBinary(installDir)) return true;
1840
+ const canInstall = await isAutoInstallEnabled();
1841
+ if (!canInstall) return false;
1842
+ ensureInstallDir();
1843
+ try {
1844
+ await installMetals(installDir);
1845
+ return true;
1846
+ } catch (error) {
1847
+ debug.error("LSP_INSTALL_FAILED", { server: "metals", error: String(error) });
1848
+ return false;
1849
+ }
1850
+ },
1851
+ spawn: async () => {
1852
+ const installDir = join2(lspInstallDir, "metals");
1853
+ const binary = resolveMetalsBinary(installDir);
1854
+ if (!binary) return null;
1855
+ return { command: binary, args: ["--stdio"] };
1856
+ }
1857
+ }));
1669
1858
  this.register(new GenericBinaryServer(
1670
1859
  "haskell-language-server",
1671
1860
  [".hs", ".lhs"],
@@ -1811,6 +2000,8 @@ var LspServerRegistry = class _LspServerRegistry {
1811
2000
 
1812
2001
  export {
1813
2002
  findNearestRoot,
2003
+ getInstallNotices,
2004
+ __resetInstallNoticesForTests,
1814
2005
  getEslintInstallDir,
1815
2006
  getEslintServerPath,
1816
2007
  installEslintServer,
@@ -1826,6 +2017,7 @@ export {
1826
2017
  installZls,
1827
2018
  installElixirLs,
1828
2019
  installJdtls,
2020
+ installMetals,
1829
2021
  ServerDefinition,
1830
2022
  GenericNpmServer,
1831
2023
  LspServerRegistry