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
|
@@ -311,7 +311,7 @@ function createIdleTimer(idleTimeoutMs, log, onTimeout) {
|
|
|
311
311
|
}
|
|
312
312
|
// ../lsp-core/src/lsp/client-wrapper.ts
|
|
313
313
|
import { existsSync as existsSync10, realpathSync as realpathSync5, statSync as statSync3 } from "node:fs";
|
|
314
|
-
import { basename as basename3, dirname as
|
|
314
|
+
import { basename as basename3, dirname as dirname8, join as join5, resolve as resolve8 } from "node:path";
|
|
315
315
|
|
|
316
316
|
// ../lsp-core/src/request-context.ts
|
|
317
317
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -593,6 +593,7 @@ var DEFAULT_MAX_DIRECTORY_FILES = 50;
|
|
|
593
593
|
var REQUEST_TIMEOUT_MS = 15000;
|
|
594
594
|
var INIT_TIMEOUT_MS = 60000;
|
|
595
595
|
var IDLE_TIMEOUT_MS = 5 * 60000;
|
|
596
|
+
var MAX_RESIDENT_CLIENTS = 6;
|
|
596
597
|
var REAPER_INTERVAL_MS = 60000;
|
|
597
598
|
var STOP_HARD_KILL_TIMEOUT_MS = 5000;
|
|
598
599
|
var STOP_SIGKILL_GRACE_MS = 1000;
|
|
@@ -1118,6 +1119,7 @@ class LspClientTransport {
|
|
|
1118
1119
|
this.diagnosticsStore = new Map;
|
|
1119
1120
|
this.workspaceApplyEditHandler = null;
|
|
1120
1121
|
this.diagnosticPullSupported = false;
|
|
1122
|
+
this.documentFormattingSupported = false;
|
|
1121
1123
|
this.requestTimeoutMs = timeouts.requestTimeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
1122
1124
|
this.initializeTimeoutMs = timeouts.initializeTimeoutMs ?? INIT_TIMEOUT_MS;
|
|
1123
1125
|
}
|
|
@@ -1139,6 +1141,12 @@ class LspClientTransport {
|
|
|
1139
1141
|
isDiagnosticPullSupported() {
|
|
1140
1142
|
return this.diagnosticPullSupported;
|
|
1141
1143
|
}
|
|
1144
|
+
setDocumentFormattingSupported(supported) {
|
|
1145
|
+
this.documentFormattingSupported = supported;
|
|
1146
|
+
}
|
|
1147
|
+
isDocumentFormattingSupported() {
|
|
1148
|
+
return this.documentFormattingSupported;
|
|
1149
|
+
}
|
|
1142
1150
|
handlePublishDiagnostics(params) {
|
|
1143
1151
|
this.diagnosticsStore.set(params.uri, [...params.diagnostics]);
|
|
1144
1152
|
}
|
|
@@ -1352,6 +1360,14 @@ function supportsDiagnosticPull(capabilities) {
|
|
|
1352
1360
|
return false;
|
|
1353
1361
|
return Object.hasOwn(capabilities, "diagnosticProvider");
|
|
1354
1362
|
}
|
|
1363
|
+
function supportsDocumentFormatting(capabilities) {
|
|
1364
|
+
if (capabilities === undefined)
|
|
1365
|
+
return false;
|
|
1366
|
+
const provider = capabilities.documentFormattingProvider;
|
|
1367
|
+
if (provider === undefined || provider === null || provider === false)
|
|
1368
|
+
return false;
|
|
1369
|
+
return provider === true || typeof provider === "object";
|
|
1370
|
+
}
|
|
1355
1371
|
|
|
1356
1372
|
class LspClientConnection extends LspClientTransport {
|
|
1357
1373
|
async initialize() {
|
|
@@ -1368,6 +1384,7 @@ class LspClientConnection extends LspClientTransport {
|
|
|
1368
1384
|
references: {},
|
|
1369
1385
|
documentSymbol: { hierarchicalDocumentSymbolSupport: true },
|
|
1370
1386
|
publishDiagnostics: {},
|
|
1387
|
+
formatting: { dynamicRegistration: false },
|
|
1371
1388
|
rename: {
|
|
1372
1389
|
prepareSupport: true,
|
|
1373
1390
|
prepareSupportDefaultBehavior: 1
|
|
@@ -1409,6 +1426,7 @@ class LspClientConnection extends LspClientTransport {
|
|
|
1409
1426
|
initializationOptions: this.server.initialization
|
|
1410
1427
|
}, { timeoutMs: this.initializeTimeoutMs });
|
|
1411
1428
|
this.setDiagnosticPullSupported(supportsDiagnosticPull(result?.capabilities));
|
|
1429
|
+
this.setDocumentFormattingSupported(supportsDocumentFormatting(result?.capabilities));
|
|
1412
1430
|
await this.sendNotification("initialized", {});
|
|
1413
1431
|
await this.sendNotification("workspace/didChangeConfiguration", {
|
|
1414
1432
|
settings: { json: { validate: { enable: true } } }
|
|
@@ -2993,6 +3011,18 @@ class LspClient extends LspClientConnection {
|
|
|
2993
3011
|
textDocument: { uri: pathToFileURL3(absPath).href }
|
|
2994
3012
|
}, options);
|
|
2995
3013
|
}
|
|
3014
|
+
async formatDocument(filePath, options, signal) {
|
|
3015
|
+
if (!this.isDocumentFormattingSupported())
|
|
3016
|
+
return null;
|
|
3017
|
+
const absPath = this.resolveWorkspacePath(filePath);
|
|
3018
|
+
await this.openFile(absPath);
|
|
3019
|
+
const requestOptions = signal === undefined ? {} : { signal };
|
|
3020
|
+
const edits = await this.sendRequest("textDocument/formatting", {
|
|
3021
|
+
textDocument: { uri: pathToFileURL3(absPath).href },
|
|
3022
|
+
options
|
|
3023
|
+
}, requestOptions);
|
|
3024
|
+
return edits ?? [];
|
|
3025
|
+
}
|
|
2996
3026
|
async workspaceSymbols(query, signal) {
|
|
2997
3027
|
const options = signal === undefined ? {} : { signal };
|
|
2998
3028
|
return this.sendRequest("workspace/symbol", { query }, options);
|
|
@@ -3245,6 +3275,7 @@ class LspManager {
|
|
|
3245
3275
|
this.disposed = false;
|
|
3246
3276
|
this.idleTimeoutMs = options.idleTimeoutMs ?? IDLE_TIMEOUT_MS;
|
|
3247
3277
|
this.initTimeoutMs = options.initTimeoutMs ?? INIT_TIMEOUT_MS;
|
|
3278
|
+
this.maxResidentClients = options.maxResidentClients ?? MAX_RESIDENT_CLIENTS;
|
|
3248
3279
|
this.reaperIntervalMs = options.reaperIntervalMs ?? REAPER_INTERVAL_MS;
|
|
3249
3280
|
this.clientFactory = options.clientFactory ?? ((root, server2) => new LspClient(root, server2));
|
|
3250
3281
|
this.now = options.now ?? (() => Date.now());
|
|
@@ -3278,6 +3309,26 @@ class LspManager {
|
|
|
3278
3309
|
}
|
|
3279
3310
|
}
|
|
3280
3311
|
}
|
|
3312
|
+
evictForAdmission() {
|
|
3313
|
+
while (this.clients.size >= this.maxResidentClients) {
|
|
3314
|
+
const victim = this.leastRecentlyUsedIdleClient();
|
|
3315
|
+
if (!victim)
|
|
3316
|
+
return;
|
|
3317
|
+
this.clients.delete(victim.key);
|
|
3318
|
+
stopClientBestEffort(victim.managed.client);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
leastRecentlyUsedIdleClient() {
|
|
3322
|
+
let candidate = null;
|
|
3323
|
+
for (const [key, managed] of this.clients) {
|
|
3324
|
+
if (managed.refCount > 0 || managed.pendingWaiters > 0 || managed.isInitializing)
|
|
3325
|
+
continue;
|
|
3326
|
+
if (candidate === null || managed.lastUsedAt < candidate.managed.lastUsedAt) {
|
|
3327
|
+
candidate = { key, managed };
|
|
3328
|
+
}
|
|
3329
|
+
}
|
|
3330
|
+
return candidate;
|
|
3331
|
+
}
|
|
3281
3332
|
async tryDeleteIfOrphaned(key, managed) {
|
|
3282
3333
|
if (managed.refCount === 0 && managed.pendingWaiters === 0 && !managed.isInitializing && this.clients.get(key) === managed) {
|
|
3283
3334
|
this.clients.delete(key);
|
|
@@ -3324,6 +3375,7 @@ class LspManager {
|
|
|
3324
3375
|
managed.lastUsedAt = this.now();
|
|
3325
3376
|
return managed.client;
|
|
3326
3377
|
}
|
|
3378
|
+
this.evictForAdmission();
|
|
3327
3379
|
const client = this.clientFactory(root, server2);
|
|
3328
3380
|
const initStartedAt = this.now();
|
|
3329
3381
|
const initPromise = (async () => {
|
|
@@ -3386,6 +3438,7 @@ class LspManager {
|
|
|
3386
3438
|
const key = this.getKey(root, server2.id);
|
|
3387
3439
|
if (this.clients.has(key))
|
|
3388
3440
|
return;
|
|
3441
|
+
this.evictForAdmission();
|
|
3389
3442
|
const client = this.clientFactory(root, server2);
|
|
3390
3443
|
const initStartedAt = this.now();
|
|
3391
3444
|
const initPromise = (async () => {
|
|
@@ -3511,57 +3564,6 @@ function nearestExistingAncestor(directory) {
|
|
|
3511
3564
|
return current;
|
|
3512
3565
|
}
|
|
3513
3566
|
|
|
3514
|
-
// ../lsp-core/src/lsp/server-install-state.ts
|
|
3515
|
-
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3516
|
-
import { dirname as dirname6 } from "node:path";
|
|
3517
|
-
function getInstallDecisionsPath() {
|
|
3518
|
-
return lspRequestContext().installDecisionsPath;
|
|
3519
|
-
}
|
|
3520
|
-
function loadInstallDecisions() {
|
|
3521
|
-
const path = getInstallDecisionsPath();
|
|
3522
|
-
if (!existsSync7(path))
|
|
3523
|
-
return {};
|
|
3524
|
-
try {
|
|
3525
|
-
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
3526
|
-
return isInstallDecisions(parsed) ? parsed : {};
|
|
3527
|
-
} catch {
|
|
3528
|
-
return {};
|
|
3529
|
-
}
|
|
3530
|
-
}
|
|
3531
|
-
function loadInstallDecision(serverId) {
|
|
3532
|
-
return loadInstallDecisions()[serverId];
|
|
3533
|
-
}
|
|
3534
|
-
function recordInstallDecision(serverId, decision, decidedAt = new Date().toISOString()) {
|
|
3535
|
-
const decisions = loadInstallDecisions();
|
|
3536
|
-
decisions[serverId] = { decision, decidedAt };
|
|
3537
|
-
writeInstallDecisions(decisions);
|
|
3538
|
-
}
|
|
3539
|
-
function isInstallDecision(value) {
|
|
3540
|
-
return value === "declined" || value === "allowed";
|
|
3541
|
-
}
|
|
3542
|
-
function writeInstallDecisions(decisions) {
|
|
3543
|
-
const path = getInstallDecisionsPath();
|
|
3544
|
-
mkdirSync(dirname6(path), { recursive: true });
|
|
3545
|
-
const tmpPath = `${path}.tmp`;
|
|
3546
|
-
writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
|
|
3547
|
-
`, "utf8");
|
|
3548
|
-
renameSync2(tmpPath, path);
|
|
3549
|
-
}
|
|
3550
|
-
function isInstallDecisions(value) {
|
|
3551
|
-
return isRecord5(value) && Object.values(value).every(isInstallDecisionRecord);
|
|
3552
|
-
}
|
|
3553
|
-
function isInstallDecisionRecord(value) {
|
|
3554
|
-
if (!isRecord5(value))
|
|
3555
|
-
return false;
|
|
3556
|
-
return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
|
|
3557
|
-
}
|
|
3558
|
-
function isRecord5(value) {
|
|
3559
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3560
|
-
}
|
|
3561
|
-
|
|
3562
|
-
// ../lsp-core/src/lsp/config-loader.ts
|
|
3563
|
-
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
|
|
3564
|
-
|
|
3565
3567
|
// ../lsp-core/src/lsp/server-definitions.ts
|
|
3566
3568
|
var LSP_INSTALL_HINTS = {
|
|
3567
3569
|
typescript: "npm install -g typescript-language-server typescript",
|
|
@@ -3607,6 +3609,26 @@ var LSP_INSTALL_HINTS = {
|
|
|
3607
3609
|
julials: `julia -e 'using Pkg; Pkg.add("LanguageServer")'`,
|
|
3608
3610
|
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"
|
|
3609
3611
|
};
|
|
3612
|
+
var LSP_LOCAL_INSTALL_HINTS = {
|
|
3613
|
+
typescript: "bun add -d typescript-language-server typescript",
|
|
3614
|
+
vue: "bun add -d @vue/language-server",
|
|
3615
|
+
eslint: "bun add -d vscode-langservers-extracted",
|
|
3616
|
+
oxlint: "bun add -d oxlint",
|
|
3617
|
+
biome: "bun add -d @biomejs/biome",
|
|
3618
|
+
svelte: "bun add -d svelte-language-server",
|
|
3619
|
+
astro: "bun add -d @astrojs/language-server",
|
|
3620
|
+
bash: "bun add -d bash-language-server",
|
|
3621
|
+
"bash-ls": "bun add -d bash-language-server",
|
|
3622
|
+
"yaml-ls": "bun add -d yaml-language-server",
|
|
3623
|
+
php: "bun add -d intelephense",
|
|
3624
|
+
prisma: "bun add -d prisma",
|
|
3625
|
+
dockerfile: "bun add -d dockerfile-language-server-nodejs",
|
|
3626
|
+
pyright: "uv add --dev pyright",
|
|
3627
|
+
basedpyright: "uv add --dev basedpyright",
|
|
3628
|
+
ruff: "uv add --dev ruff",
|
|
3629
|
+
ty: "uv add --dev ty",
|
|
3630
|
+
"ruby-lsp": "bundle add ruby-lsp --group development"
|
|
3631
|
+
};
|
|
3610
3632
|
var BUILTIN_SERVERS = {
|
|
3611
3633
|
typescript: {
|
|
3612
3634
|
command: ["typescript-language-server", "--stdio"],
|
|
@@ -3709,7 +3731,56 @@ var BUILTIN_SERVERS = {
|
|
|
3709
3731
|
}
|
|
3710
3732
|
};
|
|
3711
3733
|
|
|
3734
|
+
// ../lsp-core/src/lsp/server-install-state.ts
|
|
3735
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3736
|
+
import { dirname as dirname6 } from "node:path";
|
|
3737
|
+
function getInstallDecisionsPath() {
|
|
3738
|
+
return lspRequestContext().installDecisionsPath;
|
|
3739
|
+
}
|
|
3740
|
+
function loadInstallDecisions() {
|
|
3741
|
+
const path = getInstallDecisionsPath();
|
|
3742
|
+
if (!existsSync7(path))
|
|
3743
|
+
return {};
|
|
3744
|
+
try {
|
|
3745
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
3746
|
+
return isInstallDecisions(parsed) ? parsed : {};
|
|
3747
|
+
} catch {
|
|
3748
|
+
return {};
|
|
3749
|
+
}
|
|
3750
|
+
}
|
|
3751
|
+
function loadInstallDecision(serverId) {
|
|
3752
|
+
return loadInstallDecisions()[serverId];
|
|
3753
|
+
}
|
|
3754
|
+
function recordInstallDecision(serverId, decision, decidedAt = new Date().toISOString()) {
|
|
3755
|
+
const decisions = loadInstallDecisions();
|
|
3756
|
+
decisions[serverId] = { decision, decidedAt };
|
|
3757
|
+
writeInstallDecisions(decisions);
|
|
3758
|
+
}
|
|
3759
|
+
function isInstallDecision(value) {
|
|
3760
|
+
return value === "declined" || value === "allowed";
|
|
3761
|
+
}
|
|
3762
|
+
function writeInstallDecisions(decisions) {
|
|
3763
|
+
const path = getInstallDecisionsPath();
|
|
3764
|
+
mkdirSync(dirname6(path), { recursive: true });
|
|
3765
|
+
const tmpPath = `${path}.tmp`;
|
|
3766
|
+
writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
|
|
3767
|
+
`, "utf8");
|
|
3768
|
+
renameSync2(tmpPath, path);
|
|
3769
|
+
}
|
|
3770
|
+
function isInstallDecisions(value) {
|
|
3771
|
+
return isRecord5(value) && Object.values(value).every(isInstallDecisionRecord);
|
|
3772
|
+
}
|
|
3773
|
+
function isInstallDecisionRecord(value) {
|
|
3774
|
+
if (!isRecord5(value))
|
|
3775
|
+
return false;
|
|
3776
|
+
return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
|
|
3777
|
+
}
|
|
3778
|
+
function isRecord5(value) {
|
|
3779
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3780
|
+
}
|
|
3781
|
+
|
|
3712
3782
|
// ../lsp-core/src/lsp/config-loader.ts
|
|
3783
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
|
|
3713
3784
|
function getProjectConfigPaths() {
|
|
3714
3785
|
return lspRequestContext().projectConfigPaths;
|
|
3715
3786
|
}
|
|
@@ -3906,53 +3977,154 @@ function getDisabledServerIds() {
|
|
|
3906
3977
|
|
|
3907
3978
|
// ../lsp-core/src/lsp/server-installation.ts
|
|
3908
3979
|
import { existsSync as existsSync9 } from "node:fs";
|
|
3909
|
-
import { delimiter as delimiter3, join as join4 } from "node:path";
|
|
3910
|
-
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3980
|
+
import { delimiter as delimiter3, dirname as dirname7, isAbsolute as isAbsolute3, join as join4, resolve as resolve7 } from "node:path";
|
|
3981
|
+
var LOCAL_BIN_RULES = [
|
|
3982
|
+
{
|
|
3983
|
+
markers: ["package.json", "bun.lock", "bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"],
|
|
3984
|
+
binDirs: [join4("node_modules", ".bin")]
|
|
3985
|
+
},
|
|
3986
|
+
{
|
|
3987
|
+
markers: [
|
|
3988
|
+
"pyproject.toml",
|
|
3989
|
+
"ty.toml",
|
|
3990
|
+
"requirements.txt",
|
|
3991
|
+
"setup.py",
|
|
3992
|
+
"setup.cfg",
|
|
3993
|
+
"Pipfile",
|
|
3994
|
+
"pyrightconfig.json",
|
|
3995
|
+
"ruff.toml",
|
|
3996
|
+
".ruff.toml"
|
|
3997
|
+
],
|
|
3998
|
+
binDirs: [
|
|
3999
|
+
join4(".venv", "bin"),
|
|
4000
|
+
join4(".venv", "Scripts"),
|
|
4001
|
+
join4("venv", "bin"),
|
|
4002
|
+
join4("venv", "Scripts"),
|
|
4003
|
+
join4(".env", "bin"),
|
|
4004
|
+
join4(".env", "Scripts")
|
|
4005
|
+
]
|
|
4006
|
+
},
|
|
4007
|
+
{
|
|
4008
|
+
markers: ["Gemfile", "Gemfile.lock"],
|
|
4009
|
+
binDirs: [join4("vendor", "bundle", "bin"), "bin"]
|
|
4010
|
+
},
|
|
4011
|
+
{
|
|
4012
|
+
markers: ["go.mod", "go.sum", "go.work"],
|
|
4013
|
+
binDirs: ["bin"]
|
|
3919
4014
|
}
|
|
3920
|
-
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
4015
|
+
];
|
|
4016
|
+
var resolutionCache = new Map;
|
|
4017
|
+
var probeCount = 0;
|
|
4018
|
+
function probe(path) {
|
|
4019
|
+
probeCount += 1;
|
|
4020
|
+
return existsSync9(path);
|
|
4021
|
+
}
|
|
4022
|
+
function executableSuffixes(platform, pathExt) {
|
|
4023
|
+
if (platform !== "win32")
|
|
4024
|
+
return [""];
|
|
4025
|
+
const configured = pathExt ?? process.env["PATHEXT"] ?? "";
|
|
4026
|
+
const systemExts = configured.split(";").filter((entry) => entry.length > 0).map((entry) => entry.toLowerCase());
|
|
4027
|
+
return [...new Set(["", ...systemExts, ".exe", ".cmd", ".bat", ".ps1"])];
|
|
4028
|
+
}
|
|
4029
|
+
function probeWithSuffixes(directory, command, suffixes) {
|
|
4030
|
+
for (const suffix of suffixes) {
|
|
4031
|
+
const candidate = join4(directory, command + suffix);
|
|
4032
|
+
if (probe(candidate))
|
|
4033
|
+
return candidate;
|
|
4034
|
+
}
|
|
4035
|
+
return null;
|
|
4036
|
+
}
|
|
4037
|
+
function resolveLocal(command, workingDirectory, suffixes, stopAt) {
|
|
4038
|
+
const boundary = stopAt === undefined ? undefined : resolve7(stopAt);
|
|
4039
|
+
let current = resolve7(workingDirectory);
|
|
4040
|
+
while (true) {
|
|
4041
|
+
for (const rule of LOCAL_BIN_RULES) {
|
|
4042
|
+
if (!rule.markers.some((marker) => probe(join4(current, marker))))
|
|
4043
|
+
continue;
|
|
4044
|
+
for (const binDir of rule.binDirs) {
|
|
4045
|
+
const found = probeWithSuffixes(join4(current, binDir), command, suffixes);
|
|
4046
|
+
if (found !== null)
|
|
4047
|
+
return found;
|
|
4048
|
+
}
|
|
3929
4049
|
}
|
|
4050
|
+
if (probe(join4(current, ".git")))
|
|
4051
|
+
return null;
|
|
4052
|
+
if (boundary !== undefined && current === boundary)
|
|
4053
|
+
return null;
|
|
4054
|
+
const parent = dirname7(current);
|
|
4055
|
+
if (parent === current)
|
|
4056
|
+
return null;
|
|
4057
|
+
current = parent;
|
|
3930
4058
|
}
|
|
3931
|
-
|
|
3932
|
-
|
|
4059
|
+
}
|
|
4060
|
+
function resolveFromPath(command, suffixes, platform, pathEnvOverride) {
|
|
4061
|
+
let pathEnv = pathEnvOverride ?? process.env["PATH"] ?? "";
|
|
4062
|
+
if (platform === "win32" && !pathEnv) {
|
|
3933
4063
|
pathEnv = process.env["Path"] ?? "";
|
|
3934
4064
|
}
|
|
3935
|
-
const
|
|
3936
|
-
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
}
|
|
4065
|
+
for (const entry of pathEnv.split(delimiter3)) {
|
|
4066
|
+
if (entry.length === 0)
|
|
4067
|
+
continue;
|
|
4068
|
+
const found = probeWithSuffixes(entry, command, suffixes);
|
|
4069
|
+
if (found !== null)
|
|
4070
|
+
return found;
|
|
3942
4071
|
}
|
|
3943
|
-
|
|
3944
|
-
|
|
3945
|
-
|
|
4072
|
+
return null;
|
|
4073
|
+
}
|
|
4074
|
+
function resolveServerBinary(command, workingDirectory, options = {}) {
|
|
4075
|
+
if (command.length === 0)
|
|
4076
|
+
return null;
|
|
4077
|
+
const [cmd] = command;
|
|
4078
|
+
if (cmd === undefined || cmd.length === 0)
|
|
4079
|
+
return null;
|
|
4080
|
+
const platform = options.platform ?? process.platform;
|
|
4081
|
+
const suffixes = executableSuffixes(platform, options.pathExt);
|
|
4082
|
+
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
4083
|
+
const explicit = isAbsolute3(cmd) ? cmd : resolve7(workingDirectory ?? process.cwd(), cmd);
|
|
4084
|
+
return probe(explicit) ? explicit : null;
|
|
4085
|
+
}
|
|
4086
|
+
const cacheKey = JSON.stringify([
|
|
4087
|
+
workingDirectory ?? "",
|
|
4088
|
+
cmd,
|
|
4089
|
+
platform,
|
|
4090
|
+
options.stopAt ?? "",
|
|
4091
|
+
options.pathExt ?? "",
|
|
4092
|
+
options.pathEnv ?? process.env["PATH"] ?? ""
|
|
4093
|
+
]);
|
|
4094
|
+
const cached = resolutionCache.get(cacheKey);
|
|
4095
|
+
if (cached !== undefined)
|
|
4096
|
+
return cached;
|
|
4097
|
+
let resolved = null;
|
|
4098
|
+
if (workingDirectory !== undefined && workingDirectory.length > 0) {
|
|
4099
|
+
resolved = resolveLocal(cmd, workingDirectory, suffixes, options.stopAt);
|
|
4100
|
+
}
|
|
4101
|
+
resolved ??= resolveFromPath(cmd, suffixes, platform, options.pathEnv);
|
|
4102
|
+
if (resolved === null && cmd === "node")
|
|
4103
|
+
resolved = cmd;
|
|
4104
|
+
resolutionCache.set(cacheKey, resolved);
|
|
4105
|
+
return resolved;
|
|
4106
|
+
}
|
|
4107
|
+
function isServerInstalled(command, workingDirectory, options = {}) {
|
|
4108
|
+
if (command.length === 0)
|
|
4109
|
+
return false;
|
|
4110
|
+
return resolveServerBinary(command, workingDirectory, options) !== null;
|
|
3946
4111
|
}
|
|
3947
4112
|
|
|
3948
4113
|
// ../lsp-core/src/lsp/server-resolution.ts
|
|
4114
|
+
function withResolvedCommand(command, binaryPath) {
|
|
4115
|
+
return [binaryPath, ...command.slice(1)];
|
|
4116
|
+
}
|
|
3949
4117
|
function findServerForExtension(ext) {
|
|
3950
4118
|
const servers = getMergedServers();
|
|
4119
|
+
const workingDirectory = contextCwd();
|
|
3951
4120
|
for (const server2 of servers) {
|
|
3952
|
-
if (server2.extensions.includes(ext)
|
|
4121
|
+
if (!server2.extensions.includes(ext))
|
|
4122
|
+
continue;
|
|
4123
|
+
const binaryPath = resolveServerBinary(server2.command, workingDirectory);
|
|
4124
|
+
if (binaryPath !== null) {
|
|
3953
4125
|
const resolvedServer = {
|
|
3954
4126
|
id: server2.id,
|
|
3955
|
-
command: server2.command,
|
|
4127
|
+
command: withResolvedCommand(server2.command, binaryPath),
|
|
3956
4128
|
extensions: server2.extensions,
|
|
3957
4129
|
priority: server2.priority
|
|
3958
4130
|
};
|
|
@@ -3999,6 +4171,7 @@ function findServerForExtension(ext) {
|
|
|
3999
4171
|
function getAllServers() {
|
|
4000
4172
|
const servers = getMergedServers();
|
|
4001
4173
|
const disabled = getDisabledServerIds();
|
|
4174
|
+
const workingDirectory = contextCwd();
|
|
4002
4175
|
const result = [];
|
|
4003
4176
|
const seen = new Set;
|
|
4004
4177
|
for (const server2 of servers) {
|
|
@@ -4006,7 +4179,7 @@ function getAllServers() {
|
|
|
4006
4179
|
continue;
|
|
4007
4180
|
result.push({
|
|
4008
4181
|
id: server2.id,
|
|
4009
|
-
installed: isServerInstalled(server2.command),
|
|
4182
|
+
installed: isServerInstalled(server2.command, workingDirectory),
|
|
4010
4183
|
extensions: server2.extensions,
|
|
4011
4184
|
disabled: false,
|
|
4012
4185
|
source: server2.source,
|
|
@@ -4020,7 +4193,7 @@ function getAllServers() {
|
|
|
4020
4193
|
const builtin = BUILTIN_SERVERS[id];
|
|
4021
4194
|
result.push({
|
|
4022
4195
|
id,
|
|
4023
|
-
installed: builtin ? isServerInstalled(builtin.command) : false,
|
|
4196
|
+
installed: builtin ? isServerInstalled(builtin.command, workingDirectory) : false,
|
|
4024
4197
|
extensions: builtin?.extensions ?? [],
|
|
4025
4198
|
disabled: true,
|
|
4026
4199
|
source: "disabled",
|
|
@@ -4043,7 +4216,7 @@ function findWorkspaceRoot(filePath) {
|
|
|
4043
4216
|
const abs = resolveReadablePathInsideContext(filePath);
|
|
4044
4217
|
let dir = abs;
|
|
4045
4218
|
if (!isDirectoryPath(dir)) {
|
|
4046
|
-
dir =
|
|
4219
|
+
dir = dirname8(dir);
|
|
4047
4220
|
}
|
|
4048
4221
|
if (!isPathInside(cwd, abs))
|
|
4049
4222
|
return findWorkspaceRootOutsideContext(dir);
|
|
@@ -4059,13 +4232,13 @@ function findWorkspaceRoot(filePath) {
|
|
|
4059
4232
|
}
|
|
4060
4233
|
if (dir === cwd)
|
|
4061
4234
|
break;
|
|
4062
|
-
dir =
|
|
4235
|
+
dir = dirname8(dir);
|
|
4063
4236
|
}
|
|
4064
4237
|
return fallbackRoot;
|
|
4065
4238
|
}
|
|
4066
4239
|
function resolveReadablePathInsideContext(filePath) {
|
|
4067
4240
|
const cwd = contextCwd();
|
|
4068
|
-
const abs =
|
|
4241
|
+
const abs = resolve8(cwd, filePath);
|
|
4069
4242
|
if (isPathInside(cwd, abs))
|
|
4070
4243
|
return abs;
|
|
4071
4244
|
const rebased = rebaseThroughCanonicalAncestor(abs, cwd);
|
|
@@ -4091,7 +4264,7 @@ function rebaseThroughCanonicalAncestor(path, cwd) {
|
|
|
4091
4264
|
if (isPathInside(cwd, canonical))
|
|
4092
4265
|
return suffix.length === 0 ? canonical : join5(canonical, ...suffix);
|
|
4093
4266
|
}
|
|
4094
|
-
const parent =
|
|
4267
|
+
const parent = dirname8(current);
|
|
4095
4268
|
if (parent === current)
|
|
4096
4269
|
return;
|
|
4097
4270
|
suffix.unshift(basename3(current));
|
|
@@ -4114,7 +4287,7 @@ function nearestExistingDirectoryInsideContext(directory, cwd) {
|
|
|
4114
4287
|
return canonical;
|
|
4115
4288
|
if (current === cwd)
|
|
4116
4289
|
return;
|
|
4117
|
-
current =
|
|
4290
|
+
current = dirname8(current);
|
|
4118
4291
|
}
|
|
4119
4292
|
return;
|
|
4120
4293
|
}
|
|
@@ -4141,6 +4314,19 @@ function formatServerLookupError(result) {
|
|
|
4141
4314
|
].join(`
|
|
4142
4315
|
`);
|
|
4143
4316
|
}
|
|
4317
|
+
function formatInstallOptions(serverId, installHint) {
|
|
4318
|
+
const localHint = LSP_LOCAL_INSTALL_HINTS[serverId];
|
|
4319
|
+
if (localHint === undefined) {
|
|
4320
|
+
return ["To install, run:", ` ${installHint}`];
|
|
4321
|
+
}
|
|
4322
|
+
return [
|
|
4323
|
+
"To install in THIS repository (preferred — no global install needed):",
|
|
4324
|
+
` ${localHint}`,
|
|
4325
|
+
"",
|
|
4326
|
+
"Or install it globally:",
|
|
4327
|
+
` ${installHint}`
|
|
4328
|
+
];
|
|
4329
|
+
}
|
|
4144
4330
|
function formatNotInstalled(result) {
|
|
4145
4331
|
const { server: server2, installHint } = result;
|
|
4146
4332
|
const extensions = server2.extensions.join(", ");
|
|
@@ -4155,19 +4341,19 @@ function formatNotInstalled(result) {
|
|
|
4155
4341
|
`Command not found: ${server2.command[0]}`,
|
|
4156
4342
|
""
|
|
4157
4343
|
];
|
|
4344
|
+
const installOptions = formatInstallOptions(server2.id, installHint);
|
|
4158
4345
|
if (decision === "allowed") {
|
|
4159
4346
|
return [
|
|
4160
4347
|
...header,
|
|
4161
4348
|
"The user has pre-authorized LSP installation. Run the install command, then retry this tool:",
|
|
4162
|
-
|
|
4349
|
+
...installOptions.slice(1)
|
|
4163
4350
|
].join(`
|
|
4164
4351
|
`);
|
|
4165
4352
|
}
|
|
4166
4353
|
if (!context.capabilities.installDecisionTool) {
|
|
4167
4354
|
return [
|
|
4168
4355
|
...header,
|
|
4169
|
-
|
|
4170
|
-
` ${installHint}`,
|
|
4356
|
+
...installOptions,
|
|
4171
4357
|
"",
|
|
4172
4358
|
"ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
|
|
4173
4359
|
"Install-decision recording is unavailable in this harness; proceed without LSP if the user declines."
|
|
@@ -4176,8 +4362,7 @@ function formatNotInstalled(result) {
|
|
|
4176
4362
|
}
|
|
4177
4363
|
return [
|
|
4178
4364
|
...header,
|
|
4179
|
-
|
|
4180
|
-
` ${installHint}`,
|
|
4365
|
+
...installOptions,
|
|
4181
4366
|
"",
|
|
4182
4367
|
"ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
|
|
4183
4368
|
"- If the user agrees: run the install command above, then retry this tool.",
|
|
@@ -4233,7 +4418,7 @@ async function withLspClient(filePath, fn, toolName, options = {}) {
|
|
|
4233
4418
|
|
|
4234
4419
|
// ../lsp-core/src/lsp/directory-diagnostics.ts
|
|
4235
4420
|
import { existsSync as existsSync11, lstatSync as lstatSync4, readdirSync as readdirSync3 } from "node:fs";
|
|
4236
|
-
import { join as join6, resolve as
|
|
4421
|
+
import { join as join6, resolve as resolve9 } from "node:path";
|
|
4237
4422
|
|
|
4238
4423
|
// ../lsp-core/src/lsp/formatters.ts
|
|
4239
4424
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -4388,7 +4573,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4388
4573
|
if (!extension.startsWith(".")) {
|
|
4389
4574
|
throw new LspInvalidPathError(`Extension must start with a dot (e.g., ".ts", not "${extension}"). Use ".${extension}" instead.`);
|
|
4390
4575
|
}
|
|
4391
|
-
const absDir =
|
|
4576
|
+
const absDir = resolve9(options.workspaceRoot ?? contextCwd(), directory);
|
|
4392
4577
|
if (!existsSync11(absDir)) {
|
|
4393
4578
|
throw new LspInvalidPathError(`Directory does not exist: ${absDir}`);
|
|
4394
4579
|
}
|
|
@@ -4745,6 +4930,96 @@ async function executeLspDiagnostics(params, signal) {
|
|
|
4745
4930
|
throw error;
|
|
4746
4931
|
}
|
|
4747
4932
|
}
|
|
4933
|
+
// ../lsp-core/src/lsp/format-document.ts
|
|
4934
|
+
import { readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
4935
|
+
var DEFAULT_FORMATTING_OPTIONS = {
|
|
4936
|
+
tabSize: 4,
|
|
4937
|
+
insertSpaces: false,
|
|
4938
|
+
trimTrailingWhitespace: true,
|
|
4939
|
+
insertFinalNewline: true,
|
|
4940
|
+
trimFinalNewlines: true
|
|
4941
|
+
};
|
|
4942
|
+
var UNCHANGED = { status: "unchanged", linesAdded: 0, linesRemoved: 0 };
|
|
4943
|
+
async function formatDocumentWithClient(client, filePath, options = {}) {
|
|
4944
|
+
const edits = await client.formatDocument(filePath, options.formattingOptions ?? DEFAULT_FORMATTING_OPTIONS, options.signal);
|
|
4945
|
+
if (edits === null)
|
|
4946
|
+
return { status: "unavailable", reason: "capability_not_advertised" };
|
|
4947
|
+
if (edits.length === 0)
|
|
4948
|
+
return UNCHANGED;
|
|
4949
|
+
const before = readFileSync5(filePath, "utf-8");
|
|
4950
|
+
const normalized = normalizeTextEdits(before, edits, 0);
|
|
4951
|
+
if (normalized.text === before)
|
|
4952
|
+
return UNCHANGED;
|
|
4953
|
+
writeAtomically(filePath, normalized.text);
|
|
4954
|
+
await client.openFile(filePath);
|
|
4955
|
+
return {
|
|
4956
|
+
status: "formatted",
|
|
4957
|
+
...lineDelta(normalized.edits)
|
|
4958
|
+
};
|
|
4959
|
+
}
|
|
4960
|
+
function lineDelta(edits) {
|
|
4961
|
+
let linesAdded = 0;
|
|
4962
|
+
let linesRemoved = 0;
|
|
4963
|
+
for (const edit of edits) {
|
|
4964
|
+
linesRemoved += edit.range.end.line - edit.range.start.line + 1;
|
|
4965
|
+
linesAdded += edit.newText.split(`
|
|
4966
|
+
`).length;
|
|
4967
|
+
}
|
|
4968
|
+
return { linesAdded, linesRemoved };
|
|
4969
|
+
}
|
|
4970
|
+
function writeAtomically(filePath, content) {
|
|
4971
|
+
const tempPath = `${filePath}.omo-format.tmp`;
|
|
4972
|
+
writeFileSync3(tempPath, content, "utf-8");
|
|
4973
|
+
try {
|
|
4974
|
+
renameSync3(tempPath, filePath);
|
|
4975
|
+
} catch (error) {
|
|
4976
|
+
try {
|
|
4977
|
+
unlinkSync(tempPath);
|
|
4978
|
+
} catch {}
|
|
4979
|
+
throw error;
|
|
4980
|
+
}
|
|
4981
|
+
}
|
|
4982
|
+
|
|
4983
|
+
// ../lsp-core/src/tools/format.ts
|
|
4984
|
+
async function executeLspFormat(params, signal) {
|
|
4985
|
+
const filePath = requireString(params, "filePath");
|
|
4986
|
+
try {
|
|
4987
|
+
const result = await withLspClient(filePath, async (client, _workspaceRoot, resolvedFilePath) => formatDocumentWithClient(client, resolvedFilePath, signal === undefined ? {} : { signal }), "format", clientOptions(signal));
|
|
4988
|
+
return text(describeResult(filePath, result), formatDetails(filePath, result));
|
|
4989
|
+
} catch (error) {
|
|
4990
|
+
const missingDependency = missingDependencyResult(error, {
|
|
4991
|
+
filePath,
|
|
4992
|
+
status: "unavailable",
|
|
4993
|
+
reason: "server_unavailable",
|
|
4994
|
+
linesAdded: 0,
|
|
4995
|
+
linesRemoved: 0
|
|
4996
|
+
});
|
|
4997
|
+
if (missingDependency)
|
|
4998
|
+
return missingDependency;
|
|
4999
|
+
throw error;
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
function formatDetails(filePath, result) {
|
|
5003
|
+
if (result.status === "unavailable") {
|
|
5004
|
+
return { filePath, status: "unavailable", reason: result.reason, linesAdded: 0, linesRemoved: 0 };
|
|
5005
|
+
}
|
|
5006
|
+
return {
|
|
5007
|
+
filePath,
|
|
5008
|
+
status: result.status,
|
|
5009
|
+
linesAdded: result.linesAdded,
|
|
5010
|
+
linesRemoved: result.linesRemoved
|
|
5011
|
+
};
|
|
5012
|
+
}
|
|
5013
|
+
function describeResult(filePath, result) {
|
|
5014
|
+
if (result.status === "unavailable") {
|
|
5015
|
+
return `Formatting unavailable for ${filePath}: the language server does not advertise documentFormattingProvider.`;
|
|
5016
|
+
}
|
|
5017
|
+
if (result.status === "unchanged") {
|
|
5018
|
+
return `Already formatted: ${filePath}`;
|
|
5019
|
+
}
|
|
5020
|
+
return `Formatted ${filePath} (+${result.linesAdded}/-${result.linesRemoved} lines)`;
|
|
5021
|
+
}
|
|
5022
|
+
|
|
4748
5023
|
// ../lsp-core/src/tools/install-decision.ts
|
|
4749
5024
|
async function executeLspInstallDecision(params) {
|
|
4750
5025
|
const serverId = requireString(params, "server_id");
|
|
@@ -5077,6 +5352,16 @@ var LSP_MCP_TOOLS = [
|
|
|
5077
5352
|
}, ["filePath", "line", "character", "newName"]),
|
|
5078
5353
|
execute: executeLspRename
|
|
5079
5354
|
},
|
|
5355
|
+
{
|
|
5356
|
+
name: "format",
|
|
5357
|
+
aliases: ["lsp_format"],
|
|
5358
|
+
title: "LSP Format",
|
|
5359
|
+
description: "Format a source file with its language server and write the returned edits to disk.",
|
|
5360
|
+
inputSchema: objectSchema({
|
|
5361
|
+
filePath: { type: "string", description: "Source file path to format." }
|
|
5362
|
+
}, ["filePath"]),
|
|
5363
|
+
execute: executeLspFormat
|
|
5364
|
+
},
|
|
5080
5365
|
{
|
|
5081
5366
|
name: "install_decision",
|
|
5082
5367
|
aliases: ["lsp_install_decision"],
|