@mrclrchtr/supi-code-intelligence 6.4.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.
- package/README.md +164 -82
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/README.md +35 -13
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +43 -35
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +83 -17
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +108 -92
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +44 -82
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +3 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +9 -68
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +45 -58
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +230 -331
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +201 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +129 -70
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-typescript.ts +395 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +8 -12
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +685 -183
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +60 -60
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-request-enrollment.ts +75 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-barrier.ts +443 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-errors.ts +30 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-file.ts +15 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +290 -37
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +72 -16
- package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +30 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +608 -45
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -11
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +19 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
- package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
- package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
- package/node_modules/yaml/dist/nodes/Alias.js +25 -23
- package/node_modules/yaml/package.json +1 -1
- package/package.json +5 -5
- package/src/analysis/health/diagnostics.ts +1 -2
- package/src/analysis/health/file-scope.ts +4 -1
- package/src/analysis/health/server-status.ts +1 -1
- package/src/analysis/relations/callees.ts +17 -8
- package/src/analysis/relations/implementations.ts +1 -2
- package/src/analysis/relations/references.ts +8 -4
- package/src/analysis/relations/types.ts +6 -30
- package/src/analysis/target/anchored.ts +9 -1
- package/src/analysis/target/symbol.ts +29 -11
- package/src/analysis/target/types.ts +14 -6
- package/src/api.ts +2 -0
- package/src/session/graph/collect.ts +18 -3
- package/src/session/health-refresh.ts +12 -1
- package/src/session/health-types.ts +18 -6
- package/src/session/health-workflow.ts +21 -19
- package/src/session/orientation-types.ts +5 -6
- package/src/session/orientation-workflow.ts +10 -2
- package/src/session/target-workflow.ts +9 -6
- package/src/substrate/lsp/source-tracking.ts +11 -6
- package/src/tool/code_find/guidance.ts +2 -2
- package/src/tool/code_find/spec.ts +5 -4
- package/src/tool/code_find/tui.ts +7 -1
- package/src/tool/code_graph/details.ts +32 -0
- package/src/tool/code_graph/display.ts +54 -0
- package/src/tool/code_graph/format.ts +60 -0
- package/src/tool/code_graph/guidance.ts +1 -1
- package/src/tool/code_graph/markdown.ts +76 -198
- package/src/tool/code_graph/read-next.ts +65 -0
- package/src/tool/code_graph/result.ts +118 -83
- package/src/tool/code_graph/tui.ts +216 -54
- package/src/tool/code_health/guidance.ts +2 -4
- package/src/tool/code_health/markdown.ts +75 -58
- package/src/tool/code_health/refresh-outcome.ts +41 -1
- package/src/tool/code_health/refresh-status.ts +114 -48
- package/src/tool/code_health/scope.ts +78 -0
- package/src/tool/code_health/spec.ts +1 -1
- package/src/tool/code_health/tui.ts +25 -38
- package/src/tool/code_inspect/tui.ts +7 -1
- package/src/tool/code_orientation/result.ts +31 -8
- package/src/tool/code_orientation/tui.ts +26 -5
- package/src/tool/code_refactor_apply/guidance.ts +1 -1
- package/src/tool/code_refactor_apply/tui.ts +7 -3
- package/src/tool/code_refactor_plan/guidance.ts +1 -1
- package/src/tool/code_refactor_plan/tui.ts +7 -1
- package/src/tool/code_resolve/guidance.ts +2 -4
- package/src/tool/code_resolve/result.ts +13 -10
- package/src/tool/code_resolve/tui.ts +18 -1
- package/src/tool/infra/truncate.ts +39 -13
- package/src/tool/result/candidate-row.ts +27 -0
- package/src/tool/result/errors.ts +21 -6
- package/src/tool/result/types.ts +7 -3
- package/src/tool/schemas.ts +5 -0
- package/src/types/execution.ts +2 -0
- package/src/types/index.ts +2 -0
- package/src/ui/tui/candidate-legacy-body.ts +69 -0
- package/src/ui/tui/candidate-selection.ts +366 -0
- package/src/ui/tui/common.ts +26 -31
- package/src/ui/tui/execution-error.ts +92 -0
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Report live diagnostics and language-server health. Diagnostic snapshots do not prove that the whole workspace is clean; server inventory and route-status counts are always workspace-wide.";
|
|
2
|
+
"Report live diagnostics and language-server health. Use refresh:true when you need fresh diagnostic evidence; without it, diagnostics may be a tracked-file snapshot. Diagnostic snapshots do not prove that the whole workspace is clean; server inventory and route-status counts are always workspace-wide.";
|
|
3
3
|
|
|
4
4
|
export const promptSnippet = "check live diagnostics and language-server health";
|
|
5
5
|
|
|
6
|
-
export const promptGuidelines = [
|
|
7
|
-
"Use code_health with `refresh: true` before relying on diagnostics that can be stale.",
|
|
8
|
-
];
|
|
6
|
+
export const promptGuidelines: string[] = [];
|
|
@@ -1,10 +1,4 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Markdown renderer for code_health results.
|
|
3
|
-
*
|
|
4
|
-
* Renders structured health data from the code_health executor into
|
|
5
|
-
* readable markdown sections keyed by requested `include` values.
|
|
6
|
-
*/
|
|
7
|
-
|
|
1
|
+
/** Markdown renderer for code_health results. */
|
|
8
2
|
import type { CapabilityWarningReport } from "../../analysis/capability/capability-warnings.ts";
|
|
9
3
|
import {
|
|
10
4
|
formatProjectServerRoot,
|
|
@@ -16,7 +10,9 @@ import {
|
|
|
16
10
|
formatProcessCrashRecovery,
|
|
17
11
|
formatRefreshElapsed,
|
|
18
12
|
formatSourceTracking,
|
|
13
|
+
formatSourceTrackingDetails,
|
|
19
14
|
formatStaleDiagnosticRestarts as formatStaleDiagnosticRestartsText,
|
|
15
|
+
formatStartupRetry,
|
|
20
16
|
isFileReadinessPending,
|
|
21
17
|
} from "./refresh-outcome.ts";
|
|
22
18
|
import type {
|
|
@@ -52,49 +48,52 @@ function renderRefreshStatus(
|
|
|
52
48
|
hasDiagnostics: boolean,
|
|
53
49
|
cwd: string,
|
|
54
50
|
): void {
|
|
55
|
-
if (!hasDiagnostics)
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
);
|
|
79
|
-
renderSourceTracking(lines, data.refresh);
|
|
80
|
-
lines.push("");
|
|
81
|
-
return;
|
|
82
|
-
}
|
|
83
|
-
case "not-attempted":
|
|
84
|
-
lines.push(`**Diagnostic refresh attempt**: not started — ${data.refresh.reason}`);
|
|
85
|
-
if (data.refresh.lastAttempt) renderLastAttempt(lines, data.refresh.lastAttempt, cwd);
|
|
86
|
-
lines.push("");
|
|
87
|
-
return;
|
|
88
|
-
case "not-requested":
|
|
89
|
-
if (data.refresh.lastAttempt) {
|
|
90
|
-
renderRetainedAttempt(lines, data.refresh.lastAttempt, cwd);
|
|
91
|
-
} else {
|
|
92
|
-
lines.push(
|
|
93
|
-
"**Diagnostic refresh attempt**: not requested this session. Use `refresh: true` to try one.",
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
lines.push("");
|
|
51
|
+
if (!hasDiagnostics && data.refresh.kind !== "completed" && data.refresh.kind !== "failed") {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (data.refresh.kind === "completed") {
|
|
55
|
+
renderCompletedRefreshStatus(lines, data);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (data.refresh.kind === "failed") {
|
|
59
|
+
renderFailedRefreshStatus(lines, data.refresh);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (data.refresh.kind === "not-attempted") {
|
|
63
|
+
lines.push(`**Diagnostic refresh attempt**: not started — ${data.refresh.reason}`);
|
|
64
|
+
if (data.refresh.lastAttempt) renderLastAttempt(lines, data.refresh.lastAttempt, cwd);
|
|
65
|
+
lines.push("");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
if (data.refresh.lastAttempt) {
|
|
69
|
+
renderRetainedAttempt(lines, data.refresh.lastAttempt, cwd);
|
|
70
|
+
} else {
|
|
71
|
+
lines.push(
|
|
72
|
+
"**Diagnostic refresh attempt**: not requested this session. Use `refresh: true` to try one.",
|
|
73
|
+
);
|
|
97
74
|
}
|
|
75
|
+
lines.push("");
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function renderCompletedRefreshStatus(lines: string[], data: HealthData): void {
|
|
79
|
+
if (data.refresh.kind !== "completed") return;
|
|
80
|
+
const fileReadinessPending = isFileReadinessPending(data.refresh, data.semanticState);
|
|
81
|
+
lines.push(
|
|
82
|
+
`**${refreshAttemptLabel(data.refresh)}**: ${asSentence(completedRefreshText(data.refresh, fileReadinessPending, false))}`,
|
|
83
|
+
);
|
|
84
|
+
renderSourceTracking(lines, data.refresh);
|
|
85
|
+
lines.push(`**Stale assessment**: ${asSentence(staleAssessmentText(data.refresh))}`);
|
|
86
|
+
lines.push("");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function renderFailedRefreshStatus(
|
|
90
|
+
lines: string[],
|
|
91
|
+
attempt: Extract<HealthRefreshAttempt, { kind: "failed" }>,
|
|
92
|
+
): void {
|
|
93
|
+
const outcome = failedRefreshText(attempt, false);
|
|
94
|
+
lines.push(`**${refreshAttemptLabel(attempt)}**: ${asSentence(outcome)}`);
|
|
95
|
+
renderSourceTracking(lines, attempt);
|
|
96
|
+
lines.push("");
|
|
98
97
|
}
|
|
99
98
|
|
|
100
99
|
function completedRefreshText(
|
|
@@ -103,11 +102,13 @@ function completedRefreshText(
|
|
|
103
102
|
includeSourceTracking = true,
|
|
104
103
|
): string {
|
|
105
104
|
const processCrashRecovery = formatProcessCrashRecovery(attempt.processCrashRecovery);
|
|
105
|
+
const startupRetry = formatStartupRetry(attempt.startupRetry);
|
|
106
106
|
const noOp =
|
|
107
107
|
!fileReadinessPending &&
|
|
108
108
|
attempt.attemptedActiveClients === 0 &&
|
|
109
109
|
attempt.restartedClients === 0 &&
|
|
110
|
-
processCrashRecovery === null
|
|
110
|
+
processCrashRecovery === null &&
|
|
111
|
+
startupRetry === null;
|
|
111
112
|
const base = fileReadinessPending
|
|
112
113
|
? "readiness pending — LSP may still be warming; retry shortly"
|
|
113
114
|
: noOp
|
|
@@ -119,18 +120,20 @@ function completedRefreshText(
|
|
|
119
120
|
includeSourceTracking ? sourceTracking : null,
|
|
120
121
|
staleRestart,
|
|
121
122
|
processCrashRecovery,
|
|
123
|
+
startupRetry,
|
|
122
124
|
].filter((value): value is string => value !== null);
|
|
123
125
|
const withOutcome = outcome.length > 0 ? `${base}; ${outcome.join("; ")}` : base;
|
|
126
|
+
const withScope = `${withOutcome}; maintenance scope: ${refreshOperationScopeText(attempt)}`;
|
|
124
127
|
return attempt.operationScope === "workspace-runtime"
|
|
125
|
-
? `${
|
|
126
|
-
:
|
|
128
|
+
? `${withScope}; maintenance evidence: ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}`
|
|
129
|
+
: withScope;
|
|
127
130
|
}
|
|
128
131
|
|
|
129
132
|
function renderSourceTracking(
|
|
130
133
|
lines: string[],
|
|
131
134
|
attempt: Pick<HealthRefreshAttempt, "sourceTracking">,
|
|
132
135
|
): void {
|
|
133
|
-
const sourceTracking =
|
|
136
|
+
const sourceTracking = formatSourceTrackingDetails(attempt.sourceTracking);
|
|
134
137
|
if (sourceTracking) lines.push(`**Source discovery**: ${asSentence(sourceTracking)}`);
|
|
135
138
|
}
|
|
136
139
|
|
|
@@ -157,7 +160,7 @@ function renderLastAttempt(lines: string[], attempt: HealthRefreshAttempt, cwd:
|
|
|
157
160
|
const outcome = refreshAttemptOutcome(attempt);
|
|
158
161
|
const label = refreshAttemptLabel(attempt);
|
|
159
162
|
lines.push(
|
|
160
|
-
`**Previous ${label.toLowerCase()}**: ${asSentence(outcome)} Started ${formatRefreshElapsed(Date.now() - attempt.attemptedAt)}; requested ${formatDiagnosticScope(attempt.requestedDiagnosticScope, cwd)}
|
|
163
|
+
`**Previous ${label.toLowerCase()}**: ${asSentence(outcome)} Started ${formatRefreshElapsed(Date.now() - attempt.attemptedAt)}; requested evidence scope: ${formatDiagnosticScope(attempt.requestedDiagnosticScope, cwd)}.`,
|
|
161
164
|
);
|
|
162
165
|
}
|
|
163
166
|
|
|
@@ -165,21 +168,35 @@ function renderRetainedAttempt(lines: string[], attempt: HealthRefreshAttempt, c
|
|
|
165
168
|
const label =
|
|
166
169
|
attempt.operationScope === "file-runtime" ? "File LSP maintenance" : "Diagnostic refresh";
|
|
167
170
|
lines.push(
|
|
168
|
-
`**${label}**: not requested for this call. Previous attempt ${asSentence(refreshAttemptOutcome(attempt))} Started ${formatRefreshElapsed(Date.now() - attempt.attemptedAt)}; requested ${formatDiagnosticScope(attempt.requestedDiagnosticScope, cwd)}
|
|
171
|
+
`**${label}**: not requested for this call. Previous attempt ${asSentence(refreshAttemptOutcome(attempt))} Started ${formatRefreshElapsed(Date.now() - attempt.attemptedAt)}; requested evidence scope: ${formatDiagnosticScope(attempt.requestedDiagnosticScope, cwd)}.`,
|
|
169
172
|
);
|
|
170
173
|
}
|
|
171
174
|
|
|
172
175
|
function refreshAttemptOutcome(attempt: HealthRefreshAttempt): string {
|
|
173
176
|
if (attempt.kind === "completed") return completedRefreshText(attempt);
|
|
177
|
+
return failedRefreshText(attempt);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function failedRefreshText(
|
|
181
|
+
attempt: Extract<HealthRefreshAttempt, { kind: "failed" }>,
|
|
182
|
+
includeSourceTracking = true,
|
|
183
|
+
): string {
|
|
174
184
|
const processCrashRecovery = attempt.processCrashRecovery
|
|
175
185
|
? formatProcessCrashRecovery(attempt.processCrashRecovery)
|
|
176
186
|
: null;
|
|
187
|
+
const startupRetry = attempt.startupRetry ? formatStartupRetry(attempt.startupRetry) : null;
|
|
177
188
|
const staleRestart = formatStaleDiagnosticRestarts(attempt);
|
|
178
|
-
const sourceTracking =
|
|
189
|
+
const sourceTracking = includeSourceTracking
|
|
190
|
+
? formatSourceTracking(attempt.sourceTracking)
|
|
191
|
+
: null;
|
|
179
192
|
const evidence = attempt.diagnosticEvidence
|
|
180
|
-
? `; ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}`
|
|
193
|
+
? `; maintenance evidence: ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}`
|
|
181
194
|
: "";
|
|
182
|
-
return `failed — ${attempt.reason}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${sourceTracking ? `; ${sourceTracking}` : ""}${evidence}`;
|
|
195
|
+
return `failed — ${stripTrailingPunctuation(attempt.reason)}; maintenance scope: ${refreshOperationScopeText(attempt)}${staleRestart ? `; ${staleRestart}` : ""}${processCrashRecovery ? `; ${processCrashRecovery}` : ""}${startupRetry ? `; ${startupRetry}` : ""}${sourceTracking ? `; ${sourceTracking}` : ""}${evidence}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
function stripTrailingPunctuation(text: string): string {
|
|
199
|
+
return text.replace(/[.!?]+$/, "");
|
|
183
200
|
}
|
|
184
201
|
|
|
185
202
|
function refreshOperationScopeText(attempt: HealthRefreshAttempt): string {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
ProcessCrashRecoveryEntry,
|
|
3
3
|
ProcessCrashRecoveryReport,
|
|
4
|
+
StartupRetryEntry,
|
|
5
|
+
StartupRetryReport,
|
|
4
6
|
} from "@mrclrchtr/supi-lsp/api";
|
|
5
7
|
import type { SemanticHealthState } from "../../session/health-types.ts";
|
|
6
8
|
import type { SourceTrackingReport } from "../../substrate/lsp/source-tracking.ts";
|
|
@@ -65,14 +67,52 @@ function formatProcessCrashRecoveryEntry(entry: ProcessCrashRecoveryEntry): stri
|
|
|
65
67
|
return `${entry.name} @ ${entry.root}: ${outcome}${action}${failure}`;
|
|
66
68
|
}
|
|
67
69
|
|
|
70
|
+
/** Format initial-start retry counts for the compact health view. */
|
|
71
|
+
export function formatCompactStartupRetry(
|
|
72
|
+
report: StartupRetryReport | null | undefined,
|
|
73
|
+
): string | null {
|
|
74
|
+
if (!report || (report.recoveredRoutes === 0 && report.failedRoutes === 0)) return null;
|
|
75
|
+
return `startup retry: ${report.recoveredRoutes} route${plural(report.recoveredRoutes)} recovered (${report.failedRoutes} failed)`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Format initial-start retry counts and bounded route entries. */
|
|
79
|
+
export function formatStartupRetry(report: StartupRetryReport | null | undefined): string | null {
|
|
80
|
+
const summary = formatCompactStartupRetry(report);
|
|
81
|
+
if (!summary || !report) return summary;
|
|
82
|
+
const entries = report.entries.map(formatStartupRetryEntry);
|
|
83
|
+
const omitted =
|
|
84
|
+
report.omittedEntries > 0
|
|
85
|
+
? `${report.omittedEntries} more route${plural(report.omittedEntries)}`
|
|
86
|
+
: null;
|
|
87
|
+
const details = [...entries, omitted]
|
|
88
|
+
.filter((value): value is string => value !== null)
|
|
89
|
+
.join("; ");
|
|
90
|
+
return details ? `${summary}; ${details}` : summary;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function formatStartupRetryEntry(entry: StartupRetryEntry): string {
|
|
94
|
+
const outcome = entry.outcome.replaceAll("-", " ");
|
|
95
|
+
const action = entry.nextAction ? `; next: ${entry.nextAction.replaceAll("-", " ")}` : "";
|
|
96
|
+
const failure = entry.failureMessage ? `; ${entry.failureMessage}` : "";
|
|
97
|
+
return `${entry.name} @ ${entry.root}: ${outcome}${action}${failure}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
68
100
|
/** Format source discovery and bounded tracking counts separately from diagnostics. */
|
|
69
101
|
export function formatSourceTracking(
|
|
70
102
|
report: SourceTrackingReport | null | undefined,
|
|
103
|
+
): string | null {
|
|
104
|
+
const details = formatSourceTrackingDetails(report);
|
|
105
|
+
return details ? `source discovery: ${details}` : null;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/** Format source tracking counts without a label for a standalone heading. */
|
|
109
|
+
export function formatSourceTrackingDetails(
|
|
110
|
+
report: SourceTrackingReport | null | undefined,
|
|
71
111
|
): string | null {
|
|
72
112
|
if (!report) return null;
|
|
73
113
|
const inventory =
|
|
74
114
|
report.status === "complete" ? "complete" : `limited (${report.reason ?? "unknown reason"})`;
|
|
75
|
-
return
|
|
115
|
+
return `${inventory}; ${report.observedFileCount} source files observed, ${report.discovered.length} discovered, ${report.tracked.length} tracked, ${report.skipped.length} skipped, ${report.unavailable.length} unavailable, ${report.deferred} deferred`;
|
|
76
116
|
}
|
|
77
117
|
|
|
78
118
|
/** Format the age of a retained refresh attempt for a status line. */
|
|
@@ -3,13 +3,22 @@ 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. */
|
|
@@ -19,7 +28,7 @@ export function readSourceTrackingStatus(source: Record<string, unknown> | null)
|
|
|
19
28
|
if (!source || (source.status !== "complete" && source.status !== "limited")) return null;
|
|
20
29
|
const reason =
|
|
21
30
|
source.status === "limited" && typeof source.reason === "string" ? ` (${source.reason})` : "";
|
|
22
|
-
return `${source.status}${reason}: ${readNumber(source.observedFileCount)} observed, ${readArrayLength(source.discovered)} discovered, ${readArrayLength(source.tracked)} tracked, ${readArrayLength(source.
|
|
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`;
|
|
23
32
|
}
|
|
24
33
|
|
|
25
34
|
export function readRefreshStatus(data: Record<string, unknown> | null): string | null {
|
|
@@ -49,18 +58,31 @@ function formatCompletedRefreshStatus(
|
|
|
49
58
|
const processCrashRecovery = formatProcessCrashRecovery(
|
|
50
59
|
readProcessCrashRecovery(refresh.processCrashRecovery),
|
|
51
60
|
);
|
|
61
|
+
const startupRetry = formatStartupRetry(readStartupRetry(refresh.startupRetry));
|
|
52
62
|
const noOp =
|
|
53
|
-
!fileReadinessPending &&
|
|
63
|
+
!fileReadinessPending &&
|
|
64
|
+
attempted === 0 &&
|
|
65
|
+
restarted === 0 &&
|
|
66
|
+
processCrashRecovery === null &&
|
|
67
|
+
startupRetry === null;
|
|
54
68
|
const label = refreshAttemptLabel(refresh);
|
|
55
69
|
const base = fileReadinessPending
|
|
56
70
|
? `${label} waiting — LSP may still be warming; retry shortly`
|
|
57
71
|
: noOp
|
|
58
72
|
? `${label} completed no-op`
|
|
59
73
|
: `${label} completed: ${attempted} clients targeted`;
|
|
60
|
-
const outcomes = formatRefreshOutcomes(
|
|
74
|
+
const outcomes = formatRefreshOutcomes({
|
|
75
|
+
refresh,
|
|
76
|
+
restarted,
|
|
77
|
+
processCrashRecovery,
|
|
78
|
+
startupRetry,
|
|
79
|
+
noOp,
|
|
80
|
+
});
|
|
61
81
|
const withOutcome = outcomes.length > 0 ? `${base}; ${outcomes.join("; ")}` : base;
|
|
62
|
-
const
|
|
63
|
-
const
|
|
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;
|
|
64
86
|
const stale = readRecord(refresh.staleAssessment);
|
|
65
87
|
return stale?.suspected === true
|
|
66
88
|
? `${withEvidence}; stale pattern suspected in ${readNumber(stale.matchedFileCount)} files`
|
|
@@ -71,23 +93,26 @@ function formatFailedRefreshStatus(refresh: Record<string, unknown>): string {
|
|
|
71
93
|
const processCrashRecovery = formatProcessCrashRecovery(
|
|
72
94
|
readProcessCrashRecovery(refresh.processCrashRecovery),
|
|
73
95
|
);
|
|
96
|
+
const startupRetry = formatStartupRetry(readStartupRetry(refresh.startupRetry));
|
|
74
97
|
const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
|
|
75
|
-
const
|
|
98
|
+
const maintenanceScope = formatMaintenanceScope(refresh);
|
|
99
|
+
const evidence = formatMaintenanceEvidence(readRecord(refresh.diagnosticEvidence));
|
|
76
100
|
const reason = typeof refresh.reason === "string" ? `: ${refresh.reason}` : "";
|
|
77
|
-
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}` : ""}`;
|
|
78
102
|
}
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
noOp: boolean
|
|
85
|
-
): 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[] {
|
|
86
110
|
const outcomes: string[] = [];
|
|
87
|
-
if (refresh.operationScope === "workspace-runtime" && !noOp) {
|
|
88
|
-
outcomes.push(formatStaleDiagnosticRestarts(restarted));
|
|
111
|
+
if (options.refresh.operationScope === "workspace-runtime" && !options.noOp) {
|
|
112
|
+
outcomes.push(formatStaleDiagnosticRestarts(options.restarted));
|
|
89
113
|
}
|
|
90
|
-
if (processCrashRecovery) outcomes.push(processCrashRecovery);
|
|
114
|
+
if (options.processCrashRecovery) outcomes.push(options.processCrashRecovery);
|
|
115
|
+
if (options.startupRetry) outcomes.push(options.startupRetry);
|
|
91
116
|
return outcomes;
|
|
92
117
|
}
|
|
93
118
|
|
|
@@ -100,10 +125,12 @@ export function readCompactRefreshStatus(data: Record<string, unknown> | null):
|
|
|
100
125
|
const processCrashRecovery = formatCompactProcessCrashRecovery(
|
|
101
126
|
readProcessCrashRecovery(refresh.processCrashRecovery),
|
|
102
127
|
);
|
|
128
|
+
const startupRetry = formatCompactStartupRetry(readStartupRetry(refresh.startupRetry));
|
|
129
|
+
const maintenanceScope = formatMaintenanceScope(refresh);
|
|
103
130
|
if (refresh.kind === "failed") {
|
|
104
131
|
const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
|
|
105
132
|
return (
|
|
106
|
-
[sourceTracking, staleRestart, processCrashRecovery]
|
|
133
|
+
[maintenanceScope, sourceTracking, staleRestart, processCrashRecovery, startupRetry]
|
|
107
134
|
.filter((value): value is string => value !== null)
|
|
108
135
|
.join("; ") || null
|
|
109
136
|
);
|
|
@@ -112,6 +139,7 @@ export function readCompactRefreshStatus(data: Record<string, unknown> | null):
|
|
|
112
139
|
if (
|
|
113
140
|
!sourceTracking &&
|
|
114
141
|
!processCrashRecovery &&
|
|
142
|
+
!startupRetry &&
|
|
115
143
|
readNumber(refresh.restartedClients) === 0 &&
|
|
116
144
|
!fileReadinessPending
|
|
117
145
|
) {
|
|
@@ -120,7 +148,14 @@ export function readCompactRefreshStatus(data: Record<string, unknown> | null):
|
|
|
120
148
|
|
|
121
149
|
const staleRestart = formatStaleDiagnosticRestartsForRecord(refresh);
|
|
122
150
|
const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
|
|
123
|
-
return [
|
|
151
|
+
return [
|
|
152
|
+
maintenanceScope,
|
|
153
|
+
sourceTracking,
|
|
154
|
+
staleRestart,
|
|
155
|
+
processCrashRecovery,
|
|
156
|
+
startupRetry,
|
|
157
|
+
readiness,
|
|
158
|
+
]
|
|
124
159
|
.filter((value): value is string => value !== null)
|
|
125
160
|
.join("; ");
|
|
126
161
|
}
|
|
@@ -132,17 +167,26 @@ export function readPreviousRefreshStatus(data: Record<string, unknown> | null):
|
|
|
132
167
|
}
|
|
133
168
|
const attempt = readRecord(refresh.lastAttempt);
|
|
134
169
|
if (!attempt) return null;
|
|
135
|
-
const evidence =
|
|
170
|
+
const evidence = formatCompactMaintenanceEvidence(readRecord(attempt.diagnosticEvidence));
|
|
136
171
|
const sourceTracking = formatSourceTracking(readRecord(attempt.sourceTracking));
|
|
137
172
|
const processCrashRecovery = formatCompactProcessCrashRecovery(
|
|
138
173
|
readProcessCrashRecovery(attempt.processCrashRecovery),
|
|
139
174
|
);
|
|
175
|
+
const startupRetry = formatCompactStartupRetry(readStartupRetry(attempt.startupRetry));
|
|
140
176
|
const staleRestart = formatStaleDiagnosticRestartsForRecord(attempt);
|
|
141
177
|
const fileReadinessPending =
|
|
142
178
|
attempt.kind === "completed" &&
|
|
143
179
|
isFileReadinessPending(attempt, readSemanticHealthState(data?.semanticState));
|
|
144
180
|
const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
|
|
145
|
-
const outcome = [
|
|
181
|
+
const outcome = [
|
|
182
|
+
readiness,
|
|
183
|
+
formatMaintenanceScope(attempt),
|
|
184
|
+
sourceTracking,
|
|
185
|
+
staleRestart,
|
|
186
|
+
processCrashRecovery,
|
|
187
|
+
startupRetry,
|
|
188
|
+
evidence,
|
|
189
|
+
]
|
|
146
190
|
.filter((value): value is string => value !== null)
|
|
147
191
|
.join("; ");
|
|
148
192
|
if (attempt.kind === "failed") {
|
|
@@ -180,16 +224,25 @@ function formatStaleDiagnosticRestartsForRecord(attempt: Record<string, unknown>
|
|
|
180
224
|
function formatLastRefreshSuffix(refresh: Record<string, unknown>): string {
|
|
181
225
|
const attempt = readRecord(refresh.lastAttempt);
|
|
182
226
|
if (!attempt) return "";
|
|
183
|
-
const evidence =
|
|
227
|
+
const evidence = formatMaintenanceEvidence(readRecord(attempt.diagnosticEvidence));
|
|
184
228
|
const sourceTracking = formatSourceTracking(readRecord(attempt.sourceTracking));
|
|
185
229
|
const staleRestart = formatStaleDiagnosticRestartsForRecord(attempt);
|
|
186
230
|
const processCrashRecovery = formatProcessCrashRecovery(
|
|
187
231
|
readProcessCrashRecovery(attempt.processCrashRecovery),
|
|
188
232
|
);
|
|
233
|
+
const startupRetry = formatStartupRetry(readStartupRetry(attempt.startupRetry));
|
|
189
234
|
const fileReadinessPending =
|
|
190
235
|
attempt.kind === "completed" && isFileReadinessPending(attempt, null);
|
|
191
236
|
const readiness = fileReadinessPending ? "LSP may still be warming; retry shortly" : null;
|
|
192
|
-
const outcome = [
|
|
237
|
+
const outcome = [
|
|
238
|
+
readiness,
|
|
239
|
+
formatMaintenanceScope(attempt),
|
|
240
|
+
sourceTracking,
|
|
241
|
+
staleRestart,
|
|
242
|
+
processCrashRecovery,
|
|
243
|
+
startupRetry,
|
|
244
|
+
evidence,
|
|
245
|
+
]
|
|
193
246
|
.filter((value): value is string => value !== null)
|
|
194
247
|
.join("; ");
|
|
195
248
|
if (attempt.kind === "failed") {
|
|
@@ -212,18 +265,6 @@ function readArrayLength(value: unknown): number {
|
|
|
212
265
|
return Array.isArray(value) ? value.length : 0;
|
|
213
266
|
}
|
|
214
267
|
|
|
215
|
-
function formatCompactDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
|
|
216
|
-
const counts = readEvidenceCounts(evidence);
|
|
217
|
-
if (!counts) return null;
|
|
218
|
-
return `req ${counts[0]} · conf ${counts[1]} · unconf ${counts[2]} · failed ${counts[3]} · removed ${counts[4]}`;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
function formatDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
|
|
222
|
-
const counts = readEvidenceCounts(evidence);
|
|
223
|
-
if (!counts) return null;
|
|
224
|
-
return `${counts[0]} requested, ${counts[1]} confirmed, ${counts[2]} unconfirmed, ${counts[3]} failed, ${counts[4]} removed`;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
268
|
export function isPendingFileReadiness(data: Record<string, unknown> | null): boolean {
|
|
228
269
|
const observation = readRecord(data?.diagnosticObservation);
|
|
229
270
|
const scope = readRecord(observation?.scope);
|
|
@@ -234,6 +275,43 @@ export function isPendingFileReadiness(data: Record<string, unknown> | null): bo
|
|
|
234
275
|
return isFileReadinessPending(attempt, readSemanticHealthState(data?.semanticState));
|
|
235
276
|
}
|
|
236
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
|
+
|
|
237
315
|
function readProcessCrashRecovery(value: unknown): ProcessCrashRecoveryReport | null {
|
|
238
316
|
const report = readRecord(value);
|
|
239
317
|
if (!report) return null;
|
|
@@ -267,7 +345,7 @@ function readProcessCrashRecoveryEntry(value: unknown): ProcessCrashRecoveryEntr
|
|
|
267
345
|
if (
|
|
268
346
|
entry.nextAction !== undefined &&
|
|
269
347
|
entry.nextAction !== "use-exact-file" &&
|
|
270
|
-
entry.nextAction !== "
|
|
348
|
+
entry.nextAction !== "refresh"
|
|
271
349
|
) {
|
|
272
350
|
return null;
|
|
273
351
|
}
|
|
@@ -290,18 +368,6 @@ function isProcessCrashRecoveryOutcome(value: unknown): value is ProcessCrashRec
|
|
|
290
368
|
);
|
|
291
369
|
}
|
|
292
370
|
|
|
293
|
-
function readEvidenceCounts(evidence: Record<string, unknown> | null): number[] | null {
|
|
294
|
-
if (!evidence) return null;
|
|
295
|
-
const counts = [
|
|
296
|
-
evidence.requested,
|
|
297
|
-
evidence.confirmed,
|
|
298
|
-
evidence.unconfirmed,
|
|
299
|
-
evidence.failed,
|
|
300
|
-
evidence.removed,
|
|
301
|
-
];
|
|
302
|
-
return counts.every(isEvidenceCount) ? counts : null;
|
|
303
|
-
}
|
|
304
|
-
|
|
305
371
|
function isEvidenceCount(value: unknown): value is number {
|
|
306
372
|
return typeof value === "number" && Number.isInteger(value) && value >= 0;
|
|
307
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
|
-
"
|
|
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(
|