@mrclrchtr/supi-lsp 11.0.1 → 11.2.0

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 (85) hide show
  1. package/README.md +43 -6
  2. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +41 -1
  5. package/node_modules/ignore/README.md +36 -10
  6. package/node_modules/ignore/index.d.ts +3 -2
  7. package/node_modules/ignore/index.js +57 -53
  8. package/node_modules/ignore/legacy.js +50 -46
  9. package/node_modules/ignore/package.json +1 -1
  10. package/node_modules/semver/LICENSE +15 -0
  11. package/node_modules/semver/README.md +680 -0
  12. package/node_modules/semver/bin/semver.js +195 -0
  13. package/node_modules/semver/classes/comparator.js +143 -0
  14. package/node_modules/semver/classes/index.js +7 -0
  15. package/node_modules/semver/classes/range.js +577 -0
  16. package/node_modules/semver/classes/semver.js +350 -0
  17. package/node_modules/semver/functions/clean.js +8 -0
  18. package/node_modules/semver/functions/cmp.js +54 -0
  19. package/node_modules/semver/functions/coerce.js +62 -0
  20. package/node_modules/semver/functions/compare-build.js +9 -0
  21. package/node_modules/semver/functions/compare-loose.js +5 -0
  22. package/node_modules/semver/functions/compare.js +7 -0
  23. package/node_modules/semver/functions/diff.js +60 -0
  24. package/node_modules/semver/functions/eq.js +5 -0
  25. package/node_modules/semver/functions/gt.js +5 -0
  26. package/node_modules/semver/functions/gte.js +5 -0
  27. package/node_modules/semver/functions/inc.js +21 -0
  28. package/node_modules/semver/functions/lt.js +5 -0
  29. package/node_modules/semver/functions/lte.js +5 -0
  30. package/node_modules/semver/functions/major.js +5 -0
  31. package/node_modules/semver/functions/minor.js +5 -0
  32. package/node_modules/semver/functions/neq.js +5 -0
  33. package/node_modules/semver/functions/parse.js +18 -0
  34. package/node_modules/semver/functions/patch.js +5 -0
  35. package/node_modules/semver/functions/prerelease.js +8 -0
  36. package/node_modules/semver/functions/rcompare.js +5 -0
  37. package/node_modules/semver/functions/rsort.js +5 -0
  38. package/node_modules/semver/functions/satisfies.js +12 -0
  39. package/node_modules/semver/functions/sort.js +5 -0
  40. package/node_modules/semver/functions/truncate.js +48 -0
  41. package/node_modules/semver/functions/valid.js +8 -0
  42. package/node_modules/semver/index.js +93 -0
  43. package/node_modules/semver/internal/constants.js +37 -0
  44. package/node_modules/semver/internal/debug.js +11 -0
  45. package/node_modules/semver/internal/identifiers.js +29 -0
  46. package/node_modules/semver/internal/lrucache.js +42 -0
  47. package/node_modules/semver/internal/parse-options.js +17 -0
  48. package/node_modules/semver/internal/re.js +223 -0
  49. package/node_modules/semver/package.json +78 -0
  50. package/node_modules/semver/preload.js +4 -0
  51. package/node_modules/semver/range.bnf +17 -0
  52. package/node_modules/semver/ranges/gtr.js +6 -0
  53. package/node_modules/semver/ranges/intersects.js +9 -0
  54. package/node_modules/semver/ranges/ltr.js +6 -0
  55. package/node_modules/semver/ranges/max-satisfying.js +27 -0
  56. package/node_modules/semver/ranges/min-satisfying.js +26 -0
  57. package/node_modules/semver/ranges/min-version.js +63 -0
  58. package/node_modules/semver/ranges/outside.js +82 -0
  59. package/node_modules/semver/ranges/simplify.js +49 -0
  60. package/node_modules/semver/ranges/subset.js +249 -0
  61. package/node_modules/semver/ranges/to-comparators.js +10 -0
  62. package/node_modules/semver/ranges/valid.js +13 -0
  63. package/node_modules/vscode-jsonrpc/README.md +5 -0
  64. package/node_modules/vscode-jsonrpc/lib/browser/ril.js +8 -0
  65. package/node_modules/vscode-jsonrpc/lib/common/connection.js +7 -2
  66. package/node_modules/vscode-jsonrpc/lib/common/ral.d.ts +8 -0
  67. package/node_modules/vscode-jsonrpc/lib/node/main.d.ts +14 -0
  68. package/node_modules/vscode-jsonrpc/lib/node/main.js +6 -4
  69. package/node_modules/vscode-jsonrpc/lib/node/ril.js +5 -1
  70. package/node_modules/vscode-jsonrpc/package.json +4 -2
  71. package/node_modules/vscode-languageserver-protocol/package.json +4 -3
  72. package/node_modules/vscode-languageserver-types/lib/esm/main.js +2 -2
  73. package/node_modules/vscode-languageserver-types/lib/umd/main.js +2 -2
  74. package/node_modules/vscode-languageserver-types/package.json +2 -1
  75. package/package.json +8 -6
  76. package/src/client/client.ts +44 -5
  77. package/src/config/config.ts +53 -2
  78. package/src/config/operation-support.ts +19 -4
  79. package/src/config/server-config.ts +22 -0
  80. package/src/config/typescript-launch.ts +276 -0
  81. package/src/manager/manager-project-info.ts +22 -10
  82. package/src/manager/manager.ts +53 -20
  83. package/src/provider/refactor-planning.ts +4 -3
  84. package/src/session/runtime-controller.ts +2 -2
  85. package/src/session/scanner.ts +27 -15
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "vscode-jsonrpc",
3
3
  "description": "A json rpc implementation over streams",
