@mrclrchtr/supi-code-intelligence 6.0.1 → 6.2.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 (147) hide show
  1. package/README.md +2 -2
  2. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +5 -0
  4. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  5. package/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  6. package/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  7. package/node_modules/@mrclrchtr/supi-lsp/README.md +26 -4
  8. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  9. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +5 -0
  10. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  11. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  12. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  13. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-jsonrpc/package.json +1 -1
  14. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.colorProvider.d.ts +1 -1
  15. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.d.ts +2 -2
  16. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/lib/common/protocol.fileOperations.d.ts +9 -9
  17. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-protocol/package.json +3 -3
  18. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/esm/main.d.ts +2 -2
  19. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/esm/main.js +1 -1
  20. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/umd/main.d.ts +2 -2
  21. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/lib/umd/main.js +1 -1
  22. package/node_modules/@mrclrchtr/supi-lsp/node_modules/vscode-languageserver-types/package.json +1 -1
  23. package/node_modules/@mrclrchtr/supi-lsp/package.json +4 -5
  24. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +14 -7
  25. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +1 -1
  26. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +0 -5
  27. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +6 -3
  28. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +2 -1
  29. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +5 -2
  30. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +2 -1
  31. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +23 -5
  32. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +40 -18
  33. package/node_modules/@mrclrchtr/supi-lsp/src/config/config.ts +164 -30
  34. package/node_modules/@mrclrchtr/supi-lsp/src/config/lsp-settings.ts +9 -87
  35. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +8 -1
  36. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +4 -5
  37. package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +1 -29
  38. package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +7 -21
  39. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-severity.ts +6 -0
  40. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-summary.ts +11 -14
  41. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +0 -5
  42. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/stale-diagnostics.ts +0 -1
  43. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sentinels.ts +29 -49
  44. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +1 -1
  45. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +4 -8
  46. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-helpers.ts +1 -9
  47. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +12 -6
  48. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +9 -1
  49. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +26 -41
  50. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +849 -117
  51. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +6 -4
  52. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +59 -28
  53. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +27 -19
  54. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +10 -0
  55. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +26 -0
  56. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registration.ts +0 -17
  57. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +118 -27
  58. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +7 -2
  59. package/node_modules/@mrclrchtr/supi-lsp/src/session/scanner.ts +24 -21
  60. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +33 -5
  61. package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +19 -40
  62. package/node_modules/@mrclrchtr/supi-lsp/src/utils.ts +13 -19
  63. package/node_modules/@mrclrchtr/supi-lsp/src/workspace-path-policy.ts +287 -0
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +6 -18
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +5 -0
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  68. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-identity.ts +11 -0
  69. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug.ts +5 -0
  70. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -5
  71. package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/kotlin/tree-sitter-kotlin.wasm.json +1 -1
  72. package/node_modules/@mrclrchtr/supi-tree-sitter/resources/grammars/sql/tree-sitter-sql.wasm.json +1 -1
  73. package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +1 -11
  74. package/node_modules/@mrclrchtr/supi-tree-sitter/src/coordinates.ts +19 -0
  75. package/node_modules/@mrclrchtr/supi-tree-sitter/src/language.ts +5 -23
  76. package/node_modules/@mrclrchtr/supi-tree-sitter/src/operation-support.ts +3 -21
  77. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +5 -9
  78. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +10 -20
  79. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +7 -1
  80. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +9 -5
  81. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +134 -9
  82. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-name.ts +96 -0
  83. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +11 -12
  84. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +39 -56
  85. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +9 -7
  86. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/js-binding-pattern.ts +26 -0
  87. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +2 -21
  88. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline-html-sql.ts +144 -12
  89. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/outline.ts +50 -37
  90. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/scope.ts +138 -0
  91. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +0 -7
  92. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +1 -14
  93. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +0 -10
  94. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +6 -8
  95. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +34 -10
  96. package/package.json +5 -5
  97. package/src/analysis/capability/capability-warnings.ts +4 -22
  98. package/src/analysis/health/recovery.ts +4 -0
  99. package/src/analysis/health/server-status.ts +64 -0
  100. package/src/analysis/health/signals.ts +1 -0
  101. package/src/analysis/provider.ts +2 -1
  102. package/src/analysis/search/ast-scan-timing.ts +4 -2
  103. package/src/analysis/search/ast-scan.ts +1 -1
  104. package/src/analysis/target/symbol.ts +28 -16
  105. package/src/analysis/target/types.ts +1 -0
  106. package/src/api.ts +1 -0
  107. package/src/extension.ts +4 -2
  108. package/src/session/find-workflow.ts +5 -12
  109. package/src/session/health-refresh.ts +30 -4
  110. package/src/session/health-types.ts +27 -2
  111. package/src/session/health-workflow.ts +5 -1
  112. package/src/session/input/health-refactor.ts +16 -14
  113. package/src/session/refactor-types.ts +14 -1
  114. package/src/session/refactor-workflow.ts +21 -16
  115. package/src/session/semantic-demand.ts +36 -0
  116. package/src/session/target-workflow.ts +9 -12
  117. package/src/substrate/lsp/lifecycle.ts +0 -3
  118. package/src/substrate/lsp/maintenance.ts +4 -5
  119. package/src/substrate/lsp/settings.ts +6 -9
  120. package/src/substrate/lsp/state.ts +0 -2
  121. package/src/substrate/workspace-provider-host.ts +2 -2
  122. package/src/tool/code_health/file-scope-markdown.ts +43 -0
  123. package/src/tool/code_health/guidance.ts +2 -2
  124. package/src/tool/code_health/markdown.ts +81 -69
  125. package/src/tool/code_health/refresh-outcome.ts +46 -0
  126. package/src/tool/code_health/refresh-status.ts +166 -23
  127. package/src/tool/code_health/result.ts +15 -5
  128. package/src/tool/code_health/spec.ts +2 -1
  129. package/src/tool/code_health/tui.ts +42 -8
  130. package/src/tool/code_refactor_apply/guidance.ts +3 -2
  131. package/src/tool/code_refactor_plan/guidance.ts +1 -1
  132. package/src/tool/register.ts +5 -2
  133. package/src/tool/result/errors.ts +1 -0
  134. package/src/tool/result/types.ts +6 -0
  135. package/src/tool/schemas.ts +21 -11
  136. package/src/ui/footer.ts +7 -5
  137. package/src/ui/status-command.ts +31 -0
  138. package/src/ui/status-overlay.ts +69 -23
  139. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-actions.ts +0 -59
  140. package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +0 -24
  141. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-kotlin-wasm.mjs +0 -132
  142. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/generate-sql-wasm.mjs +0 -158
  143. package/node_modules/@mrclrchtr/supi-tree-sitter/scripts/vendor-wasm.mjs +0 -167
  144. package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -42
  145. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-registration.ts +0 -37
  146. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/service-registry.ts +0 -30
  147. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/structure.ts +0 -8
