@mrclrchtr/supi-lsp 11.1.0 → 11.3.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 (80) hide show
  1. package/README.md +81 -7
  2. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +3 -1
  4. package/node_modules/@mrclrchtr/supi-code-runtime/src/query-result.ts +9 -0
  5. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  6. package/node_modules/@mrclrchtr/supi-core/src/child-web-tools.ts +110 -0
  7. package/node_modules/@mrclrchtr/supi-core/src/session.ts +11 -0
  8. package/node_modules/semver/LICENSE +15 -0
  9. package/node_modules/semver/README.md +680 -0
  10. package/node_modules/semver/bin/semver.js +195 -0
  11. package/node_modules/semver/classes/comparator.js +143 -0
  12. package/node_modules/semver/classes/index.js +7 -0
  13. package/node_modules/semver/classes/range.js +577 -0
  14. package/node_modules/semver/classes/semver.js +350 -0
  15. package/node_modules/semver/functions/clean.js +8 -0
  16. package/node_modules/semver/functions/cmp.js +54 -0
  17. package/node_modules/semver/functions/coerce.js +62 -0
  18. package/node_modules/semver/functions/compare-build.js +9 -0
  19. package/node_modules/semver/functions/compare-loose.js +5 -0
  20. package/node_modules/semver/functions/compare.js +7 -0
  21. package/node_modules/semver/functions/diff.js +60 -0
  22. package/node_modules/semver/functions/eq.js +5 -0
  23. package/node_modules/semver/functions/gt.js +5 -0
  24. package/node_modules/semver/functions/gte.js +5 -0
  25. package/node_modules/semver/functions/inc.js +21 -0
  26. package/node_modules/semver/functions/lt.js +5 -0
  27. package/node_modules/semver/functions/lte.js +5 -0
  28. package/node_modules/semver/functions/major.js +5 -0
  29. package/node_modules/semver/functions/minor.js +5 -0
  30. package/node_modules/semver/functions/neq.js +5 -0
  31. package/node_modules/semver/functions/parse.js +18 -0
  32. package/node_modules/semver/functions/patch.js +5 -0
  33. package/node_modules/semver/functions/prerelease.js +8 -0
  34. package/node_modules/semver/functions/rcompare.js +5 -0
  35. package/node_modules/semver/functions/rsort.js +5 -0
  36. package/node_modules/semver/functions/satisfies.js +12 -0
  37. package/node_modules/semver/functions/sort.js +5 -0
  38. package/node_modules/semver/functions/truncate.js +48 -0
  39. package/node_modules/semver/functions/valid.js +8 -0
  40. package/node_modules/semver/index.js +93 -0
  41. package/node_modules/semver/internal/constants.js +37 -0
  42. package/node_modules/semver/internal/debug.js +11 -0
  43. package/node_modules/semver/internal/identifiers.js +29 -0
  44. package/node_modules/semver/internal/lrucache.js +42 -0
  45. package/node_modules/semver/internal/parse-options.js +17 -0
  46. package/node_modules/semver/internal/re.js +223 -0
  47. package/node_modules/semver/package.json +78 -0
  48. package/node_modules/semver/preload.js +4 -0
  49. package/node_modules/semver/range.bnf +17 -0
  50. package/node_modules/semver/ranges/gtr.js +6 -0
  51. package/node_modules/semver/ranges/intersects.js +9 -0
  52. package/node_modules/semver/ranges/ltr.js +6 -0
  53. package/node_modules/semver/ranges/max-satisfying.js +27 -0
  54. package/node_modules/semver/ranges/min-satisfying.js +26 -0
  55. package/node_modules/semver/ranges/min-version.js +63 -0
  56. package/node_modules/semver/ranges/outside.js +82 -0
  57. package/node_modules/semver/ranges/simplify.js +49 -0
  58. package/node_modules/semver/ranges/subset.js +249 -0
  59. package/node_modules/semver/ranges/to-comparators.js +10 -0
  60. package/node_modules/semver/ranges/valid.js +13 -0
  61. package/package.json +5 -3
  62. package/src/client/client-diagnostic-collection.ts +11 -4
  63. package/src/client/client-diagnostics.ts +77 -6
  64. package/src/client/client-input-admission.ts +149 -0
  65. package/src/client/client-typescript-project.ts +40 -0
  66. package/src/client/client.ts +174 -49
  67. package/src/config/config.ts +53 -2
  68. package/src/config/operation-support.ts +19 -4
  69. package/src/config/server-config.ts +22 -0
  70. package/src/config/typescript-launch.ts +276 -0
  71. package/src/diagnostics/evidence.ts +2 -2
  72. package/src/manager/manager-project-info.ts +22 -10
  73. package/src/manager/manager-workspace-symbol.ts +157 -23
  74. package/src/manager/manager.ts +306 -75
  75. package/src/provider/refactor-planning.ts +4 -3
  76. package/src/session/runtime-controller.ts +2 -2
  77. package/src/session/runtime-registry.ts +19 -2
  78. package/src/session/runtime-rename.ts +1 -0
  79. package/src/session/scanner.ts +27 -15
  80. package/src/session/workspace-lsp-runtime.ts +5 -1
