@mrclrchtr/supi-code-intelligence 2.0.6 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (121) hide show
  1. package/README.md +71 -1
  2. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/README.md +29 -21
  3. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +2 -1
  4. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +88 -69
  5. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config.ts +22 -8
  6. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/prompt-surface.ts +357 -0
  7. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/prompt-surface.ts +4 -0
  8. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-command.ts +1 -1
  9. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +51 -29
  10. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +64 -18
  11. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings.ts +11 -5
  12. package/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +10 -0
  13. package/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
  14. package/node_modules/@mrclrchtr/supi-core/README.md +29 -21
  15. package/node_modules/@mrclrchtr/supi-core/package.json +2 -1
  16. package/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +88 -69
  17. package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +22 -8
  18. package/node_modules/@mrclrchtr/supi-core/src/config/prompt-surface.ts +357 -0
  19. package/node_modules/@mrclrchtr/supi-core/src/prompt-surface.ts +4 -0
  20. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-command.ts +1 -1
  21. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +51 -29
  22. package/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +64 -18
  23. package/node_modules/@mrclrchtr/supi-core/src/settings.ts +11 -5
  24. package/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +10 -0
  25. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/README.md +29 -21
  26. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +2 -1
  27. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +88 -69
  28. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config.ts +22 -8
  29. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/prompt-surface.ts +357 -0
  30. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/prompt-surface.ts +4 -0
  31. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-command.ts +1 -1
  32. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +51 -29
  33. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +64 -18
  34. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings.ts +11 -5
  35. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +10 -0
  36. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
  37. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +29 -21
  38. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +2 -1
  39. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +88 -69
  40. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +22 -8
  41. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/prompt-surface.ts +357 -0
  42. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/prompt-surface.ts +4 -0
  43. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-command.ts +1 -1
  44. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +51 -29
  45. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +64 -18
  46. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings.ts +11 -5
  47. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +10 -0
  48. package/node_modules/@mrclrchtr/supi-lsp/package.json +3 -3
  49. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/README.md +29 -21
  50. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/package.json +2 -1
  51. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +88 -69
  52. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/config.ts +22 -8
  53. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/config/prompt-surface.ts +357 -0
  54. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/prompt-surface.ts +4 -0
  55. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-command.ts +1 -1
  56. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +51 -29
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +64 -18
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/settings.ts +11 -5
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +10 -0
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-code-runtime/package.json +2 -2
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/README.md +29 -21
  62. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +2 -1
  63. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config-settings.ts +88 -69
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +22 -8
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/prompt-surface.ts +357 -0
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/prompt-surface.ts +4 -0
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-command.ts +1 -1
  68. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-registry.ts +51 -29
  69. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings/settings-ui.ts +64 -18
  70. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/settings.ts +11 -5
  71. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +10 -0
  72. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +3 -3
  73. package/package.json +5 -5
  74. package/src/analysis/evidence.ts +13 -0
  75. package/src/analysis/search/pattern.ts +24 -13
  76. package/src/analysis/search/ripgrep.ts +92 -7
  77. package/src/extension.ts +1 -1
  78. package/src/session/capability-adapter.ts +218 -0
  79. package/src/session/session.ts +39 -1
  80. package/src/session/target-workflow.ts +430 -0
  81. package/src/substrate/lsp/settings.ts +7 -3
  82. package/src/tool/find/execute.ts +23 -10
  83. package/src/tool/find/orchestrate.ts +67 -54
  84. package/src/tool/find/semantic-search.ts +30 -16
  85. package/src/tool/graph/collect-relation.ts +2 -1
  86. package/src/tool/graph/execute.ts +40 -12
  87. package/src/tool/graph/markdown-base.ts +5 -37
  88. package/src/tool/graph/orchestrate.ts +9 -35
  89. package/src/tool/graph/tui.ts +2 -0
  90. package/src/tool/guidance.ts +1 -1
  91. package/src/tool/health/markdown.ts +12 -70
  92. package/src/tool/health/orchestrate.ts +5 -13
  93. package/src/tool/impact/analysis.ts +0 -27
  94. package/src/tool/impact/change-set-impact.ts +3 -2
  95. package/src/tool/impact/execute.ts +27 -0
  96. package/src/tool/impact/file-level-impact.ts +3 -2
  97. package/src/tool/impact/result.ts +2 -21
  98. package/src/tool/impact/single-impact.ts +3 -2
  99. package/src/tool/infra/error-results.ts +18 -196
  100. package/src/tool/infra/pipeline.ts +24 -1
  101. package/src/tool/inspect/markdown.ts +4 -41
  102. package/src/tool/inspect/orchestrate.ts +27 -28
  103. package/src/tool/orientation/orchestrate.ts +5 -8
  104. package/src/tool/orientation/precise-target.ts +65 -69
  105. package/src/tool/refactor-apply/orchestrate.ts +2 -7
  106. package/src/tool/refactor-plan/orchestrate.ts +31 -39
  107. package/src/tool/resolve/markdown.ts +5 -6
  108. package/src/tool/resolve/orchestrate.ts +7 -103
  109. package/src/tool/result/errors.ts +199 -0
  110. package/src/tool/result/find.ts +29 -0
  111. package/src/tool/result/graph.ts +93 -0
  112. package/src/tool/result/health.ts +94 -0
  113. package/src/tool/result/impact.ts +50 -0
  114. package/src/tool/result/inspect.ts +60 -0
  115. package/src/tool/result/orientation.ts +30 -0
  116. package/src/tool/result/refactor.ts +35 -0
  117. package/src/tool/result/resolve.ts +114 -0
  118. package/src/tool/result/types.ts +188 -0
  119. package/src/tool/schemas.ts +22 -1
  120. package/src/types/details.ts +17 -185
  121. package/src/ui/tui/common.ts +45 -1
