@mrclrchtr/supi-code-intelligence 6.4.0 → 7.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +164 -82
  2. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  3. package/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
  4. package/node_modules/@mrclrchtr/supi-core/README.md +8 -0
  5. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  6. package/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
  7. package/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
  8. package/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
  9. package/node_modules/@mrclrchtr/supi-lsp/README.md +35 -13
  10. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  11. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
  12. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +8 -0
  13. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  14. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
  15. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
  16. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
  17. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/index.js +3 -1
  18. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/legacy.js +3 -1
  19. package/node_modules/@mrclrchtr/supi-lsp/node_modules/ignore/package.json +1 -1
  20. package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
  21. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +6 -0
  22. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-cache.ts +43 -35
  23. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-capabilities.ts +83 -17
  24. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-collection.ts +108 -92
  25. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-evidence.ts +44 -82
  26. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-host.ts +3 -4
  27. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-publication.ts +9 -68
  28. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-pull.ts +45 -58
  29. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-refresh.ts +230 -331
  30. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-request.ts +201 -1
  31. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-timing.ts +129 -70
  32. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-typescript.ts +395 -0
  33. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostic-waiters.ts +8 -12
  34. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-diagnostics.ts +685 -183
  35. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-state.ts +2 -2
  36. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-document-sync.ts +60 -60
  37. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-request-enrollment.ts +75 -0
  38. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-barrier.ts +443 -0
  39. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-errors.ts +30 -0
  40. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-semantic-input-file.ts +15 -0
  41. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +290 -37
  42. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +72 -16
  43. package/node_modules/@mrclrchtr/supi-lsp/src/config/capabilities.ts +1 -0
  44. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +30 -4
  45. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/evidence.ts +1 -1
  46. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
  47. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
  48. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
  49. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +608 -45
  50. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
  51. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
  52. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +31 -11
  53. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +19 -3
  54. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  55. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
  56. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
  62. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
  63. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
  66. package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
  67. package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
  68. package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
  69. package/node_modules/yaml/dist/nodes/Alias.js +25 -23
  70. package/node_modules/yaml/package.json +1 -1
  71. package/package.json +5 -5
  72. package/src/analysis/health/diagnostics.ts +1 -2
  73. package/src/analysis/health/file-scope.ts +4 -1
  74. package/src/analysis/health/server-status.ts +1 -1
  75. package/src/analysis/relations/callees.ts +17 -8
  76. package/src/analysis/relations/implementations.ts +1 -2
  77. package/src/analysis/relations/references.ts +8 -4
  78. package/src/analysis/relations/types.ts +6 -30
  79. package/src/analysis/target/anchored.ts +9 -1
  80. package/src/analysis/target/symbol.ts +29 -11
  81. package/src/analysis/target/types.ts +14 -6
  82. package/src/api.ts +2 -0
  83. package/src/session/graph/collect.ts +18 -3
  84. package/src/session/health-refresh.ts +12 -1
  85. package/src/session/health-types.ts +18 -6
  86. package/src/session/health-workflow.ts +21 -19
  87. package/src/session/orientation-types.ts +5 -6
  88. package/src/session/orientation-workflow.ts +10 -2
  89. package/src/session/target-workflow.ts +9 -6
  90. package/src/substrate/lsp/source-tracking.ts +11 -6
  91. package/src/tool/code_find/guidance.ts +2 -2
  92. package/src/tool/code_find/spec.ts +5 -4
  93. package/src/tool/code_find/tui.ts +7 -1
  94. package/src/tool/code_graph/details.ts +32 -0
  95. package/src/tool/code_graph/display.ts +54 -0
  96. package/src/tool/code_graph/format.ts +60 -0
  97. package/src/tool/code_graph/guidance.ts +1 -1
  98. package/src/tool/code_graph/markdown.ts +76 -198
  99. package/src/tool/code_graph/read-next.ts +65 -0
  100. package/src/tool/code_graph/result.ts +118 -83
  101. package/src/tool/code_graph/tui.ts +216 -54
  102. package/src/tool/code_health/guidance.ts +2 -4
  103. package/src/tool/code_health/markdown.ts +75 -58
  104. package/src/tool/code_health/refresh-outcome.ts +41 -1
  105. package/src/tool/code_health/refresh-status.ts +114 -48
  106. package/src/tool/code_health/scope.ts +78 -0
  107. package/src/tool/code_health/spec.ts +1 -1
  108. package/src/tool/code_health/tui.ts +25 -38
  109. package/src/tool/code_inspect/tui.ts +7 -1
  110. package/src/tool/code_orientation/result.ts +31 -8
  111. package/src/tool/code_orientation/tui.ts +26 -5
  112. package/src/tool/code_refactor_apply/guidance.ts +1 -1
  113. package/src/tool/code_refactor_apply/tui.ts +7 -3
  114. package/src/tool/code_refactor_plan/guidance.ts +1 -1
  115. package/src/tool/code_refactor_plan/tui.ts +7 -1
  116. package/src/tool/code_resolve/guidance.ts +2 -4
  117. package/src/tool/code_resolve/result.ts +13 -10
  118. package/src/tool/code_resolve/tui.ts +18 -1
  119. package/src/tool/infra/truncate.ts +39 -13
  120. package/src/tool/result/candidate-row.ts +27 -0
  121. package/src/tool/result/errors.ts +21 -6
  122. package/src/tool/result/types.ts +7 -3
  123. package/src/tool/schemas.ts +5 -0
  124. package/src/types/execution.ts +2 -0
  125. package/src/types/index.ts +2 -0
  126. package/src/ui/tui/candidate-legacy-body.ts +69 -0
  127. package/src/ui/tui/candidate-selection.ts +366 -0
  128. package/src/ui/tui/common.ts +26 -31
  129. package/src/ui/tui/execution-error.ts +92 -0
