@mrclrchtr/supi-code-intelligence 6.4.0 → 7.0.1

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 (129) 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 +6 -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/manager/manager-diagnostics.ts +10 -4
  47. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
  48. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
  49. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +608 -45
  50. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
  51. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
  52. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -11
  53. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +19 -3
  54. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  55. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
  56. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
  62. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
  63. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
  66. package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
  67. package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
  68. package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
  69. package/node_modules/yaml/dist/nodes/Alias.js +25 -23
  70. package/node_modules/yaml/package.json +1 -1
  71. package/package.json +5 -5
  72. package/src/analysis/health/diagnostics.ts +1 -2
  73. package/src/analysis/health/file-scope.ts +4 -1
  74. package/src/analysis/health/server-status.ts +1 -1
  75. package/src/analysis/relations/callees.ts +17 -8
  76. package/src/analysis/relations/implementations.ts +1 -2
  77. package/src/analysis/relations/references.ts +8 -4
  78. package/src/analysis/relations/types.ts +6 -30
  79. package/src/analysis/target/anchored.ts +9 -1
  80. package/src/analysis/target/symbol.ts +29 -11
  81. package/src/analysis/target/types.ts +14 -6
  82. package/src/api.ts +2 -0
  83. package/src/session/graph/collect.ts +18 -3
  84. package/src/session/health-refresh.ts +12 -1
  85. package/src/session/health-types.ts +18 -6
  86. package/src/session/health-workflow.ts +21 -19
  87. package/src/session/orientation-types.ts +5 -6
  88. package/src/session/orientation-workflow.ts +10 -2
  89. package/src/session/target-workflow.ts +9 -6
  90. package/src/substrate/lsp/source-tracking.ts +11 -6
  91. package/src/tool/code_find/guidance.ts +2 -2
  92. package/src/tool/code_find/spec.ts +5 -4
  93. package/src/tool/code_find/tui.ts +7 -1
  94. package/src/tool/code_graph/details.ts +32 -0
  95. package/src/tool/code_graph/display.ts +54 -0
  96. package/src/tool/code_graph/format.ts +60 -0
  97. package/src/tool/code_graph/guidance.ts +1 -1
  98. package/src/tool/code_graph/markdown.ts +76 -198
  99. package/src/tool/code_graph/read-next.ts +65 -0
  100. package/src/tool/code_graph/result.ts +118 -83
  101. package/src/tool/code_graph/tui.ts +216 -54
  102. package/src/tool/code_health/guidance.ts +2 -4
  103. package/src/tool/code_health/markdown.ts +75 -58
  104. package/src/tool/code_health/refresh-outcome.ts +41 -1
  105. package/src/tool/code_health/refresh-status.ts +114 -48
  106. package/src/tool/code_health/scope.ts +78 -0
  107. package/src/tool/code_health/spec.ts +1 -1
  108. package/src/tool/code_health/tui.ts +25 -38
  109. package/src/tool/code_inspect/tui.ts +7 -1
  110. package/src/tool/code_orientation/result.ts +31 -8
  111. package/src/tool/code_orientation/tui.ts +26 -5
  112. package/src/tool/code_refactor_apply/guidance.ts +1 -1
  113. package/src/tool/code_refactor_apply/tui.ts +7 -3
  114. package/src/tool/code_refactor_plan/guidance.ts +1 -1
  115. package/src/tool/code_refactor_plan/tui.ts +7 -1
  116. package/src/tool/code_resolve/guidance.ts +2 -4
  117. package/src/tool/code_resolve/result.ts +13 -10
  118. package/src/tool/code_resolve/tui.ts +18 -1
  119. package/src/tool/infra/truncate.ts +39 -13
  120. package/src/tool/result/candidate-row.ts +27 -0
  121. package/src/tool/result/errors.ts +21 -6
  122. package/src/tool/result/types.ts +7 -3
  123. package/src/tool/schemas.ts +5 -0
  124. package/src/types/execution.ts +2 -0
  125. package/src/types/index.ts +2 -0
  126. package/src/ui/tui/candidate-legacy-body.ts +69 -0
  127. package/src/ui/tui/candidate-selection.ts +366 -0
  128. package/src/ui/tui/common.ts +26 -31
  129. package/src/ui/tui/execution-error.ts +92 -0
