@mrclrchtr/supi-lsp 11.3.0 → 12.0.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 (31) hide show
  1. package/README.md +22 -17
  2. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +3 -1
  4. package/node_modules/@mrclrchtr/supi-code-runtime/src/declaration-level.ts +34 -0
  5. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +64 -28
  6. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  7. package/node_modules/semver/classes/range.js +2 -2
  8. package/node_modules/semver/internal/constants.js +30 -0
  9. package/node_modules/semver/internal/trim-comparator-whitespace.js +438 -0
  10. package/node_modules/semver/package.json +1 -1
  11. package/package.json +3 -3
  12. package/src/api.ts +2 -2
  13. package/src/client/client-diagnostic-capabilities.ts +13 -3
  14. package/src/client/client-diagnostics.ts +25 -9
  15. package/src/client/client-document-sync.ts +7 -5
  16. package/src/client/client-edit-capture.ts +258 -0
  17. package/src/client/client.ts +64 -11
  18. package/src/config/capabilities.ts +8 -0
  19. package/src/config/operation-support.ts +48 -79
  20. package/src/config/tsconfig-scope.ts +10 -0
  21. package/src/manager/manager-typescript-coverage.ts +118 -0
  22. package/src/manager/manager.ts +67 -3
  23. package/src/manager/typescript-file-local.ts +87 -0
  24. package/src/provider/lsp-refactor-provider.ts +21 -35
  25. package/src/provider/refactor-command-capture.ts +139 -0
  26. package/src/provider/refactor-planning.ts +202 -47
  27. package/src/provider/semantic-symbol-mapper.ts +22 -17
  28. package/src/session/route-boundary.ts +26 -0
  29. package/src/session/runtime-registry.ts +106 -16
  30. package/src/session/runtime-rename.ts +3 -0
  31. package/src/session/workspace-lsp-runtime.ts +22 -1
package/README.md CHANGED
@@ -59,8 +59,8 @@ A configured route is not a ready server. Features depend on the active server,
59
59
  | Hover, definitions, references, implementations, document/workspace symbols | Supported through the server's corresponding LSP methods; availability and coverage differ by server. |
60
60
  | Diagnostics | Native pull, the eligible TypeScript request adapter, or ambient push. Push observation alone cannot confirm that a file is clean. See the diagnostic policy below. |
61
61
  | `rename_symbol` | Requires server rename support and validated text edits; uses `prepareRename` when advertised. |
62
- | `update_imports` / `delete_dead_code` | Requires matching `source.organizeImports` / (`source.removeUnused` or `source.removeUnusedImports`) code actions that produce validated text edits. Imports-only advertisement reports limited support, not arbitrary unused-declaration removal. |
63
- | `extract_function` / `extract_variable` | Unavailable for all languages: standard LSP code actions do not provide a safe exact extraction-name contract. |
62
+ | `code_action` | Requires a code action of the requested `refactor*` kind for the range that produces validated text edits, directly or by capturing the one edit its command pushes. Names generated by the server appear unchanged in the plan. |
63
+ | `source_action` | Requires a code action of the requested `source*` kind for the whole document that produces validated text edits, directly or (for `source.organizeImports` and `source.fixAll`) by capturing the one edit its command pushes. |
64
64
 
65
65
  The [operation-support model](src/config/operation-support.ts) separates server advertisement from SuPi planning limits. A server can advertise an action that SuPi cannot safely turn into a plan.
66
66
 
@@ -100,13 +100,13 @@ For example, select a compiler installed as `my-ts6` without changing the TLS co
100
100
  - `code_health` and `/supi-ci-status` show the `native` or `tsserver` backend and resolved compiler version when known, separately from wrapper version. Manual/legacy-fallback launches have no inferred compiler identity. Missing versions remain unknown. Selection failures identify the package/root and a corrective action.
101
101
  - Topmost-root collapsing remains unchanged. Mixed nested backends, Windows live qualification, dependency watchers, performance benchmarking, and full TLS/native feature parity are outside this change. Existing legacy TLS support is not deliberately removed.
102
102
 
103
- Focused qualification pins native TypeScript **7.0.2**, TLS **6.0.2**, ordinary TypeScript **6.0.3**, and `@typescript/typescript6` **6.0.2** backed by `@typescript/old` → TypeScript **6.0.3**. The wrapper's dependency range does not guarantee that compiler version; the lockfile and [prerequisite pins](../../scripts/language-baseline/pins.json) fix these combinations. SuPi retains TS6 for its compiler-API dependency and legacy-server tests. Workspace typechecking and declaration builds use the native TS7 launcher directly to avoid ambiguity between the installed `tsc` binaries.
103
+ Focused qualification uses the native TypeScript 7 alias (`@typescript/native`), TLS, ordinary TypeScript 6, and `@typescript/typescript6` backed by `@typescript/old` → TypeScript 6. Exact versions come from the root `package.json` and lockfile; the wrapper's dependency range does not fix its compiler version, so the lockfile does. SuPi retains TS6 for its compiler-API dependency and legacy-server tests. Workspace typechecking and declaration builds use the native TS7 launcher directly to avoid ambiguity between the installed `tsc` binaries.
104
104
 
