@mrclrchtr/supi-code-intelligence 6.0.1 → 6.1.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 +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +5 -0
- package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/README.md +5 -1
- 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/capability/types.ts +5 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-jsonrpc/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.colorProvider.d.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.d.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.fileOperations.d.ts +9 -9
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/esm/main.d.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/esm/main.js +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/umd/main.d.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/umd/main.js +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +6 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +8 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +1 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +6 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +582 -26
- package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +2 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +10 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +36 -9
- package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +2 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +4 -2
- 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/capability/types.ts +5 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +1 -1
- package/package.json +5 -5
- package/src/analysis/health/recovery.ts +4 -0
- package/src/analysis/health/server-status.ts +64 -0
- package/src/analysis/health/signals.ts +1 -0
- package/src/analysis/provider.ts +2 -1
- package/src/analysis/target/symbol.ts +28 -16
- package/src/analysis/target/types.ts +1 -0
- package/src/session/find-workflow.ts +5 -12
- package/src/session/health-refresh.ts +6 -2
- package/src/session/health-types.ts +8 -2
- package/src/session/health-workflow.ts +5 -1
- package/src/session/semantic-demand.ts +36 -0
- package/src/session/target-workflow.ts +9 -12
- package/src/tool/code_health/guidance.ts +2 -2
- package/src/tool/code_health/markdown.ts +15 -3
- package/src/tool/code_health/result.ts +14 -5
- package/src/tool/code_health/spec.ts +2 -1
- package/src/tool/code_health/tui.ts +24 -3
- package/src/tool/result/errors.ts +1 -0
- package/src/tool/result/types.ts +6 -0
- package/src/ui/footer.ts +7 -5
- package/src/ui/status-command.ts +31 -0
- package/src/ui/status-overlay.ts +69 -23
|
@@ -23,6 +23,7 @@ import type {
|
|
|
23
23
|
} from "../analysis/target/types.ts";
|
|
24
24
|
import type { CapabilityAdapter } from "./capability-adapter.ts";
|
|
25
25
|
import { parseTargetInput } from "./input/common.ts";
|
|
26
|
+
import { resolveWorkspaceSemanticDemand } from "./semantic-demand.ts";
|
|
26
27
|
import { registerTargetCandidates, type TargetWorkflowCandidate } from "./target-candidates.ts";
|
|
27
28
|
import type { TargetInput, TargetSymbolKind } from "./target-input.ts";
|
|
28
29
|
import {
|
|
@@ -201,22 +202,15 @@ async function resolveSymbolWorkflow(
|
|
|
201
202
|
scope = resolved.path;
|
|
202
203
|
}
|
|
203
204
|
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
{ kind: "workspace" },
|
|
207
|
-
control,
|
|
208
|
-
);
|
|
209
|
-
if (readiness.kind === "timeout") {
|
|
205
|
+
const semanticDemand = await resolveWorkspaceSemanticDemand(deps.capability, deps.cwd, control);
|
|
206
|
+
if (semanticDemand.kind === "timeout") {
|
|
210
207
|
return { kind: "unavailable", reason: "LSP readiness timed out. Retry shortly." };
|
|
211
208
|
}
|
|
212
|
-
if (
|
|
213
|
-
return { kind: "unavailable", reason:
|
|
209
|
+
if (semanticDemand.kind === "unavailable") {
|
|
210
|
+
return { kind: "unavailable", reason: semanticDemand.reason };
|
|
214
211
|
}
|
|
215
212
|
|
|
216
|
-
const provider = withSemanticRequestControl(
|
|
217
|
-
deps.capability.getSemanticProvider(deps.cwd),
|
|
218
|
-
control,
|
|
219
|
-
);
|
|
213
|
+
const provider = withSemanticRequestControl(semanticDemand.provider, control);
|
|
220
214
|
if (!provider) {
|
|
221
215
|
return {
|
|
222
216
|
kind: "unavailable",
|
|
@@ -291,6 +285,9 @@ function toWorkflowOutcome(
|
|
|
291
285
|
policy: TargetWorkflowPolicy,
|
|
292
286
|
deps: TargetWorkflowDeps,
|
|
293
287
|
): TargetWorkflowOutcome {
|
|
288
|
+
if (outcome.kind === "unavailable") {
|
|
289
|
+
return { kind: "unavailable", reason: outcome.reason };
|
|
290
|
+
}
|
|
294
291
|
if (outcome.kind === "error") {
|
|
295
292
|
return { kind: "invalid-input", message: outcome.message };
|
|
296
293
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
export const toolDescription =
|
|
2
|
-
"Report live diagnostics as observations, language-server status, and final semantic health state. Tracked-file snapshots do not prove workspace completeness; server inventory
|
|
2
|
+
"Report live diagnostics as observations, language-server route status, and final semantic health state. Tracked-file snapshots do not prove workspace completeness; server inventory and route issue counts are workspace-wide. Server-only calls are passive.";
|
|
3
3
|
|
|
4
4
|
export const promptSnippet = "live workspace health observations";
|
|
5
5
|
|
|
6
6
|
export const promptGuidelines = [
|
|
7
|
-
"Use code_health with refresh:true before relying on potentially stale diagnostics.",
|
|
7
|
+
"Use code_health with refresh:true before relying on potentially stale diagnostics; explicit refresh can recover crashed routes with tracked files in scope.",
|
|
8
8
|
];
|
|
@@ -6,6 +6,11 @@
|
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import type { CapabilityWarningReport } from "../../analysis/capability/capability-warnings.ts";
|
|
9
|
+
import {
|
|
10
|
+
formatProjectServerRoot,
|
|
11
|
+
formatProjectServerRouteSummary,
|
|
12
|
+
formatProjectServerStatusReason,
|
|
13
|
+
} from "../../analysis/health/server-status.ts";
|
|
9
14
|
import type {
|
|
10
15
|
HealthData,
|
|
11
16
|
HealthDiagnosticObservation,
|
|
@@ -28,7 +33,9 @@ export function renderHealthResult(result: HealthResultAssembly, cwd: string): s
|
|
|
28
33
|
renderRefreshStatus(lines, data, hasDiagnostics, cwd);
|
|
29
34
|
if (hasDiagnostics) renderDiagnosticsSection(lines, data, cwd);
|
|
30
35
|
if (semanticRequested) renderCapabilityWarningsSection(lines, result.details.capabilityWarnings);
|
|
31
|
-
if (hasSection("servers"))
|
|
36
|
+
if (hasSection("servers")) {
|
|
37
|
+
renderServersSection(lines, data, sectionStatus(result, "servers"), cwd);
|
|
38
|
+
}
|
|
32
39
|
return lines.join("\n");
|
|
33
40
|
}
|
|
34
41
|
|
|
@@ -158,7 +165,8 @@ function renderCapabilityWarningsSection(
|
|
|
158
165
|
function renderStatusLine(lines: string[], data: HealthData, semanticRequested: boolean): void {
|
|
159
166
|
if (!semanticRequested) return;
|
|
160
167
|
|
|
161
|
-
|
|
168
|
+
const routeSummary = formatProjectServerRouteSummary(data.servers);
|
|
169
|
+
lines.push(`**LSP**: ${displaySemanticStatus(data)}${routeSummary ? ` — ${routeSummary}` : ""}`);
|
|
162
170
|
if (data.structuralStatus) lines.push(`**Structural**: ${data.structuralStatus}`);
|
|
163
171
|
lines.push("");
|
|
164
172
|
}
|
|
@@ -316,6 +324,7 @@ function renderServersSection(
|
|
|
316
324
|
lines: string[],
|
|
317
325
|
data: HealthData,
|
|
318
326
|
status: "complete" | "partial" | "unavailable" | undefined,
|
|
327
|
+
cwd: string,
|
|
319
328
|
): void {
|
|
320
329
|
lines.push("### Servers");
|
|
321
330
|
lines.push("");
|
|
@@ -329,8 +338,11 @@ function renderServersSection(
|
|
|
329
338
|
} else {
|
|
330
339
|
for (const server of data.servers) {
|
|
331
340
|
const statusIcon = server.status === "running" ? "✓" : "✗";
|
|
341
|
+
const reason = server.statusReason
|
|
342
|
+
? ` — ${formatProjectServerStatusReason(server.statusReason)}`
|
|
343
|
+
: "";
|
|
332
344
|
lines.push(
|
|
333
|
-
`- ${statusIcon} **${server.name}** (${server.fileTypes.join(", ")}) — ${server.status}`,
|
|
345
|
+
`- ${statusIcon} **${server.name}** @ \`${formatProjectServerRoot(cwd, server.root)}\` (${server.fileTypes.join(", ")}) — ${server.status}${reason}`,
|
|
334
346
|
);
|
|
335
347
|
}
|
|
336
348
|
}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
import type { ConfidenceMode } from "@mrclrchtr/supi-code-runtime/api";
|
|
2
|
+
import {
|
|
3
|
+
countProjectServerRouteStatuses,
|
|
4
|
+
formatProjectServerRoot,
|
|
5
|
+
formatProjectServerStatusReason,
|
|
6
|
+
} from "../../analysis/health/server-status.ts";
|
|
2
7
|
import type { HealthData, HealthSection } from "../../session/health-types.ts";
|
|
3
8
|
import {
|
|
4
9
|
assembleToolResult,
|
|
@@ -36,7 +41,7 @@ const SECTION_TITLES: Record<HealthSection, string> = {
|
|
|
36
41
|
};
|
|
37
42
|
|
|
38
43
|
/** Assemble public code_health evidence and details before presentation adapters render it. */
|
|
39
|
-
export function assembleHealthResult(data: HealthData): HealthResultAssembly {
|
|
44
|
+
export function assembleHealthResult(data: HealthData, cwd?: string): HealthResultAssembly {
|
|
40
45
|
const projections = data.includedSections.map((section) => projectSection(section, data));
|
|
41
46
|
const sections = projections.map((projection) => projection.section);
|
|
42
47
|
const sectionDetails = projections.map((projection) => projection.details);
|
|
@@ -55,7 +60,7 @@ export function assembleHealthResult(data: HealthData): HealthResultAssembly {
|
|
|
55
60
|
provenance,
|
|
56
61
|
});
|
|
57
62
|
|
|
58
|
-
const displaySections = buildHealthDisplaySections(data);
|
|
63
|
+
const displaySections = buildHealthDisplaySections(data, cwd);
|
|
59
64
|
|
|
60
65
|
return {
|
|
61
66
|
data,
|
|
@@ -78,12 +83,13 @@ export function assembleHealthResult(data: HealthData): HealthResultAssembly {
|
|
|
78
83
|
capabilityWarnings: data.capabilityWarnings ?? null,
|
|
79
84
|
diagnosticFileCount: data.diagnostics.entries.length,
|
|
80
85
|
serverCount: data.servers.length,
|
|
86
|
+
serverRouteStatusCounts: countProjectServerRouteStatuses(data.servers),
|
|
81
87
|
evidenceLists: [...assembled.evidenceLists],
|
|
82
88
|
},
|
|
83
89
|
};
|
|
84
90
|
}
|
|
85
91
|
|
|
86
|
-
function buildHealthDisplaySections(data: HealthData): ToolDisplaySection[] {
|
|
92
|
+
function buildHealthDisplaySections(data: HealthData, cwd?: string): ToolDisplaySection[] {
|
|
87
93
|
const sections: ToolDisplaySection[] = [];
|
|
88
94
|
|
|
89
95
|
if (data.includedSections.includes("diagnostics") && data.diagnostics.entries.length > 0) {
|
|
@@ -107,7 +113,10 @@ function buildHealthDisplaySections(data: HealthData): ToolDisplaySection[] {
|
|
|
107
113
|
title: "Servers",
|
|
108
114
|
items: data.servers,
|
|
109
115
|
totalCount: data.servers.length,
|
|
110
|
-
format: (server) =>
|
|
116
|
+
format: (server) => {
|
|
117
|
+
const root = cwd ? formatProjectServerRoot(cwd, server.root) : server.root;
|
|
118
|
+
return `${server.name} @ ${root} (${server.fileTypes.join(", ")}) — ${server.status}${server.statusReason ? ` — ${formatProjectServerStatusReason(server.statusReason)}` : ""}`;
|
|
119
|
+
},
|
|
111
120
|
}),
|
|
112
121
|
);
|
|
113
122
|
}
|
|
@@ -255,7 +264,7 @@ export function finishHealthResult(outcome: HealthOutcome, cwd: string): CodeInt
|
|
|
255
264
|
return healthErrorResult(`**Error:** ${outcome.message}`, outcome.message);
|
|
256
265
|
}
|
|
257
266
|
|
|
258
|
-
const assembly = assembleHealthResult(outcome.data);
|
|
267
|
+
const assembly = assembleHealthResult(outcome.data, cwd);
|
|
259
268
|
return {
|
|
260
269
|
content: renderHealthResult(assembly, cwd),
|
|
261
270
|
details: {
|
|
@@ -17,7 +17,8 @@ export const codeHealthSpec = {
|
|
|
17
17
|
scope: Type.Optional(ScopeParam),
|
|
18
18
|
refresh: Type.Optional(
|
|
19
19
|
Type.Boolean({
|
|
20
|
-
description:
|
|
20
|
+
description:
|
|
21
|
+
"Attempt diagnostic and required process-crash recovery before collecting; result reports the outcome.",
|
|
21
22
|
}),
|
|
22
23
|
),
|
|
23
24
|
include: Type.Optional(
|
|
@@ -4,6 +4,10 @@
|
|
|
4
4
|
|
|
5
5
|
import type { Theme } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { Container, Spacer, Text } from "@earendil-works/pi-tui";
|
|
7
|
+
import {
|
|
8
|
+
formatProjectServerRouteStatusCounts,
|
|
9
|
+
type ProjectServerRouteStatusCounts,
|
|
10
|
+
} from "../../analysis/health/server-status.ts";
|
|
7
11
|
import {
|
|
8
12
|
type EvidenceEntry,
|
|
9
13
|
formatCallValue,
|
|
@@ -132,8 +136,11 @@ function buildCompactSummary(data: Record<string, unknown> | null, theme: Theme)
|
|
|
132
136
|
);
|
|
133
137
|
if (semanticRequested) {
|
|
134
138
|
const semanticStatus = readSemanticStatus(data);
|
|
139
|
+
const routeSummary = readServerRouteStatusSummary(data);
|
|
135
140
|
const statusColor = semanticStatus.startsWith("ready") ? "success" : "warning";
|
|
136
|
-
segments.push(
|
|
141
|
+
segments.push(
|
|
142
|
+
`${theme.fg("dim", "lsp")} ${theme.fg(statusColor, semanticStatus)}${routeSummary ? ` ${theme.fg("warning", routeSummary)}` : ""}`,
|
|
143
|
+
);
|
|
137
144
|
}
|
|
138
145
|
const previousRefresh = readPreviousRefreshStatus(data);
|
|
139
146
|
if (previousRefresh) segments.push(theme.fg("dim", previousRefresh));
|
|
@@ -179,7 +186,10 @@ function buildStatusBar(data: Record<string, unknown> | null, theme: Theme): Tex
|
|
|
179
186
|
const lspColor = semanticStatus.startsWith("ready") ? "success" : "warning";
|
|
180
187
|
const structuralColor = structuralStatus === "ready" ? "success" : "muted";
|
|
181
188
|
|
|
182
|
-
const
|
|
189
|
+
const routeSummary = readServerRouteStatusSummary(data);
|
|
190
|
+
const lines: string[] = [
|
|
191
|
+
`LSP: ${theme.fg(lspColor, semanticStatus)}${routeSummary ? ` — ${theme.fg("warning", routeSummary)}` : ""}`,
|
|
192
|
+
];
|
|
183
193
|
if (structuralStatus) {
|
|
184
194
|
lines.push(`Tree-sitter: ${theme.fg(structuralColor, structuralStatus)}`);
|
|
185
195
|
}
|
|
@@ -287,7 +297,8 @@ function buildHealthSectionSummary(data: Record<string, unknown> | null, theme:
|
|
|
287
297
|
if (section.status === "unavailable" || !section.available) {
|
|
288
298
|
return `${label} unavailable`;
|
|
289
299
|
}
|
|
290
|
-
|
|
300
|
+
const routeSummary = section.key === "servers" ? readServerRouteStatusSummary(data) : null;
|
|
301
|
+
return `${label} ${section.itemCount}${routeSummary ? ` — ${routeSummary}` : ""}`;
|
|
291
302
|
});
|
|
292
303
|
return new Text(theme.fg("muted", lines.join(" ")), 0, 0);
|
|
293
304
|
}
|
|
@@ -345,6 +356,16 @@ function readSemanticStatus(data: Record<string, unknown> | null): string {
|
|
|
345
356
|
return formatSemanticHealthState(readSemanticHealthState(data?.semanticState));
|
|
346
357
|
}
|
|
347
358
|
|
|
359
|
+
function readServerRouteStatusSummary(data: Record<string, unknown> | null): string | null {
|
|
360
|
+
const counts = readRecord(data?.serverRouteStatusCounts);
|
|
361
|
+
if (!counts) return null;
|
|
362
|
+
return formatProjectServerRouteStatusCounts({
|
|
363
|
+
recovering: typeof counts.recovering === "number" ? counts.recovering : 0,
|
|
364
|
+
error: typeof counts.error === "number" ? counts.error : 0,
|
|
365
|
+
unavailable: typeof counts.unavailable === "number" ? counts.unavailable : 0,
|
|
366
|
+
} satisfies ProjectServerRouteStatusCounts);
|
|
367
|
+
}
|
|
368
|
+
|
|
348
369
|
function readString(data: Record<string, unknown> | null, key: string): string | null {
|
|
349
370
|
const value = data?.[key];
|
|
350
371
|
return typeof value === "string" ? value : null;
|
|
@@ -100,6 +100,7 @@ export function healthErrorResult(content: string, reason?: string): CodeIntelRe
|
|
|
100
100
|
capabilityWarnings: null,
|
|
101
101
|
diagnosticFileCount: 0,
|
|
102
102
|
serverCount: 0,
|
|
103
|
+
serverRouteStatusCounts: { recovering: 0, error: 0, unavailable: 0 },
|
|
103
104
|
},
|
|
104
105
|
status: "invalid-input",
|
|
105
106
|
message: reason ?? content,
|
package/src/tool/result/types.ts
CHANGED
|
@@ -251,5 +251,11 @@ export interface HealthDetails {
|
|
|
251
251
|
capabilityWarnings: CapabilityWarningReport | null;
|
|
252
252
|
diagnosticFileCount: number;
|
|
253
253
|
serverCount: number;
|
|
254
|
+
/** Workspace-wide non-running LSP route counts, separate from semantic readiness. */
|
|
255
|
+
serverRouteStatusCounts: {
|
|
256
|
+
recovering: number;
|
|
257
|
+
error: number;
|
|
258
|
+
unavailable: number;
|
|
259
|
+
};
|
|
254
260
|
evidenceLists?: EvidenceListMetadata[];
|
|
255
261
|
}
|
package/src/ui/footer.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import { footerContributions } from "@mrclrchtr/supi-core/footer-registry";
|
|
11
|
+
import { countProjectServerRouteStatuses } from "../analysis/health/server-status.ts";
|
|
11
12
|
import { LSP_STATE_CHANGE_EVENT, type LspAdapterState } from "../substrate/lsp/state.ts";
|
|
12
13
|
|
|
13
14
|
/** Build the LSP status text: "λ lsp • 3 ✓ • 1 ⟳ • 1 ✗ • 2 open files" */
|
|
@@ -22,17 +23,18 @@ export function buildLspStatusText(lspState: LspAdapterState): string | undefine
|
|
|
22
23
|
// Aggregate server states
|
|
23
24
|
const ready = servers.filter((s) => s.status === "running" && s.ready).length;
|
|
24
25
|
const starting = servers.filter((s) => s.status === "running" && !s.ready).length;
|
|
25
|
-
const
|
|
26
|
-
const unavailable = servers.filter((s) => s.status === "unavailable").length;
|
|
26
|
+
const routeCounts = countProjectServerRouteStatuses(servers);
|
|
27
27
|
|
|
28
|
-
const hasServers =
|
|
28
|
+
const hasServers =
|
|
29
|
+
ready + starting + routeCounts.recovering + routeCounts.error + routeCounts.unavailable > 0;
|
|
29
30
|
if (!hasServers && openFiles === 0) return undefined;
|
|
30
31
|
|
|
31
32
|
const parts = ["λ lsp"];
|
|
32
33
|
if (ready > 0) parts.push(`${ready} ✓`);
|
|
33
34
|
if (starting > 0) parts.push(`${starting} ⟳`);
|
|
34
|
-
if (
|
|
35
|
-
if (
|
|
35
|
+
if (routeCounts.recovering > 0) parts.push(`${routeCounts.recovering} ↻`);
|
|
36
|
+
if (routeCounts.error > 0) parts.push(`${routeCounts.error} ✗`);
|
|
37
|
+
if (routeCounts.unavailable > 0) parts.push(`${routeCounts.unavailable} ⊘`);
|
|
36
38
|
if (openFiles > 0) parts.push(`${openFiles} ${openFiles === 1 ? "open file" : "open files"}`);
|
|
37
39
|
return parts.join(" • ");
|
|
38
40
|
}
|
package/src/ui/status-command.ts
CHANGED
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
evaluateCapabilityWarnings,
|
|
10
10
|
gatherCapabilityWarningInput,
|
|
11
11
|
} from "../analysis/capability/capability-warnings.ts";
|
|
12
|
+
import { countProjectServerRouteStatuses } from "../analysis/health/server-status.ts";
|
|
12
13
|
import { type CiStatusData, createCiStatusDialog } from "./status-overlay.ts";
|
|
13
14
|
|
|
14
15
|
const STATUS_KEY = "code-intelligence";
|
|
@@ -120,6 +121,7 @@ async function gatherCiStatusData(cwd: string, pi: ExtensionAPI): Promise<CiStat
|
|
|
120
121
|
const capabilityWarnings = evaluateCapabilityWarnings(gatherCapabilityWarningInput(cwd, null));
|
|
121
122
|
|
|
122
123
|
return {
|
|
124
|
+
workspaceRoot: cwd,
|
|
123
125
|
servers,
|
|
124
126
|
diagnostics,
|
|
125
127
|
serverInventoryAvailable,
|
|
@@ -202,12 +204,40 @@ function renderFooterLine(ctx: ExtensionContext, data: CiStatusData): string {
|
|
|
202
204
|
if (totalWarnings > 0) {
|
|
203
205
|
parts.push(t.fg("warning", `${totalWarnings} warning${totalWarnings === 1 ? "" : "s"}`));
|
|
204
206
|
}
|
|
207
|
+
addProjectServerRouteCounts(parts, data, t);
|
|
205
208
|
const structKind = data.capabilities.structural.kind;
|
|
206
209
|
if (structKind === "ready") parts.push(t.fg("success", "✓ ts"));
|
|
207
210
|
if (parts.length === 0) return t.fg("dim", "CI — no data available");
|
|
208
211
|
return parts.join(t.fg("dim", " "));
|
|
209
212
|
}
|
|
210
213
|
|
|
214
|
+
function addProjectServerRouteCounts(
|
|
215
|
+
parts: string[],
|
|
216
|
+
data: CiStatusData,
|
|
217
|
+
theme: ExtensionContext["ui"]["theme"],
|
|
218
|
+
): void {
|
|
219
|
+
const counts = countProjectServerRouteStatuses(data.servers);
|
|
220
|
+
if (counts.recovering > 0) {
|
|
221
|
+
parts.push(
|
|
222
|
+
theme.fg(
|
|
223
|
+
"warning",
|
|
224
|
+
`${counts.recovering} route${counts.recovering === 1 ? "" : "s"} recovering`,
|
|
225
|
+
),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
if (counts.error > 0) {
|
|
229
|
+
parts.push(theme.fg("error", `${counts.error} route error${counts.error === 1 ? "" : "s"}`));
|
|
230
|
+
}
|
|
231
|
+
if (counts.unavailable > 0) {
|
|
232
|
+
parts.push(
|
|
233
|
+
theme.fg(
|
|
234
|
+
"warning",
|
|
235
|
+
`${counts.unavailable} route${counts.unavailable === 1 ? "" : "s"} unavailable`,
|
|
236
|
+
),
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
211
241
|
function truncateFooterLine(line: string, width: number): string {
|
|
212
242
|
return truncateToWidth(line, width, "…");
|
|
213
243
|
}
|
|
@@ -230,6 +260,7 @@ function updateStatusAndWidget(ctx: ExtensionContext, data: CiStatusData): void
|
|
|
230
260
|
if (totalWarnings > 0) {
|
|
231
261
|
parts.push(t.fg("warning", `${totalWarnings} warning${totalWarnings === 1 ? "" : "s"}`));
|
|
232
262
|
}
|
|
263
|
+
addProjectServerRouteCounts(parts, data, t);
|
|
233
264
|
const structKind = data.capabilities.structural.kind;
|
|
234
265
|
if (structKind === "ready") parts.push(t.fg("success", "✓ ts"));
|
|
235
266
|
|
package/src/ui/status-overlay.ts
CHANGED
|
@@ -18,6 +18,11 @@ import type {
|
|
|
18
18
|
ProjectServerInfo,
|
|
19
19
|
} from "@mrclrchtr/supi-lsp/api";
|
|
20
20
|
import type { CapabilityWarningReport } from "../analysis/capability/capability-warnings.ts";
|
|
21
|
+
import {
|
|
22
|
+
countProjectServerRouteStatuses,
|
|
23
|
+
formatProjectServerRoot,
|
|
24
|
+
formatProjectServerStatusReason,
|
|
25
|
+
} from "../analysis/health/server-status.ts";
|
|
21
26
|
import { diagnosticMessageString } from "../substrate/lsp/utils.ts";
|
|
22
27
|
|
|
23
28
|
/**
|
|
@@ -32,6 +37,8 @@ export interface CiDialogTheme {
|
|
|
32
37
|
}
|
|
33
38
|
|
|
34
39
|
export interface CiStatusData {
|
|
40
|
+
/** Canonical workspace root used to display LSP route roots. */
|
|
41
|
+
workspaceRoot: string;
|
|
35
42
|
servers: ProjectServerInfo[];
|
|
36
43
|
/** Whether the server list is complete status evidence. */
|
|
37
44
|
serverInventoryAvailable: boolean;
|
|
@@ -263,31 +270,65 @@ export class CiStatusDialog {
|
|
|
263
270
|
}
|
|
264
271
|
|
|
265
272
|
private renderSummaryLine(width: number): string {
|
|
266
|
-
const
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
const
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
if (totalErrors > 0) {
|
|
277
|
-
parts.push(t.fg("error", `${totalErrors} error${totalErrors === 1 ? "" : "s"}`));
|
|
278
|
-
}
|
|
279
|
-
if (totalWarnings > 0) {
|
|
280
|
-
parts.push(t.fg("warning", `${totalWarnings} warning${totalWarnings === 1 ? "" : "s"}`));
|
|
281
|
-
}
|
|
282
|
-
const structKind = this.data.capabilities.structural.kind;
|
|
283
|
-
if (structKind === "ready") parts.push(t.fg("success", "✓ ts ready"));
|
|
284
|
-
else if (structKind === "pending") parts.push(t.fg("dim", "ts pending…"));
|
|
285
|
-
else parts.push(t.fg("error", "ts unavailable"));
|
|
286
|
-
|
|
287
|
-
const joined = parts.length > 0 ? parts.join(sep) : t.fg("dim", "(no data)");
|
|
273
|
+
const parts = [
|
|
274
|
+
...this.serverSummaryParts(),
|
|
275
|
+
...this.diagnosticSummaryParts(),
|
|
276
|
+
...this.routeSummaryParts(),
|
|
277
|
+
this.structuralSummaryPart(),
|
|
278
|
+
];
|
|
279
|
+
const joined =
|
|
280
|
+
parts.length > 0
|
|
281
|
+
? parts.join(this.theme.fg("dim", " · "))
|
|
282
|
+
: this.theme.fg("dim", "(no data)");
|
|
288
283
|
return truncateToWidth(joined, width);
|
|
289
284
|
}
|
|
290
285
|
|
|
286
|
+
private serverSummaryParts(): string[] {
|
|
287
|
+
const running = this.data.servers.filter((server) => server.status === "running").length;
|
|
288
|
+
return running > 0
|
|
289
|
+
? [this.theme.fg("text", `${running} server${running === 1 ? "" : "s"}`)]
|
|
290
|
+
: [];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
private diagnosticSummaryParts(): string[] {
|
|
294
|
+
const errors = this.data.diagnostics.reduce((sum, entry) => sum + entry.errors, 0);
|
|
295
|
+
const warnings = this.data.diagnostics.reduce((sum, entry) => sum + entry.warnings, 0);
|
|
296
|
+
return [
|
|
297
|
+
errors > 0 ? this.theme.fg("error", `${errors} error${errors === 1 ? "" : "s"}`) : null,
|
|
298
|
+
warnings > 0
|
|
299
|
+
? this.theme.fg("warning", `${warnings} warning${warnings === 1 ? "" : "s"}`)
|
|
300
|
+
: null,
|
|
301
|
+
].filter((part): part is string => part !== null);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
private routeSummaryParts(): string[] {
|
|
305
|
+
const counts = countProjectServerRouteStatuses(this.data.servers);
|
|
306
|
+
return [
|
|
307
|
+
counts.recovering > 0
|
|
308
|
+
? this.theme.fg(
|
|
309
|
+
"warning",
|
|
310
|
+
`${counts.recovering} route${counts.recovering === 1 ? "" : "s"} recovering`,
|
|
311
|
+
)
|
|
312
|
+
: null,
|
|
313
|
+
counts.error > 0
|
|
314
|
+
? this.theme.fg("error", `${counts.error} route error${counts.error === 1 ? "" : "s"}`)
|
|
315
|
+
: null,
|
|
316
|
+
counts.unavailable > 0
|
|
317
|
+
? this.theme.fg(
|
|
318
|
+
"warning",
|
|
319
|
+
`${counts.unavailable} route${counts.unavailable === 1 ? "" : "s"} unavailable`,
|
|
320
|
+
)
|
|
321
|
+
: null,
|
|
322
|
+
].filter((part): part is string => part !== null);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
private structuralSummaryPart(): string {
|
|
326
|
+
const kind = this.data.capabilities.structural.kind;
|
|
327
|
+
if (kind === "ready") return this.theme.fg("success", "✓ ts ready");
|
|
328
|
+
if (kind === "pending") return this.theme.fg("dim", "ts pending…");
|
|
329
|
+
return this.theme.fg("error", "ts unavailable");
|
|
330
|
+
}
|
|
331
|
+
|
|
291
332
|
private addServerSection(container: Container): void {
|
|
292
333
|
const t = this.theme;
|
|
293
334
|
container.addChild(new Text(t.fg("accent", t.bold(" Servers")), 1, 0));
|
|
@@ -328,7 +369,7 @@ export class CiStatusDialog {
|
|
|
328
369
|
" " +
|
|
329
370
|
t.fg(statusColor, icon) +
|
|
330
371
|
" " +
|
|
331
|
-
t.fg("dim", `root: ${server.root}`),
|
|
372
|
+
t.fg("dim", `root: ${formatProjectServerRoot(this.data.workspaceRoot, server.root)}`),
|
|
332
373
|
0,
|
|
333
374
|
0,
|
|
334
375
|
),
|
|
@@ -337,6 +378,11 @@ export class CiStatusDialog {
|
|
|
337
378
|
const fileTypes = server.fileTypes.map((f) => `.${f}`).join(" ");
|
|
338
379
|
container.addChild(new Text(t.fg("dim", ` files: ${fileTypes || "(none)"}`), 0, 0));
|
|
339
380
|
|
|
381
|
+
const statusReason = formatProjectServerStatusReason(server.statusReason);
|
|
382
|
+
if (statusReason) {
|
|
383
|
+
container.addChild(new Text(t.fg("warning", ` ${statusReason}`), 0, 0));
|
|
384
|
+
}
|
|
385
|
+
|
|
340
386
|
if (server.openFiles.length > 0) {
|
|
341
387
|
const shown = server.openFiles.slice(0, MAX_OPEN_FILES_SHOWN);
|
|
342
388
|
const remaining = server.openFiles.length - shown.length;
|