@mrclrchtr/supi-code-intelligence 6.3.0 → 7.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 (136) hide show
  1. package/README.md +164 -82
  2. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
  4. package/node_modules/@mrclrchtr/supi-core/README.md +8 -0
  5. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  6. package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
  7. package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
  8. package/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
  9. package/node_modules/@mrclrchtr/supi-lsp/README.md +35 -13
  10. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  11. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
  12. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +8 -0
  13. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  14. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
  15. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
  16. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
  17. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +3 -1
  18. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +3 -1
  19. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +1 -1
  20. package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
  21. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +16 -0
  22. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +43 -35
  23. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +83 -17
  24. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +108 -92
  25. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +44 -82
  26. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +3 -4
  27. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +9 -68
  28. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +45 -58
  29. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +230 -331
  30. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +201 -1
  31. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +129 -70
  32. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-typescript.ts +395 -0
  33. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +8 -12
  34. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +685 -183
  35. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +2 -2
  36. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +60 -60
  37. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-request-enrollment.ts +75 -0
  38. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-barrier.ts +443 -0
  39. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-errors.ts +30 -0
  40. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-file.ts +15 -0
  41. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +290 -37
  42. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +72 -16
  43. package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +1 -0
  44. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +30 -4
  45. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +1 -1
  46. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +4 -32
  47. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sources.ts +108 -0
  48. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
  49. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
  50. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
  51. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +770 -56
  52. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
  53. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
  54. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +48 -12
  55. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +50 -4
  56. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
  62. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
  63. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
  68. package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
  69. package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
  70. package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
  71. package/node_modules/yaml/dist/nodes/Alias.js +25 -23
  72. package/node_modules/yaml/package.json +1 -1
  73. package/package.json +5 -5
  74. package/src/analysis/health/diagnostics.ts +1 -2
  75. package/src/analysis/health/file-scope.ts +4 -1
  76. package/src/analysis/health/server-status.ts +1 -1
  77. package/src/analysis/relations/callees.ts +17 -8
  78. package/src/analysis/relations/implementations.ts +1 -2
  79. package/src/analysis/relations/references.ts +8 -4
  80. package/src/analysis/relations/types.ts +6 -30
  81. package/src/analysis/target/anchored.ts +9 -1
  82. package/src/analysis/target/symbol.ts +29 -11
  83. package/src/analysis/target/types.ts +14 -6
  84. package/src/api.ts +2 -0
  85. package/src/session/graph/collect.ts +18 -3
  86. package/src/session/health-refresh.ts +27 -11
  87. package/src/session/health-types.ts +21 -4
  88. package/src/session/health-workflow.ts +28 -22
  89. package/src/session/orientation-types.ts +5 -6
  90. package/src/session/orientation-workflow.ts +10 -2
  91. package/src/session/session.ts +12 -5
  92. package/src/session/target-workflow.ts +9 -6
  93. package/src/substrate/lsp/maintenance.ts +53 -47
  94. package/src/substrate/lsp/source-tracking.ts +252 -0
  95. package/src/tool/code_find/guidance.ts +3 -3
  96. package/src/tool/code_find/spec.ts +5 -4
  97. package/src/tool/code_find/tui.ts +7 -1
  98. package/src/tool/code_graph/details.ts +32 -0
  99. package/src/tool/code_graph/display.ts +54 -0
  100. package/src/tool/code_graph/format.ts +60 -0
  101. package/src/tool/code_graph/guidance.ts +2 -2
  102. package/src/tool/code_graph/markdown.ts +76 -198
  103. package/src/tool/code_graph/read-next.ts +65 -0
  104. package/src/tool/code_graph/result.ts +118 -83
  105. package/src/tool/code_graph/tui.ts +216 -54
  106. package/src/tool/code_health/guidance.ts +3 -5
  107. package/src/tool/code_health/markdown.ts +90 -57
  108. package/src/tool/code_health/refresh-outcome.ts +51 -0
  109. package/src/tool/code_health/refresh-status.ts +130 -43
  110. package/src/tool/code_health/scope.ts +78 -0
  111. package/src/tool/code_health/spec.ts +1 -1
  112. package/src/tool/code_health/tui.ts +29 -38
  113. package/src/tool/code_inspect/guidance.ts +5 -3
  114. package/src/tool/code_inspect/tui.ts +7 -1
  115. package/src/tool/code_orientation/guidance.ts +3 -4
  116. package/src/tool/code_orientation/result.ts +31 -8
  117. package/src/tool/code_orientation/tui.ts +26 -5
  118. package/src/tool/code_refactor_apply/guidance.ts +1 -1
  119. package/src/tool/code_refactor_apply/tui.ts +7 -3
  120. package/src/tool/code_refactor_plan/guidance.ts +3 -5
  121. package/src/tool/code_refactor_plan/tui.ts +7 -1
  122. package/src/tool/code_resolve/guidance.ts +3 -5
  123. package/src/tool/code_resolve/result.ts +13 -10
  124. package/src/tool/code_resolve/tui.ts +18 -1
  125. package/src/tool/guidance.ts +11 -9
  126. package/src/tool/infra/truncate.ts +39 -13
  127. package/src/tool/result/candidate-row.ts +27 -0
  128. package/src/tool/result/errors.ts +21 -6
  129. package/src/tool/result/types.ts +7 -3
  130. package/src/tool/schemas.ts +5 -0
  131. package/src/types/execution.ts +2 -0
  132. package/src/types/index.ts +2 -0
  133. package/src/ui/tui/candidate-legacy-body.ts +69 -0
  134. package/src/ui/tui/candidate-selection.ts +366 -0
  135. package/src/ui/tui/common.ts +26 -31
  136. package/src/ui/tui/execution-error.ts +92 -0
