@mrclrchtr/supi-code-intelligence 2.0.5 → 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
@@ -0,0 +1,430 @@
1
+ /**
2
+ * Target workflow — session-owned deep module.
3
+ *
4
+ * Turns public target input (targetId, anchored coordinates, symbol
5
+ * query, file reference) into an immutable **resolved target** before
6
+ * graph, impact, orientation, or refactor analysis begins.
7
+ *
8
+ * One policy-driven method. Small interface, deep implementation.
9
+ * Two adapters (production capability adapter + test adapter) justify
10
+ * the seam.
11
+ *
12
+ * @mrclrchtr/supi-code-intelligence — internal, not exported via api.ts
13
+ */
14
+
15
+ import { existsSync } from "node:fs";
16
+ import { normalizePath } from "../analysis/search/ripgrep.ts";
17
+ import { resolveAnchoredSymbolTarget } from "../analysis/target/anchored.ts";
18
+ import { resolveFileTargetGroup } from "../analysis/target/file.ts";
19
+ import { resolveSymbolTarget } from "../analysis/target/symbol.ts";
20
+ import type { ResolvedTargetData, TargetOutcome } from "../analysis/target/types.ts";
21
+ import type { CapabilityAdapter } from "./capability-adapter.ts";
22
+ import type {
23
+ AnchorKind,
24
+ TargetLookupResult,
25
+ TargetRegistrationInput,
26
+ TargetRegistrationOutput,
27
+ TargetStoreEntry,
28
+ } from "./target-store.ts";
29
+
30
+ // ── Input ─────────────────────────────────────────────────────────────
31
+
32
+ /** Public target input shape — mirrors the union of code_* tool params. */
33
+ export interface TargetWorkflowInput {
34
+ /** Resolved target handle from code_resolve. Wins over everything else. */
35
+ targetId?: string;
36
+ /** File path (absolute or workspace-relative). */
37
+ file?: string;
38
+ /** 1-based line for anchored resolution. */
39
+ line?: number;
40
+ /** 1-based UTF-16 column for anchored resolution. */
41
+ character?: number;
42
+ /** Symbol name for workspace symbol query resolution. */
43
+ symbol?: string;
44
+ /** Preferred symbol kind for disambiguation. */
45
+ kind?: string;
46
+ /** Only exported symbols. */
47
+ exportedOnly?: boolean;
48
+ /** Maximum candidates for disambiguation. */
49
+ maxResults?: number;
50
+ }
51
+
52
+ // ── Policy ────────────────────────────────────────────────────────────
53
+
54
+ /**
55
+ * Target workflow policy — what the caller allows or requires.
56
+ *
57
+ * Each tool passes the policy that matches its public contract.
58
+ */
59
+ export interface TargetWorkflowPolicy {
60
+ /**
61
+ * Whether file-level targets (no line/character) are allowed.
62
+ * Disallowed → file-only input returns `invalid-input`.
63
+ */
64
+ fileLevelAllowed: boolean;
65
+
66
+ /**
67
+ * Whether the resolved target must carry a name anchor.
68
+ * Callee lookups and rename require this; references do not.
69
+ */
70
+ nameAnchorRequired: boolean;
71
+
72
+ /**
73
+ * Whether to wait for semantic (LSP) readiness before symbol-
74
+ * query or anchored resolution. Set false for tool paths that
75
+ * only need targetId expansion or file-level resolution.
76
+ */
77
+ waitForSemantic: boolean;
78
+ }
79
+
80
+ // ── Outcome ───────────────────────────────────────────────────────────
81
+
82
+ /** Typed outcome of the target workflow — no markdown, no mutated params. */
83
+ export type TargetWorkflowOutcome =
84
+ | {
85
+ kind: "resolved";
86
+ /** The stored target entry with handles. */
87
+ entry: TargetStoreEntry;
88
+ /**
89
+ * Display notes for the caller, e.g. "targetId took precedence
90
+ * over scope" or "resolved via declaration anchor snap."
91
+ */
92
+ notes: string[];
93
+ }
94
+ | {
95
+ kind: "disambiguation";
96
+ candidates: Array<{
97
+ targetId: string;
98
+ name: string;
99
+ kind: string | null;
100
+ container: string | null;
101
+ file: string;
102
+ line: number;
103
+ character: number;
104
+ rank: number;
105
+ anchorKind: AnchorKind;
106
+ }>;
107
+ omittedCount: number;
108
+ }
109
+ | {
110
+ kind: "invalid-input";
111
+ message: string;
112
+ }
113
+ | {
114
+ kind: "unavailable";
115
+ reason: string;
116
+ }
117
+ | {
118
+ /** No target input was provided. Caller decides fallback. */
119
+ kind: "no-target";
120
+ };
121
+
122
+ // ── Dependencies ──────────────────────────────────────────────────────
123
+
124
+ export interface TargetWorkflowDeps {
125
+ /** Workspace root. */
126
+ cwd: string;
127
+ /** Injected capability adapter (production or test). */
128
+ capability: CapabilityAdapter;
129
+ /** Session-scoped target store lookup. */
130
+ lookupTargetId: (targetId: string) => TargetLookupResult;
131
+ /** Session-scoped target registration. */
132
+ registerTarget: (input: TargetRegistrationInput) => TargetRegistrationOutput;
133
+ }
134
+
135
+ // ── Entry point ───────────────────────────────────────────────────────
136
+
137
+ /**
138
+ * Execute the target workflow: resolve target input into an immutable
139
+ * resolved target (or a typed failure outcome).
140
+ *
141
+ * Order of precedence:
142
+ * 1. targetId (store lookup + freshness check)
143
+ * 2. anchored coordinates (file + line + character → provider-backed)
144
+ * 3. symbol query (workspace symbol → provider-backed)
145
+ * 4. file-only (when policy.fileLevelAllowed)
146
+ */
147
+ export async function resolveTargetWorkflow(
148
+ input: TargetWorkflowInput,
149
+ policy: TargetWorkflowPolicy,
150
+ deps: TargetWorkflowDeps,
151
+ ): Promise<TargetWorkflowOutcome> {
152
+ // ── 1. targetId lookup ──────────────────────────────────────────────
153
+ if (input.targetId) {
154
+ const result = deps.lookupTargetId(input.targetId);
155
+ if (result.kind === "unavailable") {
156
+ return { kind: "invalid-input", message: result.reason };
157
+ }
158
+ const entry = result.entry;
159
+
160
+ // Anchor policy: name-anchor-required tools must refuse declaration anchors
161
+ if (policy.nameAnchorRequired && entry.anchorKind === "declaration") {
162
+ return {
163
+ kind: "invalid-input",
164
+ message:
165
+ "The target resolved to a declaration anchor, not a name anchor. " +
166
+ "Re-resolve via `code_resolve` when the LSP has indexed the file, " +
167
+ "or pass `file` + `line` + `character` anchored on the identifier directly.",
168
+ };
169
+ }
170
+
171
+ const notes = inferTargetIdNotes(input);
172
+ return { kind: "resolved", entry, notes };
173
+ }
174
+
175
+ // ── 2. Anchored coordinates ────────────────────────────────────────
176
+ if (input.file && input.line != null && input.character != null) {
177
+ return resolveAnchoredWorkflow(input, policy, deps);
178
+ }
179
+
180
+ // ── 3. Symbol query ────────────────────────────────────────────────
181
+ if (input.symbol) {
182
+ return resolveSymbolWorkflow(input, policy, deps);
183
+ }
184
+
185
+ // ── 4. File-only ───────────────────────────────────────────────────
186
+ if (input.file) {
187
+ if (!policy.fileLevelAllowed) {
188
+ return {
189
+ kind: "invalid-input",
190
+ message:
191
+ "File-level target resolution is not allowed for this tool. " +
192
+ "Provide anchored coordinates (`file`, `line`, `character`) or a `symbol`.",
193
+ };
194
+ }
195
+ return resolveFileOnlyWorkflow(input, deps);
196
+ }
197
+
198
+ // ── 5. Nothing provided ────────────────────────────────────────────
199
+ return { kind: "no-target" };
200
+ }
201
+
202
+ // ── Sub-routines ──────────────────────────────────────────────────────
203
+
204
+ async function resolveAnchoredWorkflow(
205
+ input: TargetWorkflowInput,
206
+ policy: TargetWorkflowPolicy,
207
+ deps: TargetWorkflowDeps,
208
+ ): Promise<TargetWorkflowOutcome> {
209
+ // biome-ignore lint/style/noNonNullAssertion: file is guaranteed non-null by caller guard branch
210
+ const file = normalizePath(input.file!, deps.cwd);
211
+ if (!existsSync(file)) {
212
+ return { kind: "invalid-input", message: `File not found: \`${input.file}\`` };
213
+ }
214
+
215
+ // Readiness gate
216
+ if (policy.waitForSemantic) {
217
+ const readiness = await deps.capability.ensureSemanticReadiness(deps.cwd, {
218
+ kind: "file",
219
+ file,
220
+ });
221
+ if (readiness.kind === "timeout") {
222
+ return {
223
+ kind: "unavailable",
224
+ reason: "LSP readiness timed out. Retry shortly or check `code_health`.",
225
+ };
226
+ }
227
+ if (readiness.kind === "unavailable") {
228
+ return { kind: "unavailable", reason: readiness.reason };
229
+ }
230
+ }
231
+
232
+ const provider = deps.capability.getProvider(deps.cwd);
233
+ // biome-ignore lint/style/noNonNullAssertion: line/character non-null by caller guard
234
+ const outcome = await resolveAnchoredSymbolTarget(file, input.line!, input.character!, provider);
235
+
236
+ return toWorkflowOutcome(outcome, policy, deps);
237
+ }
238
+
239
+ async function resolveSymbolWorkflow(
240
+ input: TargetWorkflowInput,
241
+ policy: TargetWorkflowPolicy,
242
+ deps: TargetWorkflowDeps,
243
+ ): Promise<TargetWorkflowOutcome> {
244
+ const semantic = deps.capability.getSemanticProvider(deps.cwd);
245
+ if (!semantic) {
246
+ // Try to wait
247
+ if (policy.waitForSemantic) {
248
+ const readiness = await deps.capability.ensureSemanticReadiness(deps.cwd, {
249
+ kind: "workspace",
250
+ });
251
+ if (readiness.kind !== "ready") {
252
+ return {
253
+ kind: "unavailable",
254
+ reason:
255
+ "Symbol query requires an active LSP. Enable LSP and retry, or use anchored coordinates.",
256
+ };
257
+ }
258
+ } else {
259
+ return {
260
+ kind: "unavailable",
261
+ reason:
262
+ "Symbol query requires an active LSP. Enable LSP and retry, or use anchored coordinates.",
263
+ };
264
+ }
265
+ }
266
+
267
+ // Re-read provider after potential wait
268
+ const provider = deps.capability.getSemanticProvider(deps.cwd);
269
+ if (!provider) {
270
+ return {
271
+ kind: "unavailable",
272
+ reason: "Symbol query requires an active LSP. Enable LSP and retry.",
273
+ };
274
+ }
275
+ // biome-ignore lint/style/noNonNullAssertion: symbol non-null by caller guard
276
+ const outcome = await resolveSymbolTarget(input.symbol!, deps.cwd, provider, {
277
+ kind: input.kind,
278
+ exportedOnly: input.exportedOnly,
279
+ maxResults: input.maxResults,
280
+ });
281
+
282
+ return toWorkflowOutcome(outcome, policy, deps);
283
+ }
284
+
285
+ async function resolveFileOnlyWorkflow(
286
+ input: TargetWorkflowInput,
287
+ deps: TargetWorkflowDeps,
288
+ ): Promise<TargetWorkflowOutcome> {
289
+ // biome-ignore lint/style/noNonNullAssertion: file non-null by caller guard
290
+ const file = normalizePath(input.file!, deps.cwd);
291
+ if (!existsSync(file)) {
292
+ return { kind: "invalid-input", message: `File not found: \`${input.file}\`` };
293
+ }
294
+
295
+ const result = await resolveFileTargetGroup(file, deps.cwd, {
296
+ semantic: deps.capability.getSemanticProvider(deps.cwd) ?? undefined,
297
+ structural: deps.capability.getStructuralProvider(deps.cwd) ?? undefined,
298
+ });
299
+ if (result.kind === "error") {
300
+ return { kind: "invalid-input", message: result.message };
301
+ }
302
+
303
+ // Register the file-level target
304
+ const group = result.group;
305
+ const entry = deps.registerTarget({
306
+ file: group.file,
307
+ position: { line: 0, character: 0 },
308
+ displayLine: 1,
309
+ displayCharacter: 1,
310
+ name: group.displayName ?? null,
311
+ kind: null,
312
+ confidence: "structural",
313
+ provenance: "file-level",
314
+ anchorKind: "name",
315
+ container: null,
316
+ });
317
+
318
+ return { kind: "resolved", entry: entry.entry, notes: [] };
319
+ }
320
+
321
+ // ── Outcome conversion ────────────────────────────────────────────────
322
+
323
+ /**
324
+ * Convert a raw {@link TargetOutcome} from analysis/target/* into a
325
+ * {@link TargetWorkflowOutcome} with registered handles.
326
+ */
327
+ function toWorkflowOutcome(
328
+ outcome: TargetOutcome,
329
+ _policy: TargetWorkflowPolicy,
330
+ deps: TargetWorkflowDeps,
331
+ ): TargetWorkflowOutcome {
332
+ if (outcome.kind === "error") {
333
+ return { kind: "invalid-input", message: outcome.message };
334
+ }
335
+
336
+ if (outcome.kind === "resolved") {
337
+ const entry = registerFromTargetData(outcome.target, deps);
338
+ return {
339
+ kind: "resolved",
340
+ entry: entry.entry,
341
+ notes: buildResolutionNotes(outcome.target),
342
+ };
343
+ }
344
+
345
+ if (outcome.kind === "disambiguation") {
346
+ const candidates = outcome.candidates.map((c, idx) => {
347
+ const registered = deps.registerTarget({
348
+ file: c.file,
349
+ position: { line: c.line - 1, character: c.character - 1 },
350
+ displayLine: c.line,
351
+ displayCharacter: c.character,
352
+ name: c.name,
353
+ kind: c.kind,
354
+ confidence: "semantic",
355
+ provenance: "symbol-query",
356
+ anchorKind: c.anchorKind ?? "declaration",
357
+ container: c.container,
358
+ });
359
+ return {
360
+ targetId: registered.targetId,
361
+ name: c.name,
362
+ kind: c.kind,
363
+ container: c.container,
364
+ file: c.file,
365
+ line: c.line,
366
+ character: c.character,
367
+ rank: idx + 1,
368
+ anchorKind: c.anchorKind ?? "declaration",
369
+ };
370
+ });
371
+ return { kind: "disambiguation", candidates, omittedCount: outcome.omittedCount };
372
+ }
373
+
374
+ // Unknown outcome kind — shouldn't happen with current resolvers
375
+ return { kind: "invalid-input", message: "Unexpected resolution outcome." };
376
+ }
377
+
378
+ // ── Helpers ───────────────────────────────────────────────────────────
379
+
380
+ /** Build notes for targetId precedence over other inputs. */
381
+ function inferTargetIdNotes(input: TargetWorkflowInput): string[] {
382
+ const notes: string[] = [];
383
+ if (input.file || input.line != null || input.character != null) {
384
+ notes.push(
385
+ "_Note: `targetId` takes precedence over the supplied file/coordinates; " +
386
+ "file, line, and character were ignored._",
387
+ );
388
+ }
389
+ if (input.symbol) {
390
+ notes.push("_Note: `targetId` takes precedence over the supplied `symbol` parameter._");
391
+ }
392
+ return notes;
393
+ }
394
+
395
+ /** Build display notes for resolved anchored targets. */
396
+ function buildResolutionNotes(target: ResolvedTargetData): string[] {
397
+ const notes: string[] = [];
398
+ if (target.resolution?.snapped) {
399
+ const src = target.resolution.source;
400
+ notes.push(
401
+ `_Note: Resolved from a declaration header snap (${src}); the anchor was moved to the identifier._`,
402
+ );
403
+ }
404
+ if (target.resolution?.source === "structural-identifier") {
405
+ notes.push(
406
+ "_Note: Resolved via tree-sitter structural evidence; semantic (LSP) symbols were unavailable._",
407
+ );
408
+ }
409
+ return notes;
410
+ }
411
+
412
+ /** Register a ResolvedTargetData and return the store entry. */
413
+ function registerFromTargetData(
414
+ target: ResolvedTargetData,
415
+ deps: TargetWorkflowDeps,
416
+ ): TargetRegistrationOutput {
417
+ return deps.registerTarget({
418
+ file: target.file,
419
+ position: target.position,
420
+ displayLine: target.displayLine,
421
+ displayCharacter: target.displayCharacter,
422
+ name: target.name,
423
+ kind: target.kind,
424
+ confidence: target.confidence,
425
+ provenance: "target-workflow",
426
+ anchorKind: target.anchorKind,
427
+ container: target.container,
428
+ resolution: target.resolution,
429
+ });
430
+ }
@@ -9,7 +9,11 @@
9
9
  // - excluded patterns