@@ -9,14 +9,10 @@ import {
9
9
  type StructuredPatternKind,
10
10
  } from "../../analysis/search/pattern.ts";
11
11
  import type { RgMatch } from "../../analysis/search/ripgrep.ts";
12
- import {
13
- normalizePath,
14
- runRipgrep,
15
- runRipgrepDetailed,
16
- toDisplayPath,
17
- } from "../../analysis/search/ripgrep.ts";
18
- import type { CodeIntelResult, SearchDetails } from "../../types/index.ts";
12
+ import { runRipgrep, runRipgrepDetailed, toDisplayPath } from "../../analysis/search/ripgrep.ts";
13
+ import type { CodeIntelResult } from "../../types/index.ts";
19
14
  import type { CodeQueryParams } from "../params.ts";
15
+ import { assembleFindResult } from "../result/find.ts";
20
16
  import {
21
17
  renderPatternResults,
22
18
  renderPatternSummary,
@@ -28,7 +24,10 @@ import { executeSemanticSearch } from "./semantic-search.ts";
28
24
 
29
25
  export interface PatternInput {
30
26
  pattern: string;
31
- path?: string;
27
+ /** One or more resolved search roots. */
28
+ paths?: string[];
29
+ /** Human-readable requested scope label for markdown/details. */
30
+ scopeLabel?: string | null;
32
31
  regex?: boolean;
33
32
  kind?: string;
34
33
  mode?: "text" | "regex" | "ast" | "semantic";
@@ -45,7 +44,6 @@ export interface PatternDeps {
45
44
  }
46
45
 
47
46
  /** Execute the pattern search use-case. */
48
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: mode dispatch branches are linear, not nested
49
47
  export async function executePattern(
50
48
  input: PatternInput,
51
49
  deps: PatternDeps,
@@ -64,16 +62,15 @@ export async function executePattern(
64
62
 
65
63
  const maxResults = input.maxResults ?? 8;
66
64
  const contextLines = input.contextLines ?? 1;
67
- const scopePath = input.path ? normalizePath(input.path, deps.cwd) : deps.cwd;
68
- const relScope = input.path ?? ".";
65
+ const scope = getPatternScope(input, deps.cwd);
69
66
 
70
67
  if (isStructuredPatternKind(input.kind)) {
71
68
  return executeStructuredSearch(
72
69
  input,
73
70
  input.kind,
74
- scopePath,
71
+ scope.paths,
75
72
  deps.cwd,
76
- relScope,
73
+ scope.label,
77
74
  maxResults,
78
75
  deps.provider,
79
76
  );
@@ -82,14 +79,14 @@ export async function executePattern(
82
79
  const matches = input.regex
83
80
  ? await getRegexMatches({
84
81
  pattern: input.pattern,
85
- scopePath,
82
+ scopePath: scope.paths,
86
83
  cwd: deps.cwd,
87
84
  maxResults,
88
85
  contextLines,
89
86
  summary: input.summary,
90
87
  signal: deps.signal,
91
88
  })
92
- : await runRipgrep(input.pattern, scopePath, deps.cwd, {
89
+ : await runRipgrep(input.pattern, scope.paths, deps.cwd, {
93
90
  contextLines,
94
91
  literal: true,
95
92
  filterLowSignal: true,
@@ -97,18 +94,22 @@ export async function executePattern(
97
94
  });
98
95
 
99
96
  if (typeof matches === "string") {
100
- const errorDetails: SearchDetails = {
101
- confidence: "unavailable",
102
- scope: input.path ?? null,
103
- candidateCount: 0,
104
- omittedCount: 0,
105
- nextQueries: ["Fix the regex pattern and retry"],
97
+ return {
98
+ content: matches,
99
+ details: {
100
+ type: "search",
101
+ data: assembleFindResult({
102
+ confidence: "unavailable",
103
+ scope: getDetailScope(input),
104
+ candidateCount: 0,
105
+ nextQueries: ["Fix the regex pattern and retry"],
106
+ }),
107
+ },
106
108
  };
107
- return { content: matches, details: { type: "search", data: errorDetails } };
108
109
  }
109
110
 
110
111
  if (matches.length === 0) {
111
- return formatEmptyResult(input, relScope);
112
+ return formatEmptyResult(input, scope.label);
112
113
  }
113
114
 
114
115
  const displayMatches = matches.map((m) => ({
@@ -118,21 +119,21 @@ export async function executePattern(
118
119
 
119
120
  const rendered = input.summary
120
121
  ? {
121
- content: renderPatternSummary(input.pattern, relScope, displayMatches, maxResults),
122
+ content: renderPatternSummary(input.pattern, scope.label, displayMatches, maxResults),
122
123
  evidenceList: undefined,
123
124
  }
124
- : renderPatternResults(input.pattern, relScope, displayMatches, maxResults);
125
+ : renderPatternResults(input.pattern, scope.label, displayMatches, maxResults);
125
126
 
126
- const details: SearchDetails = {
127
+ const details = assembleFindResult({
127
128
  confidence: "heuristic",
128
- scope: input.path ?? null,
129
+ scope: getDetailScope(input),
129
130
  candidateCount: matches.length,
130
131
  omittedCount: rendered.evidenceList?.omittedCount ?? 0,
131
132
  evidenceLists: rendered.evidenceList ? [rendered.evidenceList] : [],
132
133
  nextQueries: input.regex
133
134
  ? ["Set `regex: false` for literal matching"]
134
135
  : ["Set `regex: true` for regex matching"],
135
- };
136
+ });
136
137
  return { content: rendered.content, details: { type: "search" as const, data: details } };
137
138
  }
138
139
 
@@ -142,7 +143,7 @@ export async function executePattern(
142
143
  async function executeStructuredSearch(
143
144
  input: PatternInput,
144
145
  kind: StructuredPatternKind,
145
- scopePath: string,
146
+ scopePaths: readonly string[],
146
147
  cwd: string,
147
148
  relScope: string,
148
149
  maxResults: number,
@@ -157,21 +158,25 @@ async function executeStructuredSearch(
157
158
 
158
159
  const structured = await getStructuredPatternMatches(
159
160
  { ...input, pattern: input.pattern, kind },
160
- scopePath,
161
+ scopePaths,
161
162
  cwd,
162
163
  relScope,
163
164
  provider,
164
165
  );
165
166
 
166
167
  if (typeof structured === "string") {
167
- const errorDetails: SearchDetails = {
168
- confidence: "unavailable",
169
- scope: input.path ?? null,
170
- candidateCount: 0,
171
- omittedCount: 0,
172
- nextQueries: ["Fix the regex pattern and retry"],
168
+ return {
169
+ content: structured,
170
+ details: {
171
+ type: "search",
172
+ data: assembleFindResult({
173
+ confidence: "unavailable",
174
+ scope: getDetailScope(input),
175
+ candidateCount: 0,
176
+ nextQueries: ["Fix the regex pattern and retry"],
177
+ }),
178
+ },
173
179
  };
174
- return { content: structured, details: { type: "search", data: errorDetails } };
175
180
  }
176
181
 
177
182
  if (!structured || structured.matches.length === 0) {
@@ -186,16 +191,16 @@ async function executeStructuredSearch(
186
191
  content,
187
192
  details: {
188
193
  type: "search",
189
- data: {
194
+ data: assembleFindResult({
190
195
  confidence: "structural",
191
- scope: input.path ?? null,
196
+ scope: getDetailScope(input),
192
197
  candidateCount: 0,
193
198
  omittedCount: structured?.omittedCount ?? 0,
194
199
  nextQueries: [
195
200
  "Try a broader `pattern`, or omit `kind` for plain text search",
196
201
  "Narrow `path` if the structured scan was partial",
197
202
  ],
198
- },
203
+ }),
199
204
  },
200
205
  };
201
206
  }
@@ -206,9 +211,9 @@ async function executeStructuredSearch(
206
211
  content: rendered.content,
207
212
  details: {
208
213
  type: "search",
209
- data: {
214
+ data: assembleFindResult({
210
215
  confidence: "structural",
211
- scope: input.path ?? null,
216
+ scope: getDetailScope(input),
212
217
  candidateCount: structured.matches.length,
213
218
  omittedCount: structured.omittedCount + (rendered.evidenceList.omittedCount ?? 0),
214
219
  evidenceLists: [rendered.evidenceList],
@@ -222,13 +227,24 @@ async function executeStructuredSearch(
222
227
  "Omit `kind` for plain text matches",
223
228
  "Widen `scope` or raise `maxResults` for broader coverage",
224
229
  ],
225
- },
230
+ }),
226
231
  },
227
232
  };
228
233
  }
229
234
 
230
235
  // ── Helpers ──────────────────────────────────────────────────────────
231
236
 
237
+ function getPatternScope(input: PatternInput, cwd: string): { paths: string[]; label: string } {
238
+ if (input.paths && input.paths.length > 0) {
239
+ return { paths: input.paths, label: input.scopeLabel ?? input.paths.join(", ") };
240
+ }
241
+ return { paths: [cwd], label: "." };
242
+ }
243
+
244
+ function getDetailScope(input: PatternInput): string | null {
245
+ return input.scopeLabel ?? null;
246
+ }
247
+
232
248
  const REGEX_HINT_CHARS = /[|.*+?^${}()[\]\\]/;
233
249
 
234
250
  function hasRegexChars(pattern: string): boolean {
@@ -236,15 +252,14 @@ function hasRegexChars(pattern: string): boolean {
236
252
  }
237
253
 
238
254
  function formatEmptyResult(input: PatternInput, relScope: string): CodeIntelResult {
239
- const emptyDetails: SearchDetails = {
255
+ const emptyDetails = assembleFindResult({
240
256
  confidence: "heuristic",
241
- scope: input.path ?? null,
257
+ scope: getDetailScope(input),
242
258
  candidateCount: 0,
243
- omittedCount: 0,
244
259
  nextQueries: input.regex
245
260
  ? ["Set `regex: false` for literal matching"]
246
261
  : ["Set `regex: true` for regex matching"],
247
- };
262
+ });
248
263
  const hint = input.regex
249
264
  ? ""
250
265
  : hasRegexChars(input.pattern)
@@ -258,7 +273,7 @@ function formatEmptyResult(input: PatternInput, relScope: string): CodeIntelResu
258
273
 
259
274
  async function getRegexMatches(options: {
260
275
  pattern: string;
261
- scopePath: string;
276
+ scopePath: string | readonly string[];
262
277
  cwd: string;
263
278
  maxResults: number;
264
279
  contextLines: number;
@@ -278,12 +293,9 @@ async function getRegexMatches(options: {
278
293
  return result.matches;
279
294
  }
280
295
 
281
- // ── Backward-compatible wrapper for test migrations ────────────────
296
+ // ── Test helper wrapper ─────────────────────────────────────────────
282
297
 
283
- /**
284
- * Backward-compatible wrapper for tests that call the old (params, cwd) signature.
285
- * Prefer {@link executePattern} with the typed PatternInput/PatternDeps interface.
286
- */
298
+ /** Prefer {@link executePattern} with the typed PatternInput/PatternDeps interface. */
287
299
  export async function executePatternAction(
288
300
  params: CodeQueryParams,
289
301
  cwd: string,
@@ -292,7 +304,8 @@ export async function executePatternAction(
292
304
  return executePattern(
293
305
  {
294
306
  pattern: params.pattern ?? "",
295
- path: params.path,
307
+ paths: params.path ? [params.path] : undefined,
308
+ scopeLabel: params.path ?? null,
296
309
  regex: params.regex,
297
310
  kind: params.kind,
298
311
  maxResults: params.maxResults,
@@ -10,8 +10,8 @@ import type { CodeSymbol } from "@mrclrchtr/supi-code-runtime/api";
10
10
  import { isWithinOrEqual } from "@mrclrchtr/supi-core/project";
11
11
  import { createEvidenceList, renderEvidenceListDisclosure } from "../../analysis/evidence.ts";
12
12
  import type { CodeProvider } from "../../analysis/provider.ts";
13
- import { normalizePath } from "../../analysis/search/ripgrep.ts";
14
- import type { CodeIntelResult, SearchDetails } from "../../types/index.ts";
13
+ import type { CodeIntelResult } from "../../types/index.ts";
14
+ import { assembleFindResult } from "../result/find.ts";
15
15
  import type { PatternInput } from "./orchestrate.ts";
16
16
 
17
17
  export async function executeSemanticSearch(
@@ -32,35 +32,49 @@ export async function executeSemanticSearch(
32
32
  );
33
33
  }
34
34
 
35
- const scopePath = input.path ? normalizePath(input.path, cwd) : cwd;
36
- const scopedSymbols = filterSymbolsByScope(symbols, scopePath);
35
+ const scope = getSemanticScope(input, cwd);
36
+ const scopedSymbols = filterSymbolsByScopes(symbols, scope.paths);
37
37
  if (scopedSymbols.length === 0) {
38
- return renderSemanticEmptyResult(input);
38
+ return renderSemanticEmptyResult(input, scope.label);
39
39
  }
40
40
 
41
41
  return renderSemanticResults(input, scopedSymbols, cwd);
42
42
  }
43
43
 
44
- function filterSymbolsByScope<T extends { file: string }>(symbols: T[], scopePath: string): T[] {
45
- return symbols.filter((symbol) => isWithinOrEqual(scopePath, symbol.file));
44
+ function filterSymbolsByScopes<T extends { file: string }>(
45
+ symbols: T[],
46
+ scopePaths: string[],
47
+ ): T[] {
48
+ return symbols.filter((symbol) =>
49
+ scopePaths.some((scopePath) => isWithinOrEqual(scopePath, symbol.file)),
50
+ );
46
51
  }
47
52
 
48
- function renderSemanticEmptyResult(input: PatternInput): CodeIntelResult {
49
- const relPath = input.path ?? ".";
53
+ function getSemanticScope(input: PatternInput, cwd: string): { paths: string[]; label: string } {
54
+ if (input.paths && input.paths.length > 0) {
55
+ return { paths: input.paths, label: input.scopeLabel ?? input.paths.join(", ") };
56
+ }
57
+ return { paths: [cwd], label: "." };
58
+ }
59
+
60
+ function getDetailScope(input: PatternInput): string | null {
61
+ return input.scopeLabel ?? null;
62
+ }
63
+
64
+ function renderSemanticEmptyResult(input: PatternInput, relPath: string): CodeIntelResult {
50
65
  return {
51
66
  content: `**Semantic search** — \`${input.pattern}\`\n\nNo semantic results found in \`${relPath}\`.`,
52
67
  details: {
53
68
  type: "search",
54
- data: {
69
+ data: assembleFindResult({
55
70
  confidence: "semantic",
56
- scope: input.path ?? null,
71
+ scope: getDetailScope(input),
57
72
  candidateCount: 0,
58
- omittedCount: 0,
59
73
  nextQueries: [
60
74
  'Use `mode: "text"` for literal text search',
61
75
  'Use `mode: "ast"` with `kind` for structural filtering',
62
76
  ],
63
- } satisfies SearchDetails,
77
+ }),
64
78
  },
65
79
  };
66
80
  }
@@ -98,9 +112,9 @@ function renderSemanticResults(
98
112
  content: lines.join("\n"),
99
113
  details: {
100
114
  type: "search",
101
- data: {
115
+ data: assembleFindResult({
102
116
  confidence: "semantic",
103
- scope: input.path ?? null,
117
+ scope: getDetailScope(input),
104
118
  candidateCount: symbols.length,
105
119
  omittedCount: evidence.metadata.omittedCount ?? 0,
106
120
  evidenceLists: [evidence.metadata],
@@ -108,7 +122,7 @@ function renderSemanticResults(
108
122
  'Use `mode: "text"` for literal text search',
109
123
  'Use `mode: "ast"` with `kind` for structural filtering',
110
124
  ],
111
- } satisfies SearchDetails,
125
+ }),
112
126
  },
113
127
  };
114
128
  }
@@ -19,12 +19,13 @@ import {
19
19
  renderRankedTestLabelsForMarkdown,
20
20
  } from "../../analysis/tests/test-discovery.ts";
21
21
  import type { AnchorKind } from "../../session/target-store.ts";
22
+ import type { GraphSection } from "../result/graph.ts";
22
23
  import { collectCallees } from "./callees.ts";
23
24
  import { renderCallsResult } from "./calls-md.ts";
24
25
  import type { GraphRelation } from "./execute.ts";
25
26
  import { collectImplementations } from "./implementations.ts";
26
27
  import { renderImplementationsResult } from "./implementations-md.ts";
27
- import { type GraphSection, renderExportsResult, renderImportsResult } from "./markdown-base.ts";
28
+ import { renderExportsResult, renderImportsResult } from "./markdown-base.ts";
28
29
  import { collectCallers } from "./references.ts";
29
30
  import { renderReferencesResult } from "./references-md.ts";
30
31
  import type { CallEntry } from "./types.ts";
@@ -5,21 +5,15 @@
5
5
  * and code_implementations. Resolves one target, then dispatches to the
6
6
  * appropriate analysis service per requested relation.
7
7
  *
8
- * Thin executor: validates params via pipeline, then delegates to the
9
- * graph use-case for target resolution, data collection, and rendering.
8
+ * Thin executor: resolves target via session target workflow (deep seam),
9
+ * validates params via pipeline, then delegates to the graph use-case.
10
10
  */
11
11
 
12
12
  import type { AnchorKind } from "../../session/target-store.ts";
13
13
  import type { CodeIntelResult, CodeIntelToolExecCtx } from "../../types/index.ts";
14
14
  import { composeRules, focusedToolRules, requireAtLeastOne } from "../infra/cross-field.ts";
15
15
  import { searchErrorResult } from "../infra/error-results.ts";
16
- import {
17
- expandTargetId,
18
- gateCapability,
19
- resolveScopeParam,
20
- runPipe,
21
- validateParams,
22
- } from "../infra/pipeline.ts";
16
+ import { gateCapability, resolveScopeParam, runPipe, validateParams } from "../infra/pipeline.ts";
23
17
  import { emitToolProgress } from "../infra/progress.ts";
24
18
  import { executeGraph } from "./orchestrate.ts";
25
19
 
@@ -43,23 +37,57 @@ export interface CodeGraphToolParams {
43
37
  relations?: GraphRelation[];
44
38
  calleeDepth?: "direct" | "deep";
45
39
  maxResults?: number;
46
- /** Set by pipeline expandTargetId stage. */
40
+ /** Set by target-workflow resolution. */
47
41
  _expandedName?: string | null;
48
- /** Set by pipeline expandTargetId stage. */
42
+ /** Set by target-workflow resolution. */
49
43
  _expandedAnchorKind?: string | null;
50
44
  }
51
45
 
46
+ /** Graph target-workflow policy: no file-level targets, name anchor optional. */
47
+ const GRAPH_TARGET_POLICY = {
48
+ fileLevelAllowed: false,
49
+ nameAnchorRequired: false,
50
+ waitForSemantic: false,
51
+ } as const;
52
+
52
53
  export async function executeGraphTool(
53
54
  params: CodeGraphToolParams,
54
55
  ctx: CodeIntelToolExecCtx,
55
56
  ): Promise<CodeIntelResult> {
56
57
  emitToolProgress(ctx.onUpdate, "code_graph: resolving target...");
57
58
 
59
+ // ── Deep seam: resolve targetId through the session target workflow ──
60
+ // Replaces expandTargetId pipeline stage. Anchored coordinates and
61
+ // symbol queries still use the orchestrate's own resolution.
62
+ if (params.targetId) {
63
+ const outcome = await ctx.session.resolveTarget(
64
+ { targetId: params.targetId },
65
+ GRAPH_TARGET_POLICY,
66
+ );
67
+
68
+ if (outcome.kind === "resolved") {
69
+ params.file = outcome.entry.file;
70
+ params.line = outcome.entry.displayLine;
71
+ params.character = outcome.entry.displayCharacter;
72
+ params._expandedName = outcome.entry.name;
73
+ params._expandedAnchorKind = outcome.entry.anchorKind;
74
+ // targetId wins — drop conflicting scope to avoid validation error
75
+ params.scope = undefined;
76
+ } else if (outcome.kind !== "no-target") {
77
+ const msg =
78
+ outcome.kind === "invalid-input"
79
+ ? outcome.message
80
+ : outcome.kind === "unavailable"
81
+ ? outcome.reason
82
+ : "Target resolution failed. Verify the targetId is valid.";
83
+ return searchErrorResult(`**Error:** ${msg}`);
84
+ }
85
+ }
86
+
58
87
  return runPipe(
59
88
  params,
60
89
  ctx,
61
90
  [
62
- expandTargetId((msg) => searchErrorResult(msg)),
63
91
  resolveScopeParam((reason) => searchErrorResult(`**Error:** ${reason}`)),
64
92
  validateParams(
65
93
  composeRules(focusedToolRules(), requireAtLeastOne("file", "symbol", "scope")),
@@ -5,10 +5,10 @@ import {
5
5
  type EvidenceListMetadata,
6
6
  renderEvidenceListDisclosure,
7
7
  } from "../../analysis/evidence.ts";
8
- import { type ReadNextItem, renderReadNextSection } from "../../analysis/read-next.ts";
8
+ import { renderReadNextSection } from "../../analysis/read-next.ts";
9
9
  import type { ReferenceCollection } from "../../analysis/references/semantic-refs.ts";
10
10
  import { formatReferenceList } from "../../analysis/references/semantic-refs.ts";
11
- import type { TestSurfaceDetails } from "../../analysis/tests/test-discovery.ts";
11
+ import type { GraphResultAssembly, GraphSection } from "../result/graph.ts";
12
12
 
13
13
  // ── Callers ──────────────────────────────────────────────────────────
14
14
 
@@ -152,46 +152,14 @@ export function renderExportsResult(
152
152
 
153
153
  // ── Combined graph ───────────────────────────────────────────────────
154
154
 
155
- /** Relation families accepted by code_graph. */
156
- export type GraphRelationKind =
157
- | "all"
158
- | "references"
159
- | "callees"
160
- | "imports"
161
- | "exports"
162
- | "implements"
163
- | "tests";
164
-
165
- /** A section in the combined graph output. */
166
- export type GraphSection =
167
- | {
168
- kind: "ok";
169
- rel: GraphRelationKind;
170
- count: number;
171
- content: string;
172
- evidenceLists?: EvidenceListMetadata[];
173
- tests?: TestSurfaceDetails;
174
- readNext?: ReadNextItem[];
175
- }
176
- | { kind: "unavailable"; rel: GraphRelationKind; message: string; tests?: TestSurfaceDetails }
177
- | {
178
- kind: "not-implemented";
179
- rel: GraphRelationKind;
180
- message: string;
181
- tests?: TestSurfaceDetails;
182
- };
183
-
184
155
  /**
185
- * Render a combined graph result from multiple relation sections.
156
+ * Render an assembled graph result.
186
157
  *
187
158
  * Produces a unified markdown output with a summary header, per-section
188
159
  * pre-rendered content, and a footer with follow-up hints.
189
160
  */
190
- export function renderGraphResult(
191
- displayName: string,
192
- sections: GraphSection[],
193
- resolvedFile: string,
194
- ): string {
161
+ export function renderGraphResult(assembly: GraphResultAssembly): string {
162
+ const { displayName, sections, resolvedDisplayFile: resolvedFile } = assembly;
195
163
  const lines: string[] = [];
196
164
 
197
165
  // Header
@@ -17,6 +17,7 @@ import type { CodeIntelResult } from "../../types/index.ts";
17
17
  import { searchErrorResult } from "../infra/error-results.ts";
18
18
  import { emitToolProgress } from "../infra/progress.ts";
19
19
  import { renderSemanticReadinessTimeout } from "../infra/readiness-message.ts";
20
+ import { assembleGraphResult } from "../result/graph.ts";
20
21
  import { type CollectRelationOptions, collectRelation } from "./collect-relation.ts";
21
22
  import type { GraphRelation } from "./execute.ts";
22
23
  import { renderGraphResult } from "./markdown-base.ts";
@@ -231,46 +232,19 @@ function buildGraphResult(
231
232
  resolvedDisplayFile: string,
232
233
  scope: string | undefined,
233
234
  ): CodeIntelResult {
234
- const content = renderGraphResult(displayName, sections, resolvedDisplayFile);
235
-
236
- const hasStructural = sections.some(
237
- (s) =>
238
- s.kind === "ok" &&
239
- (s.rel === "callees" ||
240
- s.rel === "imports" ||
241
- s.rel === "exports" ||
242
- (s.rel === "tests" && s.count > 0)),
243
- );
244
- const hasSemantic = sections.some(
245
- (s) => s.kind === "ok" && (s.rel === "references" || s.rel === "implements"),
246
- );
247
- const confidence = hasSemantic ? "semantic" : hasStructural ? "structural" : "unavailable";
248
-
249
- const tests = sections.find((section) => section.rel === "tests")?.tests;
250
- const evidenceLists = sections.flatMap((section) =>
251
- section.kind === "ok" ? (section.evidenceLists ?? []) : [],
252
- );
253
- const omittedCount = evidenceLists.reduce(
254
- (sum, evidenceList) => sum + (evidenceList.omittedCount ?? 0),
255
- 0,
256
- );
235
+ const assembly = assembleGraphResult({
236
+ displayName,
237
+ sections,
238
+ resolvedDisplayFile,
239
+ scope,
240
+ });
241
+ const content = renderGraphResult(assembly);
257
242
 
258
243
  return {
259
244
  content,
260
245
  details: {
261
246
  type: "search",
262
- data: {
263
- confidence,
264
- scope: scope ?? null,
265
- candidateCount: sections.reduce((sum, s) => (s.kind === "ok" ? sum + s.count : sum), 0),
266
- omittedCount,
267
- evidenceLists,
268
- nextQueries: [
269
- "`code_orientation` on individual results for deeper orientation",
270
- "`code_impact` for impact analysis",
271
- ],
272
- tests,
273
- },
247
+ data: assembly.details,
274
248
  },
275
249
  };
276
250
  }
@@ -15,6 +15,7 @@ import {
15
15
  renderEvidenceLines,
16
16
  renderMarkdownDetail,
17
17
  renderPartial,
18
+ renderStructuredDetailBody,
18
19
  type ToolResult,
19
20
  } from "../../ui/tui/common.ts";
20
21
  import type { CodeGraphToolParams, GraphRelation } from "./execute.ts";
@@ -83,6 +84,7 @@ export function renderGraphResult(
83
84
  container.addChild(buildTestsSection(details.tests as Record<string, unknown>, theme));
84
85
  }
85
86
 
87
+ renderStructuredDetailBody(container, details ?? undefined, theme);
86
88
  renderMarkdownDetail(container, result, theme);
87
89
 
88
90
  return container;
@@ -70,7 +70,7 @@ export const CODE_INTELLIGENCE_TOOL_PROMPT_SURFACES: CodeIntelligenceToolPromptS
70
70
  },
71
71
  code_find: {
72
72
  description:
73
- 'Unified ranked code search with strict mode dispatch: default/`mode: "text"` is literal ripgrep, `mode: "regex"` is ripgrep regex, `mode: "semantic"` is LSP workspace-symbol search, and `mode: "ast"` is tree-sitter structured search. `mode: "ast"` requires `kind` and currently supports `definition`, `import`, `export`, `call`, `type`, `interface`, `class`, `method`, `enum`, and `test`. `mode: "text"`, `mode: "regex"`, and `mode: "semantic"` do not accept `kind`. `code_find` with `mode: "semantic"` does not silently fall back to text search, and unsupported mode/kind combinations fail. AST `call` mode matches call-site identifiers by name, not by symbol identity; use `code_graph` references for identity-aware callers of a resolved target. Output is truncated to 2000 lines / 50KB.',
73
+ 'Unified ranked code search with strict mode dispatch: default/`mode: "text"` is literal ripgrep, `mode: "regex"` is ripgrep regex, `mode: "semantic"` is LSP workspace-symbol search, and `mode: "ast"` is tree-sitter structured search. `mode: "ast"` requires `kind` and currently supports `definition`, `import`, `export`, `call`, `type`, `interface`, `class`, `method`, `enum`, and `test`. `mode: "text"`, `mode: "regex"`, and `mode: "semantic"` do not accept `kind`. `scope` accepts one workspace-relative path or an array of paths; delimiter-separated strings are split only when no exact path exists. `code_find` with `mode: "semantic"` does not silently fall back to text search, and unsupported mode/kind combinations fail. AST `call` mode matches call-site identifiers by name, not by symbol identity; use `code_graph` references for identity-aware callers of a resolved target. Output is truncated to 2000 lines / 50KB.',
74
74
  promptSnippet: "code_find — unified ranked code search",
75
75
  promptGuidelines: [
76
76
  "Use code_find as the sole code search tool for literal text, regex, semantic workspace-symbol, or AST structured search.",