oh-my-opencode 5.0.0-beta.21 → 5.0.0-beta.23
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.
- package/dist/cli/index.js +290 -288
- package/dist/cli-node/index.js +290 -288
- package/dist/index.js +1675 -1657
- package/dist/skills/debugging/SKILL.md +2 -0
- package/dist/skills/debugging/references/methodology/00-setup.md +12 -0
- package/dist/skills/debugging/references/methodology/02-investigate.md +21 -0
- package/dist/skills/debugging/references/runtimes/go.md +15 -1
- package/dist/skills/debugging/references/runtimes/native-binary.md +38 -0
- package/dist/skills/debugging/references/runtimes/node.md +25 -0
- package/dist/skills/debugging/references/runtimes/python.md +13 -0
- package/dist/skills/debugging/references/runtimes/rust.md +41 -0
- package/dist/skills/debugging/references/scripts/dap.mjs +267 -0
- package/dist/skills/debugging/references/scripts/dap.test.ts +86 -0
- package/dist/skills/debugging/references/scripts/fixture-adapter.mjs +59 -0
- package/dist/skills/debugging/references/tools/dap.md +103 -0
- package/dist/skills/debugging/references/tools/frida.md +193 -0
- package/dist/tools/delegate-task/types.d.ts +3 -0
- package/dist/tui.js +131 -147
- package/package.json +18 -15
- package/packages/lsp-core/src/index.ts +1 -0
- package/packages/lsp-core/src/lsp/client-wrapper.ts +25 -5
- package/packages/lsp-core/src/lsp/client.ts +26 -0
- package/packages/lsp-core/src/lsp/connection.ts +16 -0
- package/packages/lsp-core/src/lsp/constants.ts +1 -0
- package/packages/lsp-core/src/lsp/format-document.test.ts +143 -0
- package/packages/lsp-core/src/lsp/format-document.ts +90 -0
- package/packages/lsp-core/src/lsp/manager-max-clients.test.ts +151 -0
- package/packages/lsp-core/src/lsp/manager.ts +34 -1
- package/packages/lsp-core/src/lsp/server-definitions.ts +29 -0
- package/packages/lsp-core/src/lsp/server-installation.test.ts +376 -0
- package/packages/lsp-core/src/lsp/server-installation.ts +200 -27
- package/packages/lsp-core/src/lsp/server-resolution-local-binary.test.ts +170 -0
- package/packages/lsp-core/src/lsp/server-resolution.ts +18 -5
- package/packages/lsp-core/src/lsp/transport.ts +9 -0
- package/packages/lsp-core/src/lsp/types.ts +8 -0
- package/packages/lsp-core/src/tool-surface.test.ts +13 -1
- package/packages/lsp-core/src/tools/definitions.ts +14 -0
- package/packages/lsp-core/src/tools/format.test.ts +56 -0
- package/packages/lsp-core/src/tools/format.ts +56 -0
- package/packages/lsp-core/src/tools/index.ts +1 -0
- package/packages/lsp-core/src/tools/types.ts +10 -0
- package/packages/lsp-daemon/dist/cli.js +432 -144
- package/packages/lsp-daemon/dist/client.d.ts +10 -0
- package/packages/lsp-daemon/dist/client.js +401 -107
- package/packages/lsp-daemon/dist/daemon-client.d.ts +1 -0
- package/packages/lsp-daemon/dist/daemon-client.js +3 -0
- package/packages/lsp-daemon/dist/index.js +395 -107
- package/packages/lsp-daemon/package.json +1 -1
- package/packages/lsp-tools-mcp/dist/cli.js +384 -99
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +53 -0
- package/packages/lsp-tools-mcp/dist/mcp.js +384 -99
- package/packages/lsp-tools-mcp/dist/tools.js +385 -99
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +2 -2
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +69 -35
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +21 -2
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +404 -121
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +3 -3
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +3 -3
- package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +4 -4
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +30 -15
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +30 -15
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-commands.js +5 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.d.ts +2 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-output.js +11 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +22 -10
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-commands.ts +5 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-output.ts +13 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +3 -5
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +26 -2
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/model-catalog.json +3 -3
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +3 -3
- package/packages/omo-codex/plugin/skills/debugging/SKILL.md +2 -0
- package/packages/omo-codex/plugin/skills/debugging/references/methodology/00-setup.md +12 -0
- package/packages/omo-codex/plugin/skills/debugging/references/methodology/02-investigate.md +21 -0
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/go.md +15 -1
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/native-binary.md +38 -0
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/node.md +25 -0
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/python.md +13 -0
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/rust.md +41 -0
- package/packages/omo-codex/plugin/skills/debugging/references/scripts/dap.mjs +267 -0
- package/packages/omo-codex/plugin/skills/debugging/references/scripts/fixture-adapter.mjs +59 -0
- package/packages/omo-codex/plugin/skills/debugging/references/tools/dap.md +103 -0
- package/packages/omo-codex/plugin/skills/debugging/references/tools/frida.md +193 -0
- package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +1 -1
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +2 -2
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +8 -8
- package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +2 -2
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +38 -23
- package/packages/shared-skills/skills/debugging/SKILL.md +2 -0
- package/packages/shared-skills/skills/debugging/references/methodology/00-setup.md +12 -0
- package/packages/shared-skills/skills/debugging/references/methodology/02-investigate.md +21 -0
- package/packages/shared-skills/skills/debugging/references/runtimes/go.md +15 -1
- package/packages/shared-skills/skills/debugging/references/runtimes/native-binary.md +38 -0
- package/packages/shared-skills/skills/debugging/references/runtimes/node.md +25 -0
- package/packages/shared-skills/skills/debugging/references/runtimes/python.md +13 -0
- package/packages/shared-skills/skills/debugging/references/runtimes/rust.md +41 -0
- package/packages/shared-skills/skills/debugging/references/scripts/dap.mjs +267 -0
- package/packages/shared-skills/skills/debugging/references/scripts/dap.test.ts +86 -0
- package/packages/shared-skills/skills/debugging/references/scripts/fixture-adapter.mjs +59 -0
- package/packages/shared-skills/skills/debugging/references/tools/dap.md +103 -0
- package/packages/shared-skills/skills/debugging/references/tools/frida.md +193 -0
|
@@ -8,11 +8,13 @@ import { argv, execPath as execPath2, stderr } from "node:process";
|
|
|
8
8
|
import { stdin as processStdin } from "node:process";
|
|
9
9
|
|
|
10
10
|
// src/codex-hook.ts
|
|
11
|
-
import { readFileSync as
|
|
11
|
+
import { readFileSync as readFileSync10, realpathSync as realpathSync8 } from "node:fs";
|
|
12
12
|
import { homedir as homedir3 } from "node:os";
|
|
13
|
-
import { join as join12, resolve as
|
|
13
|
+
import { join as join12, resolve as resolve12 } from "node:path";
|
|
14
14
|
|
|
15
15
|
// ../../../../lsp-daemon/dist/client.js
|
|
16
|
+
import { statSync as statSync5 } from "node:fs";
|
|
17
|
+
import { isAbsolute as isAbsolute6 } from "node:path";
|
|
16
18
|
import { connect } from "node:net";
|
|
17
19
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
18
20
|
import { existsSync, realpathSync, statSync } from "node:fs";
|
|
@@ -43,7 +45,7 @@ import { fileURLToPath } from "node:url";
|
|
|
43
45
|
import { statSync as statSync2 } from "node:fs";
|
|
44
46
|
import { isAbsolute as isAbsolute2 } from "node:path";
|
|
45
47
|
import { existsSync as existsSync11, realpathSync as realpathSync5, statSync as statSync4 } from "node:fs";
|
|
46
|
-
import { basename as basename3, dirname as
|
|
48
|
+
import { basename as basename3, dirname as dirname10, join as join5, resolve as resolve8 } from "node:path";
|
|
47
49
|
import { basename as basename2, extname } from "node:path";
|
|
48
50
|
import { resolve as resolve6 } from "node:path";
|
|
49
51
|
import { pathToFileURL as pathToFileURL3 } from "node:url";
|
|
@@ -68,14 +70,13 @@ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as rea
|
|
|
68
70
|
import { dirname as dirname8 } from "node:path";
|
|
69
71
|
import { existsSync as existsSync9, readFileSync as readFileSync5 } from "node:fs";
|
|
70
72
|
import { existsSync as existsSync10 } from "node:fs";
|
|
71
|
-
import { delimiter as delimiter3, join as join4 } from "node:path";
|
|
73
|
+
import { delimiter as delimiter3, dirname as dirname9, isAbsolute as isAbsolute5, join as join4, resolve as resolve7 } from "node:path";
|
|
72
74
|
import { existsSync as existsSync12, lstatSync as lstatSync5, readdirSync as readdirSync3 } from "node:fs";
|
|
73
|
-
import { join as join6, resolve as
|
|
75
|
+
import { join as join6, resolve as resolve9 } from "node:path";
|
|
74
76
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
75
77
|
import { lstatSync as lstatSync6, readdirSync as readdirSync4 } from "node:fs";
|
|
76
78
|
import { join as join7 } from "node:path";
|
|
77
|
-
import {
|
|
78
|
-
import { isAbsolute as isAbsolute5 } from "node:path";
|
|
79
|
+
import { readFileSync as readFileSync6, renameSync as renameSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
79
80
|
|
|
80
81
|
class LspRequestContextParseError extends Error {
|
|
81
82
|
constructor(code, message) {
|
|
@@ -762,6 +763,7 @@ var DEFAULT_MAX_DIRECTORY_FILES = 50;
|
|
|
762
763
|
var REQUEST_TIMEOUT_MS = 15000;
|
|
763
764
|
var INIT_TIMEOUT_MS = 60000;
|
|
764
765
|
var IDLE_TIMEOUT_MS = 5 * 60000;
|
|
766
|
+
var MAX_RESIDENT_CLIENTS = 6;
|
|
765
767
|
var REAPER_INTERVAL_MS = 60000;
|
|
766
768
|
var STOP_HARD_KILL_TIMEOUT_MS = 5000;
|
|
767
769
|
var STOP_SIGKILL_GRACE_MS = 1000;
|
|
@@ -1277,6 +1279,7 @@ class LspClientTransport {
|
|
|
1277
1279
|
this.diagnosticsStore = new Map;
|
|
1278
1280
|
this.workspaceApplyEditHandler = null;
|
|
1279
1281
|
this.diagnosticPullSupported = false;
|
|
1282
|
+
this.documentFormattingSupported = false;
|
|
1280
1283
|
this.requestTimeoutMs = timeouts.requestTimeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
1281
1284
|
this.initializeTimeoutMs = timeouts.initializeTimeoutMs ?? INIT_TIMEOUT_MS;
|
|
1282
1285
|
}
|
|
@@ -1298,6 +1301,12 @@ class LspClientTransport {
|
|
|
1298
1301
|
isDiagnosticPullSupported() {
|
|
1299
1302
|
return this.diagnosticPullSupported;
|
|
1300
1303
|
}
|
|
1304
|
+
setDocumentFormattingSupported(supported) {
|
|
1305
|
+
this.documentFormattingSupported = supported;
|
|
1306
|
+
}
|
|
1307
|
+
isDocumentFormattingSupported() {
|
|
1308
|
+
return this.documentFormattingSupported;
|
|
1309
|
+
}
|
|
1301
1310
|
handlePublishDiagnostics(params) {
|
|
1302
1311
|
this.diagnosticsStore.set(params.uri, [...params.diagnostics]);
|
|
1303
1312
|
}
|
|
@@ -1509,6 +1518,14 @@ function supportsDiagnosticPull(capabilities) {
|
|
|
1509
1518
|
return false;
|
|
1510
1519
|
return Object.hasOwn(capabilities, "diagnosticProvider");
|
|
1511
1520
|
}
|
|
1521
|
+
function supportsDocumentFormatting(capabilities) {
|
|
1522
|
+
if (capabilities === undefined)
|
|
1523
|
+
return false;
|
|
1524
|
+
const provider = capabilities.documentFormattingProvider;
|
|
1525
|
+
if (provider === undefined || provider === null || provider === false)
|
|
1526
|
+
return false;
|
|
1527
|
+
return provider === true || typeof provider === "object";
|
|
1528
|
+
}
|
|
1512
1529
|
|
|
1513
1530
|
class LspClientConnection extends LspClientTransport {
|
|
1514
1531
|
async initialize() {
|
|
@@ -1525,6 +1542,7 @@ class LspClientConnection extends LspClientTransport {
|
|
|
1525
1542
|
references: {},
|
|
1526
1543
|
documentSymbol: { hierarchicalDocumentSymbolSupport: true },
|
|
1527
1544
|
publishDiagnostics: {},
|
|
1545
|
+
formatting: { dynamicRegistration: false },
|
|
1528
1546
|
rename: {
|
|
1529
1547
|
prepareSupport: true,
|
|
1530
1548
|
prepareSupportDefaultBehavior: 1
|
|
@@ -1566,6 +1584,7 @@ class LspClientConnection extends LspClientTransport {
|
|
|
1566
1584
|
initializationOptions: this.server.initialization
|
|
1567
1585
|
}, { timeoutMs: this.initializeTimeoutMs });
|
|
1568
1586
|
this.setDiagnosticPullSupported(supportsDiagnosticPull(result?.capabilities));
|
|
1587
|
+
this.setDocumentFormattingSupported(supportsDocumentFormatting(result?.capabilities));
|
|
1569
1588
|
await this.sendNotification("initialized", {});
|
|
1570
1589
|
await this.sendNotification("workspace/didChangeConfiguration", {
|
|
1571
1590
|
settings: { json: { validate: { enable: true } } }
|
|
@@ -3104,6 +3123,18 @@ class LspClient extends LspClientConnection {
|
|
|
3104
3123
|
textDocument: { uri: pathToFileURL3(absPath).href }
|
|
3105
3124
|
}, options);
|
|
3106
3125
|
}
|
|
3126
|
+
async formatDocument(filePath, options, signal) {
|
|
3127
|
+
if (!this.isDocumentFormattingSupported())
|
|
3128
|
+
return null;
|
|
3129
|
+
const absPath = this.resolveWorkspacePath(filePath);
|
|
3130
|
+
await this.openFile(absPath);
|
|
3131
|
+
const requestOptions = signal === undefined ? {} : { signal };
|
|
3132
|
+
const edits = await this.sendRequest("textDocument/formatting", {
|
|
3133
|
+
textDocument: { uri: pathToFileURL3(absPath).href },
|
|
3134
|
+
options
|
|
3135
|
+
}, requestOptions);
|
|
3136
|
+
return edits ?? [];
|
|
3137
|
+
}
|
|
3107
3138
|
async workspaceSymbols(query, signal) {
|
|
3108
3139
|
const options = signal === undefined ? {} : { signal };
|
|
3109
3140
|
return this.sendRequest("workspace/symbol", { query }, options);
|
|
@@ -3352,6 +3383,7 @@ class LspManager {
|
|
|
3352
3383
|
this.disposed = false;
|
|
3353
3384
|
this.idleTimeoutMs = options.idleTimeoutMs ?? IDLE_TIMEOUT_MS;
|
|
3354
3385
|
this.initTimeoutMs = options.initTimeoutMs ?? INIT_TIMEOUT_MS;
|
|
3386
|
+
this.maxResidentClients = options.maxResidentClients ?? MAX_RESIDENT_CLIENTS;
|
|
3355
3387
|
this.reaperIntervalMs = options.reaperIntervalMs ?? REAPER_INTERVAL_MS;
|
|
3356
3388
|
this.clientFactory = options.clientFactory ?? ((root, server2) => new LspClient(root, server2));
|
|
3357
3389
|
this.now = options.now ?? (() => Date.now());
|
|
@@ -3385,6 +3417,26 @@ class LspManager {
|
|
|
3385
3417
|
}
|
|
3386
3418
|
}
|
|
3387
3419
|
}
|
|
3420
|
+
evictForAdmission() {
|
|
3421
|
+
while (this.clients.size >= this.maxResidentClients) {
|
|
3422
|
+
const victim = this.leastRecentlyUsedIdleClient();
|
|
3423
|
+
if (!victim)
|
|
3424
|
+
return;
|
|
3425
|
+
this.clients.delete(victim.key);
|
|
3426
|
+
stopClientBestEffort(victim.managed.client);
|
|
3427
|
+
}
|
|
3428
|
+
}
|
|
3429
|
+
leastRecentlyUsedIdleClient() {
|
|
3430
|
+
let candidate = null;
|
|
3431
|
+
for (const [key, managed] of this.clients) {
|
|
3432
|
+
if (managed.refCount > 0 || managed.pendingWaiters > 0 || managed.isInitializing)
|
|
3433
|
+
continue;
|
|
3434
|
+
if (candidate === null || managed.lastUsedAt < candidate.managed.lastUsedAt) {
|
|
3435
|
+
candidate = { key, managed };
|
|
3436
|
+
}
|
|
3437
|
+
}
|
|
3438
|
+
return candidate;
|
|
3439
|
+
}
|
|
3388
3440
|
async tryDeleteIfOrphaned(key, managed) {
|
|
3389
3441
|
if (managed.refCount === 0 && managed.pendingWaiters === 0 && !managed.isInitializing && this.clients.get(key) === managed) {
|
|
3390
3442
|
this.clients.delete(key);
|
|
@@ -3431,6 +3483,7 @@ class LspManager {
|
|
|
3431
3483
|
managed.lastUsedAt = this.now();
|
|
3432
3484
|
return managed.client;
|
|
3433
3485
|
}
|
|
3486
|
+
this.evictForAdmission();
|
|
3434
3487
|
const client = this.clientFactory(root, server2);
|
|
3435
3488
|
const initStartedAt = this.now();
|
|
3436
3489
|
const initPromise = (async () => {
|
|
@@ -3493,6 +3546,7 @@ class LspManager {
|
|
|
3493
3546
|
const key = this.getKey(root, server2.id);
|
|
3494
3547
|
if (this.clients.has(key))
|
|
3495
3548
|
return;
|
|
3549
|
+
this.evictForAdmission();
|
|
3496
3550
|
const client = this.clientFactory(root, server2);
|
|
3497
3551
|
const initStartedAt = this.now();
|
|
3498
3552
|
const initPromise = (async () => {
|
|
@@ -3602,50 +3656,6 @@ function nearestExistingAncestor(directory) {
|
|
|
3602
3656
|
}
|
|
3603
3657
|
return current;
|
|
3604
3658
|
}
|
|
3605
|
-
function getInstallDecisionsPath() {
|
|
3606
|
-
return lspRequestContext().installDecisionsPath;
|
|
3607
|
-
}
|
|
3608
|
-
function loadInstallDecisions() {
|
|
3609
|
-
const path2 = getInstallDecisionsPath();
|
|
3610
|
-
if (!existsSync8(path2))
|
|
3611
|
-
return {};
|
|
3612
|
-
try {
|
|
3613
|
-
const parsed = JSON.parse(readFileSync4(path2, "utf8"));
|
|
3614
|
-
return isInstallDecisions(parsed) ? parsed : {};
|
|
3615
|
-
} catch {
|
|
3616
|
-
return {};
|
|
3617
|
-
}
|
|
3618
|
-
}
|
|
3619
|
-
function loadInstallDecision(serverId) {
|
|
3620
|
-
return loadInstallDecisions()[serverId];
|
|
3621
|
-
}
|
|
3622
|
-
function recordInstallDecision(serverId, decision, decidedAt = new Date().toISOString()) {
|
|
3623
|
-
const decisions = loadInstallDecisions();
|
|
3624
|
-
decisions[serverId] = { decision, decidedAt };
|
|
3625
|
-
writeInstallDecisions(decisions);
|
|
3626
|
-
}
|
|
3627
|
-
function isInstallDecision(value) {
|
|
3628
|
-
return value === "declined" || value === "allowed";
|
|
3629
|
-
}
|
|
3630
|
-
function writeInstallDecisions(decisions) {
|
|
3631
|
-
const path2 = getInstallDecisionsPath();
|
|
3632
|
-
mkdirSync3(dirname8(path2), { recursive: true });
|
|
3633
|
-
const tmpPath = `${path2}.tmp`;
|
|
3634
|
-
writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
|
|
3635
|
-
`, "utf8");
|
|
3636
|
-
renameSync2(tmpPath, path2);
|
|
3637
|
-
}
|
|
3638
|
-
function isInstallDecisions(value) {
|
|
3639
|
-
return isRecord5(value) && Object.values(value).every(isInstallDecisionRecord);
|
|
3640
|
-
}
|
|
3641
|
-
function isInstallDecisionRecord(value) {
|
|
3642
|
-
if (!isRecord5(value))
|
|
3643
|
-
return false;
|
|
3644
|
-
return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
|
|
3645
|
-
}
|
|
3646
|
-
function isRecord5(value) {
|
|
3647
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3648
|
-
}
|
|
3649
3659
|
var LSP_INSTALL_HINTS = {
|
|
3650
3660
|
typescript: "npm install -g typescript-language-server typescript",
|
|
3651
3661
|
deno: "Install Deno from https://deno.land",
|
|
@@ -3690,6 +3700,26 @@ var LSP_INSTALL_HINTS = {
|
|
|
3690
3700
|
julials: `julia -e 'using Pkg; Pkg.add("LanguageServer")'`,
|
|
3691
3701
|
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"
|
|
3692
3702
|
};
|
|
3703
|
+
var LSP_LOCAL_INSTALL_HINTS = {
|
|
3704
|
+
typescript: "bun add -d typescript-language-server typescript",
|
|
3705
|
+
vue: "bun add -d @vue/language-server",
|
|
3706
|
+
eslint: "bun add -d vscode-langservers-extracted",
|
|
3707
|
+
oxlint: "bun add -d oxlint",
|
|
3708
|
+
biome: "bun add -d @biomejs/biome",
|
|
3709
|
+
svelte: "bun add -d svelte-language-server",
|
|
3710
|
+
astro: "bun add -d @astrojs/language-server",
|
|
3711
|
+
bash: "bun add -d bash-language-server",
|
|
3712
|
+
"bash-ls": "bun add -d bash-language-server",
|
|
3713
|
+
"yaml-ls": "bun add -d yaml-language-server",
|
|
3714
|
+
php: "bun add -d intelephense",
|
|
3715
|
+
prisma: "bun add -d prisma",
|
|
3716
|
+
dockerfile: "bun add -d dockerfile-language-server-nodejs",
|
|
3717
|
+
pyright: "uv add --dev pyright",
|
|
3718
|
+
basedpyright: "uv add --dev basedpyright",
|
|
3719
|
+
ruff: "uv add --dev ruff",
|
|
3720
|
+
ty: "uv add --dev ty",
|
|
3721
|
+
"ruby-lsp": "bundle add ruby-lsp --group development"
|
|
3722
|
+
};
|
|
3693
3723
|
var BUILTIN_SERVERS = {
|
|
3694
3724
|
typescript: {
|
|
3695
3725
|
command: ["typescript-language-server", "--stdio"],
|
|
@@ -3791,6 +3821,50 @@ var BUILTIN_SERVERS = {
|
|
|
3791
3821
|
extensions: [".razor", ".cshtml"]
|
|
3792
3822
|
}
|
|
3793
3823
|
};
|
|
3824
|
+
function getInstallDecisionsPath() {
|
|
3825
|
+
return lspRequestContext().installDecisionsPath;
|
|
3826
|
+
}
|
|
3827
|
+
function loadInstallDecisions() {
|
|
3828
|
+
const path2 = getInstallDecisionsPath();
|
|
3829
|
+
if (!existsSync8(path2))
|
|
3830
|
+
return {};
|
|
3831
|
+
try {
|
|
3832
|
+
const parsed = JSON.parse(readFileSync4(path2, "utf8"));
|
|
3833
|
+
return isInstallDecisions(parsed) ? parsed : {};
|
|
3834
|
+
} catch {
|
|
3835
|
+
return {};
|
|
3836
|
+
}
|
|
3837
|
+
}
|
|
3838
|
+
function loadInstallDecision(serverId) {
|
|
3839
|
+
return loadInstallDecisions()[serverId];
|
|
3840
|
+
}
|
|
3841
|
+
function recordInstallDecision(serverId, decision, decidedAt = new Date().toISOString()) {
|
|
3842
|
+
const decisions = loadInstallDecisions();
|
|
3843
|
+
decisions[serverId] = { decision, decidedAt };
|
|
3844
|
+
writeInstallDecisions(decisions);
|
|
3845
|
+
}
|
|
3846
|
+
function isInstallDecision(value) {
|
|
3847
|
+
return value === "declined" || value === "allowed";
|
|
3848
|
+
}
|
|
3849
|
+
function writeInstallDecisions(decisions) {
|
|
3850
|
+
const path2 = getInstallDecisionsPath();
|
|
3851
|
+
mkdirSync3(dirname8(path2), { recursive: true });
|
|
3852
|
+
const tmpPath = `${path2}.tmp`;
|
|
3853
|
+
writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
|
|
3854
|
+
`, "utf8");
|
|
3855
|
+
renameSync2(tmpPath, path2);
|
|
3856
|
+
}
|
|
3857
|
+
function isInstallDecisions(value) {
|
|
3858
|
+
return isRecord5(value) && Object.values(value).every(isInstallDecisionRecord);
|
|
3859
|
+
}
|
|
3860
|
+
function isInstallDecisionRecord(value) {
|
|
3861
|
+
if (!isRecord5(value))
|
|
3862
|
+
return false;
|
|
3863
|
+
return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
|
|
3864
|
+
}
|
|
3865
|
+
function isRecord5(value) {
|
|
3866
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3867
|
+
}
|
|
3794
3868
|
function getProjectConfigPaths() {
|
|
3795
3869
|
return lspRequestContext().projectConfigPaths;
|
|
3796
3870
|
}
|
|
@@ -3984,50 +4058,151 @@ function getDisabledServerIds() {
|
|
|
3984
4058
|
}
|
|
3985
4059
|
return disabled;
|
|
3986
4060
|
}
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
|
|
3990
|
-
|
|
3991
|
-
|
|
3992
|
-
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
4061
|
+
var LOCAL_BIN_RULES = [
|
|
4062
|
+
{
|
|
4063
|
+
markers: ["package.json", "bun.lock", "bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"],
|
|
4064
|
+
binDirs: [join4("node_modules", ".bin")]
|
|
4065
|
+
},
|
|
4066
|
+
{
|
|
4067
|
+
markers: [
|
|
4068
|
+
"pyproject.toml",
|
|
4069
|
+
"ty.toml",
|
|
4070
|
+
"requirements.txt",
|
|
4071
|
+
"setup.py",
|
|
4072
|
+
"setup.cfg",
|
|
4073
|
+
"Pipfile",
|
|
4074
|
+
"pyrightconfig.json",
|
|
4075
|
+
"ruff.toml",
|
|
4076
|
+
".ruff.toml"
|
|
4077
|
+
],
|
|
4078
|
+
binDirs: [
|
|
4079
|
+
join4(".venv", "bin"),
|
|
4080
|
+
join4(".venv", "Scripts"),
|
|
4081
|
+
join4("venv", "bin"),
|
|
4082
|
+
join4("venv", "Scripts"),
|
|
4083
|
+
join4(".env", "bin"),
|
|
4084
|
+
join4(".env", "Scripts")
|
|
4085
|
+
]
|
|
4086
|
+
},
|
|
4087
|
+
{
|
|
4088
|
+
markers: ["Gemfile", "Gemfile.lock"],
|
|
4089
|
+
binDirs: [join4("vendor", "bundle", "bin"), "bin"]
|
|
4090
|
+
},
|
|
4091
|
+
{
|
|
4092
|
+
markers: ["go.mod", "go.sum", "go.work"],
|
|
4093
|
+
binDirs: ["bin"]
|
|
4094
|
+
}
|
|
4095
|
+
];
|
|
4096
|
+
var resolutionCache = new Map;
|
|
4097
|
+
var probeCount = 0;
|
|
4098
|
+
function probe(path2) {
|
|
4099
|
+
probeCount += 1;
|
|
4100
|
+
return existsSync10(path2);
|
|
4101
|
+
}
|
|
4102
|
+
function executableSuffixes(platform, pathExt) {
|
|
4103
|
+
if (platform !== "win32")
|
|
4104
|
+
return [""];
|
|
4105
|
+
const configured = pathExt ?? process.env["PATHEXT"] ?? "";
|
|
4106
|
+
const systemExts = configured.split(";").filter((entry) => entry.length > 0).map((entry) => entry.toLowerCase());
|
|
4107
|
+
return [...new Set(["", ...systemExts, ".exe", ".cmd", ".bat", ".ps1"])];
|
|
4108
|
+
}
|
|
4109
|
+
function probeWithSuffixes(directory, command, suffixes) {
|
|
4110
|
+
for (const suffix of suffixes) {
|
|
4111
|
+
const candidate = join4(directory, command + suffix);
|
|
4112
|
+
if (probe(candidate))
|
|
4113
|
+
return candidate;
|
|
3996
4114
|
}
|
|
3997
|
-
|
|
3998
|
-
|
|
3999
|
-
|
|
4000
|
-
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4115
|
+
return null;
|
|
4116
|
+
}
|
|
4117
|
+
function resolveLocal(command, workingDirectory, suffixes, stopAt) {
|
|
4118
|
+
const boundary = stopAt === undefined ? undefined : resolve7(stopAt);
|
|
4119
|
+
let current = resolve7(workingDirectory);
|
|
4120
|
+
while (true) {
|
|
4121
|
+
for (const rule of LOCAL_BIN_RULES) {
|
|
4122
|
+
if (!rule.markers.some((marker) => probe(join4(current, marker))))
|
|
4123
|
+
continue;
|
|
4124
|
+
for (const binDir of rule.binDirs) {
|
|
4125
|
+
const found = probeWithSuffixes(join4(current, binDir), command, suffixes);
|
|
4126
|
+
if (found !== null)
|
|
4127
|
+
return found;
|
|
4128
|
+
}
|
|
4006
4129
|
}
|
|
4130
|
+
if (probe(join4(current, ".git")))
|
|
4131
|
+
return null;
|
|
4132
|
+
if (boundary !== undefined && current === boundary)
|
|
4133
|
+
return null;
|
|
4134
|
+
const parent = dirname9(current);
|
|
4135
|
+
if (parent === current)
|
|
4136
|
+
return null;
|
|
4137
|
+
current = parent;
|
|
4007
4138
|
}
|
|
4008
|
-
|
|
4009
|
-
|
|
4139
|
+
}
|
|
4140
|
+
function resolveFromPath(command, suffixes, platform, pathEnvOverride) {
|
|
4141
|
+
let pathEnv = pathEnvOverride ?? process.env["PATH"] ?? "";
|
|
4142
|
+
if (platform === "win32" && !pathEnv) {
|
|
4010
4143
|
pathEnv = process.env["Path"] ?? "";
|
|
4011
4144
|
}
|
|
4012
|
-
const
|
|
4013
|
-
|
|
4014
|
-
|
|
4015
|
-
|
|
4016
|
-
|
|
4017
|
-
|
|
4018
|
-
}
|
|
4145
|
+
for (const entry of pathEnv.split(delimiter3)) {
|
|
4146
|
+
if (entry.length === 0)
|
|
4147
|
+
continue;
|
|
4148
|
+
const found = probeWithSuffixes(entry, command, suffixes);
|
|
4149
|
+
if (found !== null)
|
|
4150
|
+
return found;
|
|
4019
4151
|
}
|
|
4020
|
-
|
|
4021
|
-
|
|
4022
|
-
|
|
4152
|
+
return null;
|
|
4153
|
+
}
|
|
4154
|
+
function resolveServerBinary(command, workingDirectory, options = {}) {
|
|
4155
|
+
if (command.length === 0)
|
|
4156
|
+
return null;
|
|
4157
|
+
const [cmd] = command;
|
|
4158
|
+
if (cmd === undefined || cmd.length === 0)
|
|
4159
|
+
return null;
|
|
4160
|
+
const platform = options.platform ?? process.platform;
|
|
4161
|
+
const suffixes = executableSuffixes(platform, options.pathExt);
|
|
4162
|
+
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
4163
|
+
const explicit = isAbsolute5(cmd) ? cmd : resolve7(workingDirectory ?? process.cwd(), cmd);
|
|
4164
|
+
return probe(explicit) ? explicit : null;
|
|
4165
|
+
}
|
|
4166
|
+
const cacheKey = JSON.stringify([
|
|
4167
|
+
workingDirectory ?? "",
|
|
4168
|
+
cmd,
|
|
4169
|
+
platform,
|
|
4170
|
+
options.stopAt ?? "",
|
|
4171
|
+
options.pathExt ?? "",
|
|
4172
|
+
options.pathEnv ?? process.env["PATH"] ?? ""
|
|
4173
|
+
]);
|
|
4174
|
+
const cached = resolutionCache.get(cacheKey);
|
|
4175
|
+
if (cached !== undefined)
|
|
4176
|
+
return cached;
|
|
4177
|
+
let resolved = null;
|
|
4178
|
+
if (workingDirectory !== undefined && workingDirectory.length > 0) {
|
|
4179
|
+
resolved = resolveLocal(cmd, workingDirectory, suffixes, options.stopAt);
|
|
4180
|
+
}
|
|
4181
|
+
resolved ??= resolveFromPath(cmd, suffixes, platform, options.pathEnv);
|
|
4182
|
+
if (resolved === null && cmd === "node")
|
|
4183
|
+
resolved = cmd;
|
|
4184
|
+
resolutionCache.set(cacheKey, resolved);
|
|
4185
|
+
return resolved;
|
|
4186
|
+
}
|
|
4187
|
+
function isServerInstalled(command, workingDirectory, options = {}) {
|
|
4188
|
+
if (command.length === 0)
|
|
4189
|
+
return false;
|
|
4190
|
+
return resolveServerBinary(command, workingDirectory, options) !== null;
|
|
4191
|
+
}
|
|
4192
|
+
function withResolvedCommand(command, binaryPath) {
|
|
4193
|
+
return [binaryPath, ...command.slice(1)];
|
|
4023
4194
|
}
|
|
4024
4195
|
function findServerForExtension(ext) {
|
|
4025
4196
|
const servers = getMergedServers();
|
|
4197
|
+
const workingDirectory = contextCwd();
|
|
4026
4198
|
for (const server2 of servers) {
|
|
4027
|
-
if (server2.extensions.includes(ext)
|
|
4199
|
+
if (!server2.extensions.includes(ext))
|
|
4200
|
+
continue;
|
|
4201
|
+
const binaryPath = resolveServerBinary(server2.command, workingDirectory);
|
|
4202
|
+
if (binaryPath !== null) {
|
|
4028
4203
|
const resolvedServer = {
|
|
4029
4204
|
id: server2.id,
|
|
4030
|
-
command: server2.command,
|
|
4205
|
+
command: withResolvedCommand(server2.command, binaryPath),
|
|
4031
4206
|
extensions: server2.extensions,
|
|
4032
4207
|
priority: server2.priority
|
|
4033
4208
|
};
|
|
@@ -4074,6 +4249,7 @@ function findServerForExtension(ext) {
|
|
|
4074
4249
|
function getAllServers() {
|
|
4075
4250
|
const servers = getMergedServers();
|
|
4076
4251
|
const disabled = getDisabledServerIds();
|
|
4252
|
+
const workingDirectory = contextCwd();
|
|
4077
4253
|
const result = [];
|
|
4078
4254
|
const seen = new Set;
|
|
4079
4255
|
for (const server2 of servers) {
|
|
@@ -4081,7 +4257,7 @@ function getAllServers() {
|
|
|
4081
4257
|
continue;
|
|
4082
4258
|
result.push({
|
|
4083
4259
|
id: server2.id,
|
|
4084
|
-
installed: isServerInstalled(server2.command),
|
|
4260
|
+
installed: isServerInstalled(server2.command, workingDirectory),
|
|
4085
4261
|
extensions: server2.extensions,
|
|
4086
4262
|
disabled: false,
|
|
4087
4263
|
source: server2.source,
|
|
@@ -4095,7 +4271,7 @@ function getAllServers() {
|
|
|
4095
4271
|
const builtin = BUILTIN_SERVERS[id];
|
|
4096
4272
|
result.push({
|
|
4097
4273
|
id,
|
|
4098
|
-
installed: builtin ? isServerInstalled(builtin.command) : false,
|
|
4274
|
+
installed: builtin ? isServerInstalled(builtin.command, workingDirectory) : false,
|
|
4099
4275
|
extensions: builtin?.extensions ?? [],
|
|
4100
4276
|
disabled: true,
|
|
4101
4277
|
source: "disabled",
|
|
@@ -4116,7 +4292,7 @@ function findWorkspaceRoot(filePath) {
|
|
|
4116
4292
|
const abs = resolveReadablePathInsideContext(filePath);
|
|
4117
4293
|
let dir = abs;
|
|
4118
4294
|
if (!isDirectoryPath(dir)) {
|
|
4119
|
-
dir =
|
|
4295
|
+
dir = dirname10(dir);
|
|
4120
4296
|
}
|
|
4121
4297
|
if (!isPathInside(cwd, abs))
|
|
4122
4298
|
return findWorkspaceRootOutsideContext(dir);
|
|
@@ -4132,13 +4308,13 @@ function findWorkspaceRoot(filePath) {
|
|
|
4132
4308
|
}
|
|
4133
4309
|
if (dir === cwd)
|
|
4134
4310
|
break;
|
|
4135
|
-
dir =
|
|
4311
|
+
dir = dirname10(dir);
|
|
4136
4312
|
}
|
|
4137
4313
|
return fallbackRoot;
|
|
4138
4314
|
}
|
|
4139
4315
|
function resolveReadablePathInsideContext(filePath) {
|
|
4140
4316
|
const cwd = contextCwd();
|
|
4141
|
-
const abs =
|
|
4317
|
+
const abs = resolve8(cwd, filePath);
|
|
4142
4318
|
if (isPathInside(cwd, abs))
|
|
4143
4319
|
return abs;
|
|
4144
4320
|
const rebased = rebaseThroughCanonicalAncestor(abs, cwd);
|
|
@@ -4164,7 +4340,7 @@ function rebaseThroughCanonicalAncestor(path2, cwd) {
|
|
|
4164
4340
|
if (isPathInside(cwd, canonical))
|
|
4165
4341
|
return suffix.length === 0 ? canonical : join5(canonical, ...suffix);
|
|
4166
4342
|
}
|
|
4167
|
-
const parent =
|
|
4343
|
+
const parent = dirname10(current);
|
|
4168
4344
|
if (parent === current)
|
|
4169
4345
|
return;
|
|
4170
4346
|
suffix.unshift(basename3(current));
|
|
@@ -4187,7 +4363,7 @@ function nearestExistingDirectoryInsideContext(directory, cwd) {
|
|
|
4187
4363
|
return canonical;
|
|
4188
4364
|
if (current === cwd)
|
|
4189
4365
|
return;
|
|
4190
|
-
current =
|
|
4366
|
+
current = dirname10(current);
|
|
4191
4367
|
}
|
|
4192
4368
|
return;
|
|
4193
4369
|
}
|
|
@@ -4214,6 +4390,19 @@ function formatServerLookupError(result) {
|
|
|
4214
4390
|
].join(`
|
|
4215
4391
|
`);
|
|
4216
4392
|
}
|
|
4393
|
+
function formatInstallOptions(serverId, installHint) {
|
|
4394
|
+
const localHint = LSP_LOCAL_INSTALL_HINTS[serverId];
|
|
4395
|
+
if (localHint === undefined) {
|
|
4396
|
+
return ["To install, run:", ` ${installHint}`];
|
|
4397
|
+
}
|
|
4398
|
+
return [
|
|
4399
|
+
"To install in THIS repository (preferred — no global install needed):",
|
|
4400
|
+
` ${localHint}`,
|
|
4401
|
+
"",
|
|
4402
|
+
"Or install it globally:",
|
|
4403
|
+
` ${installHint}`
|
|
4404
|
+
];
|
|
4405
|
+
}
|
|
4217
4406
|
function formatNotInstalled(result) {
|
|
4218
4407
|
const { server: server2, installHint } = result;
|
|
4219
4408
|
const extensions = server2.extensions.join(", ");
|
|
@@ -4228,19 +4417,19 @@ function formatNotInstalled(result) {
|
|
|
4228
4417
|
`Command not found: ${server2.command[0]}`,
|
|
4229
4418
|
""
|
|
4230
4419
|
];
|
|
4420
|
+
const installOptions = formatInstallOptions(server2.id, installHint);
|
|
4231
4421
|
if (decision === "allowed") {
|
|
4232
4422
|
return [
|
|
4233
4423
|
...header,
|
|
4234
4424
|
"The user has pre-authorized LSP installation. Run the install command, then retry this tool:",
|
|
4235
|
-
|
|
4425
|
+
...installOptions.slice(1)
|
|
4236
4426
|
].join(`
|
|
4237
4427
|
`);
|
|
4238
4428
|
}
|
|
4239
4429
|
if (!context.capabilities.installDecisionTool) {
|
|
4240
4430
|
return [
|
|
4241
4431
|
...header,
|
|
4242
|
-
|
|
4243
|
-
` ${installHint}`,
|
|
4432
|
+
...installOptions,
|
|
4244
4433
|
"",
|
|
4245
4434
|
"ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
|
|
4246
4435
|
"Install-decision recording is unavailable in this harness; proceed without LSP if the user declines."
|
|
@@ -4249,8 +4438,7 @@ function formatNotInstalled(result) {
|
|
|
4249
4438
|
}
|
|
4250
4439
|
return [
|
|
4251
4440
|
...header,
|
|
4252
|
-
|
|
4253
|
-
` ${installHint}`,
|
|
4441
|
+
...installOptions,
|
|
4254
4442
|
"",
|
|
4255
4443
|
"ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
|
|
4256
4444
|
"- If the user agrees: run the install command above, then retry this tool.",
|
|
@@ -4452,7 +4640,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4452
4640
|
if (!extension.startsWith(".")) {
|
|
4453
4641
|
throw new LspInvalidPathError(`Extension must start with a dot (e.g., ".ts", not "${extension}"). Use ".${extension}" instead.`);
|
|
4454
4642
|
}
|
|
4455
|
-
const absDir =
|
|
4643
|
+
const absDir = resolve9(options.workspaceRoot ?? contextCwd(), directory);
|
|
4456
4644
|
if (!existsSync12(absDir)) {
|
|
4457
4645
|
throw new LspInvalidPathError(`Directory does not exist: ${absDir}`);
|
|
4458
4646
|
}
|
|
@@ -4793,6 +4981,91 @@ async function executeLspDiagnostics(params, signal) {
|
|
|
4793
4981
|
throw error;
|
|
4794
4982
|
}
|
|
4795
4983
|
}
|
|
4984
|
+
var DEFAULT_FORMATTING_OPTIONS = {
|
|
4985
|
+
tabSize: 4,
|
|
4986
|
+
insertSpaces: false,
|
|
4987
|
+
trimTrailingWhitespace: true,
|
|
4988
|
+
insertFinalNewline: true,
|
|
4989
|
+
trimFinalNewlines: true
|
|
4990
|
+
};
|
|
4991
|
+
var UNCHANGED = { status: "unchanged", linesAdded: 0, linesRemoved: 0 };
|
|
4992
|
+
async function formatDocumentWithClient(client, filePath, options = {}) {
|
|
4993
|
+
const edits = await client.formatDocument(filePath, options.formattingOptions ?? DEFAULT_FORMATTING_OPTIONS, options.signal);
|
|
4994
|
+
if (edits === null)
|
|
4995
|
+
return { status: "unavailable", reason: "capability_not_advertised" };
|
|
4996
|
+
if (edits.length === 0)
|
|
4997
|
+
return UNCHANGED;
|
|
4998
|
+
const before = readFileSync6(filePath, "utf-8");
|
|
4999
|
+
const normalized = normalizeTextEdits(before, edits, 0);
|
|
5000
|
+
if (normalized.text === before)
|
|
5001
|
+
return UNCHANGED;
|
|
5002
|
+
writeAtomically(filePath, normalized.text);
|
|
5003
|
+
await client.openFile(filePath);
|
|
5004
|
+
return {
|
|
5005
|
+
status: "formatted",
|
|
5006
|
+
...lineDelta(normalized.edits)
|
|
5007
|
+
};
|
|
5008
|
+
}
|
|
5009
|
+
function lineDelta(edits) {
|
|
5010
|
+
let linesAdded = 0;
|
|
5011
|
+
let linesRemoved = 0;
|
|
5012
|
+
for (const edit of edits) {
|
|
5013
|
+
linesRemoved += edit.range.end.line - edit.range.start.line + 1;
|
|
5014
|
+
linesAdded += edit.newText.split(`
|
|
5015
|
+
`).length;
|
|
5016
|
+
}
|
|
5017
|
+
return { linesAdded, linesRemoved };
|
|
5018
|
+
}
|
|
5019
|
+
function writeAtomically(filePath, content) {
|
|
5020
|
+
const tempPath = `${filePath}.omo-format.tmp`;
|
|
5021
|
+
writeFileSync3(tempPath, content, "utf-8");
|
|
5022
|
+
try {
|
|
5023
|
+
renameSync3(tempPath, filePath);
|
|
5024
|
+
} catch (error) {
|
|
5025
|
+
try {
|
|
5026
|
+
unlinkSync2(tempPath);
|
|
5027
|
+
} catch {}
|
|
5028
|
+
throw error;
|
|
5029
|
+
}
|
|
5030
|
+
}
|
|
5031
|
+
async function executeLspFormat(params, signal) {
|
|
5032
|
+
const filePath = requireString(params, "filePath");
|
|
5033
|
+
try {
|
|
5034
|
+
const result = await withLspClient(filePath, async (client, _workspaceRoot, resolvedFilePath) => formatDocumentWithClient(client, resolvedFilePath, signal === undefined ? {} : { signal }), "format", clientOptions(signal));
|
|
5035
|
+
return text(describeResult(filePath, result), formatDetails(filePath, result));
|
|
5036
|
+
} catch (error) {
|
|
5037
|
+
const missingDependency = missingDependencyResult(error, {
|
|
5038
|
+
filePath,
|
|
5039
|
+
status: "unavailable",
|
|
5040
|
+
reason: "server_unavailable",
|
|
5041
|
+
linesAdded: 0,
|
|
5042
|
+
linesRemoved: 0
|
|
5043
|
+
});
|
|
5044
|
+
if (missingDependency)
|
|
5045
|
+
return missingDependency;
|
|
5046
|
+
throw error;
|
|
5047
|
+
}
|
|
5048
|
+
}
|
|
5049
|
+
function formatDetails(filePath, result) {
|
|
5050
|
+
if (result.status === "unavailable") {
|
|
5051
|
+
return { filePath, status: "unavailable", reason: result.reason, linesAdded: 0, linesRemoved: 0 };
|
|
5052
|
+
}
|
|
5053
|
+
return {
|
|
5054
|
+
filePath,
|
|
5055
|
+
status: result.status,
|
|
5056
|
+
linesAdded: result.linesAdded,
|
|
5057
|
+
linesRemoved: result.linesRemoved
|
|
5058
|
+
};
|
|
5059
|
+
}
|
|
5060
|
+
function describeResult(filePath, result) {
|
|
5061
|
+
if (result.status === "unavailable") {
|
|
5062
|
+
return `Formatting unavailable for ${filePath}: the language server does not advertise documentFormattingProvider.`;
|
|
5063
|
+
}
|
|
5064
|
+
if (result.status === "unchanged") {
|
|
5065
|
+
return `Already formatted: ${filePath}`;
|
|
5066
|
+
}
|
|
5067
|
+
return `Formatted ${filePath} (+${result.linesAdded}/-${result.linesRemoved} lines)`;
|
|
5068
|
+
}
|
|
4796
5069
|
async function executeLspInstallDecision(params) {
|
|
4797
5070
|
const serverId = requireString(params, "server_id");
|
|
4798
5071
|
const decision = params["decision"];
|
|
@@ -5112,6 +5385,16 @@ var LSP_MCP_TOOLS = [
|
|
|
5112
5385
|
}, ["filePath", "line", "character", "newName"]),
|
|
5113
5386
|
execute: executeLspRename
|
|
5114
5387
|
},
|
|
5388
|
+
{
|
|
5389
|
+
name: "format",
|
|
5390
|
+
aliases: ["lsp_format"],
|
|
5391
|
+
title: "LSP Format",
|
|
5392
|
+
description: "Format a source file with its language server and write the returned edits to disk.",
|
|
5393
|
+
inputSchema: objectSchema({
|
|
5394
|
+
filePath: { type: "string", description: "Source file path to format." }
|
|
5395
|
+
}, ["filePath"]),
|
|
5396
|
+
execute: executeLspFormat
|
|
5397
|
+
},
|
|
5115
5398
|
{
|
|
5116
5399
|
name: "install_decision",
|
|
5117
5400
|
aliases: ["lsp_install_decision"],
|
|
@@ -5178,7 +5461,7 @@ function withContext(args, context) {
|
|
|
5178
5461
|
function ensureDaemonAvailable(paths, ensure, signal) {
|
|
5179
5462
|
if (!signal)
|
|
5180
5463
|
return ensure(paths);
|
|
5181
|
-
return new Promise((
|
|
5464
|
+
return new Promise((resolve10, reject) => {
|
|
5182
5465
|
let settled = false;
|
|
5183
5466
|
const finish = (run) => {
|
|
5184
5467
|
if (settled)
|
|
@@ -5193,11 +5476,11 @@ function ensureDaemonAvailable(paths, ensure, signal) {
|
|
|
5193
5476
|
return;
|
|
5194
5477
|
}
|
|
5195
5478
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
5196
|
-
Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() =>
|
|
5479
|
+
Promise.resolve().then(() => ensure(paths, signal)).then(() => finish(() => resolve10()), (error) => finish(() => reject(signal.aborted ? new DaemonRequestCancelledError(false) : error)));
|
|
5197
5480
|
});
|
|
5198
5481
|
}
|
|
5199
5482
|
function sendToolCall(paths, token, name, args, options) {
|
|
5200
|
-
return new Promise((
|
|
5483
|
+
return new Promise((resolve10, reject) => {
|
|
5201
5484
|
const socket = connect(paths.socket);
|
|
5202
5485
|
const requestId = allocateProxyRequestId();
|
|
5203
5486
|
let settled = false;
|
|
@@ -5250,7 +5533,7 @@ function sendToolCall(paths, token, name, args, options) {
|
|
|
5250
5533
|
}
|
|
5251
5534
|
const result = toToolResult(message, requestId);
|
|
5252
5535
|
if (result)
|
|
5253
|
-
finish(() =>
|
|
5536
|
+
finish(() => resolve10(result));
|
|
5254
5537
|
else
|
|
5255
5538
|
finish(() => reject(new DaemonRequestError("invalid daemon response", requestWritten)));
|
|
5256
5539
|
});
|
|
@@ -5321,12 +5604,12 @@ function resolveDaemonRuntime2(env, defaults) {
|
|
|
5321
5604
|
throw new InvalidRuntimeOverrideError2("paired_values_required", `${OMO_LSP_DAEMON_CLI2} and ${OMO_LSP_DAEMON_VERSION2} must be set together`);
|
|
5322
5605
|
}
|
|
5323
5606
|
if (!hasCliOverride || !hasVersionOverride) {
|
|
5324
|
-
if (!
|
|
5607
|
+
if (!isAbsolute6(defaults.cliPath)) {
|
|
5325
5608
|
throw new InvalidRuntimeOverrideError2("packaged_cli_must_be_absolute", "Packaged LSP daemon CLI path must be absolute");
|
|
5326
5609
|
}
|
|
5327
5610
|
return { cliPath: defaults.cliPath, version: validateDaemonVersion2(defaults.version) };
|
|
5328
5611
|
}
|
|
5329
|
-
if (!
|
|
5612
|
+
if (!isAbsolute6(cliOverride)) {
|
|
5330
5613
|
throw new InvalidRuntimeOverrideError2("cli_must_be_absolute", `${OMO_LSP_DAEMON_CLI2} must be an absolute path to an existing regular file`);
|
|
5331
5614
|
}
|
|
5332
5615
|
const stat = statSync5(cliOverride, { throwIfNoEntry: false });
|
|
@@ -5448,7 +5731,7 @@ function extensionKey(filePath) {
|
|
|
5448
5731
|
// ../../../../lsp-core/src/request-context.ts
|
|
5449
5732
|
import { AsyncLocalStorage as AsyncLocalStorage2 } from "node:async_hooks";
|
|
5450
5733
|
import { existsSync as existsSync13, realpathSync as realpathSync6, statSync as statSync6 } from "node:fs";
|
|
5451
|
-
import { basename as basename4, delimiter as delimiter4, dirname as dirname2, isAbsolute as
|
|
5734
|
+
import { basename as basename4, delimiter as delimiter4, dirname as dirname2, isAbsolute as isAbsolute7, join as join8, relative as relative5, resolve as resolve10 } from "node:path";
|
|
5452
5735
|
|
|
5453
5736
|
class LspRequestContextParseError2 extends Error {
|
|
5454
5737
|
constructor(code, message) {
|
|
@@ -5489,14 +5772,14 @@ function parseLspRequestContext2(value) {
|
|
|
5489
5772
|
};
|
|
5490
5773
|
}
|
|
5491
5774
|
function canonicalCwd2(cwd) {
|
|
5492
|
-
const resolved =
|
|
5775
|
+
const resolved = resolve10(cwd);
|
|
5493
5776
|
if (!existsSync13(resolved) || !statSync6(resolved).isDirectory()) {
|
|
5494
5777
|
throw new LspRequestContextParseError2("invalid_cwd", `LSP request cwd must be an existing directory: ${cwd}`);
|
|
5495
5778
|
}
|
|
5496
5779
|
return realpathSync6(resolved);
|
|
5497
5780
|
}
|
|
5498
5781
|
function canonicalizeExistingOrNearestAncestor2(path2) {
|
|
5499
|
-
let current =
|
|
5782
|
+
let current = resolve10(path2);
|
|
5500
5783
|
const suffix = [];
|
|
5501
5784
|
while (true) {
|
|
5502
5785
|
try {
|
|
@@ -5539,14 +5822,14 @@ function stringArrayField2(value, field) {
|
|
|
5539
5822
|
return fieldValue;
|
|
5540
5823
|
}
|
|
5541
5824
|
function requireAbsolutePath2(path2, field) {
|
|
5542
|
-
if (!
|
|
5825
|
+
if (!isAbsolute7(path2)) {
|
|
5543
5826
|
throw new LspRequestContextParseError2("relative_path", `LSP request context.${field} must be absolute: ${path2}`);
|
|
5544
5827
|
}
|
|
5545
5828
|
}
|
|
5546
5829
|
function isPathInside2(parent, child) {
|
|
5547
|
-
const childPath =
|
|
5830
|
+
const childPath = resolve10(child);
|
|
5548
5831
|
const relativePath = relative5(parent, childPath);
|
|
5549
|
-
return relativePath === "" || !relativePath.startsWith("..") && !
|
|
5832
|
+
return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute7(relativePath);
|
|
5550
5833
|
}
|
|
5551
5834
|
function isMissingPathError2(error) {
|
|
5552
5835
|
const code = errorCode2(error);
|
|
@@ -5569,9 +5852,9 @@ function errorCode2(error) {
|
|
|
5569
5852
|
}
|
|
5570
5853
|
|
|
5571
5854
|
// src/daemon-cli-path.ts
|
|
5572
|
-
import { existsSync as existsSync15, readFileSync as
|
|
5855
|
+
import { existsSync as existsSync15, readFileSync as readFileSync7 } from "node:fs";
|
|
5573
5856
|
import { createRequire as createRequire2 } from "node:module";
|
|
5574
|
-
import { dirname as
|
|
5857
|
+
import { dirname as dirname11, join as join10 } from "node:path";
|
|
5575
5858
|
import { fileURLToPath as fileURLToPath4 } from "node:url";
|
|
5576
5859
|
var requireFromHere2 = createRequire2(import.meta.url);
|
|
5577
5860
|
var PACKAGE_LSP_DAEMON_CLI = "@code-yeongyu/lsp-daemon/cli";
|
|
@@ -5618,7 +5901,7 @@ function resolveConfiguredLspDaemonCli(cliPath) {
|
|
|
5618
5901
|
}
|
|
5619
5902
|
function readDaemonPackageVersion(cliPath) {
|
|
5620
5903
|
try {
|
|
5621
|
-
const parsed = JSON.parse(
|
|
5904
|
+
const parsed = JSON.parse(readFileSync7(join10(dirname11(cliPath), "package.json"), "utf8"));
|
|
5622
5905
|
if (isRecord9(parsed) && typeof parsed["version"] === "string" && parsed["version"].length > 0) {
|
|
5623
5906
|
return parsed["version"];
|
|
5624
5907
|
}
|
|
@@ -5633,9 +5916,9 @@ function isRecord9(value) {
|
|
|
5633
5916
|
}
|
|
5634
5917
|
|
|
5635
5918
|
// src/lsp-session-state.ts
|
|
5636
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
5919
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
5637
5920
|
import { homedir } from "node:os";
|
|
5638
|
-
import { dirname as
|
|
5921
|
+
import { dirname as dirname13, join as join11 } from "node:path";
|
|
5639
5922
|
function sessionIdFrom(input) {
|
|
5640
5923
|
return typeof input.session_id === "string" && input.session_id.length > 0 ? input.session_id : undefined;
|
|
5641
5924
|
}
|
|
@@ -5666,7 +5949,7 @@ function sessionStatePath(sessionId) {
|
|
|
5666
5949
|
}
|
|
5667
5950
|
function readSessionState(path2) {
|
|
5668
5951
|
try {
|
|
5669
|
-
const parsed = JSON.parse(
|
|
5952
|
+
const parsed = JSON.parse(readFileSync9(path2, "utf8"));
|
|
5670
5953
|
if (isRecord10(parsed) && isLspSessionState(parsed))
|
|
5671
5954
|
return normalizeSessionState(parsed);
|
|
5672
5955
|
return emptyState();
|
|
@@ -5677,8 +5960,8 @@ function readSessionState(path2) {
|
|
|
5677
5960
|
}
|
|
5678
5961
|
}
|
|
5679
5962
|
function writeSessionState(path2, state) {
|
|
5680
|
-
mkdirSync4(
|
|
5681
|
-
|
|
5963
|
+
mkdirSync4(dirname13(path2), { recursive: true });
|
|
5964
|
+
writeFileSync4(path2, `${JSON.stringify(state)}
|
|
5682
5965
|
`);
|
|
5683
5966
|
}
|
|
5684
5967
|
function emptyState() {
|
|
@@ -5821,8 +6104,8 @@ function notConfiguredAvailability(details) {
|
|
|
5821
6104
|
return typeof extension === "string" && extension.length > 0 ? { extension } : undefined;
|
|
5822
6105
|
}
|
|
5823
6106
|
function codexLspRequestContext(env = process.env, cwd = process.cwd()) {
|
|
5824
|
-
const canonicalCwd3 = realpathSync8(
|
|
5825
|
-
const codexHome =
|
|
6107
|
+
const canonicalCwd3 = realpathSync8(resolve12(cwd));
|
|
6108
|
+
const codexHome = resolve12(env["CODEX_HOME"]?.trim() || join12(homedir3(), ".codex"));
|
|
5826
6109
|
return parseLspRequestContext2({
|
|
5827
6110
|
cwd: canonicalCwd3,
|
|
5828
6111
|
projectConfigPaths: [join12(canonicalCwd3, ".codex", "lsp-client.json")],
|
|
@@ -5921,7 +6204,7 @@ function isContextPressureTranscript(transcriptPath) {
|
|
|
5921
6204
|
if (typeof transcriptPath !== "string")
|
|
5922
6205
|
return false;
|
|
5923
6206
|
try {
|
|
5924
|
-
return hasContextPressureMarker(
|
|
6207
|
+
return hasContextPressureMarker(readFileSync10(transcriptPath, "utf8"));
|
|
5925
6208
|
} catch (error) {
|
|
5926
6209
|
if (error instanceof Error)
|
|
5927
6210
|
return false;
|
|
@@ -6004,14 +6287,14 @@ main().catch((error) => {
|
|
|
6004
6287
|
async function runPackageLspMcpCli() {
|
|
6005
6288
|
const cliPath = resolveLspDaemonCliPath();
|
|
6006
6289
|
const child = spawn3(execPath2, [cliPath, "mcp"], { stdio: "inherit", windowsHide: true });
|
|
6007
|
-
await new Promise((
|
|
6290
|
+
await new Promise((resolve11, reject) => {
|
|
6008
6291
|
child.once("error", reject);
|
|
6009
6292
|
child.once("exit", (code, signal) => {
|
|
6010
6293
|
if (code !== null && code !== 0)
|
|
6011
6294
|
process.exitCode = code;
|
|
6012
6295
|
if (code === null && signal !== null)
|
|
6013
6296
|
process.exitCode = 1;
|
|
6014
|
-
|
|
6297
|
+
resolve11();
|
|
6015
6298
|
});
|
|
6016
6299
|
});
|
|
6017
6300
|
}
|