@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
@@ -57,7 +57,7 @@ export function formatProjectServerStatusReason(
57
57
  case "process-crash-recovery-pending":
58
58
  return "process recovery in progress";
59
59
  case "process-crash-recovery-exhausted":
60
- return "process recovery exhausted; reload required";
60
+ return "process recovery exhausted; try another explicit health refresh";
61
61
  default:
62
62
  return null;
63
63
  }
@@ -11,6 +11,8 @@ export interface CalleesResult {
11
11
  enclosingScope: CalleeScope | null;
12
12
  callees: CalleeEntry[];
13
13
  confidence: "structural" | "unavailable";
14
+ /** Original provider reason when callee evidence is unavailable. */
15
+ message: string | null;
14
16
  depth: "direct" | "deep";
15
17
  }
16
18
 
@@ -30,7 +32,7 @@ export async function collectCallees(
30
32
  control?: CodeRequestControl,
31
33
  ): Promise<CalleesResult> {
32
34
  if (!deps.provider?.calleesAt) {
33
- return unavailableCallees(targetName);
35
+ return unavailableCallees(targetName, "No structural callee provider", depth);
34
36
  }
35
37
 
36
38
  const depthOrOptions = control ? { depth, control } : depth;
@@ -40,8 +42,8 @@ export async function collectCallees(
40
42
  targetCharacter,
41
43
  depthOrOptions,
42
44
  );
43
- if (result.kind !== "success" || !result.data) {
44
- return unavailableCallees(targetName);
45
+ if (result.kind !== "success") {
46
+ return unavailableCallees(targetName, result.message, depth);
45
47
  }
46
48
 
47
49
  void maxResults;
@@ -53,9 +55,10 @@ export async function collectCallees(
53
55
  );
54
56
  const callees: CalleeEntry[] = result.data.callees.map((c) => ({
55
57
  name: c.name,
56
- file: c.file ?? c.location ?? targetFile,
57
- line: c.startLine ?? targetLine,
58
- character: c.startCharacter ?? targetCharacter,
58
+ ...(c.displayName ? { displayName: c.displayName } : {}),
59
+ file: targetFile,
60
+ line: c.startLine,
61
+ character: c.startCharacter,
59
62
  }));
60
63
 
61
64
  return {
@@ -64,18 +67,24 @@ export async function collectCallees(
64
67
  enclosingScope,
65
68
  callees,
66
69
  confidence: "structural",
70
+ message: null,
67
71
  depth: result.data.depth ?? depth,
68
72
  };
69
73
  }
70
74
 
71
- function unavailableCallees(targetName: string | null): CalleesResult {
75
+ function unavailableCallees(
76
+ targetName: string | null,
77
+ message: string,
78
+ depth: "direct" | "deep",
79
+ ): CalleesResult {
72
80
  return {
73
81
  kind: "callees",
74
82
  targetName: targetName ?? "symbol",
75
83
  enclosingScope: null,
76
84
  callees: [],
77
85
  confidence: "unavailable",
78
- depth: "direct",
86
+ message,
87
+ depth,
79
88
  };
80
89
  }
81
90
 
@@ -73,8 +73,7 @@ export async function collectImplementations(
73
73
  implementations: project,
74
74
  externalCount: normalized.external.length,
75
75
  invalidLocationCount: normalized.invalidLocationCount,
76
- partialReason:
77
- normalized.partialReason ?? (result.kind === "partial" ? "provider-limited" : null),
76
+ partialReason: result.kind === "partial" ? "provider-limited" : normalized.partialReason,
78
77
  confidence: "semantic",
79
78
  };
80
79
  }
@@ -13,7 +13,7 @@ import {
13
13
  } from "./provider-locations.ts";
14
14
  import type { CallerEvidence, CallerReference, RelationsServiceDeps } from "./types.ts";
15
15
 
16
- export interface CallersResult {
16
+ interface CallerEvidenceData {
17
17
  kind: "callers";
18
18
  targetName: string;
19
19
  references: CallerReference[];
@@ -22,9 +22,12 @@ export interface CallersResult {
22
22
  invalidLocationCount: number;
23
23
  partialReason: RelationLocationPartialReason | null;
24
24
  evidence: CallerEvidence;
25
- confidence: "semantic" | "unavailable";
26
25
  }
27
26
 
27
+ /** Reference evidence, or the provider reason that prevented collection. */
28
+ export type CallersResult = CallerEvidenceData &
29
+ ({ confidence: "semantic" } | { confidence: "unavailable"; message: string });
30
+
28
31
  /**
29
32
  * Collect callers (references) for a target file/position using semantic provider.
30
33
  */
@@ -46,6 +49,7 @@ export async function collectCallers(
46
49
  partialReason: null,
47
50
  evidence: "semantic-references",
48
51
  confidence: "unavailable",
52
+ message: "No semantic references provider",
49
53
  };
50
54
  }
51
55
 
@@ -60,6 +64,7 @@ export async function collectCallers(
60
64
  partialReason: null,
61
65
  evidence: "semantic-references",
62
66
  confidence: "unavailable",
67
+ message: result.reason,
63
68
  };
64
69
  }
65
70
 
@@ -77,8 +82,7 @@ export async function collectCallers(
77
82
  references: inProject,
78
83
  externalCount: normalized.external.length,
79
84
  invalidLocationCount: normalized.invalidLocationCount,
80
- partialReason:
81
- normalized.partialReason ?? (result.kind === "partial" ? "provider-limited" : null),
85
+ partialReason: result.kind === "partial" ? "provider-limited" : normalized.partialReason,
82
86
  evidence: "semantic-references",
83
87
  confidence: "semantic",
84
88
  };
@@ -1,4 +1,4 @@
1
- import type { CodeRequestControl, SemanticProvider } from "@mrclrchtr/supi-code-runtime/api";
1
+ import type { SemanticProvider, StructuralProvider } from "@mrclrchtr/supi-code-runtime/api";
2
2
 
3
3
  /**
4
4
  * Shared types for the relations analysis modules.
@@ -32,13 +32,15 @@ export interface CalleeScope {
32
32
  /** One direct structural callee entry from an enclosing-scope lookup. */
33
33
  export interface CalleeEntry {
34
34
  name: string;
35
+ /** Syntax-based preview only; the full name remains the grouping key. */
36
+ displayName?: string;
35
37
  file: string;
36
38
  line: number;
37
39
  character: number;
38
40
  }
39
41
 
40
- /** Consumer-facing display entry for structural calls (no character). */
41
- export type CallEntry = Omit<CalleeEntry, "character">;
42
+ /** One structural call site with a 1-based UTF-16 coordinate. */
43
+ export type CallEntry = CalleeEntry;
42
44
 
43
45
  /** One implementation entry from a semantic implementation lookup. */
44
46
  export interface ImplementationEntry {
@@ -54,32 +56,6 @@ export interface RelationsServiceDeps {
54
56
  provider: {
55
57
  references?: SemanticProvider["references"];
56
58
  implementation?: SemanticProvider["implementation"];
57
- calleesAt?: (
58
- file: string,
59
- line: number,
60
- character: number,
61
- depthOrOptions?:
62
- | "direct"
63
- | "deep"
64
- | { depth?: "direct" | "deep"; control?: CodeRequestControl },
65
- ) => Promise<{
66
- kind: string;
67
- data?: {
68
- enclosingScope?: {
69
- name?: string;
70
- startLine?: number;
71
- endLine?: number;
72
- };
73
- callees: Array<{
74
- name: string;
75
- file?: string;
76
- location?: string;
77
- startLine?: number;
78
- startCharacter?: number;
79
- }>;
80
- depth?: "direct" | "deep";
81
- };
82
- message?: string;
83
- }>;
59
+ calleesAt?: StructuralProvider["calleesAt"];
84
60
  } | null;
85
61
  }
@@ -191,7 +191,9 @@ async function candidatesFromSymbols(
191
191
  ): Promise<{
192
192
  kind: "disambiguation";
193
193
  candidates: DisambiguationCandidateData[];
194
+ totalCount: number;
194
195
  omittedCount: number;
196
+ partialReason: null;
195
197
  }> {
196
198
  const candidates = await Promise.all(
197
199
  matched.map(async (s, idx) => {
@@ -215,7 +217,13 @@ async function candidatesFromSymbols(
215
217
  } satisfies DisambiguationCandidateData;
216
218
  }),
217
219
  );
218
- return { kind: "disambiguation", candidates, omittedCount: 0 };
220
+ return {
221
+ kind: "disambiguation",
222
+ candidates,
223
+ totalCount: candidates.length,
224
+ omittedCount: 0,
225
+ partialReason: null,
226
+ };
219
227
  }
220
228
 
221
229
  /** Layer 1: match the coordinate against LSP document symbols. Returns null to fall through. */
@@ -18,7 +18,11 @@ import { isWithinOrEqual } from "@mrclrchtr/supi-core/project";
18
18
  import type { TargetSymbolKind } from "../../session/target-input.ts";
19
19
  import type { AnchorKind } from "../../session/target-store.ts";
20
20
  import { normalizePath } from "../search/paths.ts";
21
- import type { DisambiguationCandidateData, TargetOutcome } from "./types.ts";
21
+ import type {
22
+ DisambiguationCandidateData,
23
+ TargetCandidateCompleteness,
24
+ TargetOutcome,
25
+ } from "./types.ts";
22
26
 
23
27
  const MAX_CANDIDATES = 8; // default fallback when maxResults is not provided
24
28
  const NON_EXPORTED_KINDS = new Set(["Variable", "Field", "Property"]);
@@ -146,6 +150,7 @@ export async function resolveSymbolTarget(
146
150
  return buildCandidateOutcome({
147
151
  kind: "kind-mismatch",
148
152
  candidates: eligible,
153
+ ...candidateCompleteness(result, eligible.length),
149
154
  semantic,
150
155
  cwd,
151
156
  maxResults: options.maxResults,
@@ -155,6 +160,7 @@ export async function resolveSymbolTarget(
155
160
  }
156
161
  return resolveCandidates({
157
162
  candidates: exactKind,
163
+ ...candidateCompleteness(result, exactKind.length),
158
164
  semantic,
159
165
  cwd,
160
166
  maxResults: options.maxResults,
@@ -164,6 +170,7 @@ export async function resolveSymbolTarget(
164
170
 
165
171
  return resolveCandidates({
166
172
  candidates: eligible,
173
+ ...candidateCompleteness(result, eligible.length),
167
174
  semantic,
168
175
  cwd,
169
176
  maxResults: options?.maxResults,
@@ -179,19 +186,15 @@ function normalizeProviderKind(kind: string): string {
179
186
  return kind.replace(/[\s_-]/g, "").toLowerCase();
180
187
  }
181
188
 
182
- async function resolveCandidates(options: {
183
- candidates: readonly CodeSymbol[];
184
- semantic: SemanticSubstrate;
185
- cwd: string;
186
- maxResults?: number;
187
- control?: CodeRequestControl;
188
- }): Promise<TargetOutcome> {
189
- const { candidates, semantic, cwd, maxResults, control } = options;
189
+ async function resolveCandidates(
190
+ options: CandidateOutcomeBase & Pick<TargetCandidateCompleteness, "totalCount" | "partialReason">,
191
+ ): Promise<TargetOutcome> {
192
+ const { candidates, semantic, cwd, maxResults, control, totalCount, partialReason } = options;
190
193
  const ranged = candidates.filter(
191
194
  (candidate) =>
192
195
  candidate.declarationAnchor.line > 0 || candidate.declarationAnchor.character > 0,
193
196
  );
194
- if (ranged.length === 1) {
197
+ if (ranged.length === 1 && totalCount !== null) {
195
198
  return resolvedTarget(await refineResolvedSymbolAnchor(ranged[0], semantic, control));
196
199
  }
197
200
  return buildCandidateOutcome({
@@ -200,6 +203,8 @@ async function resolveCandidates(options: {
200
203
  semantic,
201
204
  cwd,
202
205
  maxResults,
206
+ totalCount,
207
+ partialReason,
203
208
  control,
204
209
  });
205
210
  }
@@ -230,13 +235,24 @@ interface CandidateOutcomeBase {
230
235
  semantic: SemanticSubstrate;
231
236
  cwd: string;
232
237
  maxResults?: number;
238
+ control?: CodeRequestControl;
239
+ }
240
+
241
+ function candidateCompleteness(
242
+ result: Exclude<CodeQueryResult<CodeSymbol[]>, { kind: "unavailable" }>,
243
+ totalCount: number,
244
+ ): Pick<TargetCandidateCompleteness, "totalCount" | "partialReason"> {
245
+ return result.kind === "partial"
246
+ ? { totalCount: null, partialReason: "provider-limited" }
247
+ : { totalCount, partialReason: null };
233
248
  }
234
249
 
235
250
  type CandidateOutcomeOptions = CandidateOutcomeBase &
251
+ Pick<TargetCandidateCompleteness, "totalCount" | "partialReason"> &
236
252
  (
237
253
  | { kind: "disambiguation"; requestedKind?: never }
238
254
  | { kind: "kind-mismatch"; requestedKind: TargetSymbolKind }
239
- ) & { control?: CodeRequestControl };
255
+ );
240
256
 
241
257
  /**
242
258
  * Refine only bounded visible candidates to name anchors while retaining the
@@ -271,7 +287,9 @@ async function buildCandidateOutcome(
271
287
  );
272
288
  const common = {
273
289
  candidates: toDisambiguationCandidates(refined, options.cwd),
290
+ totalCount: options.totalCount,
274
291
  omittedCount: Math.max(0, options.candidates.length - cap),
292
+ partialReason: options.partialReason,
275
293
  };
276
294
  return options.kind === "kind-mismatch"
277
295
  ? {
@@ -136,6 +136,16 @@ export interface DisambiguationCandidateData {
136
136
  anchorKind: AnchorKind;
137
137
  }
138
138
 
139
+ /** Completeness metadata for a bounded candidate collection. */
140
+ export interface TargetCandidateCompleteness {
141
+ /** Exact candidate total, or null when the provider returned partial data. */
142
+ totalCount: number | null;
143
+ /** Candidates returned by the provider but hidden by the target cap. */
144
+ omittedCount: number;
145
+ /** Why the provider cannot establish an exact candidate total. */
146
+ partialReason: "provider-limited" | null;
147
+ }
148
+
139
149
  /**
140
150
  * Typed outcome of a resolution attempt.
141
151
  *
@@ -145,16 +155,14 @@ export interface DisambiguationCandidateData {
145
155
  export type TargetOutcome =
146
156
  | { kind: "resolved"; target: ResolvedTargetData }
147
157
  | { kind: "group"; group: ResolvedTargetGroupData }
148
- | {
158
+ | ({
149
159
  kind: "disambiguation";
150
160
  candidates: DisambiguationCandidateData[];
151
- omittedCount: number;
152
- }
153
- | {
161
+ } & TargetCandidateCompleteness)
162
+ | ({
154
163
  kind: "kind-mismatch";
155
164
  requestedKind: TargetSymbolKind;
156
165
  candidates: DisambiguationCandidateData[];
157
- omittedCount: number;
158
- }
166
+ } & TargetCandidateCompleteness)
159
167
  | { kind: "unavailable"; reason: string }
160
168
  | { kind: "error"; message: string };
package/src/api.ts CHANGED
@@ -61,6 +61,8 @@ export type {
61
61
  CodeIntelResultDetails,
62
62
  ContextDetails,
63
63
  DisambiguationCandidate,
64
+ GraphDetails,
65
+ GraphSectionDetails,
64
66
  HealthDetails,
65
67
  HealthSectionDetails,
66
68
  InspectDetails,
@@ -7,6 +7,7 @@ import {
7
7
  } from "@mrclrchtr/supi-code-runtime/api";
8
8
  import type { CodeProvider } from "../../analysis/provider.ts";
9
9
  import {
10
+ readNextAround,
10
11
  readNextEnclosingScope,
11
12
  readNextTarget,
12
13
  readNextTopSites,
@@ -75,7 +76,7 @@ async function collectReferences(options: CollectRelationOptions): Promise<Graph
75
76
  },
76
77
  });
77
78
  if (result.confidence === "unavailable") {
78
- return { kind: "unavailable", rel: "references", message: "References unavailable" };
79
+ return { kind: "unavailable", rel: "references", message: result.message };
79
80
  }
80
81
 
81
82
  const targetLine = options.position.line + 1;
@@ -132,7 +133,11 @@ async function collectCalleesRelation(options: CollectRelationOptions): Promise<
132
133
  options.requestControl,
133
134
  );
134
135
  if (result.confidence === "unavailable") {
135
- return { kind: "unavailable", rel: "callees", message: "Callees unavailable" };
136
+ return {
137
+ kind: "unavailable",
138
+ rel: "callees",
139
+ message: `${result.message ?? "Callees unavailable"}\nUse code_inspect at the target position to check the scope.`,
140
+ };
136
141
  }
137
142
 
138
143
  const enclosingScope = result.enclosingScope ?? {
@@ -143,8 +148,10 @@ async function collectCalleesRelation(options: CollectRelationOptions): Promise<
143
148
  };
144
149
  const calls: CallEntry[] = result.callees.map((callee) => ({
145
150
  name: callee.name,
151
+ ...(callee.displayName ? { displayName: callee.displayName } : {}),
146
152
  file: callee.file,
147
153
  line: callee.line,
154
+ character: callee.character,
148
155
  }));
149
156
  return {
150
157
  kind: "ok",
@@ -192,6 +199,14 @@ async function collectImplementationsRelation(
192
199
  invalidLocationCount: result.invalidLocationCount,
193
200
  partialReason: result.partialReason,
194
201
  },
195
- readNext: [],
202
+ readNext: result.implementations
203
+ .slice(0, Math.min(options.maxResults, 2))
204
+ .map((implementation) =>
205
+ readNextAround(
206
+ toDisplayPath(options.cwd, implementation.file),
207
+ implementation.line,
208
+ "inspect an implementation site",
209
+ ),
210
+ ),
196
211
  };
197
212
  }
@@ -4,12 +4,15 @@ import {
4
4
  } from "@mrclrchtr/supi-code-runtime/api";
5
5
  import {
6
6
  emptyProcessCrashRecoveryReport,
7
+ emptyStartupRetryReport,
8
+ type SemanticReadinessResult,
7
9
  type WorkspaceDiagnosticReport,
8
10
  type WorkspaceLspRuntime,
9
11
  } from "@mrclrchtr/supi-lsp/api";
10
12
  import { recoverDiagnosticRuntime } from "../analysis/health/recovery.ts";
11
13
  import { mergeDiagnosticEvidence } from "../diagnostics/evidence.ts";
12
14
  import { refreshFileLspMaintenance, refreshLspMaintenance } from "../substrate/lsp/maintenance.ts";
15
+ import type { LspMaintenanceState } from "../substrate/lsp/source-tracking.ts";
13
16
  import type {
14
17
  HealthDiagnosticScope,
15
18
  HealthFileReadiness,
@@ -21,13 +24,17 @@ interface HealthRefreshAttemptOptions {
21
24
  readonly diagnosticsScope: HealthDiagnosticScope;
22
25
  readonly attemptedAt: number;
23
26
  readonly cwd: string;
24
- readonly sentinelSnapshot: Map<string, number>;
27
+ readonly maintenanceState: LspMaintenanceState;
25
28
  readonly control?: CodeRequestControl;
26
29
  readonly reportRecoveryProgress?: () => void;
27
30
  }
28
31
 
29
32
  export interface HealthRefreshCollection {
30
33
  readonly attempt: HealthRefreshAttempt;
34
+ /** File readiness observed by the same explicit refresh call. */
35
+ readonly fileReadiness?: SemanticReadinessResult;
36
+ /** Next typed maintenance state, committed only after the attempt returns. */
37
+ readonly maintenanceState: LspMaintenanceState;
31
38
  /** Final report from the runtime, retained only for the current workflow. */
32
39
  readonly diagnosticReport?: WorkspaceDiagnosticReport;
33
40
  }
@@ -49,19 +56,20 @@ async function collectFileRefreshAttempt(
49
56
  const maintenance = await refreshFileLspMaintenance({
50
57
  runtime: options.runtime,
51
58
  cwd: options.cwd,
52
- sentinelSnapshot: options.sentinelSnapshot,
59
+ maintenanceState: options.maintenanceState,
53
60
  filePath: scope.path,
54
61
  control: options.control,
55
62
  });
56
- updateSentinelSnapshot(options.sentinelSnapshot, maintenance.snapshot);
57
63
  const readiness = await options.runtime.waitUntilReadyForFile(
58
64
  scope.path,
59
- undefined,
65
+ { retryFailedRoute: true },
60
66
  options.control,
61
67
  );
62
68
  const fileReadiness: HealthFileReadiness =
63
69
  readiness.kind === "ready" ? "ready" : readiness.kind === "timeout" ? "pending" : "unavailable";
64
70
  return {
71
+ fileReadiness: readiness,
72
+ maintenanceState: maintenance.maintenanceState,
65
73
  attempt: {
66
74
  kind: "completed",
67
75
  attemptedAt: options.attemptedAt,
@@ -72,6 +80,7 @@ async function collectFileRefreshAttempt(
72
80
  fileReadiness,
73
81
  restartedClients: 0,
74
82
  processCrashRecovery: readiness.processCrashRecovery ?? emptyProcessCrashRecoveryReport(),
83
+ startupRetry: readiness.startupRetry ?? emptyStartupRetryReport(),
75
84
  staleAssessment: {
76
85
  scope: "file",
77
86
  suspected: null,
@@ -90,16 +99,16 @@ async function collectWorkspaceRefreshAttempt(
90
99
  const maintenance = await refreshLspMaintenance(
91
100
  options.runtime,
92
101
  options.cwd,
93
- options.sentinelSnapshot,
102
+ options.maintenanceState,
94
103
  {
95
104
  control: options.control,
96
105
  scope: workspaceScope?.filter ?? null,
97
106
  trackSources: workspaceScope !== null,
98
107
  },
99
108
  );
100
- updateSentinelSnapshot(options.sentinelSnapshot, maintenance.snapshot);
101
109
  if (maintenance.failureReason) {
102
110
  return {
111
+ maintenanceState: maintenance.maintenanceState,
103
112
  attempt: {
104
113
  kind: "failed",
105
114
  attemptedAt: options.attemptedAt,
@@ -108,6 +117,8 @@ async function collectWorkspaceRefreshAttempt(
108
117
  operationScope: "workspace-runtime",
109
118
  diagnosticEvidence: maintenance.diagnosticEvidence,
110
119
  processCrashRecovery: emptyProcessCrashRecoveryReport(),
120
+ startupRetry: emptyStartupRetryReport(),
121
+ ...(maintenance.sourceTracking ? { sourceTracking: maintenance.sourceTracking } : {}),
111
122
  reason: maintenance.failureReason,
112
123
  },
113
124
  };
@@ -123,6 +134,7 @@ async function collectWorkspaceRefreshAttempt(
123
134
  // runtime then performs a fresh pass that includes the replacement.
124
135
  initialEvidence: maintenance.diagnosticEvidence,
125
136
  processCrashDemand: {
137
+ explicit: true,
126
138
  ...(workspaceScope?.filter ? { scopes: [workspaceScope.filter] } : {}),
127
139
  },
128
140
  });
@@ -133,6 +145,7 @@ async function collectWorkspaceRefreshAttempt(
133
145
  );
134
146
  if (recovery.refreshFailureReason) {
135
147
  return {
148
+ maintenanceState: maintenance.maintenanceState,
136
149
  attempt: {
137
150
  kind: "failed",
138
151
  attemptedAt: options.attemptedAt,
@@ -149,12 +162,15 @@ async function collectWorkspaceRefreshAttempt(
149
162
  },
150
163
  diagnosticEvidence,
151
164
  processCrashRecovery: recovery.processCrashRecovery,
165
+ startupRetry: recovery.startupRetry,
166
+ ...(maintenance.sourceTracking ? { sourceTracking: maintenance.sourceTracking } : {}),
152
167
  reason: recovery.refreshFailureReason,
153
168
  },
154
169
  diagnosticReport: recovery.diagnosticReport,
155
170
  };
156
171
  }
157
172
  return {
173
+ maintenanceState: maintenance.maintenanceState,
158
174
  attempt: {
159
175
  kind: "completed",
160
176
  attemptedAt: options.attemptedAt,
@@ -164,7 +180,9 @@ async function collectWorkspaceRefreshAttempt(
164
180
  attemptedActiveClients: recovery.attemptedClients,
165
181
  restartedClients: recovery.restartedClients,
166
182
  processCrashRecovery: recovery.processCrashRecovery,
183
+ startupRetry: recovery.startupRetry,
167
184
  diagnosticEvidence,
185
+ ...(maintenance.sourceTracking ? { sourceTracking: maintenance.sourceTracking } : {}),
168
186
  staleAssessment: {
169
187
  scope: "workspace",
170
188
  suspected: recovery.staleAssessment.suspected,
@@ -179,6 +197,7 @@ async function collectWorkspaceRefreshAttempt(
179
197
  // recorded failed attempt.
180
198
  if (isCodeRequestInterruption(error, options.control)) throw error;
181
199
  return {
200
+ maintenanceState: maintenance.maintenanceState,
182
201
  attempt: {
183
202
  kind: "failed",
184
203
  attemptedAt: options.attemptedAt,
@@ -187,17 +206,14 @@ async function collectWorkspaceRefreshAttempt(
187
206
  operationScope: "workspace-runtime",
188
207
  diagnosticEvidence: maintenance.diagnosticEvidence,
189
208
  processCrashRecovery: emptyProcessCrashRecoveryReport(),
209
+ startupRetry: emptyStartupRetryReport(),
210
+ ...(maintenance.sourceTracking ? { sourceTracking: maintenance.sourceTracking } : {}),
190
211
  reason: errorMessage(error),
191
212
  },
192
213
  };
193
214
  }
194
215
  }
195
216
 
196
- function updateSentinelSnapshot(target: Map<string, number>, next: Map<string, number>): void {
197
- target.clear();
198
- for (const [key, value] of next) target.set(key, value);
199
- }
200
-
201
217
  function errorMessage(error: unknown): string {
202
218
  return error instanceof Error && error.message ? error.message : "Diagnostic refresh failed.";
203
219
  }
@@ -4,8 +4,10 @@ import type {
4
4
  ProcessCrashRecoveryReport,
5
5
  ProjectServerInfo,
6
6
  ProjectServerStatusReason,
7
+ StartupRetryReport,
7
8
  } from "@mrclrchtr/supi-lsp/api";
8
9
  import type { CapabilityWarningReport } from "../analysis/capability/capability-warnings.ts";
10
+ import type { SourceTrackingReport } from "../substrate/lsp/source-tracking.ts";
9
11
 
10
12
  export type HealthSection = "diagnostics" | "servers";
11
13
 
@@ -62,7 +64,7 @@ export type HealthDiagnosticObservation =
62
64
  readonly scope: HealthDiagnosticScope;
63
65
  readonly entries: readonly HealthDiagnosticEntry[];
64
66
  readonly evidence: DiagnosticEvidenceSummary;
65
- /** Tsconfig scope decision for the requested file (file scope only). */
67
+ /** Tsconfig scope decision for an applicable TypeScript or JavaScript file. */
66
68
  readonly scopeStatus?: FileScopeDecision;
67
69
  }
68
70
  | {
@@ -71,7 +73,7 @@ export type HealthDiagnosticObservation =
71
73
  readonly entries: readonly HealthDiagnosticEntry[];
72
74
  readonly evidence: DiagnosticEvidenceSummary;
73
75
  readonly reason: string;
74
- /** Tsconfig scope decision for the requested file (file scope only). */
76
+ /** Tsconfig scope decision for an applicable TypeScript or JavaScript file. */
75
77
  readonly scopeStatus?: FileScopeDecision;
76
78
  }
77
79
  | {
@@ -80,7 +82,7 @@ export type HealthDiagnosticObservation =
80
82
  readonly entries: readonly HealthDiagnosticEntry[];
81
83
  readonly evidence: DiagnosticEvidenceSummary;
82
84
  readonly reason: string;
83
- /** Tsconfig scope decision for the requested file (file scope only). */
85
+ /** Tsconfig scope decision for an applicable TypeScript or JavaScript file. */
84
86
  readonly scopeStatus?: FileScopeDecision;
85
87
  };
86
88
 
@@ -92,6 +94,7 @@ export interface HealthStaleAssessment {
92
94
  readonly warning: string | null;
93
95
  }
94
96
 
97
+ /** Runtime scope of maintenance; it is not the diagnostic result scope. */
95
98
  export type HealthRefreshOperationScope = "file-runtime" | "workspace-runtime";
96
99
 
97
100
  /** Readiness result for a file-scoped maintenance attempt. */
@@ -113,10 +116,17 @@ interface CompletedHealthRefreshAttempt {
113
116
  readonly restartedClients: number;
114
117
  /** Separate outcome for process-crash route recovery. */
115
118
  readonly processCrashRecovery: ProcessCrashRecoveryReport;
119
+ /** Separate outcome for initial-start retries. */
120
+ readonly startupRetry?: StartupRetryReport;
116
121
  readonly staleAssessment: HealthStaleAssessment;
122
+ /**
123
+ * Source discovery and bounded tracking facts for broad refreshes. The
124
+ * report's skipped list describes automatic tracking policy, not capability.
125
+ */
126
+ readonly sourceTracking?: SourceTrackingReport;
117
127
  }
118
128
 
119
- /** A diagnostic refresh attempt against an explicit LSP runtime scope. */
129
+ /** A diagnostic refresh attempt with separate maintenance and result scopes. */
120
130
  export type HealthRefreshAttempt =
121
131
  | (CompletedHealthRefreshAttempt & {
122
132
  readonly operationScope: "workspace-runtime";
@@ -144,6 +154,13 @@ export type HealthRefreshAttempt =
144
154
  readonly diagnosticEvidence?: DiagnosticEvidenceSummary;
145
155
  /** Process-crash outcome, when the recovery pass returned one. */
146
156
  readonly processCrashRecovery?: ProcessCrashRecoveryReport;
157
+ /** Initial-start retry outcome, when the recovery pass returned one. */
158
+ readonly startupRetry?: StartupRetryReport;
159
+ /**
160
+ * Source discovery and bounded tracking facts for broad refreshes. The
161
+ * report's skipped list describes automatic tracking policy, not capability.
162
+ */
163
+ readonly sourceTracking?: SourceTrackingReport;
147
164
  /** File readiness outcome, when a file-scoped attempt failed. */
148
165
  readonly fileReadiness?: HealthFileReadiness;
149
166
  readonly reason: string;