4
- "version": "9.0.3",
4
+ "version": "10.0.0",
5
5
  "author": "Microsoft Corporation",
6
6
  "license": "MIT",
7
+ "homepage": "https://github.com/microsoft/vscode-languageserver-node/tree/main/jsonrpc",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "https://github.com/Microsoft/vscode-languageserver-node.git",
@@ -13,7 +14,7 @@
13
14
  "url": "https://github.com/Microsoft/vscode-languageserver-node/issues"
14
15
  },
15
16
  "engines": {
16
- "node": ">=14.0.0"
17
+ "node": ">=14.17.0"
17
18
  },
18
19
  "exports": {
19
20
  ".": {
@@ -31,6 +32,7 @@
31
32
  },
32
33
  "devDependencies": {
33
34
  "@types/msgpack-lite": "^0.1.12",
35
+ "@types/node": "22.13.14",
34
36
  "msgpack-lite": "^0.1.26"
35
37
  },
36
38
  "scripts": {
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "vscode-languageserver-protocol",
3
3
  "description": "VSCode Language Server Protocol implementation",
4
- "version": "3.18.4",
4
+ "version": "3.18.5",
5
5
  "author": "Microsoft Corporation",
6
6
  "license": "MIT",
7
+ "homepage": "https://github.com/microsoft/vscode-languageserver-node/tree/main/protocol",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "https://github.com/Microsoft/vscode-languageserver-node.git",
@@ -27,8 +28,8 @@
27
28
  }
28
29
  },
29
30
  "dependencies": {
30
- "vscode-jsonrpc": "9.0.3",
31
- "vscode-languageserver-types": "3.18.4"
31
+ "vscode-jsonrpc": "10.0.0",
32
+ "vscode-languageserver-types": "3.18.5"
32
33
  },
