holycodex 0.5.3 → 0.6.1-dev.29547206710.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/README.md +6 -89
  2. package/THIRD-PARTY-NOTICES.md +6 -2
  3. package/bin/holycodex.js +1 -1
  4. package/dist/cli.js +834 -0
  5. package/package.json +15 -27
  6. package/marketplace.json +0 -12
  7. package/plugin/.codex-plugin/plugin.json +0 -23
  8. package/plugin/.mcp.json +0 -7
  9. package/plugin/agents/explorer.toml +0 -6
  10. package/plugin/agents/librarian.toml +0 -6
  11. package/plugin/agents/worker.toml +0 -6
  12. package/plugin/hooks/hooks.json +0 -62
  13. package/plugin/runtime/bootstrap.js +0 -31
  14. package/plugin/runtime/cli.js +0 -235
  15. package/plugin/runtime/core-instructions.js +0 -4
  16. package/plugin/runtime/git-bash.js +0 -355
  17. package/plugin/runtime/lsp.js +0 -3611
  18. package/plugin/runtime/mcp-stdio-core.js +0 -212
  19. package/plugin/runtime/rules.js +0 -171
  20. package/plugin/skills/ast-grep/LICENSE +0 -21
  21. package/plugin/skills/ast-grep/SKILL.md +0 -20
  22. package/plugin/skills/ast-grep/references/cli.md +0 -3
  23. package/plugin/skills/ast-grep/references/install.md +0 -3
  24. package/plugin/skills/ast-grep/references/patterns.md +0 -3
  25. package/plugin/skills/ast-grep/references/pitfalls.md +0 -3
  26. package/plugin/skills/ast-grep/references/recipes.md +0 -9
  27. package/plugin/skills/ast-grep/references/sgconfig.md +0 -3
  28. package/plugin/skills/ast-grep/references/yaml-rules.md +0 -3
  29. package/plugin/skills/caveman/LICENSE +0 -21
  30. package/plugin/skills/caveman/SKILL.md +0 -31
  31. package/plugin/skills/compress/SKILL.md +0 -18
  32. package/plugin/skills/debugging/SKILL.md +0 -22
  33. package/plugin/skills/debugging/references/runtimes/README.md +0 -9
  34. package/plugin/skills/debugging/references/tools/README.md +0 -8
  35. package/plugin/skills/define-goal/LICENSE.txt +0 -201
  36. package/plugin/skills/define-goal/SKILL.md +0 -37
  37. package/plugin/skills/frontend/ATTRIBUTION.md +0 -218
  38. package/plugin/skills/frontend/LICENSE-Apache-2.0.txt +0 -201
  39. package/plugin/skills/frontend/SKILL.md +0 -50
  40. package/plugin/skills/frontend/references/perfection/README.md +0 -5
  41. package/plugin/skills/frontend/references/perfection/react-perf-tooling.md +0 -3
  42. package/plugin/skills/frontend/references/ui-ux-db/README.md +0 -5
  43. package/plugin/skills/frontend/references/ui-ux-db/data/charts.csv +0 -26
  44. package/plugin/skills/frontend/references/ui-ux-db/data/colors.csv +0 -162
  45. package/plugin/skills/frontend/references/ui-ux-db/data/icons.csv +0 -106
  46. package/plugin/skills/frontend/references/ui-ux-db/data/landing.csv +0 -35
  47. package/plugin/skills/frontend/references/ui-ux-db/data/products.csv +0 -162
  48. package/plugin/skills/frontend/references/ui-ux-db/data/react-performance.csv +0 -45
  49. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/astro.csv +0 -54
  50. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/flutter.csv +0 -53
  51. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/html-tailwind.csv +0 -56
  52. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/jetpack-compose.csv +0 -53
  53. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/nextjs.csv +0 -53
  54. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/nuxt-ui.csv +0 -51
  55. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/nuxtjs.csv +0 -59
  56. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/react-native.csv +0 -52
  57. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/react.csv +0 -54
  58. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/shadcn.csv +0 -61
  59. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/svelte.csv +0 -54
  60. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/swiftui.csv +0 -51
  61. package/plugin/skills/frontend/references/ui-ux-db/data/stacks/vue.csv +0 -50
  62. package/plugin/skills/frontend/references/ui-ux-db/data/styles.csv +0 -85
  63. package/plugin/skills/frontend/references/ui-ux-db/data/typography.csv +0 -74
  64. package/plugin/skills/frontend/references/ui-ux-db/data/ui-reasoning.csv +0 -162
  65. package/plugin/skills/frontend/references/ui-ux-db/data/ux-guidelines.csv +0 -100
  66. package/plugin/skills/frontend/references/ui-ux-db/data/web-interface.csv +0 -31
  67. package/plugin/skills/frontend/references/ui-ux-db/scripts/core.py +0 -262
  68. package/plugin/skills/frontend/references/ui-ux-db/scripts/design_system.py +0 -1148
  69. package/plugin/skills/frontend/references/ui-ux-db/scripts/search.py +0 -114
  70. package/plugin/skills/handoff/SKILL.md +0 -10
  71. package/plugin/skills/lsp/SKILL.md +0 -35
  72. package/plugin/skills/lsp-setup/SKILL.md +0 -18
  73. package/plugin/skills/lsp-setup/scripts/detect-lsp.ts +0 -233
  74. package/plugin/skills/lsp-setup/scripts/lsp-server-table.ts +0 -188
  75. package/plugin/skills/lsp-setup/scripts/tsconfig.json +0 -17
  76. package/plugin/skills/lsp-setup/scripts/verify-lsp.ts +0 -166
  77. package/plugin/skills/plan/SKILL.md +0 -24
  78. package/plugin/skills/plan-review/SKILL.md +0 -22
  79. package/plugin/skills/programming/SKILL.md +0 -42
  80. package/plugin/skills/programming/references/code-smells.md +0 -3
  81. package/plugin/skills/programming/references/go/README.md +0 -3
  82. package/plugin/skills/programming/references/logging.md +0 -3
  83. package/plugin/skills/programming/references/python/README.md +0 -3
  84. package/plugin/skills/programming/references/rust/README.md +0 -3
  85. package/plugin/skills/programming/references/rust-ub/README.md +0 -3
  86. package/plugin/skills/programming/references/typescript/README.md +0 -3
  87. package/plugin/skills/refactor/SKILL.md +0 -21
  88. package/plugin/skills/remove-ai-slops/SKILL.md +0 -20
  89. package/plugin/skills/rules/SKILL.md +0 -16
  90. package/plugin/skills/security-research/SKILL.md +0 -38
