@mrclrchtr/supi-code-intelligence 6.3.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 (136) 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 +16 -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/diagnostics/workspace-sentinels.ts +4 -32
  47. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/workspace-sources.ts +108 -0
  48. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +10 -4
  49. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-process-crash-report.ts +50 -1
  50. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +11 -7
  51. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +770 -56
  52. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostic-surface.ts +3 -1
  53. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-diagnostics.ts +37 -4
  54. package/node_modules/@mrclrchtr/supi-lsp/src/session/runtime-registry.ts +48 -12
  55. package/node_modules/@mrclrchtr/supi-lsp/src/session/workspace-lsp-runtime.ts +50 -4
  56. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +1 -1
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/src/types.ts +8 -1
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +8 -0
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/footer-registry.ts +10 -3
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +2 -0
  62. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/llm.ts +141 -16
  63. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/src/provider/tree-sitter-provider.ts +2 -0
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callee-display-name.ts +165 -0
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/callees.ts +67 -9
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/src/types.ts +2 -0
  68. package/node_modules/yaml/browser/dist/compose/resolve-flow-scalar.js +19 -18
  69. package/node_modules/yaml/browser/dist/nodes/Alias.js +25 -23
  70. package/node_modules/yaml/dist/compose/resolve-flow-scalar.js +19 -18
  71. package/node_modules/yaml/dist/nodes/Alias.js +25 -23
  72. package/node_modules/yaml/package.json +1 -1
  73. package/package.json +5 -5
  74. package/src/analysis/health/diagnostics.ts +1 -2
  75. package/src/analysis/health/file-scope.ts +4 -1
  76. package/src/analysis/health/server-status.ts +1 -1
  77. package/src/analysis/relations/callees.ts +17 -8
  78. package/src/analysis/relations/implementations.ts +1 -2
  79. package/src/analysis/relations/references.ts +8 -4
  80. package/src/analysis/relations/types.ts +6 -30
  81. package/src/analysis/target/anchored.ts +9 -1
  82. package/src/analysis/target/symbol.ts +29 -11
  83. package/src/analysis/target/types.ts +14 -6
  84. package/src/api.ts +2 -0
  85. package/src/session/graph/collect.ts +18 -3
  86. package/src/session/health-refresh.ts +27 -11
  87. package/src/session/health-types.ts +21 -4
  88. package/src/session/health-workflow.ts +28 -22
  89. package/src/session/orientation-types.ts +5 -6
  90. package/src/session/orientation-workflow.ts +10 -2
  91. package/src/session/session.ts +12 -5
  92. package/src/session/target-workflow.ts +9 -6
  93. package/src/substrate/lsp/maintenance.ts +53 -47
  94. package/src/substrate/lsp/source-tracking.ts +252 -0
  95. package/src/tool/code_find/guidance.ts +3 -3
  96. package/src/tool/code_find/spec.ts +5 -4
  97. package/src/tool/code_find/tui.ts +7 -1
  98. package/src/tool/code_graph/details.ts +32 -0
  99. package/src/tool/code_graph/display.ts +54 -0
  100. package/src/tool/code_graph/format.ts +60 -0
  101. package/src/tool/code_graph/guidance.ts +2 -2
  102. package/src/tool/code_graph/markdown.ts +76 -198
  103. package/src/tool/code_graph/read-next.ts +65 -0
  104. package/src/tool/code_graph/result.ts +118 -83
  105. package/src/tool/code_graph/tui.ts +216 -54
  106. package/src/tool/code_health/guidance.ts +3 -5
  107. package/src/tool/code_health/markdown.ts +90 -57
  108. package/src/tool/code_health/refresh-outcome.ts +51 -0
  109. package/src/tool/code_health/refresh-status.ts +130 -43
  110. package/src/tool/code_health/scope.ts +78 -0
  111. package/src/tool/code_health/spec.ts +1 -1
  112. package/src/tool/code_health/tui.ts +29 -38
  113. package/src/tool/code_inspect/guidance.ts +5 -3
  114. package/src/tool/code_inspect/tui.ts +7 -1
  115. package/src/tool/code_orientation/guidance.ts +3 -4
  116. package/src/tool/code_orientation/result.ts +31 -8
  117. package/src/tool/code_orientation/tui.ts +26 -5
  118. package/src/tool/code_refactor_apply/guidance.ts +1 -1
  119. package/src/tool/code_refactor_apply/tui.ts +7 -3
  120. package/src/tool/code_refactor_plan/guidance.ts +3 -5
  121. package/src/tool/code_refactor_plan/tui.ts +7 -1
  122. package/src/tool/code_resolve/guidance.ts +3 -5
  123. package/src/tool/code_resolve/result.ts +13 -10
  124. package/src/tool/code_resolve/tui.ts +18 -1
  125. package/src/tool/guidance.ts +11 -9
  126. package/src/tool/infra/truncate.ts +39 -13
  127. package/src/tool/result/candidate-row.ts +27 -0
  128. package/src/tool/result/errors.ts +21 -6
  129. package/src/tool/result/types.ts +7 -3
  130. package/src/tool/schemas.ts +5 -0
  131. package/src/types/execution.ts +2 -0
  132. package/src/types/index.ts +2 -0
  133. package/src/ui/tui/candidate-legacy-body.ts +69 -0
  134. package/src/ui/tui/candidate-selection.ts +366 -0
  135. package/src/ui/tui/common.ts +26 -31
  136. package/src/ui/tui/execution-error.ts +92 -0