@@ -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,
@@ -14,6 +19,7 @@ export type {
14
19
  HealthDiagnosticEntry,
15
20
  HealthDiagnosticObservation,
16
21
  HealthDiagnosticScope,
22
+ HealthFileReadiness,
17
23
  HealthRefreshAttempt,
18
24
  HealthRefreshState,
19
25
  HealthSection,
@@ -36,7 +42,7 @@ const SECTION_TITLES: Record<HealthSection, string> = {
36
42
  };
37
43
 
38
44
  /** Assemble public code_health evidence and details before presentation adapters render it. */
39
- export function assembleHealthResult(data: HealthData): HealthResultAssembly {
45
+ export function assembleHealthResult(data: HealthData, cwd?: string): HealthResultAssembly {
40
46
  const projections = data.includedSections.map((section) => projectSection(section, data));
41
47
  const sections = projections.map((projection) => projection.section);
42
48
  const sectionDetails = projections.map((projection) => projection.details);
@@ -55,7 +61,7 @@ export function assembleHealthResult(data: HealthData): HealthResultAssembly {
55
61
  provenance,
56
62
  });
57
63
 
58
- const displaySections = buildHealthDisplaySections(data);
64
+ const displaySections = buildHealthDisplaySections(data, cwd);
59
65
 
60
66
  return {
61
67
  data,
@@ -78,12 +84,13 @@ export function assembleHealthResult(data: HealthData): HealthResultAssembly {
78
84
  capabilityWarnings: data.capabilityWarnings ?? null,
79
85
  diagnosticFileCount: data.diagnostics.entries.length,
80
86
  serverCount: data.servers.length,
87
+ serverRouteStatusCounts: countProjectServerRouteStatuses(data.servers),
81
88
  evidenceLists: [...assembled.evidenceLists],
82
89
  },
83
90
  };
84
91
  }
85
92
 
86
- function buildHealthDisplaySections(data: HealthData): ToolDisplaySection[] {
93
+ function buildHealthDisplaySections(data: HealthData, cwd?: string): ToolDisplaySection[] {
87
94
  const sections: ToolDisplaySection[] = [];
88
95
 
89
96
  if (data.includedSections.includes("diagnostics") && data.diagnostics.entries.length > 0) {
@@ -107,7 +114,10 @@ function buildHealthDisplaySections(data: HealthData): ToolDisplaySection[] {
107
114
  title: "Servers",
108
115
  items: data.servers,
109
116
  totalCount: data.servers.length,
110
- format: (server) => `${server.name} (${server.fileTypes.join(", ")}) — ${server.status}`,
117
+ format: (server) => {
118
+ const root = cwd ? formatProjectServerRoot(cwd, server.root) : server.root;
119
+ return `${server.name} @ ${root} (${server.fileTypes.join(", ")}) — ${server.status}${server.statusReason ? ` — ${formatProjectServerStatusReason(server.statusReason)}` : ""}`;
120
+ },
111
121
  }),
112
122
  );
113
123
  }
@@ -255,7 +265,7 @@ export function finishHealthResult(outcome: HealthOutcome, cwd: string): CodeInt
255
265
  return healthErrorResult(`**Error:** ${outcome.message}`, outcome.message);
256
266
  }