105
105
  The required [Linux/macOS CI matrix](../../.github/workflows/ci.yml) keeps [selection/layout tests](__tests__/unit/typescript-launch.test.ts), [real native lifecycle tests](__tests__/integration/workspace-runtime.integration.typescript-native.test.ts), and [wrapper lifecycle tests](__tests__/integration/workspace-runtime.integration.typescript-launch.test.ts) separate from the [public TS/JS semantic matrix](../supi-code-intelligence/__tests__/acceptance/language-baseline-harness.ts). Required missing/unavailable behavior fails rather than skips. The matrix covers hover, definitions, references, meaningful implementations, document/workspace symbols, confirmed error → clean diagnostics, refresh, cross-file invalidation, Unicode-safe rename plan/apply and stale-plan rejection, and precise organize-imports. The TypeScript public matrix also checks unused-import removal, unchanged source during planning, stale-plan rejection, and preservation of unrelated text; native support is imports-only. Native TSX/JSX and module suffixes receive routing/synchronization checks rather than duplicate matrices. Existing stale-result and edit-safety suites remain in place.
106
106
 
107
107
  [Bounded config comparisons](__tests__/integration/typescript-config-coverage.test.ts) compare TS6 and native TS7 `--showConfig` root-file coverage for TS/JS defaults, include/exclude, `allowJs`/`checkJs`, inherited patterns (including late-created files), and separate referenced projects. These cases found no false exclusion; filtering is unchanged. **Config coverage state is not TypeScript program membership**: imports may bring excluded files into a program, and references do not merge root-file lists.
108
108
 