@@ -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 one fresh stored refactor plan by planId after safety checks; mutates its files.";
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,8 +1,6 @@
1
1
  export const toolDescription =
2
- "Preview one semantic rename, extraction, import cleanup, or dead-code deletion and return a planId without mutating files.";
2
+ "Create and store a semantic refactor preview without changing files. Unsupported operations do not fall back to text edits.";
3
3
 
4
- export const promptSnippet = "preview a precise semantic refactor";
4
+ export const promptSnippet = "preview a semantic refactor";
5
5
 
6
- export const promptGuidelines = [
7
- "Use code_refactor_plan for preview only, then call code_refactor_apply with its planId.",
8
- ];
6
+ export const promptGuidelines: string[] = [];
@@ -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 one provider-backed anchor or semantic symbol query to session handles, or enumerate a file's declarations as a bounded Target group. Anchors must identify real symbols";
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
- export const promptSnippet = "resolve a precise target or enumerate a file’s target group";
4
+ export const promptSnippet = "resolve symbols or file declarations";
5
5
 
6
- export const promptGuidelines = [
7
- "Use code_resolve when a symbol query may be ambiguous, when later calls should share a stable target handle, or when a known file’s declarations should be enumerated.",
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
  }
@@ -1,10 +1,8 @@
1
- // Model-facing prompt surfaces for the eight public code-intelligence tools.
2
- //
3
- // Ownership map (docs/pi/tool-guidance.md): each model-facing fact lives in
4
- // exactly one home — the per-tool guidance module. This aggregator assembles
5
- // the canonical surface map consumed by registration.
6
- // Parameter mechanics (formats, enum semantics, cross-field rules) live in
7
- // schemas.ts / per-tool spec.ts and are not repeated here.
1
+ /**
2
+ * Each tool owns its model-facing text. This module assembles the canonical
3
+ * surface map used by registration. Parameter mechanics stay in specs and
4
+ * shared schemas.
5
+ */
8
6
 
9
7
  import type { CodeIntelligenceToolName } from "../types/index.ts";