257
267
 
258
- const assembly = assembleHealthResult(outcome.data);
268
+ const assembly = assembleHealthResult(outcome.data, cwd);
259
269
  return {
260
270
  content: renderHealthResult(assembly, cwd),
261
271
  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: "Attempt diagnostic recovery before collecting; result reports the outcome.",
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,
@@ -20,7 +24,12 @@ import {
20
24
  type ToolResult,
21
25
  } from "../../ui/tui/common.ts";
22
26
  import type { CodeHealthToolParams } from "./execute.ts";
23
- import { readPreviousRefreshStatus, readRefreshStatus } from "./refresh-status.ts";
27
+ import {
28
+ isPendingFileReadiness,
29
+ readCompactRefreshStatus,
30
+ readPreviousRefreshStatus,
31
+ readRefreshStatus,
32
+ } from "./refresh-status.ts";
24
33
  import { formatSemanticHealthState, readSemanticHealthState } from "./semantic-state.ts";
25
34
 
26
35
  /** ── renderCall ────────────────────────────────────────────────── */
@@ -121,6 +130,7 @@ function buildCompactSummary(data: Record<string, unknown> | null, theme: Theme)
121
130
  const semanticRequested = sections.some(
122
131
  (section) => section.key === "diagnostics" || section.key === "servers",
123
132
  );
133
+ const fileReadinessPending = isPendingFileReadiness(data);
124
134
  const segments = sections.map((section) =>
125
135
  formatSectionSummary(
126
136
  section,
@@ -128,15 +138,19 @@ function buildCompactSummary(data: Record<string, unknown> | null, theme: Theme)
128
138
  ? formatCompactDiagnosticEvidence(readDiagnosticEvidence(data))
129
139
  : null,
130
140
  theme,
141
+ fileReadinessPending,
131
142
  ),
132
143
  );
133
144
  if (semanticRequested) {
134
145
  const semanticStatus = readSemanticStatus(data);
146
+ const routeSummary = readServerRouteStatusSummary(data);
135
147
  const statusColor = semanticStatus.startsWith("ready") ? "success" : "warning";
136
- segments.push(`${theme.fg("dim", "lsp")} ${theme.fg(statusColor, semanticStatus)}`);
148
+ segments.push(
149
+ `${theme.fg("dim", "lsp")} ${theme.fg(statusColor, semanticStatus)}${routeSummary ? ` ${theme.fg("warning", routeSummary)}` : ""}`,
150
+ );
137
151
  }
138
- const previousRefresh = readPreviousRefreshStatus(data);
139
- if (previousRefresh) segments.push(theme.fg("dim", previousRefresh));
152
+ const refreshStatus = readCompactRefreshStatus(data) ?? readPreviousRefreshStatus(data);
153
+ if (refreshStatus) segments.push(theme.fg("dim", refreshStatus));
140
154
  const capabilityWarningCount = readCapabilityWarnings(data).length;
141
155
  if (capabilityWarningCount > 0) {
142
156
  const label = capabilityWarningCount === 1 ? "capability warning" : "capability warnings";
@@ -151,11 +165,14 @@ function formatSectionSummary(
151
165
  section: HealthSectionSummary,
152
166
  coverage: string | null,
153
167
  theme: Theme,
168
+ fileReadinessPending: boolean,
154
169
  ): string {
155
170
  const label = sectionLabel(section.key);
156
171
  const coverageSuffix = coverage ? ` (${coverage})` : "";
157
172
  if (section.status === "unavailable" || !section.available) {
158
- return `${theme.fg("dim", label)} ${theme.fg("warning", "unavailable")}${coverageSuffix}`;
173
+ const status =
174
+ section.key === "diagnostics" && fileReadinessPending ? "pending" : "unavailable";
175
+ return `${theme.fg("dim", label)} ${theme.fg("warning", status)}${coverageSuffix}`;
159
176
  }
160
177
  if (section.status === "partial") {
161
178
  return `${theme.fg("dim", label)} ${theme.fg("warning", "partial")}${coverageSuffix}`;
@@ -179,7 +196,10 @@ function buildStatusBar(data: Record<string, unknown> | null, theme: Theme): Tex
179
196
  const lspColor = semanticStatus.startsWith("ready") ? "success" : "warning";
180
197
  const structuralColor = structuralStatus === "ready" ? "success" : "muted";
181
198
 
182
- const lines: string[] = [`LSP: ${theme.fg(lspColor, semanticStatus)}`];
199
+ const routeSummary = readServerRouteStatusSummary(data);
200
+ const lines: string[] = [
201
+ `LSP: ${theme.fg(lspColor, semanticStatus)}${routeSummary ? ` — ${theme.fg("warning", routeSummary)}` : ""}`,
202
+ ];
183
203
  if (structuralStatus) {
184
204
  lines.push(`Tree-sitter: ${theme.fg(structuralColor, structuralStatus)}`);
185
205
  }
@@ -287,7 +307,8 @@ function buildHealthSectionSummary(data: Record<string, unknown> | null, theme:
287
307
  if (section.status === "unavailable" || !section.available) {
288
308
  return `${label} unavailable`;
289
309
  }
290
- return `${label} ${section.itemCount}`;
310
+ const routeSummary = section.key === "servers" ? readServerRouteStatusSummary(data) : null;
311
+ return `${label} ${section.itemCount}${routeSummary ? ` — ${routeSummary}` : ""}`;
291
312
  });
292
313
  return new Text(theme.fg("muted", lines.join(" ")), 0, 0);
293
314
  }
@@ -296,7 +317,10 @@ function buildDiagnosticSummary(data: Record<string, unknown> | null, theme: The
296
317
  const section = readHealthSections(data).find((entry) => entry.key === "diagnostics");
297
318
  if (!section) return new Text("", 0, 0);
298
319
  if (section.status === "unavailable" || !section.available) {
299
- return new Text(theme.fg("warning", "Diagnostics unavailable"), 0, 0);
320
+ const label = isPendingFileReadiness(data)
321
+ ? "Diagnostics pending — LSP may still be warming; retry shortly"
322
+ : "Diagnostics unavailable";
323
+ return new Text(theme.fg("warning", label), 0, 0);
300
324
  }
301
325
  const trackedFiles = diagnosticScopeKind(data) === "tracked-files";
302
326
  if (section.status === "partial") {
@@ -345,6 +369,16 @@ function readSemanticStatus(data: Record<string, unknown> | null): string {
345
369
  return formatSemanticHealthState(readSemanticHealthState(data?.semanticState));
346
370
  }
347
371
 
372
+ function readServerRouteStatusSummary(data: Record<string, unknown> | null): string | null {
373
+ const counts = readRecord(data?.serverRouteStatusCounts);
374
+ if (!counts) return null;
375
+ return formatProjectServerRouteStatusCounts({
376
+ recovering: typeof counts.recovering === "number" ? counts.recovering : 0,
377
+ error: typeof counts.error === "number" ? counts.error : 0,
378
+ unavailable: typeof counts.unavailable === "number" ? counts.unavailable : 0,
379
+ } satisfies ProjectServerRouteStatusCounts);
380
+ }
381
+
348
382
  function readString(data: Record<string, unknown> | null, key: string): string | null {
349
383
  const value = data?.[key];
350
384
  return typeof value === "string" ? value : null;
@@ -1,5 +1,6 @@
1
- export const toolDescription = "Apply one stored refactor plan by planId.";
1
+ export const toolDescription =
2
+ "Apply one fresh stored refactor plan by planId after safety checks; mutates its files.";
2
3
 
3
- export const promptSnippet = "apply a fresh stored refactor plan";
4
+ export const promptSnippet = "apply a stored refactor plan";
4
5
 
5
6
  export const promptGuidelines: string[] = [];
@@ -1,5 +1,5 @@
1
1
  export const toolDescription =
2
- "Preview one semantic rename or extraction and return a planId without mutating files.";
2
+ "Preview one semantic rename, extraction, import cleanup, or dead-code deletion and return a planId without mutating files.";
3
3
 
4
4
  export const promptSnippet = "preview a precise semantic refactor";
5
5
 
@@ -1,7 +1,10 @@
1
1
  import { createHmac, randomBytes } from "node:crypto";
2
2
  import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
3
- import { recordDebugEvent, startDebugTimer } from "@mrclrchtr/supi-core/debug";
4
- import { truncateIdentity } from "@mrclrchtr/supi-lsp/debug-telemetry";
3
+ import {
4
+ recordDebugEvent,
5
+ startDebugTimer,
6
+ truncateDebugIdentity as truncateIdentity,
7
+ } from "@mrclrchtr/supi-core/debug";
5
8
  import type { WorkspaceCodeIntelligenceSession } from "../session/session.ts";
6
9
  import {
7
10
  CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES,
@@ -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,
@@ -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
  }
@@ -1,5 +1,9 @@
1
1
  import { StringEnum } from "@earendil-works/pi-ai";
2
2
  import { type TSchema, Type } from "typebox";
3
+ import {
4
+ PUBLIC_REFACTOR_OPERATION_NAMES,
5
+ type PublicRefactorOperationName,
6
+ } from "../session/refactor-types.ts";
3
7
  import { TARGET_SYMBOL_KINDS } from "../session/target-input.ts";
4
8
 
5
9
  export const ScopeParam = Type.String({
@@ -127,18 +131,24 @@ const RangeParam = Type.Object(
127
131
 
128
132
  const NewNameParam = Type.String({ description: "New symbol name.", minLength: 1 });
129
133
 
134
+ const RefactorOperationProperties = {
135
+ rename_symbol: Type.Object({ newName: NewNameParam }, { additionalProperties: false }),
136
+ extract_function: Type.Object(
137
+ { newName: NewNameParam, range: RangeParam },
138
+ { additionalProperties: false },
139
+ ),
140
+ extract_variable: Type.Object(
141
+ { newName: NewNameParam, range: RangeParam },
142
+ { additionalProperties: false },
143
+ ),
144
+ update_imports: Type.Object({}, { additionalProperties: false }),
145
+ delete_dead_code: Type.Object({}, { additionalProperties: false }),
146
+ } satisfies Record<PublicRefactorOperationName, TSchema>;
147
+
130
148
  export const RefactorOperationParam = exactOneSelector(
131
- {
132
- rename_symbol: Type.Object({ newName: NewNameParam }, { additionalProperties: false }),
133
- extract_function: Type.Object(
134
- { newName: NewNameParam, range: RangeParam },
135
- { additionalProperties: false },
136
- ),
137
- extract_variable: Type.Object(
138
- { newName: NewNameParam, range: RangeParam },
139
- { additionalProperties: false },
140
- ),
141
- },
149
+ Object.fromEntries(
150
+ PUBLIC_REFACTOR_OPERATION_NAMES.map((name) => [name, RefactorOperationProperties[name]]),
151
+ ),
142
152
  "Exactly one refactor operation.",
143
153
  );
144
154
 
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 error = servers.filter((s) => s.status === "error").length;
26
- const unavailable = servers.filter((s) => s.status === "unavailable").length;
26
+ const routeCounts = countProjectServerRouteStatuses(servers);
27
27
 
28
- const hasServers = ready + starting + error + unavailable > 0;
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 (error > 0) parts.push(`${error} ✗`);
35
- if (unavailable > 0) parts.push(`${unavailable} ⊘`);
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
  }
@@ -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
 
@@ -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 t = this.theme;
267
- const running = this.data.servers.filter((s) => s.status === "running").length;
268
- const totalErrors = this.data.diagnostics.reduce((sum, d) => sum + d.errors, 0);
269
- const totalWarnings = this.data.diagnostics.reduce((sum, d) => sum + d.warnings, 0);
270
- const sep = t.fg("dim", " · ");
271
-
272
- const parts: string[] = [];
273
- if (running > 0) {
274
- parts.push(t.fg("text", `${running} server${running === 1 ? "" : "s"}`));
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;
@@ -1,59 +0,0 @@
1
- // Server capability action label helpers — maps LSP capabilities to readable action labels.
2
-
3
- import type { ServerCapabilities } from "../config/types.ts";
4
-
5
- interface LspServerSupportedActionSpec {
6
- label: string;
7
- isSupported: (capabilities: ServerCapabilities | null | undefined) => boolean;
8
- }
9
-
10
- const LSP_SERVER_SUPPORTED_ACTION_SPECS: readonly LspServerSupportedActionSpec[] = [
11
- {
12
- label: "diagnostics [optional file]",
13
- isSupported: () => true,
14
- },
15
- {
16
- label: "hover(file,line,char)",
17
- isSupported: (capabilities) => Boolean(capabilities?.hoverProvider),
18
- },
19
- {
20
- label: "definition(file,line,char)",
21
- isSupported: (capabilities) => Boolean(capabilities?.definitionProvider),
22
- },
23
- {
24
- label: "references(file,line,char)",
25
- isSupported: (capabilities) => Boolean(capabilities?.referencesProvider),
26
- },
27
- {
28
- label: "implementation(file,line,char)",
29
- isSupported: (capabilities) => Boolean(capabilities?.implementationProvider),
30
- },
31
- {
32
- label: "symbols(file)",
33
- isSupported: (capabilities) => Boolean(capabilities?.documentSymbolProvider),
34
- },
35
- {
36
- label: "workspace_symbols(query)",
37
- isSupported: (capabilities) => Boolean(capabilities?.workspaceSymbolProvider),
38
- },
39
- {
40
- label: "rename(file,line,char,newName)",
41
- isSupported: (capabilities) => Boolean(capabilities?.renameProvider),
42
- },
43
- {
44
- label: "code_actions(file,line,char)",
45
- isSupported: (capabilities) => Boolean(capabilities?.codeActionProvider),
46
- },
47
- ] as const;
48
-
49
- /**
50
- * Get the list of supported LSP server actions based on capabilities.
51
- */
52
- export function getSupportedLspServerActions(
53
- capabilities: ServerCapabilities | null | undefined,
54
- ): string[] {
55
- if (!capabilities) return [];
56
- return LSP_SERVER_SUPPORTED_ACTION_SPECS.filter((spec) => spec.isSupported(capabilities)).map(
57
- (spec) => spec.label,
58
- );
59
- }
@@ -1,24 +0,0 @@
1
- import ignore from "ignore";
2
-
3
- /** Gitignore-style glob pattern matching for path exclusion.
4
- *
5
- * Delegates to the {@link https://github.com/kaelzhang/node-ignore | ignore} package,
6
- * which provides battle-tested .gitignore semantics used by ESLint, Prettier, and others.
7
- *
8
- * Supports full gitignore syntax: literal names, `*` / `?` wildcards, `**` recursive globs,
9
- * leading `/` anchored patterns, trailing `/` directory-only patterns, and `!` negation.
10
- *
11
- * **Note:** Patterns that start with `#` are treated as comments unless escaped with `\#`.
12
- */
13
-
14
- /**
15
- * Check whether a project-relative file path matches a gitignore-style glob pattern.
16
- *
17
- * @param filePath - Relative file path with forward slashes (e.g. `"src/__tests__/bar.test.ts"`)
18
- * @param pattern - Gitignore-style glob pattern (e.g. `"__tests__/"`, `"*.test.ts"`, `"/build"`)
19
- * @returns `true` if the file path matches the pattern
20
- */
21
- export function isGlobMatch(filePath: string, pattern: string): boolean {
22
- if (!filePath || !pattern) return false;
23
- return ignore().add(pattern).ignores(filePath);
24
- }