10
10
  // - a "Disabled Servers" submenu that writes per-language disable config
11
11
 
12
- import { CONFIG_DIR_NAME, getSettingsListTheme } from "@earendil-works/pi-coding-agent";
12
+ import {
13
+ CONFIG_DIR_NAME,
14
+ type ExtensionAPI,
15
+ getSettingsListTheme,
16
+ } from "@earendil-works/pi-coding-agent";
13
17
  import type { SettingItem } from "@earendil-works/pi-tui";
14
18
  import { Container, Key, matchesKey, SettingsList, Text } from "@earendil-works/pi-tui";
15
19
 
@@ -75,8 +79,8 @@ function getDisabledServersFromConfig(scope: SettingsScope, cwd: string): Set<st
75
79
  return disabled;
76
80
  }
77
81
 
78
- export function registerLspSettings(): void {
79
- registerConfigSettings({
82
+ export function registerLspSettings(pi: ExtensionAPI): void {
83
+ registerConfigSettings(pi, {
80
84
  id: "lsp",
81
85
  label: "LSP",
82
86
  section: "lsp",
@@ -16,7 +16,7 @@ import { findModeKindRules } from "../infra/cross-field.ts";
16
16
  import { unavailableSearchDetails } from "../infra/error-results.ts";
17
17
  import {
18
18
  gateSemanticReadiness,
19
- resolveScopeParam,
19
+ resolveScopeSetParam,
20
20
  runPipe,
21
21
  validateParams,
22
22
  } from "../infra/pipeline.ts";
@@ -26,7 +26,7 @@ import { executePattern } from "./orchestrate.ts";
26
26
 
27
27
  export interface CodeFindToolParams {
28
28
  query: string;
29
- scope?: string;
29
+ scope?: string | string[];
30
30
  mode?: "text" | "regex" | "ast" | "semantic";
31
31
  kind?:
32
32
  | "definition"
@@ -43,6 +43,16 @@ export interface CodeFindToolParams {
43
43
  maxResults?: number;
44
44
  }
45
45
 
46
+ interface CodeFindPipelineParams extends CodeFindToolParams {
47
+ _resolvedScopePaths?: string[];
48
+ _resolvedScopeDisplay?: string | null;
49
+ }
50
+
51
+ function scopeForDetails(scope: CodeFindToolParams["scope"]): string | null {
52
+ if (Array.isArray(scope)) return scope.join(", ");
53
+ return scope ?? null;
54
+ }
55
+
46
56
  export async function executeFindTool(
47
57
  params: CodeFindToolParams,
48
58
  ctx: CodeIntelToolExecCtx,
@@ -50,7 +60,7 @@ export async function executeFindTool(
50
60
  if (!params.query || params.query.trim().length === 0) {
51
61
  return {
52
62
  content: "**Error:** `code_find` requires a non-empty `query` parameter.",
53
- details: unavailableSearchDetails(params.scope ?? null, [
63
+ details: unavailableSearchDetails(scopeForDetails(params.scope), [
54
64
  "Provide a non-empty `query` parameter",
55
65
  ]),
56
66
  };
@@ -61,20 +71,22 @@ export async function executeFindTool(
61
71
  const mode = params.mode ?? "text";
62
72
  const needsSemanticReadiness = mode === "semantic";
63
73
 
74
+ const pipelineParams = params as CodeFindPipelineParams;
75
+
64
76
  return runPipe(
65
- params,
77
+ pipelineParams,
66
78
  ctx,
67
79
  [
68
80
  validateParams(findModeKindRules(), (msg) => ({
69
81
  content: msg,
70
- details: unavailableSearchDetails(params.scope ?? null, [
82
+ details: unavailableSearchDetails(scopeForDetails(params.scope), [
71
83
  'Use `mode: "ast"` with `kind`, or remove `kind` for text/regex/semantic search',
72
84
  ]),
73
85
  })),
74
- resolveScopeParam((reason) => ({
86
+ resolveScopeSetParam((reason) => ({
75
87
  content: `**Error:** ${reason}`,
76
- details: unavailableSearchDetails(params.scope ?? null, [
77
- "Verify the `scope` path exists and is within the workspace",
88
+ details: unavailableSearchDetails(scopeForDetails(params.scope), [
89
+ "Verify each `scope` path exists and is within the workspace",
78
90
  ]),
79
91
  })),
80
92
  ...(needsSemanticReadiness
@@ -82,7 +94,7 @@ export async function executeFindTool(
82
94
  gateSemanticReadiness("code_find", {
83
95
  onTimeout: () => ({
84
96
  content: renderSemanticReadinessTimeout("code_find", 15_000),
85
- details: unavailableSearchDetails(params.scope ?? null, [
97
+ details: unavailableSearchDetails(scopeForDetails(params.scope), [
86
98
  "Retry shortly or check `code_health`",
87
99
  ]),
88
100
  }),
@@ -104,7 +116,8 @@ export async function executeFindTool(
104
116
  return executePattern(
105
117
  {
106
118
  pattern: p.query,
107
- path: p.scope ?? c.cwd,
119
+ paths: p._resolvedScopePaths ?? [c.cwd],
120
+ scopeLabel: p._resolvedScopeDisplay ?? null,
108
121
  regex: mode === "regex",
109
122
  kind: p.kind,
110
123
  mode,