@mrclrchtr/supi-lsp 1.16.0 → 2.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 (77) hide show
  1. package/README.md +44 -56
  2. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  4. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  5. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  6. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  7. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  8. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  9. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  10. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  11. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  12. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  13. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  14. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  15. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +3 -2
  16. package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +7 -0
  17. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +56 -2
  18. package/node_modules/@mrclrchtr/supi-code-runtime/src/evidence-badge.ts +40 -0
  19. package/node_modules/@mrclrchtr/supi-code-runtime/src/index.ts +5 -0
  20. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +76 -3
  21. package/node_modules/@mrclrchtr/supi-code-runtime/src/workspace/runtime.ts +37 -2
  22. package/node_modules/@mrclrchtr/supi-core/package.json +6 -1
  23. package/node_modules/@mrclrchtr/supi-core/src/abort-utils.ts +31 -0
  24. package/node_modules/@mrclrchtr/supi-core/src/api.ts +8 -0
  25. package/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +10 -4
  26. package/node_modules/@mrclrchtr/supi-core/src/config.ts +1 -0
  27. package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +1 -1
  28. package/node_modules/@mrclrchtr/supi-core/src/index.ts +6 -0
  29. package/node_modules/@mrclrchtr/supi-core/src/model-selection.ts +134 -0
  30. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +9 -2
  31. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +109 -4
  32. package/node_modules/@mrclrchtr/supi-core/src/settings-ui.ts +5 -1
  33. package/node_modules/@mrclrchtr/supi-core/src/spinner-frames.ts +11 -0
  34. package/node_modules/@mrclrchtr/supi-core/src/status-spinner.ts +68 -0
  35. package/node_modules/vscode-languageserver-protocol/lib/common/protocol.diagnostic.d.ts +21 -2
  36. package/node_modules/vscode-languageserver-protocol/lib/common/protocol.diagnostic.js +1 -0
  37. package/node_modules/vscode-languageserver-protocol/lib/common/protocol.inlineValue.d.ts +2 -1
  38. package/node_modules/vscode-languageserver-protocol/package.json +1 -1
  39. package/package.json +4 -31
  40. package/src/api.ts +27 -0
  41. package/src/client/client-refresh.ts +5 -0
  42. package/src/client/client.ts +246 -1
  43. package/src/client/transport.ts +4 -0
  44. package/src/config/capabilities.ts +3 -0
  45. package/src/config/config.ts +4 -2
  46. package/src/config/lsp-settings.ts +102 -0
  47. package/src/config/server-actions.ts +59 -0
  48. package/src/config/server-config.ts +4 -0
  49. package/src/index.ts +11 -0
  50. package/src/manager/capability-index.ts +1 -1
  51. package/src/manager/manager-project-info.ts +2 -1
  52. package/src/manager/manager.ts +115 -26
  53. package/src/provider/lsp-semantic-provider.ts +177 -134
  54. package/src/provider/refactor-planning.ts +214 -0
  55. package/src/session/runtime-controller.ts +320 -0
  56. package/src/session/runtime-registration.ts +26 -5
  57. package/src/session/service-registry.ts +81 -4
  58. package/src/summary.ts +2 -0
  59. package/src/extension.ts +0 -1
  60. package/src/format.ts +0 -373
  61. package/src/handlers/diagnostic-injection.ts +0 -182
  62. package/src/handlers/session-lifecycle.ts +0 -149
  63. package/src/handlers/status-command.ts +0 -38
  64. package/src/handlers/workspace-recovery.ts +0 -92
  65. package/src/lsp.ts +0 -37
  66. package/src/session/lsp-state.ts +0 -93
  67. package/src/session/settings-registration.ts +0 -296
  68. package/src/session/tree-persist.ts +0 -75
  69. package/src/tool/guidance.ts +0 -69
  70. package/src/tool/names.ts +0 -27
  71. package/src/tool/overrides.ts +0 -204
  72. package/src/tool/register-tools.ts +0 -69
  73. package/src/tool/service-actions.ts +0 -399
  74. package/src/tool/tool-specs.ts +0 -248
  75. package/src/ui/renderer.ts +0 -120
  76. package/src/ui/ui.ts +0 -360
  77. package/src/workspace-change.ts +0 -40