10
8
  import {
@@ -48,9 +46,13 @@ import {
48
46
  promptSnippet as resolveSnippet,
49
47
  } from "./code_resolve/guidance.ts";
50
48
 
49
+ /** Model-facing fields registered for one public code-intelligence tool. */
51
50
  export interface CodeIntelligenceToolPromptSurface {
51
+ /** Selection contract in the active provider tool definition. */
52
52
  description: string;
53
+ /** One-line capability phrase in the default tool list. */
53
54
  promptSnippet: string;
55
+ /** Optional active-tool routing or ordering reminders. */
54
56
  promptGuidelines: string[];
55
57
  }
56
58
 
@@ -59,7 +61,7 @@ export type CodeIntelligenceToolPromptSurfaceMap = Record<
59
61
  CodeIntelligenceToolPromptSurface
60
62
  >;
61
63
 
62
- export const CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES: CodeIntelligenceToolPromptSurfaceMap = {
64
+ export const CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES = {
63
65
  code_resolve: {
64
66
  description: resolveDescription,
65
67
  promptSnippet: resolveSnippet,
@@ -100,4 +102,4 @@ export const CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES: CodeIntelligenceToolPromptS
100
102
  promptSnippet: applySnippet,
101
103
  promptGuidelines: applyGuidelines,
102
104
  },
103
- };
105
+ } satisfies CodeIntelligenceToolPromptSurfaceMap;
@@ -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,
@@ -0,0 +1,69 @@
1
+ /** Bounded raw Markdown rendering for legacy target-selection results. */
2
+ import { getMarkdownTheme, type Theme } from "@earendil-works/pi-coding-agent";
3
+ import { type Component, Markdown, Text, truncateToWidth } from "@earendil-works/pi-tui";
4
+
5
+ /** Maximum source characters rendered by the legacy candidate body. */
6
+ const MAX_LEGACY_SOURCE_CHARACTERS = 4096;
7
+
8
+ /** Maximum physical rows rendered by the legacy candidate body. */
9
+ const MAX_LEGACY_BODY_ROWS = 40;
10
+
11
+ const LEGACY_TRUNCATION_NOTICE = `Legacy candidate body truncated for display (source limit: ${MAX_LEGACY_SOURCE_CHARACTERS} characters; body limit: ${MAX_LEGACY_BODY_ROWS} rows).`;
12
+
13
+ /** Read valid Markdown text blocks without trusting persisted content shape. */
14
+ export function readLegacyCandidateText(value: unknown): string {
15
+ if (!Array.isArray(value)) return "";
16
+ return value
17
+ .flatMap((block) => {
18
+ if (block === null || typeof block !== "object" || Array.isArray(block)) return [];
19
+ const record = block as Record<string, unknown>;
20
+ return record.type === "text" && typeof record.text === "string" ? [record.text] : [];
21
+ })
22
+ .join("\n");
23
+ }
24
+
25
+ /** Create a bounded legacy candidate body, or null when no valid text exists. */
26
+ export function createLegacyCandidateBody(markdownText: string, theme: Theme): Component | null {
27
+ if (!markdownText.trim()) return null;
28
+ return new BoundedLegacyCandidateBody(markdownText, theme);
29
+ }
30
+
31
+ class BoundedLegacyCandidateBody implements Component {
32
+ private readonly markdownText: string;
33
+ private readonly sourceTruncated: boolean;
34
+
35
+ constructor(
36
+ markdownText: string,
37
+ private readonly theme: Theme,
38
+ ) {
39
+ const sourceCharacters = Array.from(markdownText);
40
+ this.sourceTruncated = sourceCharacters.length > MAX_LEGACY_SOURCE_CHARACTERS;
41
+ this.markdownText = this.sourceTruncated
42
+ ? sourceCharacters.slice(0, MAX_LEGACY_SOURCE_CHARACTERS).join("")
43
+ : markdownText;
44
+ }
45
+
46
+ render(width: number): string[] {
47
+ const safeWidth = Math.max(1, width);
48
+ const markdownRows = new Markdown(this.markdownText, 0, 0, getMarkdownTheme())
49
+ .render(safeWidth)
50
+ .map((row) => truncateToWidth(row, safeWidth, ""));
51
+
52
+ if (!this.sourceTruncated && markdownRows.length <= MAX_LEGACY_BODY_ROWS) {
53
+ return markdownRows;
54
+ }
55
+
56
+ const noticeRows = new Text(this.theme.fg("warning", LEGACY_TRUNCATION_NOTICE), 0, 0).render(
57
+ safeWidth,
58
+ );
59
+ const markdownRowBudget = Math.max(0, MAX_LEGACY_BODY_ROWS - noticeRows.length);
60
+ return [...markdownRows.slice(0, markdownRowBudget), ...noticeRows].slice(
61
+ 0,
62
+ MAX_LEGACY_BODY_ROWS,
63
+ );
64
+ }
65
+
66
+ invalidate(): void {
67
+ // The Markdown and notice components are rebuilt for each render.
68
+ }
69
+ }