package/README.md CHANGED
@@ -20,7 +20,7 @@ npm install @mrclrchtr/supi-lsp
20
20
 
21
21
  ## Language-server support
22
22
 
23
- SuPi provides 13 built-in server definitions. All are enabled unless `lsp.servers.<key>.enabled` is `false`. Install the language servers yourself; SuPi does not download or install them. Built-in commands must be on `PATH`; a configured absolute command path is also supported. Code Intelligence starts the runtime only for trusted projects.
23
+ SuPi provides 13 built-in server definitions. All are enabled unless `lsp.servers.<key>.enabled` is `false`. Install the language servers yourself; SuPi does not download or install them. Built-in commands must be on `PATH`, except project-local native TypeScript; a configured absolute command path is also supported. Code Intelligence starts the runtime only for trusted projects.
24
24
 
25
25
  ### Built-in route matrix
26
26
 
@@ -28,7 +28,7 @@ This matrix describes the default routing configuration, not a live test or a gu
28
28
 
29
29
  | Language / config key | Default command and arguments | File extensions | Root markers for automatic discovery |
30
30
  |---|---|---|---|
31
- | TypeScript / JavaScript — `typescript` | `typescript-language-server --stdio` | `.ts`, `.tsx`, `.js`, `.jsx`, `.mts`, `.cts`, `.mjs`, `.cjs` | `tsconfig.json`, `jsconfig.json`, `package.json` |
31
+ | TypeScript / JavaScript — `typescript` | Project-local native `tsc --lsp --stdio`, otherwise `typescript-language-server --stdio` | `.ts`, `.tsx`, `.js`, `.jsx`, `.mts`, `.cts`, `.mjs`, `.cjs` | `tsconfig.json`, `jsconfig.json`, `package.json` |
32
32
  | Python — `python` | `pyright-langserver --stdio` | `.py`, `.pyi` | `pyproject.toml`, `setup.py`, `setup.cfg`, `requirements.txt`, `pyrightconfig.json` |
33
33
  | Rust — `rust` | `rust-analyzer` | `.rs` | `Cargo.toml` |
34
34
  | Go — `go` | `gopls serve` | `.go`, `.mod` | `go.mod`, `go.sum` |
