@mrclrchtr/supi-code-intelligence 4.10.0 → 5.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (203) hide show
  1. package/README.md +2 -1
  2. package/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  3. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  5. package/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  6. package/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  7. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  8. package/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  9. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  10. package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  11. package/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  12. package/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
  13. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  14. package/node_modules/@mrclrchtr/supi-lsp/README.md +80 -5
  15. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  16. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  17. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  18. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  19. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  20. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  21. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  22. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  23. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  24. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  25. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
  26. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  27. package/node_modules/@mrclrchtr/supi-lsp/package.json +7 -6
  28. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +10 -0
  29. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +206 -0
  30. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +78 -0
  31. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +114 -0
  32. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +372 -0
  33. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +14 -0
  34. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +62 -0
  35. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +491 -0
  36. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +9 -0
  37. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +71 -11
  38. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +206 -0
  39. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +365 -297
  40. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +52 -0
  41. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +185 -0
  42. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-file-state.ts +25 -0
  43. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +467 -115
  44. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +161 -33
  45. package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +10 -1
  46. package/node_modules/@mrclrchtr/supi-lsp/src/config/defaults.json +1 -1
  47. package/node_modules/@mrclrchtr/supi-lsp/src/debug-telemetry.ts +45 -0
  48. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +38 -0
  49. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-client-state.ts +79 -9
  50. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +13 -4
  51. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +300 -24
  52. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +5 -2
  53. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +464 -96
  54. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-refactor-provider.ts +130 -0
  55. package/node_modules/@mrclrchtr/supi-lsp/src/provider/lsp-semantic-provider.ts +45 -329
  56. package/node_modules/@mrclrchtr/supi-lsp/src/provider/refactor-planning.ts +57 -120
  57. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-edit-normalizer.ts +320 -0
  58. package/node_modules/@mrclrchtr/supi-lsp/src/provider/semantic-symbol-mapper.ts +207 -0
  59. package/node_modules/@mrclrchtr/supi-lsp/src/session/readiness.ts +72 -2
  60. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-controller.ts +151 -26
  61. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +40 -0
  62. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +50 -0
  63. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +136 -120
  64. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-transition-debug.ts +49 -0
  65. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +102 -0
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +17 -7
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/README.md +2 -0
  68. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  69. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/api.ts +9 -0
  70. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/capability/types.ts +53 -13
  71. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/request-control.ts +47 -0
  72. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +13 -1
  73. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +2 -0
  74. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  75. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +31 -0
  76. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config.ts +2 -0
  77. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/debug-registry.ts +19 -3
  78. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +3 -0
  79. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/LICENSE +21 -0
  80. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/README.md +258 -0
  81. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/babel.cjs +257 -0
  82. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/dist/jiti.cjs +1 -0
  83. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-cli.mjs +34 -0
  84. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-hooks.mjs +124 -0
  85. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-native.mjs +121 -0
  86. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.d.mts +1 -0
  87. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-register.mjs +4 -0
  88. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti-static.mjs +23 -0
  89. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.cjs +30 -0
  90. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.cts +8 -0
  91. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.d.mts +8 -0
  92. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/jiti.mjs +29 -0
  93. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/lib/types.d.ts +420 -0
  94. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/jiti/package.json +146 -0
  95. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +6 -3
  96. package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +0 -9
  97. package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +0 -8
  98. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +16 -12
  99. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime-controller.ts +65 -87
  100. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/session.ts +47 -95
  101. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-timing.ts +147 -0
  102. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client-lifecycle.ts +39 -0
  103. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-client.ts +561 -0
  104. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-message-size.ts +24 -0
  105. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/structural-worker-protocol.ts +452 -0
  106. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/call-sites.ts +4 -2
  107. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +12 -4
  108. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/exports.ts +4 -2
  109. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/imports.ts +4 -2
  110. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/node-at.ts +5 -3
  111. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +33 -12
  112. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/blocking-test-bootstrap.mjs +34 -0
  113. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/bootstrap.mjs +8 -0
  114. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store-helpers.ts +23 -0
  115. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/parsed-file-store.ts +400 -0
  116. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/request-control.ts +48 -0
  117. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-parser-helpers.ts +24 -0
  118. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime-query-helpers.ts +43 -0
  119. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/runtime.ts +421 -0
  120. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/service.ts +116 -0
  121. package/node_modules/@mrclrchtr/supi-tree-sitter/src/worker/worker-main.ts +201 -0
  122. package/package.json +5 -5
  123. package/src/analysis/health/diagnostics.ts +236 -24
  124. package/src/analysis/health/recovery.ts +6 -2
  125. package/src/analysis/provider.ts +58 -0
  126. package/src/analysis/readiness.ts +30 -9
  127. package/src/analysis/refactor/apply.ts +45 -1
  128. package/src/analysis/refactor/mutation-authority.ts +143 -0
  129. package/src/analysis/relations/callees.ts +9 -1
  130. package/src/analysis/relations/types.ts +5 -2
  131. package/src/analysis/search/ast-scan-timing.ts +9 -2
  132. package/src/analysis/search/ast-scan.ts +1 -1
  133. package/src/analysis/search/deadline.ts +2 -2
  134. package/src/analysis/search/pattern-analysis.ts +46 -32
  135. package/src/analysis/search/pattern.ts +12 -2
  136. package/src/analysis/target/anchored.ts +20 -11
  137. package/src/analysis/target/file.ts +18 -11
  138. package/src/analysis/target/identity.ts +19 -3
  139. package/src/analysis/target/symbol.ts +36 -15
  140. package/src/api.ts +4 -0
  141. package/src/diagnostics/evidence.ts +51 -0
  142. package/src/session/capability-adapter.ts +12 -3
  143. package/src/session/find-workflow.ts +17 -7
  144. package/src/session/graph/collect.ts +18 -2
  145. package/src/session/graph-workflow.ts +19 -12
  146. package/src/session/health-refresh.ts +148 -0
  147. package/src/session/health-types.ts +37 -15
  148. package/src/session/health-workflow.ts +37 -39
  149. package/src/session/inspect/collect.ts +20 -3
  150. package/src/session/inspect-workflow.ts +10 -5
  151. package/src/session/orientation/collect.ts +10 -1
  152. package/src/session/orientation/context-facts.ts +3 -1
  153. package/src/session/orientation/context-sections.ts +20 -7
  154. package/src/session/orientation/gather.ts +17 -11
  155. package/src/session/orientation-types.ts +2 -1
  156. package/src/session/orientation-workflow.ts +15 -8
  157. package/src/session/refactor-plans.ts +2 -0
  158. package/src/session/refactor-workflow.ts +59 -14
  159. package/src/session/session.ts +4 -1
  160. package/src/session/target-workflow.ts +54 -23
  161. package/src/session/workflow-control.ts +7 -3
  162. package/src/substrate/lsp/lifecycle.ts +26 -46
  163. package/src/substrate/lsp/maintenance.ts +199 -25
  164. package/src/substrate/lsp/state.ts +2 -2
  165. package/src/substrate/workspace-provider-host.ts +25 -3
  166. package/src/tool/find/execute.ts +7 -1
  167. package/src/tool/find/render.ts +20 -3
  168. package/src/tool/find/tui.ts +23 -21
  169. package/src/tool/graph/execute.ts +23 -3
  170. package/src/tool/graph/tui.ts +59 -46
  171. package/src/tool/health/execute.ts +7 -2
  172. package/src/tool/health/markdown.ts +56 -14
  173. package/src/tool/health/refresh-status.ts +105 -0
  174. package/src/tool/health/tui.ts +95 -98
  175. package/src/tool/infra/workflow-control.ts +8 -0
  176. package/src/tool/inspect/execute.ts +7 -1
  177. package/src/tool/inspect/tui.ts +26 -18
  178. package/src/tool/orientation/execute.ts +21 -3
  179. package/src/tool/orientation/tui.ts +51 -35
  180. package/src/tool/refactor-apply/execute.ts +3 -0
  181. package/src/tool/refactor-apply/tui.ts +43 -25
  182. package/src/tool/refactor-plan/execute.ts +4 -0
  183. package/src/tool/refactor-plan/tui.ts +33 -20
  184. package/src/tool/register.ts +127 -22
  185. package/src/tool/resolve/execute.ts +18 -1
  186. package/src/tool/resolve/tui.ts +42 -24
  187. package/src/tool/result/display.ts +107 -0
  188. package/src/tool/result/errors.ts +20 -17
  189. package/src/tool/result/find.ts +52 -8
  190. package/src/tool/result/graph.ts +44 -1
  191. package/src/tool/result/health.ts +53 -1
  192. package/src/tool/result/inspect.ts +113 -1
  193. package/src/tool/result/orientation.ts +66 -14
  194. package/src/tool/result/refactor.ts +36 -4
  195. package/src/tool/result/resolve.ts +44 -7
  196. package/src/tool/result/types.ts +20 -0
  197. package/src/types/execution.ts +18 -5
  198. package/src/types/index.ts +8 -1
  199. package/src/ui/status-command.ts +5 -3
  200. package/src/ui/status-overlay.ts +4 -0
  201. package/src/ui/tui/common.ts +108 -10
  202. package/src/ui/tui/display.ts +48 -0
  203. package/node_modules/@mrclrchtr/supi-tree-sitter/src/session/runtime.ts +0 -351
