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.
Files changed (142) hide show
  1. package/dist/cli/index.js +141 -184
  2. package/dist/cli-node/index.js +141 -184
  3. package/dist/index.js +1508 -1567
  4. package/dist/skills/debugging/SKILL.md +0 -2
  5. package/dist/skills/debugging/references/methodology/00-setup.md +0 -12
  6. package/dist/skills/debugging/references/methodology/02-investigate.md +0 -21
  7. package/dist/skills/debugging/references/runtimes/go.md +1 -15
  8. package/dist/skills/debugging/references/runtimes/native-binary.md +0 -38
  9. package/dist/skills/debugging/references/runtimes/node.md +0 -25
  10. package/dist/skills/debugging/references/runtimes/python.md +0 -13
  11. package/dist/skills/debugging/references/runtimes/rust.md +0 -41
  12. package/dist/tools/delegate-task/types.d.ts +0 -3
  13. package/dist/tui.js +0 -25
  14. package/package.json +15 -18
  15. package/packages/lsp-core/src/index.ts +0 -1
  16. package/packages/lsp-core/src/lsp/client-wrapper.ts +5 -25
  17. package/packages/lsp-core/src/lsp/client.ts +0 -26
  18. package/packages/lsp-core/src/lsp/connection.ts +0 -16
  19. package/packages/lsp-core/src/lsp/constants.ts +0 -1
  20. package/packages/lsp-core/src/lsp/manager.ts +1 -34
  21. package/packages/lsp-core/src/lsp/server-definitions.ts +0 -29
  22. package/packages/lsp-core/src/lsp/server-installation.ts +27 -200
  23. package/packages/lsp-core/src/lsp/server-resolution.ts +5 -18
  24. package/packages/lsp-core/src/lsp/transport.ts +0 -9
  25. package/packages/lsp-core/src/lsp/types.ts +0 -8
  26. package/packages/lsp-core/src/tool-surface.test.ts +1 -13
  27. package/packages/lsp-core/src/tools/definitions.ts +0 -14
  28. package/packages/lsp-core/src/tools/index.ts +0 -1
  29. package/packages/lsp-core/src/tools/types.ts +0 -10
  30. package/packages/lsp-daemon/dist/cli.js +144 -432
  31. package/packages/lsp-daemon/dist/client.d.ts +0 -10
  32. package/packages/lsp-daemon/dist/client.js +107 -401
  33. package/packages/lsp-daemon/dist/daemon-client.d.ts +0 -1
  34. package/packages/lsp-daemon/dist/daemon-client.js +0 -3
  35. package/packages/lsp-daemon/dist/index.js +107 -395
  36. package/packages/lsp-tools-mcp/dist/cli.js +99 -384
  37. package/packages/lsp-tools-mcp/dist/lsp/manager.js +0 -53
  38. package/packages/lsp-tools-mcp/dist/mcp.js +99 -384
  39. package/packages/lsp-tools-mcp/dist/tools.js +99 -385
  40. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  41. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +2 -2
  42. package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
  43. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  44. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +35 -69
  45. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +2 -21
  46. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
  48. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  49. package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
  50. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  51. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
  52. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  53. package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +3 -3
  54. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +121 -404
  55. package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
  56. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  57. package/packages/omo-codex/plugin/components/rules/dist/cli.js +3 -3
  58. package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
  59. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  60. package/packages/omo-codex/plugin/components/rules/src/post-compact-budget.ts +3 -3
  61. package/packages/omo-codex/plugin/components/rules/test/post-compact-budget.test.ts +4 -4
  62. package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
  63. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  64. package/packages/omo-codex/plugin/components/telemetry/dist/cli.js +15 -30
  65. package/packages/omo-codex/plugin/components/telemetry/dist/posthog.js +15 -30
  66. package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
  67. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  68. package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
  69. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  70. package/packages/omo-codex/plugin/components/ulw-execute-continuation/hooks/hooks.json +2 -2
  71. package/packages/omo-codex/plugin/components/ulw-execute-continuation/package.json +1 -1
  72. package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
  73. package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
  74. package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
  75. package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
  76. package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
  77. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
  78. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
  79. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
  80. package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
  81. package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
  82. package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
  83. package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
  84. package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
  85. package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
  86. package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
  87. package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
  88. package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
  89. package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
  90. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-execute-continuation.json +1 -1
  91. package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
  92. package/packages/omo-codex/plugin/hooks/subagent-stop-checking-ulw-execute-continuation.json +1 -1
  93. package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
  94. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
  95. package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
  96. package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
  97. package/packages/omo-codex/plugin/model-catalog.json +3 -3
  98. package/packages/omo-codex/plugin/package-lock.json +13 -13
  99. package/packages/omo-codex/plugin/package.json +1 -1
  100. package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +3 -3
  101. package/packages/omo-codex/plugin/skills/debugging/SKILL.md +0 -2
  102. package/packages/omo-codex/plugin/skills/debugging/references/methodology/00-setup.md +0 -12
  103. package/packages/omo-codex/plugin/skills/debugging/references/methodology/02-investigate.md +0 -21
  104. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/go.md +1 -15
  105. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/native-binary.md +0 -38
  106. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/node.md +0 -25
  107. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/python.md +0 -13
  108. package/packages/omo-codex/plugin/skills/debugging/references/runtimes/rust.md +0 -41
  109. package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +1 -1
  110. package/packages/omo-codex/plugin/test/auto-update.test.mjs +2 -2
  111. package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +8 -8
  112. package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +2 -2
  113. package/packages/omo-codex/scripts/install-dist/install-local.mjs +23 -38
  114. package/packages/shared-skills/skills/debugging/SKILL.md +0 -2
  115. package/packages/shared-skills/skills/debugging/references/methodology/00-setup.md +0 -12
  116. package/packages/shared-skills/skills/debugging/references/methodology/02-investigate.md +0 -21
  117. package/packages/shared-skills/skills/debugging/references/runtimes/go.md +1 -15
  118. package/packages/shared-skills/skills/debugging/references/runtimes/native-binary.md +0 -38
  119. package/packages/shared-skills/skills/debugging/references/runtimes/node.md +0 -25
  120. package/packages/shared-skills/skills/debugging/references/runtimes/python.md +0 -13
  121. package/packages/shared-skills/skills/debugging/references/runtimes/rust.md +0 -41
  122. package/dist/skills/debugging/references/scripts/dap.mjs +0 -267
  123. package/dist/skills/debugging/references/scripts/dap.test.ts +0 -86
  124. package/dist/skills/debugging/references/scripts/fixture-adapter.mjs +0 -59
  125. package/dist/skills/debugging/references/tools/dap.md +0 -103
  126. package/dist/skills/debugging/references/tools/frida.md +0 -193
  127. package/packages/lsp-core/src/lsp/format-document.test.ts +0 -143
  128. package/packages/lsp-core/src/lsp/format-document.ts +0 -90
  129. package/packages/lsp-core/src/lsp/manager-max-clients.test.ts +0 -151
  130. package/packages/lsp-core/src/lsp/server-installation.test.ts +0 -376
  131. package/packages/lsp-core/src/lsp/server-resolution-local-binary.test.ts +0 -170
  132. package/packages/lsp-core/src/tools/format.test.ts +0 -56
  133. package/packages/lsp-core/src/tools/format.ts +0 -56
  134. package/packages/omo-codex/plugin/skills/debugging/references/scripts/dap.mjs +0 -267
  135. package/packages/omo-codex/plugin/skills/debugging/references/scripts/fixture-adapter.mjs +0 -59
  136. package/packages/omo-codex/plugin/skills/debugging/references/tools/dap.md +0 -103
  137. package/packages/omo-codex/plugin/skills/debugging/references/tools/frida.md +0 -193
  138. package/packages/shared-skills/skills/debugging/references/scripts/dap.mjs +0 -267
  139. package/packages/shared-skills/skills/debugging/references/scripts/dap.test.ts +0 -86
  140. package/packages/shared-skills/skills/debugging/references/scripts/fixture-adapter.mjs +0 -59
  141. package/packages/shared-skills/skills/debugging/references/tools/dap.md +0 -103
  142. 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
- });