@@ -1,2 +1,6 @@
1
1
  // supi-core settings-ui domain — settings TUI components (imports pi-tui at runtime, heavy).
2
- export { createInputSubmenu, openSettingsOverlay } from "./settings/settings-ui.ts";
2
+ export {
3
+ createInputSubmenu,
4
+ createModelPickerSubmenu,
5
+ openSettingsOverlay,
6
+ } from "./settings/settings-ui.ts";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Shared UI constants for SuPi extensions.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ /** Braille spinner frames used across SuPi extensions for animated loaders. */
8
+ export const BRAILLE_SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"];
9
+
10
+ /** Tick interval (ms) shared by all braille-spinner consumers. */
11
+ export const SPINNER_INTERVAL_MS = 80;
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Lightweight status-bar spinner for SuPi extensions.
3
+ *
4
+ * Manages a setInterval-based animated spinner that writes to
5
+ * `ctx.ui.setStatus`. Each tick advances the frame and re-renders
6
+ * with the current message.
7
+ *
8
+ * @module
9
+ */
10
+
11
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
12
+ import { BRAILLE_SPINNER_FRAMES, SPINNER_INTERVAL_MS } from "./spinner-frames.ts";
13
+
14
+ /**
15
+ * Manages an animated braille spinner on the status bar.
16
+ *
17
+ * Usage:
18
+ * ```ts
19
+ * const spinner = new StatusSpinner(ctx, "my-package");
20
+ * spinner.start("generating…");
21
+ * // later
22
+ * spinner.stop();
23
+ * ```
24
+ */
25
+ export class StatusSpinner {
26
+ private interval: ReturnType<typeof setInterval> | null = null;
27
+ private frame = 0;
28
+ private currentMessage = "";
29
+
30
+ constructor(
31
+ private ctx: ExtensionContext,
32
+ private source: string,
33
+ private frames: readonly string[] = BRAILLE_SPINNER_FRAMES,
34
+ ) {}
35
+
36
+ /** Start the spinner with the given message. Overwrites any active spinner. */
37
+ start(message: string): void {
38
+ this.stop();
39
+ this.currentMessage = message;
40
+ this.render();
41
+
42
+ this.interval = setInterval(() => {
43
+ this.frame++;
44
+ this.render();
45
+ }, SPINNER_INTERVAL_MS);
46
+ }
47
+
48
+ /** Update the display message without resetting the spinner. */
49
+ update(message: string): void {
50
+ this.currentMessage = message;
51
+ }
52
+
53
+ /** Stop the spinner and clear the status. */
54
+ stop(): void {
55
+ if (this.interval !== null) {
56
+ clearInterval(this.interval);
57
+ this.interval = null;
58
+ }
59
+ this.ctx.ui.setStatus(this.source, "");
60
+ }
61
+
62
+ // ── Private ──────────────────────────────────────────────────────────
63
+
64
+ private render(): void {
65
+ const icon = this.frames[this.frame % this.frames.length];
66
+ this.ctx.ui.setStatus(this.source, `${icon} ${this.currentMessage}`);
67
+ }
68
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-code-runtime",
3
- "version": "1.16.0",
3
+ "version": "2.0.0",
4
4
  "description": "SuPi code-runtime — shared workspace context, capability contracts, and canonical types for the code-understanding stack",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,12 +23,13 @@
23
23
  "workspace-context",
24
24
  "provider-api"
25
25
  ],
26
+ "type": "module",
26
27
  "files": [
27
28
  "src/**/*.ts",
28
29
  "!__tests__"
29
30
  ],