@@ -1,73 +1,76 @@
1
- /**
2
- * TUI renderer for code_graph — renderCall + renderResult.
3
- *
4
- * Dual-surface rendering: chrome built from details, markdown body
5
- * available as a collapsible detail view.
6
- */
7
-
1
+ /** TUI renderer for code_graph. */
8
2
  import type { Theme } from "@earendil-works/pi-coding-agent";
9
3
  import { Container, Spacer, Text } from "@earendil-works/pi-tui";
10
4
  import {
11
5
  buildSimpleCompact,
12
6
  buildSimpleHeader,
13
7
  type EvidenceEntry,
8
+ formatCallPath,
9
+ formatCallValue,
14
10
  type ResultOptios,
11
+ renderDomainError,
12
+ renderDomainResult,
15
13
  renderEvidenceLines,
14
+ renderExecutionError,
16
15
  renderMarkdownDetail,
17
16
  renderPartial,
18
17
  renderStructuredDetailBody,
18
+ renderToolDisplaySections,
19
+ renderTruncationDisclosure,
20
+ type ToolRendererContext,
19
21
  type ToolResult,
20
22
  } from "../../ui/tui/common.ts";
21
23
  import type { CodeGraphToolParams, GraphRelation } from "./execute.ts";
22
24
 
23
- /** ── renderCall ────────────────────────────────────────────────── */
24
-
25
- export function renderGraphCall(args: unknown, theme: Theme, _context: unknown): Text {
26
- const params = (args ?? {}) as CodeGraphToolParams;
27
- const relations = params.relations ?? ["references"];
25
+ /** Render the compact code_graph call header. */
26
+ export function renderGraphCall(
27
+ args: unknown,
28
+ theme: Theme,
29
+ _context: ToolRendererContext | undefined,
30
+ ): Text {
31
+ const params = (args ?? {}) as Partial<CodeGraphToolParams>;
32
+ const relations: GraphRelation[] = Array.isArray(params.relations)
33
+ ? (params.relations as GraphRelation[])
34
+ : ["references"];
28
35
  const relationLabel = formatRelations(relations);
29
36
 
30
37
  let content = theme.fg("toolTitle", "code_graph");
38
+ if (relationLabel) content += ` ${theme.fg("accent", relationLabel)}`;
31
39
 
32
- if (relationLabel) {
33
- content += ` ${theme.fg("accent", relationLabel)}`;
34
- }
35
-
36
- const target = formatTarget(params);
37
- if (target) {
38
- content += ` ${theme.fg("muted", target)}`;
39
- }
40
+ const target = formatTarget(params.target);
41
+ if (target) content += ` ${theme.fg("muted", target)}`;
40
42
 
41
43
  return new Text(content, 0, 0);
42
44
  }
43
45
 
44
- /** ── renderResult ──────────────────────────────────────────────── */
45
-
46
+ /** Render code_graph progress, status, and structured result details. */
46
47
  export function renderGraphResult(
47
48
  result: ToolResult,
48
49
  options: ResultOptios,
49
50
  theme: Theme,
50
- _context: unknown,
51
+ context: ToolRendererContext | undefined,
51
52
  ): Container | Text {
52
- if (options.isPartial) {
53
- return renderPartial("Collecting relations…", theme);
54
- }
53
+ if (options.isPartial) return renderPartial("Collecting relations…", theme);
55
54
 
56
- const container = new Container();
57
55
  const details =
58
56
  result.details?.type === "search" ? (result.details.data as Record<string, unknown>) : null;
59
-
60
- if (result.isError) {
61
- container.addChild(new Text(theme.fg("error", "code_graph failed"), 0, 0));
62
- return container;
63
- }
57
+ const executionError = renderExecutionError(context, "code_graph failed", theme);
58
+ if (executionError) return executionError;
59
+ const domainError = renderDomainError(result, theme);
60
+ if (domainError) return renderDomainResult(result, options, theme, domainError);
64
61
 
65
62
  if (!options.expanded) {
66
- container.addChild(buildSimpleCompact(details ?? undefined, theme));
63
+ const compact = buildSimpleCompact(details ?? undefined, theme);
64
+ const truncation = renderTruncationDisclosure(result, theme);
65
+ if (!truncation) return compact;
66
+ const container = new Container();
67
+ container.addChild(compact);
68
+ container.addChild(new Spacer(1));
69
+ container.addChild(truncation);
67
70
  return container;
68
71
  }
69
72
 
70
- // Expanded view
73
+ const container = new Container();
71
74
  const header = buildSimpleHeader(details ?? undefined, theme);
72
75
  if (header) container.addChild(header);
73
76
 
@@ -77,28 +80,38 @@ export function renderGraphResult(
77
80
  renderEvidenceLines(container, evidenceLists, theme);
78
81
  }
79
82
 
83
+ renderToolDisplaySections(container, result.details?.displaySections, theme);
80
84
  renderStructuredDetailBody(container, details ?? undefined, theme);
85
+ const truncation = renderTruncationDisclosure(result, theme);
86
+ if (truncation) {
87
+ container.addChild(new Spacer(1));
88
+ container.addChild(truncation);
89
+ }
81
90
  renderMarkdownDetail(container, result, theme);
82
-
83
91
  return container;
84
92
  }
85
93
 
86
- /** ── Helpers ───────────────────────────────────────────────────── */
87
-
88
- function formatRelations(relations: GraphRelation[]): string {
94
+ function formatRelations(relations: readonly GraphRelation[]): string {
89
95
  if (relations.length === 0) return "";
90
96
  if (relations.length === 1) return `→ ${relations[0]}`;
91
97
  if (relations.length <= 3) return `→ ${relations.join(", ")}`;
92
98
  return `→ ${relations.slice(0, 2).join(", ")} +${relations.length - 2}`;
93
99
  }
94
100
 
95
- function formatTarget(params: CodeGraphToolParams): string {
96
- if ("symbol" in params.target) return `of ${params.target.symbol.query}`;
97
- if ("anchor" in params.target) {
98
- const point = params.target.anchor;
99
- const file = point.file.split("/").pop() ?? point.file;
100
- return `at ${file}:${point.line}`;
101
+ function formatTarget(target: unknown): string {
102
+ if (!target || typeof target !== "object") return "";
103
+ const record = target as Record<string, unknown>;
104
+
105
+ if (record.symbol && typeof record.symbol === "object") {
106
+ const query = formatCallValue((record.symbol as Record<string, unknown>).query);
107
+ return query ? `of ${query}` : "";
108
+ }
109
+ if (record.anchor && typeof record.anchor === "object") {
110
+ const anchor = record.anchor as Record<string, unknown>;
111
+ const file = formatCallPath(anchor.file);
112
+ const line = typeof anchor.line === "number" ? `:${anchor.line}` : "";
113
+ return file ? `at ${file}${line}` : "";
101
114
  }
102
- if ("handle" in params.target) return `of ${params.target.handle}`;
103
- return "";
115
+ const handle = formatCallValue(record.handle);
116
+ return handle ? `of ${handle}` : "";
104
117
  }
@@ -21,12 +21,17 @@ export async function executeHealthTool(
21
21
  const outcome = await ctx.session.health(params as HealthWorkflowInput, toWorkflowControl(ctx));
22
22
  if (outcome.kind === "unavailable") throw new Error(outcome.reason);
23
23
  if (outcome.kind === "invalid-input") {
24
- return healthErrorResult(`**Error:** ${outcome.message}`, "invalid input");
24
+ return healthErrorResult(`**Error:** ${outcome.message}`, outcome.message);
25
25
  }
26
26
 
27
27
  const assembly = assembleHealthResult(outcome.data);
28
28
  return {
29
29
  content: renderHealthResult(assembly, ctx.cwd),
30
- details: { type: "health", data: assembly.details },
30
+ details: {
31
+ type: "health",
32
+ data: assembly.details,
33
+ status: "completed",
34
+ displaySections: assembly.displaySections,
35
+ },
31
36
  };
32
37
  }
@@ -51,16 +51,24 @@ function renderRefreshStatus(
51
51
 
52
52
  switch (data.refresh.kind) {
53
53
  case "completed":
54
- lines.push(`**Diagnostic refresh**: ${completedRefreshText(data.refresh)}.`);
55
- lines.push(`**Stale assessment**: ${staleAssessmentText(data.refresh)}.`);
54
+ lines.push(
55
+ `**${refreshAttemptLabel(data.refresh)}**: ${asSentence(completedRefreshText(data.refresh))}`,
56
+ );
57
+ lines.push(`**Stale assessment**: ${asSentence(staleAssessmentText(data.refresh))}`);
56
58
  lines.push("");
57
59
  return;
58
- case "failed":
59
- lines.push(`**Diagnostic refresh**: failed — ${data.refresh.reason}`);
60
+ case "failed": {
61
+ const evidence = data.refresh.diagnosticEvidence
62
+ ? `; ${formatDiagnosticEvidence(data.refresh.diagnosticEvidence)}`
63
+ : "";
64
+ lines.push(
65
+ `**${refreshAttemptLabel(data.refresh)}**: failed — ${data.refresh.reason}${evidence}`,
66
+ );
60
67
  lines.push("");
61
68
  return;
69
+ }
62
70
  case "not-attempted":
63
- lines.push(`**Diagnostic refresh**: not attempted — ${data.refresh.reason}`);
71
+ lines.push(`**Diagnostic refresh attempt**: not started — ${data.refresh.reason}`);
64
72
  if (data.refresh.lastAttempt) renderLastAttempt(lines, data.refresh.lastAttempt, cwd);
65
73
  lines.push("");
66
74
  return;
@@ -69,7 +77,7 @@ function renderRefreshStatus(
69
77
  renderLastAttempt(lines, data.refresh.lastAttempt, cwd);
70
78
  } else {
71
79
  lines.push(
72
- "**Diagnostic refresh**: not attempted this session. Use `refresh: true` to try one.",
80
+ "**Diagnostic refresh attempt**: not requested this session. Use `refresh: true` to try one.",
73
81
  );
74
82
  }
75
83
  lines.push("");
@@ -79,10 +87,19 @@ function renderRefreshStatus(
79
87
  function completedRefreshText(
80
88
  attempt: Extract<HealthRefreshAttempt, { kind: "completed" }>,
81
89
  ): string {
82
- if (attempt.attemptedActiveClients === 0 && attempt.restartedClients === 0) {
83
- return "completed no-op no active clients were targeted";
84
- }
85
- return `completed — ${attempt.attemptedActiveClients} active client${plural(attempt.attemptedActiveClients)} targeted, ${attempt.restartedClients} restarted`;
90
+ const base =
91
+ attempt.attemptedActiveClients === 0 && attempt.restartedClients === 0
92
+ ? "completed no-op — no active clients were targeted"
93
+ : `completed — ${attempt.attemptedActiveClients} active client${plural(attempt.attemptedActiveClients)} targeted, ${attempt.restartedClients} restarted`;
94
+ return attempt.operationScope === "workspace-runtime"
95
+ ? `${base}; ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}`
96
+ : base;
97
+ }
98
+
99
+ function refreshAttemptLabel(attempt: Pick<HealthRefreshAttempt, "operationScope">): string {
100
+ return attempt.operationScope === "file-runtime"
101
+ ? "File LSP maintenance attempt"
102
+ : "Diagnostic refresh attempt";
86
103
  }
87
104
 
88
105
  function staleAssessmentText(
@@ -90,6 +107,9 @@ function staleAssessmentText(
90
107
  ): string {
91
108
  if (attempt.staleAssessment.warning) return attempt.staleAssessment.warning;
92
109
  const matches = `${attempt.staleAssessment.matchedFileCount} file${plural(attempt.staleAssessment.matchedFileCount)} match the stale-module heuristic`;
110
+ if (attempt.staleAssessment.scope === "file") {
111
+ return `${matches} in the requested file; workspace clustering was not assessed`;
112
+ }
93
113
  return attempt.staleAssessment.suspected
94
114
  ? `${matches}; a clustered stale-module pattern is suspected`
95
115
  : `${matches}; no clustered stale-module pattern is suspected`;
@@ -97,12 +117,19 @@ function staleAssessmentText(
97
117
 
98
118
  function renderLastAttempt(lines: string[], attempt: HealthRefreshAttempt, cwd: string): void {
99
119
  const outcome =
100
- attempt.kind === "completed" ? completedRefreshText(attempt) : `failed — ${attempt.reason}`;
120
+ attempt.kind === "completed"
121
+ ? completedRefreshText(attempt)
122
+ : `failed — ${attempt.reason}${attempt.diagnosticEvidence ? `; ${formatDiagnosticEvidence(attempt.diagnosticEvidence)}` : ""}`;
123
+ const label = refreshAttemptLabel(attempt);
101
124
  lines.push(
102
- `**Last diagnostic refresh attempt**: ${outcome}, started ${formatElapsed(Date.now() - attempt.attemptedAt)}; requested ${formatDiagnosticScope(attempt.requestedDiagnosticScope, cwd)}; operation scope: workspace runtime.`,
125
+ `**Last ${label.toLowerCase()}**: ${asSentence(outcome)} Started ${formatElapsed(Date.now() - attempt.attemptedAt)}; requested ${formatDiagnosticScope(attempt.requestedDiagnosticScope, cwd)}; operation scope: ${refreshOperationScopeText(attempt)}.`,
103
126
  );
104
127
  }
105
128
 
129
+ function refreshOperationScopeText(attempt: HealthRefreshAttempt): string {
130
+ return attempt.operationScope === "file-runtime" ? "file runtime" : "workspace runtime";
131
+ }
132
+
106
133
  function formatElapsed(milliseconds: number): string {
107
134
  const seconds = Math.max(0, Math.round(milliseconds / 1_000));
108
135
  if (seconds < 60) return `${seconds}s ago`;
@@ -143,6 +170,7 @@ function renderDiagnosticsSection(lines: string[], data: HealthData, cwd: string
143
170
  lines.push("Diagnostics were not requested.");
144
171
  } else {
145
172
  lines.push(`**Evidence scope**: ${formatDiagnosticScope(observation.scope, cwd)}.`);
173
+ lines.push(`**Evidence coverage**: ${formatDiagnosticEvidence(observation.evidence)}.`);
146
174
  lines.push("");
147
175
  renderDiagnosticObservation(lines, data, cwd);
148
176
  }
@@ -150,15 +178,25 @@ function renderDiagnosticsSection(lines: string[], data: HealthData, cwd: string
150
178
  lines.push("");
151
179
  }
152
180
 
181
+ function formatDiagnosticEvidence(evidence: {
182
+ requested: number;
183
+ confirmed: number;
184
+ unconfirmed: number;
185
+ failed: number;
186
+ removed: number;
187
+ }): string {
188
+ return `${evidence.requested} requested, ${evidence.confirmed} confirmed, ${evidence.unconfirmed} unconfirmed, ${evidence.failed} failed, ${evidence.removed} removed`;
189
+ }
190
+
153
191
  function renderDiagnosticObservation(lines: string[], data: HealthData, cwd: string): void {
154
192
  const observation = data.diagnostics;
155
193
  if (observation.kind === "not-requested") return;
156
194
  if (observation.kind === "unavailable") {
157
- lines.push(`Diagnostics unavailable — ${observation.reason}.`);
195
+ lines.push(`Diagnostics unavailable — ${asSentence(observation.reason)}`);
158
196
  return;
159
197
  }
160
198
  if (observation.kind === "partial") {
161
- lines.push(`Diagnostics partially collected — ${observation.reason}.`);
199
+ lines.push(`Diagnostics partially collected — ${asSentence(observation.reason)}`);
162
200
  if (observation.entries.length > 0) {
163
201
  lines.push("");
164
202
  lines.push("Partial results:");
@@ -173,6 +211,10 @@ function renderDiagnosticObservation(lines: string[], data: HealthData, cwd: str
173
211
  renderDiagnosticEntries(lines, data, cwd);
174
212
  }
175
213
 
214
+ function asSentence(text: string): string {
215
+ return /[.!?]$/.test(text) ? text : `${text}.`;
216
+ }
217
+
176
218
  function emptyDiagnosticText(scope: HealthDiagnosticScope, cwd: string): string {
177
219
  if (scope.kind === "file") {
178
220
  return `No errors or warnings found for \`${makeRelative(cwd, scope.path)}\`.`;
@@ -0,0 +1,105 @@
1
+ /** Render current and retained health refresh status for the TUI. */
2
+
3
+ export function readRefreshStatus(data: Record<string, unknown> | null): string | null {
4
+ const refresh = readRecord(data?.refresh);
5
+ if (!refresh) return null;
6
+
7
+ switch (refresh.kind) {
8
+ case "completed": {
9
+ const attempted = readNumber(refresh.attemptedActiveClients);
10
+ const restarted = readNumber(refresh.restartedClients);
11
+ const stale = readRecord(refresh.staleAssessment);
12
+ const noOp = attempted === 0 && restarted === 0;
13
+ const label = refreshAttemptLabel(refresh);
14
+ const base = noOp
15
+ ? `${label} completed no-op`
16
+ : `${label} completed: ${attempted} clients targeted, ${restarted} restarted`;
17
+ const evidence = formatDiagnosticEvidence(readRecord(refresh.diagnosticEvidence));
18
+ const withEvidence = evidence ? `${base}; ${evidence}` : base;
19
+ return stale?.suspected === true
20
+ ? `${withEvidence}; stale pattern suspected in ${readNumber(stale.matchedFileCount)} files`
21
+ : withEvidence;
22
+ }
23
+ case "failed": {
24
+ const evidence = formatDiagnosticEvidence(readRecord(refresh.diagnosticEvidence));
25
+ return `${refreshAttemptLabel(refresh)} failed${typeof refresh.reason === "string" ? `: ${refresh.reason}` : ""}${evidence ? `; ${evidence}` : ""}`;
26
+ }
27
+ case "not-attempted":
28
+ return `refresh attempt not started${formatLastRefreshSuffix(refresh)}`;
29
+ case "not-requested":
30
+ return `refresh not requested${formatLastRefreshSuffix(refresh)}`;
31
+ default:
32
+ return null;
33
+ }
34
+ }
35
+
36
+ export function readPreviousRefreshStatus(data: Record<string, unknown> | null): string | null {
37
+ const refresh = readRecord(data?.refresh);
38
+ if (!refresh || (refresh.kind !== "not-requested" && refresh.kind !== "not-attempted")) {
39
+ return null;
40
+ }
41
+ const attempt = readRecord(refresh.lastAttempt);
42
+ if (!attempt) return null;
43
+ const evidence = formatCompactDiagnosticEvidence(readRecord(attempt.diagnosticEvidence));
44
+ if (attempt.kind === "failed") {
45
+ return `last ${refreshAttemptLabel(attempt)} failed${evidence ? ` (${evidence})` : ""}`;
46
+ }
47
+ if (attempt.kind === "completed") {
48
+ return `last ${refreshAttemptLabel(attempt)} completed${evidence ? ` (${evidence})` : ""}`;
49
+ }
50
+ return null;
51
+ }
52
+
53
+ function refreshAttemptLabel(attempt: Record<string, unknown>): string {
54
+ return attempt.operationScope === "file-runtime" ? "file maintenance attempt" : "refresh attempt";
55
+ }
56
+
57
+ function formatLastRefreshSuffix(refresh: Record<string, unknown>): string {
58
+ const attempt = readRecord(refresh.lastAttempt);
59
+ if (!attempt) return "";
60
+ const evidence = formatDiagnosticEvidence(readRecord(attempt.diagnosticEvidence));
61
+ if (attempt.kind === "failed") {
62
+ const reason = typeof attempt.reason === "string" ? `: ${attempt.reason}` : "";
63
+ return `; last ${refreshAttemptLabel(attempt)} failed${reason}${evidence ? `; ${evidence}` : ""}`;
64
+ }
65
+ if (attempt.kind === "completed") {
66
+ return `; last ${refreshAttemptLabel(attempt)} completed${evidence ? `; ${evidence}` : ""}`;
67
+ }
68
+ return "";
69
+ }
70
+
71
+ function formatCompactDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
72
+ const counts = readEvidenceCounts(evidence);
73
+ if (!counts) return null;
74
+ return `req ${counts[0]} · conf ${counts[1]} · unconf ${counts[2]} · failed ${counts[3]} · removed ${counts[4]}`;
75
+ }
76
+
77
+ function formatDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
78
+ const counts = readEvidenceCounts(evidence);
79
+ if (!counts) return null;
80
+ return `${counts[0]} requested, ${counts[1]} confirmed, ${counts[2]} unconfirmed, ${counts[3]} failed, ${counts[4]} removed`;
81
+ }
82
+
83
+ function readEvidenceCounts(evidence: Record<string, unknown> | null): number[] | null {
84
+ if (!evidence) return null;
85
+ const counts = [
86
+ evidence.requested,
87
+ evidence.confirmed,
88
+ evidence.unconfirmed,
89
+ evidence.failed,
90
+ evidence.removed,
91
+ ];
92
+ return counts.every(isEvidenceCount) ? counts : null;
93
+ }
94
+
95
+ function isEvidenceCount(value: unknown): value is number {
96
+ return typeof value === "number" && Number.isInteger(value) && value >= 0;
97
+ }
98
+
99
+ function readRecord(value: unknown): Record<string, unknown> | null {
100
+ return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : null;
101
+ }
102
+
103
+ function readNumber(value: unknown): number {
104
+ return typeof value === "number" ? value : 0;
105
+ }