oh-my-opencode 5.0.0-beta.23 → 5.0.0-beta.25
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 +141 -184
- package/dist/cli-node/index.js +141 -184
- package/dist/index.js +1508 -1567
- package/dist/skills/debugging/SKILL.md +0 -2
- package/dist/skills/debugging/references/methodology/00-setup.md +0 -12
- package/dist/skills/debugging/references/methodology/02-investigate.md +0 -21
- package/dist/skills/debugging/references/runtimes/go.md +1 -15
- package/dist/skills/debugging/references/runtimes/native-binary.md +0 -38
- package/dist/skills/debugging/references/runtimes/node.md +0 -25
- package/dist/skills/debugging/references/runtimes/python.md +0 -13
- package/dist/skills/debugging/references/runtimes/rust.md +0 -41
- package/dist/tools/delegate-task/types.d.ts +0 -3
- package/dist/tui.js +0 -25
- package/package.json +15 -18
- package/packages/lsp-core/src/index.ts +0 -1
- package/packages/lsp-core/src/lsp/client-wrapper.ts +5 -25
- package/packages/lsp-core/src/lsp/client.ts +0 -26
- package/packages/lsp-core/src/lsp/connection.ts +0 -16
- package/packages/lsp-core/src/lsp/constants.ts +0 -1
- package/packages/lsp-core/src/lsp/manager.ts +1 -34
- package/packages/lsp-core/src/lsp/server-definitions.ts +0 -29
- package/packages/lsp-core/src/lsp/server-installation.ts +27 -200
- package/packages/lsp-core/src/lsp/server-resolution.ts +5 -18
- package/packages/lsp-core/src/lsp/transport.ts +0 -9
- package/packages/lsp-core/src/lsp/types.ts +0 -8
- package/packages/lsp-core/src/tool-surface.test.ts +1 -13
- package/packages/lsp-core/src/tools/definitions.ts +0 -14
- package/packages/lsp-core/src/tools/index.ts +0 -1
- package/packages/lsp-core/src/tools/types.ts +0 -10
- package/packages/lsp-daemon/dist/cli.js +144 -432
- package/packages/lsp-daemon/dist/client.d.ts +0 -10
- package/packages/lsp-daemon/dist/client.js +107 -401
- package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -1
- package/packages/lsp-daemon/dist/daemon-client.js +0 -3
- package/packages/lsp-daemon/dist/index.js +107 -395
- package/packages/lsp-tools-mcp/dist/cli.js +99 -384
- package/packages/lsp-tools-mcp/dist/lsp/manager.js +0 -53
- package/packages/lsp-tools-mcp/dist/mcp.js +99 -384
- package/packages/lsp-tools-mcp/dist/tools.js +99 -385
- 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 +35 -69
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +2 -21
- 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 +121 -404
- 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 +15 -30
- package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +15 -30
- 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/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- 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 +0 -2
- package/packages/omo-codex/plugin/skills/debugging/references/methodology/00-setup.md +0 -12
- package/packages/omo-codex/plugin/skills/debugging/references/methodology/02-investigate.md +0 -21
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/go.md +1 -15
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/native-binary.md +0 -38
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/node.md +0 -25
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/python.md +0 -13
- package/packages/omo-codex/plugin/skills/debugging/references/runtimes/rust.md +0 -41
- 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 +23 -38
- package/packages/shared-skills/skills/debugging/SKILL.md +0 -2
- package/packages/shared-skills/skills/debugging/references/methodology/00-setup.md +0 -12
- package/packages/shared-skills/skills/debugging/references/methodology/02-investigate.md +0 -21
- package/packages/shared-skills/skills/debugging/references/runtimes/go.md +1 -15
- package/packages/shared-skills/skills/debugging/references/runtimes/native-binary.md +0 -38
- package/packages/shared-skills/skills/debugging/references/runtimes/node.md +0 -25
- package/packages/shared-skills/skills/debugging/references/runtimes/python.md +0 -13
- package/packages/shared-skills/skills/debugging/references/runtimes/rust.md +0 -41
- package/dist/skills/debugging/references/scripts/dap.mjs +0 -267
- package/dist/skills/debugging/references/scripts/dap.test.ts +0 -86
- package/dist/skills/debugging/references/scripts/fixture-adapter.mjs +0 -59
- package/dist/skills/debugging/references/tools/dap.md +0 -103
- package/dist/skills/debugging/references/tools/frida.md +0 -193
- package/packages/lsp-core/src/lsp/format-document.test.ts +0 -143
- package/packages/lsp-core/src/lsp/format-document.ts +0 -90
- package/packages/lsp-core/src/lsp/manager-max-clients.test.ts +0 -151
- package/packages/lsp-core/src/lsp/server-installation.test.ts +0 -376
- package/packages/lsp-core/src/lsp/server-resolution-local-binary.test.ts +0 -170
- package/packages/lsp-core/src/tools/format.test.ts +0 -56
- package/packages/lsp-core/src/tools/format.ts +0 -56
- package/packages/omo-codex/plugin/skills/debugging/references/scripts/dap.mjs +0 -267
- package/packages/omo-codex/plugin/skills/debugging/references/scripts/fixture-adapter.mjs +0 -59
- package/packages/omo-codex/plugin/skills/debugging/references/tools/dap.md +0 -103
- package/packages/omo-codex/plugin/skills/debugging/references/tools/frida.md +0 -193
- package/packages/shared-skills/skills/debugging/references/scripts/dap.mjs +0 -267
- package/packages/shared-skills/skills/debugging/references/scripts/dap.test.ts +0 -86
- package/packages/shared-skills/skills/debugging/references/scripts/fixture-adapter.mjs +0 -59
- package/packages/shared-skills/skills/debugging/references/tools/dap.md +0 -103
- package/packages/shared-skills/skills/debugging/references/tools/frida.md +0 -193
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from "bun:test";
|
|
2
|
-
|
|
3
|
-
import type { LspClient } from "./client.js";
|
|
4
|
-
import { LspManager } from "./manager.js";
|
|
5
|
-
import type { ResolvedServer } from "./types.js";
|
|
6
|
-
|
|
7
|
-
const SERVER: ResolvedServer = {
|
|
8
|
-
id: "typescript",
|
|
9
|
-
command: ["typescript-language-server", "--stdio"],
|
|
10
|
-
extensions: [".ts"],
|
|
11
|
-
priority: 1,
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
class FakeLspClient {
|
|
15
|
-
alive = true;
|
|
16
|
-
stopCallCount = 0;
|
|
17
|
-
|
|
18
|
-
constructor(readonly root: string) {}
|
|
19
|
-
|
|
20
|
-
async start(): Promise<void> {}
|
|
21
|
-
|
|
22
|
-
async initialize(): Promise<void> {}
|
|
23
|
-
|
|
24
|
-
isAlive(): boolean {
|
|
25
|
-
return this.alive;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
command(): string[] {
|
|
29
|
-
return [...SERVER.command];
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
async stop(): Promise<void> {
|
|
33
|
-
this.stopCallCount += 1;
|
|
34
|
-
this.alive = false;
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
function managerWithFakes(maxResidentClients: number): {
|
|
39
|
-
readonly manager: LspManager;
|
|
40
|
-
readonly clients: Map<string, FakeLspClient>;
|
|
41
|
-
readonly clock: { value: number };
|
|
42
|
-
} {
|
|
43
|
-
const clients = new Map<string, FakeLspClient>();
|
|
44
|
-
const clock = { value: 1_000 };
|
|
45
|
-
const manager = new LspManager({
|
|
46
|
-
maxResidentClients,
|
|
47
|
-
reaperIntervalMs: 60_000,
|
|
48
|
-
idleTimeoutMs: 60_000_000,
|
|
49
|
-
now: () => clock.value,
|
|
50
|
-
clientFactory: (root) => {
|
|
51
|
-
const client = new FakeLspClient(root);
|
|
52
|
-
clients.set(root, client);
|
|
53
|
-
return client as unknown as LspClient;
|
|
54
|
-
},
|
|
55
|
-
});
|
|
56
|
-
return { manager, clients, clock };
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
describe("LspManager resident client cap", () => {
|
|
60
|
-
it("#given a cap of two idle clients #when a third client is admitted #then the least recently used one is stopped and evicted", async () => {
|
|
61
|
-
// given
|
|
62
|
-
const { manager, clients, clock } = managerWithFakes(2);
|
|
63
|
-
try {
|
|
64
|
-
await manager.getClient("/root-a", SERVER);
|
|
65
|
-
manager.releaseClient("/root-a", SERVER.id);
|
|
66
|
-
clock.value += 10;
|
|
67
|
-
await manager.getClient("/root-b", SERVER);
|
|
68
|
-
manager.releaseClient("/root-b", SERVER.id);
|
|
69
|
-
clock.value += 10;
|
|
70
|
-
|
|
71
|
-
// when
|
|
72
|
-
await manager.getClient("/root-c", SERVER);
|
|
73
|
-
manager.releaseClient("/root-c", SERVER.id);
|
|
74
|
-
|
|
75
|
-
// then
|
|
76
|
-
expect(manager.clientCount()).toBe(2);
|
|
77
|
-
expect(manager.hasClient("/root-a", SERVER.id)).toBe(false);
|
|
78
|
-
expect(manager.hasClient("/root-b", SERVER.id)).toBe(true);
|
|
79
|
-
expect(manager.hasClient("/root-c", SERVER.id)).toBe(true);
|
|
80
|
-
expect(clients.get("/root-a")?.stopCallCount).toBe(1);
|
|
81
|
-
} finally {
|
|
82
|
-
await manager.stopAll();
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
it("#given the least recently used client is mid-request #when a new client is admitted #then the busy client survives and an idle one is evicted", async () => {
|
|
87
|
-
// given
|
|
88
|
-
const { manager, clients, clock } = managerWithFakes(2);
|
|
89
|
-
try {
|
|
90
|
-
await manager.getClient("/root-busy", SERVER);
|
|
91
|
-
clock.value += 10;
|
|
92
|
-
await manager.getClient("/root-idle", SERVER);
|
|
93
|
-
manager.releaseClient("/root-idle", SERVER.id);
|
|
94
|
-
clock.value += 10;
|
|
95
|
-
|
|
96
|
-
// when
|
|
97
|
-
await manager.getClient("/root-new", SERVER);
|
|
98
|
-
manager.releaseClient("/root-new", SERVER.id);
|
|
99
|
-
|
|
100
|
-
// then
|
|
101
|
-
expect(manager.hasClient("/root-busy", SERVER.id)).toBe(true);
|
|
102
|
-
expect(manager.hasClient("/root-idle", SERVER.id)).toBe(false);
|
|
103
|
-
expect(clients.get("/root-busy")?.stopCallCount).toBe(0);
|
|
104
|
-
expect(clients.get("/root-idle")?.stopCallCount).toBe(1);
|
|
105
|
-
} finally {
|
|
106
|
-
manager.releaseClient("/root-busy", SERVER.id);
|
|
107
|
-
await manager.stopAll();
|
|
108
|
-
}
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it("#given every resident client is busy #when a new client is admitted #then nothing is evicted and the cap is exceeded rather than breaking a live request", async () => {
|
|
112
|
-
// given
|
|
113
|
-
const { manager, clients } = managerWithFakes(1);
|
|
114
|
-
try {
|
|
115
|
-
await manager.getClient("/root-busy", SERVER);
|
|
116
|
-
|
|
117
|
-
// when
|
|
118
|
-
await manager.getClient("/root-second", SERVER);
|
|
119
|
-
|
|
120
|
-
// then
|
|
121
|
-
expect(manager.clientCount()).toBe(2);
|
|
122
|
-
expect(clients.get("/root-busy")?.stopCallCount).toBe(0);
|
|
123
|
-
} finally {
|
|
124
|
-
manager.releaseClient("/root-busy", SERVER.id);
|
|
125
|
-
manager.releaseClient("/root-second", SERVER.id);
|
|
126
|
-
await manager.stopAll();
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it("#given warmup requests beyond the cap #when a warm client is admitted #then the idle least recently used client is evicted too", async () => {
|
|
131
|
-
// given
|
|
132
|
-
const { manager, clients, clock } = managerWithFakes(1);
|
|
133
|
-
try {
|
|
134
|
-
await manager.getClient("/root-first", SERVER);
|
|
135
|
-
manager.releaseClient("/root-first", SERVER.id);
|
|
136
|
-
clock.value += 10;
|
|
137
|
-
|
|
138
|
-
// when
|
|
139
|
-
manager.warmupClient("/root-warm", SERVER);
|
|
140
|
-
await Promise.resolve();
|
|
141
|
-
await Promise.resolve();
|
|
142
|
-
|
|
143
|
-
// then
|
|
144
|
-
expect(manager.hasClient("/root-first", SERVER.id)).toBe(false);
|
|
145
|
-
expect(clients.get("/root-first")?.stopCallCount).toBe(1);
|
|
146
|
-
expect(manager.hasClient("/root-warm", SERVER.id)).toBe(true);
|
|
147
|
-
} finally {
|
|
148
|
-
await manager.stopAll();
|
|
149
|
-
}
|
|
150
|
-
});
|
|
151
|
-
});
|
|
@@ -1,376 +0,0 @@
|
|
|
1
|
-
import { chmodSync, mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
__resetServerBinaryResolutionCacheForTests,
|
|
8
|
-
__serverBinaryProbeCountForTests,
|
|
9
|
-
isServerInstalled,
|
|
10
|
-
resolveServerBinary,
|
|
11
|
-
} from "./server-installation.js";
|
|
12
|
-
|
|
13
|
-
const tempDirectories: string[] = [];
|
|
14
|
-
let previousPath: string | undefined;
|
|
15
|
-
|
|
16
|
-
function makeTempRoot(prefix: string): string {
|
|
17
|
-
const root = mkdtempSync(join(tmpdir(), prefix));
|
|
18
|
-
tempDirectories.push(root);
|
|
19
|
-
return root;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
function writeExecutable(path: string, body = "#!/bin/sh\nexit 0\n"): string {
|
|
23
|
-
mkdirSync(join(path, ".."), { recursive: true });
|
|
24
|
-
writeFileSync(path, body);
|
|
25
|
-
chmodSync(path, 0o755);
|
|
26
|
-
return path;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
beforeEach(() => {
|
|
30
|
-
previousPath = process.env["PATH"];
|
|
31
|
-
__resetServerBinaryResolutionCacheForTests();
|
|
32
|
-
});
|
|
33
|
-
|
|
34
|
-
afterEach(() => {
|
|
35
|
-
if (previousPath === undefined) delete process.env["PATH"];
|
|
36
|
-
else process.env["PATH"] = previousPath;
|
|
37
|
-
for (const directory of tempDirectories.splice(0)) {
|
|
38
|
-
rmSync(directory, { recursive: true, force: true });
|
|
39
|
-
}
|
|
40
|
-
__resetServerBinaryResolutionCacheForTests();
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
describe("resolveServerBinary node marker resolution", () => {
|
|
44
|
-
test("#given a package.json marker and a local node bin #when resolving with an empty PATH #then it returns the local absolute path", () => {
|
|
45
|
-
// given
|
|
46
|
-
const root = makeTempRoot("lsp-local-node-");
|
|
47
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
48
|
-
const binaryPath = writeExecutable(join(root, "node_modules", ".bin", "biome"));
|
|
49
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
50
|
-
|
|
51
|
-
// when
|
|
52
|
-
const resolved = resolveServerBinary(["biome", "lsp-proxy", "--stdio"], root);
|
|
53
|
-
|
|
54
|
-
// then
|
|
55
|
-
expect(resolved).toBe(binaryPath);
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
test("#given a nested working directory #when resolving #then it walks up to the marker root", () => {
|
|
59
|
-
// given
|
|
60
|
-
const root = makeTempRoot("lsp-local-nested-");
|
|
61
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
62
|
-
const binaryPath = writeExecutable(join(root, "node_modules", ".bin", "biome"));
|
|
63
|
-
const nested = join(root, "packages", "deep", "src");
|
|
64
|
-
mkdirSync(nested, { recursive: true });
|
|
65
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
66
|
-
|
|
67
|
-
// when
|
|
68
|
-
const resolved = resolveServerBinary(["biome", "lsp-proxy", "--stdio"], nested);
|
|
69
|
-
|
|
70
|
-
// then
|
|
71
|
-
expect(resolved).toBe(binaryPath);
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
test("#given a local bin without any project marker #when resolving #then the unmarked directory is not trusted", () => {
|
|
75
|
-
// given
|
|
76
|
-
const root = makeTempRoot("lsp-local-unmarked-");
|
|
77
|
-
writeExecutable(join(root, "node_modules", ".bin", "biome"));
|
|
78
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
79
|
-
|
|
80
|
-
// when
|
|
81
|
-
const resolved = resolveServerBinary(["biome", "lsp-proxy", "--stdio"], root);
|
|
82
|
-
|
|
83
|
-
// then
|
|
84
|
-
expect(resolved).toBeNull();
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("#given a bun.lock marker #when resolving #then the node bin directory is probed", () => {
|
|
88
|
-
// given
|
|
89
|
-
const root = makeTempRoot("lsp-local-bunlock-");
|
|
90
|
-
writeFileSync(join(root, "bun.lock"), "");
|
|
91
|
-
const binaryPath = writeExecutable(join(root, "node_modules", ".bin", "biome"));
|
|
92
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
93
|
-
|
|
94
|
-
// when / then
|
|
95
|
-
expect(resolveServerBinary(["biome"], root)).toBe(binaryPath);
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
describe("resolveServerBinary non-node ecosystems", () => {
|
|
100
|
-
test("#given a pyproject marker and a venv binary #when resolving #then it returns the venv path", () => {
|
|
101
|
-
// given
|
|
102
|
-
const root = makeTempRoot("lsp-local-python-");
|
|
103
|
-
writeFileSync(join(root, "pyproject.toml"), "[project]\n");
|
|
104
|
-
const binaryPath = writeExecutable(join(root, ".venv", "bin", "ruff"));
|
|
105
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
106
|
-
|
|
107
|
-
// when / then
|
|
108
|
-
expect(resolveServerBinary(["ruff", "server"], root)).toBe(binaryPath);
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
test("#given a Gemfile marker and a bundled binary #when resolving #then it returns the vendored bundle path", () => {
|
|
112
|
-
// given
|
|
113
|
-
const root = makeTempRoot("lsp-local-ruby-");
|
|
114
|
-
writeFileSync(join(root, "Gemfile"), "source 'https://rubygems.org'\n");
|
|
115
|
-
const binaryPath = writeExecutable(join(root, "vendor", "bundle", "bin", "rubocop"));
|
|
116
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
117
|
-
|
|
118
|
-
// when / then
|
|
119
|
-
expect(resolveServerBinary(["rubocop", "--lsp"], root)).toBe(binaryPath);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
test("#given a go.mod marker and a project bin #when resolving #then it returns the project bin path", () => {
|
|
123
|
-
// given
|
|
124
|
-
const root = makeTempRoot("lsp-local-go-");
|
|
125
|
-
writeFileSync(join(root, "go.mod"), "module example.com/demo\n");
|
|
126
|
-
const binaryPath = writeExecutable(join(root, "bin", "gopls"));
|
|
127
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
128
|
-
|
|
129
|
-
// when / then
|
|
130
|
-
expect(resolveServerBinary(["gopls"], root)).toBe(binaryPath);
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
test("#given a python marker without a matching venv binary #when resolving #then node bin dirs are not borrowed", () => {
|
|
134
|
-
// given
|
|
135
|
-
const root = makeTempRoot("lsp-local-python-miss-");
|
|
136
|
-
writeFileSync(join(root, "requirements.txt"), "ruff\n");
|
|
137
|
-
writeExecutable(join(root, "node_modules", ".bin", "ruff"));
|
|
138
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
139
|
-
|
|
140
|
-
// when / then
|
|
141
|
-
expect(resolveServerBinary(["ruff", "server"], root)).toBeNull();
|
|
142
|
-
});
|
|
143
|
-
});
|
|
144
|
-
|
|
145
|
-
describe("resolveServerBinary probe ordering", () => {
|
|
146
|
-
test("#given a local binary and a PATH binary #when resolving #then the local binary wins", () => {
|
|
147
|
-
// given
|
|
148
|
-
const root = makeTempRoot("lsp-local-priority-");
|
|
149
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
150
|
-
const localBinary = writeExecutable(join(root, "node_modules", ".bin", "biome"));
|
|
151
|
-
const pathDir = makeTempRoot("lsp-path-priority-");
|
|
152
|
-
writeExecutable(join(pathDir, "biome"));
|
|
153
|
-
process.env["PATH"] = pathDir;
|
|
154
|
-
|
|
155
|
-
// when / then
|
|
156
|
-
expect(resolveServerBinary(["biome"], root)).toBe(localBinary);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
test("#given only a PATH binary #when resolving #then the PATH entry is returned", () => {
|
|
160
|
-
// given
|
|
161
|
-
const root = makeTempRoot("lsp-path-fallback-root-");
|
|
162
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
163
|
-
const pathDir = makeTempRoot("lsp-path-fallback-");
|
|
164
|
-
const pathBinary = writeExecutable(join(pathDir, "biome"));
|
|
165
|
-
process.env["PATH"] = pathDir;
|
|
166
|
-
|
|
167
|
-
// when / then
|
|
168
|
-
expect(resolveServerBinary(["biome"], root)).toBe(pathBinary);
|
|
169
|
-
});
|
|
170
|
-
|
|
171
|
-
test("#given a path-shaped command #when resolving #then the command path itself is validated", () => {
|
|
172
|
-
// given
|
|
173
|
-
const root = makeTempRoot("lsp-explicit-path-");
|
|
174
|
-
const binaryPath = writeExecutable(join(root, "tools", "custom-ls"));
|
|
175
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
176
|
-
|
|
177
|
-
// when / then
|
|
178
|
-
expect(resolveServerBinary([binaryPath, "--stdio"], root)).toBe(binaryPath);
|
|
179
|
-
expect(resolveServerBinary([join(root, "tools", "absent-ls")], root)).toBeNull();
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test("#given no working directory #when resolving #then it falls back to PATH only", () => {
|
|
183
|
-
// given
|
|
184
|
-
const pathDir = makeTempRoot("lsp-no-cwd-");
|
|
185
|
-
const pathBinary = writeExecutable(join(pathDir, "biome"));
|
|
186
|
-
process.env["PATH"] = pathDir;
|
|
187
|
-
|
|
188
|
-
// when / then
|
|
189
|
-
expect(resolveServerBinary(["biome"])).toBe(pathBinary);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test("#given nothing anywhere #when resolving #then it returns null", () => {
|
|
193
|
-
// given
|
|
194
|
-
const root = makeTempRoot("lsp-unresolved-");
|
|
195
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
196
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
197
|
-
|
|
198
|
-
// when / then
|
|
199
|
-
expect(resolveServerBinary(["biome"], root)).toBeNull();
|
|
200
|
-
expect(resolveServerBinary([], root)).toBeNull();
|
|
201
|
-
});
|
|
202
|
-
});
|
|
203
|
-
|
|
204
|
-
describe("resolveServerBinary containment", () => {
|
|
205
|
-
test("#given a marker only above the working directory root #when resolving with a stop boundary #then it does not escape the boundary", () => {
|
|
206
|
-
// given
|
|
207
|
-
const outer = makeTempRoot("lsp-boundary-outer-");
|
|
208
|
-
writeFileSync(join(outer, "package.json"), "{}\n");
|
|
209
|
-
writeExecutable(join(outer, "node_modules", ".bin", "biome"));
|
|
210
|
-
const inner = join(outer, "inner");
|
|
211
|
-
mkdirSync(inner, { recursive: true });
|
|
212
|
-
process.env["PATH"] = join(outer, "definitely-empty");
|
|
213
|
-
|
|
214
|
-
// when / then
|
|
215
|
-
expect(resolveServerBinary(["biome"], inner, { stopAt: inner })).toBeNull();
|
|
216
|
-
expect(resolveServerBinary(["biome"], inner)).toBe(join(outer, "node_modules", ".bin", "biome"));
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
test("#given a git repository boundary #when resolving #then the walk stops at the repository root", () => {
|
|
220
|
-
// given
|
|
221
|
-
const outer = makeTempRoot("lsp-git-boundary-");
|
|
222
|
-
writeFileSync(join(outer, "package.json"), "{}\n");
|
|
223
|
-
writeExecutable(join(outer, "node_modules", ".bin", "biome"));
|
|
224
|
-
const repo = join(outer, "repo");
|
|
225
|
-
mkdirSync(join(repo, ".git"), { recursive: true });
|
|
226
|
-
process.env["PATH"] = join(outer, "definitely-empty");
|
|
227
|
-
|
|
228
|
-
// when / then
|
|
229
|
-
expect(resolveServerBinary(["biome"], repo)).toBeNull();
|
|
230
|
-
});
|
|
231
|
-
});
|
|
232
|
-
|
|
233
|
-
describe("resolveServerBinary windows suffix probing", () => {
|
|
234
|
-
test("#given a windows platform and a .cmd shim #when resolving locally #then the suffixed binary is found", () => {
|
|
235
|
-
// given
|
|
236
|
-
const root = makeTempRoot("lsp-win-local-");
|
|
237
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
238
|
-
const binaryPath = writeExecutable(join(root, "node_modules", ".bin", "biome.cmd"));
|
|
239
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
240
|
-
|
|
241
|
-
// when
|
|
242
|
-
const resolved = resolveServerBinary(["biome"], root, { platform: "win32", pathExt: ".COM;.EXE;.CMD" });
|
|
243
|
-
|
|
244
|
-
// then
|
|
245
|
-
expect(resolved).toBe(binaryPath);
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
test("#given a windows platform and a PATH .exe #when resolving #then the suffixed PATH entry is found", () => {
|
|
249
|
-
// given
|
|
250
|
-
const pathDir = makeTempRoot("lsp-win-path-");
|
|
251
|
-
const binaryPath = writeExecutable(join(pathDir, "biome.exe"));
|
|
252
|
-
process.env["PATH"] = pathDir;
|
|
253
|
-
|
|
254
|
-
// when / then
|
|
255
|
-
expect(resolveServerBinary(["biome"], undefined, { platform: "win32", pathExt: ".COM;.EXE" })).toBe(binaryPath);
|
|
256
|
-
});
|
|
257
|
-
});
|
|
258
|
-
|
|
259
|
-
describe("resolveServerBinary caching", () => {
|
|
260
|
-
test("#given a repeated lookup #when resolving twice #then the filesystem is probed only once", () => {
|
|
261
|
-
// given
|
|
262
|
-
const root = makeTempRoot("lsp-cache-hit-");
|
|
263
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
264
|
-
const binaryPath = writeExecutable(join(root, "node_modules", ".bin", "biome"));
|
|
265
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
266
|
-
|
|
267
|
-
// when
|
|
268
|
-
const first = resolveServerBinary(["biome"], root);
|
|
269
|
-
const probesAfterFirst = __serverBinaryProbeCountForTests();
|
|
270
|
-
const second = resolveServerBinary(["biome"], root);
|
|
271
|
-
const probesAfterSecond = __serverBinaryProbeCountForTests();
|
|
272
|
-
|
|
273
|
-
// then
|
|
274
|
-
expect(first).toBe(binaryPath);
|
|
275
|
-
expect(second).toBe(binaryPath);
|
|
276
|
-
expect(probesAfterFirst).toBeGreaterThan(0);
|
|
277
|
-
expect(probesAfterSecond).toBe(probesAfterFirst);
|
|
278
|
-
});
|
|
279
|
-
|
|
280
|
-
test("#given a cached negative lookup #when resolving twice #then the miss is also cached", () => {
|
|
281
|
-
// given
|
|
282
|
-
const root = makeTempRoot("lsp-cache-miss-");
|
|
283
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
284
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
285
|
-
|
|
286
|
-
// when
|
|
287
|
-
expect(resolveServerBinary(["biome"], root)).toBeNull();
|
|
288
|
-
const probesAfterFirst = __serverBinaryProbeCountForTests();
|
|
289
|
-
expect(resolveServerBinary(["biome"], root)).toBeNull();
|
|
290
|
-
|
|
291
|
-
// then
|
|
292
|
-
expect(__serverBinaryProbeCountForTests()).toBe(probesAfterFirst);
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
test("#given distinct working directories #when resolving the same command #then the cache keys stay separate", () => {
|
|
296
|
-
// given
|
|
297
|
-
const first = makeTempRoot("lsp-cache-key-a-");
|
|
298
|
-
writeFileSync(join(first, "package.json"), "{}\n");
|
|
299
|
-
const firstBinary = writeExecutable(join(first, "node_modules", ".bin", "biome"));
|
|
300
|
-
const second = makeTempRoot("lsp-cache-key-b-");
|
|
301
|
-
writeFileSync(join(second, "package.json"), "{}\n");
|
|
302
|
-
process.env["PATH"] = join(first, "definitely-empty");
|
|
303
|
-
|
|
304
|
-
// when / then
|
|
305
|
-
expect(resolveServerBinary(["biome"], first)).toBe(firstBinary);
|
|
306
|
-
expect(resolveServerBinary(["biome"], second)).toBeNull();
|
|
307
|
-
});
|
|
308
|
-
});
|
|
309
|
-
|
|
310
|
-
describe("isServerInstalled compatibility", () => {
|
|
311
|
-
test("#given a repo-local binary #when checking installation #then it reports installed", () => {
|
|
312
|
-
// given
|
|
313
|
-
const root = makeTempRoot("lsp-compat-local-");
|
|
314
|
-
writeFileSync(join(root, "package.json"), "{}\n");
|
|
315
|
-
writeExecutable(join(root, "node_modules", ".bin", "biome"));
|
|
316
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
317
|
-
|
|
318
|
-
// when / then
|
|
319
|
-
expect(isServerInstalled(["biome", "lsp-proxy"], root)).toBe(true);
|
|
320
|
-
});
|
|
321
|
-
|
|
322
|
-
test("#given a PATH binary and no working directory #when checking installation #then it reports installed", () => {
|
|
323
|
-
// given
|
|
324
|
-
const pathDir = makeTempRoot("lsp-compat-path-");
|
|
325
|
-
writeExecutable(join(pathDir, "typescript-language-server"));
|
|
326
|
-
process.env["PATH"] = pathDir;
|
|
327
|
-
|
|
328
|
-
// when / then
|
|
329
|
-
expect(isServerInstalled(["typescript-language-server", "--stdio"])).toBe(true);
|
|
330
|
-
});
|
|
331
|
-
|
|
332
|
-
test("#given no binary at all #when checking installation #then it reports not installed", () => {
|
|
333
|
-
// given
|
|
334
|
-
const root = makeTempRoot("lsp-compat-missing-");
|
|
335
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
336
|
-
|
|
337
|
-
// when / then
|
|
338
|
-
expect(isServerInstalled(["totally-absent-language-server"], root)).toBe(false);
|
|
339
|
-
expect(isServerInstalled([])).toBe(false);
|
|
340
|
-
});
|
|
341
|
-
|
|
342
|
-
test("#given the node command #when checking installation #then the existing node allowance is preserved", () => {
|
|
343
|
-
// given
|
|
344
|
-
const root = makeTempRoot("lsp-compat-node-");
|
|
345
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
346
|
-
|
|
347
|
-
// when / then
|
|
348
|
-
expect(isServerInstalled(["node", "server.js"], root)).toBe(true);
|
|
349
|
-
});
|
|
350
|
-
});
|
|
351
|
-
|
|
352
|
-
describe("resolveServerBinary node fallback", () => {
|
|
353
|
-
test("#given a node command missing from PATH #when resolving #then the name is kept so the server stays selectable", () => {
|
|
354
|
-
// given: a user-configured server such as { command: ["node", "my-ls.js"] } must keep
|
|
355
|
-
// working, matching the node allowance isServerInstalled() has always had.
|
|
356
|
-
const root = makeTempRoot("lsp-node-fallback-");
|
|
357
|
-
process.env["PATH"] = join(root, "definitely-empty");
|
|
358
|
-
|
|
359
|
-
// when
|
|
360
|
-
const resolved = resolveServerBinary(["node", "server.js"], root);
|
|
361
|
-
|
|
362
|
-
// then: unresolved rather than rewritten, so the spawn still goes through node
|
|
363
|
-
expect(resolved).toBe("node");
|
|
364
|
-
expect(isServerInstalled(["node", "server.js"], root)).toBe(true);
|
|
365
|
-
});
|
|
366
|
-
|
|
367
|
-
test("#given a node command present on PATH #when resolving #then the PATH entry still wins", () => {
|
|
368
|
-
// given
|
|
369
|
-
const pathDir = makeTempRoot("lsp-node-on-path-");
|
|
370
|
-
const pathBinary = writeExecutable(join(pathDir, "node"));
|
|
371
|
-
process.env["PATH"] = pathDir;
|
|
372
|
-
|
|
373
|
-
// when / then
|
|
374
|
-
expect(resolveServerBinary(["node", "server.js"])).toBe(pathBinary);
|
|
375
|
-
});
|
|
376
|
-
});
|