30
31
  "dependencies": {
31
- "@mrclrchtr/supi-core": "1.16.0"
32
+ "@mrclrchtr/supi-core": "2.0.0"
32
33
  },
33
34
  "bundledDependencies": [
34
35
  "@mrclrchtr/supi-core"
@@ -13,9 +13,13 @@ export type {
13
13
  StructuralProvider,
14
14
  StructuralResult,
15
15
  } from "./capability/types.ts";
16
+ // Evidence display
17
+ export { type EvidenceBadgeInput, formatEvidenceBadge } from "./evidence-badge.ts";
16
18
  // Shared canonical types
17
19
  export type {
20
+ CalleeDepth,
18
21
  CalleesData,
22
+ CallSite,
19
23
  CodeLocation,
20
24
  CodePosition,
21
25
  CodeResult,
@@ -27,10 +31,13 @@ export type {
27
31
  ImportData,
28
32
  NodeAtData,
29
33
  OutlineData,
34
+ RefactorOperation,
35
+ RefactorRequest,
30
36
  RefactorResult,
31
37
  SourceRange,
32
38
  WorkspaceEdit,
33
39
  } from "./types.ts";
40
+ export { normalizeRefactorOperation } from "./types.ts";
34
41
  export type { WorkspaceContext } from "./workspace/context.ts";
35
42
  // Workspace context
36
43
  export { createWorkspaceContext } from "./workspace/context.ts";
@@ -5,7 +5,9 @@
5
5
  */
6
6
 
7
7
  import type {
8
+ CalleeDepth,
8
9
  CalleesData,
10
+ CallSite,
9
11
  CodeLocation,
10
12
  CodePosition,
11
13
  CodeResult,
@@ -14,7 +16,9 @@ import type {
14
16
  ImportData,
15
17
  NodeAtData,
16
18
  OutlineData,
19
+ RefactorRequest,
17
20
  RefactorResult,
21
+ SourceRange,
18
22
  } from "../types.ts";
19
23
 
20
24
  // ── Availability state ─────────────────────────────────────────────────
@@ -49,17 +53,60 @@ export interface SemanticProvider {
49
53
  documentSymbols(filePath: string): Promise<CodeSymbol[] | null>;
50
54
  workspaceSymbols(query: string): Promise<CodeSymbol[] | null>;
51
55
 
56
+ /**
57
+ * Optional definition capability. Returns the definition location(s) for
58
+ * the symbol at the given position. When the provider cannot produce
59
+ * definition info, returns `null`.
60
+ */
61
+ definition?(filePath: string, position: CodePosition): Promise<CodeLocation[] | null>;
62
+
63
+ /**
64
+ * Optional hover capability. Returns a simplified type/signature info
65
+ * shape that does not depend on vscode-languageserver-types. When the
66
+ * provider cannot produce hover info (unavailable, unsupported file,
67
+ * no result at the given position), returns `null`.
68
+ */
69
+ hover?(
70
+ filePath: string,
71
+ position: CodePosition,
72
+ ): Promise<{ contents: string; range?: SourceRange } | null>;
73
+
74
+ /**
75
+ * Optional operation-aware refactor capability.
76
+ *
77
+ * This is the preferred planning entrypoint for higher-level tools because it
78
+ * lets the provider choose the honest substrate path per operation (rename,
79
+ * organize imports, dead-code cleanup, etc.) without exposing that branching
80
+ * to callers.
81
+ */
82
+ refactor?(request: RefactorRequest): Promise<RefactorResult>;
83
+
52
84
  /**
53
85
  * Optional rename capability. When present, the provider supports
54
- * precise semantic rename operations.
86
+ * precise semantic symbol-rename operations.
87
+ *
88
+ * Kept as a low-level substrate helper for compatibility while the public
89
+ * tool surface still exposes a legacy `rename` alias.
55
90
  */
56
91
  rename?(file: string, position: CodePosition, newName: string): Promise<RefactorResult>;
57
92
 
58
93
  /**
59
94
  * Optional code actions capability. When present, the provider
60
95
  * supports code-action-based refactors.
96
+ *
97
+ * Kept as a low-level substrate helper and for lightweight introspection.
61
98
  */
62
99
  codeActions?(file: string, position: CodePosition): Promise<RefactorResult[]>;
100
+
101
+ /**
102
+ * Optional lightweight code action titles for display purposes.
103
+ * Returns simplified title/kind pairs at the given position.
104
+ * When the provider cannot produce code actions, returns `null`.
105
+ */
106
+ codeActionTitles?(
107
+ file: string,
108
+ position: CodePosition,
109
+ ): Promise<Array<{ title: string; kind?: string }> | null>;
63
110
  }
64
111
 
65
112
  /**
@@ -70,11 +117,18 @@ export interface SemanticProvider {
70
117
  * and runtime error states.
71
118
  */
72
119
  export interface StructuralProvider {
73
- calleesAt(file: string, line: number, character: number): Promise<CodeResult<CalleesData>>;
120
+ calleesAt(
121
+ file: string,
122
+ line: number,
123
+ character: number,
124
+ depth?: CalleeDepth,
125
+ ): Promise<CodeResult<CalleesData>>;
74
126
  exports(file: string): Promise<CodeResult<ExportData[]>>;
75
127
  outline(file: string): Promise<CodeResult<OutlineData[]>>;
76
128
  imports(file: string): Promise<CodeResult<ImportData[]>>;
77
129
  nodeAt(file: string, line: number, character: number): Promise<CodeResult<NodeAtData>>;
130
+ /** Find all call-site identifiers in a file. Returns name + start line for each match. */
131
+ callSites(file: string): Promise<CodeResult<CallSite[]>>;
78
132
  }
79
133
 
80
134
  /** Convenience alias for `CodeResult` used in structural contexts. */
@@ -0,0 +1,40 @@
1
+ /**
2
+ * TUI-facing evidence badge string formatter.
3
+ *
4
+ * Pure string formatting — no pi-tui dependency. Consumes evidence
5
+ * completeness metadata and produces compact human-readable badges.
6
+ */
7
+
8
+ /** Metadata describing how many evidence atoms were shown vs exist. */
9
+ export interface EvidenceBadgeInput {
10
+ shownCount: number;
11
+ totalCount: number | null;
12
+ omittedCount: number | null;
13
+ partialReason: string | null;
14
+ /** Human-readable label for the badge, e.g. "references", "symbols". */
15
+ label: string;
16
+ }
17
+
18
+ /**
19
+ * Format a compact evidence completeness badge.
20
+ *
21
+ * | Input | Output |
22
+ * |------------------------------------------------------|---------------------------------------|
23
+ * | shown=12, total=12, omitted=0, label="references" | `12 references` |
24
+ * | shown=8, total=20, omitted=12, label="symbols" | `8 of 20 symbols (12 omitted)` |
25
+ * | shown=5, total=null, reason="timeout", label="matches" | `5 matches — timeout` |
26
+ */
27
+ export function formatEvidenceBadge(input: EvidenceBadgeInput): string {
28
+ const { shownCount, totalCount, omittedCount, partialReason, label } = input;
29
+
30
+ if (totalCount === null) {
31
+ const reasonSuffix = partialReason ? ` — ${partialReason}` : "";
32
+ return `${shownCount} ${label}${reasonSuffix}`;
33
+ }
34
+
35
+ if (omittedCount !== null && omittedCount > 0) {
36
+ return `${shownCount} of ${totalCount} ${label} (${omittedCount} omitted)`;
37
+ }
38
+
39
+ return `${shownCount} ${label}`;
40
+ }
@@ -6,7 +6,9 @@
6
6
  */
7
7
 
8
8
  export type {
9
+ CalleeDepth,
9
10
  CalleesData,
11
+ CallSite,
10
12
  CapabilityState,
11
13
  CodeLocation,
12
14
  CodePosition,
@@ -17,6 +19,8 @@ export type {
17
19
  ImportData,
18
20
  NodeAtData,
19
21
  OutlineData,
22
+ RefactorOperation,
23
+ RefactorRequest,
20
24
  SemanticProvider,
21
25
  SourceRange,
22
26
  StructuralProvider,
@@ -27,5 +31,6 @@ export type {
27
31
  export {
28
32
  createWorkspaceContext,
29
33
  getDefaultWorkspaceRuntime,
34
+ normalizeRefactorOperation,
30
35
  WorkspaceRuntime,
31
36
  } from "./api.ts";
@@ -28,13 +28,29 @@ export interface CodeLocation {
28
28
 
29
29
  // ── Symbol types ───────────────────────────────────────────────────────
30
30
 
31
- /** A discovered symbol / declaration. */
31
+ /** A 1-based source position used as a symbol anchor. */
32
+ export interface SymbolAnchor {
33
+ line: number;
34
+ character: number;
35
+ }
36
+
37
+ /**
38
+ * A discovered symbol / declaration.
39
+ *
40
+ * Per ADR 0003, anchors are split so position-strict substrates
41
+ * (structural callee lookup, LSP `rename`) cannot silently consume a
42
+ * declaration anchor (the `export` keyword) as if it were the identifier.
43
+ * - `declarationAnchor` is always present (the defining node start).
44
+ * - `nameAnchor` is best-effort (the identifier token), present when the
45
+ * provider can derive it (LSP `selectionRange`, or a tree-sitter snap).
46
+ * Strict consumers must prefer it and hard-fail when it is absent.
47
+ */
32
48
  export interface CodeSymbol {
33
49
  name: string;
34
50
  kind: string;
35
51
  file: string;
36
- line: number;
37
- character: number;
52
+ declarationAnchor: SymbolAnchor;
53
+ nameAnchor?: SymbolAnchor;
38
54
  container?: string | null;
39
55
  }
40
56
 
@@ -125,6 +141,44 @@ export interface WorkspaceEdit {
125
141
  edits: FileEdit[];
126
142
  }
127
143
 
144
+ /**
145
+ * Supported refactor operation names for the current semantic planning path.
146
+ *
147
+ * `rename` is kept as a legacy alias for the public rename-only surface.
148
+ */
149
+ export type RefactorOperation =
150
+ | "rename"
151
+ | "rename_symbol"
152
+ | "extract_function"
153
+ | "extract_variable"
154
+ | "rename_file"
155
+ | "move_file"
156
+ | "update_imports"
157
+ | "delete_dead_code";
158
+
159
+ /** Normalize legacy refactor aliases to their canonical operation names. */
160
+ export function normalizeRefactorOperation(
161
+ operation: RefactorOperation,
162
+ ): Exclude<RefactorOperation, "rename"> {
163
+ return operation === "rename" ? "rename_symbol" : operation;
164
+ }
165
+
166
+ /**
167
+ * Operation-aware refactor planning request.
168
+ *
169
+ * Consumers provide a target file/position plus operation-specific options.
170
+ * File/resource operations remain requestable so providers can reject them
171
+ * explicitly and honestly until shared workspace-edit resource ops exist.
172
+ */
173
+ export interface RefactorRequest {
174
+ operation: RefactorOperation;
175
+ file: string;
176
+ position: CodePosition;
177
+ range?: SourceRange;
178
+ newName?: string;
179
+ destination?: string;
180
+ }
181
+
128
182
  /**
129
183
  * A disambiguation candidate when a refactor target is ambiguous.
130
184
  */
@@ -149,7 +203,26 @@ export type RefactorResult =
149
203
 
150
204
  // ── Structural data shapes (value types, range-flattened) ──────────────
151
205
 
206
+ /** Callee collection depth. */
207
+ export type CalleeDepth = "direct" | "deep";
208
+
209
+ /**
210
+ * Structural outgoing calls from the enclosing executable scope at a
211
+ * position. Callee names are source-shape facts, not resolved symbol
212
+ * identities.
213
+ *
214
+ * In `direct` depth, nested function/callback scopes are excluded from the
215
+ * outer scope. In `deep` depth, all callees within the enclosing scope are
216
+ * included, including those inside nested scopes.
217
+ */
152
218
  export interface CalleesData {
153
219
  enclosingScope: { name: string; startLine: number; endLine: number };
154
220
  callees: Array<{ name: string; startLine: number }>;
221
+ depth: CalleeDepth;
222
+ }
223
+
224
+ /** A single call-site match with name and start line. */
225
+ export interface CallSite {
226
+ name: string;
227
+ startLine: number;
155
228
  }
@@ -48,7 +48,12 @@ function createDefaultCapabilities(): WorkspaceCapabilities {
48
48
  }
49
49
 
50
50
  function hasRefactorCapability(provider: SemanticProvider | null): boolean {
51
- return !!(provider && (provider.rename !== undefined || provider.codeActions !== undefined));
51
+ return !!(
52
+ provider &&
53
+ (provider.refactor !== undefined ||
54
+ provider.rename !== undefined ||
55
+ provider.codeActions !== undefined)
56
+ );
52
57
  }
53
58
 
54
59
  // ── Runtime ────────────────────────────────────────────────────────────
@@ -78,7 +83,27 @@ export class WorkspaceRuntime {
78
83
  }
79
84
 
80
85
  /**
81
- * Register a semantic provider for a workspace.
86
+ * Register a semantic provider for a workspace in pending state.
87
+ *
88
+ * Use this when the provider object exists and can accept calls, but the
89
+ * underlying semantic substrate is still warming (for example, an LSP server
90
+ * that has started but has not finished initial indexing yet).
91
+ */
92
+ registerSemanticPending(cwd: string, provider: SemanticProvider): void {
93
+ const refactorAvailable = hasRefactorCapability(provider);
94
+ const existing = this.#workspaces.get(cwd);
95
+ if (existing) {
96
+ existing.semantic = { state: { kind: "pending" }, provider, refactorAvailable };
97
+ } else {
98
+ this.#workspaces.set(cwd, {
99
+ semantic: { state: { kind: "pending" }, provider, refactorAvailable },
100
+ structural: createDefaultCapabilities().structural,
101
+ });
102
+ }
103
+ }
104
+
105
+ /**
106
+ * Register a semantic provider for a workspace as ready.
82
107
  * Replaces any existing semantic provider for the same cwd
83
108
  * without affecting the structural provider.
84
109
  */
@@ -95,6 +120,16 @@ export class WorkspaceRuntime {
95
120
  }
96
121
  }
97
122
 
123
+ /**
124
+ * Promote an already-registered semantic provider from pending to ready.
125
+ * If no semantic provider is registered for the workspace, this is a no-op.
126
+ */
127
+ markSemanticReady(cwd: string): void {
128
+ const ws = this.#workspaces.get(cwd);
129
+ if (!ws || ws.semantic.provider === null) return;
130
+ ws.semantic.state = { kind: "ready" };
131
+ }
132
+
98
133
  /**
99
134
  * Register a structural provider for a workspace.
100
135
  * Replaces any existing structural provider for the same cwd
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-core",
3
- "version": "1.16.0",
3
+ "version": "2.0.0",
4
4
  "description": "SuPi core — shared infrastructure for SuPi extensions (XML context tags, config system)",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,6 +23,7 @@
23
23
  "settings",
24
24
  "configuration"
25
25
  ],
26
+ "type": "module",
26
27
  "files": [
27
28
  "src/**/*.ts",
28
29
  "!__tests__"
@@ -49,12 +50,14 @@
49
50
  },
50
51
  "main": "src/api.ts",
51
52
  "exports": {
53
+ "./abort-utils": "./src/abort-utils.ts",
52
54
  "./api": "./src/api.ts",
53
55
  "./config": "./src/config.ts",
54
56
  "./context": "./src/context.ts",
55
57
  "./debug": "./src/debug-registry.ts",
56
58
  "./footer-registry": "./src/footer-registry.ts",
57
59
  "./llm": "./src/llm.ts",
60
+ "./model-selection": "./src/model-selection.ts",
58
61
  "./package.json": "./package.json",
59
62
  "./path": "./src/path.ts",
60
63
  "./report": "./src/report.ts",
@@ -63,6 +66,8 @@
63
66
  "./session": "./src/session.ts",
64
67
  "./settings": "./src/settings.ts",
65
68
  "./settings-ui": "./src/settings-ui.ts",
69
+ "./spinner-frames": "./src/spinner-frames.ts",
70
+ "./status-spinner": "./src/status-spinner.ts",
66
71
  "./terminal": "./src/terminal.ts",
67
72
  "./tool-framework": "./src/tool-framework.ts",
68
73
  "./types": "./src/types.ts"
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Abort controller/signal utility helpers.
3
+ *
4
+ * @module
5
+ */
6
+
7
+ /**
8
+ * Combine a caller-provided abort controller with a generation timeout signal.
9
+ *
10
+ * Returns the combined signal and a cleanup function that removes both
11
+ * event listeners. Callers must invoke cleanup in a `finally` block
12
+ * to avoid listener leaks.
13
+ */
14
+ export function combineAbortSignals(
15
+ abort: AbortController,
16
+ timeoutMs: number,
17
+ ): { signal: AbortSignal; cleanup: () => void } {
18
+ const timeoutSignal = AbortSignal.timeout(timeoutMs);
19
+ const combinedController = new AbortController();
20
+ const onAbort = () => combinedController.abort();
21
+ abort.signal.addEventListener("abort", onAbort, { once: true });
22
+ timeoutSignal.addEventListener("abort", onAbort, { once: true });
23
+
24
+ return {
25
+ signal: combinedController.signal,
26
+ cleanup: () => {
27
+ abort.signal.removeEventListener("abort", onAbort);
28
+ timeoutSignal.removeEventListener("abort", onAbort);
29
+ },
30
+ };
31
+ }
@@ -6,6 +6,8 @@
6
6
  // For lighter imports, use one of the domain subpaths directly
7
7
  // (e.g. @mrclrchtr/supi-core/config, @mrclrchtr/supi-core/context).
8
8
 
9
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
10
+ export * from "./abort-utils.ts";
9
11
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
10
12
  export * from "./config.ts";
11
13
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
@@ -17,6 +19,8 @@ export * from "./footer-registry.ts";
17
19
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
18
20
  export * from "./llm.ts";
19
21
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
22
+ export * from "./model-selection.ts";
23
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
20
24
  export * from "./path.ts";
21
25
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
22
26
  export * from "./project.ts";
@@ -29,6 +33,10 @@ export * from "./settings.ts";
29
33
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
30
34
  export * from "./settings-ui.ts";
31
35
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
36
+ export * from "./spinner-frames.ts";
37
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
38
+ export * from "./status-spinner.ts";
39
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
32
40
  export * from "./terminal.ts";
33
41
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
34
42
  export * from "./tool-framework.ts";
@@ -5,6 +5,7 @@
5
5
  // to enable auto-generated persistChange. When all items have a configType,
6
6
  // the persistChange callback can be omitted.
7
7
 
8
+ import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
8
9
  import type { SettingItem } from "@earendil-works/pi-tui";
9
10
  import type { SettingsScope } from "../settings/settings-registry.ts";
10
11
  import { registerSettings } from "../settings/settings-registry.ts";
@@ -60,7 +61,12 @@ export interface ConfigSettingsOptions<T> {
60
61
  * persistChange handling. When ALL items declare a configType,
61
62
  * the `persistChange` callback can be omitted.
62
63
  */
63
- buildItems: (settings: T, scope: SettingsScope, cwd: string) => ConfigSettingItem[];
64
+ buildItems: (
65
+ settings: T,
66
+ scope: SettingsScope,
67
+ cwd: string,
68
+ ctx?: ExtensionContext,
69
+ ) => ConfigSettingItem[];
64
70
  /**
65
71
  * Handle a settings change with scoped persistence helpers.
66
72
  *
@@ -141,12 +147,12 @@ export function registerConfigSettings<T>(options: ConfigSettingsOptions<T>): vo
141
147
  registerSettings({
142
148
  id: options.id,
143
149
  label: options.label,
144
- loadValues: (scope, cwd) => {
150
+ loadValues: (scope, cwd, ctx) => {
145
151
  const settings = loadSupiConfigForScope(options.section, cwd, options.defaults, {
146
152
  scope,
147
153
  homeDir: options.homeDir,
148
154
  });
149
- const items = options.buildItems(settings, scope, cwd);
155
+ const items = options.buildItems(settings, scope, cwd, ctx);
150
156
  cachedItems = items;
151
157
  return items;
152
158
  },
@@ -173,7 +179,7 @@ export function registerConfigSettings<T>(options: ConfigSettingsOptions<T>): vo
173
179
  }
174
180
 
175
181
  // Auto-generate when all items are declarative
176
- const items = cachedItems ?? options.buildItems(options.defaults, scope, cwd);
182
+ const items = cachedItems ?? options.buildItems(options.defaults, scope, cwd, undefined);
177
183
  if (areAllItemsDeclarative(items)) {
178
184
  autoPersistChange(settingId, value, helpers, items);
179
185
  }
@@ -1,6 +1,7 @@
1
1
  // supi-core config domain — config loading and config-settings helpers.
2
2
  export type { SupiConfigLocation, SupiConfigOptions } from "./config/config.ts";
3
3
  export {
4
+ loadSectionConfig,
4
5
  loadSupiConfig,
5
6
  loadSupiConfigForScope,
6
7
  readJsonFile,
@@ -8,7 +8,7 @@
8
8
  import { createRegistry } from "./registry-utils.ts";
9
9
 
10
10
  /** Where the contribution should appear in the footer. */
11
- export type FooterPlacement = "stats" | "status";
11
+ export type FooterPlacement = "stats" | "stats-end" | "status";
12
12
 
13
13
  /** A single footer contribution registered by an extension. */
14
14
  export interface FooterContribution {
@@ -6,6 +6,8 @@
6
6
  // For lighter imports, use one of the domain subpaths directly
7
7
  // (e.g. @mrclrchtr/supi-core/config, @mrclrchtr/supi-core/context).
8
8
 
9
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
10
+ export * from "./abort-utils.ts";
9
11
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
10
12
  export * from "./config.ts";
11
13
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
@@ -15,6 +17,8 @@ export * from "./debug-registry.ts";
15
17
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
16
18
  export * from "./footer-registry.ts";
17
19
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
20
+ export * from "./model-selection.ts";
21
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
18
22
  export * from "./path.ts";
19
23
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
20
24
  export * from "./project.ts";
@@ -27,6 +31,8 @@ export * from "./settings.ts";
27
31
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
28
32
  export * from "./settings-ui.ts";
29
33
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
34
+ export * from "./status-spinner.ts";
35
+ // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
30
36
  export * from "./terminal.ts";
31
37
  // biome-ignore lint/performance/noReExportAll: intentional convenience barrel
32
38
  export * from "./tool-framework.ts";