@@ -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,6 +4,8 @@ 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";
@@ -29,6 +31,8 @@ interface HealthRefreshAttemptOptions {
29
31
 
30
32
  export interface HealthRefreshCollection {
31
33
  readonly attempt: HealthRefreshAttempt;
34
+ /** File readiness observed by the same explicit refresh call. */
35
+ readonly fileReadiness?: SemanticReadinessResult;
32
36
  /** Next typed maintenance state, committed only after the attempt returns. */
33
37
  readonly maintenanceState: LspMaintenanceState;
34
38
  /** Final report from the runtime, retained only for the current workflow. */
@@ -58,12 +62,13 @@ async function collectFileRefreshAttempt(
58
62
  });
59
63
  const readiness = await options.runtime.waitUntilReadyForFile(
60
64
  scope.path,
61
- undefined,
65
+ { retryFailedRoute: true },
62
66
  options.control,
63
67
  );
64
68
  const fileReadiness: HealthFileReadiness =
65
69
  readiness.kind === "ready" ? "ready" : readiness.kind === "timeout" ? "pending" : "unavailable";
66
70
  return {
71
+ fileReadiness: readiness,
67
72
  maintenanceState: maintenance.maintenanceState,
68
73
  attempt: {
69
74
  kind: "completed",
@@ -75,6 +80,7 @@ async function collectFileRefreshAttempt(
75
80
  fileReadiness,
76
81
  restartedClients: 0,
77
82
  processCrashRecovery: readiness.processCrashRecovery ?? emptyProcessCrashRecoveryReport(),
83
+ startupRetry: readiness.startupRetry ?? emptyStartupRetryReport(),
78
84
  staleAssessment: {
79
85
  scope: "file",
80
86
  suspected: null,
@@ -111,6 +117,7 @@ async function collectWorkspaceRefreshAttempt(
111
117
  operationScope: "workspace-runtime",
112
118
  diagnosticEvidence: maintenance.diagnosticEvidence,
113
119
  processCrashRecovery: emptyProcessCrashRecoveryReport(),
120
+ startupRetry: emptyStartupRetryReport(),
114
121
  ...(maintenance.sourceTracking ? { sourceTracking: maintenance.sourceTracking } : {}),
115
122
  reason: maintenance.failureReason,
116
123
  },
@@ -127,6 +134,7 @@ async function collectWorkspaceRefreshAttempt(
127
134
  // runtime then performs a fresh pass that includes the replacement.
128
135
  initialEvidence: maintenance.diagnosticEvidence,
129
136
  processCrashDemand: {
137
+ explicit: true,
130
138
  ...(workspaceScope?.filter ? { scopes: [workspaceScope.filter] } : {}),
131
139
  },
132
140
  });
@@ -154,6 +162,7 @@ async function collectWorkspaceRefreshAttempt(
154
162
  },
155
163
  diagnosticEvidence,
156
164
  processCrashRecovery: recovery.processCrashRecovery,
165
+ startupRetry: recovery.startupRetry,
157
166
  ...(maintenance.sourceTracking ? { sourceTracking: maintenance.sourceTracking } : {}),
158
167
  reason: recovery.refreshFailureReason,
159
168
  },
@@ -171,6 +180,7 @@ async function collectWorkspaceRefreshAttempt(
171
180
  attemptedActiveClients: recovery.attemptedClients,
172
181
  restartedClients: recovery.restartedClients,
173
182
  processCrashRecovery: recovery.processCrashRecovery,
183
+ startupRetry: recovery.startupRetry,
174
184
  diagnosticEvidence,
175
185
  ...(maintenance.sourceTracking ? { sourceTracking: maintenance.sourceTracking } : {}),
176
186
  staleAssessment: {
@@ -196,6 +206,7 @@ async function collectWorkspaceRefreshAttempt(
196
206
  operationScope: "workspace-runtime",
197
207
  diagnosticEvidence: maintenance.diagnosticEvidence,
198
208
  processCrashRecovery: emptyProcessCrashRecoveryReport(),
209
+ startupRetry: emptyStartupRetryReport(),
199
210
  ...(maintenance.sourceTracking ? { sourceTracking: maintenance.sourceTracking } : {}),
200
211
  reason: errorMessage(error),
201
212
  },
@@ -4,6 +4,7 @@ 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";
9
10
  import type { SourceTrackingReport } from "../substrate/lsp/source-tracking.ts";
@@ -63,7 +64,7 @@ export type HealthDiagnosticObservation =
63
64
  readonly scope: HealthDiagnosticScope;
64
65
  readonly entries: readonly HealthDiagnosticEntry[];
65
66
  readonly evidence: DiagnosticEvidenceSummary;
66
- /** Tsconfig scope decision for the requested file (file scope only). */
67
+ /** Tsconfig scope decision for an applicable TypeScript or JavaScript file. */
67
68
  readonly scopeStatus?: FileScopeDecision;
68
69
  }
69
70
  | {
@@ -72,7 +73,7 @@ export type HealthDiagnosticObservation =
72
73
  readonly entries: readonly HealthDiagnosticEntry[];
73
74
  readonly evidence: DiagnosticEvidenceSummary;
74
75
  readonly reason: string;
75
- /** Tsconfig scope decision for the requested file (file scope only). */
76
+ /** Tsconfig scope decision for an applicable TypeScript or JavaScript file. */
76
77
  readonly scopeStatus?: FileScopeDecision;
77
78
  }
78
79
  | {
@@ -81,7 +82,7 @@ export type HealthDiagnosticObservation =
81
82
  readonly entries: readonly HealthDiagnosticEntry[];
82
83
  readonly evidence: DiagnosticEvidenceSummary;
83
84
  readonly reason: string;
84
- /** Tsconfig scope decision for the requested file (file scope only). */
85
+ /** Tsconfig scope decision for an applicable TypeScript or JavaScript file. */
85
86
  readonly scopeStatus?: FileScopeDecision;
86
87
  };
87
88
 
@@ -93,6 +94,7 @@ export interface HealthStaleAssessment {
93
94
  readonly warning: string | null;
94
95
  }
95
96
 
97
+ /** Runtime scope of maintenance; it is not the diagnostic result scope. */
96
98
  export type HealthRefreshOperationScope = "file-runtime" | "workspace-runtime";
97
99
 
98
100
  /** Readiness result for a file-scoped maintenance attempt. */
@@ -114,12 +116,17 @@ interface CompletedHealthRefreshAttempt {
114
116
  readonly restartedClients: number;
115
117
  /** Separate outcome for process-crash route recovery. */
116
118
  readonly processCrashRecovery: ProcessCrashRecoveryReport;
119
+ /** Separate outcome for initial-start retries. */
120
+ readonly startupRetry?: StartupRetryReport;
117
121
  readonly staleAssessment: HealthStaleAssessment;
118
- /** Source discovery and bounded tracking facts for broad refreshes. */
122
+ /**
123
+ * Source discovery and bounded tracking facts for broad refreshes. The
124
+ * report's skipped list describes automatic tracking policy, not capability.
125
+ */
119
126
  readonly sourceTracking?: SourceTrackingReport;
120
127
  }
121
128
 
122
- /** A diagnostic refresh attempt against an explicit LSP runtime scope. */
129
+ /** A diagnostic refresh attempt with separate maintenance and result scopes. */
123
130
  export type HealthRefreshAttempt =
124
131
  | (CompletedHealthRefreshAttempt & {
125
132
  readonly operationScope: "workspace-runtime";
@@ -147,7 +154,12 @@ export type HealthRefreshAttempt =
147
154
  readonly diagnosticEvidence?: DiagnosticEvidenceSummary;
148
155
  /** Process-crash outcome, when the recovery pass returned one. */
149
156
  readonly processCrashRecovery?: ProcessCrashRecoveryReport;
150
- /** Source discovery and bounded tracking facts for broad refreshes. */
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
+ */
151
163
  readonly sourceTracking?: SourceTrackingReport;
152
164
  /** File readiness outcome, when a file-scoped attempt failed. */
153
165
  readonly fileReadiness?: HealthFileReadiness;
@@ -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,
@@ -80,7 +81,6 @@ export async function runHealthWorkflow(
80
81
  const diagnosticsScope = diagnosticScope(scopeFilter);
81
82
  const refreshCollection = await collectRefreshState({
82
83
  refreshRequested: request.refresh === true,
83
- diagnosticsRequested: included.includes("diagnostics"),
84
84
  runtime,
85
85
  lspState,
86
86
  diagnosticsScope,
@@ -94,6 +94,7 @@ export async function runHealthWorkflow(
94
94
  scopeFilter,
95
95
  lspState,
96
96
  capabilityState: capabilityStates.semantic,
97
+ refreshReadiness: refreshCollection.fileReadiness,
97
98
  control,
98
99
  });
99
100
  const semanticReady = semanticState?.kind === "ready";
@@ -146,6 +147,8 @@ interface SemanticHealthStateOptions {
146
147
  requested: boolean;
147
148
  /** Whether this call requests file-routed evidence rather than passive inventory. */
148
149
  fileReadinessRequested: boolean;
150
+ /** Readiness observed by the explicit refresh phase, when one ran. */
151
+ refreshReadiness?: SemanticReadinessResult;
149
152
  runtime: WorkspaceLspRuntime | null;
150
153
  scopeFilter: string | null;
151
154
  lspState: WorkspaceLspRuntimeState;
@@ -157,12 +160,17 @@ async function establishSemanticHealthState(
157
160
  options: SemanticHealthStateOptions,
158
161
  ): Promise<SemanticHealthState | null> {
159
162
  if (!options.requested) return null;
160
- if (options.fileReadinessRequested && options.runtime && isScopedFile(options.scopeFilter)) {
161
- const readiness = await options.runtime.waitUntilReadyForFile(
162
- options.scopeFilter,
163
- undefined,
164
- options.control,
165
- );
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);
166
174
  if (readiness.kind === "ready") return { kind: "ready" };
167
175
  if (readiness.kind === "timeout") {
168
176
  return { kind: "pending", reason: SEMANTIC_READINESS_TIMEOUT_REASON };
@@ -226,12 +234,12 @@ function collectCapabilityWarnings(
226
234
 
227
235
  interface HealthRefreshStateCollection {
228
236
  readonly attempt: HealthRefreshState;
237
+ readonly fileReadiness?: SemanticReadinessResult;
229
238
  readonly diagnosticReport?: WorkspaceDiagnosticReport;
230
239
  }
231
240
 
232
241
  interface RefreshStateOptions {
233
242
  readonly refreshRequested: boolean;
234
- readonly diagnosticsRequested: boolean;
235
243
  readonly runtime: WorkspaceLspRuntime | null;
236
244
  readonly lspState: WorkspaceLspRuntimeState;
237
245
  readonly diagnosticsScope: HealthDiagnosticScope;
@@ -243,16 +251,7 @@ interface RefreshStateOptions {
243
251
  async function collectRefreshState(
244
252
  options: RefreshStateOptions,
245
253
  ): Promise<HealthRefreshStateCollection> {
246
- const { deps, diagnosticsRequested, diagnosticsScope, lspState, runtime } = options;
247
- if (!diagnosticsRequested) {
248
- return {
249
- attempt: {
250
- kind: "not-requested",
251
- reason: "Diagnostics were not requested.",
252
- lastAttempt: deps.lastRefreshAttempt,
253
- },
254
- };
255
- }
254
+ const { deps, diagnosticsScope, lspState, runtime } = options;
256
255
  if (!options.refreshRequested) {
257
256
  return {
258
257
  attempt: {
@@ -296,7 +295,10 @@ async function collectRefreshState(
296
295
  });
297
296
  deps.updateMaintenanceState(attempt.maintenanceState);
298
297
  deps.trackRefreshAttempt(attempt.attempt);
299
- return attempt;
298
+ return {
299
+ ...attempt,
300
+ ...(attempt.fileReadiness ? { fileReadiness: attempt.fileReadiness } : {}),
301
+ };
300
302
  } catch (error) {
301
303
  // Cancellation must propagate; a cancelled caller no longer awaits a
302
304
  // recorded failed attempt or the refresh data it would carry.