@@ -3,7 +3,7 @@
3
3
  */
4
4
 
5
5
  import type { Theme } from "@earendil-works/pi-coding-agent";
6
- import { Container, Spacer, Text } from "@earendil-works/pi-tui";
6
+ import { type Component, Container, Spacer, Text } from "@earendil-works/pi-tui";
7
7
  import {
8
8
  formatProjectServerRouteStatusCounts,
9
9
  type ProjectServerRouteStatusCounts,
@@ -31,6 +31,11 @@ import {
31
31
  readRefreshStatus,
32
32
  readSourceTrackingStatus,
33
33
  } from "./refresh-status.ts";
34
+ import {
35
+ formatCompactDiagnosticEvidence,
36
+ formatDiagnosticEvidence,
37
+ withDiagnosticScope,
38
+ } from "./scope.ts";
34
39
  import { formatSemanticHealthState, readSemanticHealthState } from "./semantic-state.ts";
35
40
 
36
41
  /** ── renderCall ────────────────────────────────────────────────── */
@@ -64,12 +69,16 @@ export function renderHealthResult(
64
69
  options: ResultOptios,
65
70
  theme: Theme,
66
71
  context: ToolRendererContext | undefined,
67
- ): Container | Text {
72
+ ): Component {
68
73
  if (options.isPartial) return renderPartial("Gathering workspace health…", theme);
69
74
 
70
75
  const data =
71
76
  result.details?.type === "health" ? (result.details.data as Record<string, unknown>) : null;
72
- const executionError = renderExecutionError(context, "code_health failed", theme);
77
+ const executionError = renderExecutionError(
78
+ result,
79
+ { isError: context?.isError, expanded: options.expanded, label: "code_health failed" },
80
+ theme,
81
+ );
73
82
  if (executionError) return executionError;
74
83
  const domainError = renderDomainError(result, theme);
75
84
  if (domainError) return renderDomainResult(result, options, theme, domainError);
@@ -136,7 +145,7 @@ function buildCompactSummary(data: Record<string, unknown> | null, theme: Theme)
136
145
  formatSectionSummary(
137
146
  section,
138
147
  section.key === "diagnostics"
139
- ? formatCompactDiagnosticEvidence(readDiagnosticEvidence(data))
148
+ ? formatCompactDiagnosticEvidence(readDiagnosticEvidence(data), diagnosticScopeKind(data))
140
149
  : null,
141
150
  theme,
142
151
  fileReadinessPending,
@@ -230,36 +239,6 @@ function readDiagnosticEvidence(
230
239
  return readRecord(readRecord(data?.diagnosticObservation)?.evidence);
231
240
  }
232
241
 
233
- function formatCompactDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
234
- if (!evidence) return null;
235
- const counts = [
236
- evidence.requested,
237
- evidence.confirmed,
238
- evidence.unconfirmed,
239
- evidence.failed,
240
- evidence.removed,
241
- ];
242
- if (!counts.every(isEvidenceCount)) return null;
243
- return `req ${evidence.requested} · conf ${evidence.confirmed} · unconf ${evidence.unconfirmed} · failed ${evidence.failed} · removed ${evidence.removed}`;
244
- }
245
-
246
- function formatDiagnosticEvidence(evidence: Record<string, unknown> | null): string | null {
247
- if (!evidence) return null;
248
- const counts = [
249
- evidence.requested,
250
- evidence.confirmed,
251
- evidence.unconfirmed,
252
- evidence.failed,
253
- evidence.removed,
254
- ];
255
- if (!counts.every(isEvidenceCount)) return null;
256
- return `${evidence.requested} requested, ${evidence.confirmed} confirmed, ${evidence.unconfirmed} unconfirmed, ${evidence.failed} failed, ${evidence.removed} removed`;
257
- }
258
-
259
- function isEvidenceCount(value: unknown): value is number {
260
- return typeof value === "number" && Number.isInteger(value) && value >= 0;
261
- }
262
-
263
242
  function readRecord(value: unknown): Record<string, unknown> | null {
264
243
  return typeof value === "object" && value !== null ? (value as Record<string, unknown>) : null;
265
244
  }
@@ -324,21 +303,29 @@ function buildDiagnosticSummary(data: Record<string, unknown> | null, theme: The
324
303
  const label = isPendingFileReadiness(data)
325
304
  ? "Diagnostics pending — LSP may still be warming; retry shortly"
326
305
  : "Diagnostics unavailable";
327
- return new Text(theme.fg("warning", label), 0, 0);
306
+ return new Text(theme.fg("warning", withDiagnosticScope(label, data)), 0, 0);
328
307
  }
329
308
  const trackedFiles = diagnosticScopeKind(data) === "tracked-files";
330
309
  if (section.status === "partial") {
331
310
  const coverage = formatDiagnosticEvidence(readDiagnosticEvidence(data));
332
311
  const label = trackedFiles ? "Tracked-file diagnostics partial" : "Diagnostics partial";
333
- return new Text(theme.fg("warning", coverage ? `${label} (${coverage})` : label), 0, 0);
312
+ const summary = coverage ? `${label} (${coverage})` : label;
313
+ return new Text(theme.fg("warning", withDiagnosticScope(summary, data)), 0, 0);
334
314
  }
335
315
  if (section.itemCount === 0) {
336
- return new Text(theme.fg("success", diagnosticEmptySummary(data)), 0, 0);
316
+ return new Text(
317
+ theme.fg("success", withDiagnosticScope(diagnosticEmptySummary(data), data)),
318
+ 0,
319
+ 0,
320
+ );
337
321
  }
338
322
 
339
323
  const fileLabel = `${trackedFiles ? "tracked " : ""}${section.itemCount === 1 ? "file" : "files"}`;
340
324
  return new Text(
341
- theme.fg("warning", theme.bold(`${section.itemCount} ${fileLabel} with issues`)),
325
+ theme.fg(
326
+ "warning",
327
+ theme.bold(withDiagnosticScope(`${section.itemCount} ${fileLabel} with issues`, data)),
328
+ ),
342
329
  0,
343
330
  0,
344
331
  );
@@ -40,5 +40,11 @@ export function renderInspectResult(
40
40
  theme: Theme,
41
41
  context: ToolRendererContext | undefined,
42
42
  ): ReturnType<typeof renderSimpleResult> {
43
- return renderSimpleResult(result, options, theme, "Inspecting…", context);
43
+ return renderSimpleResult(
44
+ result,
45
+ options,
46
+ theme,
47
+ { progress: "Inspecting…", failure: "code_inspect failed" },
48
+ context,
49
+ );
44
50
  }
@@ -1,5 +1,8 @@
1
1
  import type { ConfidenceMode } from "@mrclrchtr/supi-code-runtime/api";
2
- import type { EvidenceListMetadata } from "../../analysis/evidence.ts";
2
+ import {
3
+ type EvidenceListMetadata,
4
+ renderEvidenceListMetadataDisclosure,
5
+ } from "../../analysis/evidence.ts";
3
6
  import type { ReadNextItem } from "../../analysis/read-next.ts";
4
7
  import type {
5
8
  OrientationCandidate,
@@ -15,6 +18,7 @@ import {
15
18
  type ResultSection,
16
19
  type ToolResultAssembly,
17
20
  } from "../result/assembly.ts";
21
+ import { formatCandidateRow } from "../result/candidate-row.ts";
18
22
  import { createToolDisplaySection } from "../result/display.ts";
19
23
  import type {
20
24
  ContextDetails,
@@ -47,17 +51,17 @@ export interface OrientationResultAssembly {
47
51
  /** Build structured candidate rows for an unresolved Orientation target. */
48
52
  export function orientationCandidateDisplaySections(
49
53
  candidates: readonly OrientationCandidate[],
50
- omittedCount = 0,
54
+ evidence: EvidenceListMetadata,
51
55
  ): readonly ToolDisplaySection[] {
52
56
  return [
53
57
  createToolDisplaySection({
54
58
  key: "orientation.candidates",
55
59
  title: "Candidates",
56
60
  items: candidates,
57
- totalCount: candidates.length + omittedCount,
58
- omittedCount,
59
- format: (candidate) =>
60
- `${candidate.rank}. ${candidate.name} (${candidate.kind ?? "unknown"}) — ${candidate.file}:${candidate.line}:${candidate.character} [${candidate.targetId}]`,
61
+ totalCount: evidence.totalCount,
62
+ omittedCount: evidence.omittedCount,
63
+ partialReason: evidence.partialReason,
64
+ format: (candidate) => formatCandidateRow(candidate, "rank-first"),
61
65
  }),
62
66
  ];
63
67
  }
@@ -215,13 +219,23 @@ export function finishOrientationResult(outcome: OrientationOutcome): CodeIntelR
215
219
  `${candidate.rank}. **${candidate.name}** (\`${candidate.kind ?? "unknown"}\`) — \`${candidate.file}\`:${candidate.line}:${candidate.character} — \`${candidate.targetId}\``,
216
220
  );
217
221
  }
222
+ const evidence: EvidenceListMetadata = {
223
+ key: "orientation.candidates",
224
+ totalCount: outcome.totalCount,
225
+ shownCount: candidates.length,
226
+ omittedCount: outcome.omittedCount,
227
+ partialReason: outcome.partialReason,
228
+ };
229
+ const disclosure = renderEvidenceListMetadataDisclosure(evidence);
230
+ if (disclosure) lines.push("", disclosure);
218
231
  const nextQueries =
219
232
  outcome.kind === "kind-mismatch"
220
233
  ? ["Retry without symbolKind, use an observed provider kind, or focus one handle"]
221
234
  : ["Use one candidate handle as focus.target.handle"];
222
235
  const details = assembleOrientationDetails({
223
236
  confidence: "semantic",
224
- omittedCount: outcome.omittedCount,
237
+ omittedCount: evidence.omittedCount ?? 0,
238
+ evidenceLists: [evidence],
225
239
  candidates,
226
240
  nextQueries,
227
241
  });
@@ -231,7 +245,8 @@ export function finishOrientationResult(outcome: OrientationOutcome): CodeIntelR
231
245
  type: "context",
232
246
  data: details,
233
247
  status: "completed",
234
- displaySections: orientationCandidateDisplaySections(candidates, outcome.omittedCount),
248
+ message: orientationSelectionMessage(outcome),
249
+ displaySections: orientationCandidateDisplaySections(candidates, evidence),
235
250
  },
236
251
  };
237
252
  }