33
34
  "scripts": {
34
35
  "prepublishOnly": "npm run all:publish",
@@ -222,7 +222,7 @@ export var ColorPresentation;
222
222
  function is(value) {
223
223
  const candidate = value;
224
224
  return Is.objectLiteral(candidate) && Is.string(candidate.label)
225
- && (Is.undefined(candidate.textEdit) || TextEdit.is(candidate))
225
+ && (Is.undefined(candidate.textEdit) || TextEdit.is(candidate.textEdit))
226
226
  && (Is.undefined(candidate.additionalTextEdits) || Is.typedArray(candidate.additionalTextEdits, TextEdit.is));
227
227
  }
228
228
  ColorPresentation.is = is;
@@ -279,7 +279,7 @@ export var FoldingRange;
279
279
  */
280
280
  function is(value) {
281
281
  const candidate = value;
282
- return Is.objectLiteral(candidate) && Is.uinteger(candidate.startLine) && Is.uinteger(candidate.startLine)
282
+ return Is.objectLiteral(candidate) && Is.uinteger(candidate.startLine) && Is.uinteger(candidate.endLine)
283
283
  && (Is.undefined(candidate.startCharacter) || Is.uinteger(candidate.startCharacter))
284
284
  && (Is.undefined(candidate.endCharacter) || Is.uinteger(candidate.endCharacter))
285
285
  && (Is.undefined(candidate.kind) || Is.string(candidate.kind));
@@ -233,7 +233,7 @@
233
233
  function is(value) {
234
234
  var candidate = value;
235
235
  return Is.objectLiteral(candidate) && Is.string(candidate.label)
236
- && (Is.undefined(candidate.textEdit) || TextEdit.is(candidate))
236
+ && (Is.undefined(candidate.textEdit) || TextEdit.is(candidate.textEdit))
237
237
  && (Is.undefined(candidate.additionalTextEdits) || Is.typedArray(candidate.additionalTextEdits, TextEdit.is));
238
238
  }
239
239
  ColorPresentation.is = is;
@@ -290,7 +290,7 @@
290
290
  */
291
291
  function is(value) {
292
292
  var candidate = value;
293
- return Is.objectLiteral(candidate) && Is.uinteger(candidate.startLine) && Is.uinteger(candidate.startLine)
293
+ return Is.objectLiteral(candidate) && Is.uinteger(candidate.startLine) && Is.uinteger(candidate.endLine)
294
294
  && (Is.undefined(candidate.startCharacter) || Is.uinteger(candidate.startCharacter))
295
295
  && (Is.undefined(candidate.endCharacter) || Is.uinteger(candidate.endCharacter))
296
296
  && (Is.undefined(candidate.kind) || Is.string(candidate.kind));
@@ -1,9 +1,10 @@
1
1
  {
2
2
  "name": "vscode-languageserver-types",
3
3
  "description": "Types used by the Language server for node",
4
- "version": "3.18.4",
4
+ "version": "3.18.5",
5
5
  "author": "Microsoft Corporation",
6
6
  "license": "MIT",
7
+ "homepage": "https://github.com/microsoft/vscode-languageserver-node/tree/main/types",
7
8
  "repository": {
8
9
  "type": "git",
9
10
  "url": "https://github.com/Microsoft/vscode-languageserver-node.git",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-lsp",
3
- "version": "11.0.1",
3
+ "version": "11.2.0",
4
4
  "description": "Language Server Protocol runtime for SuPi code intelligence",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -33,17 +33,19 @@
33
33
  ],
34
34
  "dependencies": {
35
35
  "ignore": "^7.0.5",
36
+ "semver": "^7.8.5",
36
37
  "typescript": "6.0.3",
37
- "vscode-jsonrpc": "^9.0.0",
38
- "vscode-languageserver-protocol": "^3.18.0",
39
- "vscode-languageserver-types": "^3.18.0",
40
- "@mrclrchtr/supi-core": "11.0.1",
41
- "@mrclrchtr/supi-code-runtime": "11.0.1"
38
+ "vscode-jsonrpc": "^10.0.0",
39
+ "vscode-languageserver-protocol": "^3.18.5",
40
+ "vscode-languageserver-types": "^3.18.5",
41
+ "@mrclrchtr/supi-core": "11.2.0",
42
+ "@mrclrchtr/supi-code-runtime": "11.2.0"
42
43
  },
43
44
  "bundledDependencies": [
44
45
  "@mrclrchtr/supi-code-runtime",
45
46
  "@mrclrchtr/supi-core",
46
47
  "ignore",
48
+ "semver",
47
49
  "typescript",
48
50
  "vscode-jsonrpc",
49
51
  "vscode-languageserver-protocol",
@@ -51,6 +51,7 @@ import type {
51
51
  WorkspaceEdit,
52
52
  WorkspaceSymbol,
53
53
  } from "../config/types.ts";
54
+ import { resolveTypeScriptLaunch, type TypeScriptLaunch } from "../config/typescript-launch.ts";
54
55
  import { boundCwd } from "../debug-telemetry.ts";
55
56
  import type { DiagnosticEvidenceSummary } from "../diagnostics/evidence.ts";
56
57
  import { raceRequestControl } from "../session/readiness.ts";
@@ -82,6 +83,9 @@ import { SemanticInputSynchronizationError } from "./client-semantic-input-error
82
83
  import { JsonRpcClient, JsonRpcRequestError } from "./transport.ts";
83
84
 
84
85
  const SHUTDOWN_TIMEOUT_MS = 5_000;
86
+ // ContentModified can outlive the server's progress notifications. Yield before
87
+ // spending the single retry instead of immediately racing the same analysis.
88
+ const CONTENT_MODIFIED_RETRY_DELAY_MS = 100;
85
89
 
86
90
  /** Bounded stderr tail kept for startup-failure error messages. */
87
91
  const STDERR_TAIL_MAX_CHARS = 4_000;
@@ -588,6 +592,13 @@ export class LspClient {
588
592
  });
589
593
  }
590
594
 
595
+ private launchSelection: TypeScriptLaunch | undefined;
596
+
597
+ /** Compiler used by this generation, never re-probed while it is healthy. */
598
+ get typescriptSelection() {
599
+ return this.launchSelection?.typescript;
600
+ }
601
+
591
602
  get status(): ClientStatus {
592
603
  return this._status;
593
604
  }
@@ -667,8 +678,14 @@ export class LspClient {
667
678
  // ── Lifecycle ───────────────────────────────────────────────────────
668
679
  /** Spawn the server process and perform the initialize handshake. */
669
680
  async start(): Promise<void> {
670
- const cmd = this.config.command;
671
- const args = this.config.args ?? [];
681
+ try {
682
+ this.launchSelection = resolveTypeScriptLaunch(this.name, this.config, this.root);
683
+ } catch (error) {
684
+ this.handleProcessFailure(error instanceof Error ? error : new Error(String(error)));
685
+ throw error;
686
+ }
687
+ const { command: cmd = this.config.command, args = this.config.args ?? [] } =
688
+ this.launchSelection;
672
689
  this.startedAt = Date.now();
673
690
 
674
691
  try {
@@ -738,7 +755,7 @@ export class LspClient {
738
755
  },
739
756
  ],
740
757
  capabilities: CLIENT_CAPABILITIES,
741
- initializationOptions: this.config.initializationOptions,
758
+ initializationOptions: this.launchSelection.initializationOptions,
742
759
  })) as InitializeResult;
743
760
 
744
761
  const positionEncoding = result.capabilities.positionEncoding ?? "utf-16";
@@ -1270,8 +1287,10 @@ export class LspClient {
1270
1287
  context: CodeActionContext,
1271
1288
  control?: CodeRequestControl,
1272
1289
  ): Promise<CodeQueryResult<CodeAction[] | null>> {
1290
+ const backend = this.typescriptSelection?.backend;
1291
+ const server = `LSP server "${this.name}"${backend ? ` (${backend} backend)` : ""}`;
1273
1292
  if (!this.supportsCodeActions) {
1274
- return unavailableCodeQuery("The server does not advertise textDocument/codeAction.");
1293
+ return unavailableCodeQuery(`${server} does not advertise textDocument/codeAction.`);
1275
1294
  }
1276
1295
  if (typeof filePath !== "string" || filePath.length === 0 || !isRange(range)) {
1277
1296
  return unavailableCodeQuery("Code actions require a valid file and range.");
@@ -1280,7 +1299,9 @@ export class LspClient {
1280
1299
  return unavailableCodeQuery("Code actions require a valid context.");
1281
1300
  }
1282
1301
  if (context.only && !this.supportsCodeActionKinds(context.only)) {
1283
- return unavailableCodeQuery("The server does not advertise the requested code-action kind.");
1302
+ return unavailableCodeQuery(
1303
+ `${server} does not advertise the requested code-action kind: ${context.only.join(", ")}.`,
1304
+ );
1284
1305
  }
1285
1306
 
1286
1307
  const requestContext: CodeActionContext = {
@@ -1498,6 +1519,9 @@ export class LspClient {
1498
1519
  for (;;) {
1499
1520
  const attempt: SemanticRequestAttemptState = { inputRevision: undefined };
1500
1521
  try {
1522
+ if (retryGuard.retryReason === "content-modified") {
1523
+ await this.waitForContentModifiedRetry(control);
1524
+ }
1501
1525
  const data = await this.runSemanticRequestAttempt<T>({
1502
1526
  method,
1503
1527
  params,
@@ -1515,6 +1539,21 @@ export class LspClient {
1515
1539
  }
1516
1540
  }
1517
1541
 
1542
+ /** Pause only the protocol retry, without extending the caller's deadline. */
1543
+ private async waitForContentModifiedRetry(control?: CodeRequestControl): Promise<void> {
1544
+ let timer: ReturnType<typeof setTimeout> | undefined;
1545
+ try {
1546
+ await raceRequestControl(
1547
+ new Promise<void>((resolve) => {
1548
+ timer = setTimeout(resolve, CONTENT_MODIFIED_RETRY_DELAY_MS);
1549
+ }),
1550
+ control,
1551
+ );
1552
+ } finally {
1553
+ if (timer) clearTimeout(timer);
1554
+ }
1555
+ }
1556
+
1518
1557
  /** Return only when another attempt is permitted; retain the cause when retry stops. */
1519
1558
  private prepareSemanticRetry(options: {
1520
1559
  method: string;
@@ -3,10 +3,16 @@
3
3
  import * as fs from "node:fs";
4
4
  import * as path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
- import { loadSupiConfigForScope, loadSupiConfigSectionForScope } from "@mrclrchtr/supi-core/config";
6
+ import {
7
+ getSupiConfigPath,
8
+ loadSupiConfigForScope,
9
+ loadSupiConfigSectionForScope,
10
+ } from "@mrclrchtr/supi-core/config";
7
11
  import { truncateDebugIdentity } from "@mrclrchtr/supi-core/debug";
8
12
  import type { JsonObject, JsonValue, LspConfig, ServerConfig } from "./types.ts";
9
13
 
14
+ import { isManualTypeScriptLaunch, isTypeScriptPackageName } from "./typescript-launch.ts";
15
+
10
16
  const CONFIG_DIR = path.dirname(fileURLToPath(import.meta.url));
11
17
 
12
18
  // Load defaults at module level — resolve relative to this file.
@@ -65,11 +71,47 @@ export function loadConfig(cwd: string, options?: LoadConfigOptions): LspConfig
65
71
  { scope: "project" },
66
72
  );
67
73
 
74
+ const fields: NonNullable<ServerConfig["typescriptExplicitFields"]> = {};
75
+ for (const [scope, servers] of [
76
+ ["global", globalLsp.servers],
77
+ ["project", projectLsp.servers],
78
+ ] as const) {
79
+ captureTypeScriptOrigins(fields, servers?.typescript, getSupiConfigPath(scope, cwd, options));
80
+ }
68
81
  const merged = mergeServerConfigs(defaults.servers, globalLsp.servers, projectLsp.servers);
69
-
82
+ if (merged.typescript) {
83
+ merged.typescript = { ...merged.typescript, typescriptExplicitFields: fields };
84
+ isManualTypeScriptLaunch(merged.typescript, cwd);
85
+ }
70
86
  return { servers: merged };
71
87
  }
72
88
 
89
+ /** Retain only effective explicit launch fields; initializationOptions replaces its inherited object. */
90
+ function captureTypeScriptOrigins(
91
+ fields: NonNullable<ServerConfig["typescriptExplicitFields"]>,
92
+ value: unknown,
93
+ origin: string,
94
+ ): void {
95
+ if (!isRecord(value)) return;
96
+ if (
97
+ Object.hasOwn(value, "typescriptPackage") &&
98
+ !isTypeScriptPackageName(value.typescriptPackage)
99
+ ) {
100
+ throw new Error(
101
+ `Invalid lsp.servers.typescript.typescriptPackage in ${origin}: use an installed package name or alias, not a path.`,
102
+ );
103
+ }
104
+ const { override } = validateServerOverride(value);
105
+ for (const field of ["command", "args", "typescriptPackage"] as const) {
106
+ if (Object.hasOwn(override, field)) fields[field] = origin;
107
+ }
108
+ if (!Object.hasOwn(override, "initializationOptions")) return;
109
+ delete fields.tsserverPath;
110
+ const init = override.initializationOptions;
111
+ if (isRecord(init) && isRecord(init.tsserver) && Object.hasOwn(init.tsserver, "path"))
112
+ fields.tsserverPath = origin;
113
+ }
114
+
73
115
  /**
74
116
  * Read effective explicit language disablement for the active LSP snapshot.
75
117
  * Project values override global values, including an explicit re-enable.
@@ -205,6 +247,10 @@ function mergeSingleServer(options: {
205
247
  }): ServerConfig | null {
206
248
  const { name, base, value, builtIn, reportWarning } = options;
207
249
  const { override, invalidFields } = validateServerOverride(value);
250
+ if (name !== "typescript" && override.typescriptPackage !== undefined) {
251
+ delete override.typescriptPackage;
252
+ invalidFields.push("typescriptPackage");
253
+ }
208
254
  const missingFields = base
209
255
  ? []
210
256
  : [
@@ -232,6 +278,11 @@ function mergeSingleServer(options: {
232
278
  type ServerOverrideSetter = (override: Partial<ServerConfig>, candidate: unknown) => boolean;
233
279
 
234
280
  const SERVER_OVERRIDE_SETTERS: Record<string, ServerOverrideSetter> = {
281
+ typescriptPackage(override, candidate) {
282
+ if (!isTypeScriptPackageName(candidate)) return false;
283
+ override.typescriptPackage = candidate;
284
+ return true;
285
+ },
235
286
  command(override, candidate) {
236
287
  if (typeof candidate !== "string" || candidate.trim().length === 0) return false;
237
288
  override.command = candidate;
@@ -26,12 +26,15 @@ export type ServerOperationSupportMap = Partial<
26
26
  Record<ServerOperationName, ServerOperationSupport>
27
27
  >;
28
28
 
29
+ const REMOVE_UNUSED_KIND = "source.removeUnused";
30
+ const REMOVE_UNUSED_IMPORTS_KIND = "source.removeUnusedImports";
31
+
29
32
  /** Code-action kinds requested by each operation-specific planner. */
30
33
  export const REFACTOR_CODE_ACTION_KINDS = {
31
34
  extract_function: ["refactor.extract.function"],
32
35
  extract_variable: ["refactor.extract.constant", "refactor.extract.variable"],
33
36
  update_imports: ["source.organizeImports"],
34
- delete_dead_code: ["source.removeUnused"],
37
+ delete_dead_code: [REMOVE_UNUSED_KIND, REMOVE_UNUSED_IMPORTS_KIND],
35
38
  } as const satisfies Record<
36
39
  "extract_function" | "extract_variable" | "update_imports" | "delete_dead_code",
37
40
  readonly string[]
@@ -143,6 +146,20 @@ function supportedOperation(
143
146
  return { server, supi, ...(reason ? { reason } : {}) };
144
147
  }
145
148
 
149
+ function deleteDeadCodeSupport(capabilities: ServerCapabilities | null): ServerOperationSupport {
150
+ const server = codeActionAdvertisement(capabilities, REFACTOR_CODE_ACTION_KINDS.delete_dead_code);
151
+ const importsOnly =
152
+ codeActionAdvertisement(capabilities, [REMOVE_UNUSED_KIND]) !== "advertised" &&
153
+ codeActionAdvertisement(capabilities, [REMOVE_UNUSED_IMPORTS_KIND]) === "advertised";
154
+ return importsOnly
155
+ ? supportedOperation(
156
+ server,
157
+ "limited",
158
+ "Only unused-import removal is supported by this mapping, not arbitrary unused declarations.",
159
+ )
160
+ : supportedOperation(server);
161
+ }
162
+
146
163
  /** Build the honest operation-support projection for one negotiated server. */
147
164
  export function getServerOperationSupport(
148
165
  capabilities: ServerCapabilities | null,
@@ -181,9 +198,7 @@ export function getServerOperationSupport(
181
198
  update_imports: supportedOperation(
182
199
  codeActionAdvertisement(capabilities, REFACTOR_CODE_ACTION_KINDS.update_imports),
183
200
  ),
184
- delete_dead_code: supportedOperation(
185
- codeActionAdvertisement(capabilities, REFACTOR_CODE_ACTION_KINDS.delete_dead_code),
186
- ),
201
+ delete_dead_code: deleteDeadCodeSupport(capabilities),
187
202
  code_action_resolve: supportedOperation(resolveServer),
188
203
  };
189
204
  }
@@ -26,6 +26,20 @@ export interface ServerConfig {
26
26
  initializationOptions?: unknown;
27
27
  /** Maximum time to wait for a single $/progress cycle, in ms. Default: 10_000. */
28
28
  readinessTimeoutMs?: number;
29
+ /** TypeScript compiler package name or installed alias, resolved from the route root. */
30
+ typescriptPackage?: string;
31
+ /** @internal Effective explicit-field origins for TypeScript launch policy, not user configuration. */
32
+ typescriptExplicitFields?: Partial<
33
+ Record<"command" | "args" | "tsserverPath" | "typescriptPackage", string>
34
+ >;
35
+ }
36
+
37
+ /** Compiler selection facts; absent versions are unknown, not inferred from the wrapper. */
38
+ export interface TypeScriptSelection {
39
+ backend: "tsserver" | "native";
40
+ packageName: string;
41
+ compilerVersion?: string;
42
+ wrapperVersion?: string;
29
43
  }
30
44
 
31
45
  /** LSP configuration keyed by language name (e.g. `typescript`, `python`). */
@@ -37,6 +51,10 @@ export interface DetectedProjectServer {
37
51
  name: string;
38
52
  root: string;
39
53
  fileTypes: string[];
54
+ /** Resolved automatic compiler selection, when available. */
55
+ typescript?: TypeScriptSelection;
56
+ /** Actionable launch-selection failure, including its root and package. */
57
+ failureMessage?: string;
40
58
  }
41
59
 
42
60
  /** Structured reason for a route-level process-crash recovery state. */
@@ -64,4 +82,8 @@ export interface MissingServer {
64
82
  command: string;
65
83
  /** File extensions found in the project (subset of server.fileTypes). */
66
84
  foundExtensions: string[];
85
+ /** Route root when a compiler-selection failure prevents launch. */
86
+ root?: string;
87
+ /** Compiler selection failure, distinct from a missing executable. */
88
+ failureMessage?: string;
67
89
  }