@@ -59,11 +59,13 @@ 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` code actions that produce validated text edits. |
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
63
  | `extract_function` / `extract_variable` | Unavailable for all languages: standard LSP code actions do not provide a safe exact extraction-name contract. |
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
 
67
+ Workspace-symbol scopes select contributing routes and bound query completeness; returned data can still contain out-of-scope rows, which consumers must filter. An automatic-policy exclusion does not make a scoped query partial when both its observed path and physical identity are proven outside every requested scope. In-scope exclusions, aliases that may overlap, unresolved paths, and contributing route failures remain partial. Unscoped queries retain all exclusion evidence. Partial metadata sets `exclusionOnly` when policy exclusions are the only gap, with no route failure or inferred-project coverage gap. Explicitly selected excluded files and directories remain inspectable; when a scoped query returns no row inside any scope, warm-up may open files inside those selected scopes so a server that only indexes opened documents can report them. Automatic path rules and target-uniqueness checks are unchanged.
68
+
67
69
  ### Reference-server acceptance
68
70
 
69
71
  The [Code Intelligence acceptance suite](../supi-code-intelligence/__tests__/acceptance/language-baseline-harness.ts) exercises public tools against real servers for TypeScript, JavaScript, Python, Java, Go, Rust, C, and C++. Its [pinned Linux/macOS configuration](../../scripts/language-baseline/pins.json) is separate from the built-in route matrix. Qualification remains pending until both pinned CI jobs pass. Missing test prerequisites are failures, not skipped passes.
@@ -72,6 +74,40 @@ Pyright remains the default Python server, but the audited server lacks implemen
72
74
 
73
75
  JDTLS acceptance uses a compatible pinned JDK and isolated writable `-data` and `-configuration` directories. Keep test workspaces separate from personal JDTLS state. The [capability baseline](../../docs/conventions/code-language-baseline.md) defines the operation and evidence requirements.
74
76
 
77
+ ### TypeScript compiler selection
78
+
79
+ The default TypeScript route prefers a project-local stable native TS7 server. Native launches use the selected package's declared `tsc` Node launcher with `--lsp --stdio`; neither TLS nor a global native executable is required. Legacy compiler launches still require `typescript-language-server` (TLS) on PATH. Nothing is installed automatically.
80
+
81
+ | Configuration | Launch policy |
82
+ |---|---|
83
+ | No explicit launch fields | Prefer stable native TS7 as `typescript`, then as `@typescript/native`. Otherwise use legacy `typescript`, or—only if absent—`@typescript/typescript6`. If all are absent, retain TLS's existing lookup. |
84
+ | `lsp.servers.typescript.typescriptPackage` | Resolve exactly this installed package name/alias. No arbitrary alias scan, package subpath, URL, or filesystem-path selector. |
85
+ | Explicit `command` **or** `args` | Manual launch, including inherited values equal to defaults. No automatic compiler selection or initialization-option injection. |
86
+ | Explicit `initializationOptions.tsserver.path` | Use the configured path without automatic compiler selection. |
87
+ | Package selector plus any explicit launch field above | Configuration conflict; the failure identifies fields and their configuration origins when available. Remove one side and reload. |
88
+
89
+ For example, select a compiler installed as `my-ts6` without changing the TLS command:
90
+
91
+ ```json
92
+ { "lsp": { "servers": { "typescript": { "typescriptPackage": "my-ts6" } } } }
93
+ ```
94
+
95
+ - Resolve packages using the route root's normal Node package search paths, including hoisted and symlinked packages. A package whose actual identity is `@typescript/typescript6`, including one installed as `typescript` or another alias, resolves `@typescript/old` from the wrapper's dependency graph. Pass the underlying implementation's actual `lib/tsserver.js` through `initializationOptions.tsserver.path`; preserve other initialization options.
96
+ - Native-first precedence deliberately replaces canonical-package-first selection: a native `@typescript/native` alias wins over legacy `typescript`. This alias must resolve to the stable native `typescript` package, not legacy TS or the old preview package. Other aliases require `typescriptPackage`.
97
+ - A consulted broken symlink, malformed manifest, missing compiler version, unsupported identity/major, or prerelease is an error, not absence. Stable TS7 releases are eligible without a patch allowlist; unknown native majors and preview/nightly packages require manual launch. Do not inspect lower-priority packages after a native winner. Only the winner's launch files are validated, so a legacy loser's missing `tsserver.js` does not block a healthy native alias.
98
+ - Never fall back after resolution or startup failure of the selected package. Manual launches receive no automatic initialization-option injection.
99
+ - Discovery, missing-server warnings, lazy starts, and recovery use the same policy. Every actual start/retry resolves fresh package files using the active settings snapshot. Installing or repairing a compiler can be followed by explicit health refresh; changing settings requires reload/restart. Healthy clients do not hot-swap.
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
+ - 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
+
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.
104
+
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
+
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
+
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.
110
+
75
111
  ### Java startup and project JDKs
76
112
 
77
113
  The JDK that starts JDTLS can differ from the JDK that builds your project. Set a compatible server runtime through the Java route without changing the shell or project compiler:
@@ -127,6 +163,22 @@ Protocol support is separate from the configured mode. A server may support pull
127
163
 
128
164
  The LSP 3.18 specification adds `Diagnostic.message` markup content, guarded by the client capability `textDocument.diagnostic.markupMessageSupport`; SuPi's validator already accepts plaintext and markdown messages but does not advertise the capability. Other 3.18 features (snippet text edits, inline completion, folding-range refresh, multi-range formatting) are outside the diagnostic surface and are not implemented.
129
165
 
166
+ ### Texlab diagnostic verification
167
+
168
+ Verified binary: **Texlab 5.26.0**. This separate verification does not revise the historical audit above or qualify other Texlab versions. The probe advertised pull support, opened a document with a syntax error, and then corrected and saved it.
169
+
170
+ | Protocol check | Observed result |
171
+ |---|---|
172
+ | Static `diagnosticProvider` | Not advertised |
173
+ | Dynamic `textDocument/diagnostic` registration | None observed |
174
+ | Explicit `textDocument/diagnostic` request | JSON-RPC `-32601` (`method not found`) |
175
+ | `publishDiagnostics` | Publications without a document version |
176
+ | Syntax error → corrected document | Error publication followed by empty publications |
177
+
178
+ Texlab supplies useful diagnostics through ambient push. SuPi shows observed errors as partial evidence and removes them after a correcting publication, but an empty publication does not confirm a completed clean check. `code_health` therefore keeps the file unconfirmed. A `push-settle` timeout ends the bounded observation window; it is not a failed diagnostic request or proof that Texlab is unresponsive. Longer waits, repeated publications, and restarts do not establish completion.
179
+
180
+ The [installed Texlab integration test](__tests__/integration/client.integration.texlab.test.ts) covers routing, the absence of a diagnostic provider/request adapter, and unconfirmed coverage. It runs when `texlab` is installed. See [ADR 0022](../../docs/adr/0022-request-confirmed-lsp-agnostic-diagnostics.md) for the unchanged evidence policy.
181
+
130
182
  ### Diagnostic evidence policy
131
183
 
132
184
  See [`ADR 0022`](../../docs/adr/0022-request-confirmed-lsp-agnostic-diagnostics.md) for the decision record. SuPi uses one shared diagnostic engine with optional internal request adapters. It selects one source for each file:
@@ -151,11 +203,13 @@ Refresh and file collection share synchronization, evidence-revision, deadline,
151
203
 
152
204
  Caller cancellation or deadline stops only that caller's wait. Explicit diagnostic requests use an owned transport lifetime: supersession drops queued jobs and stops future adapter phases but does not cancel an active protocol request. The route stays occupied until actual settlement or connection disposal. The owner timeout attempts protocol cancellation but does not prove that the backend stopped. The owner bound is at least 30 seconds (or a larger collection budget). A result from an obsolete document generation is discarded. Ordinary semantic requests still pass caller cancellation to the protocol transport.
153
205
 
206
+ First document enrollment is admitted between semantic query and refactor-planning attempts on the same current client. Each attempt covers input synchronization, its RPCs, and final acceptance; reads can still run concurrently. A refactor attempt retains admission through `prepareRename`/`rename` or `codeAction`/`resolve` and verified snapshot capture, but releases it before retries and the rename workflow's own cross-file enrollment. FIFO admission prevents later reads from overtaking a waiting first open. At most 32 operations may wait; excess demand fails rather than growing an unbounded queue. Waiting preserves the caller's original signal and absolute deadline, and interrupted operations are removed before they can open a document. Disk content is read after admission. First opens for semantic or refactor demand also discard the caller's original parameters after a known input change; ordinary tracking can still adopt fresh content. Tracked-document updates, closes, workspace changes, and lifecycle invalidation do not wait: they still invalidate active evidence and discard queued request parameters. Closing a file cancels its queued opens, and stopping a client discards its queued work. Admission does not span retries, project warm-up, or whole tool/refactor workflows. Diagnostic transport ownership and confirmation remain separate and unchanged.
207
+
154
208
  If another request first opens a document during an active semantic or diagnostic request, the client can synchronize and repeat the request once. The retry retains the caller's original signal and deadline. Content, workspace, close, and lifecycle changes still reject stale results. A diagnostic retry collects new request evidence; it does not promote an old cache. This retry is not route recovery. A second enrollment during the retry still rejects the result. Semantic failure reasons state whether the retry failed or was exhausted. The bounded `request.retry` debug event records the retry reason and outcome with the public call's opaque operation ID, when supplied. A completed RPC alone does not establish accepted semantic evidence.
155
209
 
156
210
  A read-only semantic request can also retry once after an LSP `ContentModified` error (`-32801`). This retry shares the same one-retry budget as enrollment; it does not add another try. Before this protocol retry, the client waits 100 ms so an immediate retry does not race the same server analysis. The wait is cancellable, respects the original absolute deadline, and does not delay enrollment-only retries. The client keeps the original request parameters and checks changes from the first failed input revision. It runs the full input sync before retry and checks current inputs before it accepts a result. An enrollment is still the only input change that can permit a retry. A content, workspace, close, lifecycle, or retirement change blocks it. Client replacement, caller cancellation, and a deadline also block retry. The post-request check rejects a result if inputs change while that attempt runs. This error-code retry applies only to read-only semantic requests. Diagnostics, rename, code actions, and refactors keep the default policy and do not retry `ContentModified`. See the [LSP 3.17 message-ordering rule](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#messageOrdering), the [retry guard](src/client/client-request-enrollment.ts), and the [client query path](src/client/client.ts).
157
211
 
158
- Diagnostic collection does not close and reopen a document to obtain confirmation. It does not send a no-op `didChange` for that purpose. Refresh retains unchanged documents and their current server state, and resynchronizes changed or invalidated documents. A client restart may reopen tracked documents to restore client state; that is recovery, not diagnostic confirmation.
212
+ Diagnostic collection does not close and reopen a document to obtain confirmation. It does not send a no-op `didChange` for that purpose. Refresh retains unchanged documents and their current server state, and resynchronizes changed or invalidated documents. A client restart may reopen tracked documents to restore client state; that is recovery, not diagnostic confirmation. Recovery skips documents closed or already reopened on the replacement and checks client ownership before reading or restoring failed state.
159
213
 
160
214
  Server readiness follows LSP work-done progress: a created progress token is pending and never blocks readiness; an observed `begin` marks active work and makes the client not ready until its `end` or the bounded per-token timeout.
161
215
 
@@ -199,7 +253,7 @@ Set `code-intelligence.exclude` in project or global SuPi configuration. A proje
199
253
 
200
254
  An old `lsp.exclude` value is a migration error. Move it to `code-intelligence.exclude`; SuPi does not edit personal configuration for you.
201
255
 
202
- An exact semantic request can still route an excluded file when a compatible server is available. This does not add the file to automatic work. Configured diagnostic suppression still applies to diagnostic output.
256
+ An exact semantic request or exact-file health refresh can still route an excluded or external file when a compatible server is available. Explicit refresh can retry that file's failed route without applying automatic-discovery exclusions. Original route and document identities are preserved; display-path canonicalization does not merge alias routes. This does not add the file to automatic work. Configured diagnostic suppression still applies to diagnostic output.
203
257
 
204
258
  Tsconfig and jsconfig filtering applies only to TypeScript and JavaScript-family files (`.ts`, `.tsx`, `.mts`, `.cts`, `.js`, `.jsx`, `.mjs`, and `.cjs`). Other-language diagnostics are not filtered by a nearby TypeScript or JavaScript project config. Automatic path exclusions and configured diagnostic suppression still apply. A single-file health result omits the `Tsconfig` coverage line for other languages.
205
259
 
@@ -238,6 +292,23 @@ Server settings use one snapshot for the active workspace runtime:
238
292
  - SuPi validates the JSON object shape, not server-specific setting names or values.
239
293
  - Saved changes apply after reload or restart. A saved value does not prove that the server accepted it.
240
294
 
295
+ TypeScript and JavaScript servers format code-action edits, such as organize imports, with their own defaults (4-space indentation) unless settings say otherwise. SuPi does not infer formatting from Biome, Prettier, or EditorConfig. Both the `typescript-language-server` and native TS 7 backends read `typescript.format` / `javascript.format`:
296
+
297
+ ```json
298
+ {
299
+ "lsp": {
300
+ "servers": {
301
+ "typescript": {
302
+ "settings": {
303
+ "typescript": { "format": { "indentSize": 2, "tabSize": 2, "convertTabsToSpaces": true } },
304
+ "javascript": { "format": { "indentSize": 2, "tabSize": 2, "convertTabsToSpaces": true } }
305
+ }
306
+ }
307
+ }
308
+ }
309
+ }
310
+ ```
311
+
241
312
  File types do not include a leading dot. If `rootMarkers` is omitted, the server uses the session root.
242
313
 
243
314
  Disable one language with `lsp.servers.<language>.enabled: false`. Use `/supi-ci-status` from `@mrclrchtr/supi-code-intelligence` to see detected, running, and missing servers.
@@ -253,8 +324,11 @@ Disable one language with `lsp.servers.<language>.enabled: false`. Use `/supi-ci
253
324
  - negotiated operation support in project-server status