@@ -246,3 +261,11 @@ export function finishOrientationResult(outcome: OrientationOutcome): CodeIntelR
246
261
  },
247
262
  };
248
263
  }
264
+
265
+ function orientationSelectionMessage(
266
+ outcome: Extract<OrientationOutcome, { kind: "disambiguation" | "kind-mismatch" }>,
267
+ ): string {
268
+ return outcome.kind === "kind-mismatch"
269
+ ? `No Orientation target matched provider kind ${outcome.requestedKind}.`
270
+ : "Multiple Orientation targets require one candidate.";
271
+ }
@@ -1,6 +1,7 @@
1
1
  /** TUI renderer for code_orientation. */
2
2
  import { getMarkdownTheme, type Theme } from "@earendil-works/pi-coding-agent";
3
- import { Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
3
+ import { type Component, Container, Markdown, Spacer, Text } from "@earendil-works/pi-tui";
4
+ import { renderCandidateSelection } from "../../ui/tui/candidate-selection.ts";
4
5
  import {
5
6
  type EvidenceEntry,
6
7
  formatCallPath,
@@ -50,15 +51,35 @@ export function renderOrientationResult(
50
51
  options: ResultOptios,
51
52
  theme: Theme,
52
53
  context: ToolRendererContext | undefined,
53
- ): Container | Text {
54
+ ): Component {
54
55
  if (options.isPartial) return renderPartial("Orienting…", theme);
55
56
 
57
+ const executionError = renderExecutionError(
58
+ result,
59
+ { isError: context?.isError, expanded: options.expanded, label: "code_orientation failed" },
60
+ theme,
61
+ );
62
+ if (executionError) return executionError;
63
+
64
+ const candidateSelection = renderCandidateSelection(
65
+ result,
66
+ options,
67
+ theme,
68
+ "orientation.candidates",
69
+ );
70
+ if (candidateSelection) return candidateSelection;
71
+
72
+ return renderOrientationDetails(result, options, theme);
73
+ }
74
+
75
+ function renderOrientationDetails(
76
+ result: ToolResult,
77
+ options: ResultOptios,
78
+ theme: Theme,
79
+ ): Component {
56
80
  const data =
57
81
  result.details?.type === "context" ? (result.details.data as Record<string, unknown>) : null;
58
82
  const markdownText = result.content.find((c) => c.type === "text")?.text ?? "";
59
-
60
- const executionError = renderExecutionError(context, "code_orientation failed", theme);
61
- if (executionError) return executionError;
62
83
  const domainError = renderDomainError(result, theme);
63
84
  if (domainError) return renderDomainResult(result, options, theme, domainError);
64
85
 
@@ -1,5 +1,5 @@
1
1
  export const toolDescription =
2
- "Apply a fresh stored refactor plan to change its files. It does not create or regenerate a plan.";
2
+ "Apply a stored refactor plan by its returned planId after freshness and edit-safety checks. Changed files reject the apply and require a new plan; this tool does not regenerate one.";
3
3
 
4
4
  export const promptSnippet = "apply a stored refactor plan";
5
5
 
@@ -1,6 +1,6 @@
1
1
  /** TUI renderer for code_refactor_apply. */
2
2
  import type { Theme } from "@earendil-works/pi-coding-agent";
3
- import { Container, Spacer, Text } from "@earendil-works/pi-tui";
3
+ import { type Component, Container, Spacer, Text } from "@earendil-works/pi-tui";
4
4
  import {
5
5
  formatCallValue,
6
6
  type ResultOptios,
@@ -33,10 +33,14 @@ export function renderRefactorApplyResult(
33
33
  options: ResultOptios,
34
34
  theme: Theme,
35
35
  context: ToolRendererContext | undefined,
36
- ): Container | Text {
36
+ ): Component {
37
37
  if (options.isPartial) return renderPartial("Applying…", theme);
38
38
 
39
- const executionError = renderExecutionError(context, "code_refactor_apply failed", theme);
39
+ const executionError = renderExecutionError(
40
+ result,
41
+ { isError: context?.isError, expanded: options.expanded, label: "code_refactor_apply failed" },
42
+ theme,
43
+ );
40
44
  if (executionError) return executionError;
41
45
  const domainError = renderDomainError(result, theme);
42
46
  if (domainError) return renderDomainResult(result, options, theme, domainError);
@@ -1,5 +1,5 @@
1
1
  export const toolDescription =
2
- "Preview a semantic refactor without changing files or falling back to text edits.";
2
+ "Create and store a semantic refactor preview without changing files. Unsupported operations do not fall back to text edits.";
3
3
 
4
4
  export const promptSnippet = "preview a semantic refactor";
5
5
 
@@ -50,5 +50,11 @@ export function renderRefactorPlanResult(
50
50
  theme: Theme,
51
51
  context: ToolRendererContext | undefined,
52
52
  ): ReturnType<typeof renderSimpleResult> {
53
- return renderSimpleResult(result, options, theme, "Planning…", context);
53
+ return renderSimpleResult(
54
+ result,
55
+ options,
56
+ theme,
57
+ { progress: "Planning…", failure: "code_refactor_plan failed" },
58
+ context,
59
+ );
54
60
  }
@@ -1,8 +1,6 @@
1
1
  export const toolDescription =
2
- "Resolve a real symbol anchor or semantic query to target handles, or list a file's declarations as a target group. Symbol lookup does not fall back to text search.";
2
+ "Resolve a real symbol anchor or semantic query to target handles for later code tools, or list a file's declarations as a target group. This tool does not search file text.";
3
3
 
4
4
  export const promptSnippet = "resolve symbols or file declarations";
5
5
 
6
- export const promptGuidelines = [
7
- "Use code_resolve when a symbol query can match multiple symbols or when a later tool requires a target handle.",
8
- ];
6
+ export const promptGuidelines: string[] = [];
@@ -10,6 +10,7 @@ import {
10
10
  type ResultProvenance,
11
11
  type ToolResultAssembly,
12
12
  } from "../result/assembly.ts";
13
+ import { formatCandidateRow } from "../result/candidate-row.ts";
13
14
  import { createToolDisplaySection } from "../result/display.ts";
14
15
  import type { ResolveDetails, ToolDisplaySection } from "../result/types.ts";
15
16
 
@@ -25,7 +26,7 @@ export interface ResolveResultAssembly {
25
26
  interface ResolveProjection {
26
27
  readonly key: string;
27
28
  readonly title: string;
28
- readonly status: "complete" | "unavailable";
29
+ readonly status: "complete" | "partial" | "unavailable";
29
30
  readonly items: readonly unknown[];
30
31
  readonly confidence: ConfidenceMode;
31
32
  readonly evidence: EvidenceListMetadata | null;
@@ -153,19 +154,19 @@ function projectTargetGroup(
153
154
  function projectCandidateOutcome(
154
155
  outcome: Extract<TargetWorkflowOutcome, { kind: "disambiguation" | "kind-mismatch" }>,
155
156
  ): ResolveProjection {
156
- const totalCount = outcome.candidates.length + outcome.omittedCount;
157
157
  const evidence: EvidenceListMetadata = {
158
158
  key: "resolve.candidates",
159
- totalCount,
159
+ totalCount: outcome.totalCount,
160
160
  shownCount: outcome.candidates.length,
161
161
  omittedCount: outcome.omittedCount,
162
- partialReason: null,
162
+ partialReason: outcome.partialReason,
163
163
  };
164
+ const collectedCount = evidence.shownCount + (evidence.omittedCount ?? 0);
164
165
  const mismatch = outcome.kind === "kind-mismatch";
165
166
  return {
166
167
  key: "resolve.candidates",
167
168
  title: mismatch ? "Near matches" : "Candidates",
168
- status: "complete",
169
+ status: outcome.partialReason ? "partial" : "complete",
169
170
  items: outcome.candidates,
170
171
  confidence: "semantic",
171
172
  evidence,
@@ -173,7 +174,7 @@ function projectCandidateOutcome(
173
174
  resultKind: outcome.kind,
174
175
  ...(mismatch ? { requestedKind: outcome.requestedKind } : {}),
175
176
  confidence: "semantic",
176
- targetCount: totalCount,
177
+ targetCount: evidence.totalCount ?? collectedCount,
177
178
  omittedCount: outcome.omittedCount,
178
179
  evidenceLists: [evidence],
179
180
  targets: [],
@@ -254,15 +255,17 @@ function resolveDisplaySections(details: ResolveDetails): ToolDisplaySection[] {
254
255
  }
255
256
 
256
257
  if (details.candidates && details.candidates.length > 0) {
258
+ const evidence = details.evidenceLists?.find((entry) => entry.key === "resolve.candidates");
259
+ if (!evidence) return [];
257
260
  return [
258
261
  createToolDisplaySection({
259
262
  key: "resolve.candidates",
260
263
  title: "Candidates",
261
264
  items: details.candidates,
262
- totalCount: details.targetCount,
263
- omittedCount: details.omittedCount,
264
- format: (candidate) =>
265
- `${candidate.rank}. ${candidate.name} (${candidate.kind ?? "unknown"}) — ${candidate.file}:${candidate.line}:${candidate.character} [${candidate.targetId}]`,
265
+ totalCount: evidence.totalCount,
266
+ omittedCount: evidence.omittedCount,
267
+ partialReason: evidence.partialReason,
268
+ format: (candidate) => formatCandidateRow(candidate, "rank-first"),
266
269
  }),
267
270
  ];
268
271
  }
@@ -1,6 +1,7 @@
1
1
  /** TUI renderer for code_resolve. */
2
2
  import type { Theme } from "@earendil-works/pi-coding-agent";
3
3
  import { Text } from "@earendil-works/pi-tui";
4
+ import { renderCandidateSelection } from "../../ui/tui/candidate-selection.ts";
4
5
  import {
5
6
  formatCallPath,
6
7
  formatCallValue,
@@ -56,5 +57,21 @@ export function renderResolveResult(
56
57
  theme: Theme,
57
58
  context: ToolRendererContext | undefined,
58
59
  ): ReturnType<typeof renderSimpleResult> {
59
- return renderSimpleResult(result, options, theme, "Resolving…", context);
60
+ if (!options.isPartial && !context?.isError) {
61
+ const candidateSelection = renderCandidateSelection(
62
+ result,
63
+ options,
64
+ theme,
65
+ "resolve.candidates",
66
+ );
67
+ if (candidateSelection) return candidateSelection;
68
+ }
69
+
70
+ return renderSimpleResult(
71
+ result,
72
+ options,
73
+ theme,
74
+ { progress: "Resolving…", failure: "code_resolve failed" },
75
+ context,
76
+ );
60
77
  }
@@ -48,7 +48,7 @@ export function truncateToolContent(
48
48
  return { text: `${result.content}${notice}`, truncated: true };
49
49
  }
50
50
 
51
- import { mkdtempSync, writeFileSync } from "node:fs";
51
+ import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
52
52
  import { tmpdir } from "node:os";
53
53
  import { join } from "node:path";
54
54
  import type { CodeIntelResult } from "../../types/index.ts";
@@ -57,7 +57,9 @@ import type { ToolOutputTruncationDetails } from "../result/types.ts";
57
57
  /**
58
58
  * Apply the canonical output bound to one assembled code-tool result:
59
59
  * truncate at PI limits, spill the full content to a temp file when it
60
- * overflowed, and record truncation details.
60
+ * overflowed or the human display omits returned evidence. A display-only
61
+ * continuation does not add text to model content. File failure is fatal only
62
+ * when model content would otherwise be lost.
61
63
  */
62
64
  export function boundCodeToolResult(
63
65
  content: string,
@@ -71,19 +73,43 @@ export function boundCodeToolResult(
71
73
  maxLines: options.maxLines,
72
74
  maxBytes: options.maxBytes,
73
75
  });
74
- if (truncated && content.length > 0) {
75
- const dir = mkdtempSync(join(tmpdir(), "supi-ci-"));
76
- const spillPath = join(dir, `${options.toolName}-output.md`);
77
- writeFileSync(spillPath, content, "utf-8");
78
- return {
79
- content: [
80
- { type: "text" as const, text: `${text}\n_Full output saved to: \`${spillPath}\`_` },
81
- ],
82
- details: withTruncation({ truncated: true, fullOutputPath: spillPath }),
83
- };
76
+ const displayTruncated = details?.truncation?.displayTruncated === true;
77
+ const truncation = { truncated, ...(displayTruncated ? { displayTruncated } : {}) };
78
+ if ((truncated || displayTruncated) && content.length > 0) {
79
+ try {
80
+ const spillPath = saveFullContent(content, options.toolName);
81
+ return {
82
+ content: [
83
+ {
84
+ type: "text" as const,
85
+ text: truncated ? `${text}\n_Full output saved to: \`${spillPath}\`_` : text,
86
+ },
87
+ ],
88
+ details: withTruncation({ ...truncation, fullOutputPath: spillPath }),
89
+ };
90
+ } catch (error) {
91
+ if (truncated) throw error;
92
+ // Keep valid evidence when only its optional human continuation failed.
93
+ }
84
94
  }
85
95
  return {
86
96
  content: [{ type: "text" as const, text }],
87
- details: withTruncation({ truncated: false }),
97
+ details: withTruncation(truncation),
88
98
  };
89
99
  }
100
+
101
+ function saveFullContent(content: string, toolName: string): string {
102
+ const dir = mkdtempSync(join(tmpdir(), "supi-ci-"));
103
+ const spillPath = join(dir, `${toolName}-output.md`);
104
+ try {
105
+ writeFileSync(spillPath, content, "utf-8");
106
+ return spillPath;
107
+ } catch (error) {
108
+ try {
109
+ rmSync(dir, { recursive: true, force: true });
110
+ } catch {
111
+ /* Preserve the write error. */
112
+ }
113
+ throw error;
114
+ }
115
+ }
@@ -0,0 +1,27 @@
1
+ /** Candidate fields used by the three target-selection row formats. */
2
+ export interface CandidateRowData {
3
+ readonly targetId: string;
4
+ readonly name: string;
5
+ readonly kind: string | null;
6
+ readonly file: string;
7
+ readonly line: number;
8
+ readonly character: number;
9
+ readonly rank?: number;
10
+ }
11
+
12
+ /** The order used for the target id and rank in one candidate row. */
13
+ export type CandidateRowOrder = "handle-first" | "rank-first";
14
+
15
+ /** Format one candidate row for a serialized display section or TUI fallback. */
16
+ export function formatCandidateRow(
17
+ candidate: CandidateRowData,
18
+ order: CandidateRowOrder,
19
+ fallbackRank = 1,
20
+ ): string {
21
+ const kind = candidate.kind ?? "unknown";
22
+ if (order === "handle-first") {
23
+ return `${candidate.targetId} — ${candidate.name} (${kind}) at ${candidate.file}:${candidate.line}:${candidate.character}`;
24
+ }
25
+ const rank = candidate.rank && candidate.rank > 0 ? candidate.rank : fallbackRank;
26
+ return `${rank}. ${candidate.name} (${kind}) — ${candidate.file}:${candidate.line}:${candidate.character} [${candidate.targetId}]`;
27
+ }
@@ -1,27 +1,42 @@
1
+ import type { ConfidenceMode } from "@mrclrchtr/supi-code-runtime/api";
2
+ import type { EvidenceListMetadata } from "../../analysis/evidence.ts";
1
3
  import type { CodeIntelResult, ToolDisplaySection } from "../../types/index.ts";
4
+ import { assembledNextQueries, assembleToolResult } from "./assembly.ts";
2
5
 
3
- /** Full error result for search-family tools. */
6
+ /** Full failure or candidate-selection result for search-family tools. */
4
7
  export function searchErrorResult(
5
8
  content: string,
6
9
  opts?: {
10
+ /** Candidate selection is not invalid usage. Defaults to invalid-input. */
11
+ status?: "invalid-input" | "disambiguation";
7
12
  scope?: string | null;
8
13
  nextQueries?: string[];
9
14
  message?: string;
10
15
  displaySections?: readonly ToolDisplaySection[];
16
+ /** Evidence retained when a target still needs selection. */
17
+ evidenceLists?: readonly EvidenceListMetadata[];
18
+ confidence?: ConfidenceMode;
11
19
  },
12
20
  ): CodeIntelResult {
21
+ const assembled = assembleToolResult({
22
+ data: null,
23
+ confidence: opts?.confidence ?? "unavailable",
24
+ evidenceLists: opts?.evidenceLists,
25
+ nextQueries: opts?.nextQueries,
26
+ });
13
27
  return {
14
28
  content,
15
29
  details: {
16
30
  type: "search" as const,
17
31
  data: {
18
- confidence: "unavailable" as const,
32
+ confidence: assembled.confidence,
19
33
  scope: opts?.scope ?? null,
20
- candidateCount: 0,
21
- omittedCount: 0,
22
- nextQueries: opts?.nextQueries ?? [],
34
+ candidateCount: assembled.totals.candidateCount,
35
+ omittedCount: assembled.totals.omittedCount,
36
+ ...(opts?.evidenceLists ? { evidenceLists: [...assembled.evidenceLists] } : {}),
37
+ nextQueries: assembledNextQueries(assembled),
23
38
  },
24
- status: "invalid-input",
39
+ status: opts?.status ?? "invalid-input",
25
40
  ...(opts?.message ? { message: opts.message } : {}),
26
41
  ...(opts?.displaySections ? { displaySections: opts.displaySections } : {}),
27
42
  },
@@ -22,9 +22,13 @@ import type { ResultProvenance } from "./assembly.ts";
22
22
  /** Final state for a public tool result. */
23
23
  export type ToolResultStatus = "completed" | "invalid-input" | "disambiguation" | "unavailable";
24
24
 
25
- /** Truncation state for model-facing tool content. */
25
+ /** Separate model-content and human-display bounds, with one optional continuation file. */
26
26
  export interface ToolOutputTruncationDetails {
27
+ /** Model content exceeded the PI output limit. */
27
28
  truncated: boolean;
29
+ /** The TUI omits evidence that remains in the model content. */
30
+ displayTruncated?: boolean;
31
+ /** Full returned content, not evidence beyond the requested result limit. */
28
32
  fullOutputPath?: string;
29
33
  }
30
34
 
@@ -240,9 +244,9 @@ export interface HealthDetails {
240
244
  serverInventoryAvailable: boolean;
241
245
  /** Server inventory remains workspace-wide even for a scoped diagnostic request. */
242
246
  serverInventoryScope: "workspace";
243
- /** Exact status and evidence boundary of the diagnostic observation. */
247
+ /** Diagnostic result scope and exact evidence boundary for this health call. */
244
248
  diagnosticObservation: HealthDiagnosticObservation;
245
- /** Current or most recent explicit diagnostic refresh attempt. */
249
+ /** Current or most recent refresh, including typed maintenance scope and evidence. */
246
250
  refresh: HealthRefreshState;
247
251
  structuralAvailable: boolean;
248
252
  /** Structural (tree-sitter) substrate readiness. Undefined when not evaluated. */
@@ -21,6 +21,11 @@ export const QueryParam = Type.String({
21
21
  description: "Human or code reference to resolve or search for.",
22
22
  minLength: 1,
23
23
  });
24
+ export const FindQueryParam = Type.String({
25
+ description:
26
+ "AST mode: name or import module-specifier substring. Semantic mode: workspace-symbol query.",
27
+ minLength: 1,
28
+ });
24
29
  const LineParam = Type.Integer({ description: "1-based line.", minimum: 1 });
25
30
  const CharacterParam = Type.Integer({
26
31
  description: "1-based UTF-16 column.",
@@ -1,5 +1,6 @@
1
1
  import type { AgentToolUpdateCallback } from "@earendil-works/pi-coding-agent";
2
2
  import type { WorkspaceCodeIntelligenceSession } from "../session/session.ts";
3
+ import type { GraphDetails } from "../tool/code_graph/details.ts";
3
4
  import type {
4
5
  ContextDetails,
5
6
  HealthDetails,
@@ -59,6 +60,7 @@ export interface CodeIntelResult {
59
60
  | (CodeIntelResultDetails & { type: "context"; data: ContextDetails })
60
61
  | (CodeIntelResultDetails & { type: "inspect"; data: InspectDetails })
61
62
  | (CodeIntelResultDetails & { type: "search"; data: SearchDetails })
63
+ | (CodeIntelResultDetails & { type: "graph"; data: GraphDetails })
62
64
  | (CodeIntelResultDetails & { type: "resolve"; data: ResolveDetails })
63
65
  | (CodeIntelResultDetails & { type: "health"; data: HealthDetails });
64
66
  }
@@ -16,6 +16,8 @@ export type {
16
16
 
17
17
  // ── Detail types ────────────────────────────────────────────────────────
18
18
 
19
+ export type { GraphDetails, GraphSectionDetails } from "../tool/code_graph/details.ts";
20
+
19
21
  export type {
20
22
  AnchoredResolutionMetadata,
21
23
  AnchoredResolutionSource,