@@ -1,3611 +0,0 @@
1
- #!/usr/bin/env node
2
- import { a as stackOrMessageFromError, i as messageFromError, n as errorResponse, o as successResponse, r as jsonRpcId, s as isPlainRecord, t as runJsonRpcStdioServer } from "./mcp-stdio-core.js";
3
- import { basename, delimiter, dirname, extname, isAbsolute, join, relative, resolve } from "node:path";
4
- import { argv, execPath, stderr } from "node:process";
5
- import { homedir, tmpdir } from "node:os";
6
- import { fileURLToPath, pathToFileURL } from "node:url";
7
- import { spawn, spawnSync } from "node:child_process";
8
- import { closeSync, existsSync, lstatSync, mkdirSync, openSync, readFileSync, readdirSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, writeSync } from "node:fs";
9
- import { AsyncLocalStorage } from "node:async_hooks";
10
- import { connect, createServer } from "node:net";
11
- import { createHash } from "node:crypto";
12
- import { createRequire } from "node:module";
13
- //#region packages/lsp-core/src/request-context.ts
14
- var storage = new AsyncLocalStorage();
15
- function runWithRequestContext(context, fn) {
16
- return storage.run(context, fn);
17
- }
18
- function contextCwd() {
19
- return storage.getStore()?.cwd ?? process.cwd();
20
- }
21
- function contextEnv(key) {
22
- const store = storage.getStore();
23
- if (store?.env) return store.env[key];
24
- return process.env[key];
25
- }
26
- //#endregion
27
- //#region packages/lsp-core/src/lsp/effective-extension.ts
28
- var BASENAME_EXTENSIONS = {
29
- Dockerfile: ".dockerfile",
30
- Containerfile: ".dockerfile"
31
- };
32
- function effectiveExtension(filePath) {
33
- return BASENAME_EXTENSIONS[basename(filePath)] ?? extname(filePath);
34
- }
35
- //#endregion
36
- //#region packages/lsp-core/src/lsp/errors.ts
37
- var LspConnectionClosedError = class extends Error {
38
- constructor(serverId, root, message) {
39
- super(message ?? `LSP connection closed for ${serverId} at ${root}`);
40
- this.serverId = serverId;
41
- this.root = root;
42
- this.name = "LspConnectionClosedError";
43
- }
44
- };
45
- var LspProcessExitedError = class extends Error {
46
- constructor(serverId, root, exitCode, stderrTail) {
47
- const stderrSuffix = stderrTail ? `\nstderr tail: ${stderrTail}` : "";
48
- super(`LSP server ${serverId} at ${root} exited with code ${exitCode ?? "null"}${stderrSuffix}`);
49
- this.serverId = serverId;
50
- this.root = root;
51
- this.exitCode = exitCode;
52
- this.stderrTail = stderrTail;
53
- this.name = "LspProcessExitedError";
54
- }
55
- };
56
- var LspRequestTimeoutError = class extends Error {
57
- constructor(method, stderrTail) {
58
- const stderrSuffix = stderrTail ? `\nrecent stderr: ${stderrTail}` : "";
59
- super(`LSP request timeout (method: ${method})${stderrSuffix}`);
60
- this.method = method;
61
- this.stderrTail = stderrTail;
62
- this.name = "LspRequestTimeoutError";
63
- }
64
- };
65
- var LspInvalidPathError = class extends Error {
66
- constructor(..._args) {
67
- super(..._args);
68
- this.name = "LspInvalidPathError";
69
- }
70
- };
71
- var LspServerLookupError = class extends Error {
72
- constructor(..._args2) {
73
- super(..._args2);
74
- this.name = "LspServerLookupError";
75
- }
76
- };
77
- var LspServerInitializingError = class extends Error {
78
- constructor(originalError) {
79
- super(`LSP server is still initializing. Please retry in a few seconds. Original error: ${originalError.message}`);
80
- this.originalError = originalError;
81
- this.name = "LspServerInitializingError";
82
- }
83
- };
84
- var LspProcessSpawnError = class extends Error {
85
- constructor(..._args3) {
86
- super(..._args3);
87
- this.name = "LspProcessSpawnError";
88
- }
89
- };
90
- function isLspDeadConnectionError(err) {
91
- return err instanceof LspConnectionClosedError || err instanceof LspProcessExitedError;
92
- }
93
- //#endregion
94
- //#region packages/lsp-core/src/lsp/cleanup-errors.ts
95
- function reportBestEffortCleanupError(operation, error) {
96
- if (process.env["CODEX_LSP_DEBUG_CLEANUP"] !== "1") return;
97
- const message = messageFromError(error);
98
- console.error(`[codex-lsp] ignored ${operation} failure during cleanup: ${message}`);
99
- }
100
- var STOP_HARD_KILL_TIMEOUT_MS = 5e3;
101
- var STOP_SIGKILL_GRACE_MS = 1e3;
102
- //#endregion
103
- //#region packages/lsp-core/src/lsp/json-rpc-connection.ts
104
- var HEADER_SEPARATOR = "\r\n\r\n";
105
- var PARSE_ERROR = -32700;
106
- var INVALID_REQUEST = -32600;
107
- var METHOD_NOT_FOUND = -32601;
108
- var INTERNAL_ERROR = -32603;
109
- var JsonRpcConnection = class {
110
- constructor(reader, writer) {
111
- this.reader = reader;
112
- this.writer = writer;
113
- this.pendingRequests = /* @__PURE__ */ new Map();
114
- this.notificationHandlers = /* @__PURE__ */ new Map();
115
- this.requestHandlers = /* @__PURE__ */ new Map();
116
- this.closeHandlers = [];
117
- this.errorHandlers = [];
118
- this.inputBuffer = Buffer.alloc(0);
119
- this.nextRequestId = 1;
120
- this.listening = false;
121
- this.disposed = false;
122
- this.handleData = (chunk) => {
123
- const chunkBuffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, "utf8");
124
- this.inputBuffer = Buffer.concat([this.inputBuffer, chunkBuffer]);
125
- this.drainInputBuffer();
126
- };
127
- this.handleClose = () => {
128
- for (const handler of this.closeHandlers) handler();
129
- };
130
- this.handleStreamError = (error) => {
131
- this.emitError(error);
132
- };
133
- }
134
- listen() {
135
- if (this.listening) return;
136
- this.listening = true;
137
- this.reader.on("data", this.handleData);
138
- this.reader.on("close", this.handleClose);
139
- this.reader.on("end", this.handleClose);
140
- this.reader.on("error", this.handleStreamError);
141
- this.writer.on("error", this.handleStreamError);
142
- }
143
- onNotification(method, handler) {
144
- this.notificationHandlers.set(method, handler);
145
- }
146
- onRequest(method, handler) {
147
- this.requestHandlers.set(method, handler);
148
- }
149
- onClose(handler) {
150
- this.closeHandlers.push(handler);
151
- }
152
- onError(handler) {
153
- this.errorHandlers.push(handler);
154
- }
155
- async sendRequest(method, params) {
156
- if (this.disposed) throw new Error("JSON-RPC connection is disposed");
157
- const id = this.nextRequestId;
158
- this.nextRequestId += 1;
159
- const message = params === void 0 ? {
160
- jsonrpc: "2.0",
161
- id,
162
- method
163
- } : {
164
- jsonrpc: "2.0",
165
- id,
166
- method,
167
- params
168
- };
169
- const responsePromise = new Promise((resolve, reject) => {
170
- this.pendingRequests.set(String(id), {
171
- resolve(result) {
172
- resolve(result);
173
- },
174
- reject
175
- });
176
- });
177
- try {
178
- await this.writeMessage(message);
179
- } catch (error) {
180
- this.pendingRequests.delete(String(id));
181
- throw error;
182
- }
183
- return responsePromise;
184
- }
185
- async sendNotification(method, params) {
186
- if (this.disposed) return;
187
- const message = params === void 0 ? {
188
- jsonrpc: "2.0",
189
- method
190
- } : {
191
- jsonrpc: "2.0",
192
- method,
193
- params
194
- };
195
- await this.writeMessage(message);
196
- }
197
- dispose() {
198
- if (this.disposed) return;
199
- this.disposed = true;
200
- this.reader.off("data", this.handleData);
201
- this.reader.off("close", this.handleClose);
202
- this.reader.off("end", this.handleClose);
203
- this.reader.off("error", this.handleStreamError);
204
- this.writer.off("error", this.handleStreamError);
205
- for (const pending of this.pendingRequests.values()) pending.reject(/* @__PURE__ */ new Error("JSON-RPC connection disposed"));
206
- this.pendingRequests.clear();
207
- this.notificationHandlers.clear();
208
- this.requestHandlers.clear();
209
- }
210
- drainInputBuffer() {
211
- while (true) {
212
- const headerEnd = this.inputBuffer.indexOf(HEADER_SEPARATOR);
213
- if (headerEnd === -1) return;
214
- const contentLength = parseContentLength(this.inputBuffer.subarray(0, headerEnd).toString("ascii"));
215
- if (contentLength === null) {
216
- this.inputBuffer = Buffer.alloc(0);
217
- this.emitError(/* @__PURE__ */ new Error("JSON-RPC message is missing Content-Length header"));
218
- return;
219
- }
220
- const bodyStart = headerEnd + Buffer.byteLength(HEADER_SEPARATOR);
221
- const bodyEnd = bodyStart + contentLength;
222
- if (this.inputBuffer.length < bodyEnd) return;
223
- const body = this.inputBuffer.subarray(bodyStart, bodyEnd).toString("utf8");
224
- this.inputBuffer = this.inputBuffer.subarray(bodyEnd);
225
- this.dispatchBody(body);
226
- }
227
- }
228
- dispatchBody(body) {
229
- let parsed;
230
- try {
231
- parsed = JSON.parse(body);
232
- } catch (error) {
233
- this.writeError(null, PARSE_ERROR, error instanceof Error ? error.message : "Parse error").catch((writeError) => this.emitError(toError(writeError)));
234
- return;
235
- }
236
- if (!isJsonRpcObject(parsed)) {
237
- this.writeError(null, INVALID_REQUEST, "Invalid JSON-RPC message").catch((error) => this.emitError(toError(error)));
238
- return;
239
- }
240
- if ("id" in parsed && ("result" in parsed || "error" in parsed)) {
241
- this.handleResponse(parsed);
242
- return;
243
- }
244
- if (typeof parsed["method"] !== "string") {
245
- const id = getMessageId(parsed) ?? null;
246
- this.writeError(id, INVALID_REQUEST, "Invalid JSON-RPC method").catch((error) => this.emitError(toError(error)));
247
- return;
248
- }
249
- if ("id" in parsed) {
250
- this.handleRequest(parsed);
251
- return;
252
- }
253
- this.handleNotification(parsed["method"], parsed["params"]);
254
- }
255
- handleResponse(message) {
256
- const id = getMessageId(message);
257
- if (id === void 0) return;
258
- const pending = this.pendingRequests.get(String(id));
259
- if (!pending) return;
260
- this.pendingRequests.delete(String(id));
261
- if ("error" in message) {
262
- pending.reject(jsonRpcErrorToError(message["error"]));
263
- return;
264
- }
265
- pending.resolve(message["result"]);
266
- }
267
- handleNotification(method, params) {
268
- const handler = this.notificationHandlers.get(method);
269
- if (!handler) return;
270
- try {
271
- handler(params);
272
- } catch (error) {
273
- this.emitError(toError(error));
274
- }
275
- }
276
- handleRequest(message) {
277
- const id = getMessageId(message);
278
- if (id === void 0) {
279
- this.writeError(null, INVALID_REQUEST, "Invalid JSON-RPC id").catch((error) => this.emitError(toError(error)));
280
- return;
281
- }
282
- const method = typeof message["method"] === "string" ? message["method"] : "";
283
- const handler = this.requestHandlers.get(method);
284
- if (!handler) {
285
- this.writeError(id, METHOD_NOT_FOUND, `Method not found: ${method}`).catch((error) => this.emitError(toError(error)));
286
- return;
287
- }
288
- Promise.resolve().then(() => handler(message["params"])).then((result) => this.writeMessage({
289
- jsonrpc: "2.0",
290
- id,
291
- result
292
- }), (error) => this.writeError(id, INTERNAL_ERROR, toError(error).message)).catch((error) => this.emitError(toError(error)));
293
- }
294
- async writeError(id, code, message) {
295
- await this.writeMessage({
296
- jsonrpc: "2.0",
297
- id,
298
- error: {
299
- code,
300
- message
301
- }
302
- });
303
- }
304
- writeMessage(message) {
305
- const body = JSON.stringify(message);
306
- const payload = `Content-Length: ${Buffer.byteLength(body, "utf8")}\r\n\r\n${body}`;
307
- return new Promise((resolve, reject) => {
308
- this.writer.write(payload, (error) => {
309
- if (error) {
310
- reject(error);
311
- return;
312
- }
313
- resolve();
314
- });
315
- });
316
- }
317
- emitError(error) {
318
- for (const handler of this.errorHandlers) handler(error);
319
- }
320
- };
321
- function parseContentLength(headers) {
322
- for (const line of headers.split("\r\n")) {
323
- const separatorIndex = line.indexOf(":");
324
- if (separatorIndex === -1) continue;
325
- if (line.slice(0, separatorIndex).trim().toLowerCase() !== "content-length") continue;
326
- const value = Number.parseInt(line.slice(separatorIndex + 1).trim(), 10);
327
- return Number.isFinite(value) && value >= 0 ? value : null;
328
- }
329
- return null;
330
- }
331
- function getMessageId(message) {
332
- const id = message["id"];
333
- if (typeof id === "number" || typeof id === "string" || id === null) return id;
334
- }
335
- function jsonRpcErrorToError(value) {
336
- if (!isPlainRecord(value)) return /* @__PURE__ */ new Error("JSON-RPC request failed");
337
- const message = typeof value["message"] === "string" ? value["message"] : "JSON-RPC request failed";
338
- const error = new Error(message);
339
- if (typeof value["code"] === "number") error.name = `JsonRpcError(${value["code"]})`;
340
- return error;
341
- }
342
- function toError(error) {
343
- return error instanceof Error ? error : new Error(String(error));
344
- }
345
- //#endregion
346
- //#region packages/lsp-core/src/lsp/process.ts
347
- function isMissingProcessError(error) {
348
- if (!(error instanceof Error) || !("code" in error)) return false;
349
- return error.code === "ESRCH";
350
- }
351
- function reportKillError(context, error) {
352
- if (!isMissingProcessError(error)) reportBestEffortCleanupError(context, error);
353
- }
354
- function validateCwd(cwd) {
355
- try {
356
- if (!existsSync(cwd)) return {
357
- valid: false,
358
- error: `Working directory does not exist: ${cwd}`
359
- };
360
- if (!statSync(cwd).isDirectory()) return {
361
- valid: false,
362
- error: `Path is not a directory: ${cwd}`
363
- };
364
- return { valid: true };
365
- } catch (err) {
366
- return {
367
- valid: false,
368
- error: `Cannot access working directory: ${cwd} (${err instanceof Error ? err.message : String(err)})`
369
- };
370
- }
371
- }
372
- function wrap(proc) {
373
- const exitedPromise = new Promise((resolve) => {
374
- proc.once("close", (code) => resolve(code ?? 0));
375
- proc.once("error", () => resolve(1));
376
- });
377
- if (!proc.stdin || !proc.stdout || !proc.stderr) throw new LspProcessSpawnError("Spawned process is missing one of stdin/stdout/stderr pipes");
378
- return {
379
- stdin: proc.stdin,
380
- stdout: proc.stdout,
381
- stderr: proc.stderr,
382
- get pid() {
383
- return proc.pid ?? void 0;
384
- },
385
- get exitCode() {
386
- return proc.exitCode;
387
- },
388
- get killed() {
389
- return proc.killed;
390
- },
391
- exited: exitedPromise,
392
- kill(signal) {
393
- killProcessTree(proc, signal ?? "SIGTERM");
394
- }
395
- };
396
- }
397
- function killProcessTree(proc, signal) {
398
- if (process.platform === "win32" && proc.pid) {
399
- const result = spawnSync("taskkill", [
400
- "/pid",
401
- String(proc.pid),
402
- "/f",
403
- "/t"
404
- ], {
405
- stdio: "ignore",
406
- windowsHide: true
407
- });
408
- if (!result.error && result.status === 0) return;
409
- if (result.error) reportKillError("windows process tree kill", result.error);
410
- }
411
- if (process.platform !== "win32" && proc.pid) try {
412
- process.kill(-proc.pid, signal);
413
- return;
414
- } catch (error) {
415
- reportKillError("process group kill", error);
416
- }
417
- try {
418
- proc.kill(signal);
419
- } catch (error) {
420
- reportKillError("process kill", error);
421
- }
422
- }
423
- function isWindowsShellShim(command) {
424
- const lowerCommand = command.toLowerCase();
425
- return lowerCommand.endsWith(".cmd") || lowerCommand.endsWith(".bat");
426
- }
427
- function splitPath(pathValue, platform) {
428
- const separator = platform === "win32" ? ";" : delimiter;
429
- return pathValue.split(separator).filter(Boolean);
430
- }
431
- function getWindowsPathExtensions(env) {
432
- const extensions = (env["PATHEXT"] ?? ".COM;.EXE;.BAT;.CMD").split(";").map((extension) => extension.trim()).filter(Boolean).map((extension) => extension.startsWith(".") ? extension : `.${extension}`);
433
- return [...new Set([
434
- ...extensions,
435
- ".exe",
436
- ".cmd",
437
- ".bat",
438
- ""
439
- ])];
440
- }
441
- function resolveWindowsCommand(command, env) {
442
- const hasPathSeparator = command.includes("/") || command.includes("\\");
443
- const pathValue = env["PATH"] ?? env["Path"] ?? "";
444
- const baseDirectories = hasPathSeparator ? [""] : splitPath(pathValue, "win32");
445
- const extensions = getWindowsPathExtensions(env);
446
- for (const baseDirectory of baseDirectories) for (const extension of extensions) {
447
- const candidate = baseDirectory ? join(baseDirectory, `${command}${extension}`) : `${command}${extension}`;
448
- if (existsSync(candidate)) return candidate;
449
- }
450
- return command;
451
- }
452
- function createSpawnCommand(command, platform = process.platform, commandProcessor = process.env["ComSpec"] ?? "cmd.exe", env = process.env) {
453
- const [cmd, ...args] = command;
454
- if (!cmd) throw new LspProcessSpawnError("[lsp] empty command");
455
- if (platform !== "win32") return {
456
- command: cmd,
457
- args,
458
- shell: false
459
- };
460
- const resolvedCommand = resolveWindowsCommand(cmd, env);
461
- if (!isWindowsShellShim(resolvedCommand)) return {
462
- command: resolvedCommand,
463
- args,
464
- shell: false
465
- };
466
- return {
467
- command: commandProcessor,
468
- args: [
469
- "/d",
470
- "/s",
471
- "/c",
472
- resolvedCommand,
473
- ...args
474
- ],
475
- shell: false
476
- };
477
- }
478
- function spawnProcess(command, options) {
479
- const cwdValidation = validateCwd(options.cwd);
480
- if (!cwdValidation.valid) throw new LspInvalidPathError(`[lsp] ${cwdValidation.error}`);
481
- const [cmd] = command;
482
- if (!cmd) throw new LspProcessSpawnError("[lsp] empty command");
483
- const preparedCommand = createSpawnCommand(command, process.platform, process.env["ComSpec"] ?? "cmd.exe", options.env);
484
- return wrap(spawn(preparedCommand.command, preparedCommand.args, {
485
- cwd: options.cwd,
486
- env: options.env,
487
- stdio: [
488
- "pipe",
489
- "pipe",
490
- "pipe"
491
- ],
492
- windowsHide: true,
493
- shell: preparedCommand.shell,
494
- detached: process.platform !== "win32"
495
- }));
496
- }
497
- //#endregion
498
- //#region packages/lsp-core/src/lsp/transport.ts
499
- function parseConfigurationItems(params) {
500
- if (!isPlainRecord(params) || !Array.isArray(params["items"])) return [];
501
- const items = [];
502
- for (const item of params["items"]) {
503
- if (!isPlainRecord(item)) continue;
504
- const section = item["section"];
505
- items.push(section === void 0 || typeof section !== "string" ? {} : { section });
506
- }
507
- return items;
508
- }
509
- function parseDiagnosticsParams(params) {
510
- if (!isPlainRecord(params) || typeof params["uri"] !== "string") return null;
511
- const diagnostics = Array.isArray(params["diagnostics"]) ? params["diagnostics"].filter(isDiagnostic) : [];
512
- return {
513
- uri: params["uri"],
514
- diagnostics
515
- };
516
- }
517
- var LspClientTransport = class {
518
- constructor(root, server, timeouts = {}) {
519
- this.root = root;
520
- this.server = server;
521
- this.proc = null;
522
- this.connection = null;
523
- this.stderrBuffer = [];
524
- this.processExited = false;
525
- this.diagnosticsStore = /* @__PURE__ */ new Map();
526
- this.requestTimeoutMs = timeouts.requestTimeoutMs ?? 15e3;
527
- this.initializeTimeoutMs = timeouts.initializeTimeoutMs ?? 6e4;
528
- }
529
- pid() {
530
- return this.proc?.pid;
531
- }
532
- command() {
533
- return [...this.server.command];
534
- }
535
- async start() {
536
- const env = createLspSpawnEnv(this.root, {
537
- ...process.env,
538
- ...this.server.env
539
- });
540
- this.proc = spawnProcess(this.server.command, {
541
- cwd: this.root,
542
- env
543
- });
544
- this.startStderrReading();
545
- await new Promise((resolve) => setTimeout(resolve, 100));
546
- if (this.proc.exitCode !== null) {
547
- const stderr = this.stderrBuffer.join("\n");
548
- throw new LspProcessExitedError(this.server.id, this.root, this.proc.exitCode, stderr.slice(-2e3));
549
- }
550
- this.connection = new JsonRpcConnection(this.proc.stdout, this.proc.stdin);
551
- this.connection.onNotification("textDocument/publishDiagnostics", (params) => {
552
- const diagnosticsParams = parseDiagnosticsParams(params);
553
- if (diagnosticsParams?.uri) this.diagnosticsStore.set(diagnosticsParams.uri, diagnosticsParams.diagnostics);
554
- });
555
- this.connection.onRequest("workspace/configuration", (params) => {
556
- return parseConfigurationItems(params).map((item) => {
557
- if (item.section === "json") return { validate: { enable: true } };
558
- return {};
559
- });
560
- });
561
- this.connection.onRequest("client/registerCapability", () => null);
562
- this.connection.onRequest("window/workDoneProgress/create", () => null);
563
- this.connection.onClose(() => {
564
- this.processExited = true;
565
- });
566
- this.connection.onError((error) => {
567
- reportBestEffortCleanupError("connection error notification", error);
568
- });
569
- this.connection.listen();
570
- }
571
- startStderrReading() {
572
- if (!this.proc) return;
573
- this.proc.stderr.setEncoding("utf-8");
574
- this.proc.stderr.on("data", (chunk) => {
575
- this.stderrBuffer.push(chunk);
576
- if (this.stderrBuffer.length > 100) this.stderrBuffer.shift();
577
- });
578
- }
579
- isConnectionClosedError(error) {
580
- if (!(error instanceof Error)) return false;
581
- return ("code" in error && typeof error.code === "string" ? error.code : void 0) === "ERR_STREAM_DESTROYED" || /connection closed|connection is disposed|stream was destroyed/i.test(error.message);
582
- }
583
- async sendRequest(method, ...args) {
584
- if (!this.connection) throw new Error("LSP client not started");
585
- if (this.processExited || this.proc && this.proc.exitCode !== null) {
586
- const stderrTail = this.stderrBuffer.slice(-10).join("\n");
587
- throw new LspProcessExitedError(this.server.id, this.root, this.proc?.exitCode ?? null, stderrTail || void 0);
588
- }
589
- const timeoutMs = args[1]?.timeoutMs ?? this.requestTimeoutMs;
590
- let timeoutHandle = null;
591
- const timeoutPromise = new Promise((_, reject) => {
592
- timeoutHandle = setTimeout(() => {
593
- reject(new LspRequestTimeoutError(method, this.stderrBuffer.slice(-5).join("\n") || void 0));
594
- }, timeoutMs);
595
- });
596
- try {
597
- const requestPromise = args.length === 0 ? this.connection.sendRequest(method) : this.connection.sendRequest(method, args[0]);
598
- const result = await Promise.race([requestPromise, timeoutPromise]);
599
- if (timeoutHandle !== null) clearTimeout(timeoutHandle);
600
- return result;
601
- } catch (error) {
602
- if (timeoutHandle !== null) clearTimeout(timeoutHandle);
603
- if (this.processExited || this.proc && this.proc.exitCode !== null) throw new LspProcessExitedError(this.server.id, this.root, this.proc?.exitCode ?? null, this.stderrBuffer.slice(-10).join("\n") || void 0);
604
- if (this.isConnectionClosedError(error)) throw new LspConnectionClosedError(this.server.id, this.root, error.message);
605
- throw error;
606
- }
607
- }
608
- async sendNotification(method, ...args) {
609
- if (!this.connection) return;
610
- if (this.processExited || this.proc && this.proc.exitCode !== null) return;
611
- try {
612
- if (args.length === 0) await this.connection.sendNotification(method);
613
- else await this.connection.sendNotification(method, args[0]);
614
- } catch (error) {
615
- if (this.isConnectionClosedError(error)) throw new LspConnectionClosedError(this.server.id, this.root, error.message);
616
- throw error;
617
- }
618
- }
619
- isAlive() {
620
- return this.proc !== null && !this.processExited && this.proc.exitCode === null;
621
- }
622
- async stop() {
623
- if (this.connection) {
624
- try {
625
- await this.sendRequest("shutdown");
626
- } catch (error) {
627
- reportBestEffortCleanupError("shutdown request", error);
628
- }
629
- try {
630
- await this.sendNotification("exit");
631
- } catch (error) {
632
- reportBestEffortCleanupError("exit notification", error);
633
- }
634
- try {
635
- this.connection.dispose();
636
- } catch (error) {
637
- reportBestEffortCleanupError("connection dispose", error);
638
- }
639
- this.connection = null;
640
- }
641
- const proc = this.proc;
642
- if (proc) {
643
- this.proc = null;
644
- let exitedBeforeTimeout = false;
645
- try {
646
- proc.kill();
647
- let timeoutId;
648
- const timeoutPromise = new Promise((resolve) => {
649
- timeoutId = setTimeout(resolve, STOP_HARD_KILL_TIMEOUT_MS);
650
- });
651
- await Promise.race([proc.exited.then(() => {
652
- exitedBeforeTimeout = true;
653
- }).finally(() => {
654
- if (timeoutId) clearTimeout(timeoutId);
655
- }), timeoutPromise]);
656
- if (!exitedBeforeTimeout) try {
657
- proc.kill("SIGKILL");
658
- await Promise.race([proc.exited, new Promise((resolve) => setTimeout(resolve, STOP_SIGKILL_GRACE_MS))]);
659
- } catch (error) {
660
- reportBestEffortCleanupError("hard process kill", error);
661
- }
662
- } catch (error) {
663
- reportBestEffortCleanupError("process stop", error);
664
- }
665
- }
666
- this.processExited = true;
667
- this.diagnosticsStore.clear();
668
- }
669
- getStoredDiagnostics(uri) {
670
- return this.diagnosticsStore.get(uri) ?? [];
671
- }
672
- };
673
- function createLspSpawnEnv(_root, input) {
674
- return { ...input };
675
- }
676
- function isDiagnostic(value) {
677
- return isPlainRecord(value) && isRange(value["range"]) && typeof value["message"] === "string";
678
- }
679
- function isRange(value) {
680
- return isPlainRecord(value) && isPosition(value["start"]) && isPosition(value["end"]);
681
- }
682
- function isPosition(value) {
683
- return isPlainRecord(value) && typeof value["line"] === "number" && typeof value["character"] === "number";
684
- }
685
- //#endregion
686
- //#region packages/lsp-core/src/lsp/connection.ts
687
- var INITIALIZE_SETTLE_MS = 300;
688
- var LspClientConnection = class extends LspClientTransport {
689
- async initialize() {
690
- const rootUri = pathToFileURL(this.root).href;
691
- await this.sendRequest("initialize", {
692
- processId: process.pid,
693
- rootUri,
694
- rootPath: this.root,
695
- workspaceFolders: [{
696
- uri: rootUri,
697
- name: "workspace"
698
- }],
699
- capabilities: {
700
- textDocument: {
701
- hover: { contentFormat: ["markdown", "plaintext"] },
702
- definition: { linkSupport: true },
703
- references: {},
704
- documentSymbol: { hierarchicalDocumentSymbolSupport: true },
705
- publishDiagnostics: {},
706
- rename: {
707
- prepareSupport: true,
708
- prepareSupportDefaultBehavior: 1,
709
- honorsChangeAnnotations: true
710
- },
711
- codeAction: {
712
- codeActionLiteralSupport: { codeActionKind: { valueSet: [
713
- "quickfix",
714
- "refactor",
715
- "refactor.extract",
716
- "refactor.inline",
717
- "refactor.rewrite",
718
- "source",
719
- "source.organizeImports",
720
- "source.fixAll"
721
- ] } },
722
- isPreferredSupport: true,
723
- disabledSupport: true,
724
- dataSupport: true,
725
- resolveSupport: { properties: ["edit", "command"] }
726
- }
727
- },
728
- workspace: {
729
- symbol: {},
730
- workspaceFolders: true,
731
- configuration: true,
732
- applyEdit: true,
733
- workspaceEdit: { documentChanges: true }
734
- }
735
- },
736
- initializationOptions: this.server.initialization
737
- }, { timeoutMs: this.initializeTimeoutMs });
738
- await this.sendNotification("initialized");
739
- await this.sendNotification("workspace/didChangeConfiguration", { settings: { json: { validate: { enable: true } } } });
740
- await new Promise((r) => setTimeout(r, INITIALIZE_SETTLE_MS));
741
- }
742
- };
743
- //#endregion
744
- //#region packages/lsp-core/src/lsp/language-mappings.ts
745
- var SYMBOL_KIND_MAP = {
746
- 1: "File",
747
- 2: "Module",
748
- 3: "Namespace",
749
- 4: "Package",
750
- 5: "Class",
751
- 6: "Method",
752
- 7: "Property",
753
- 8: "Field",
754
- 9: "Constructor",
755
- 10: "Enum",
756
- 11: "Interface",
757
- 12: "Function",
758
- 13: "Variable",
759
- 14: "Constant",
760
- 15: "String",
761
- 16: "Number",
762
- 17: "Boolean",
763
- 18: "Array",
764
- 19: "Object",
765
- 20: "Key",
766
- 21: "Null",
767
- 22: "EnumMember",
768
- 23: "Struct",
769
- 24: "Event",
770
- 25: "Operator",
771
- 26: "TypeParameter"
772
- };
773
- var SEVERITY_MAP = {
774
- 1: "error",
775
- 2: "warning",
776
- 3: "information",
777
- 4: "hint"
778
- };
779
- var EXT_TO_LANG = {
780
- ".abap": "abap",
781
- ".bat": "bat",
782
- ".bib": "bibtex",
783
- ".bibtex": "bibtex",
784
- ".clj": "clojure",
785
- ".cljs": "clojure",
786
- ".cljc": "clojure",
787
- ".edn": "clojure",
788
- ".coffee": "coffeescript",
789
- ".c": "c",
790
- ".cpp": "cpp",
791
- ".cxx": "cpp",
792
- ".cc": "cpp",
793
- ".c++": "cpp",
794
- ".cs": "csharp",
795
- ".css": "css",
796
- ".d": "d",
797
- ".pas": "pascal",
798
- ".pascal": "pascal",
799
- ".diff": "diff",
800
- ".patch": "diff",
801
- ".dart": "dart",
802
- ".dockerfile": "dockerfile",
803
- ".ex": "elixir",
804
- ".exs": "elixir",
805
- ".erl": "erlang",
806
- ".hrl": "erlang",
807
- ".fs": "fsharp",
808
- ".fsi": "fsharp",
809
- ".fsx": "fsharp",
810
- ".fsscript": "fsharp",
811
- ".gitcommit": "git-commit",
812
- ".gitrebase": "git-rebase",
813
- ".go": "go",
814
- ".groovy": "groovy",
815
- ".gleam": "gleam",
816
- ".hbs": "handlebars",
817
- ".handlebars": "handlebars",
818
- ".hs": "haskell",
819
- ".html": "html",
820
- ".htm": "html",
821
- ".ini": "ini",
822
- ".java": "java",
823
- ".jl": "julia",
824
- ".js": "javascript",
825
- ".jsx": "javascriptreact",
826
- ".json": "json",
827
- ".jsonc": "jsonc",
828
- ".tex": "latex",
829
- ".latex": "latex",
830
- ".less": "less",
831
- ".lua": "lua",
832
- ".makefile": "makefile",
833
- makefile: "makefile",
834
- ".md": "markdown",
835
- ".markdown": "markdown",
836
- ".m": "objective-c",
837
- ".mm": "objective-cpp",
838
- ".pl": "perl",
839
- ".pm": "perl",
840
- ".pm6": "perl6",
841
- ".php": "php",
842
- ".ps1": "powershell",
843
- ".psm1": "powershell",
844
- ".pug": "jade",
845
- ".jade": "jade",
846
- ".py": "python",
847
- ".pyi": "python",
848
- ".r": "r",
849
- ".cshtml": "razor",
850
- ".razor": "razor",
851
- ".rb": "ruby",
852
- ".rake": "ruby",
853
- ".gemspec": "ruby",
854
- ".ru": "ruby",
855
- ".erb": "erb",
856
- ".html.erb": "erb",
857
- ".js.erb": "erb",
858
- ".css.erb": "erb",
859
- ".json.erb": "erb",
860
- ".rs": "rust",
861
- ".scss": "scss",
862
- ".sass": "sass",
863
- ".scala": "scala",
864
- ".shader": "shaderlab",
865
- ".sh": "shellscript",
866
- ".bash": "shellscript",
867
- ".zsh": "shellscript",
868
- ".ksh": "shellscript",
869
- ".sql": "sql",
870
- ".svelte": "svelte",
871
- ".swift": "swift",
872
- ".ts": "typescript",
873
- ".tsx": "typescriptreact",
874
- ".mts": "typescript",
875
- ".cts": "typescript",
876
- ".mtsx": "typescriptreact",
877
- ".ctsx": "typescriptreact",
878
- ".xml": "xml",
879
- ".xsl": "xsl",
880
- ".yaml": "yaml",
881
- ".yml": "yaml",
882
- ".mjs": "javascript",
883
- ".cjs": "javascript",
884
- ".vue": "vue",
885
- ".zig": "zig",
886
- ".zon": "zig",
887
- ".astro": "astro",
888
- ".ml": "ocaml",
889
- ".mli": "ocaml",
890
- ".tf": "terraform",
891
- ".tfvars": "terraform-vars",
892
- ".hcl": "hcl",
893
- ".nix": "nix",
894
- ".typ": "typst",
895
- ".typc": "typst",
896
- ".ets": "typescript",
897
- ".lhs": "haskell",
898
- ".kt": "kotlin",
899
- ".kts": "kotlin",
900
- ".prisma": "prisma",
901
- ".h": "c",
902
- ".hpp": "cpp",
903
- ".hh": "cpp",
904
- ".hxx": "cpp",
905
- ".h++": "cpp",
906
- ".objc": "objective-c",
907
- ".objcpp": "objective-cpp",
908
- ".fish": "fish",
909
- ".graphql": "graphql",
910
- ".gql": "graphql"
911
- };
912
- function getLanguageId(ext) {
913
- return EXT_TO_LANG[ext] ?? "plaintext";
914
- }
915
- //#endregion
916
- //#region packages/lsp-core/src/lsp/client.ts
917
- var POST_OPEN_DELAY_MS = 1e3;
918
- var POST_DIAGNOSTICS_WAIT_MS = 500;
919
- var LspClient = class extends LspClientConnection {
920
- constructor(..._args) {
921
- super(..._args);
922
- this.openedFiles = /* @__PURE__ */ new Set();
923
- this.documentVersions = /* @__PURE__ */ new Map();
924
- this.lastSyncedText = /* @__PURE__ */ new Map();
925
- this.diagnosticPullErrors = [];
926
- }
927
- getDiagnosticPullErrors() {
928
- return this.diagnosticPullErrors;
929
- }
930
- async openFile(filePath) {
931
- const absPath = resolve(contextCwd(), filePath);
932
- const uri = pathToFileURL(absPath).href;
933
- const text = readFileSync(absPath, "utf-8");
934
- if (!this.openedFiles.has(absPath)) {
935
- const languageId = getLanguageId(effectiveExtension(absPath));
936
- const version = 1;
937
- await this.sendNotification("textDocument/didOpen", { textDocument: {
938
- uri,
939
- languageId,
940
- version,
941
- text
942
- } });
943
- this.openedFiles.add(absPath);
944
- this.documentVersions.set(uri, version);
945
- this.lastSyncedText.set(uri, text);
946
- await new Promise((r) => setTimeout(r, POST_OPEN_DELAY_MS));
947
- return;
948
- }
949
- if (this.lastSyncedText.get(uri) === text) return;
950
- const nextVersion = (this.documentVersions.get(uri) ?? 1) + 1;
951
- this.documentVersions.set(uri, nextVersion);
952
- this.lastSyncedText.set(uri, text);
953
- await this.sendNotification("textDocument/didChange", {
954
- textDocument: {
955
- uri,
956
- version: nextVersion
957
- },
958
- contentChanges: [{ text }]
959
- });
960
- await this.sendNotification("textDocument/didSave", {
961
- textDocument: { uri },
962
- text
963
- });
964
- }
965
- async definition(filePath, line, character) {
966
- const textDocument = await this.openTextDocument(filePath);
967
- return this.sendRequest("textDocument/definition", {
968
- textDocument,
969
- position: {
970
- line: line - 1,
971
- character
972
- }
973
- });
974
- }
975
- async references(filePath, line, character, includeDeclaration = true) {
976
- const textDocument = await this.openTextDocument(filePath);
977
- return this.sendRequest("textDocument/references", {
978
- textDocument,
979
- position: {
980
- line: line - 1,
981
- character
982
- },
983
- context: { includeDeclaration }
984
- });
985
- }
986
- async documentSymbols(filePath) {
987
- const textDocument = await this.openTextDocument(filePath);
988
- return this.sendRequest("textDocument/documentSymbol", { textDocument });
989
- }
990
- async workspaceSymbols(query) {
991
- return this.sendRequest("workspace/symbol", { query });
992
- }
993
- isUnsupportedDiagnosticPullError(error) {
994
- if (!(error instanceof Error)) return false;
995
- if (("code" in error && typeof error.code === "number" ? error.code : void 0) === -32601) return true;
996
- return /unsupported|not supported|method not found|unknown request/i.test(error.message);
997
- }
998
- async diagnostics(filePath) {
999
- const { uri } = await this.openTextDocument(filePath);
1000
- await new Promise((r) => setTimeout(r, POST_DIAGNOSTICS_WAIT_MS));
1001
- try {
1002
- const result = await this.sendRequest("textDocument/diagnostic", { textDocument: { uri } });
1003
- if (result.items) return { items: result.items };
1004
- } catch (error) {
1005
- if (!this.isUnsupportedDiagnosticPullError(error)) this.diagnosticPullErrors.push(error instanceof Error ? error : new Error(String(error)));
1006
- }
1007
- return { items: this.getStoredDiagnostics(uri) };
1008
- }
1009
- async prepareRename(filePath, line, character) {
1010
- const textDocument = await this.openTextDocument(filePath);
1011
- return this.sendRequest("textDocument/prepareRename", {
1012
- textDocument,
1013
- position: {
1014
- line: line - 1,
1015
- character
1016
- }
1017
- });
1018
- }
1019
- async rename(filePath, line, character, newName) {
1020
- const textDocument = await this.openTextDocument(filePath);
1021
- return this.sendRequest("textDocument/rename", {
1022
- textDocument,
1023
- position: {
1024
- line: line - 1,
1025
- character
1026
- },
1027
- newName
1028
- });
1029
- }
1030
- async openTextDocument(filePath) {
1031
- const absPath = resolve(contextCwd(), filePath);
1032
- await this.openFile(absPath);
1033
- return { uri: pathToFileURL(absPath).href };
1034
- }
1035
- };
1036
- //#endregion
1037
- //#region packages/lsp-core/src/lsp/process-signal-cleanup.ts
1038
- function installProcessSignalCleanup(cleanup) {
1039
- const signals = process.platform === "win32" ? [
1040
- "SIGINT",
1041
- "SIGTERM",
1042
- "SIGBREAK"
1043
- ] : ["SIGINT", "SIGTERM"];
1044
- const handler = () => {
1045
- cleanup().catch((error) => {
1046
- reportBestEffortCleanupError("signal cleanup", error);
1047
- });
1048
- };
1049
- for (const signal of signals) process.on(signal, handler);
1050
- return () => {
1051
- for (const signal of signals) process.removeListener(signal, handler);
1052
- };
1053
- }
1054
- //#endregion
1055
- //#region packages/lsp-core/src/lsp/manager.ts
1056
- async function stopClientBestEffort(client) {
1057
- try {
1058
- await client.stop();
1059
- } catch (error) {
1060
- reportBestEffortCleanupError("client stop", error);
1061
- }
1062
- }
1063
- function awaitWithSignal(promise, signal) {
1064
- if (!signal) return promise;
1065
- return new Promise((resolve, reject) => {
1066
- let settled = false;
1067
- const onAbort = () => {
1068
- if (settled) return;
1069
- settled = true;
1070
- reject(new DOMException("Aborted", "AbortError"));
1071
- };
1072
- if (signal.aborted) {
1073
- onAbort();
1074
- return;
1075
- }
1076
- signal.addEventListener("abort", onAbort, { once: true });
1077
- promise.then((value) => {
1078
- if (settled) return;
1079
- settled = true;
1080
- signal.removeEventListener("abort", onAbort);
1081
- resolve(value);
1082
- }, (err) => {
1083
- if (settled) return;
1084
- settled = true;
1085
- signal.removeEventListener("abort", onAbort);
1086
- reject(err);
1087
- });
1088
- });
1089
- }
1090
- var LspManager = class {
1091
- constructor(options = {}) {
1092
- this.clients = /* @__PURE__ */ new Map();
1093
- this.reaperHandle = null;
1094
- this.signalDisposer = null;
1095
- this.disposed = false;
1096
- this.idleTimeoutMs = options.idleTimeoutMs ?? 3e5;
1097
- this.initTimeoutMs = options.initTimeoutMs ?? 6e4;
1098
- this.reaperIntervalMs = options.reaperIntervalMs ?? 6e4;
1099
- this.clientFactory = options.clientFactory ?? ((root, server) => new LspClient(root, server));
1100
- this.now = options.now ?? (() => Date.now());
1101
- this.startReaper();
1102
- this.signalDisposer = installProcessSignalCleanup(() => this.stopAll());
1103
- }
1104
- startReaper() {
1105
- if (this.reaperHandle) return;
1106
- this.reaperHandle = setInterval(() => {
1107
- this.reapStale();
1108
- }, this.reaperIntervalMs);
1109
- if (typeof this.reaperHandle.unref === "function") this.reaperHandle.unref();
1110
- }
1111
- getKey(root, serverId) {
1112
- return `${root}::${serverId}`;
1113
- }
1114
- reapStale() {
1115
- const t = this.now();
1116
- for (const [key, managed] of this.clients) {
1117
- if (managed.isInitializing && managed.initializingSince !== null && t - managed.initializingSince > this.initTimeoutMs) {
1118
- stopClientBestEffort(managed.client);
1119
- this.clients.delete(key);
1120
- continue;
1121
- }
1122
- if (!managed.isInitializing && managed.refCount === 0 && managed.pendingWaiters === 0 && t - managed.lastUsedAt > this.idleTimeoutMs) {
1123
- stopClientBestEffort(managed.client);
1124
- this.clients.delete(key);
1125
- }
1126
- }
1127
- }
1128
- async tryDeleteIfOrphaned(key, managed) {
1129
- if (managed.refCount === 0 && managed.pendingWaiters === 0 && !managed.isInitializing && this.clients.get(key) === managed) {
1130
- this.clients.delete(key);
1131
- await stopClientBestEffort(managed.client);
1132
- }
1133
- }
1134
- async getClient(root, server, signal) {
1135
- if (this.disposed) throw new Error("LspManager has been disposed");
1136
- signal?.throwIfAborted();
1137
- const key = this.getKey(root, server.id);
1138
- let managed = this.clients.get(key);
1139
- if (managed) {
1140
- const t = this.now();
1141
- if (managed.isInitializing && managed.initializingSince !== null && t - managed.initializingSince > this.initTimeoutMs) {
1142
- await stopClientBestEffort(managed.client);
1143
- this.clients.delete(key);
1144
- managed = void 0;
1145
- }
1146
- }
1147
- if (managed) {
1148
- if (managed.initPromise) {
1149
- managed.pendingWaiters++;
1150
- try {
1151
- await awaitWithSignal(managed.initPromise, signal);
1152
- } catch (err) {
1153
- managed.pendingWaiters--;
1154
- await this.tryDeleteIfOrphaned(key, managed);
1155
- throw err;
1156
- }
1157
- managed.pendingWaiters--;
1158
- }
1159
- if (signal?.aborted) {
1160
- await this.tryDeleteIfOrphaned(key, managed);
1161
- signal.throwIfAborted();
1162
- }
1163
- if (!managed.client.isAlive()) {
1164
- await stopClientBestEffort(managed.client);
1165
- this.clients.delete(key);
1166
- return this.getClient(root, server, signal);
1167
- }
1168
- managed.refCount++;
1169
- managed.lastUsedAt = this.now();
1170
- return managed.client;
1171
- }
1172
- const client = this.clientFactory(root, server);
1173
- const initStartedAt = this.now();
1174
- const initPromise = (async () => {
1175
- await client.start();
1176
- await client.initialize();
1177
- })();
1178
- const newManaged = {
1179
- client,
1180
- refCount: 0,
1181
- pendingWaiters: 1,
1182
- lastUsedAt: initStartedAt,
1183
- initPromise,
1184
- isInitializing: true,
1185
- initializingSince: initStartedAt
1186
- };
1187
- this.clients.set(key, newManaged);
1188
- try {
1189
- await awaitWithSignal(initPromise, signal);
1190
- } catch (err) {
1191
- newManaged.pendingWaiters--;
1192
- if (this.clients.get(key) === newManaged) this.clients.delete(key);
1193
- await stopClientBestEffort(client);
1194
- throw err;
1195
- }
1196
- newManaged.pendingWaiters--;
1197
- newManaged.isInitializing = false;
1198
- newManaged.initializingSince = null;
1199
- newManaged.initPromise = null;
1200
- if (signal?.aborted) {
1201
- await this.tryDeleteIfOrphaned(key, newManaged);
1202
- signal.throwIfAborted();
1203
- }
1204
- newManaged.refCount++;
1205
- newManaged.lastUsedAt = this.now();
1206
- return client;
1207
- }
1208
- releaseClient(root, serverId) {
1209
- const key = this.getKey(root, serverId);
1210
- const managed = this.clients.get(key);
1211
- if (managed && managed.refCount > 0) {
1212
- managed.refCount--;
1213
- managed.lastUsedAt = this.now();
1214
- }
1215
- }
1216
- invalidateClient(root, serverId, client) {
1217
- const key = this.getKey(root, serverId);
1218
- const managed = this.clients.get(key);
1219
- if (!managed) return;
1220
- if (client && managed.client !== client) return;
1221
- this.clients.delete(key);
1222
- stopClientBestEffort(managed.client);
1223
- }
1224
- warmupClient(root, server) {
1225
- if (this.disposed) return;
1226
- const key = this.getKey(root, server.id);
1227
- if (this.clients.has(key)) return;
1228
- const client = this.clientFactory(root, server);
1229
- const initStartedAt = this.now();
1230
- const initPromise = (async () => {
1231
- await client.start();
1232
- await client.initialize();
1233
- })();
1234
- const managed = {
1235
- client,
1236
- refCount: 0,
1237
- pendingWaiters: 0,
1238
- lastUsedAt: initStartedAt,
1239
- initPromise,
1240
- isInitializing: true,
1241
- initializingSince: initStartedAt
1242
- };
1243
- this.clients.set(key, managed);
1244
- initPromise.then(() => {
1245
- managed.isInitializing = false;
1246
- managed.initializingSince = null;
1247
- managed.initPromise = null;
1248
- managed.lastUsedAt = this.now();
1249
- }, () => {
1250
- if (this.clients.get(key) === managed) this.clients.delete(key);
1251
- stopClientBestEffort(client);
1252
- });
1253
- }
1254
- isServerInitializing(root, serverId) {
1255
- return this.clients.get(this.getKey(root, serverId))?.isInitializing ?? false;
1256
- }
1257
- getSnapshot() {
1258
- const snapshots = [];
1259
- for (const [key, managed] of this.clients) {
1260
- const [root, serverId] = key.split("::");
1261
- snapshots.push({
1262
- root,
1263
- serverId,
1264
- refCount: managed.refCount,
1265
- pendingWaiters: managed.pendingWaiters,
1266
- lastUsedAt: managed.lastUsedAt,
1267
- isInitializing: managed.isInitializing,
1268
- alive: managed.client.isAlive(),
1269
- command: managed.client.command()
1270
- });
1271
- }
1272
- return snapshots;
1273
- }
1274
- hasClient(root, serverId) {
1275
- return this.clients.has(this.getKey(root, serverId));
1276
- }
1277
- clientCount() {
1278
- return this.clients.size;
1279
- }
1280
- async stopAll() {
1281
- this.disposed = true;
1282
- if (this.reaperHandle) {
1283
- clearInterval(this.reaperHandle);
1284
- this.reaperHandle = null;
1285
- }
1286
- if (this.signalDisposer) {
1287
- this.signalDisposer();
1288
- this.signalDisposer = null;
1289
- }
1290
- const stopPromises = [];
1291
- for (const managed of this.clients.values()) stopPromises.push(stopClientBestEffort(managed.client));
1292
- this.clients.clear();
1293
- await Promise.allSettled(stopPromises);
1294
- }
1295
- };
1296
- var _defaultInstance = null;
1297
- function getLspManager() {
1298
- if (!_defaultInstance) _defaultInstance = new LspManager();
1299
- return _defaultInstance;
1300
- }
1301
- async function disposeDefaultLspManager() {
1302
- if (_defaultInstance) {
1303
- const m = _defaultInstance;
1304
- _defaultInstance = null;
1305
- await m.stopAll();
1306
- }
1307
- }
1308
- //#endregion
1309
- //#region packages/lsp-core/src/lsp/server-install-state.ts
1310
- function getInstallDecisionsPath() {
1311
- const override = contextEnv("LSP_TOOLS_MCP_INSTALL_DECISIONS");
1312
- if (!override) return join(homedir(), ".codex", "lsp-install-decisions.json");
1313
- return isAbsolute(override) ? override : join(homedir(), override);
1314
- }
1315
- function loadInstallDecisions() {
1316
- const path = getInstallDecisionsPath();
1317
- if (!existsSync(path)) return {};
1318
- try {
1319
- const parsed = JSON.parse(readFileSync(path, "utf8"));
1320
- return isInstallDecisions(parsed) ? parsed : {};
1321
- } catch {
1322
- return {};
1323
- }
1324
- }
1325
- function loadInstallDecision(serverId) {
1326
- return loadInstallDecisions()[serverId];
1327
- }
1328
- function recordInstallDecision(serverId, decision, decidedAt = (/* @__PURE__ */ new Date()).toISOString()) {
1329
- const decisions = loadInstallDecisions();
1330
- decisions[serverId] = {
1331
- decision,
1332
- decidedAt
1333
- };
1334
- writeInstallDecisions(decisions);
1335
- }
1336
- function isInstallDecision(value) {
1337
- return value === "declined" || value === "allowed";
1338
- }
1339
- function writeInstallDecisions(decisions) {
1340
- const path = getInstallDecisionsPath();
1341
- mkdirSync(dirname(path), { recursive: true });
1342
- const tmpPath = `${path}.tmp`;
1343
- writeFileSync(tmpPath, `${JSON.stringify(decisions, null, 2)}\n`, "utf8");
1344
- renameSync(tmpPath, path);
1345
- }
1346
- function isInstallDecisions(value) {
1347
- return isPlainRecord(value) && Object.values(value).every(isInstallDecisionRecord);
1348
- }
1349
- function isInstallDecisionRecord(value) {
1350
- if (!isPlainRecord(value)) return false;
1351
- return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
1352
- }
1353
- //#endregion
1354
- //#region packages/lsp-core/src/lsp/server-definitions.ts
1355
- var LSP_INSTALL_HINTS = {
1356
- typescript: "npm install -g typescript-language-server typescript",
1357
- deno: "Install Deno from https://deno.land",
1358
- vue: "npm install -g @vue/language-server",
1359
- eslint: "npm install -g vscode-langservers-extracted",
1360
- oxlint: "npm install -g oxlint",
1361
- biome: "npm install -g @biomejs/biome",
1362
- gopls: "go install golang.org/x/tools/gopls@latest",
1363
- "ruby-lsp": "gem install ruby-lsp",
1364
- basedpyright: "pip install basedpyright",
1365
- pyright: "pip install pyright",
1366
- ty: "pip install ty",
1367
- ruff: "pip install ruff",
1368
- "elixir-ls": "See https://github.com/elixir-lsp/elixir-ls",
1369
- zls: "See https://github.com/zigtools/zls",
1370
- csharp: "dotnet tool install -g csharp-ls",
1371
- fsharp: "dotnet tool install -g fsautocomplete",
1372
- "sourcekit-lsp": "Included with Xcode or Swift toolchain",
1373
- rust: "Install rust-analyzer and ensure it is in PATH. If using rustup: rustup component add rust-analyzer. If rust-analyzer exits while loading rust-src: rustup component remove rust-src && rustup component add rust-src.",
1374
- clangd: "See https://clangd.llvm.org/installation",
1375
- svelte: "npm install -g svelte-language-server",
1376
- astro: "npm install -g @astrojs/language-server",
1377
- "bash-ls": "npm install -g bash-language-server",
1378
- jdtls: "See https://github.com/eclipse-jdtls/eclipse.jdt.ls",
1379
- "yaml-ls": "npm install -g yaml-language-server",
1380
- "lua-ls": "See https://github.com/LuaLS/lua-language-server",
1381
- php: "npm install -g intelephense",
1382
- dart: "Included with Dart SDK",
1383
- "terraform-ls": "See https://github.com/hashicorp/terraform-ls",
1384
- terraform: "See https://github.com/hashicorp/terraform-ls",
1385
- prisma: "npm install -g prisma",
1386
- "ocaml-lsp": "opam install ocaml-lsp-server",
1387
- texlab: "See https://github.com/latex-lsp/texlab",
1388
- dockerfile: "npm install -g dockerfile-language-server-nodejs",
1389
- gleam: "See https://gleam.run/getting-started/installing/",
1390
- "clojure-lsp": "See https://clojure-lsp.io/installation/",
1391
- nixd: "nix profile install nixpkgs#nixd",
1392
- tinymist: "See https://github.com/Myriad-Dreamin/tinymist",
1393
- "haskell-language-server": "ghcup install hls",
1394
- bash: "npm install -g bash-language-server",
1395
- "kotlin-ls": "See https://github.com/Kotlin/kotlin-lsp",
1396
- julials: "julia -e 'using Pkg; Pkg.add(\"LanguageServer\")'",
1397
- razor: "Razor runs through the Roslyn language server (cohosting). Install: dotnet tool install -g roslyn-language-server --prerelease (requires v5.8.0+). See https://github.com/dotnet/razor"
1398
- };
1399
- var BUILTIN_SERVERS = {
1400
- typescript: {
1401
- command: ["typescript-language-server", "--stdio"],
1402
- extensions: [
1403
- ".ts",
1404
- ".tsx",
1405
- ".js",
1406
- ".jsx",
1407
- ".mjs",
1408
- ".cjs",
1409
- ".mts",
1410
- ".cts"
1411
- ]
1412
- },
1413
- deno: {
1414
- command: ["deno", "lsp"],
1415
- extensions: [
1416
- ".ts",
1417
- ".tsx",
1418
- ".js",
1419
- ".jsx",
1420
- ".mjs"
1421
- ]
1422
- },
1423
- vue: {
1424
- command: ["vue-language-server", "--stdio"],
1425
- extensions: [".vue"]
1426
- },
1427
- eslint: {
1428
- command: ["vscode-eslint-language-server", "--stdio"],
1429
- extensions: [
1430
- ".ts",
1431
- ".tsx",
1432
- ".js",
1433
- ".jsx",
1434
- ".mjs",
1435
- ".cjs",
1436
- ".mts",
1437
- ".cts",
1438
- ".vue"
1439
- ]
1440
- },
1441
- oxlint: {
1442
- command: ["oxlint", "--lsp"],
1443
- extensions: [
1444
- ".ts",
1445
- ".tsx",
1446
- ".js",
1447
- ".jsx",
1448
- ".mjs",
1449
- ".cjs",
1450
- ".mts",
1451
- ".cts",
1452
- ".vue",
1453
- ".astro",
1454
- ".svelte"
1455
- ]
1456
- },
1457
- biome: {
1458
- command: [
1459
- "biome",
1460
- "lsp-proxy",
1461
- "--stdio"
1462
- ],
1463
- extensions: [
1464
- ".ts",
1465
- ".tsx",
1466
- ".js",
1467
- ".jsx",
1468
- ".mjs",
1469
- ".cjs",
1470
- ".mts",
1471
- ".cts",
1472
- ".json",
1473
- ".jsonc",
1474
- ".vue",
1475
- ".astro",
1476
- ".svelte",
1477
- ".css",
1478
- ".graphql",
1479
- ".gql",
1480
- ".html"
1481
- ]
1482
- },
1483
- gopls: {
1484
- command: ["gopls"],
1485
- extensions: [".go"]
1486
- },
1487
- "ruby-lsp": {
1488
- command: ["rubocop", "--lsp"],
1489
- extensions: [
1490
- ".rb",
1491
- ".rake",
1492
- ".gemspec",
1493
- ".ru"
1494
- ]
1495
- },
1496
- basedpyright: {
1497
- command: ["basedpyright-langserver", "--stdio"],
1498
- extensions: [".py", ".pyi"]
1499
- },
1500
- pyright: {
1501
- command: ["pyright-langserver", "--stdio"],
1502
- extensions: [".py", ".pyi"]
1503
- },
1504
- ty: {
1505
- command: ["ty", "server"],
1506
- extensions: [".py", ".pyi"]
1507
- },
1508
- ruff: {
1509
- command: ["ruff", "server"],
1510
- extensions: [".py", ".pyi"]
1511
- },
1512
- "elixir-ls": {
1513
- command: ["elixir-ls"],
1514
- extensions: [".ex", ".exs"]
1515
- },
1516
- zls: {
1517
- command: ["zls"],
1518
- extensions: [".zig", ".zon"]
1519
- },
1520
- csharp: {
1521
- command: ["csharp-ls"],
1522
- extensions: [".cs"]
1523
- },
1524
- fsharp: {
1525
- command: ["fsautocomplete"],
1526
- extensions: [
1527
- ".fs",
1528
- ".fsi",
1529
- ".fsx",
1530
- ".fsscript"
1531
- ]
1532
- },
1533
- "sourcekit-lsp": {
1534
- command: ["sourcekit-lsp"],
1535
- extensions: [
1536
- ".swift",
1537
- ".m",
1538
- ".mm"
1539
- ]
1540
- },
1541
- rust: {
1542
- command: ["rust-analyzer"],
1543
- extensions: [".rs"]
1544
- },
1545
- clangd: {
1546
- command: [
1547
- "clangd",
1548
- "--background-index",
1549
- "--clang-tidy"
1550
- ],
1551
- extensions: [
1552
- ".c",
1553
- ".cpp",
1554
- ".cc",
1555
- ".cxx",
1556
- ".c++",
1557
- ".h",
1558
- ".hpp",
1559
- ".hh",
1560
- ".hxx",
1561
- ".h++"
1562
- ]
1563
- },
1564
- svelte: {
1565
- command: ["svelteserver", "--stdio"],
1566
- extensions: [".svelte"]
1567
- },
1568
- astro: {
1569
- command: ["astro-ls", "--stdio"],
1570
- extensions: [".astro"]
1571
- },
1572
- bash: {
1573
- command: ["bash-language-server", "start"],
1574
- extensions: [
1575
- ".sh",
1576
- ".bash",
1577
- ".zsh",
1578
- ".ksh"
1579
- ]
1580
- },
1581
- "bash-ls": {
1582
- command: ["bash-language-server", "start"],
1583
- extensions: [
1584
- ".sh",
1585
- ".bash",
1586
- ".zsh",
1587
- ".ksh"
1588
- ]
1589
- },
1590
- jdtls: {
1591
- command: ["jdtls"],
1592
- extensions: [".java"]
1593
- },
1594
- "yaml-ls": {
1595
- command: ["yaml-language-server", "--stdio"],
1596
- extensions: [".yaml", ".yml"]
1597
- },
1598
- "lua-ls": {
1599
- command: ["lua-language-server"],
1600
- extensions: [".lua"]
1601
- },
1602
- php: {
1603
- command: ["intelephense", "--stdio"],
1604
- extensions: [".php"]
1605
- },
1606
- dart: {
1607
- command: [
1608
- "dart",
1609
- "language-server",
1610
- "--lsp"
1611
- ],
1612
- extensions: [".dart"]
1613
- },
1614
- terraform: {
1615
- command: ["terraform-ls", "serve"],
1616
- extensions: [".tf", ".tfvars"]
1617
- },
1618
- "terraform-ls": {
1619
- command: ["terraform-ls", "serve"],
1620
- extensions: [".tf", ".tfvars"]
1621
- },
1622
- prisma: {
1623
- command: ["prisma", "language-server"],
1624
- extensions: [".prisma"]
1625
- },
1626
- "ocaml-lsp": {
1627
- command: ["ocamllsp"],
1628
- extensions: [".ml", ".mli"]
1629
- },
1630
- texlab: {
1631
- command: ["texlab"],
1632
- extensions: [".tex", ".bib"]
1633
- },
1634
- dockerfile: {
1635
- command: ["docker-langserver", "--stdio"],
1636
- extensions: [".dockerfile"]
1637
- },
1638
- gleam: {
1639
- command: ["gleam", "lsp"],
1640
- extensions: [".gleam"]
1641
- },
1642
- "clojure-lsp": {
1643
- command: ["clojure-lsp", "listen"],
1644
- extensions: [
1645
- ".clj",
1646
- ".cljs",
1647
- ".cljc",
1648
- ".edn"
1649
- ]
1650
- },
1651
- nixd: {
1652
- command: ["nixd"],
1653
- extensions: [".nix"]
1654
- },
1655
- tinymist: {
1656
- command: ["tinymist"],
1657
- extensions: [".typ", ".typc"]
1658
- },
1659
- "haskell-language-server": {
1660
- command: ["haskell-language-server-wrapper", "--lsp"],
1661
- extensions: [".hs", ".lhs"]
1662
- },
1663
- "kotlin-ls": {
1664
- command: ["kotlin-lsp", "--stdio"],
1665
- extensions: [".kt", ".kts"]
1666
- },
1667
- julials: {
1668
- command: [
1669
- "julia",
1670
- "--startup-file=no",
1671
- "--history-file=no",
1672
- "-e",
1673
- "using LanguageServer; runserver()"
1674
- ],
1675
- extensions: [".jl"]
1676
- },
1677
- razor: {
1678
- command: ["roslyn-language-server", "--stdio"],
1679
- extensions: [".razor", ".cshtml"]
1680
- }
1681
- };
1682
- //#endregion
1683
- //#region packages/lsp-core/src/lsp/config-loader.ts
1684
- function resolveProjectConfigPath(path) {
1685
- return isAbsolute(path) ? path : join(contextCwd(), path);
1686
- }
1687
- function getProjectConfigPaths() {
1688
- const projectOverride = contextEnv("LSP_TOOLS_MCP_PROJECT_CONFIG");
1689
- if (projectOverride) return projectOverride.split(delimiter).filter(Boolean).map(resolveProjectConfigPath);
1690
- return [join(contextCwd(), ".codex", "lsp-client.json")];
1691
- }
1692
- function getUserConfigPath() {
1693
- const userOverride = contextEnv("LSP_TOOLS_MCP_USER_CONFIG");
1694
- if (!userOverride) return join(homedir(), ".codex", "lsp-client.json");
1695
- return isAbsolute(userOverride) ? userOverride : join(homedir(), userOverride);
1696
- }
1697
- function loadJsonFile(path) {
1698
- if (!existsSync(path)) return null;
1699
- try {
1700
- const parsed = JSON.parse(readFileSync(path, "utf-8"));
1701
- return isConfigJson(parsed) ? parsed : null;
1702
- } catch {
1703
- return null;
1704
- }
1705
- }
1706
- function loadAllConfigs() {
1707
- const configs = /* @__PURE__ */ new Map();
1708
- const project = loadFirstJsonFile(getProjectConfigPaths());
1709
- if (project) configs.set("project", project);
1710
- const user = loadJsonFile(getUserConfigPath());
1711
- if (user) configs.set("user", user);
1712
- return configs;
1713
- }
1714
- function loadFirstJsonFile(paths) {
1715
- for (const path of paths) {
1716
- const config = loadJsonFile(path);
1717
- if (config) return config;
1718
- }
1719
- return null;
1720
- }
1721
- function getMergedServers() {
1722
- const configs = loadAllConfigs();
1723
- const servers = [];
1724
- const disabled = /* @__PURE__ */ new Set();
1725
- const seen = /* @__PURE__ */ new Set();
1726
- for (const source of ["project", "user"]) {
1727
- const config = configs.get(source);
1728
- if (!config?.lsp) continue;
1729
- for (const [id, rawEntry] of Object.entries(config.lsp)) {
1730
- const entry = parseLspEntry(rawEntry);
1731
- if (!entry) continue;
1732
- if (entry.disabled) {
1733
- disabled.add(id);
1734
- continue;
1735
- }
1736
- if (seen.has(id)) continue;
1737
- const server = createServerFromEntry(id, entry, source);
1738
- if (!server) continue;
1739
- servers.push(server);
1740
- seen.add(id);
1741
- }
1742
- }
1743
- for (const [id, config] of Object.entries(BUILTIN_SERVERS)) {
1744
- if (disabled.has(id) || seen.has(id)) continue;
1745
- servers.push({
1746
- id,
1747
- command: config.command,
1748
- extensions: config.extensions,
1749
- priority: -100,
1750
- source: "builtin"
1751
- });
1752
- }
1753
- return servers.sort((a, b) => {
1754
- if (a.source !== b.source) {
1755
- const order = {
1756
- project: 0,
1757
- user: 1,
1758
- builtin: 2
1759
- };
1760
- return order[a.source] - order[b.source];
1761
- }
1762
- return b.priority - a.priority;
1763
- });
1764
- }
1765
- function createServerFromEntry(id, entry, source) {
1766
- const builtin = BUILTIN_SERVERS[id];
1767
- if (source === "project") {
1768
- if (!builtin) return null;
1769
- const server = createServer$1({
1770
- id,
1771
- command: builtin.command,
1772
- extensions: entry.extensions ?? builtin.extensions,
1773
- priority: entry.priority ?? 0,
1774
- source
1775
- });
1776
- if (entry.initialization !== void 0) server.initialization = entry.initialization;
1777
- return server;
1778
- }
1779
- if (entry.command && entry.extensions) {
1780
- const server = createServer$1({
1781
- id,
1782
- command: entry.command,
1783
- extensions: entry.extensions,
1784
- priority: entry.priority ?? 0,
1785
- source
1786
- });
1787
- applyOptionalServerFields(server, entry);
1788
- return server;
1789
- }
1790
- if (!builtin) return null;
1791
- const server = createServer$1({
1792
- id,
1793
- command: entry.command ?? builtin.command,
1794
- extensions: entry.extensions ?? builtin.extensions,
1795
- priority: entry.priority ?? 0,
1796
- source
1797
- });
1798
- applyOptionalServerFields(server, entry);
1799
- return server;
1800
- }
1801
- function createServer$1(input) {
1802
- const server = {
1803
- id: input.id,
1804
- command: input.command,
1805
- extensions: input.extensions,
1806
- priority: input.priority,
1807
- source: input.source
1808
- };
1809
- if (input.env !== void 0) server.env = input.env;
1810
- if (input.initialization !== void 0) server.initialization = input.initialization;
1811
- return server;
1812
- }
1813
- function applyOptionalServerFields(server, entry) {
1814
- if (entry.env !== void 0) server.env = entry.env;
1815
- if (entry.initialization !== void 0) server.initialization = entry.initialization;
1816
- }
1817
- function isConfigJson(value) {
1818
- if (!isPlainRecord(value)) return false;
1819
- const lsp = value["lsp"];
1820
- return lsp === void 0 || isPlainRecord(lsp);
1821
- }
1822
- function parseLspEntry(value) {
1823
- return isLspEntry(value) ? value : null;
1824
- }
1825
- function isLspEntry(value) {
1826
- if (!isPlainRecord(value)) return false;
1827
- const disabled = value["disabled"];
1828
- const command = value["command"];
1829
- const extensions = value["extensions"];
1830
- const priority = value["priority"];
1831
- const env = value["env"];
1832
- const initialization = value["initialization"];
1833
- return (disabled === void 0 || typeof disabled === "boolean") && (command === void 0 || isStringArray(command)) && (extensions === void 0 || isStringArray(extensions)) && (priority === void 0 || typeof priority === "number") && (env === void 0 || isStringRecord$1(env)) && (initialization === void 0 || isPlainRecord(initialization));
1834
- }
1835
- function isStringArray(value) {
1836
- return Array.isArray(value) && value.every((item) => typeof item === "string");
1837
- }
1838
- function isStringRecord$1(value) {
1839
- return isPlainRecord(value) && Object.values(value).every((item) => typeof item === "string");
1840
- }
1841
- function getDisabledServerIds() {
1842
- const configs = loadAllConfigs();
1843
- const disabled = /* @__PURE__ */ new Set();
1844
- for (const config of configs.values()) {
1845
- if (!config.lsp) continue;
1846
- for (const [id, rawEntry] of Object.entries(config.lsp)) {
1847
- const entry = parseLspEntry(rawEntry);
1848
- if (!entry) continue;
1849
- if (entry.disabled) disabled.add(id);
1850
- }
1851
- }
1852
- return disabled;
1853
- }
1854
- //#endregion
1855
- //#region packages/lsp-core/src/lsp/server-installation.ts
1856
- function isServerInstalled(command, _workingDirectory) {
1857
- if (command.length === 0) return false;
1858
- const [cmd] = command;
1859
- if (!cmd) return false;
1860
- if (cmd.includes("/") || cmd.includes("\\")) {
1861
- if (existsSync(cmd)) return true;
1862
- }
1863
- const isWindows = process.platform === "win32";
1864
- let exts = [""];
1865
- if (isWindows) {
1866
- const pathExt = process.env["PATHEXT"] ?? "";
1867
- if (pathExt) {
1868
- const systemExts = pathExt.split(";").filter(Boolean);
1869
- exts = [...new Set([
1870
- ...exts,
1871
- ...systemExts,
1872
- ".exe",
1873
- ".cmd",
1874
- ".bat",
1875
- ".ps1"
1876
- ])];
1877
- } else exts = [
1878
- "",
1879
- ".exe",
1880
- ".cmd",
1881
- ".bat",
1882
- ".ps1"
1883
- ];
1884
- }
1885
- let pathEnv = process.env["PATH"] ?? "";
1886
- if (isWindows && !pathEnv) pathEnv = process.env["Path"] ?? "";
1887
- const paths = pathEnv.split(delimiter);
1888
- for (const p of paths) for (const suffix of exts) if (existsSync(join(p, cmd + suffix))) return true;
1889
- if (cmd === "node") return true;
1890
- return false;
1891
- }
1892
- //#endregion
1893
- //#region packages/lsp-core/src/lsp/server-resolution.ts
1894
- function findServerForExtension(ext) {
1895
- const servers = getMergedServers();
1896
- for (const server of servers) if (server.extensions.includes(ext) && isServerInstalled(server.command)) {
1897
- const resolvedServer = {
1898
- id: server.id,
1899
- command: server.command,
1900
- extensions: server.extensions,
1901
- priority: server.priority
1902
- };
1903
- if (server.env !== void 0) return {
1904
- status: "found",
1905
- server: {
1906
- ...resolvedServer,
1907
- env: server.env,
1908
- ...server.initialization === void 0 ? {} : { initialization: server.initialization }
1909
- }
1910
- };
1911
- return {
1912
- status: "found",
1913
- server: {
1914
- ...resolvedServer,
1915
- ...server.initialization === void 0 ? {} : { initialization: server.initialization }
1916
- }
1917
- };
1918
- }
1919
- for (const server of servers) if (server.extensions.includes(ext)) {
1920
- const installHint = LSP_INSTALL_HINTS[server.id] ?? `Install '${server.command[0]}' and ensure it's in your PATH`;
1921
- return {
1922
- status: "not_installed",
1923
- server: {
1924
- id: server.id,
1925
- command: server.command,
1926
- extensions: server.extensions
1927
- },
1928
- installHint
1929
- };
1930
- }
1931
- return {
1932
- status: "not_configured",
1933
- extension: ext,
1934
- availableServers: [...new Set(servers.map((s) => s.id))]
1935
- };
1936
- }
1937
- function getAllServers() {
1938
- const servers = getMergedServers();
1939
- const disabled = getDisabledServerIds();
1940
- const result = [];
1941
- const seen = /* @__PURE__ */ new Set();
1942
- for (const server of servers) {
1943
- if (seen.has(server.id)) continue;
1944
- result.push({
1945
- id: server.id,
1946
- installed: isServerInstalled(server.command),
1947
- extensions: server.extensions,
1948
- disabled: false,
1949
- source: server.source,
1950
- priority: server.priority
1951
- });
1952
- seen.add(server.id);
1953
- }
1954
- for (const id of disabled) {
1955
- if (seen.has(id)) continue;
1956
- const builtin = BUILTIN_SERVERS[id];
1957
- result.push({
1958
- id,
1959
- installed: builtin ? isServerInstalled(builtin.command) : false,
1960
- extensions: builtin?.extensions ?? [],
1961
- disabled: true,
1962
- source: "disabled",
1963
- priority: 0
1964
- });
1965
- }
1966
- return result;
1967
- }
1968
- //#endregion
1969
- //#region packages/lsp-core/src/lsp/client-wrapper.ts
1970
- var WORKSPACE_MARKERS = [
1971
- ".git",
1972
- "package.json",
1973
- "pyproject.toml",
1974
- "Cargo.toml",
1975
- "go.mod",
1976
- "pom.xml",
1977
- "build.gradle"
1978
- ];
1979
- function isDirectoryPath(filePath) {
1980
- try {
1981
- return statSync(filePath).isDirectory();
1982
- } catch {
1983
- return false;
1984
- }
1985
- }
1986
- function findWorkspaceRoot(filePath) {
1987
- const abs = resolve(contextCwd(), filePath);
1988
- let dir = abs;
1989
- if (!isDirectoryPath(dir)) dir = dirname(dir);
1990
- let prevDir = "";
1991
- while (dir !== prevDir) {
1992
- for (const marker of WORKSPACE_MARKERS) if (existsSync(join(dir, marker))) return dir;
1993
- prevDir = dir;
1994
- dir = dirname(dir);
1995
- }
1996
- return dirname(abs);
1997
- }
1998
- function formatServerLookupError(result) {
1999
- if (result.status === "not_installed") return formatNotInstalled(result);
2000
- return [
2001
- `No LSP server configured for extension: ${result.extension}`,
2002
- "",
2003
- `Available servers: ${result.availableServers.slice(0, 10).join(", ")}${result.availableServers.length > 10 ? "..." : ""}`,
2004
- "",
2005
- "Configure a custom server in '.codex/lsp-client.json':",
2006
- " {",
2007
- " \"lsp\": {",
2008
- " \"my-server\": {",
2009
- " \"command\": [\"my-lsp\", \"--stdio\"],",
2010
- ` "extensions": ["${result.extension}"]`,
2011
- " }",
2012
- " }",
2013
- " }"
2014
- ].join("\n");
2015
- }
2016
- function formatNotInstalled(result) {
2017
- const { server, installHint } = result;
2018
- const extensions = server.extensions.join(", ");
2019
- const decision = loadInstallDecision(server.id)?.decision;
2020
- if (decision === "declined") return `LSP server '${server.id}' (${extensions}) is NOT INSTALLED; user previously declined installation — proceed without LSP.`;
2021
- const header = [
2022
- `LSP server '${server.id}' for ${extensions} is NOT INSTALLED.`,
2023
- "",
2024
- `Command not found: ${server.command[0]}`,
2025
- ""
2026
- ];
2027
- if (decision === "allowed") return [
2028
- ...header,
2029
- "The user has pre-authorized LSP installation. Run the install command, then retry this tool:",
2030
- ` ${installHint}`
2031
- ].join("\n");
2032
- return [
2033
- ...header,
2034
- "To install, run:",
2035
- ` ${installHint}`,
2036
- "",
2037
- "ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
2038
- "- If the user agrees: run the install command above, then retry this tool.",
2039
- "- If the user declines, OR has NOT explicitly asked for LSP installation:",
2040
- ` call lsp_install_decision { server_id: "${server.id}", decision: "declined" },`,
2041
- " then ignore this message and proceed WITHOUT LSP."
2042
- ].join("\n");
2043
- }
2044
- var READ_ONLY_RETRY_TOOLS = new Set([
2045
- "diagnostics",
2046
- "definition",
2047
- "references",
2048
- "documentSymbols",
2049
- "workspaceSymbols",
2050
- "prepareRename"
2051
- ]);
2052
- async function withLspClient(filePath, fn, toolName, options = {}) {
2053
- const absPath = resolve(contextCwd(), filePath);
2054
- if (isDirectoryPath(absPath)) throw new LspInvalidPathError("Directory paths are not supported by this LSP tool. Use lsp.diagnostics with a directory path for directory diagnostics.");
2055
- const result = findServerForExtension(effectiveExtension(absPath));
2056
- if (result.status !== "found") throw new LspServerLookupError(formatServerLookupError(result));
2057
- const server = result.server;
2058
- const root = findWorkspaceRoot(absPath);
2059
- const manager = options.manager ?? getLspManager();
2060
- const acquireAndCall = async (allowRetry) => {
2061
- const client = await manager.getClient(root, server, options.signal);
2062
- try {
2063
- return await fn(client, root);
2064
- } catch (err) {
2065
- if (allowRetry && READ_ONLY_RETRY_TOOLS.has(toolName) && isLspDeadConnectionError(err)) {
2066
- manager.invalidateClient(root, server.id, client);
2067
- return acquireAndCall(false);
2068
- }
2069
- if (err instanceof LspRequestTimeoutError) {
2070
- if (manager.isServerInitializing(root, server.id)) throw new LspServerInitializingError(err);
2071
- }
2072
- throw err;
2073
- } finally {
2074
- manager.releaseClient(root, server.id);
2075
- }
2076
- };
2077
- return acquireAndCall(true);
2078
- }
2079
- //#endregion
2080
- //#region packages/lsp-core/src/lsp/formatters.ts
2081
- var DIAGNOSTIC_SEVERITY_FILTERS = {
2082
- error: 1,
2083
- warning: 2,
2084
- information: 3,
2085
- hint: 4
2086
- };
2087
- function uriToPath(uri) {
2088
- return fileURLToPath(uri);
2089
- }
2090
- function formatLocation(loc) {
2091
- if ("targetUri" in loc) return `${uriToPath(loc.targetUri)}:${loc.targetRange.start.line + 1}:${loc.targetRange.start.character}`;
2092
- return `${uriToPath(loc.uri)}:${loc.range.start.line + 1}:${loc.range.start.character}`;
2093
- }
2094
- function formatSymbolKind(kind) {
2095
- return SYMBOL_KIND_MAP[kind] ?? `Unknown(${kind})`;
2096
- }
2097
- function formatSeverity(severity) {
2098
- if (!severity) return "unknown";
2099
- return SEVERITY_MAP[severity] ?? `unknown(${severity})`;
2100
- }
2101
- function formatDocumentSymbol(symbol, indent = 0) {
2102
- const prefix = " ".repeat(indent);
2103
- const kind = formatSymbolKind(symbol.kind);
2104
- const line = symbol.range.start.line + 1;
2105
- let result = `${prefix}${symbol.name} (${kind}) - line ${line}`;
2106
- if (symbol.children && symbol.children.length > 0) for (const child of symbol.children) result += `\n${formatDocumentSymbol(child, indent + 1)}`;
2107
- return result;
2108
- }
2109
- function formatSymbolInfo(symbol) {
2110
- const kind = formatSymbolKind(symbol.kind);
2111
- const loc = formatLocation(symbol.location);
2112
- const container = symbol.containerName ? ` (in ${symbol.containerName})` : "";
2113
- return `${symbol.name} (${kind})${container} - ${loc}`;
2114
- }
2115
- function formatDiagnostic(diag) {
2116
- const severity = formatSeverity(diag.severity);
2117
- const line = diag.range.start.line + 1;
2118
- const char = diag.range.start.character;
2119
- return `${severity}${diag.source ? `[${diag.source}]` : ""}${diag.code ? ` (${diag.code})` : ""} at ${line}:${char}: ${diag.message}`;
2120
- }
2121
- function filterDiagnosticsBySeverity(diagnostics, severityFilter) {
2122
- if (!severityFilter || severityFilter === "all") return diagnostics;
2123
- const targetSeverity = DIAGNOSTIC_SEVERITY_FILTERS[severityFilter];
2124
- return diagnostics.filter((d) => d.severity === targetSeverity);
2125
- }
2126
- function formatPrepareRenameResult(result) {
2127
- if (!result) return "Cannot rename at this position";
2128
- if ("defaultBehavior" in result) return result.defaultBehavior ? "Rename supported (using default behavior)" : "Cannot rename at this position";
2129
- if ("range" in result && result.range) return `Rename available at ${result.range.start.line + 1}:${result.range.start.character}-${result.range.end.line + 1}:${result.range.end.character}${result.placeholder ? ` (current: "${result.placeholder}")` : ""}`;
2130
- if ("start" in result && "end" in result) return `Rename available at ${result.start.line + 1}:${result.start.character}-${result.end.line + 1}:${result.end.character}`;
2131
- return "Cannot rename at this position";
2132
- }
2133
- function formatApplyResult(result) {
2134
- const lines = [];
2135
- if (result.success) {
2136
- lines.push(`Applied ${result.totalEdits} edit(s) to ${result.filesModified.length} file(s):`);
2137
- for (const file of result.filesModified) lines.push(` - ${file}`);
2138
- } else {
2139
- lines.push("Failed to apply some changes:");
2140
- for (const err of result.errors) lines.push(` Error: ${err}`);
2141
- if (result.filesModified.length > 0) lines.push(`Successfully modified: ${result.filesModified.join(", ")}`);
2142
- }
2143
- return lines.join("\n");
2144
- }
2145
- //#endregion
2146
- //#region packages/lsp-core/src/lsp/directory-diagnostics.ts
2147
- var SKIP_DIRECTORIES$1 = new Set([
2148
- "node_modules",
2149
- ".git",
2150
- "dist",
2151
- "build",
2152
- ".next",
2153
- "out"
2154
- ]);
2155
- function collectFilesWithExtension(dir, extension, maxFiles) {
2156
- const files = [];
2157
- function walk(currentDir) {
2158
- if (files.length >= maxFiles) return;
2159
- let entries = [];
2160
- try {
2161
- entries = readdirSync(currentDir);
2162
- } catch {
2163
- return;
2164
- }
2165
- for (const entry of entries) {
2166
- if (files.length >= maxFiles) return;
2167
- const fullPath = join(currentDir, entry);
2168
- let stat;
2169
- try {
2170
- stat = lstatSync(fullPath);
2171
- } catch {
2172
- continue;
2173
- }
2174
- if (!stat || stat.isSymbolicLink()) continue;
2175
- if (stat.isDirectory()) {
2176
- if (!SKIP_DIRECTORIES$1.has(entry)) walk(fullPath);
2177
- } else if (stat.isFile() && effectiveExtension(fullPath) === extension) files.push(fullPath);
2178
- }
2179
- }
2180
- walk(dir);
2181
- return files;
2182
- }
2183
- async function aggregateDiagnosticsForDirectory(directory, extension, severity, maxFiles = 50) {
2184
- if (!extension.startsWith(".")) throw new LspInvalidPathError(`Extension must start with a dot (e.g., ".ts", not "${extension}"). Use ".${extension}" instead.`);
2185
- const absDir = resolve(contextCwd(), directory);
2186
- if (!existsSync(absDir)) throw new LspInvalidPathError(`Directory does not exist: ${absDir}`);
2187
- const serverResult = findServerForExtension(extension);
2188
- if (serverResult.status !== "found") throw new LspServerLookupError(formatServerLookupError(serverResult));
2189
- const server = serverResult.server;
2190
- const allFiles = collectFilesWithExtension(absDir, extension, maxFiles + 1);
2191
- const wasCapped = allFiles.length > maxFiles;
2192
- const filesToProcess = allFiles.slice(0, maxFiles);
2193
- if (filesToProcess.length === 0) return [
2194
- `Directory: ${absDir}`,
2195
- `Extension: ${extension}`,
2196
- "Files scanned: 0",
2197
- `No files found with extension "${extension}".`
2198
- ].join("\n");
2199
- const root = findWorkspaceRoot(absDir);
2200
- const manager = getLspManager();
2201
- const allDiagnostics = [];
2202
- const fileErrors = [];
2203
- const client = await manager.getClient(root, server);
2204
- try {
2205
- for (const file of filesToProcess) try {
2206
- const filtered = filterDiagnosticsBySeverity((await client.diagnostics(file)).items, severity);
2207
- allDiagnostics.push(...filtered.map((diagnostic) => ({
2208
- filePath: file,
2209
- diagnostic
2210
- })));
2211
- } catch (e) {
2212
- fileErrors.push({
2213
- file,
2214
- error: e instanceof Error ? e.message : String(e)
2215
- });
2216
- }
2217
- } finally {
2218
- manager.releaseClient(root, server.id);
2219
- }
2220
- const displayDiagnostics = allDiagnostics.slice(0, 200);
2221
- const wasDiagCapped = allDiagnostics.length > 200;
2222
- const lines = [
2223
- `Directory: ${absDir}`,
2224
- `Extension: ${extension}`,
2225
- `Files scanned: ${filesToProcess.length}${wasCapped ? ` (capped at ${maxFiles})` : ""}`,
2226
- `Files with errors: ${fileErrors.length}`,
2227
- `Total diagnostics: ${allDiagnostics.length}`
2228
- ];
2229
- if (fileErrors.length > 0) {
2230
- lines.push("", "File processing errors:");
2231
- for (const { file, error } of fileErrors) lines.push(` ${file}: ${error}`);
2232
- }
2233
- if (displayDiagnostics.length > 0) {
2234
- lines.push("");
2235
- for (const { filePath, diagnostic } of displayDiagnostics) lines.push(`${filePath}: ${formatDiagnostic(diagnostic)}`);
2236
- if (wasDiagCapped) lines.push("", `... (${allDiagnostics.length - 200} more diagnostics not shown)`);
2237
- }
2238
- return lines.join("\n");
2239
- }
2240
- //#endregion
2241
- //#region packages/lsp-core/src/lsp/infer-extension.ts
2242
- var SKIP_DIRECTORIES = new Set([
2243
- "node_modules",
2244
- ".git",
2245
- "dist",
2246
- "build",
2247
- ".next",
2248
- "out"
2249
- ]);
2250
- var MAX_SCAN_ENTRIES = 500;
2251
- function inferExtensionFromDirectory(directory) {
2252
- const extensionCounts = /* @__PURE__ */ new Map();
2253
- let scanned = 0;
2254
- function walk(dir) {
2255
- if (scanned >= MAX_SCAN_ENTRIES) return;
2256
- let entries;
2257
- try {
2258
- entries = readdirSync(dir);
2259
- } catch {
2260
- return;
2261
- }
2262
- for (const entry of entries) {
2263
- if (scanned >= MAX_SCAN_ENTRIES) return;
2264
- const fullPath = join(dir, entry);
2265
- let stat;
2266
- try {
2267
- stat = lstatSync(fullPath);
2268
- } catch {
2269
- continue;
2270
- }
2271
- if (stat.isSymbolicLink()) continue;
2272
- scanned++;
2273
- if (stat.isDirectory()) {
2274
- if (!SKIP_DIRECTORIES.has(entry)) walk(fullPath);
2275
- } else if (stat.isFile()) {
2276
- const ext = effectiveExtension(fullPath);
2277
- if (ext && ext in EXT_TO_LANG) extensionCounts.set(ext, (extensionCounts.get(ext) ?? 0) + 1);
2278
- }
2279
- }
2280
- }
2281
- walk(directory);
2282
- if (extensionCounts.size === 0) return null;
2283
- let maxExt = "";
2284
- let maxCount = 0;
2285
- for (const [ext, count] of extensionCounts) if (count > maxCount) {
2286
- maxCount = count;
2287
- maxExt = ext;
2288
- }
2289
- return maxExt || null;
2290
- }
2291
- //#endregion
2292
- //#region packages/lsp-core/src/lsp/utils.ts
2293
- var RUST_SRC_REPAIR_MESSAGE = [
2294
- "rust-analyzer exited while loading Rust standard library sources.",
2295
- "",
2296
- "Repair rust-src for the active toolchain:",
2297
- " rustup component remove rust-src",
2298
- " rustup component add rust-src"
2299
- ];
2300
- var errorMessage = messageFromError;
2301
- function formatKnownLspStartupFailure(error) {
2302
- if (!(error instanceof LspProcessExitedError)) return null;
2303
- if (error.serverId !== "rust") return null;
2304
- const details = error.stderrTail ?? error.message;
2305
- const lowerDetails = details.toLowerCase();
2306
- if (!(lowerDetails.includes("rust-src") && (lowerDetails.includes("failed to install component") || lowerDetails.includes("detected conflict") || lowerDetails.includes("can't load standard library") || lowerDetails.includes("try installing") || lowerDetails.includes("sysroot")))) return null;
2307
- return [
2308
- ...RUST_SRC_REPAIR_MESSAGE,
2309
- "",
2310
- "Original stderr tail:",
2311
- details
2312
- ].join("\n");
2313
- }
2314
- function handleMissingDependencyError(error) {
2315
- const knownStartupFailure = formatKnownLspStartupFailure(error);
2316
- if (knownStartupFailure) return knownStartupFailure;
2317
- const message = errorMessage(error);
2318
- return message.includes("NOT INSTALLED") || message.includes("No LSP server configured") ? message : null;
2319
- }
2320
- //#endregion
2321
- //#region packages/lsp-core/src/missing-dependency-result.ts
2322
- function missingDependencyResult(error, details) {
2323
- const message = handleMissingDependencyError(error);
2324
- if (!message) return null;
2325
- return {
2326
- content: [{
2327
- type: "text",
2328
- text: message
2329
- }],
2330
- details: {
2331
- ...details,
2332
- error: message,
2333
- errorKind: "missing_dependency"
2334
- }
2335
- };
2336
- }
2337
- function missingDependencyResultOrThrow(error, details) {
2338
- const result = missingDependencyResult(error, details);
2339
- if (result !== null) return result;
2340
- throw error;
2341
- }
2342
- //#endregion
2343
- //#region packages/lsp-core/src/tools/parameters.ts
2344
- var isRecord = isPlainRecord;
2345
- function sourcePosition(params) {
2346
- return {
2347
- filePath: requireString(params, "filePath"),
2348
- line: requireNumber(params, "line"),
2349
- character: requireNumber(params, "character")
2350
- };
2351
- }
2352
- function requireString(params, key) {
2353
- const value = params[key];
2354
- if (typeof value !== "string" || value.length === 0) throw new Error(`Missing required string parameter '${key}'`);
2355
- return value;
2356
- }
2357
- function optionalString(params, key) {
2358
- const value = params[key];
2359
- return typeof value === "string" ? value : void 0;
2360
- }
2361
- function requireNumber(params, key) {
2362
- const value = params[key];
2363
- if (typeof value !== "number" || !Number.isFinite(value)) throw new Error(`Missing required number parameter '${key}'`);
2364
- return value;
2365
- }
2366
- function optionalNumber(params, key) {
2367
- const value = params[key];
2368
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
2369
- }
2370
- function optionalBoolean(params, key) {
2371
- const value = params[key];
2372
- return typeof value === "boolean" ? value : void 0;
2373
- }
2374
- function severityFilter(params) {
2375
- const value = params["severity"];
2376
- if (value === "error" || value === "warning" || value === "information" || value === "hint" || value === "all") return value;
2377
- return "all";
2378
- }
2379
- function clientOptions(signal) {
2380
- return signal === void 0 ? {} : { signal };
2381
- }
2382
- //#endregion
2383
- //#region packages/lsp-core/src/tools/result.ts
2384
- function text(text, details, isError = false) {
2385
- return {
2386
- content: [{
2387
- type: "text",
2388
- text
2389
- }],
2390
- details,
2391
- isError
2392
- };
2393
- }
2394
- //#endregion
2395
- //#region packages/lsp-core/src/tools/diagnostics.ts
2396
- function asDiagnosticArray(result) {
2397
- if (!result) return [];
2398
- if (Array.isArray(result)) return result;
2399
- return result.items ?? [];
2400
- }
2401
- async function executeLspDiagnostics(params, signal) {
2402
- const filePath = requireString(params, "filePath");
2403
- const severity = severityFilter(params);
2404
- try {
2405
- const absPath = resolve(contextCwd(), filePath);
2406
- if (isDirectoryPath(absPath)) {
2407
- const extension = inferExtensionFromDirectory(absPath);
2408
- if (!extension) {
2409
- const message = `No supported source files found in directory: ${absPath}`;
2410
- return text(message, {
2411
- filePath,
2412
- severity,
2413
- mode: "directory",
2414
- diagnostics: [],
2415
- totalDiagnostics: 0,
2416
- truncated: false,
2417
- error: message,
2418
- errorKind: "no_files"
2419
- });
2420
- }
2421
- return text(await aggregateDiagnosticsForDirectory(absPath, extension, severity), {
2422
- filePath,
2423
- severity,
2424
- mode: "directory",
2425
- diagnostics: [],
2426
- totalDiagnostics: 0,
2427
- truncated: false
2428
- });
2429
- }
2430
- const diagnostics = filterDiagnosticsBySeverity(asDiagnosticArray(await withLspClient(filePath, async (client) => client.diagnostics(filePath), "diagnostics", clientOptions(signal))), severity);
2431
- const total = diagnostics.length;
2432
- const truncated = total > 200;
2433
- const limited = truncated ? diagnostics.slice(0, 200) : diagnostics;
2434
- return text(total === 0 ? "No diagnostics found" : [...truncated ? [`Found ${total} diagnostics (showing first 200):`] : [], ...limited.map(formatDiagnostic)].join("\n"), {
2435
- filePath,
2436
- severity,
2437
- mode: "file",
2438
- diagnostics: diagnostics.map((diagnostic) => ({
2439
- file: absPath,
2440
- diagnostic
2441
- })),
2442
- totalDiagnostics: total,
2443
- truncated
2444
- });
2445
- } catch (error) {
2446
- return missingDependencyResultOrThrow(error, {
2447
- filePath,
2448
- severity,
2449
- mode: "file",
2450
- diagnostics: [],
2451
- totalDiagnostics: 0,
2452
- truncated: false
2453
- });
2454
- }
2455
- }
2456
- //#endregion
2457
- //#region packages/lsp-core/src/tools/install-decision.ts
2458
- async function executeLspInstallDecision(params) {
2459
- const serverId = requireString(params, "server_id");
2460
- const decision = params["decision"];
2461
- if (!isInstallDecision(decision)) return text(`Invalid decision '${String(decision)}'. Expected "declined" or "allowed".`, {
2462
- serverId,
2463
- errorKind: "invalid_decision"
2464
- }, true);
2465
- const serverIds = [...new Set(getMergedServers().map((server) => server.id))];
2466
- if (!serverIds.includes(serverId)) return text(`Unknown LSP server '${serverId}'. Known servers: ${serverIds.slice(0, 20).join(", ")}${serverIds.length > 20 ? "..." : ""}`, {
2467
- serverId,
2468
- errorKind: "unknown_server"
2469
- }, true);
2470
- recordInstallDecision(serverId, decision);
2471
- return text(`Recorded install decision for '${serverId}': ${decision}. ${decision === "declined" ? "Future LSP lookups for this server stay quiet; proceed without LSP." : "Future LSP lookups keep install instructions without asking the user."}`, {
2472
- serverId,
2473
- decision
2474
- });
2475
- }
2476
- //#endregion
2477
- //#region packages/lsp-core/src/tools/navigation.ts
2478
- async function executeLspGotoDefinition(params, signal) {
2479
- const { filePath, line, character } = sourcePosition(params);
2480
- try {
2481
- const result = await withLspClient(filePath, async (client) => client.definition(filePath, line, character), "definition", clientOptions(signal));
2482
- const locations = !result ? [] : Array.isArray(result) ? result : [result];
2483
- const details = {
2484
- filePath,
2485
- line,
2486
- character,
2487
- locations
2488
- };
2489
- if (locations.length === 0) return text("No definition found", details);
2490
- return text(locations.map(formatLocation).join("\n"), details);
2491
- } catch (error) {
2492
- return missingDependencyResultOrThrow(error, {
2493
- filePath,
2494
- line,
2495
- character,
2496
- locations: []
2497
- });
2498
- }
2499
- }
2500
- async function executeLspFindReferences(params, signal) {
2501
- const { filePath, line, character } = sourcePosition(params);
2502
- const includeDeclaration = optionalBoolean(params, "includeDeclaration") ?? true;
2503
- try {
2504
- const result = await withLspClient(filePath, async (client) => client.references(filePath, line, character, includeDeclaration), "references", clientOptions(signal));
2505
- const references = Array.isArray(result) ? result : [];
2506
- const total = references.length;
2507
- const truncated = total > 200;
2508
- const limited = truncated ? references.slice(0, 200) : references;
2509
- const details = {
2510
- filePath,
2511
- line,
2512
- character,
2513
- references,
2514
- totalReferences: total,
2515
- truncated
2516
- };
2517
- if (total === 0) return text("No references found", details);
2518
- return text([...truncated ? [`Found ${total} references (showing first 200):`] : [], ...limited.map(formatLocation)].join("\n"), details);
2519
- } catch (error) {
2520
- return missingDependencyResultOrThrow(error, {
2521
- filePath,
2522
- line,
2523
- character,
2524
- references: [],
2525
- totalReferences: 0,
2526
- truncated: false
2527
- });
2528
- }
2529
- }
2530
- //#endregion
2531
- //#region packages/lsp-core/src/lsp/workspace-edit.ts
2532
- function isPathInsideWorkspace(filePath, workspaceRoot) {
2533
- const relativePath = relative(workspaceRoot, filePath);
2534
- return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
2535
- }
2536
- function realpathForValidation(filePath) {
2537
- if (existsSync(filePath)) return realpathSync(filePath);
2538
- const parent = dirname(filePath);
2539
- return resolve(realpathSync(parent), relative(parent, filePath));
2540
- }
2541
- function uriToWorkspacePath(uri, workspaceRoot) {
2542
- let filePath;
2543
- try {
2544
- filePath = fileURLToPath(uri);
2545
- } catch (error) {
2546
- return {
2547
- success: false,
2548
- error: `non-file URI ${uri}: ${messageFromError(error)}`
2549
- };
2550
- }
2551
- let validatedPath;
2552
- try {
2553
- validatedPath = realpathForValidation(filePath);
2554
- } catch (error) {
2555
- return {
2556
- success: false,
2557
- error: `${filePath}: ${messageFromError(error)}`
2558
- };
2559
- }
2560
- if (!isPathInsideWorkspace(validatedPath, workspaceRoot)) return {
2561
- success: false,
2562
- error: `${filePath}: outside workspace ${workspaceRoot}`
2563
- };
2564
- return {
2565
- success: true,
2566
- path: filePath
2567
- };
2568
- }
2569
- function applyTextEditsToFile(filePath, edits) {
2570
- try {
2571
- const lines = readFileSync(filePath, "utf-8").split("\n");
2572
- const sortedEdits = [...edits].sort((a, b) => {
2573
- if (b.range.start.line !== a.range.start.line) return b.range.start.line - a.range.start.line;
2574
- return b.range.start.character - a.range.start.character;
2575
- });
2576
- for (const edit of sortedEdits) {
2577
- const startLine = edit.range.start.line;
2578
- const startChar = edit.range.start.character;
2579
- const endLine = edit.range.end.line;
2580
- const endChar = edit.range.end.character;
2581
- if (startLine === endLine) {
2582
- const line = lines[startLine] ?? "";
2583
- lines[startLine] = line.substring(0, startChar) + edit.newText + line.substring(endChar);
2584
- } else {
2585
- const firstLine = lines[startLine] ?? "";
2586
- const lastLine = lines[endLine] ?? "";
2587
- const newContent = firstLine.substring(0, startChar) + edit.newText + lastLine.substring(endChar);
2588
- lines.splice(startLine, endLine - startLine + 1, ...newContent.split("\n"));
2589
- }
2590
- }
2591
- writeFileSync(filePath, lines.join("\n"), "utf-8");
2592
- return {
2593
- success: true,
2594
- editCount: edits.length
2595
- };
2596
- } catch (err) {
2597
- return {
2598
- success: false,
2599
- editCount: 0,
2600
- error: err instanceof Error ? err.message : String(err)
2601
- };
2602
- }
2603
- }
2604
- function applyWorkspaceEdit(edit, options = {}) {
2605
- if (!edit) return {
2606
- success: false,
2607
- filesModified: [],
2608
- totalEdits: 0,
2609
- errors: ["No edit provided"]
2610
- };
2611
- const result = {
2612
- success: true,
2613
- filesModified: [],
2614
- totalEdits: 0,
2615
- errors: []
2616
- };
2617
- const workspaceRoot = realpathSync(options.workspaceRoot ?? contextCwd());
2618
- if (edit.changes) for (const [uri, edits] of Object.entries(edit.changes)) {
2619
- const validatedPath = uriToWorkspacePath(uri, workspaceRoot);
2620
- if (!validatedPath.success) {
2621
- result.success = false;
2622
- result.errors.push(validatedPath.error);
2623
- continue;
2624
- }
2625
- const applyResult = applyTextEditsToFile(validatedPath.path, edits);
2626
- if (applyResult.success) {
2627
- result.filesModified.push(validatedPath.path);
2628
- result.totalEdits += applyResult.editCount;
2629
- } else {
2630
- result.success = false;
2631
- result.errors.push(`${validatedPath.path}: ${applyResult.error}`);
2632
- }
2633
- }
2634
- if (edit.documentChanges) for (const change of edit.documentChanges) {
2635
- if (!("kind" in change)) {
2636
- const validatedPath = uriToWorkspacePath(change.textDocument.uri, workspaceRoot);
2637
- if (!validatedPath.success) {
2638
- result.success = false;
2639
- result.errors.push(validatedPath.error);
2640
- continue;
2641
- }
2642
- const applyResult = applyTextEditsToFile(validatedPath.path, change.edits);
2643
- if (applyResult.success) {
2644
- result.filesModified.push(validatedPath.path);
2645
- result.totalEdits += applyResult.editCount;
2646
- } else {
2647
- result.success = false;
2648
- result.errors.push(`${validatedPath.path}: ${applyResult.error}`);
2649
- }
2650
- continue;
2651
- }
2652
- if (change.kind === "create") try {
2653
- const validatedPath = uriToWorkspacePath(change.uri, workspaceRoot);
2654
- if (!validatedPath.success) {
2655
- result.success = false;
2656
- result.errors.push(`Create ${change.uri}: ${validatedPath.error}`);
2657
- continue;
2658
- }
2659
- writeFileSync(validatedPath.path, "", "utf-8");
2660
- result.filesModified.push(validatedPath.path);
2661
- } catch (err) {
2662
- result.success = false;
2663
- result.errors.push(`Create ${change.uri}: ${String(err)}`);
2664
- }
2665
- else if (change.kind === "rename") try {
2666
- const oldPath = uriToWorkspacePath(change.oldUri, workspaceRoot);
2667
- const newPath = uriToWorkspacePath(change.newUri, workspaceRoot);
2668
- if (!oldPath.success || !newPath.success) {
2669
- const error = oldPath.success ? newPath.success ? "invalid URI" : newPath.error : oldPath.error;
2670
- result.success = false;
2671
- result.errors.push(`Rename ${change.oldUri}: ${error}`);
2672
- continue;
2673
- }
2674
- const content = readFileSync(oldPath.path, "utf-8");
2675
- writeFileSync(newPath.path, content, "utf-8");
2676
- unlinkSync(oldPath.path);
2677
- result.filesModified.push(newPath.path);
2678
- } catch (err) {
2679
- result.success = false;
2680
- result.errors.push(`Rename ${change.oldUri}: ${String(err)}`);
2681
- }
2682
- else if (change.kind === "delete") try {
2683
- const validatedPath = uriToWorkspacePath(change.uri, workspaceRoot);
2684
- if (!validatedPath.success) {
2685
- result.success = false;
2686
- result.errors.push(`Delete ${change.uri}: ${validatedPath.error}`);
2687
- continue;
2688
- }
2689
- unlinkSync(validatedPath.path);
2690
- result.filesModified.push(validatedPath.path);
2691
- } catch (err) {
2692
- result.success = false;
2693
- result.errors.push(`Delete ${change.uri}: ${String(err)}`);
2694
- }
2695
- }
2696
- return result;
2697
- }
2698
- //#endregion
2699
- //#region packages/lsp-core/src/tools/rename.ts
2700
- async function executeLspPrepareRename(params, signal) {
2701
- const { filePath, line, character } = sourcePosition(params);
2702
- try {
2703
- const result = await withLspClient(filePath, async (client) => client.prepareRename(filePath, line, character), "prepareRename", clientOptions(signal));
2704
- const details = {
2705
- filePath,
2706
- line,
2707
- character,
2708
- result
2709
- };
2710
- return text(formatPrepareRenameResult(result), details);
2711
- } catch (error) {
2712
- return missingDependencyResultOrThrow(error, {
2713
- filePath,
2714
- line,
2715
- character,
2716
- result: null
2717
- });
2718
- }
2719
- }
2720
- async function executeLspRename(params, signal) {
2721
- const { filePath, line, character } = sourcePosition(params);
2722
- const newName = requireString(params, "newName");
2723
- try {
2724
- const edit = await withLspClient(filePath, async (client, workspaceRoot) => ({
2725
- edit: await client.rename(filePath, line, character, newName),
2726
- workspaceRoot
2727
- }), "rename", clientOptions(signal));
2728
- const apply = applyWorkspaceEdit(edit.edit, { workspaceRoot: edit.workspaceRoot });
2729
- const details = {
2730
- filePath,
2731
- line,
2732
- character,
2733
- newName,
2734
- apply,
2735
- edit: edit.edit
2736
- };
2737
- return text(formatApplyResult(apply), details, !apply.success);
2738
- } catch (error) {
2739
- return missingDependencyResultOrThrow(error, {
2740
- filePath,
2741
- line,
2742
- character,
2743
- newName,
2744
- apply: null,
2745
- edit: null
2746
- });
2747
- }
2748
- }
2749
- //#endregion
2750
- //#region packages/lsp-core/src/tools/schema.ts
2751
- function objectSchema(properties, required = []) {
2752
- return {
2753
- type: "object",
2754
- properties,
2755
- required
2756
- };
2757
- }
2758
- //#endregion
2759
- //#region packages/lsp-core/src/tools/status.ts
2760
- async function executeLspStatus() {
2761
- const servers = getAllServers();
2762
- const snapshots = getLspManager().getSnapshot();
2763
- const installed = servers.filter((server) => server.installed && !server.disabled);
2764
- const configuredLines = servers.map((server) => {
2765
- const state = server.disabled ? "disabled" : server.installed ? "installed" : "missing";
2766
- return `- ${server.id}: ${state}; source=${server.source}; extensions=${server.extensions.join(", ")}`;
2767
- });
2768
- const activeLines = snapshots.map((snapshot) => {
2769
- const state = snapshot.alive ? snapshot.isInitializing ? "initializing" : "alive" : "dead";
2770
- return `- ${snapshot.serverId}: ${state}; root=${snapshot.root}; refs=${snapshot.refCount}`;
2771
- });
2772
- return text([
2773
- `Configured LSP servers: ${servers.length}`,
2774
- `Installed LSP servers: ${installed.length}`,
2775
- "",
2776
- ...configuredLines,
2777
- "",
2778
- `Active LSP clients: ${snapshots.length}`,
2779
- ...activeLines
2780
- ].join("\n"), {
2781
- servers,
2782
- snapshots
2783
- });
2784
- }
2785
- //#endregion
2786
- //#region packages/lsp-core/src/tools/symbols.ts
2787
- function isDocumentSymbol(symbol) {
2788
- return "range" in symbol;
2789
- }
2790
- async function executeLspSymbols(params, signal) {
2791
- const filePath = requireString(params, "filePath");
2792
- const scope = (optionalString(params, "scope") ?? "document") === "workspace" ? "workspace" : "document";
2793
- const limit = Math.min(optionalNumber(params, "limit") ?? 200, 200);
2794
- try {
2795
- if (scope === "workspace") {
2796
- const query = optionalString(params, "query");
2797
- if (!query) {
2798
- const message = "Error: 'query' is required for workspace scope";
2799
- return text(message, {
2800
- filePath,
2801
- scope,
2802
- symbols: [],
2803
- totalSymbols: 0,
2804
- truncated: false,
2805
- error: message,
2806
- errorKind: "missing_query"
2807
- });
2808
- }
2809
- return formatSymbolsResult(filePath, scope, await withLspClient(filePath, async (client) => client.workspaceSymbols(query), "workspaceSymbols", clientOptions(signal)), limit, query);
2810
- }
2811
- return formatSymbolsResult(filePath, scope, await withLspClient(filePath, async (client) => client.documentSymbols(filePath), "documentSymbols", clientOptions(signal)), limit);
2812
- } catch (error) {
2813
- const query = optionalString(params, "query");
2814
- return missingDependencyResultOrThrow(error, {
2815
- filePath,
2816
- scope,
2817
- symbols: [],
2818
- totalSymbols: 0,
2819
- truncated: false,
2820
- ...query === void 0 ? {} : { query }
2821
- });
2822
- }
2823
- }
2824
- function formatSymbolsResult(filePath, scope, symbols, limit, query) {
2825
- const total = symbols.length;
2826
- const truncated = total > limit;
2827
- const limited = truncated ? symbols.slice(0, limit) : symbols;
2828
- const details = {
2829
- filePath,
2830
- scope,
2831
- symbols,
2832
- totalSymbols: total,
2833
- truncated,
2834
- ...query === void 0 ? {} : { query }
2835
- };
2836
- if (total === 0) return text("No symbols found", details);
2837
- const lines = [];
2838
- if (truncated) lines.push(`Found ${total} symbols (showing first ${limit}):`);
2839
- const documentSymbols = limited.filter(isDocumentSymbol);
2840
- if (documentSymbols.length === limited.length) lines.push(...documentSymbols.map((symbol) => formatDocumentSymbol(symbol)));
2841
- else lines.push(...limited.filter((symbol) => !isDocumentSymbol(symbol)).map(formatSymbolInfo));
2842
- return text(lines.join("\n"), details);
2843
- }
2844
- //#endregion
2845
- //#region packages/lsp-core/src/tools/definitions.ts
2846
- var POSITION_PROPERTIES = {
2847
- line: {
2848
- type: "number",
2849
- description: "1-based line number."
2850
- },
2851
- character: {
2852
- type: "number",
2853
- description: "0-based column."
2854
- }
2855
- };
2856
- var POSITION_REQUIRED = [
2857
- "filePath",
2858
- "line",
2859
- "character"
2860
- ];
2861
- var LSP_MCP_TOOLS = [
2862
- {
2863
- name: "status",
2864
- aliases: ["lsp_status"],
2865
- title: "LSP Status",
2866
- description: "Use to list LSP servers without starting them.",
2867
- inputSchema: objectSchema({}),
2868
- execute: executeLspStatus
2869
- },
2870
- {
2871
- name: "diagnostics",
2872
- aliases: ["lsp_diagnostics"],
2873
- title: "LSP Diagnostics",
2874
- description: "Use to get file or directory diagnostics.",
2875
- inputSchema: objectSchema({
2876
- filePath: {
2877
- type: "string",
2878
- description: "File or directory path to check."
2879
- },
2880
- severity: {
2881
- type: "string",
2882
- enum: [
2883
- "error",
2884
- "warning",
2885
- "information",
2886
- "hint",
2887
- "all"
2888
- ],
2889
- description: "Severity filter. Defaults to all."
2890
- }
2891
- }, ["filePath"]),
2892
- execute: executeLspDiagnostics
2893
- },
2894
- {
2895
- name: "goto_definition",
2896
- aliases: ["lsp_goto_definition"],
2897
- title: "LSP Goto Definition",
2898
- description: "Use to find a symbol's definition.",
2899
- inputSchema: objectSchema({
2900
- filePath: {
2901
- type: "string",
2902
- description: "Source file containing the symbol."
2903
- },
2904
- ...POSITION_PROPERTIES
2905
- }, POSITION_REQUIRED),
2906
- execute: executeLspGotoDefinition
2907
- },
2908
- {
2909
- name: "find_references",
2910
- aliases: ["lsp_find_references"],
2911
- title: "LSP Find References",
2912
- description: "Use to find all workspace references to a symbol.",
2913
- inputSchema: objectSchema({
2914
- filePath: {
2915
- type: "string",
2916
- description: "Source file containing the symbol."
2917
- },
2918
- ...POSITION_PROPERTIES,
2919
- includeDeclaration: {
2920
- type: "boolean",
2921
- description: "Include the declaration. Defaults to true."
2922
- }
2923
- }, POSITION_REQUIRED),
2924
- execute: executeLspFindReferences
2925
- },
2926
- {
2927
- name: "symbols",
2928
- aliases: ["lsp_symbols"],
2929
- title: "LSP Symbols",
2930
- description: "Use to outline a file or search workspace symbols.",
2931
- inputSchema: objectSchema({
2932
- filePath: {
2933
- type: "string",
2934
- description: "File path used as LSP context."
2935
- },
2936
- scope: {
2937
- type: "string",
2938
- enum: ["document", "workspace"],
2939
- description: "document outlines a file; workspace searches the project."
2940
- },
2941
- query: {
2942
- type: "string",
2943
- description: "Workspace symbol query."
2944
- },
2945
- limit: {
2946
- type: "number",
2947
- description: "Maximum number of symbols to return."
2948
- }
2949
- }, ["filePath", "scope"]),
2950
- execute: executeLspSymbols
2951
- },
2952
- {
2953
- name: "prepare_rename",
2954
- aliases: ["lsp_prepare_rename"],
2955
- title: "LSP Prepare Rename",
2956
- description: "Use to check whether a symbol supports semantic rename.",
2957
- inputSchema: objectSchema({
2958
- filePath: {
2959
- type: "string",
2960
- description: "Source file path."
2961
- },
2962
- ...POSITION_PROPERTIES
2963
- }, POSITION_REQUIRED),
2964
- execute: executeLspPrepareRename
2965
- },
2966
- {
2967
- name: "rename",
2968
- aliases: ["lsp_rename"],
2969
- title: "LSP Rename",
2970
- description: "Use to rename a symbol workspace-wide after prepare_rename succeeds.",
2971
- inputSchema: objectSchema({
2972
- filePath: {
2973
- type: "string",
2974
- description: "Source file path."
2975
- },
2976
- ...POSITION_PROPERTIES,
2977
- newName: {
2978
- type: "string",
2979
- description: "New symbol name."
2980
- }
2981
- }, [
2982
- "filePath",
2983
- "line",
2984
- "character",
2985
- "newName"
2986
- ]),
2987
- execute: executeLspRename
2988
- },
2989
- {
2990
- name: "install_decision",
2991
- aliases: ["lsp_install_decision"],
2992
- title: "LSP Install Decision",
2993
- description: "Use to record explicit LSP install permission or decline.",
2994
- inputSchema: objectSchema({
2995
- server_id: {
2996
- type: "string",
2997
- description: "Server id from the not-installed message, e.g. rust."
2998
- },
2999
- decision: {
3000
- type: "string",
3001
- enum: ["declined", "allowed"],
3002
- description: "declined silences prompts; allowed authorizes installation."
3003
- }
3004
- }, ["server_id", "decision"]),
3005
- execute: executeLspInstallDecision
3006
- }
3007
- ];
3008
- //#endregion
3009
- //#region packages/lsp-core/src/tools/runtime.ts
3010
- async function executeLspTool(name, params, signal) {
3011
- const tool = LSP_MCP_TOOLS.find((candidate) => candidate.name === name || (candidate.aliases?.includes(name) ?? false));
3012
- if (!tool) throw new Error(`Unknown LSP tool: ${name}`);
3013
- return tool.execute(params, signal);
3014
- }
3015
- function coerceToolArguments(value) {
3016
- return isRecord(value) ? value : {};
3017
- }
3018
- //#endregion
3019
- //#region packages/lsp-core/src/mcp.ts
3020
- var SERVER_NAME = "lsp";
3021
- var SERVER_VERSION = "0.5.3";
3022
- async function handleLspMcpRequest(input) {
3023
- if (!isPlainRecord(input)) return errorResponse(null, -32600, "Invalid Request");
3024
- const id = jsonRpcId(input["id"]);
3025
- const method = input["method"];
3026
- if (method === "notifications/initialized") return void 0;
3027
- if (method === "ping") return successResponse(id, {});
3028
- if (method === "initialize") {
3029
- const protocolVersion = requestedProtocolVersion(input["params"]);
3030
- return successResponse(id, {
3031
- capabilities: { tools: { listChanged: false } },
3032
- serverInfo: {
3033
- name: SERVER_NAME,
3034
- version: SERVER_VERSION
3035
- },
3036
- protocolVersion
3037
- });
3038
- }
3039
- if (method === "tools/list") return successResponse(id, { tools: LSP_MCP_TOOLS.map(describeTool) });
3040
- if (method === "tools/call") return handleToolCall(id, input["params"]);
3041
- return errorResponse(id, -32601, `Method not found: ${String(method)}`);
3042
- }
3043
- async function handleToolCall(id, params) {
3044
- if (!isPlainRecord(params) || typeof params["name"] !== "string") return errorResponse(id, -32602, "tools/call requires params.name");
3045
- try {
3046
- const result = await executeLspTool(params["name"], coerceToolArguments(params["arguments"]));
3047
- return successResponse(id, {
3048
- content: result.content,
3049
- isError: result.isError ?? false,
3050
- details: result.details
3051
- });
3052
- } catch (error) {
3053
- return successResponse(id, {
3054
- content: [{
3055
- type: "text",
3056
- text: messageFromError(error)
3057
- }],
3058
- isError: true
3059
- });
3060
- }
3061
- }
3062
- function describeTool(tool) {
3063
- return {
3064
- name: tool.name,
3065
- title: tool.title,
3066
- description: tool.description,
3067
- inputSchema: tool.inputSchema
3068
- };
3069
- }
3070
- function requestedProtocolVersion(params) {
3071
- if (!isPlainRecord(params) || typeof params["protocolVersion"] !== "string") return "2024-11-05";
3072
- return params["protocolVersion"];
3073
- }
3074
- //#endregion
3075
- //#region packages/lsp-daemon/src/lock.ts
3076
- function isProcessAlive(pid) {
3077
- if (!Number.isInteger(pid) || pid <= 0) return false;
3078
- try {
3079
- process.kill(pid, 0);
3080
- return true;
3081
- } catch (error) {
3082
- return error.code === "EPERM";
3083
- }
3084
- }
3085
- function readLockPid(lockPath) {
3086
- try {
3087
- const pid = Number.parseInt(readFileSync(lockPath, "utf8").trim(), 10);
3088
- return Number.isInteger(pid) ? pid : null;
3089
- } catch {
3090
- return null;
3091
- }
3092
- }
3093
- function tryAcquireLock(lockPath, ownerPid = process.pid) {
3094
- mkdirSync(dirname(lockPath), { recursive: true });
3095
- for (let attempt = 0; attempt < 2; attempt += 1) {
3096
- const handle = writeLockFile(lockPath, ownerPid);
3097
- if (handle) return handle;
3098
- if (!reapStaleLock(lockPath)) return null;
3099
- }
3100
- return null;
3101
- }
3102
- function writeLockFile(lockPath, ownerPid) {
3103
- try {
3104
- const fd = openSync(lockPath, "wx");
3105
- writeSync(fd, `${ownerPid}\n`);
3106
- closeSync(fd);
3107
- return { release: () => unlinkQuietly(lockPath) };
3108
- } catch (error) {
3109
- if (error.code === "EEXIST") return null;
3110
- throw error;
3111
- }
3112
- }
3113
- function reapStaleLock(lockPath) {
3114
- const pid = readLockPid(lockPath);
3115
- if (pid !== null && isProcessAlive(pid)) return false;
3116
- unlinkQuietly(lockPath);
3117
- return true;
3118
- }
3119
- function unlinkQuietly(path) {
3120
- try {
3121
- unlinkSync(path);
3122
- } catch {}
3123
- }
3124
- //#endregion
3125
- //#region packages/lsp-daemon/src/ensure-daemon.ts
3126
- var PROBE_TIMEOUT_MS = 500;
3127
- var DEFAULT_READY_TIMEOUT_MS = 5e3;
3128
- var DEFAULT_POLL_INTERVAL_MS = 100;
3129
- var CODEX_LSP_DAEMON_CLI_ENV = "CODEX_LSP_DAEMON_CLI";
3130
- var DaemonUnreachableError = class extends Error {
3131
- constructor(socketPath) {
3132
- super(`LSP daemon did not become reachable at ${socketPath}`);
3133
- this.name = "DaemonUnreachableError";
3134
- }
3135
- };
3136
- async function ensureDaemonRunning(paths, deps = defaultEnsureDaemonDeps(), options = {}) {
3137
- const readyTimeoutMs = options.readyTimeoutMs ?? DEFAULT_READY_TIMEOUT_MS;
3138
- const pollIntervalMs = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS;
3139
- if (await deps.probe(paths.socket)) return;
3140
- const lock = deps.acquireLock(paths.lock);
3141
- if (!lock) {
3142
- await waitUntilReachable(paths.socket, deps, readyTimeoutMs, pollIntervalMs);
3143
- return;
3144
- }
3145
- try {
3146
- if (await deps.probe(paths.socket)) return;
3147
- deps.cleanupStaleSocket(paths.socket);
3148
- deps.spawnDaemon(paths);
3149
- await waitUntilReachable(paths.socket, deps, readyTimeoutMs, pollIntervalMs);
3150
- } finally {
3151
- lock.release();
3152
- }
3153
- }
3154
- async function waitUntilReachable(socketPath, deps, readyTimeoutMs, pollIntervalMs) {
3155
- const deadline = deps.now() + readyTimeoutMs;
3156
- for (;;) {
3157
- if (await deps.probe(socketPath)) return;
3158
- if (deps.now() >= deadline) throw new DaemonUnreachableError(socketPath);
3159
- await deps.sleep(pollIntervalMs);
3160
- }
3161
- }
3162
- function probeSocket(socketPath, timeoutMs = PROBE_TIMEOUT_MS) {
3163
- return new Promise((resolve) => {
3164
- const socket = connect(socketPath);
3165
- const finish = (ok) => {
3166
- socket.destroy();
3167
- resolve(ok);
3168
- };
3169
- const timer = setTimeout(() => finish(false), timeoutMs);
3170
- timer.unref?.();
3171
- socket.once("connect", () => {
3172
- clearTimeout(timer);
3173
- finish(true);
3174
- });
3175
- socket.once("error", () => {
3176
- clearTimeout(timer);
3177
- finish(false);
3178
- });
3179
- });
3180
- }
3181
- function spawnDaemonProcess(paths) {
3182
- mkdirSync(dirname(paths.log), { recursive: true });
3183
- const logFd = openSync(paths.log, "a");
3184
- try {
3185
- spawn(execPath, [resolveDaemonCliPath(), "daemon"], {
3186
- detached: true,
3187
- stdio: [
3188
- "ignore",
3189
- logFd,
3190
- logFd
3191
- ]
3192
- }).unref();
3193
- } finally {
3194
- closeSync(logFd);
3195
- }
3196
- }
3197
- function resolveDaemonCliPath(env = process.env) {
3198
- const override = env[CODEX_LSP_DAEMON_CLI_ENV]?.trim();
3199
- if (override) return override;
3200
- const current = fileURLToPath(import.meta.url);
3201
- return basename(current) === "ensure-daemon.ts" ? join(dirname(current), "cli.ts") : current;
3202
- }
3203
- function defaultEnsureDaemonDeps() {
3204
- return {
3205
- probe: (socketPath) => probeSocket(socketPath),
3206
- acquireLock: (lockPath) => tryAcquireLock(lockPath),
3207
- cleanupStaleSocket: (socketPath) => {
3208
- if (existsSync(socketPath)) unlinkQuietly(socketPath);
3209
- },
3210
- spawnDaemon: (paths) => spawnDaemonProcess(paths),
3211
- sleep: (ms) => new Promise((resolve) => {
3212
- setTimeout(resolve, ms);
3213
- }),
3214
- now: () => Date.now()
3215
- };
3216
- }
3217
- //#endregion
3218
- //#region packages/lsp-daemon/src/paths.ts
3219
- var requireFromHere = createRequire(import.meta.url);
3220
- var MAX_SOCKET_PATH_LENGTH = 100;
3221
- var CODEX_LSP_DAEMON_VERSION_ENV = "CODEX_LSP_DAEMON_VERSION";
3222
- function resolveDaemonVersion(requireFn = requireFromHere) {
3223
- for (const candidate of ["./package.json", "../package.json"]) try {
3224
- const pkg = requireFn(candidate);
3225
- if (typeof pkg.version === "string" && pkg.version.length > 0) return pkg.version;
3226
- } catch {}
3227
- return "0";
3228
- }
3229
- function daemonBaseDir(env = process.env) {
3230
- const explicit = env["CODEX_LSP_DAEMON_DIR"]?.trim();
3231
- if (explicit) return explicit;
3232
- const pluginData = env["PLUGIN_DATA"]?.trim();
3233
- if (pluginData) return join(pluginData, "daemon");
3234
- const codexHome = env["CODEX_HOME"]?.trim();
3235
- return join(codexHome && codexHome.length > 0 ? codexHome : join(homedir(), ".codex"), "codex-lsp", "daemon");
3236
- }
3237
- function daemonPaths(env = process.env, version = resolveDaemonVersionFromEnv(env) ?? resolveDaemonVersion()) {
3238
- const dir = join(daemonBaseDir(env), `v${version}`);
3239
- return {
3240
- version,
3241
- dir,
3242
- socket: resolveSocketPath(dir, version),
3243
- lock: join(dir, "daemon.lock"),
3244
- pid: join(dir, "daemon.pid"),
3245
- log: join(dir, "daemon.log")
3246
- };
3247
- }
3248
- function resolveDaemonVersionFromEnv(env = process.env) {
3249
- const version = env[CODEX_LSP_DAEMON_VERSION_ENV]?.trim();
3250
- return version && version.length > 0 ? version : null;
3251
- }
3252
- function resolveSocketPath(dir, version) {
3253
- const digest = createHash("sha256").update(dir).digest("hex").slice(0, 16);
3254
- if (process.platform === "win32") return `\\\\.\\pipe\\holycodex-lsp-${version}-${digest}`;
3255
- const natural = join(dir, "daemon.sock");
3256
- if (natural.length < MAX_SOCKET_PATH_LENGTH) return natural;
3257
- return join(tmpdir(), `holycodex-lsp-${version}-${digest}.sock`);
3258
- }
3259
- //#endregion
3260
- //#region packages/lsp-daemon/src/request-routing.ts
3261
- var CONTEXT_KEY = "_context";
3262
- function extractRequestContext(raw) {
3263
- if (!isPlainRecord(raw) || raw["method"] !== "tools/call") return {
3264
- input: raw,
3265
- context: void 0
3266
- };
3267
- const params = raw["params"];
3268
- if (!isPlainRecord(params)) return {
3269
- input: raw,
3270
- context: void 0
3271
- };
3272
- const args = params["arguments"];
3273
- if (!isPlainRecord(args)) return {
3274
- input: raw,
3275
- context: void 0
3276
- };
3277
- const context = parseContext(args[CONTEXT_KEY]);
3278
- if (!context) return {
3279
- input: raw,
3280
- context: void 0
3281
- };
3282
- const cleanedArgs = { ...args };
3283
- delete cleanedArgs[CONTEXT_KEY];
3284
- return {
3285
- input: {
3286
- ...raw,
3287
- params: {
3288
- ...params,
3289
- arguments: cleanedArgs
3290
- }
3291
- },
3292
- context
3293
- };
3294
- }
3295
- function handleDaemonMessage(raw) {
3296
- const { input, context } = extractRequestContext(raw);
3297
- if (context) return runWithRequestContext(context, () => handleLspMcpRequest(input));
3298
- return handleLspMcpRequest(input);
3299
- }
3300
- function parseContext(value) {
3301
- if (!isPlainRecord(value)) return void 0;
3302
- const context = {};
3303
- const cwd = value["cwd"];
3304
- if (typeof cwd === "string") context.cwd = cwd;
3305
- const env = value["env"];
3306
- if (isStringRecord(env)) context.env = env;
3307
- return context.cwd === void 0 && context.env === void 0 ? void 0 : context;
3308
- }
3309
- function isStringRecord(value) {
3310
- return isPlainRecord(value) && Object.values(value).every((item) => typeof item === "string");
3311
- }
3312
- //#endregion
3313
- //#region packages/lsp-daemon/src/socket-jsonrpc.ts
3314
- function encodeJsonLine(message) {
3315
- return `${JSON.stringify(message)}\n`;
3316
- }
3317
- function createLineDecoder(onMessage, onParseError) {
3318
- let buffer = "";
3319
- return { push(chunk) {
3320
- buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
3321
- let index = buffer.indexOf("\n");
3322
- while (index !== -1) {
3323
- const raw = buffer.slice(0, index).trim();
3324
- buffer = buffer.slice(index + 1);
3325
- if (raw.length > 0) try {
3326
- onMessage(JSON.parse(raw));
3327
- } catch (error) {
3328
- onParseError?.(raw, error);
3329
- }
3330
- index = buffer.indexOf("\n");
3331
- }
3332
- } };
3333
- }
3334
- //#endregion
3335
- //#region packages/lsp-daemon/src/daemon-client.ts
3336
- var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
3337
- var REQUEST_ID = 1;
3338
- var DaemonRequestError = class extends Error {
3339
- constructor(message, requestWritten) {
3340
- super(message);
3341
- this.name = "DaemonRequestError";
3342
- this.requestWritten = requestWritten;
3343
- }
3344
- };
3345
- async function callToolViaDaemon(name, args, options = {}) {
3346
- const paths = options.paths ?? daemonPaths();
3347
- const ensure = options.ensure ?? ensureDaemonRunning;
3348
- const timeoutMs = options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS;
3349
- const requestArgs = withContext(args, options.context);
3350
- let lastError;
3351
- for (let attempt = 0; attempt < 2; attempt += 1) try {
3352
- await ensure(paths);
3353
- return await sendToolCall(paths.socket, name, requestArgs, timeoutMs);
3354
- } catch (error) {
3355
- lastError = error;
3356
- if (error instanceof DaemonRequestError && error.requestWritten) break;
3357
- }
3358
- return daemonUnreachableResult(paths, lastError);
3359
- }
3360
- var FORWARDED_ENV_KEYS = [
3361
- "LSP_TOOLS_MCP_PROJECT_CONFIG",
3362
- "LSP_TOOLS_MCP_USER_CONFIG",
3363
- "LSP_TOOLS_MCP_INSTALL_DECISIONS"
3364
- ];
3365
- function currentRequestContext(env = process.env) {
3366
- const forwarded = {};
3367
- for (const key of FORWARDED_ENV_KEYS) {
3368
- const value = env[key];
3369
- if (value !== void 0) forwarded[key] = value;
3370
- }
3371
- return {
3372
- cwd: process.cwd(),
3373
- env: forwarded
3374
- };
3375
- }
3376
- function withContext(args, context) {
3377
- if (!context || context.cwd === void 0 && context.env === void 0) return args;
3378
- return {
3379
- ...args,
3380
- [CONTEXT_KEY]: context
3381
- };
3382
- }
3383
- function daemonUnreachableResult(paths, error) {
3384
- return {
3385
- content: [{
3386
- type: "text",
3387
- text: [
3388
- `LSP daemon unreachable: ${messageFromError(error)}.`,
3389
- "The MCP server is a thin proxy and never runs language servers in-process.",
3390
- `Socket: ${paths.socket}`,
3391
- `Logs: ${paths.log}`,
3392
- "The daemon is auto-started on demand and will be retried on the next request."
3393
- ].join("\n")
3394
- }],
3395
- isError: true
3396
- };
3397
- }
3398
- function sendToolCall(socketPath, name, args, timeoutMs) {
3399
- return new Promise((resolve, reject) => {
3400
- const socket = connect(socketPath);
3401
- let settled = false;
3402
- let requestWritten = false;
3403
- const finish = (run) => {
3404
- if (settled) return;
3405
- settled = true;
3406
- clearTimeout(timer);
3407
- socket.destroy();
3408
- run();
3409
- };
3410
- const timer = setTimeout(() => finish(() => reject(new DaemonRequestError("daemon request timed out", requestWritten))), timeoutMs);
3411
- timer.unref();
3412
- const decoder = createLineDecoder((message) => {
3413
- const result = toToolResult(message);
3414
- if (result) finish(() => resolve(result));
3415
- else finish(() => reject(new DaemonRequestError("invalid daemon response", requestWritten)));
3416
- });
3417
- socket.once("connect", () => {
3418
- requestWritten = true;
3419
- socket.write(encodeJsonLine({
3420
- jsonrpc: "2.0",
3421
- id: REQUEST_ID,
3422
- method: "tools/call",
3423
- params: {
3424
- name,
3425
- arguments: args
3426
- }
3427
- }));
3428
- });
3429
- socket.on("data", (chunk) => decoder.push(chunk));
3430
- socket.once("error", (error) => finish(() => reject(new DaemonRequestError(error.message, requestWritten))));
3431
- socket.once("close", () => finish(() => reject(new DaemonRequestError("daemon connection closed", requestWritten))));
3432
- });
3433
- }
3434
- function toToolResult(message) {
3435
- if (!isPlainRecord(message) || message["id"] !== REQUEST_ID) return null;
3436
- const result = message["result"];
3437
- if (!isPlainRecord(result) || !Array.isArray(result["content"])) return null;
3438
- return {
3439
- content: result["content"],
3440
- isError: result["isError"] === true,
3441
- details: result["details"]
3442
- };
3443
- }
3444
- //#endregion
3445
- //#region packages/lsp-daemon/src/proxy.ts
3446
- async function runMcpStdioProxy(options = {}) {
3447
- await runJsonRpcStdioServer({
3448
- input: options.input ?? process.stdin,
3449
- output: options.output ?? process.stdout,
3450
- idleTimeoutMs: 0,
3451
- handler: handleProxyRequest,
3452
- handlerOptions: {
3453
- paths: options.paths ?? daemonPaths(),
3454
- context: options.context ?? currentRequestContext(),
3455
- ...options.ensure ? { ensure: options.ensure } : {}
3456
- },
3457
- onHandlerError: (error) => {
3458
- process.stderr.write(`[lsp-daemon] proxy error: ${messageFromError(error)}\n`);
3459
- }
3460
- });
3461
- }
3462
- async function handleProxyRequest(parsed, callOptions) {
3463
- const toolCall = asToolCall(parsed);
3464
- if (!toolCall) return handleLspMcpRequest(parsed);
3465
- const result = await callToolViaDaemon(toolCall.name, toolCall.args, callOptions);
3466
- return successResponse(toolCall.id, {
3467
- content: result.content,
3468
- isError: result.isError ?? false,
3469
- details: result.details
3470
- });
3471
- }
3472
- function asToolCall(parsed) {
3473
- if (!isPlainRecord(parsed) || parsed["method"] !== "tools/call") return null;
3474
- const params = parsed["params"];
3475
- if (!isPlainRecord(params) || typeof params["name"] !== "string") return null;
3476
- const args = params["arguments"];
3477
- return {
3478
- id: jsonRpcId(parsed["id"]),
3479
- name: params["name"],
3480
- args: isPlainRecord(args) ? args : {}
3481
- };
3482
- }
3483
- //#endregion
3484
- //#region packages/lsp-daemon/src/daemon-server.ts
3485
- var DEFAULT_IDLE_SHUTDOWN_MS = 30 * 6e4;
3486
- var DEFAULT_IDLE_CHECK_INTERVAL_MS = 6e4;
3487
- async function startDaemonServer(paths, options = {}) {
3488
- const idleShutdownMs = options.idleShutdownMs ?? DEFAULT_IDLE_SHUTDOWN_MS;
3489
- const idleCheckIntervalMs = options.idleCheckIntervalMs ?? DEFAULT_IDLE_CHECK_INTERVAL_MS;
3490
- mkdirSync(paths.dir, { recursive: true });
3491
- unlinkQuietly(paths.socket);
3492
- const connections = /* @__PURE__ */ new Set();
3493
- let lastActiveAt = Date.now();
3494
- const touch = () => {
3495
- lastActiveAt = Date.now();
3496
- };
3497
- const server = createServer((socket) => {
3498
- connections.add(socket);
3499
- touch();
3500
- const decoder = createLineDecoder((message) => {
3501
- touch();
3502
- respond(socket, message);
3503
- });
3504
- socket.on("data", (chunk) => decoder.push(chunk));
3505
- socket.on("error", () => socket.destroy());
3506
- socket.on("close", () => {
3507
- connections.delete(socket);
3508
- touch();
3509
- });
3510
- });
3511
- server.on("error", (error) => logServerError(error));
3512
- const endpointPath = join(paths.dir, "daemon.endpoint");
3513
- await listen(server, paths.socket);
3514
- writeFileSync(paths.pid, `${process.pid}\n`);
3515
- writeFileSync(endpointPath, paths.socket);
3516
- let closed = false;
3517
- const close = async () => {
3518
- if (closed) return;
3519
- closed = true;
3520
- clearInterval(idleTimer);
3521
- for (const socket of connections) socket.destroy();
3522
- connections.clear();
3523
- await closeServer(server);
3524
- unlinkQuietly(paths.socket);
3525
- unlinkQuietly(paths.pid);
3526
- unlinkQuietly(endpointPath);
3527
- await disposeDefaultLspManager();
3528
- };
3529
- const idleTimer = setInterval(() => {
3530
- if (connections.size > 0) return;
3531
- if (getLspManager().clientCount() > 0) {
3532
- touch();
3533
- return;
3534
- }
3535
- if (Date.now() - lastActiveAt < idleShutdownMs) return;
3536
- if (options.onIdleShutdown) {
3537
- options.onIdleShutdown();
3538
- return;
3539
- }
3540
- close().then(() => process.exit(0));
3541
- }, idleCheckIntervalMs);
3542
- idleTimer.unref();
3543
- installSignalHandlers(close);
3544
- return {
3545
- server,
3546
- close
3547
- };
3548
- }
3549
- async function respond(socket, message) {
3550
- try {
3551
- const response = await handleDaemonMessage(message);
3552
- if (response && socket.writable) socket.write(encodeJsonLine(response));
3553
- } catch (error) {
3554
- logServerError(error);
3555
- }
3556
- }
3557
- function listen(server, socketPath) {
3558
- return new Promise((resolve, reject) => {
3559
- const onError = (error) => reject(error);
3560
- server.once("error", onError);
3561
- server.listen(socketPath, () => {
3562
- server.removeListener("error", onError);
3563
- resolve();
3564
- });
3565
- });
3566
- }
3567
- function closeServer(server) {
3568
- return new Promise((resolve) => server.close(() => resolve()));
3569
- }
3570
- function installSignalHandlers(close) {
3571
- const handler = () => {
3572
- close().then(() => process.exit(0));
3573
- };
3574
- process.once("SIGTERM", handler);
3575
- process.once("SIGINT", handler);
3576
- }
3577
- function logServerError(error) {
3578
- const message = error instanceof Error ? error.stack ?? error.message : String(error);
3579
- process.stderr.write(`[lsp-daemon] ${message}\n`);
3580
- }
3581
- //#endregion
3582
- //#region packages/lsp-daemon/src/run-daemon.ts
3583
- async function runDaemon() {
3584
- process.on("uncaughtException", (error) => logNonFatal("uncaughtException", error));
3585
- process.on("unhandledRejection", (reason) => logNonFatal("unhandledRejection", reason));
3586
- await startDaemonServer(daemonPaths());
3587
- }
3588
- function logNonFatal(kind, error) {
3589
- const message = error instanceof Error ? error.stack ?? error.message : String(error);
3590
- process.stderr.write(`[lsp-daemon] ${kind}: ${message}\n`);
3591
- }
3592
- //#endregion
3593
- //#region packages/lsp-daemon/src/cli.ts
3594
- async function main() {
3595
- const [command = "mcp"] = argv.slice(2);
3596
- if (command === "daemon") {
3597
- await runDaemon();
3598
- return;
3599
- }
3600
- if (command === "mcp") {
3601
- await runMcpStdioProxy();
3602
- return;
3603
- }
3604
- stderr.write("Usage: holycodex-lsp-daemon [mcp | daemon]\n");
3605
- process.exitCode = 2;
3606
- }
3607
- main().catch((error) => {
3608
- stderr.write(`${stackOrMessageFromError(error)}\n`);
3609
- process.exitCode = 1;
3610
- });
3611
- //#endregion