254
325
 
255
326
  Operation-specific planners request `source.organizeImports` for import updates and
256
- `source.removeUnused` for dead-code removal. They match the returned `CodeAction.kind`,
257
- not its title. Edit-only actions are validated before planning. Command actions,
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,
258
332
  resource operations, snippets, annotations, malformed actions, and stale responses
259
333
  stay unavailable. Distinct matching plans return an ambiguous result instead of
260
334
  selecting the first action. Rename uses `prepareRename` when the server advertises
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-code-runtime",
3
- "version": "11.1.0",
3
+ "version": "11.3.0",
4
4
  "description": "Shared workspace context and capability contracts for code intelligence",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -77,7 +77,9 @@ export interface SemanticProvider {
77
77
  ): Promise<CodeQueryResult<DocumentCodeSymbol[]>>;
78
78
  /**
79
79
  * Collect workspace symbols from routes that can contribute within the
80
- * optional canonical path scopes.
80
+ * optional path scopes. Completeness is relative to those scopes, but data
81
+ * may include out-of-scope rows; consumers must filter rows themselves.
82
+ * Policy exclusions proven outside every scope do not make the query partial.
81
83
  */
82
84
  workspaceSymbols(
83
85
  query: string,
@@ -3,8 +3,17 @@ export interface CodeQueryPartialMetadata {
3
3
  /** Automatic path-policy evidence removed from a collected result. */
4
4
  readonly automaticPathExclusion?: {
5
5
  readonly excludedCount: number;
6
+ /** Observed policy paths, not canonical navigation paths; retain aliases when presenting exclusions. */
6
7
  readonly examples: readonly string[];
7
8
  };
9
+ /**
10
+ * True when automatic path-policy exclusions are the only reason the result
11
+ * is partial: every contributing provider branch completed and no coverage
12
+ * gap was observed. Excluded rows matched paths the workspace policy treats
13
+ * as out of automatic scope, so the retained rows are the complete in-policy
14
+ * answer.
15
+ */
16
+ readonly exclusionOnly?: boolean;
8
17
  }
9
18
 
10
19
  /**
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "11.1.0",
3
+ "version": "11.3.0",
4
4
  "description": "Shared settings, configuration, reporting, and session infrastructure",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -0,0 +1,110 @@
1
+ // Session-provided Web tools for SuPi child sessions (Agent Runs and Reviewer Sessions).
2
+ //
3
+ // The loaded supi-web extension answers a synchronous request on the parent event bus.
4
+ // Consumers never depend on supi-web; when it is absent or provides no tools, the
5
+ // request returns undefined and the child simply lacks Web tools.
6
+
7
+ import type { ExtensionAPI, ExtensionFactory } from "@earendil-works/pi-coding-agent";
8
+
9
+ /** Web tools that child sessions may receive when the containing session provides them. */
10
+ export const CHILD_WEB_TOOL_NAMES = [
11
+ "web_fetch_md",
12
+ "web_search",
13
+ "web_docs_search",
14
+ "web_docs_fetch",
15
+ ] as const;
16
+
17
+ /** One session-provided Web tool name. */
18
+ export type ChildWebToolName = (typeof CHILD_WEB_TOOL_NAMES)[number];
19
+
20
+ /**
21
+ * Web tools currently callable in the containing session plus a child-only extension
22
+ * factory that registers them. The factory must not capture the parent session; it may
23
+ * capture plain data such as the containing session's configuration directory.
24
+ */
25
+ export interface ChildWebToolProvision {
26
+ readonly toolNames: readonly ChildWebToolName[];
27
+ /** Register the requested subset of `toolNames` in a child session. */
28
+ readonly createFactory: (toolNames: readonly ChildWebToolName[]) => ExtensionFactory;
29
+ }
30
+
31
+ /** Plain containing-session data supplied with a child Web tool request. */
32
+ export interface ChildWebToolsRequestContext {
33
+ /**
34
+ * Working directory whose SuPi configuration governs Web settings such as Firecrawl.
35
+ * Children with a different cwd (for example a frozen Review Workspace) keep this policy.
36
+ */
37
+ readonly configCwd: string;
38
+ }
39
+
40
+ type EventBusLike = Pick<ExtensionAPI["events"], "on" | "emit">;
41
+
42
+ const REQUEST_CHANNEL = "supi:child-web-tools:request";
43
+ const RESPONSE_CHANNEL = "supi:child-web-tools:response";
44
+
45
+ interface ChildWebToolsRequest extends ChildWebToolsRequestContext {
46
+ readonly requestId: string;
47
+ }
48
+
49
+ interface ChildWebToolsResponse {
50
+ readonly requestId: string;
51
+ readonly provision: ChildWebToolProvision;
52
+ }
53
+
54
+ let nextRequestId = 0;
55
+
56
+ /**
57
+ * Answer child Web tool requests on the parent event bus. The callback must be synchronous
58
+ * because Pi's event bus does not await listeners. Returns an unsubscribe function.
59
+ */
60
+ export function provideChildWebTools(
61
+ events: EventBusLike,
62
+ resolve: (context: ChildWebToolsRequestContext) => ChildWebToolProvision | undefined,
63
+ ): () => void {
64
+ return events.on(REQUEST_CHANNEL, (data) => {
65
+ const request = data as Partial<ChildWebToolsRequest> | undefined;
66
+ if (typeof request?.requestId !== "string" || typeof request.configCwd !== "string") return;
67
+ const { requestId, configCwd } = request;
68
+ const provision = resolve({ configCwd });
69
+ if (!provision || provision.toolNames.length === 0) return;
70
+ events.emit(RESPONSE_CHANNEL, { requestId, provision } satisfies ChildWebToolsResponse);
71
+ });
72
+ }
73
+
74
+ /** Capture the Web tools the containing session currently provides, if any. */
75
+ export function requestChildWebTools(
76
+ events: EventBusLike,
77
+ context: ChildWebToolsRequestContext,
78
+ ): ChildWebToolProvision | undefined {
79
+ const requestId = `child-web-${process.pid}-${++nextRequestId}`;
80
+ let provision: ChildWebToolProvision | undefined;
81
+ const unsubscribe = events.on(RESPONSE_CHANNEL, (data) => {
82
+ const response = data as Partial<ChildWebToolsResponse> | undefined;
83
+ if (response?.requestId === requestId && response.provision) provision = response.provision;
84
+ });
85
+ try {
86
+ events.emit(REQUEST_CHANNEL, { requestId, ...context } satisfies ChildWebToolsRequest);
87
+ } finally {
88
+ unsubscribe();
89
+ }
90
+ return provision;
91
+ }
92
+
93
+ /**
94
+ * Return the Web tools callable in a session under Pi's exposure rules: active `direct`
95
+ * tools, plus registered `codemode` and `deferred` tools. `model-only` and `hidden`
96
+ * tools are not callable.
97
+ */
98
+ export function callableChildWebToolNames(
99
+ pi: Pick<ExtensionAPI, "getAllTools" | "getActiveTools">,
100
+ ): ChildWebToolName[] {
101
+ const active = new Set(pi.getActiveTools());
102
+ const exposures = new Map(
103
+ pi.getAllTools().map((tool) => [tool.name, tool.exposure ?? "direct"] as const),
104
+ );
105
+ return CHILD_WEB_TOOL_NAMES.filter((name) => {
106
+ const exposure = exposures.get(name);
107
+ if (exposure === "direct") return active.has(name);
108
+ return exposure === "codemode" || exposure === "deferred";
109
+ });
110
+ }
@@ -1,5 +1,16 @@
1
1
  // supi-core session domain — session utilities and registries.
2
2
 
3
+ export type {
4
+ ChildWebToolName,
5
+ ChildWebToolProvision,
6
+ ChildWebToolsRequestContext,
7
+ } from "./child-web-tools.ts";
8
+ export {
9
+ CHILD_WEB_TOOL_NAMES,
10
+ callableChildWebToolNames,
11
+ provideChildWebTools,
12
+ requestChildWebTools,
13
+ } from "./child-web-tools.ts";
3
14
  export { createRegistry, createSessionStateRegistry } from "./registry-utils.ts";
4
15
  export type {
5
16
  SessionCapabilitySkill,
@@ -0,0 +1,15 @@
1
+ The ISC License
2
+
3
+ Copyright (c) Isaac Z. Schlueter and Contributors
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
15
+ IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.