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
|
@@ -25,6 +25,7 @@ var DEFAULT_MAX_DIRECTORY_FILES = 50;
|
|
|
25
25
|
var REQUEST_TIMEOUT_MS = 15000;
|
|
26
26
|
var INIT_TIMEOUT_MS = 60000;
|
|
27
27
|
var IDLE_TIMEOUT_MS = 5 * 60000;
|
|
28
|
+
var MAX_RESIDENT_CLIENTS = 6;
|
|
28
29
|
var REAPER_INTERVAL_MS = 60000;
|
|
29
30
|
var STOP_HARD_KILL_TIMEOUT_MS = 5000;
|
|
30
31
|
var STOP_SIGKILL_GRACE_MS = 1000;
|
|
@@ -617,6 +618,7 @@ class LspClientTransport {
|
|
|
617
618
|
this.diagnosticsStore = new Map;
|
|
618
619
|
this.workspaceApplyEditHandler = null;
|
|
619
620
|
this.diagnosticPullSupported = false;
|
|
621
|
+
this.documentFormattingSupported = false;
|
|
620
622
|
this.requestTimeoutMs = timeouts.requestTimeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
621
623
|
this.initializeTimeoutMs = timeouts.initializeTimeoutMs ?? INIT_TIMEOUT_MS;
|
|
622
624
|
}
|
|
@@ -638,6 +640,12 @@ class LspClientTransport {
|
|
|
638
640
|
isDiagnosticPullSupported() {
|
|
639
641
|
return this.diagnosticPullSupported;
|
|
640
642
|
}
|
|
643
|
+
setDocumentFormattingSupported(supported) {
|
|
644
|
+
this.documentFormattingSupported = supported;
|
|
645
|
+
}
|
|
646
|
+
isDocumentFormattingSupported() {
|
|
647
|
+
return this.documentFormattingSupported;
|
|
648
|
+
}
|
|
641
649
|
handlePublishDiagnostics(params) {
|
|
642
650
|
this.diagnosticsStore.set(params.uri, [...params.diagnostics]);
|
|
643
651
|
}
|
|
@@ -851,6 +859,14 @@ function supportsDiagnosticPull(capabilities) {
|
|
|
851
859
|
return false;
|
|
852
860
|
return Object.hasOwn(capabilities, "diagnosticProvider");
|
|
853
861
|
}
|
|
862
|
+
function supportsDocumentFormatting(capabilities) {
|
|
863
|
+
if (capabilities === undefined)
|
|
864
|
+
return false;
|
|
865
|
+
const provider = capabilities.documentFormattingProvider;
|
|
866
|
+
if (provider === undefined || provider === null || provider === false)
|
|
867
|
+
return false;
|
|
868
|
+
return provider === true || typeof provider === "object";
|
|
869
|
+
}
|
|
854
870
|
|
|
855
871
|
class LspClientConnection extends LspClientTransport {
|
|
856
872
|
async initialize() {
|
|
@@ -867,6 +883,7 @@ class LspClientConnection extends LspClientTransport {
|
|
|
867
883
|
references: {},
|
|
868
884
|
documentSymbol: { hierarchicalDocumentSymbolSupport: true },
|
|
869
885
|
publishDiagnostics: {},
|
|
886
|
+
formatting: { dynamicRegistration: false },
|
|
870
887
|
rename: {
|
|
871
888
|
prepareSupport: true,
|
|
872
889
|
prepareSupportDefaultBehavior: 1
|
|
@@ -908,6 +925,7 @@ class LspClientConnection extends LspClientTransport {
|
|
|
908
925
|
initializationOptions: this.server.initialization
|
|
909
926
|
}, { timeoutMs: this.initializeTimeoutMs });
|
|
910
927
|
this.setDiagnosticPullSupported(supportsDiagnosticPull(result?.capabilities));
|
|
928
|
+
this.setDocumentFormattingSupported(supportsDocumentFormatting(result?.capabilities));
|
|
911
929
|
await this.sendNotification("initialized", {});
|
|
912
930
|
await this.sendNotification("workspace/didChangeConfiguration", {
|
|
913
931
|
settings: { json: { validate: { enable: true } } }
|
|
@@ -2502,6 +2520,18 @@ class LspClient extends LspClientConnection {
|
|
|
2502
2520
|
textDocument: { uri: pathToFileURL3(absPath).href }
|
|
2503
2521
|
}, options);
|
|
2504
2522
|
}
|
|
2523
|
+
async formatDocument(filePath, options, signal) {
|
|
2524
|
+
if (!this.isDocumentFormattingSupported())
|
|
2525
|
+
return null;
|
|
2526
|
+
const absPath = this.resolveWorkspacePath(filePath);
|
|
2527
|
+
await this.openFile(absPath);
|
|
2528
|
+
const requestOptions = signal === undefined ? {} : { signal };
|
|
2529
|
+
const edits = await this.sendRequest("textDocument/formatting", {
|
|
2530
|
+
textDocument: { uri: pathToFileURL3(absPath).href },
|
|
2531
|
+
options
|
|
2532
|
+
}, requestOptions);
|
|
2533
|
+
return edits ?? [];
|
|
2534
|
+
}
|
|
2505
2535
|
async workspaceSymbols(query, signal) {
|
|
2506
2536
|
const options = signal === undefined ? {} : { signal };
|
|
2507
2537
|
return this.sendRequest("workspace/symbol", { query }, options);
|
|
@@ -2754,6 +2784,7 @@ class LspManager {
|
|
|
2754
2784
|
this.disposed = false;
|
|
2755
2785
|
this.idleTimeoutMs = options.idleTimeoutMs ?? IDLE_TIMEOUT_MS;
|
|
2756
2786
|
this.initTimeoutMs = options.initTimeoutMs ?? INIT_TIMEOUT_MS;
|
|
2787
|
+
this.maxResidentClients = options.maxResidentClients ?? MAX_RESIDENT_CLIENTS;
|
|
2757
2788
|
this.reaperIntervalMs = options.reaperIntervalMs ?? REAPER_INTERVAL_MS;
|
|
2758
2789
|
this.clientFactory = options.clientFactory ?? ((root, server) => new LspClient(root, server));
|
|
2759
2790
|
this.now = options.now ?? (() => Date.now());
|
|
@@ -2787,6 +2818,26 @@ class LspManager {
|
|
|
2787
2818
|
}
|
|
2788
2819
|
}
|
|
2789
2820
|
}
|
|
2821
|
+
evictForAdmission() {
|
|
2822
|
+
while (this.clients.size >= this.maxResidentClients) {
|
|
2823
|
+
const victim = this.leastRecentlyUsedIdleClient();
|
|
2824
|
+
if (!victim)
|
|
2825
|
+
return;
|
|
2826
|
+
this.clients.delete(victim.key);
|
|
2827
|
+
stopClientBestEffort(victim.managed.client);
|
|
2828
|
+
}
|
|
2829
|
+
}
|
|
2830
|
+
leastRecentlyUsedIdleClient() {
|
|
2831
|
+
let candidate = null;
|
|
2832
|
+
for (const [key, managed] of this.clients) {
|
|
2833
|
+
if (managed.refCount > 0 || managed.pendingWaiters > 0 || managed.isInitializing)
|
|
2834
|
+
continue;
|
|
2835
|
+
if (candidate === null || managed.lastUsedAt < candidate.managed.lastUsedAt) {
|
|
2836
|
+
candidate = { key, managed };
|
|
2837
|
+
}
|
|
2838
|
+
}
|
|
2839
|
+
return candidate;
|
|
2840
|
+
}
|
|
2790
2841
|
async tryDeleteIfOrphaned(key, managed) {
|
|
2791
2842
|
if (managed.refCount === 0 && managed.pendingWaiters === 0 && !managed.isInitializing && this.clients.get(key) === managed) {
|
|
2792
2843
|
this.clients.delete(key);
|
|
@@ -2833,6 +2884,7 @@ class LspManager {
|
|
|
2833
2884
|
managed.lastUsedAt = this.now();
|
|
2834
2885
|
return managed.client;
|
|
2835
2886
|
}
|
|
2887
|
+
this.evictForAdmission();
|
|
2836
2888
|
const client = this.clientFactory(root, server);
|
|
2837
2889
|
const initStartedAt = this.now();
|
|
2838
2890
|
const initPromise = (async () => {
|
|
@@ -2895,6 +2947,7 @@ class LspManager {
|
|
|
2895
2947
|
const key = this.getKey(root, server.id);
|
|
2896
2948
|
if (this.clients.has(key))
|
|
2897
2949
|
return;
|
|
2950
|
+
this.evictForAdmission();
|
|
2898
2951
|
const client = this.clientFactory(root, server);
|
|
2899
2952
|
const initStartedAt = this.now();
|
|
2900
2953
|
const initPromise = (async () => {
|
|
@@ -3295,7 +3348,7 @@ function createIdleTimer(idleTimeoutMs, log, onTimeout) {
|
|
|
3295
3348
|
}
|
|
3296
3349
|
// ../lsp-core/src/lsp/client-wrapper.ts
|
|
3297
3350
|
import { existsSync as existsSync10, realpathSync as realpathSync5, statSync as statSync3 } from "node:fs";
|
|
3298
|
-
import { basename as basename3, dirname as
|
|
3351
|
+
import { basename as basename3, dirname as dirname8, join as join5, resolve as resolve8 } from "node:path";
|
|
3299
3352
|
|
|
3300
3353
|
// ../lsp-core/src/request-context.ts
|
|
3301
3354
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
@@ -3512,57 +3565,6 @@ function nearestExistingAncestor(directory) {
|
|
|
3512
3565
|
return current;
|
|
3513
3566
|
}
|
|
3514
3567
|
|
|
3515
|
-
// ../lsp-core/src/lsp/server-install-state.ts
|
|
3516
|
-
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3517
|
-
import { dirname as dirname6 } from "node:path";
|
|
3518
|
-
function getInstallDecisionsPath() {
|
|
3519
|
-
return lspRequestContext().installDecisionsPath;
|
|
3520
|
-
}
|
|
3521
|
-
function loadInstallDecisions() {
|
|
3522
|
-
const path = getInstallDecisionsPath();
|
|
3523
|
-
if (!existsSync7(path))
|
|
3524
|
-
return {};
|
|
3525
|
-
try {
|
|
3526
|
-
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
3527
|
-
return isInstallDecisions(parsed) ? parsed : {};
|
|
3528
|
-
} catch {
|
|
3529
|
-
return {};
|
|
3530
|
-
}
|
|
3531
|
-
}
|
|
3532
|
-
function loadInstallDecision(serverId) {
|
|
3533
|
-
return loadInstallDecisions()[serverId];
|
|
3534
|
-
}
|
|
3535
|
-
function recordInstallDecision(serverId, decision, decidedAt = new Date().toISOString()) {
|
|
3536
|
-
const decisions = loadInstallDecisions();
|
|
3537
|
-
decisions[serverId] = { decision, decidedAt };
|
|
3538
|
-
writeInstallDecisions(decisions);
|
|
3539
|
-
}
|
|
3540
|
-
function isInstallDecision(value) {
|
|
3541
|
-
return value === "declined" || value === "allowed";
|
|
3542
|
-
}
|
|
3543
|
-
function writeInstallDecisions(decisions) {
|
|
3544
|
-
const path = getInstallDecisionsPath();
|
|
3545
|
-
mkdirSync(dirname6(path), { recursive: true });
|
|
3546
|
-
const tmpPath = `${path}.tmp`;
|
|
3547
|
-
writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
|
|
3548
|
-
`, "utf8");
|
|
3549
|
-
renameSync2(tmpPath, path);
|
|
3550
|
-
}
|
|
3551
|
-
function isInstallDecisions(value) {
|
|
3552
|
-
return isRecord5(value) && Object.values(value).every(isInstallDecisionRecord);
|
|
3553
|
-
}
|
|
3554
|
-
function isInstallDecisionRecord(value) {
|
|
3555
|
-
if (!isRecord5(value))
|
|
3556
|
-
return false;
|
|
3557
|
-
return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
|
|
3558
|
-
}
|
|
3559
|
-
function isRecord5(value) {
|
|
3560
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3561
|
-
}
|
|
3562
|
-
|
|
3563
|
-
// ../lsp-core/src/lsp/config-loader.ts
|
|
3564
|
-
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
|
|
3565
|
-
|
|
3566
3568
|
// ../lsp-core/src/lsp/server-definitions.ts
|
|
3567
3569
|
var LSP_INSTALL_HINTS = {
|
|
3568
3570
|
typescript: "npm install -g typescript-language-server typescript",
|
|
@@ -3608,6 +3610,26 @@ var LSP_INSTALL_HINTS = {
|
|
|
3608
3610
|
julials: `julia -e 'using Pkg; Pkg.add("LanguageServer")'`,
|
|
3609
3611
|
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"
|
|
3610
3612
|
};
|
|
3613
|
+
var LSP_LOCAL_INSTALL_HINTS = {
|
|
3614
|
+
typescript: "bun add -d typescript-language-server typescript",
|
|
3615
|
+
vue: "bun add -d @vue/language-server",
|
|
3616
|
+
eslint: "bun add -d vscode-langservers-extracted",
|
|
3617
|
+
oxlint: "bun add -d oxlint",
|
|
3618
|
+
biome: "bun add -d @biomejs/biome",
|
|
3619
|
+
svelte: "bun add -d svelte-language-server",
|
|
3620
|
+
astro: "bun add -d @astrojs/language-server",
|
|
3621
|
+
bash: "bun add -d bash-language-server",
|
|
3622
|
+
"bash-ls": "bun add -d bash-language-server",
|
|
3623
|
+
"yaml-ls": "bun add -d yaml-language-server",
|
|
3624
|
+
php: "bun add -d intelephense",
|
|
3625
|
+
prisma: "bun add -d prisma",
|
|
3626
|
+
dockerfile: "bun add -d dockerfile-language-server-nodejs",
|
|
3627
|
+
pyright: "uv add --dev pyright",
|
|
3628
|
+
basedpyright: "uv add --dev basedpyright",
|
|
3629
|
+
ruff: "uv add --dev ruff",
|
|
3630
|
+
ty: "uv add --dev ty",
|
|
3631
|
+
"ruby-lsp": "bundle add ruby-lsp --group development"
|
|
3632
|
+
};
|
|
3611
3633
|
var BUILTIN_SERVERS = {
|
|
3612
3634
|
typescript: {
|
|
3613
3635
|
command: ["typescript-language-server", "--stdio"],
|
|
@@ -3710,7 +3732,56 @@ var BUILTIN_SERVERS = {
|
|
|
3710
3732
|
}
|
|
3711
3733
|
};
|
|
3712
3734
|
|
|
3735
|
+
// ../lsp-core/src/lsp/server-install-state.ts
|
|
3736
|
+
import { existsSync as existsSync7, mkdirSync, readFileSync as readFileSync3, renameSync as renameSync2, writeFileSync as writeFileSync2 } from "node:fs";
|
|
3737
|
+
import { dirname as dirname6 } from "node:path";
|
|
3738
|
+
function getInstallDecisionsPath() {
|
|
3739
|
+
return lspRequestContext().installDecisionsPath;
|
|
3740
|
+
}
|
|
3741
|
+
function loadInstallDecisions() {
|
|
3742
|
+
const path = getInstallDecisionsPath();
|
|
3743
|
+
if (!existsSync7(path))
|
|
3744
|
+
return {};
|
|
3745
|
+
try {
|
|
3746
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
3747
|
+
return isInstallDecisions(parsed) ? parsed : {};
|
|
3748
|
+
} catch {
|
|
3749
|
+
return {};
|
|
3750
|
+
}
|
|
3751
|
+
}
|
|
3752
|
+
function loadInstallDecision(serverId) {
|
|
3753
|
+
return loadInstallDecisions()[serverId];
|
|
3754
|
+
}
|
|
3755
|
+
function recordInstallDecision(serverId, decision, decidedAt = new Date().toISOString()) {
|
|
3756
|
+
const decisions = loadInstallDecisions();
|
|
3757
|
+
decisions[serverId] = { decision, decidedAt };
|
|
3758
|
+
writeInstallDecisions(decisions);
|
|
3759
|
+
}
|
|
3760
|
+
function isInstallDecision(value) {
|
|
3761
|
+
return value === "declined" || value === "allowed";
|
|
3762
|
+
}
|
|
3763
|
+
function writeInstallDecisions(decisions) {
|
|
3764
|
+
const path = getInstallDecisionsPath();
|
|
3765
|
+
mkdirSync(dirname6(path), { recursive: true });
|
|
3766
|
+
const tmpPath = `${path}.tmp`;
|
|
3767
|
+
writeFileSync2(tmpPath, `${JSON.stringify(decisions, null, 2)}
|
|
3768
|
+
`, "utf8");
|
|
3769
|
+
renameSync2(tmpPath, path);
|
|
3770
|
+
}
|
|
3771
|
+
function isInstallDecisions(value) {
|
|
3772
|
+
return isRecord5(value) && Object.values(value).every(isInstallDecisionRecord);
|
|
3773
|
+
}
|
|
3774
|
+
function isInstallDecisionRecord(value) {
|
|
3775
|
+
if (!isRecord5(value))
|
|
3776
|
+
return false;
|
|
3777
|
+
return isInstallDecision(value["decision"]) && typeof value["decidedAt"] === "string";
|
|
3778
|
+
}
|
|
3779
|
+
function isRecord5(value) {
|
|
3780
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
3781
|
+
}
|
|
3782
|
+
|
|
3713
3783
|
// ../lsp-core/src/lsp/config-loader.ts
|
|
3784
|
+
import { existsSync as existsSync8, readFileSync as readFileSync4 } from "node:fs";
|
|
3714
3785
|
function getProjectConfigPaths() {
|
|
3715
3786
|
return lspRequestContext().projectConfigPaths;
|
|
3716
3787
|
}
|
|
@@ -3907,53 +3978,154 @@ function getDisabledServerIds() {
|
|
|
3907
3978
|
|
|
3908
3979
|
// ../lsp-core/src/lsp/server-installation.ts
|
|
3909
3980
|
import { existsSync as existsSync9 } from "node:fs";
|
|
3910
|
-
import { delimiter as delimiter3, join as join4 } from "node:path";
|
|
3911
|
-
|
|
3912
|
-
|
|
3913
|
-
|
|
3914
|
-
|
|
3915
|
-
|
|
3916
|
-
|
|
3917
|
-
|
|
3918
|
-
|
|
3919
|
-
|
|
3981
|
+
import { delimiter as delimiter3, dirname as dirname7, isAbsolute as isAbsolute3, join as join4, resolve as resolve7 } from "node:path";
|
|
3982
|
+
var LOCAL_BIN_RULES = [
|
|
3983
|
+
{
|
|
3984
|
+
markers: ["package.json", "bun.lock", "bun.lockb", "package-lock.json", "yarn.lock", "pnpm-lock.yaml"],
|
|
3985
|
+
binDirs: [join4("node_modules", ".bin")]
|
|
3986
|
+
},
|
|
3987
|
+
{
|
|
3988
|
+
markers: [
|
|
3989
|
+
"pyproject.toml",
|
|
3990
|
+
"ty.toml",
|
|
3991
|
+
"requirements.txt",
|
|
3992
|
+
"setup.py",
|
|
3993
|
+
"setup.cfg",
|
|
3994
|
+
"Pipfile",
|
|
3995
|
+
"pyrightconfig.json",
|
|
3996
|
+
"ruff.toml",
|
|
3997
|
+
".ruff.toml"
|
|
3998
|
+
],
|
|
3999
|
+
binDirs: [
|
|
4000
|
+
join4(".venv", "bin"),
|
|
4001
|
+
join4(".venv", "Scripts"),
|
|
4002
|
+
join4("venv", "bin"),
|
|
4003
|
+
join4("venv", "Scripts"),
|
|
4004
|
+
join4(".env", "bin"),
|
|
4005
|
+
join4(".env", "Scripts")
|
|
4006
|
+
]
|
|
4007
|
+
},
|
|
4008
|
+
{
|
|
4009
|
+
markers: ["Gemfile", "Gemfile.lock"],
|
|
4010
|
+
binDirs: [join4("vendor", "bundle", "bin"), "bin"]
|
|
4011
|
+
},
|
|
4012
|
+
{
|
|
4013
|
+
markers: ["go.mod", "go.sum", "go.work"],
|
|
4014
|
+
binDirs: ["bin"]
|
|
3920
4015
|
}
|
|
3921
|
-
|
|
3922
|
-
|
|
3923
|
-
|
|
3924
|
-
|
|
3925
|
-
|
|
3926
|
-
|
|
3927
|
-
|
|
3928
|
-
|
|
3929
|
-
|
|
4016
|
+
];
|
|
4017
|
+
var resolutionCache = new Map;
|
|
4018
|
+
var probeCount = 0;
|
|
4019
|
+
function probe(path) {
|
|
4020
|
+
probeCount += 1;
|
|
4021
|
+
return existsSync9(path);
|
|
4022
|
+
}
|
|
4023
|
+
function executableSuffixes(platform, pathExt) {
|
|
4024
|
+
if (platform !== "win32")
|
|
4025
|
+
return [""];
|
|
4026
|
+
const configured = pathExt ?? process.env["PATHEXT"] ?? "";
|
|
4027
|
+
const systemExts = configured.split(";").filter((entry) => entry.length > 0).map((entry) => entry.toLowerCase());
|
|
4028
|
+
return [...new Set(["", ...systemExts, ".exe", ".cmd", ".bat", ".ps1"])];
|
|
4029
|
+
}
|
|
4030
|
+
function probeWithSuffixes(directory, command, suffixes) {
|
|
4031
|
+
for (const suffix of suffixes) {
|
|
4032
|
+
const candidate = join4(directory, command + suffix);
|
|
4033
|
+
if (probe(candidate))
|
|
4034
|
+
return candidate;
|
|
4035
|
+
}
|
|
4036
|
+
return null;
|
|
4037
|
+
}
|
|
4038
|
+
function resolveLocal(command, workingDirectory, suffixes, stopAt) {
|
|
4039
|
+
const boundary = stopAt === undefined ? undefined : resolve7(stopAt);
|
|
4040
|
+
let current = resolve7(workingDirectory);
|
|
4041
|
+
while (true) {
|
|
4042
|
+
for (const rule of LOCAL_BIN_RULES) {
|
|
4043
|
+
if (!rule.markers.some((marker) => probe(join4(current, marker))))
|
|
4044
|
+
continue;
|
|
4045
|
+
for (const binDir of rule.binDirs) {
|
|
4046
|
+
const found = probeWithSuffixes(join4(current, binDir), command, suffixes);
|
|
4047
|
+
if (found !== null)
|
|
4048
|
+
return found;
|
|
4049
|
+
}
|
|
3930
4050
|
}
|
|
4051
|
+
if (probe(join4(current, ".git")))
|
|
4052
|
+
return null;
|
|
4053
|
+
if (boundary !== undefined && current === boundary)
|
|
4054
|
+
return null;
|
|
4055
|
+
const parent = dirname7(current);
|
|
4056
|
+
if (parent === current)
|
|
4057
|
+
return null;
|
|
4058
|
+
current = parent;
|
|
3931
4059
|
}
|
|
3932
|
-
|
|
3933
|
-
|
|
4060
|
+
}
|
|
4061
|
+
function resolveFromPath(command, suffixes, platform, pathEnvOverride) {
|
|
4062
|
+
let pathEnv = pathEnvOverride ?? process.env["PATH"] ?? "";
|
|
4063
|
+
if (platform === "win32" && !pathEnv) {
|
|
3934
4064
|
pathEnv = process.env["Path"] ?? "";
|
|
3935
4065
|
}
|
|
3936
|
-
const
|
|
3937
|
-
|
|
3938
|
-
|
|
3939
|
-
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
}
|
|
4066
|
+
for (const entry of pathEnv.split(delimiter3)) {
|
|
4067
|
+
if (entry.length === 0)
|
|
4068
|
+
continue;
|
|
4069
|
+
const found = probeWithSuffixes(entry, command, suffixes);
|
|
4070
|
+
if (found !== null)
|
|
4071
|
+
return found;
|
|
3943
4072
|
}
|
|
3944
|
-
|
|
3945
|
-
|
|
3946
|
-
|
|
4073
|
+
return null;
|
|
4074
|
+
}
|
|
4075
|
+
function resolveServerBinary(command, workingDirectory, options = {}) {
|
|
4076
|
+
if (command.length === 0)
|
|
4077
|
+
return null;
|
|
4078
|
+
const [cmd] = command;
|
|
4079
|
+
if (cmd === undefined || cmd.length === 0)
|
|
4080
|
+
return null;
|
|
4081
|
+
const platform = options.platform ?? process.platform;
|
|
4082
|
+
const suffixes = executableSuffixes(platform, options.pathExt);
|
|
4083
|
+
if (cmd.includes("/") || cmd.includes("\\")) {
|
|
4084
|
+
const explicit = isAbsolute3(cmd) ? cmd : resolve7(workingDirectory ?? process.cwd(), cmd);
|
|
4085
|
+
return probe(explicit) ? explicit : null;
|
|
4086
|
+
}
|
|
4087
|
+
const cacheKey = JSON.stringify([
|
|
4088
|
+
workingDirectory ?? "",
|
|
4089
|
+
cmd,
|
|
4090
|
+
platform,
|
|
4091
|
+
options.stopAt ?? "",
|
|
4092
|
+
options.pathExt ?? "",
|
|
4093
|
+
options.pathEnv ?? process.env["PATH"] ?? ""
|
|
4094
|
+
]);
|
|
4095
|
+
const cached = resolutionCache.get(cacheKey);
|
|
4096
|
+
if (cached !== undefined)
|
|
4097
|
+
return cached;
|
|
4098
|
+
let resolved = null;
|
|
4099
|
+
if (workingDirectory !== undefined && workingDirectory.length > 0) {
|
|
4100
|
+
resolved = resolveLocal(cmd, workingDirectory, suffixes, options.stopAt);
|
|
4101
|
+
}
|
|
4102
|
+
resolved ??= resolveFromPath(cmd, suffixes, platform, options.pathEnv);
|
|
4103
|
+
if (resolved === null && cmd === "node")
|
|
4104
|
+
resolved = cmd;
|
|
4105
|
+
resolutionCache.set(cacheKey, resolved);
|
|
4106
|
+
return resolved;
|
|
4107
|
+
}
|
|
4108
|
+
function isServerInstalled(command, workingDirectory, options = {}) {
|
|
4109
|
+
if (command.length === 0)
|
|
4110
|
+
return false;
|
|
4111
|
+
return resolveServerBinary(command, workingDirectory, options) !== null;
|
|
3947
4112
|
}
|
|
3948
4113
|
|
|
3949
4114
|
// ../lsp-core/src/lsp/server-resolution.ts
|
|
4115
|
+
function withResolvedCommand(command, binaryPath) {
|
|
4116
|
+
return [binaryPath, ...command.slice(1)];
|
|
4117
|
+
}
|
|
3950
4118
|
function findServerForExtension(ext) {
|
|
3951
4119
|
const servers = getMergedServers();
|
|
4120
|
+
const workingDirectory = contextCwd();
|
|
3952
4121
|
for (const server2 of servers) {
|
|
3953
|
-
if (server2.extensions.includes(ext)
|
|
4122
|
+
if (!server2.extensions.includes(ext))
|
|
4123
|
+
continue;
|
|
4124
|
+
const binaryPath = resolveServerBinary(server2.command, workingDirectory);
|
|
4125
|
+
if (binaryPath !== null) {
|
|
3954
4126
|
const resolvedServer = {
|
|
3955
4127
|
id: server2.id,
|
|
3956
|
-
command: server2.command,
|
|
4128
|
+
command: withResolvedCommand(server2.command, binaryPath),
|
|
3957
4129
|
extensions: server2.extensions,
|
|
3958
4130
|
priority: server2.priority
|
|
3959
4131
|
};
|
|
@@ -4000,6 +4172,7 @@ function findServerForExtension(ext) {
|
|
|
4000
4172
|
function getAllServers() {
|
|
4001
4173
|
const servers = getMergedServers();
|
|
4002
4174
|
const disabled = getDisabledServerIds();
|
|
4175
|
+
const workingDirectory = contextCwd();
|
|
4003
4176
|
const result = [];
|
|
4004
4177
|
const seen = new Set;
|
|
4005
4178
|
for (const server2 of servers) {
|
|
@@ -4007,7 +4180,7 @@ function getAllServers() {
|
|
|
4007
4180
|
continue;
|
|
4008
4181
|
result.push({
|
|
4009
4182
|
id: server2.id,
|
|
4010
|
-
installed: isServerInstalled(server2.command),
|
|
4183
|
+
installed: isServerInstalled(server2.command, workingDirectory),
|
|
4011
4184
|
extensions: server2.extensions,
|
|
4012
4185
|
disabled: false,
|
|
4013
4186
|
source: server2.source,
|
|
@@ -4021,7 +4194,7 @@ function getAllServers() {
|
|
|
4021
4194
|
const builtin = BUILTIN_SERVERS[id];
|
|
4022
4195
|
result.push({
|
|
4023
4196
|
id,
|
|
4024
|
-
installed: builtin ? isServerInstalled(builtin.command) : false,
|
|
4197
|
+
installed: builtin ? isServerInstalled(builtin.command, workingDirectory) : false,
|
|
4025
4198
|
extensions: builtin?.extensions ?? [],
|
|
4026
4199
|
disabled: true,
|
|
4027
4200
|
source: "disabled",
|
|
@@ -4044,7 +4217,7 @@ function findWorkspaceRoot(filePath) {
|
|
|
4044
4217
|
const abs = resolveReadablePathInsideContext(filePath);
|
|
4045
4218
|
let dir = abs;
|
|
4046
4219
|
if (!isDirectoryPath(dir)) {
|
|
4047
|
-
dir =
|
|
4220
|
+
dir = dirname8(dir);
|
|
4048
4221
|
}
|
|
4049
4222
|
if (!isPathInside(cwd, abs))
|
|
4050
4223
|
return findWorkspaceRootOutsideContext(dir);
|
|
@@ -4060,13 +4233,13 @@ function findWorkspaceRoot(filePath) {
|
|
|
4060
4233
|
}
|
|
4061
4234
|
if (dir === cwd)
|
|
4062
4235
|
break;
|
|
4063
|
-
dir =
|
|
4236
|
+
dir = dirname8(dir);
|
|
4064
4237
|
}
|
|
4065
4238
|
return fallbackRoot;
|
|
4066
4239
|
}
|
|
4067
4240
|
function resolveReadablePathInsideContext(filePath) {
|
|
4068
4241
|
const cwd = contextCwd();
|
|
4069
|
-
const abs =
|
|
4242
|
+
const abs = resolve8(cwd, filePath);
|
|
4070
4243
|
if (isPathInside(cwd, abs))
|
|
4071
4244
|
return abs;
|
|
4072
4245
|
const rebased = rebaseThroughCanonicalAncestor(abs, cwd);
|
|
@@ -4092,7 +4265,7 @@ function rebaseThroughCanonicalAncestor(path, cwd) {
|
|
|
4092
4265
|
if (isPathInside(cwd, canonical))
|
|
4093
4266
|
return suffix.length === 0 ? canonical : join5(canonical, ...suffix);
|
|
4094
4267
|
}
|
|
4095
|
-
const parent =
|
|
4268
|
+
const parent = dirname8(current);
|
|
4096
4269
|
if (parent === current)
|
|
4097
4270
|
return;
|
|
4098
4271
|
suffix.unshift(basename3(current));
|
|
@@ -4115,7 +4288,7 @@ function nearestExistingDirectoryInsideContext(directory, cwd) {
|
|
|
4115
4288
|
return canonical;
|
|
4116
4289
|
if (current === cwd)
|
|
4117
4290
|
return;
|
|
4118
|
-
current =
|
|
4291
|
+
current = dirname8(current);
|
|
4119
4292
|
}
|
|
4120
4293
|
return;
|
|
4121
4294
|
}
|
|
@@ -4142,6 +4315,19 @@ function formatServerLookupError(result) {
|
|
|
4142
4315
|
].join(`
|
|
4143
4316
|
`);
|
|
4144
4317
|
}
|
|
4318
|
+
function formatInstallOptions(serverId, installHint) {
|
|
4319
|
+
const localHint = LSP_LOCAL_INSTALL_HINTS[serverId];
|
|
4320
|
+
if (localHint === undefined) {
|
|
4321
|
+
return ["To install, run:", ` ${installHint}`];
|
|
4322
|
+
}
|
|
4323
|
+
return [
|
|
4324
|
+
"To install in THIS repository (preferred — no global install needed):",
|
|
4325
|
+
` ${localHint}`,
|
|
4326
|
+
"",
|
|
4327
|
+
"Or install it globally:",
|
|
4328
|
+
` ${installHint}`
|
|
4329
|
+
];
|
|
4330
|
+
}
|
|
4145
4331
|
function formatNotInstalled(result) {
|
|
4146
4332
|
const { server: server2, installHint } = result;
|
|
4147
4333
|
const extensions = server2.extensions.join(", ");
|
|
@@ -4156,19 +4342,19 @@ function formatNotInstalled(result) {
|
|
|
4156
4342
|
`Command not found: ${server2.command[0]}`,
|
|
4157
4343
|
""
|
|
4158
4344
|
];
|
|
4345
|
+
const installOptions = formatInstallOptions(server2.id, installHint);
|
|
4159
4346
|
if (decision === "allowed") {
|
|
4160
4347
|
return [
|
|
4161
4348
|
...header,
|
|
4162
4349
|
"The user has pre-authorized LSP installation. Run the install command, then retry this tool:",
|
|
4163
|
-
|
|
4350
|
+
...installOptions.slice(1)
|
|
4164
4351
|
].join(`
|
|
4165
4352
|
`);
|
|
4166
4353
|
}
|
|
4167
4354
|
if (!context.capabilities.installDecisionTool) {
|
|
4168
4355
|
return [
|
|
4169
4356
|
...header,
|
|
4170
|
-
|
|
4171
|
-
` ${installHint}`,
|
|
4357
|
+
...installOptions,
|
|
4172
4358
|
"",
|
|
4173
4359
|
"ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
|
|
4174
4360
|
"Install-decision recording is unavailable in this harness; proceed without LSP if the user declines."
|
|
@@ -4177,8 +4363,7 @@ function formatNotInstalled(result) {
|
|
|
4177
4363
|
}
|
|
4178
4364
|
return [
|
|
4179
4365
|
...header,
|
|
4180
|
-
|
|
4181
|
-
` ${installHint}`,
|
|
4366
|
+
...installOptions,
|
|
4182
4367
|
"",
|
|
4183
4368
|
"ACTION REQUIRED — ASK THE USER whether to install this LSP server.",
|
|
4184
4369
|
"- If the user agrees: run the install command above, then retry this tool.",
|
|
@@ -4234,7 +4419,7 @@ async function withLspClient(filePath, fn, toolName, options = {}) {
|
|
|
4234
4419
|
|
|
4235
4420
|
// ../lsp-core/src/lsp/directory-diagnostics.ts
|
|
4236
4421
|
import { existsSync as existsSync11, lstatSync as lstatSync4, readdirSync as readdirSync3 } from "node:fs";
|
|
4237
|
-
import { join as join6, resolve as
|
|
4422
|
+
import { join as join6, resolve as resolve9 } from "node:path";
|
|
4238
4423
|
|
|
4239
4424
|
// ../lsp-core/src/lsp/formatters.ts
|
|
4240
4425
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
@@ -4389,7 +4574,7 @@ async function aggregateDiagnosticsForDirectory(directory, extension, severity,
|
|
|
4389
4574
|
if (!extension.startsWith(".")) {
|
|
4390
4575
|
throw new LspInvalidPathError(`Extension must start with a dot (e.g., ".ts", not "${extension}"). Use ".${extension}" instead.`);
|
|
4391
4576
|
}
|
|
4392
|
-
const absDir =
|
|
4577
|
+
const absDir = resolve9(options.workspaceRoot ?? contextCwd(), directory);
|
|
4393
4578
|
if (!existsSync11(absDir)) {
|
|
4394
4579
|
throw new LspInvalidPathError(`Directory does not exist: ${absDir}`);
|
|
4395
4580
|
}
|
|
@@ -4746,6 +4931,96 @@ async function executeLspDiagnostics(params, signal) {
|
|
|
4746
4931
|
throw error;
|
|
4747
4932
|
}
|
|
4748
4933
|
}
|
|
4934
|
+
// ../lsp-core/src/lsp/format-document.ts
|
|
4935
|
+
import { readFileSync as readFileSync5, renameSync as renameSync3, unlinkSync, writeFileSync as writeFileSync3 } from "node:fs";
|
|
4936
|
+
var DEFAULT_FORMATTING_OPTIONS = {
|
|
4937
|
+
tabSize: 4,
|
|
4938
|
+
insertSpaces: false,
|
|
4939
|
+
trimTrailingWhitespace: true,
|
|
4940
|
+
insertFinalNewline: true,
|
|
4941
|
+
trimFinalNewlines: true
|
|
4942
|
+
};
|
|
4943
|
+
var UNCHANGED = { status: "unchanged", linesAdded: 0, linesRemoved: 0 };
|
|
4944
|
+
async function formatDocumentWithClient(client, filePath, options = {}) {
|
|
4945
|
+
const edits = await client.formatDocument(filePath, options.formattingOptions ?? DEFAULT_FORMATTING_OPTIONS, options.signal);
|
|
4946
|
+
if (edits === null)
|
|
4947
|
+
return { status: "unavailable", reason: "capability_not_advertised" };
|
|
4948
|
+
if (edits.length === 0)
|
|
4949
|
+
return UNCHANGED;
|
|
4950
|
+
const before = readFileSync5(filePath, "utf-8");
|
|
4951
|
+
const normalized = normalizeTextEdits(before, edits, 0);
|
|
4952
|
+
if (normalized.text === before)
|
|
4953
|
+
return UNCHANGED;
|
|
4954
|
+
writeAtomically(filePath, normalized.text);
|
|
4955
|
+
await client.openFile(filePath);
|
|
4956
|
+
return {
|
|
4957
|
+
status: "formatted",
|
|
4958
|
+
...lineDelta(normalized.edits)
|
|
4959
|
+
};
|
|
4960
|
+
}
|
|
4961
|
+
function lineDelta(edits) {
|
|
4962
|
+
let linesAdded = 0;
|
|
4963
|
+
let linesRemoved = 0;
|
|
4964
|
+
for (const edit of edits) {
|
|
4965
|
+
linesRemoved += edit.range.end.line - edit.range.start.line + 1;
|
|
4966
|
+
linesAdded += edit.newText.split(`
|
|
4967
|
+
`).length;
|
|
4968
|
+
}
|
|
4969
|
+
return { linesAdded, linesRemoved };
|
|
4970
|
+
}
|
|
4971
|
+
function writeAtomically(filePath, content) {
|
|
4972
|
+
const tempPath = `${filePath}.omo-format.tmp`;
|
|
4973
|
+
writeFileSync3(tempPath, content, "utf-8");
|
|
4974
|
+
try {
|
|
4975
|
+
renameSync3(tempPath, filePath);
|
|
4976
|
+
} catch (error) {
|
|
4977
|
+
try {
|
|
4978
|
+
unlinkSync(tempPath);
|
|
4979
|
+
} catch {}
|
|
4980
|
+
throw error;
|
|
4981
|
+
}
|
|
4982
|
+
}
|
|
4983
|
+
|
|
4984
|
+
// ../lsp-core/src/tools/format.ts
|
|
4985
|
+
async function executeLspFormat(params, signal) {
|
|
4986
|
+
const filePath = requireString(params, "filePath");
|
|
4987
|
+
try {
|
|
4988
|
+
const result = await withLspClient(filePath, async (client, _workspaceRoot, resolvedFilePath) => formatDocumentWithClient(client, resolvedFilePath, signal === undefined ? {} : { signal }), "format", clientOptions(signal));
|
|
4989
|
+
return text(describeResult(filePath, result), formatDetails(filePath, result));
|
|
4990
|
+
} catch (error) {
|
|
4991
|
+
const missingDependency = missingDependencyResult(error, {
|
|
4992
|
+
filePath,
|
|
4993
|
+
status: "unavailable",
|
|
4994
|
+
reason: "server_unavailable",
|
|
4995
|
+
linesAdded: 0,
|
|
4996
|
+
linesRemoved: 0
|
|
4997
|
+
});
|
|
4998
|
+
if (missingDependency)
|
|
4999
|
+
return missingDependency;
|
|
5000
|
+
throw error;
|
|
5001
|
+
}
|
|
5002
|
+
}
|
|
5003
|
+
function formatDetails(filePath, result) {
|
|
5004
|
+
if (result.status === "unavailable") {
|
|
5005
|
+
return { filePath, status: "unavailable", reason: result.reason, linesAdded: 0, linesRemoved: 0 };
|
|
5006
|
+
}
|
|
5007
|
+
return {
|
|
5008
|
+
filePath,
|
|
5009
|
+
status: result.status,
|
|
5010
|
+
linesAdded: result.linesAdded,
|
|
5011
|
+
linesRemoved: result.linesRemoved
|
|
5012
|
+
};
|
|
5013
|
+
}
|
|
5014
|
+
function describeResult(filePath, result) {
|
|
5015
|
+
if (result.status === "unavailable") {
|
|
5016
|
+
return `Formatting unavailable for ${filePath}: the language server does not advertise documentFormattingProvider.`;
|
|
5017
|
+
}
|
|
5018
|
+
if (result.status === "unchanged") {
|
|
5019
|
+
return `Already formatted: ${filePath}`;
|
|
5020
|
+
}
|
|
5021
|
+
return `Formatted ${filePath} (+${result.linesAdded}/-${result.linesRemoved} lines)`;
|
|
5022
|
+
}
|
|
5023
|
+
|
|
4749
5024
|
// ../lsp-core/src/tools/install-decision.ts
|
|
4750
5025
|
async function executeLspInstallDecision(params) {
|
|
4751
5026
|
const serverId = requireString(params, "server_id");
|
|
@@ -5078,6 +5353,16 @@ var LSP_MCP_TOOLS = [
|
|
|
5078
5353
|
}, ["filePath", "line", "character", "newName"]),
|
|
5079
5354
|
execute: executeLspRename
|
|
5080
5355
|
},
|
|
5356
|
+
{
|
|
5357
|
+
name: "format",
|
|
5358
|
+
aliases: ["lsp_format"],
|
|
5359
|
+
title: "LSP Format",
|
|
5360
|
+
description: "Format a source file with its language server and write the returned edits to disk.",
|
|
5361
|
+
inputSchema: objectSchema({
|
|
5362
|
+
filePath: { type: "string", description: "Source file path to format." }
|
|
5363
|
+
}, ["filePath"]),
|
|
5364
|
+
execute: executeLspFormat
|
|
5365
|
+
},
|
|
5081
5366
|
{
|
|
5082
5367
|
name: "install_decision",
|
|
5083
5368
|
aliases: ["lsp_install_decision"],
|