109
- Native `delete_dead_code` consumes `source.removeUnusedImports` through the same safe plan/apply workflow ([#554](https://github.com/mrclrchtr/supi/issues/554)). This mapping removes unused imports only: operation support reports `server: advertised`, `supi: limited`, with an imports-only reason. It does not qualify arbitrary unused-declaration removal. Extraction remains unavailable ([#467](https://github.com/mrclrchtr/supi/issues/467)); no TLS/native feature-parity claim is made. Only the pinned combinations are tested, not every stable TS7 release. Focused qualification passed on macOS arm64 (Node 24.22.1) and Linux arm64 (Debian container, Node 24.22.0), including the native/TS6 public TS/JS matrix. The unused-import follow-up has local macOS arm64 verification only; its Linux verification remains pending. The pinned Linux/macOS CI jobs remain pending until they run; this is not a qualification claim for the complete multi-language baseline.
109
+ Native TypeScript 7 offers `source.removeUnusedImports` through the same safe plan/apply workflow ([#554](https://github.com/mrclrchtr/supi/issues/554)); it removes unused imports only, not arbitrary unused declarations. Native TypeScript 7 advertises no `refactor*` kinds, so `code_action` extraction is unavailable there; no TLS/native feature-parity claim is made. Only the pinned combinations are tested, not every stable TS7 release. Focused qualification passed on macOS arm64 (Node 24.22.1) and Linux arm64 (Debian container, Node 24.22.0), including the native/TS6 public TS/JS matrix. The unused-import follow-up has local macOS arm64 verification only; its Linux verification remains pending. The pinned Linux/macOS CI jobs remain pending until they run; this is not a qualification claim for the complete multi-language baseline.
110
110
 
111
111
  ### Java startup and project JDKs
112
112
 
@@ -320,21 +320,26 @@ Disable one language with `lsp.servers.<language>.enabled: false`. Use `/supi-ci
320
320
  - explicit ready, pending, inactive, disabled, and unavailable registry states
321
321
  - a `SemanticProvider` adapter for `supi-code-runtime`
322
322
  - precise rename and code-action edit conversion
323
- - operation-specific code-action kind filters and standard lazy edit resolution
323
+ - code-action kind-prefix filters, exact-title selection, and standard lazy edit resolution
324
324
  - negotiated operation support in project-server status
325
325
 
326
- Operation-specific planners request `source.organizeImports` for import updates and
327
- both `source.removeUnused` and `source.removeUnusedImports` for dead-code removal.
328
- These distinct kinds are alternatives, not parent/child kinds; either exact kind
329
- or its dotted descendants can match the returned `CodeAction.kind`, never its title.
330
- An imports-only advertisement reports a SuPi limitation rather than general
331
- unused-declaration support. Edit-only actions are validated before planning. Command actions,
332
- resource operations, snippets, annotations, malformed actions, and stale responses
333
- stay unavailable. Distinct matching plans return an ambiguous result instead of
334
- selecting the first action. Rename uses `prepareRename` when the server advertises
335
- it and otherwise uses direct standard rename. Standard LSP does not provide
336
- a safe exact extraction-name contract, so `extract_function` and `extract_variable`
337
- plans report a SuPi planning limitation instead of selecting a generated name.
326
+ Code- and source-action planners request exactly the caller's kind with `only` and
327
+ match the returned `CodeAction.kind` or its dotted descendants, never the title.
328
+ Entries outside `only` are dropped before validation. Disabled actions never match;
329
+ an optional exact title narrows the matches. Edit-only actions are validated before
330
+ planning. One eligible command-only action (`refactor*`, `source.organizeImports*`,
331
+ `source.fixAll*`; a command the server lists when it lists any) runs once: SuPi
332
+ advertises `workspace.applyEdit`, refuses every pushed edit with `applied: false`,
333
+ and plans the single edit captured while the command runs or shortly after it
334
+ answers, even when the server then reports the command as failed because the
335
+ edit was refused (clangd does). A timed-out or cancelled command keeps refusing late edits until it
336
+ settles. Touched open documents are re-sent as full text under a new version.
337
+ Running a command may still change server-side state. Resource operations,
338
+ snippets, annotations, malformed actions, and stale responses stay unavailable. Distinct matching plans return an
339
+ ambiguous result with each candidate's kind and title instead of selecting the first
340
+ action. Rename uses `prepareRename` when the server advertises it and otherwise uses
341
+ direct standard rename. Code actions choose generated names; plans preview them as
342
+ the server returns them.
338
343
 
339
344
  Rename responses that need additional document state can trigger one bounded discovery/enrollment retry. SuPi validates edit shape and canonical mutation paths, enrolls at most 64 affected files on the same route, and requests fresh edits. The first response is not a plan. Numeric versions must still match. A null version for an open document requires verified synchronized-content evidence; the plan retains the request fingerprint and client version. Missing snapshots, changed inputs, a larger follow-up edit set, and unsafe edits remain unavailable. Planning and apply revalidate these preconditions; no version is discarded to make an edit fit.
340
345
 
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-code-runtime",
3
- "version": "11.3.0",
3
+ "version": "12.0.0",
4
4
  "description": "Shared workspace context and capability contracts for code intelligence",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -14,6 +14,7 @@ export type {
14
14
  StructuralProvider,
15
15
  StructuralResult,
16
16
  } from "./capability/types.ts";
17
+ export { childDeclarationLevel } from "./declaration-level.ts";
17
18
  export type {
18
19
  CFamilyExtension,
19
20
  CFamilyFileAssociationResolver,
@@ -50,7 +51,7 @@ export type {
50
51
  CodeResult,
51
52
  CodeSymbol,
52
53
  ConfidenceMode,
53
- DeclarationNesting,
54
+ DeclarationLevel,
54
55
  DisambiguationCandidate,
55
56
  DocumentCodeSymbol,
56
57
  DocumentEditPrecondition,
@@ -59,6 +60,7 @@ export type {
59
60
  ImportData,
60
61
  NodeAtData,
61
62
  OutlineData,
63
+ RefactorActionIdentity,
62
64
  RefactorOperation,
63
65
  RefactorRequest,
64
66
  RefactorResult,
@@ -0,0 +1,34 @@
1
+ import type { DeclarationLevel } from "./types.ts";
2
+
3
+ /** Declarations whose body holds statements, so nested declarations are local to it. */
4
+ const FUNCTION_LIKE_KINDS = new Set(["function", "method", "constructor"]);
5
+ /** Bindings whose value may be a function (locals) or an object literal (members). */
6
+ const VALUE_BINDING_KINDS = new Set(["variable", "constant", "property", "field"]);
7
+ /** Kinds that name an entry of an object, type, or enum rather than a statement-level binding. */
8
+ const MEMBER_ENTRY_KINDS = new Set(["property", "field", "method", "enummember", "key", "event"]);
9
+
10
+ /**
11
+ * Level of a declaration reported directly inside `parent`.
12
+ *
13
+ * Inside a local declaration everything stays local. A function, method, or
14
+ * constructor makes its nested declarations local. A variable, constant,
15
+ * property, or field holds either a function body (nested bindings are local)
16
+ * or an object literal (nested properties and methods are members). Every
17
+ * other container — class, interface, enum, struct, namespace, module, or a
18
+ * document section — makes its nested declarations members.
19
+ *
20
+ * Kinds compare case-insensitively, so LSP SymbolKind names and Tree-sitter
21
+ * outline kinds share one rule.
22
+ */
23
+ export function childDeclarationLevel(
24
+ parent: { readonly kind: string | null; readonly level: DeclarationLevel },
25
+ childKind: string | null,
26
+ ): Exclude<DeclarationLevel, "top-level" | "unknown"> {
27
+ if (parent.level === "local") return "local";
28
+ const parentKind = parent.kind?.toLowerCase() ?? "";
29
+ if (FUNCTION_LIKE_KINDS.has(parentKind)) return "local";
30
+ if (VALUE_BINDING_KINDS.has(parentKind)) {
31
+ return MEMBER_ENTRY_KINDS.has(childKind?.toLowerCase() ?? "") ? "member" : "local";
32
+ }
33
+ return "member";
34
+ }
@@ -34,8 +34,16 @@ export interface SymbolAnchor {
34
34
  character: number;
35
35
  }
36
36
 
37
- /** Provider-backed nesting evidence for a declaration reported within one document. */
38
- export type DeclarationNesting = "top-level" | "nested" | "unknown";
37
+ /**
38
+ * Where a declaration sits within its file, as reported by a provider.
39
+ *
40
+ * - `top-level`: directly in the file, not inside another declaration.
41
+ * - `member`: directly inside a class, interface, enum, struct, namespace,
42
+ * module, or object literal (methods, fields, properties, enum members).
43
+ * - `local`: inside the body of a function, method, or constructor.
44
+ * - `unknown`: the provider reported a flat symbol list without hierarchy.
45
+ */
46
+ export type DeclarationLevel = "top-level" | "member" | "local" | "unknown";
39
47
 
40
48
  /**
41
49
  * A discovered symbol / declaration.
@@ -54,7 +62,7 @@ export interface CodeSymbol {
54
62
  file: string;
55
63
  declarationAnchor: SymbolAnchor;
56
64
  nameAnchor?: SymbolAnchor;
57
- /** Named symbolic container when reported; absence does not prove top-level nesting. */
65
+ /** Named enclosing declaration when reported; absence does not prove a top-level declaration. */
58
66
  container?: string | null;
59
67
  }
60
68
 
@@ -63,10 +71,10 @@ export interface CodeSymbol {
63
71
  *
64
72
  * `unknown` means the provider returned a flat observation without an actual
65
73
  * hierarchy. A reported container name remains metadata and does not promote
66
- * a flat observation to known nesting.
74
+ * a flat observation to a known level.
67
75
  */
68
76
  export interface DocumentCodeSymbol extends CodeSymbol {
69
- nesting: DeclarationNesting;
77
+ level: DeclarationLevel;
70
78
  }
71
79
 
72
80
  // ── Result types ───────────────────────────────────────────────────────
@@ -185,31 +193,53 @@ export interface WorkspaceEdit {
185
193
  }
186
194
 
187
195
  /**
188
- * Supported refactor operation names for the current semantic planning path.
189
- */
190
- export type RefactorOperation =
191
- | "rename_symbol"
192
- | "extract_function"
193
- | "extract_variable"
194
- | "rename_file"
195
- | "move_file"
196
- | "update_imports"
197
- | "delete_dead_code";
198
-
199
- /**
200
- * Operation-aware refactor planning request.
196
+ * Operation-aware refactor planning request. Each operation carries only the
197
+ * scope it needs:
198
+ *
199
+ * - `rename_symbol`: one symbol position and the new name.
200
+ * - `code_action`: one file range and a CodeActionKind prefix under `refactor`.
201
+ * - `source_action`: one whole file and a CodeActionKind prefix under `source`.
201
202
  *
202
- * Consumers provide a target file/position plus operation-specific options.
203
- * File/resource operations remain requestable so providers can reject them
204
- * explicitly and honestly until shared workspace-edit resource ops exist.
203
+ * `kind` matches the requested kind and its dotted sub-kinds. `title`, when
204
+ * present, selects one action by its exact title.
205
205
  */
206
- export interface RefactorRequest {
207
- operation: RefactorOperation;
208
- file: string;
209
- position: CodePosition;
210
- range?: SourceRange;
211
- newName?: string;
212
- destination?: string;
206
+ export type RefactorRequest =
207
+ | {
208
+ operation: "rename_symbol";
209
+ file: string;
210
+ position: CodePosition;
211
+ newName: string;
212
+ }
213
+ | {
214
+ operation: "code_action";
215
+ file: string;
216
+ range: SourceRange;
217
+ kind: string;
218
+ title?: string;
219
+ }
220
+ | {
221
+ operation: "source_action";
222
+ file: string;
223
+ kind: string;
224
+ title?: string;
225
+ };
226
+
227
+ /** Supported refactor operation names. */
228
+ export type RefactorOperation = RefactorRequest["operation"];
229
+
230
+ /** The code action selected for a precise code- or source-action plan. */
231
+ export interface RefactorActionIdentity {
232
+ /** Server-reported CodeActionKind. */
233
+ kind: string;
234
+ /** Server-reported action title. */
235
+ title: string;
236
+ /**
237
+ * How the edits were obtained: `edit` from the action itself (after
238
+ * `codeAction/resolve` when needed), or `captured-command` from the single
239
+ * `workspace/applyEdit` the server pushed while running the action's command.
240
+ * Captured edits were refused, never applied.
241
+ */
242
+ provenance?: "edit" | "captured-command";
213
243
  }
214
244
 
215
245
  /**
@@ -220,6 +250,10 @@ export interface DisambiguationCandidate {
220
250
  file?: string;
221
251
  line?: number;
222
252
  character?: number;
253
+ /** Server-reported CodeActionKind, for code-action candidates. */
254
+ kind?: string;
255
+ /** Exact action title to pass back as `title`, for code-action candidates. */
256
+ title?: string;
223
257
  }
224
258
 
225
259
  /**
@@ -233,6 +267,8 @@ export type RefactorResult =
233
267
  | {
234
268
  kind: "precise";
235
269
  edits: WorkspaceEdit;
270
+ /** The selected code action, for code- and source-action plans. */
271
+ action?: RefactorActionIdentity;
236
272
  /** Provider roots authorized by the semantic route. Consumers canonicalize before storage. */
237
273
  authorizedMutationRoots: string[];
238
274
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "11.3.0",
3
+ "version": "12.0.0",
4
4
  "description": "Shared settings, configuration, reporting, and session infrastructure",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -119,7 +119,7 @@ class Range {
119
119
  debug('hyphen replace', range)
120
120
 
121
121
  // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
122
- range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace)
122
+ range = trimComparatorWhitespace(range)
123
123
  debug('comparator trim', range)
124
124
 
125
125
  // `~ 1.2.3` => `~1.2.3`
@@ -224,11 +224,11 @@ const parseOptions = require('../internal/parse-options')
224
224
  const Comparator = require('./comparator')
225
225
  const debug = require('../internal/debug')
226
226
  const SemVer = require('./semver')
227
+ const trimComparatorWhitespace = require('../internal/trim-comparator-whitespace')
227
228
  const {
228
229
  safeRe: re,
229
230
  src,
230
231
  t,
231
- comparatorTrimReplace,
232
232
  tildeTrimReplace,
233
233
  caretTrimReplace,
234
234
  } = require('../internal/re')
@@ -15,6 +15,35 @@ const MAX_SAFE_COMPONENT_LENGTH = 16
15
15
  // the shortest version with a build 0.0.0+BUILD.
16
16
  const MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6
17
17
 
18
+ // ECMAScript whitespace and line terminators matched by \s.
19
+ const WHITESPACE_CHARACTERS = [
20
+ '\t',
21
+ '\n',
22
+ '\v',
23
+ '\f',
24
+ '\r',
25
+ ' ',
26
+ '\u00a0',
27
+ '\u1680',
28
+ '\u2000',
29
+ '\u2001',
30
+ '\u2002',
31
+ '\u2003',
32
+ '\u2004',
33
+ '\u2005',
34
+ '\u2006',
35
+ '\u2007',
36
+ '\u2008',
37
+ '\u2009',
38
+ '\u200a',
39
+ '\u2028',
40
+ '\u2029',
41
+ '\u202f',
42
+ '\u205f',
43
+ '\u3000',
44
+ '\ufeff',
45
+ ]
46
+
18
47
  const RELEASE_TYPES = [
19
48
  'major',
20
49
  'premajor',
@@ -30,6 +59,7 @@ module.exports = {
30
59
  MAX_SAFE_COMPONENT_LENGTH,
31
60
  MAX_SAFE_BUILD_LENGTH,
32
61
  MAX_SAFE_INTEGER,
62
+ WHITESPACE_CHARACTERS,
33
63
  RELEASE_TYPES,
34
64
  SEMVER_SPEC_VERSION,
35
65
  FLAG_INCLUDE_PRERELEASE: 0b001,