@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
@@ -3,17 +3,34 @@ import {
3
3
  type ProcessCrashRecoveryEntry,
4
4
  type ProcessCrashRecoveryOutcome,
5
5
  type ProcessCrashRecoveryReport,
6
+ type StartupRetryEntry,
7
+ type StartupRetryReport,
6
8
  } from "@mrclrchtr/supi-lsp/api";
7
9
  import {
8
10
  formatCompactProcessCrashRecovery,
11
+ formatCompactStartupRetry,
9
12
  formatProcessCrashRecovery,
10
13
  formatStaleDiagnosticRestarts,
14
+ formatStartupRetry,
11
15
  isFileReadinessPending,
12
16
  } from "./refresh-outcome.ts";
17
+ import {
18
+ formatCompactMaintenanceEvidence,
19
+ formatMaintenanceEvidence,
20
+ formatMaintenanceScope,
21
+ } from "./scope.ts";
13
22
  import { readSemanticHealthState } from "./semantic-state.ts";
14
23
 
15
24
  /** Render current and retained health refresh status for the TUI. */
16
25
 
26
+ /** Read source discovery counts from structured health details. */
27
+ export function readSourceTrackingStatus(source: Record<string, unknown> | null): string | null {
28
+ if (!source || (source.status !== "complete" && source.status !== "limited")) return null;
29
+ const reason =
30
+ source.status === "limited" && typeof source.reason === "string" ? ` (${source.reason})` : "";
31
+ return `${source.status}${reason}: ${readNumber(source.observedFileCount)} observed, ${readArrayLength(source.discovered)} discovered, ${readArrayLength(source.tracked)} tracked, ${readArrayLength(source.skipped)} skipped, ${readArrayLength(source.unavailable)} unavailable, ${readNumber(source.deferred)} deferred`;
32
+ }
33
+
17
34
  export function readRefreshStatus(data: Record<string, unknown> | null): string | null {
18
35
  const refresh = readRecord(data?.refresh);
19
36
  if (!refresh) return null;
@@ -41,18 +58,31 @@ function formatCompletedRefreshStatus(
41
58
  const processCrashRecovery = formatProcessCrashRecovery(
42
59
  readProcessCrashRecovery(refresh.processCrashRecovery),
43
60
  );
61
+ const startupRetry = formatStartupRetry(readStartupRetry(refresh.startupRetry));
44
62
  const noOp =
45
- !fileReadinessPending && attempted === 0 && restarted === 0 && processCrashRecovery === null;
63
+ !fileReadinessPending &&
64
+ attempted === 0 &&
65
+ restarted === 0 &&
66
+ processCrashRecovery === null &&
67
+ startupRetry === null;
46
68
  const label = refreshAttemptLabel(refresh);
47
69
  const base = fileReadinessPending
48
70
  ? `${label} waiting — LSP may still be warming; retry shortly`
49
71
  : noOp
50
72
  ? `${label} completed no-op`
51
73
  : `${label} completed: ${attempted} clients targeted`;
52
- const outcomes = formatRefreshOutcomes(refresh, restarted, processCrashRecovery, noOp);
74
+ const outcomes = formatRefreshOutcomes({
75
+ refresh,
76
+ restarted,
77
+ processCrashRecovery,
78
+ startupRetry,
79
+ noOp,
80
+ });
53
81
  const withOutcome = outcomes.length > 0 ? `${base}; ${outcomes.join("; ")}` : base;
54
- const evidence = formatDiagnosticEvidence(readRecord(refresh.diagnosticEvidence));
55
- const withEvidence = evidence ? `${withOutcome}; ${evidence}` : withOutcome;
82
+ const maintenanceScope = formatMaintenanceScope(refresh);
83
+ const withScope = maintenanceScope ? `${withOutcome}; ${maintenanceScope}` : withOutcome;
84
+ const evidence = formatMaintenanceEvidence(readRecord(refresh.diagnosticEvidence));
85
+ const withEvidence = evidence ? `${withScope}; ${evidence}` : withScope;
56
86
  const stale = readRecord(refresh.staleAssessment);
57
87
  return stale?.suspected === true
58
88
  ? `${withEvidence}; stale pattern suspected in ${readNumber(stale.matchedFileCount)} files`
@@ -63,23 +93,26 @@ function formatFailedRefreshStatus(refresh: Record<string, unknown>): string {
63
93
  const processCrashRecovery = formatProcessCrashRecovery(
64
94
  readProcessCrashRecovery(refresh.processCrashRecovery),
65
95
  );
96
+ const startupRetry = formatStartupRetry(readStartupRetry(refresh.startupRetry));
66
97
  const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
67
- const evidence = formatDiagnosticEvidence(readRecord(refresh.diagnosticEvidence));
98
+ const maintenanceScope = formatMaintenanceScope(refresh);
99
+ const evidence = formatMaintenanceEvidence(readRecord(refresh.diagnosticEvidence));
68
100
  const reason = typeof refresh.reason === "string" ? `: ${refresh.reason}` : "";
69
- return `${refreshAttemptLabel(refresh)} failed${reason}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${evidence ? `; ${evidence}` : ""}`;
101
+ return `${refreshAttemptLabel(refresh)} failed${reason}${maintenanceScope ? `; ${maintenanceScope}` : ""}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${startupRetry ? `; ${startupRetry}` : ""}${evidence ? `; ${evidence}` : ""}`;
70
102
  }
71
-
72
- function formatRefreshOutcomes(
73
- refresh: Record<string, unknown>,
74
- restarted: number,
75
- processCrashRecovery: string | null,
76
- noOp: boolean,
77
- ): string[] {
103
+ function formatRefreshOutcomes(options: {
104
+ refresh: Record<string, unknown>;
105
+ restarted: number;
106
+ processCrashRecovery: string | null;
107
+ startupRetry: string | null;
108
+ noOp: boolean;
109
+ }): string[] {
78
110
  const outcomes: string[] = [];
79
- if (refresh.operationScope === "workspace-runtime" && !noOp) {
80
- outcomes.push(formatStaleDiagnosticRestarts(restarted));
111
+ if (options.refresh.operationScope === "workspace-runtime" && !options.noOp) {
112
+ outcomes.push(formatStaleDiagnosticRestarts(options.restarted));
81
113
  }
82
- if (processCrashRecovery) outcomes.push(processCrashRecovery);
114
+ if (options.processCrashRecovery) outcomes.push(options.processCrashRecovery);
115
+ if (options.startupRetry) outcomes.push(options.startupRetry);
83
116
  return outcomes;
84
117
  }
85
118
 
@@ -88,20 +121,25 @@ export function readCompactRefreshStatus(data: Record<string, unknown> | null):
88
121
  const refresh = readRecord(data?.refresh);
89
122
  if (!refresh || (refresh.kind !== "completed" && refresh.kind !== "failed")) return null;
90
123
 
124
+ const sourceTracking = formatSourceTracking(readRecord(refresh.sourceTracking));
91
125
  const processCrashRecovery = formatCompactProcessCrashRecovery(
92
126
  readProcessCrashRecovery(refresh.processCrashRecovery),
93
127
  );
128
+ const startupRetry = formatCompactStartupRetry(readStartupRetry(refresh.startupRetry));
129
+ const maintenanceScope = formatMaintenanceScope(refresh);
94
130
  if (refresh.kind === "failed") {
95
131
  const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
96
132
  return (
97
- [staleRestart, processCrashRecovery]
133
+ [maintenanceScope, sourceTracking, staleRestart, processCrashRecovery, startupRetry]
98
134
  .filter((value): value is string => value !== null)
99
135
  .join("; ") || null
100
136
  );
101
137
  }
102
138
  const fileReadinessPending = isPendingFileReadiness(data);
103
139
  if (
140
+ !sourceTracking &&
104
141
  !processCrashRecovery &&
142
+ !startupRetry &&
105
143
  readNumber(refresh.restartedClients) === 0 &&
106
144
  !fileReadinessPending
107
145
  ) {
@@ -110,7 +148,14 @@ export function readCompactRefreshStatus(data: Record<string, unknown> | null):
110
148
 
111
149
  const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
112
150
  const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
113
- return [staleRestart, processCrashRecovery, readiness]
151
+ return [
152
+ maintenanceScope,
153
+ sourceTracking,
154
+ staleRestart,
155
+ processCrashRecovery,
156
+ startupRetry,
157
+ readiness,
158
+ ]
114
159
  .filter((value): value is string => value !== null)
115
160
  .join("; ");
116
161
  }
@@ -122,16 +167,26 @@ export function readPreviousRefreshStatus(data: Record<string, unknown> | null):
122
167
  }
123
168
  const attempt = readRecord(refresh.lastAttempt);
124
169
  if (!attempt) return null;
125
- const evidence = formatCompactDiagnosticEvidence(readRecord(attempt.diagnosticEvidence));
170
+ const evidence = formatCompactMaintenanceEvidence(readRecord(attempt.diagnosticEvidence));
171
+ const sourceTracking = formatSourceTracking(readRecord(attempt.sourceTracking));
126
172
  const processCrashRecovery = formatCompactProcessCrashRecovery(
127
173
  readProcessCrashRecovery(attempt.processCrashRecovery),
128
174
  );
175
+ const startupRetry = formatCompactStartupRetry(readStartupRetry(attempt.startupRetry));
129
176
  const staleRestart = formatStaleDiagnosticRestartsForRecord(attempt);
130
177
  const fileReadinessPending =
131
178
  attempt.kind === "completed" &&
132
179
  isFileReadinessPending(attempt, readSemanticHealthState(data?.semanticState));
133
180
  const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
134
- const outcome = [readiness, staleRestart, processCrashRecovery, evidence]
181
+ const outcome = [
182
+ readiness,
183
+ formatMaintenanceScope(attempt),
184
+ sourceTracking,
185
+ staleRestart,
186
+ processCrashRecovery,
187
+ startupRetry,
188
+ evidence,
189
+ ]
135
190
  .filter((value): value is string => value !== null)
136
191
  .join("; ");
137
192
  if (attempt.kind === "failed") {
@@ -169,15 +224,25 @@ function formatStaleDiagnosticRestartsForRecord(attempt: Record<string, unknown>
169
224
  function formatLastRefreshSuffix(refresh: Record<string, unknown>): string {
170
225
  const attempt = readRecord(refresh.lastAttempt);
171
226
  if (!attempt) return "";
172
- const evidence = formatDiagnosticEvidence(readRecord(attempt.diagnosticEvidence));
227
+ const evidence = formatMaintenanceEvidence(readRecord(attempt.diagnosticEvidence));
228
+ const sourceTracking = formatSourceTracking(readRecord(attempt.sourceTracking));
173
229
  const staleRestart = formatStaleDiagnosticRestartsForRecord(attempt);
174
230
  const processCrashRecovery = formatProcessCrashRecovery(
175
231
  readProcessCrashRecovery(attempt.processCrashRecovery),
176
232
  );
233
+ const startupRetry = formatStartupRetry(readStartupRetry(attempt.startupRetry));
177
234
  const fileReadinessPending =
178
235
  attempt.kind === "completed" && isFileReadinessPending(attempt, null);
179
236
  const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
180
- const outcome = [readiness, staleRestart, processCrashRecovery, evidence]
237
+ const outcome = [
238
+ readiness,
239
+ formatMaintenanceScope(attempt),
240
+ sourceTracking,
241
+ staleRestart,
242
+ processCrashRecovery,
243
+ startupRetry,
244
+ evidence,
245
+ ]
181
246
  .filter((value): value is string => value !== null)
182
247
  .join("; ");
183
248
  if (attempt.kind === "failed") {
@@ -191,16 +256,13 @@ function formatLastRefreshSuffix(refresh: Record<string, unknown>): string {
191
256
  return "";
192
257
  }
193
258
 
194
- function formatCompactDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
195
- const counts = readEvidenceCounts(evidence);
196
- if (!counts) return null;
197
- return `req ${counts[0]} · conf ${counts[1]} · unconf ${counts[2]} · failed ${counts[3]} · removed ${counts[4]}`;
259
+ function formatSourceTracking(source: Record<string, unknown> | null): string | null {
260
+ const status = readSourceTrackingStatus(source);
261
+ return status ? `source discovery ${status}` : null;
198
262
  }
199
263
 
200
- function formatDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
201
- const counts = readEvidenceCounts(evidence);
202
- if (!counts) return null;
203
- return `${counts[0]} requested, ${counts[1]} confirmed, ${counts[2]} unconfirmed, ${counts[3]} failed, ${counts[4]} removed`;
264
+ function readArrayLength(value: unknown): number {
265
+ return Array.isArray(value) ? value.length : 0;
204
266
  }
205
267
 
206
268
  export function isPendingFileReadiness(data: Record<string, unknown> | null): boolean {
@@ -213,6 +275,43 @@ export function isPendingFileReadiness(data: Record<string, unknown> | null): bo
213
275
  return isFileReadinessPending(attempt, readSemanticHealthState(data?.semanticState));
214
276
  }
215
277
 
278
+ function readStartupRetry(value: unknown): StartupRetryReport | null {
279
+ const report = readRecord(value);
280
+ if (!report) return null;
281
+ const recoveredRoutes = report.recoveredRoutes;
282
+ const failedRoutes = report.failedRoutes;
283
+ const omittedEntries = report.omittedEntries;
284
+ if (
285
+ !isEvidenceCount(recoveredRoutes) ||
286
+ !isEvidenceCount(failedRoutes) ||
287
+ !isEvidenceCount(omittedEntries) ||
288
+ !Array.isArray(report.entries) ||
289
+ report.entries.length > MAX_PROCESS_CRASH_RECOVERY_ENTRIES
290
+ ) {
291
+ return null;
292
+ }
293
+ const entries = report.entries
294
+ .map(readStartupRetryEntry)
295
+ .filter((entry): entry is StartupRetryEntry => entry !== null);
296
+ if (entries.length !== report.entries.length) return null;
297
+ return { recoveredRoutes, failedRoutes, entries, omittedEntries };
298
+ }
299
+
300
+ function readStartupRetryEntry(value: unknown): StartupRetryEntry | null {
301
+ const entry = readRecord(value);
302
+ if (!entry || typeof entry.name !== "string" || typeof entry.root !== "string") return null;
303
+ if (entry.outcome !== "recovered" && entry.outcome !== "retry-failed") return null;
304
+ if (entry.nextAction !== undefined && entry.nextAction !== "refresh") return null;
305
+ if (entry.failureMessage !== undefined && typeof entry.failureMessage !== "string") return null;
306
+ return {
307
+ name: entry.name,
308
+ root: entry.root,
309
+ outcome: entry.outcome,
310
+ ...(entry.nextAction !== undefined ? { nextAction: entry.nextAction } : {}),
311
+ ...(entry.failureMessage !== undefined ? { failureMessage: entry.failureMessage } : {}),
312
+ };
313
+ }
314
+
216
315
  function readProcessCrashRecovery(value: unknown): ProcessCrashRecoveryReport | null {
217
316
  const report = readRecord(value);
218
317
  if (!report) return null;
@@ -246,7 +345,7 @@ function readProcessCrashRecoveryEntry(value: unknown): ProcessCrashRecoveryEntr
246
345
  if (
247
346
  entry.nextAction !== undefined &&
248
347
  entry.nextAction !== "use-exact-file" &&
249
- entry.nextAction !== "reload-workspace"
348
+ entry.nextAction !== "refresh"
250
349
  ) {
251
350
  return null;
252
351
  }
@@ -269,18 +368,6 @@ function isProcessCrashRecoveryOutcome(value: unknown): value is ProcessCrashRec
269
368
  );
270
369
  }
271
370
 
272
- function readEvidenceCounts(evidence: Record<string, unknown> | null): number[] | null {
273
- if (!evidence) return null;
274
- const counts = [
275
- evidence.requested,
276
- evidence.confirmed,
277
- evidence.unconfirmed,
278
- evidence.failed,
279
- evidence.removed,
280
- ];
281
- return counts.every(isEvidenceCount) ? counts : null;
282
- }
283
-
284
371
  function isEvidenceCount(value: unknown): value is number {
285
372
  return typeof value === "number" && Number.isInteger(value) && value >= 0;
286
373
  }
@@ -0,0 +1,78 @@
1
+ /** Formatting for diagnostic result and maintenance scopes and their evidence. */
2
+
3
+ function readRecord(value: unknown): Record<string, unknown> | null {
4
+ return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : null;
5
+ }
6
+
7
+ export function withDiagnosticScope(summary: string, data: Record<string, unknown> | null): string {
8
+ const scope = formatDiagnosticScope(readDiagnosticScope(data));
9
+ return scope ? `${summary} — evidence scope: ${scope}` : summary;
10
+ }
11
+
12
+ function readDiagnosticScope(data: Record<string, unknown> | null): Record<string, unknown> | null {
13
+ return readRecord(readRecord(data?.diagnosticObservation)?.scope);
14
+ }
15
+
16
+ function formatDiagnosticScope(scope: Record<string, unknown> | null): string | null {
17
+ if (scope?.kind === "file" && typeof scope.path === "string") {
18
+ return `live file diagnostic request for ${scope.path}`;
19
+ }
20
+ if (scope?.kind === "tracked-files") {
21
+ return typeof scope.filter === "string"
22
+ ? `tracked-file diagnostic snapshot under ${scope.filter}`
23
+ : "tracked-file diagnostic snapshot";
24
+ }
25
+ return null;
26
+ }
27
+
28
+ export function formatCompactDiagnosticEvidence(
29
+ evidence: Record<string, unknown> | null,
30
+ scopeKind: string | null,
31
+ ): string | null {
32
+ const counts = readEvidenceCounts(evidence);
33
+ if (!counts) return null;
34
+ const scopeLabel = scopeKind === "file" ? "file" : "tracked-file";
35
+ return `${scopeLabel} evidence req ${counts[0]} · conf ${counts[1]} · unconf ${counts[2]} · failed ${counts[3]} · removed ${counts[4]}`;
36
+ }
37
+
38
+ export function formatDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
39
+ const counts = readEvidenceCounts(evidence);
40
+ if (!counts) return null;
41
+ return `${counts[0]} requested, ${counts[1]} confirmed, ${counts[2]} unconfirmed, ${counts[3]} failed, ${counts[4]} removed`;
42
+ }
43
+
44
+ export function formatMaintenanceEvidence(evidence: Record<string, unknown> | null): string | null {
45
+ const counts = readEvidenceCounts(evidence);
46
+ if (!counts) return null;
47
+ return `maintenance evidence: ${counts[0]} requested, ${counts[1]} confirmed, ${counts[2]} unconfirmed, ${counts[3]} failed, ${counts[4]} removed`;
48
+ }
49
+
50
+ export function formatCompactMaintenanceEvidence(
51
+ evidence: Record<string, unknown> | null,
52
+ ): string | null {
53
+ const counts = readEvidenceCounts(evidence);
54
+ if (!counts) return null;
55
+ return `maintenance evidence req ${counts[0]} · conf ${counts[1]} · unconf ${counts[2]} · failed ${counts[3]} · removed ${counts[4]}`;
56
+ }
57
+
58
+ export function formatMaintenanceScope(refresh: Record<string, unknown>): string | null {
59
+ if (refresh.operationScope === "file-runtime") return "maintenance scope: file runtime";
60
+ if (refresh.operationScope === "workspace-runtime") return "maintenance scope: workspace runtime";
61
+ return null;
62
+ }
63
+
64
+ function readEvidenceCounts(evidence: Record<string, unknown> | null): number[] | null {
65
+ if (!evidence) return null;
66
+ const counts = [
67
+ evidence.requested,
68
+ evidence.confirmed,
69
+ evidence.unconfirmed,
70
+ evidence.failed,
71
+ evidence.removed,
72
+ ];
73
+ return counts.every(isEvidenceCount) ? counts : null;
74
+ }
75
+
76
+ function isEvidenceCount(value: unknown): value is number {
77
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
78
+ }
@@ -18,7 +18,7 @@ export const codeHealthSpec = {
18
18
  refresh: Type.Optional(
19
19
  Type.Boolean({
20
20
  description:
21
- "Attempt diagnostic and required process-crash recovery before collecting; result reports the outcome.",
21
+ "Retry failed LSP routes and collect fresh diagnostic health evidence; result reports route recovery separately from diagnostic confirmation.",
22
22
  }),
23
23
  ),
24
24
  include: Type.Optional(
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { Theme } from "@earendil-works/pi-coding-agent";
6
- import { Container, Spacer, Text } from "@earendil-works/pi-tui";
6
+ import { type Component, Container, Spacer, Text } from "@earendil-works/pi-tui";
7
7
  import {
8
8
  formatProjectServerRouteStatusCounts,
9
9
  type ProjectServerRouteStatusCounts,
@@ -29,7 +29,13 @@ import {
29
29
  readCompactRefreshStatus,
30
30
  readPreviousRefreshStatus,
31
31
  readRefreshStatus,
32
+ readSourceTrackingStatus,
32
33
  } from "./refresh-status.ts";
34
+ import {
35
+ formatCompactDiagnosticEvidence,
36
+ formatDiagnosticEvidence,
37
+ withDiagnosticScope,
38
+ } from "./scope.ts";
33
39
  import { formatSemanticHealthState, readSemanticHealthState } from "./semantic-state.ts";
34
40
 
35
41
  /** ── renderCall ────────────────────────────────────────────────── */
@@ -63,12 +69,16 @@ export function renderHealthResult(
63
69
  options: ResultOptios,
64
70
  theme: Theme,
65
71
  context: ToolRendererContext | undefined,
66
- ): Container | Text {
72
+ ): Component {
67
73
  if (options.isPartial) return renderPartial("Gathering workspace health…", theme);
68
74
 
69
75
  const data =
70
76
  result.details?.type === "health" ? (result.details.data as Record<string, unknown>) : null;
71
- const executionError = renderExecutionError(context, "code_health failed", theme);
77
+ const executionError = renderExecutionError(
78
+ result,
79
+ { isError: context?.isError, expanded: options.expanded, label: "code_health failed" },
80
+ theme,
81
+ );
72
82
  if (executionError) return executionError;
73
83
  const domainError = renderDomainError(result, theme);
74
84
  if (domainError) return renderDomainResult(result, options, theme, domainError);
@@ -135,7 +145,7 @@ function buildCompactSummary(data: Record<string, unknown> | null, theme: Theme)
135
145
  formatSectionSummary(
136
146
  section,
137
147
  section.key === "diagnostics"
138
- ? formatCompactDiagnosticEvidence(readDiagnosticEvidence(data))
148
+ ? formatCompactDiagnosticEvidence(readDiagnosticEvidence(data), diagnosticScopeKind(data))
139
149
  : null,
140
150
  theme,
141
151
  fileReadinessPending,
@@ -192,6 +202,8 @@ function buildStatusBar(data: Record<string, unknown> | null, theme: Theme): Tex
192
202
  const semanticStatus = readSemanticStatus(data);
193
203
  const structuralStatus = readString(data, "structuralStatus");
194
204
  const refreshStatus = readRefreshStatus(data);
205
+ const refresh = readRecord(data?.refresh);
206
+ const sourceTracking = readSourceTrackingStatus(readRecord(refresh?.sourceTracking));
195
207
 
196
208
  const lspColor = semanticStatus.startsWith("ready") ? "success" : "warning";
197
209
  const structuralColor = structuralStatus === "ready" ? "success" : "muted";
@@ -204,6 +216,7 @@ function buildStatusBar(data: Record<string, unknown> | null, theme: Theme): Tex
204
216
  lines.push(`Tree-sitter: ${theme.fg(structuralColor, structuralStatus)}`);
205
217
  }
206
218
  if (refreshStatus) lines.push(`Diagnostics: ${theme.fg("dim", refreshStatus)}`);
219
+ if (sourceTracking) lines.push(`Source discovery: ${theme.fg("dim", sourceTracking)}`);
207
220
 
208
221
  return new Text(lines.join(" "), 0, 0);
209
222
  }
@@ -226,36 +239,6 @@ function readDiagnosticEvidence(
226
239
  return readRecord(readRecord(data?.diagnosticObservation)?.evidence);
227
240
  }
228
241
 
229
- function formatCompactDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
230
- if (!evidence) return null;
231
- const counts = [
232
- evidence.requested,
233
- evidence.confirmed,
234
- evidence.unconfirmed,
235
- evidence.failed,
236
- evidence.removed,
237
- ];
238
- if (!counts.every(isEvidenceCount)) return null;
239
- return `req ${evidence.requested} · conf ${evidence.confirmed} · unconf ${evidence.unconfirmed} · failed ${evidence.failed} · removed ${evidence.removed}`;
240
- }
241
-
242
- function formatDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
243
- if (!evidence) return null;
244
- const counts = [
245
- evidence.requested,
246
- evidence.confirmed,
247
- evidence.unconfirmed,
248
- evidence.failed,
249
- evidence.removed,
250
- ];
251
- if (!counts.every(isEvidenceCount)) return null;
252
- return `${evidence.requested} requested, ${evidence.confirmed} confirmed, ${evidence.unconfirmed} unconfirmed, ${evidence.failed} failed, ${evidence.removed} removed`;
253
- }
254
-
255
- function isEvidenceCount(value: unknown): value is number {
256
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
257
- }
258
-
259
242
  function readRecord(value: unknown): Record<string, unknown> | null {
260
243
  return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : null;
261
244
  }
@@ -320,21 +303,29 @@ function buildDiagnosticSummary(data: Record<string, unknown> | null, theme: The
320
303
  const label = isPendingFileReadiness(data)
321
304
  ? "Diagnostics pending — LSP may still be warming; retry shortly"
322
305
  : "Diagnostics unavailable";
323
- return new Text(theme.fg("warning", label), 0, 0);
306
+ return new Text(theme.fg("warning", withDiagnosticScope(label, data)), 0, 0);
324
307
  }
325
308
  const trackedFiles = diagnosticScopeKind(data) === "tracked-files";
326
309
  if (section.status === "partial") {
327
310
  const coverage = formatDiagnosticEvidence(readDiagnosticEvidence(data));
328
311
  const label = trackedFiles ? "Tracked-file diagnostics partial" : "Diagnostics partial";
329
- return new Text(theme.fg("warning", coverage ? `${label} (${coverage})` : label), 0, 0);
312
+ const summary = coverage ? `${label} (${coverage})` : label;
313
+ return new Text(theme.fg("warning", withDiagnosticScope(summary, data)), 0, 0);
330
314
  }
331
315
  if (section.itemCount === 0) {
332
- return new Text(theme.fg("success", diagnosticEmptySummary(data)), 0, 0);
316
+ return new Text(
317
+ theme.fg("success", withDiagnosticScope(diagnosticEmptySummary(data), data)),
318
+ 0,
319
+ 0,
320
+ );
333
321
  }
334
322
 
335
323
  const fileLabel = `${trackedFiles ? "tracked " : ""}${section.itemCount === 1 ? "file" : "files"}`;
336
324
  return new Text(
337
- theme.fg("warning", theme.bold(`${section.itemCount} ${fileLabel} with issues`)),
325
+ theme.fg(
326
+ "warning",
327
+ theme.bold(withDiagnosticScope(`${section.itemCount} ${fileLabel} with issues`, data)),
328
+ ),
338
329
  0,
339
330
  0,
340
331
  );
@@ -1,6 +1,8 @@
1
1
  export const toolDescription =
2
- "Inspect one exact point for syntax, the narrowest enclosing declaration, hover, definition, and nearby diagnostics. Use for point facts, not broad Orientation.";
2
+ "Inspect one source location for syntax, its enclosing declaration, hover information, definitions, and nearby diagnostics. Use it for point-local facts, not broad code context.";
3
3
 
4
- export const promptSnippet = "factual point inspection";
4
+ export const promptSnippet = "inspect a source location";
5
5
 
6
- export const promptGuidelines: string[] = [];
6
+ export const promptGuidelines = [
7
+ "Use code_inspect for diagnostics near a source location; use code_health for broader diagnostics or language-server status.",
8
+ ];
@@ -40,5 +40,11 @@ export function renderInspectResult(
40
40
  theme: Theme,
41
41
  context: ToolRendererContext | undefined,
42
42
  ): ReturnType<typeof renderSimpleResult> {
43
- return renderSimpleResult(result, options, theme, "Inspecting…", context);
43
+ return renderSimpleResult(
44
+ result,
45
+ options,
46
+ theme,
47
+ { progress: "Inspecting…", failure: "code_inspect failed" },
48
+ context,
49
+ );
44
50
  }
@@ -1,9 +1,8 @@
1
1
  export const toolDescription =
2
- "Orient around the workspace or one path, module, or target focus before surgical work. Omit focus for workspace Orientation. Workspace Orientation includes manifest and dependency inventories. For one-file work, use focused path Orientation. Directory focus may surface local instruction files.";
2
+ "Return observed workspace or focused code context to choose what source to inspect next. Omit `focus` for workspace context. A directory focus can surface local instruction files.";
3
3
 
4
- export const promptSnippet = "workspace/path/module/symbol Orientation";
4
+ export const promptSnippet = "orient around workspace or code context";
5
5
 
6
6
  export const promptGuidelines = [
7
- "Use code_orientation before broad file reading when you need direct workspace, package, directory, file, or symbol facts.",
8
- "Use code_graph for relationships and code_health for provider or diagnostic state.",
7
+ "Use code_orientation before broad source reading when workspace or path context can narrow the files.",
9
8
  ];
@@ -1,5 +1,8 @@
1
1
  import type { ConfidenceMode } from "@mrclrchtr/supi-code-runtime/api";
2
- import type { EvidenceListMetadata } from "../../analysis/evidence.ts";
2
+ import {
3
+ type EvidenceListMetadata,
4
+ renderEvidenceListMetadataDisclosure,
5
+ } from "../../analysis/evidence.ts";
3
6
  import type { ReadNextItem } from "../../analysis/read-next.ts";
4
7
  import type {
5
8
  OrientationCandidate,
@@ -15,6 +18,7 @@ import {
15
18
  type ResultSection,
16
19
  type ToolResultAssembly,
17
20
  } from "../result/assembly.ts";
21
+ import { formatCandidateRow } from "../result/candidate-row.ts";
18
22
  import { createToolDisplaySection } from "../result/display.ts";
19
23
  import type {
20
24
  ContextDetails,
@@ -47,17 +51,17 @@ export interface OrientationResultAssembly {
47
51
  /** Build structured candidate rows for an unresolved Orientation target. */
48
52
  export function orientationCandidateDisplaySections(
49
53
  candidates: readonly OrientationCandidate[],
50
- omittedCount = 0,
54
+ evidence: EvidenceListMetadata,
51
55
  ): readonly ToolDisplaySection[] {
52
56
  return [
53
57
  createToolDisplaySection({
54
58
  key: "orientation.candidates",
55
59
  title: "Candidates",
56
60
  items: candidates,
57
- totalCount: candidates.length + omittedCount,
58
- omittedCount,
59
- format: (candidate) =>
60
- `${candidate.rank}. ${candidate.name} (${candidate.kind ?? "unknown"}) — ${candidate.file}:${candidate.line}:${candidate.character} [${candidate.targetId}]`,
61
+ totalCount: evidence.totalCount,
62
+ omittedCount: evidence.omittedCount,
63
+ partialReason: evidence.partialReason,
64
+ format: (candidate) => formatCandidateRow(candidate, "rank-first"),
61
65
  }),
62
66
  ];
63
67
  }
@@ -215,13 +219,23 @@ export function finishOrientationResult(outcome: OrientationOutcome): CodeIntelR
215
219
  `${candidate.rank}. **${candidate.name}** (\`${candidate.kind ?? "unknown"}\`) — \`${candidate.file}\`:${candidate.line}:${candidate.character} — \`${candidate.targetId}\``,
216
220
  );
217
221
  }
222
+ const evidence: EvidenceListMetadata = {
223
+ key: "orientation.candidates",
224
+ totalCount: outcome.totalCount,
225
+ shownCount: candidates.length,
226
+ omittedCount: outcome.omittedCount,
227
+ partialReason: outcome.partialReason,
228
+ };
229
+ const disclosure = renderEvidenceListMetadataDisclosure(evidence);
230
+ if (disclosure) lines.push("", disclosure);
218
231
  const nextQueries =
219
232
  outcome.kind === "kind-mismatch"
220
233
  ? ["Retry without symbolKind, use an observed provider kind, or focus one handle"]
221
234
  : ["Use one candidate handle as focus.target.handle"];
222
235
  const details = assembleOrientationDetails({
223
236
  confidence: "semantic",
224
- omittedCount: outcome.omittedCount,
237
+ omittedCount: evidence.omittedCount ?? 0,
238
+ evidenceLists: [evidence],
225
239
  candidates,
226
240
  nextQueries,
227
241
  });
@@ -231,7 +245,8 @@ export function finishOrientationResult(outcome: OrientationOutcome): CodeIntelR
231
245
  type: "context",
232
246
  data: details,
233
247
  status: "completed",
234
- displaySections: orientationCandidateDisplaySections(candidates, outcome.omittedCount),
248
+ message: orientationSelectionMessage(outcome),
249
+ displaySections: orientationCandidateDisplaySections(candidates, evidence),
235
250
  },
236
251
  };
237
252
  }
@@ -246,3 +261,11 @@ export function finishOrientationResult(outcome: OrientationOutcome): CodeIntelR
246
261
  },
247
262
  };
248
263
  }
264
+
265
+ function orientationSelectionMessage(
266
+ outcome: Extract<OrientationOutcome, { kind: "disambiguation" | "kind-mismatch" }>,
267
+ ): string {
268
+ return outcome.kind === "kind-mismatch"
269
+ ? `No Orientation target matched provider kind ${outcome.requestedKind}.`
270
+ : "Multiple Orientation targets require one candidate.";
271
+ }