@@ -4,6 +4,7 @@ import type { CapabilityState } from "@mrclrchtr/supi-code-runtime/api";
4
4
  import { isCodeRequestInterruption } from "@mrclrchtr/supi-code-runtime/api";
5
5
  import type {
6
6
  LspRuntimeController,
7
+ SemanticReadinessResult,
7
8
  WorkspaceDiagnosticReport,
8
9
  WorkspaceLspRuntime,
9
10
  WorkspaceLspRuntimeState,
@@ -21,6 +22,7 @@ import { describeStructuralState } from "../analysis/health/recovery.ts";
21
22
  import { collectServers } from "../analysis/health/signals.ts";
22
23
  import { SEMANTIC_READINESS_TIMEOUT_REASON } from "../analysis/readiness.ts";
23
24
  import { resolveScope } from "../analysis/search/paths.ts";
25
+ import type { LspMaintenanceState } from "../substrate/lsp/source-tracking.ts";
24
26
  import type { CapabilityAdapter } from "./capability-adapter.ts";
25
27
  import { collectHealthRefreshAttempt } from "./health-refresh.ts";
26
28
  import type {
@@ -45,8 +47,10 @@ export interface HealthWorkflowDeps {
45
47
  readonly lspController: LspRuntimeController | null;
46
48
  readonly lastRefreshAttempt: HealthRefreshAttempt | null;
47
49
  readonly trackRefreshAttempt: (attempt: HealthRefreshAttempt) => void;
48
- /** Workspace sentinel snapshot for change detection. */
49
- readonly sentinelSnapshot: Map<string, number>;
50
+ /** Typed sentinel, source-baseline, and created-source queue state. */
51
+ readonly maintenanceState: LspMaintenanceState;
52
+ /** Commit maintenance state only after a health attempt completes. */
53
+ readonly updateMaintenanceState: (state: LspMaintenanceState) => void;
50
54
  }
51
55
 
52
56
  /** Collect health facts without rendering a public result. */
@@ -77,7 +81,6 @@ export async function runHealthWorkflow(
77
81
  const diagnosticsScope = diagnosticScope(scopeFilter);
78
82
  const refreshCollection = await collectRefreshState({
79
83
  refreshRequested: request.refresh === true,
80
- diagnosticsRequested: included.includes("diagnostics"),
81
84
  runtime,
82
85
  lspState,
83
86
  diagnosticsScope,
@@ -91,6 +94,7 @@ export async function runHealthWorkflow(
91
94
  scopeFilter,
92
95
  lspState,
93
96
  capabilityState: capabilityStates.semantic,
97
+ refreshReadiness: refreshCollection.fileReadiness,
94
98
  control,
95
99
  });
96
100
  const semanticReady = semanticState?.kind === "ready";
@@ -143,6 +147,8 @@ interface SemanticHealthStateOptions {
143
147
  requested: boolean;
144
148
  /** Whether this call requests file-routed evidence rather than passive inventory. */
145
149
  fileReadinessRequested: boolean;
150
+ /** Readiness observed by the explicit refresh phase, when one ran. */
151
+ refreshReadiness?: SemanticReadinessResult;
146
152
  runtime: WorkspaceLspRuntime | null;
147
153
  scopeFilter: string | null;
148
154
  lspState: WorkspaceLspRuntimeState;
@@ -154,12 +160,17 @@ async function establishSemanticHealthState(
154
160
  options: SemanticHealthStateOptions,
155
161
  ): Promise<SemanticHealthState | null> {
156
162
  if (!options.requested) return null;
157
- if (options.fileReadinessRequested && options.runtime && isScopedFile(options.scopeFilter)) {
158
- const readiness = await options.runtime.waitUntilReadyForFile(
159
- options.scopeFilter,
160
- undefined,
161
- options.control,
162
- );
163
+ if (options.fileReadinessRequested && isScopedFile(options.scopeFilter)) {
164
+ const readiness =
165
+ options.refreshReadiness ??
166
+ (options.runtime
167
+ ? await options.runtime.waitUntilReadyForFile(
168
+ options.scopeFilter,
169
+ undefined,
170
+ options.control,
171
+ )
172
+ : null);
173
+ if (!readiness) return deriveNonReadySemanticState(options.lspState, options.capabilityState);
163
174
  if (readiness.kind === "ready") return { kind: "ready" };
164
175
  if (readiness.kind === "timeout") {
165
176
  return { kind: "pending", reason: SEMANTIC_READINESS_TIMEOUT_REASON };
@@ -223,12 +234,12 @@ function collectCapabilityWarnings(
223
234
 
224
235
  interface HealthRefreshStateCollection {
225
236
  readonly attempt: HealthRefreshState;
237
+ readonly fileReadiness?: SemanticReadinessResult;
226
238
  readonly diagnosticReport?: WorkspaceDiagnosticReport;
227
239
  }
228
240
 
229
241
  interface RefreshStateOptions {
230
242
  readonly refreshRequested: boolean;
231
- readonly diagnosticsRequested: boolean;
232
243
  readonly runtime: WorkspaceLspRuntime | null;
233
244
  readonly lspState: WorkspaceLspRuntimeState;
234
245
  readonly diagnosticsScope: HealthDiagnosticScope;
@@ -240,16 +251,7 @@ interface RefreshStateOptions {
240
251
  async function collectRefreshState(
241
252
  options: RefreshStateOptions,
242
253
  ): Promise<HealthRefreshStateCollection> {
243
- const { deps, diagnosticsRequested, diagnosticsScope, lspState, runtime } = options;
244
- if (!diagnosticsRequested) {
245
- return {
246
- attempt: {
247
- kind: "not-requested",
248
- reason: "Diagnostics were not requested.",
249
- lastAttempt: deps.lastRefreshAttempt,
250
- },
251
- };
252
- }
254
+ const { deps, diagnosticsScope, lspState, runtime } = options;
253
255
  if (!options.refreshRequested) {
254
256
  return {
255
257
  attempt: {
@@ -282,7 +284,7 @@ async function collectRefreshState(
282
284
  diagnosticsScope,
283
285
  attemptedAt,
284
286
  cwd: options.deps.cwd,
285
- sentinelSnapshot: options.deps.sentinelSnapshot,
287
+ maintenanceState: options.deps.maintenanceState,
286
288
  control: options.control,
287
289
  reportRecoveryProgress: () =>
288
290
  reportProgress(options.control, {
@@ -291,8 +293,12 @@ async function collectRefreshState(
291
293
  message: "Refreshing diagnostics and recovery state",
292
294
  }),
293
295
  });
296
+ deps.updateMaintenanceState(attempt.maintenanceState);
294
297
  deps.trackRefreshAttempt(attempt.attempt);
295
- return attempt;
298
+ return {
299
+ ...attempt,
300
+ ...(attempt.fileReadiness ? { fileReadiness: attempt.fileReadiness } : {}),
301
+ };
296
302
  } catch (error) {
297
303
  // Cancellation must propagate; a cancelled caller no longer awaits a
298
304
  // recorded failed attempt or the refresh data it would carry.
@@ -5,6 +5,7 @@ import type { EvidenceListMetadata } from "../analysis/evidence.ts";
5
5
  import type { InstructionFilesMetadata } from "../analysis/instruction-files.ts";
6
6
  import type { CodeProvider } from "../analysis/provider.ts";
7
7
  import type { ReadNextItem } from "../analysis/read-next.ts";
8
+ import type { TargetCandidateCompleteness } from "../analysis/target/types.ts";
8
9
  import type { OrientationTargetInput, TargetSymbolKind } from "./target-input.ts";
9
10
  import type { AnchorKind, TargetStoreEntry } from "./target-store.ts";
10
11
 
@@ -118,16 +119,14 @@ export interface OrientationResultData {
118
119
 
119
120
  export type OrientationWorkflowOutcome =
120
121
  | { readonly kind: "completed"; readonly data: OrientationResultData }
121
- | {
122
+ | ({
122
123
  readonly kind: "disambiguation";
123
124
  readonly candidates: readonly OrientationCandidate[];
124
- readonly omittedCount: number;
125
- }
126
- | {
125
+ } & TargetCandidateCompleteness)
126
+ | ({
127
127
  readonly kind: "kind-mismatch";
128
128
  readonly requestedKind: TargetSymbolKind;
129
129
  readonly candidates: readonly OrientationCandidate[];
130
- readonly omittedCount: number;
131
- }
130
+ } & TargetCandidateCompleteness)
132
131
  | { readonly kind: "invalid-input"; readonly message: string }
133
132
  | { readonly kind: "unavailable"; readonly reason: string };
@@ -131,10 +131,18 @@ async function orientTarget(options: {
131
131
  ? {
132
132
  kind: "kind-mismatch",
133
133
  requestedKind: target.requestedKind,
134
- omittedCount: target.omittedCount,
135
134
  candidates,
135
+ totalCount: target.totalCount,
136
+ omittedCount: target.omittedCount,
137
+ partialReason: target.partialReason,
136
138
  }
137
- : { kind: "disambiguation", omittedCount: target.omittedCount, candidates };
139
+ : {
140
+ kind: "disambiguation",
141
+ candidates,
142
+ totalCount: target.totalCount,
143
+ omittedCount: target.omittedCount,
144
+ partialReason: target.partialReason,
145
+ };
138
146
  }
139
147
  if (target.kind !== "resolved") return target;
140
148
  throwIfAborted(control);
@@ -20,6 +20,10 @@ import { realpathSync } from "node:fs";
20
20
  import * as path from "node:path";
21
21
  import type { SessionEntry } from "@earendil-works/pi-coding-agent";
22
22
  import type { LspRuntimeController } from "@mrclrchtr/supi-lsp/api";
23
+ import {
24
+ createLspMaintenanceState,
25
+ type LspMaintenanceState,
26
+ } from "../substrate/lsp/source-tracking.ts";
23
27
  import { type CapabilityAdapter, WorkspaceCapabilityAdapter } from "./capability-adapter.ts";
24
28
  import type { FindWorkflowInput, FindWorkflowOutcome } from "./find-types.ts";
25
29
  import { runFindWorkflow } from "./find-workflow.ts";
@@ -162,8 +166,8 @@ export class WorkspaceCodeIntelligenceSession {
162
166
  */
163
167
  #lspController: LspRuntimeController | null = null;
164
168
 
165
- /** Workspace sentinel snapshot for change detection across explicit queries. */
166
- #sentinelSnapshot: Map<string, number> = new Map();
169
+ /** Typed sentinel, source-baseline, and created-source queue state. */
170
+ #maintenanceState: LspMaintenanceState = createLspMaintenanceState();
167
171
 
168
172
  /** Whether the project owning this session is trusted for config loading. */
169
173
  #projectTrusted = false;
@@ -196,7 +200,7 @@ export class WorkspaceCodeIntelligenceSession {
196
200
  * Called by the extension entry point after LSP initialization.
197
201
  */
198
202
  seedSentinelSnapshot(snapshot: Map<string, number>): void {
199
- this.#sentinelSnapshot = snapshot;
203
+ this.#maintenanceState = createLspMaintenanceState(snapshot);
200
204
  }
201
205
 
202
206
  /** Restore overview state from the active session branch. */
@@ -283,7 +287,10 @@ export class WorkspaceCodeIntelligenceSession {
283
287
  trackRefreshAttempt: (attempt) => {
284
288
  this.#lastHealthRefreshAttempt = attempt;
285
289
  },
286
- sentinelSnapshot: this.#sentinelSnapshot,
290
+ maintenanceState: this.#maintenanceState,
291
+ updateMaintenanceState: (state) => {
292
+ this.#maintenanceState = state;
293
+ },
287
294
  },
288
295
  control,
289
296
  );
@@ -429,7 +436,7 @@ export class WorkspaceCodeIntelligenceSession {
429
436
  this.#workflowTargets.clear();
430
437
  this.#surfacedInstructionDirs.clear();
431
438
  this.#nativeInstructionPaths.clear();
432
- this.#sentinelSnapshot.clear();
439
+ this.#maintenanceState = createLspMaintenanceState();
433
440
  this.#lspController = null;
434
441
  this.#projectTrusted = false;
435
442
  }
@@ -19,6 +19,7 @@ import { resolveSymbolTarget } from "../analysis/target/symbol.ts";
19
19
  import type {
20
20
  ResolvedTargetData,
21
21
  ResolvedTargetGroupData,
22
+ TargetCandidateCompleteness,
22
23
  TargetOutcome,
23
24
  } from "../analysis/target/types.ts";
24
25
  import type { CapabilityAdapter } from "./capability-adapter.ts";
@@ -59,17 +60,15 @@ export type TargetWorkflowOutcome =
59
60
  omittedCount: number;
60
61
  unknownNestingCount: number;
61
62
  }
62
- | {
63
+ | ({
63
64
  kind: "disambiguation";
64
65
  candidates: ReadonlyArray<TargetWorkflowCandidate>;
65
- omittedCount: number;
66
- }
67
- | {
66
+ } & TargetCandidateCompleteness)
67
+ | ({
68
68
  kind: "kind-mismatch";
69
69
  requestedKind: TargetSymbolKind;
70
70
  candidates: ReadonlyArray<TargetWorkflowCandidate>;
71
- omittedCount: number;
72
- }
71
+ } & TargetCandidateCompleteness)
73
72
  | { kind: "invalid-input"; message: string }
74
73
  | { kind: "unavailable"; reason: string };
75
74
 
@@ -318,12 +317,16 @@ function toWorkflowOutcome(
318
317
  kind: "kind-mismatch",
319
318
  requestedKind: outcome.requestedKind,
320
319
  candidates,
320
+ totalCount: outcome.totalCount,
321
321
  omittedCount: outcome.omittedCount,
322
+ partialReason: outcome.partialReason,
322
323
  }
323
324
  : {
324
325
  kind: "disambiguation",
325
326
  candidates,
327
+ totalCount: outcome.totalCount,
326
328
  omittedCount: outcome.omittedCount,
329
+ partialReason: outcome.partialReason,
327
330
  };
328
331
  }
329
332
 
@@ -13,7 +13,6 @@ import {
13
13
  isCodeRequestInterruption,
14
14
  throwIfCodeRequestInterrupted,
15
15
  } from "@mrclrchtr/supi-code-runtime/api";
16
- import { isWithinOrEqual } from "@mrclrchtr/supi-core/api";
17
16
  import { uriToFile } from "@mrclrchtr/supi-core/path";
18
17
  import type { DiagnosticEvidenceSummary } from "@mrclrchtr/supi-lsp/api";
19
18
  import {
@@ -27,6 +26,12 @@ import {
27
26
  type WorkspaceLspRuntime,
28
27
  } from "@mrclrchtr/supi-lsp/api";
29
28
  import { mergeDiagnosticEvidence } from "../../diagnostics/evidence.ts";
29
+ import {
30
+ type LspMaintenanceState,
31
+ type SourceTrackingReport,
32
+ trackCreatedSources,
33
+ withSentinelSnapshot,
34
+ } from "./source-tracking.ts";
30
35
 
31
36
  /** Options shared by the workspace and file-scoped maintenance passes. */
32
37
  export interface LspMaintenanceOptions {
@@ -36,11 +41,7 @@ export interface LspMaintenanceOptions {
36
41
  * tracks created files inside this prefix; null disables the scope bound.
37
42
  */
38
43
  scope?: string | null;
39
- /**
40
- * Track files created since the last refresh so the following refresh pull
41
- * includes them. Only the workspace-runtime path sets this; the snapshot is
42
- * still widened on file-scoped passes so later diffs stay correct.
43
- */
44
+ /** Track source additions only for a broad tracked-file refresh. */
44
45
  trackSources?: boolean;
45
46
  }
46
47
 
@@ -52,10 +53,11 @@ export interface LspMaintenanceOptions {
52
53
  export async function refreshLspMaintenance(
53
54
  runtime: WorkspaceLspRuntime,
54
55
  cwd: string,
55
- sentinelSnapshot: Map<string, number>,
56
+ maintenanceState: LspMaintenanceState,
56
57
  options: LspMaintenanceOptions = {},
57
58
  ): Promise<WorkspaceLspMaintenanceResult> {
58
- const { snapshot } = await synchronizeSentinels(runtime, sentinelSnapshot, options);
59
+ const synchronized = await synchronizeSentinels(runtime, cwd, maintenanceState, options);
60
+ const { snapshot, nextState, sourceTracking } = synchronized;
59
61
  let diagnosticEvidence = emptyEvidence();
60
62
  let failureReason: string | undefined;
61
63
 
@@ -80,15 +82,21 @@ export async function refreshLspMaintenance(
80
82
 
81
83
  return {
82
84
  snapshot,
85
+ maintenanceState: nextState,
83
86
  diagnosticEvidence,
87
+ ...(sourceTracking ? { sourceTracking } : {}),
84
88
  ...(failureReason ? { failureReason } : {}),
85
89
  };
86
90
  }
87
91
 
88
92
  /** Sentinel state and diagnostic evidence retained from one workspace pass. */
89
93
  export interface WorkspaceLspMaintenanceResult {
94
+ /** The next typed maintenance state for the session. */
95
+ maintenanceState: LspMaintenanceState;
90
96
  /** The next sentinel snapshot for the session-owned maintenance state. */
91
97
  snapshot: Map<string, number>;
98
+ /** Source discovery and bounded tracking facts for broad refreshes. */
99
+ sourceTracking?: SourceTrackingReport;
92
100
  /** Evidence from every diagnostic refresh performed by this pass. */
93
101
  diagnosticEvidence: DiagnosticEvidenceSummary;
94
102
  /** Failure from the first workspace refresh, when no fresh pass completed. */
@@ -97,6 +105,8 @@ export interface WorkspaceLspMaintenanceResult {
97
105
 
98
106
  /** Sentinel state and stale-file facts from one file-scoped pass. */
99
107
  export interface FileLspMaintenanceResult {
108
+ /** The next typed maintenance state for the session. */
109
+ maintenanceState: LspMaintenanceState;
100
110
  /** The next sentinel snapshot for the session-owned maintenance state. */
101
111
  snapshot: Map<string, number>;
102
112
  /** Number of stale files that matched the requested file. */
@@ -107,14 +117,14 @@ export interface FileLspMaintenanceResult {
107
117
  export async function refreshFileLspMaintenance(options: {
108
118
  runtime: WorkspaceLspRuntime;
109
119
  cwd: string;
110
- sentinelSnapshot: Map<string, number>;
120
+ maintenanceState: LspMaintenanceState;
111
121
  filePath: string;
112
122
  control?: CodeRequestControl;
113
123
  }): Promise<FileLspMaintenanceResult> {
114
- const { runtime, cwd, sentinelSnapshot, filePath, control } = options;
124
+ const { runtime, cwd, maintenanceState, filePath, control } = options;
115
125
  // A cancelled caller stops before any maintenance work starts.
116
126
  throwIfCodeRequestInterrupted(control);
117
- const { snapshot } = await synchronizeSentinels(runtime, sentinelSnapshot);
127
+ const { snapshot, nextState } = await synchronizeSentinels(runtime, cwd, maintenanceState);
118
128
  const target = nodePath.resolve(filePath);
119
129
  const stale = confirmedOutstandingDiagnostics(runtime, cwd).some(
120
130
  (entry) =>
@@ -132,38 +142,51 @@ export async function refreshFileLspMaintenance(options: {
132
142
  runtime.pruneMissingFiles();
133
143
  throwIfCodeRequestInterrupted(control);
134
144
 
135
- return { snapshot, matchedStaleFileCount: stale ? 1 : 0 };
145
+ return {
146
+ snapshot,
147
+ maintenanceState: nextState,
148
+ matchedStaleFileCount: stale ? 1 : 0,
149
+ };
136
150
  }
137
151
 
138
152
  /**
139
- * Sync the widened workspace snapshot, forward sentinel changes, and track
140
- * source files newly created since the last pass.
153
+ * Sync sentinel state and, only for broad refreshes, process source additions.
141
154
  *
142
- * The snapshot is widened to every regular file on every pass so later diffs
143
- * see genuine creations. Only sentinel-file events are forwarded as workspace
144
- * changes (config semantics unchanged); created source files are tracked only
145
- * on the workspace-runtime path and only once the snapshot was primed by an
146
- * earlier pass — the first pass establishes the baseline and cannot tell a
147
- * just-created file from a long-existing one.
155
+ * Sentinel state and source state use separate baselines. A complete source
156
+ * inventory establishes the first baseline without treating existing files as
157
+ * created. Limited inventories leave the previous source state unchanged.
148
158
  */
149
159
  async function synchronizeSentinels(
150
160
  runtime: WorkspaceLspRuntime,
151
- sentinelSnapshot: Map<string, number>,
161
+ cwd: string,
162
+ maintenanceState: LspMaintenanceState,
152
163
  options: LspMaintenanceOptions = {},
153
- ) {
154
- const primed = sentinelSnapshot.size > 0;
155
- const state = runtime.syncWorkspaceSentinelSnapshot(sentinelSnapshot, {
156
- includeSourceFiles: true,
157
- });
158
-
164
+ ): Promise<{
165
+ snapshot: Map<string, number>;
166
+ nextState: LspMaintenanceState;
167
+ sourceTracking?: SourceTrackingReport;
168
+ }> {
169
+ const state = runtime.syncWorkspaceSentinelSnapshot(maintenanceState.sentinelSnapshot);
159
170
  invalidateChangedProjectConfigs(state.changes);
160
171
  if (state.changes.length > 0) runtime.noteWorkspaceChanges(state.changes);
161
172
 
162
- if (options.trackSources && primed) {
163
- await trackCreatedSourceFiles(runtime, state.sourceChanges, options.scope);
173
+ const nextSentinelState = withSentinelSnapshot(maintenanceState, state.snapshot);
174
+ if (!options.trackSources) {
175
+ return { snapshot: state.snapshot, nextState: nextSentinelState };
164
176
  }
165
177
 
166
- return state;
178
+ const sourceResult = await trackCreatedSources({
179
+ runtime,
180
+ cwd,
181
+ state: nextSentinelState,
182
+ scope: options.scope,
183
+ control: options.control,
184
+ });
185
+ return {
186
+ snapshot: state.snapshot,
187
+ nextState: sourceResult.state,
188
+ sourceTracking: sourceResult.report,
189
+ };
167
190
  }
168
191
 
169
192
  /** Invalidate cached tsconfig scope parses only for config files that changed. */
@@ -181,23 +204,6 @@ function invalidateChangedProjectConfigs(changes: readonly FileEvent[]): void {
181
204
  }
182
205
  }
183
206
 
184
- /** Track source files newly created since the last pass, within the scope bound. */
185
- async function trackCreatedSourceFiles(
186
- runtime: WorkspaceLspRuntime,
187
- sourceChanges: readonly FileEvent[],
188
- scope: string | null | undefined,
189
- ): Promise<void> {
190
- for (const change of sourceChanges) {
191
- if (change.type !== FileChangeType.Created) continue;
192
- const filePath = uriToFile(change.uri);
193
- if (scope && !isWithinOrEqual(scope, filePath)) continue;
194
- if (!runtime.isSupportedSourceFile(filePath)) continue;
195
- // Best-effort: a file no client can serve stays untracked and is
196
- // simply absent from evidence until a later explicit request.
197
- await runtime.trackFile(filePath);
198
- }
199
- }
200
-
201
207
  /** Return only diagnostics whose document evidence is confirmed. */
202
208
  function confirmedOutstandingDiagnostics(runtime: WorkspaceLspRuntime, cwd: string) {
203
209
  const outstanding = runtime.getOutstandingDiagnostics(1);