@mrclrchtr/supi-code-intelligence 1.3.0 → 1.4.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 (103) hide show
  1. package/README.md +70 -32
  2. package/node_modules/@mrclrchtr/supi-core/README.md +52 -41
  3. package/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  4. package/node_modules/@mrclrchtr/supi-core/src/api.ts +13 -13
  5. package/node_modules/@mrclrchtr/supi-core/src/{config-settings.ts → config/config-settings.ts} +2 -2
  6. package/node_modules/@mrclrchtr/{supi-lsp/node_modules/@mrclrchtr/supi-core/src → supi-core/src/context}/context-provider-registry.ts +1 -1
  7. package/node_modules/@mrclrchtr/supi-core/src/extension.ts +1 -1
  8. package/node_modules/@mrclrchtr/supi-core/src/index.ts +13 -13
  9. package/node_modules/@mrclrchtr/{supi-lsp/node_modules/@mrclrchtr/supi-core/src → supi-core/src/settings}/settings-registry.ts +1 -1
  10. package/node_modules/@mrclrchtr/supi-lsp/README.md +58 -39
  11. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/README.md +52 -41
  12. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +1 -1
  13. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +13 -13
  14. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{config-settings.ts → config/config-settings.ts} +2 -2
  15. package/node_modules/@mrclrchtr/{supi-core/src → supi-lsp/node_modules/@mrclrchtr/supi-core/src/context}/context-provider-registry.ts +1 -1
  16. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/extension.ts +1 -1
  17. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +13 -13
  18. package/node_modules/@mrclrchtr/{supi-core/src → supi-lsp/node_modules/@mrclrchtr/supi-core/src/settings}/settings-registry.ts +1 -1
  19. package/node_modules/@mrclrchtr/supi-lsp/package.json +5 -3
  20. package/node_modules/@mrclrchtr/supi-lsp/src/api.ts +16 -3
  21. package/node_modules/@mrclrchtr/supi-lsp/src/client/client-refresh.ts +1 -1
  22. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +27 -3
  23. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +61 -5
  24. package/node_modules/@mrclrchtr/supi-lsp/src/config/tsconfig-scope.ts +244 -0
  25. package/node_modules/@mrclrchtr/supi-lsp/src/{types.ts → config/types.ts} +4 -2
  26. package/node_modules/@mrclrchtr/supi-lsp/src/coordinates.ts +11 -0
  27. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-augmentation.ts +5 -5
  28. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-context.ts +115 -0
  29. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-display.ts +1 -1
  30. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostic-summary.ts +3 -2
  31. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/diagnostics.ts +1 -1
  32. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/stale-diagnostics.ts +1 -1
  33. package/node_modules/@mrclrchtr/supi-lsp/src/diagnostics/suppression-diagnostics.ts +1 -1
  34. package/node_modules/@mrclrchtr/supi-lsp/src/{workspace-sentinels.ts → diagnostics/workspace-sentinels.ts} +2 -2
  35. package/node_modules/@mrclrchtr/supi-lsp/src/format.ts +2 -23
  36. package/node_modules/@mrclrchtr/supi-lsp/src/index.ts +18 -5
  37. package/node_modules/@mrclrchtr/supi-lsp/src/lsp.ts +72 -120
  38. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-diagnostics.ts +1 -1
  39. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-helpers.ts +4 -2
  40. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +10 -7
  41. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-recovery.ts +1 -1
  42. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-workspace-symbol.ts +158 -6
  43. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager.ts +202 -43
  44. package/node_modules/@mrclrchtr/supi-lsp/src/{lsp-state.ts → session/lsp-state.ts} +22 -11
  45. package/node_modules/@mrclrchtr/supi-lsp/src/{scanner.ts → session/scanner.ts} +3 -3
  46. package/node_modules/@mrclrchtr/supi-lsp/src/{service-registry.ts → session/service-registry.ts} +104 -12
  47. package/node_modules/@mrclrchtr/supi-lsp/src/{settings-registration.ts → session/settings-registration.ts} +1 -1
  48. package/node_modules/@mrclrchtr/supi-lsp/src/session/tree-persist.ts +75 -0
  49. package/node_modules/@mrclrchtr/supi-lsp/src/summary.ts +1 -1
  50. package/node_modules/@mrclrchtr/supi-lsp/src/tool/guidance.ts +138 -0
  51. package/node_modules/@mrclrchtr/supi-lsp/src/tool/names.ts +19 -0
  52. package/node_modules/@mrclrchtr/supi-lsp/src/{overrides.ts → tool/overrides.ts} +55 -24
  53. package/node_modules/@mrclrchtr/supi-lsp/src/tool/register-tools.ts +224 -0
  54. package/node_modules/@mrclrchtr/supi-lsp/src/tool/service-actions.ts +258 -0
  55. package/node_modules/@mrclrchtr/supi-lsp/src/{ui.ts → ui/ui.ts} +4 -4
  56. package/node_modules/@mrclrchtr/supi-lsp/src/utils.ts +11 -0
  57. package/node_modules/@mrclrchtr/supi-tree-sitter/README.md +46 -39
  58. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +1 -1
  59. package/node_modules/@mrclrchtr/supi-tree-sitter/src/api.ts +1 -1
  60. package/node_modules/@mrclrchtr/supi-tree-sitter/src/index.ts +1 -1
  61. package/node_modules/@mrclrchtr/supi-tree-sitter/src/{runtime.ts → session/runtime.ts} +3 -3
  62. package/node_modules/@mrclrchtr/supi-tree-sitter/src/{session.ts → session/session.ts} +4 -4
  63. package/node_modules/@mrclrchtr/supi-tree-sitter/src/{callees.ts → tool/callees.ts} +3 -3
  64. package/node_modules/@mrclrchtr/supi-tree-sitter/src/{exports.ts → tool/exports.ts} +4 -4
  65. package/node_modules/@mrclrchtr/supi-tree-sitter/src/{formatting.ts → tool/formatting.ts} +1 -1
  66. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tool/guidance.ts +22 -0
  67. package/node_modules/@mrclrchtr/supi-tree-sitter/src/{imports.ts → tool/imports.ts} +4 -4
  68. package/node_modules/@mrclrchtr/supi-tree-sitter/src/{node-at.ts → tool/node-at.ts} +3 -3
  69. package/node_modules/@mrclrchtr/supi-tree-sitter/src/{outline.ts → tool/outline.ts} +3 -3
  70. package/node_modules/@mrclrchtr/supi-tree-sitter/src/tree-sitter.ts +6 -29
  71. package/package.json +8 -5
  72. package/src/actions/affected-action.ts +4 -4
  73. package/src/actions/brief-action.ts +12 -13
  74. package/src/actions/callees-action.ts +14 -10
  75. package/src/actions/callers-action.ts +4 -4
  76. package/src/actions/implementations-action.ts +4 -4
  77. package/src/code-intelligence.ts +1 -1
  78. package/src/pattern-structured.ts +20 -22
  79. package/src/providers/semantic-provider.ts +34 -0
  80. package/src/providers/structural-provider.ts +14 -0
  81. package/src/target-resolution.ts +26 -35
  82. package/src/tool/guidance.ts +21 -0
  83. package/node_modules/@mrclrchtr/supi-lsp/src/guidance.ts +0 -163
  84. package/node_modules/@mrclrchtr/supi-lsp/src/search-fallback.ts +0 -98
  85. package/node_modules/@mrclrchtr/supi-lsp/src/tool-actions.ts +0 -430
  86. package/node_modules/@mrclrchtr/supi-lsp/src/tree-persist.ts +0 -48
  87. package/node_modules/@mrclrchtr/supi-lsp/src/tsconfig-scope.ts +0 -156
  88. package/src/guidance.ts +0 -42
  89. /package/node_modules/@mrclrchtr/supi-core/src/{config.ts → config/config.ts} +0 -0
  90. /package/node_modules/@mrclrchtr/supi-core/src/{context-messages.ts → context/context-messages.ts} +0 -0
  91. /package/node_modules/@mrclrchtr/supi-core/src/{context-tag.ts → context/context-tag.ts} +0 -0
  92. /package/node_modules/@mrclrchtr/supi-core/src/{settings-command.ts → settings/settings-command.ts} +0 -0
  93. /package/node_modules/@mrclrchtr/supi-core/src/{settings-ui.ts → settings/settings-ui.ts} +0 -0
  94. /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{config.ts → config/config.ts} +0 -0
  95. /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{context-messages.ts → context/context-messages.ts} +0 -0
  96. /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{context-tag.ts → context/context-tag.ts} +0 -0
  97. /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{settings-command.ts → settings/settings-command.ts} +0 -0
  98. /package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/{settings-ui.ts → settings/settings-ui.ts} +0 -0
  99. /package/node_modules/@mrclrchtr/supi-lsp/src/{capabilities.ts → config/capabilities.ts} +0 -0
  100. /package/node_modules/@mrclrchtr/supi-lsp/src/{config.ts → config/config.ts} +0 -0
  101. /package/node_modules/@mrclrchtr/supi-lsp/src/{defaults.json → config/defaults.json} +0 -0
  102. /package/node_modules/@mrclrchtr/supi-lsp/src/{renderer.ts → ui/renderer.ts} +0 -0
  103. /package/node_modules/@mrclrchtr/supi-tree-sitter/src/{structure.ts → tool/structure.ts} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrclrchtr/supi-code-intelligence",
3
- "version": "1.3.0",
3
+ "version": "1.4.0",
4
4
  "description": "SuPi Code Intelligence extension — architecture briefs, caller/callee analysis, impact assessment, and pattern search for pi",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -19,9 +19,9 @@
19
19
  "src/**/*.ts"
20
20
  ],
21
21
  "dependencies": {
22
- "@mrclrchtr/supi-tree-sitter": "1.3.0",
23
- "@mrclrchtr/supi-lsp": "1.3.0",
24
- "@mrclrchtr/supi-core": "1.3.0"
22
+ "@mrclrchtr/supi-core": "1.4.0",
23
+ "@mrclrchtr/supi-lsp": "1.4.0",
24
+ "@mrclrchtr/supi-tree-sitter": "1.4.0"
25
25
  },
26
26
  "bundledDependencies": [
27
27
  "@mrclrchtr/supi-core",
@@ -46,7 +46,10 @@
46
46
  },
47
47
  "pi": {
48
48
  "extensions": [
49
- "./src/extension.ts"
49
+ "./src/extension.ts",
50
+ "node_modules/@mrclrchtr/supi-lsp/src/extension.ts",
51
+ "node_modules/@mrclrchtr/supi-core/src/extension.ts",
52
+ "node_modules/@mrclrchtr/supi-tree-sitter/src/extension.ts"
50
53
  ]
51
54
  },
52
55
  "main": "src/api.ts",
@@ -2,12 +2,12 @@
2
2
  // biome-ignore-all lint/nursery/noExcessiveLinesPerFile: file-level and single-target affected flows share helpers to keep the blast-radius logic in one place
3
3
 
4
4
  import * as path from "node:path";
5
- import { getSessionLspService } from "@mrclrchtr/supi-lsp/api";
6
5
  import { buildArchitectureModel, findModuleForPath, getDependents } from "../architecture.ts";
7
6
  import {
8
7
  appendPrioritySignalsSection,
9
8
  summarizePrioritySignalsForFiles,
10
9
  } from "../prioritization-signals.ts";
10
+ import { getSemanticService } from "../providers/semantic-provider.ts";
11
11
  import { resolveTarget } from "../resolve-target.ts";
12
12
  import {
13
13
  escapeRegex,
@@ -188,12 +188,12 @@ async function gatherReferences(
188
188
  params: ActionParams,
189
189
  cwd: string,
190
190
  ): Promise<{ refs: GatheredRef[]; confidence: ConfidenceMode; externalCount: number }> {
191
- const lspState = getSessionLspService(cwd);
191
+ const lsp = await getSemanticService(cwd, { waitForReady: true });
192
192
  const refs: GatheredRef[] = [];
193
193
  let externalCount = 0;
194
194
 
195
- if (lspState.kind === "ready") {
196
- const lspRefs = await lspState.service.references(target.file, target.position);
195
+ if (lsp) {
196
+ const lspRefs = await lsp.references(target.file, target.position);
197
197
  if (lspRefs && lspRefs.length > 0) {
198
198
  const filtered = filterOutDeclaration(lspRefs, target.file, target.position);
199
199
  for (const ref of filtered) {
@@ -2,9 +2,10 @@
2
2
 
3
3
  import * as fs from "node:fs";
4
4
  import * as path from "node:path";
5
- import { createTreeSitterSession } from "@mrclrchtr/supi-tree-sitter/api";
5
+ import type { TreeSitterSession } from "@mrclrchtr/supi-tree-sitter/api";
6
6
  import { buildArchitectureModel, findModuleForPath } from "../architecture.ts";
7
7
  import { generateFocusedBrief, generateProjectBrief } from "../brief.ts";
8
+ import { withStructuralSession } from "../providers/structural-provider.ts";
8
9
  import { normalizePath } from "../search-helpers.ts";
9
10
  import type { ActionParams } from "../tool-actions.ts";
10
11
  import type { CodeIntelResult } from "../types.ts";
@@ -108,23 +109,21 @@ interface TreeSitterContextInput {
108
109
 
109
110
  async function addTreeSitterContext(input: TreeSitterContextInput): Promise<void> {
110
111
  const { lines, relPath, line1, char1, cwd } = input;
111
- let tsSession: ReturnType<typeof createTreeSitterSession> | null = null;
112
112
  try {
113
- tsSession = createTreeSitterSession(cwd);
114
- await addNodeContext(lines, tsSession, relPath, { line: line1, char: char1 });
115
- await addOutlineContext(lines, tsSession, relPath, line1);
116
- await addImportsContext(lines, tsSession, relPath);
117
- await addExportsContext(lines, tsSession, relPath);
113
+ await withStructuralSession(cwd, async (tsSession) => {
114
+ await addNodeContext(lines, tsSession, relPath, { line: line1, char: char1 });
115
+ await addOutlineContext(lines, tsSession, relPath, line1);
116
+ await addImportsContext(lines, tsSession, relPath);
117
+ await addExportsContext(lines, tsSession, relPath);
118
+ });
118
119
  } catch {
119
120
  // Tree-sitter not available
120
- } finally {
121
- tsSession?.dispose();
122
121
  }
123
122
  }
124
123
 
125
124
  async function addNodeContext(
126
125
  lines: string[],
127
- ts: ReturnType<typeof createTreeSitterSession>,
126
+ ts: TreeSitterSession,
128
127
  relPath: string,
129
128
  pos: { line: number; char: number },
130
129
  ): Promise<void> {
@@ -144,7 +143,7 @@ async function addNodeContext(
144
143
 
145
144
  async function addOutlineContext(
146
145
  lines: string[],
147
- ts: ReturnType<typeof createTreeSitterSession>,
146
+ ts: TreeSitterSession,
148
147
  relPath: string,
149
148
  line1: number,
150
149
  ): Promise<void> {
@@ -183,7 +182,7 @@ function getOutlinePrefix(kind: string): string {
183
182
 
184
183
  async function addImportsContext(
185
184
  lines: string[],
186
- ts: ReturnType<typeof createTreeSitterSession>,
185
+ ts: TreeSitterSession,
187
186
  relPath: string,
188
187
  ): Promise<void> {
189
188
  const result = await ts.imports(relPath);
@@ -201,7 +200,7 @@ async function addImportsContext(
201
200
 
202
201
  async function addExportsContext(
203
202
  lines: string[],
204
- ts: ReturnType<typeof createTreeSitterSession>,
203
+ ts: TreeSitterSession,
205
204
  relPath: string,
206
205
  ): Promise<void> {
207
206
  const result = await ts.exports(relPath);
@@ -3,7 +3,7 @@
3
3
  // extraction to @mrclrchtr/supi-tree-sitter/api.
4
4
 
5
5
  import * as path from "node:path";
6
- import { createTreeSitterSession } from "@mrclrchtr/supi-tree-sitter/api";
6
+ import { withStructuralSession } from "../providers/structural-provider.ts";
7
7
  import { resolveTarget } from "../resolve-target.ts";
8
8
  import { isResolvedTargetGroup } from "../semantic-action-helpers.ts";
9
9
  import type { ActionParams } from "../tool-actions.ts";
@@ -47,11 +47,11 @@ export async function executeCalleesAction(
47
47
  }
48
48
 
49
49
  const relPath = path.relative(cwd, target.file);
50
- let tsSession: ReturnType<typeof createTreeSitterSession> | null = null;
51
50
 
52
51
  try {
53
- tsSession = createTreeSitterSession(cwd);
54
- const result = await tsSession.calleesAt(relPath, target.displayLine, target.displayCharacter);
52
+ const result = await withStructuralSession(cwd, (tsSession) =>
53
+ tsSession.calleesAt(relPath, target.displayLine, target.displayCharacter),
54
+ );
55
55
 
56
56
  if (result.kind !== "success") {
57
57
  return {
@@ -63,7 +63,9 @@ export async function executeCalleesAction(
63
63
  scope: null,
64
64
  candidateCount: 0,
65
65
  omittedCount: 0,
66
- nextQueries: ["Use `lsp` for type-aware analysis on this file"],
66
+ nextQueries: [
67
+ 'Use `lsp_lookup` with `kind: "hover"` for type-aware analysis on this file',
68
+ ],
67
69
  },
68
70
  },
69
71
  };
@@ -95,7 +97,9 @@ export async function executeCalleesAction(
95
97
  scope: null,
96
98
  candidateCount: callees.length,
97
99
  omittedCount: Math.max(0, callees.length - (params.maxResults ?? 8)),
98
- nextQueries: ["Use `lsp` for precise type information on callees"],
100
+ nextQueries: [
101
+ 'Use `lsp_lookup` with `kind: "hover"` for precise type information on callees',
102
+ ],
99
103
  };
100
104
  return { content, details: { type: "search" as const, data: details } };
101
105
  } catch {
@@ -108,12 +112,12 @@ export async function executeCalleesAction(
108
112
  scope: null,
109
113
  candidateCount: 0,
110
114
  omittedCount: 0,
111
- nextQueries: ["Use `lsp` for type-aware analysis on this file"],
115
+ nextQueries: [
116
+ 'Use `lsp_lookup` with `kind: "hover"` for type-aware analysis on this file',
117
+ ],
112
118
  },
113
119
  },
114
120
  };
115
- } finally {
116
- tsSession?.dispose();
117
121
  }
118
122
  }
119
123
 
@@ -140,7 +144,7 @@ function formatCallees(
140
144
  }
141
145
  lines.push("");
142
146
  lines.push(
143
- "_Structural analysis — may include unresolved or qualified names. Use `lsp` for precise type information._",
147
+ '_Structural analysis — may include unresolved or qualified names. Use `lsp_lookup` with `kind: "hover"` for precise type information._',
144
148
  );
145
149
  lines.push("");
146
150
  return lines.join("\n");
@@ -1,7 +1,7 @@
1
1
  // Callers action — find call sites for a symbol.
2
2
 
3
3
  import * as path from "node:path";
4
- import { getSessionLspService } from "@mrclrchtr/supi-lsp/api";
4
+ import { getSemanticService } from "../providers/semantic-provider.ts";
5
5
  import { resolveTarget } from "../resolve-target.ts";
6
6
  import {
7
7
  escapeRegex,
@@ -169,10 +169,10 @@ async function collectCallerRefs(
169
169
  params: ActionParams,
170
170
  cwd: string,
171
171
  ): Promise<CallerCollection> {
172
- const lspState = getSessionLspService(cwd);
172
+ const lsp = await getSemanticService(cwd, { waitForReady: true });
173
173
 
174
- if (lspState.kind === "ready") {
175
- const refs = await lspState.service.references(target.file, target.position);
174
+ if (lsp) {
175
+ const refs = await lsp.references(target.file, target.position);
176
176
  if (refs && refs.length > 0) {
177
177
  const filtered = filterOutDeclaration(refs, target.file, target.position);
178
178
  const projectRefs: CallerRef[] = [];
@@ -1,7 +1,7 @@
1
1
  // Implementations action — find concrete implementations via LSP or heuristic.
2
2
 
3
3
  import * as path from "node:path";
4
- import { getSessionLspService } from "@mrclrchtr/supi-lsp/api";
4
+ import { getSemanticService } from "../providers/semantic-provider.ts";
5
5
  import { resolveTarget } from "../resolve-target.ts";
6
6
  import {
7
7
  escapeRegex,
@@ -52,11 +52,11 @@ export async function executeImplementationsAction(
52
52
  };
53
53
  }
54
54
 
55
- const lspState = getSessionLspService(cwd);
55
+ const lsp = await getSemanticService(cwd, { waitForReady: true });
56
56
  const relPath = path.relative(cwd, target.file);
57
57
 
58
- if (lspState.kind === "ready") {
59
- const impls = await lspState.service.implementation(target.file, target.position);
58
+ if (lsp) {
59
+ const impls = await lsp.implementation(target.file, target.position);
60
60
  if (impls) {
61
61
  const locations = Array.isArray(impls) ? impls : [impls];
62
62
  if (locations.length > 0) {
@@ -6,7 +6,7 @@ import type { BeforeAgentStartEventResult, ExtensionAPI } from "@earendil-works/
6
6
  import { Type } from "typebox";
7
7
  import { buildArchitectureModel } from "./architecture.ts";
8
8
  import { generateOverview } from "./brief.ts";
9
- import { promptGuidelines, promptSnippet, toolDescription } from "./guidance.ts";
9
+ import { promptGuidelines, promptSnippet, toolDescription } from "./tool/guidance.ts";
10
10
  import { type CodeIntelAction, executeAction } from "./tool-actions.ts";
11
11
 
12
12
  const OVERVIEW_CUSTOM_TYPE = "code-intelligence-overview";
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { createTreeSitterSession } from "@mrclrchtr/supi-tree-sitter/api";
3
+ import { withStructuralSession } from "./providers/structural-provider.ts";
4
4
  import type { ActionParams } from "./tool-actions.ts";
5
5
 
6
6
  export const STRUCTURED_PATTERN_FILE_CAP = 200;
@@ -42,31 +42,29 @@ export async function getStructuredPatternMatches(
42
42
  return matcher;
43
43
  }
44
44
 
45
- let tsSession: ReturnType<typeof createTreeSitterSession> | null = null;
46
45
  try {
47
- tsSession = createTreeSitterSession(cwd);
48
- const matches: StructuredMatch[] = [];
49
- let timedOut = collected.timedOut;
50
-
51
- for (const [index, file] of collected.files.entries()) {
52
- if (Date.now() > deadline) {
53
- collected.omittedCount += collected.files.length - index;
54
- timedOut = true;
55
- break;
46
+ return await withStructuralSession(cwd, async (tsSession) => {
47
+ const matches: StructuredMatch[] = [];
48
+ let timedOut = collected.timedOut;
49
+
50
+ for (const [index, file] of collected.files.entries()) {
51
+ if (Date.now() > deadline) {
52
+ collected.omittedCount += collected.files.length - index;
53
+ timedOut = true;
54
+ break;
55
+ }
56
+ const relFile = path.relative(cwd, file);
57
+ await collectMatchesForFile(matches, tsSession, relFile, params.kind, matcher);
56
58
  }
57
- const relFile = path.relative(cwd, file);
58
- await collectMatchesForFile(matches, tsSession, relFile, params.kind, matcher);
59
- }
60
59
 
61
- return {
62
- matches,
63
- omittedCount: timedOut ? Math.max(1, collected.omittedCount) : collected.omittedCount,
64
- partialReason: timedOut ? "timeout" : collected.omittedCount > 0 ? "file-cap" : null,
65
- };
60
+ return {
61
+ matches,
62
+ omittedCount: timedOut ? Math.max(1, collected.omittedCount) : collected.omittedCount,
63
+ partialReason: timedOut ? "timeout" : collected.omittedCount > 0 ? "file-cap" : null,
64
+ };
65
+ });
66
66
  } catch {
67
67
  return `No structured ${params.kind} search data available in \`${relScope}\`. Try omitting \`kind\` for plain text search.`;
68
- } finally {
69
- tsSession?.dispose();
70
68
  }
71
69
  }
72
70
 
@@ -74,7 +72,7 @@ export async function getStructuredPatternMatches(
74
72
  // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: kind-specific tree-sitter matching is clearest as one helper
75
73
  async function collectMatchesForFile(
76
74
  matches: StructuredMatch[],
77
- tsSession: ReturnType<typeof createTreeSitterSession>,
75
+ tsSession: import("@mrclrchtr/supi-tree-sitter/api").TreeSitterSession,
78
76
  relFile: string,
79
77
  kind: StructuredPatternKind,
80
78
  matcher: (value: string) => boolean,
@@ -0,0 +1,34 @@
1
+ import {
2
+ getSessionLspService,
3
+ type SessionLspService,
4
+ type SessionLspServiceState,
5
+ waitForSessionLspService,
6
+ } from "@mrclrchtr/supi-lsp/api";
7
+
8
+ export interface SemanticProviderOptions {
9
+ waitForReady?: boolean;
10
+ timeoutMs?: number;
11
+ }
12
+
13
+ export const DEFAULT_SEMANTIC_WAIT_MS = 250;
14
+
15
+ /** Acquire the current session-scoped LSP service, optionally waiting for startup. */
16
+ export async function getSemanticService(
17
+ cwd: string,
18
+ options: SemanticProviderOptions = {},
19
+ ): Promise<SessionLspService | null> {
20
+ const state = await getSemanticServiceState(cwd, options);
21
+ return state.kind === "ready" ? state.service : null;
22
+ }
23
+
24
+ /** Read the current LSP service state with an optional short wait for readiness. */
25
+ export async function getSemanticServiceState(
26
+ cwd: string,
27
+ options: SemanticProviderOptions = {},
28
+ ): Promise<SessionLspServiceState> {
29
+ const current = getSessionLspService(cwd);
30
+ if (!options.waitForReady || current.kind !== "pending") {
31
+ return current;
32
+ }
33
+ return waitForSessionLspService(cwd, options.timeoutMs ?? DEFAULT_SEMANTIC_WAIT_MS);
34
+ }
@@ -0,0 +1,14 @@
1
+ import { createTreeSitterSession, type TreeSitterSession } from "@mrclrchtr/supi-tree-sitter/api";
2
+
3
+ /** Run work against a short-lived Tree-sitter session and dispose it afterward. */
4
+ export async function withStructuralSession<T>(
5
+ cwd: string,
6
+ fn: (session: TreeSitterSession) => Promise<T>,
7
+ ): Promise<T> {
8
+ const session = createTreeSitterSession(cwd);
9
+ try {
10
+ return await fn(session);
11
+ } finally {
12
+ session.dispose();
13
+ }
14
+ }
@@ -5,12 +5,9 @@
5
5
  import * as fs from "node:fs";
6
6
  import * as path from "node:path";
7
7
  import { isWithinOrEqual } from "@mrclrchtr/supi-core/api";
8
- import {
9
- getSessionLspService,
10
- type Position,
11
- type SessionLspService,
12
- } from "@mrclrchtr/supi-lsp/api";
13
- import { createTreeSitterSession } from "@mrclrchtr/supi-tree-sitter/api";
8
+ import { type Position, type SessionLspService, toLspPosition } from "@mrclrchtr/supi-lsp/api";
9
+ import { getSemanticService, getSemanticServiceState } from "./providers/semantic-provider.ts";
10
+ import { withStructuralSession } from "./providers/structural-provider.ts";
14
11
  import { escapeRegex, normalizePath } from "./search-helpers.ts";
15
12
  import { highestConfidence } from "./semantic-action-helpers.ts";
16
13
  import type { ConfidenceMode, DisambiguationCandidate } from "./types.ts";
@@ -46,7 +43,7 @@ export { normalizePath } from "./search-helpers.ts";
46
43
  * Convert 1-based public coordinates to 0-based LSP Position.
47
44
  */
48
45
  export function toZeroBased(line: number, character: number): Position {
49
- return { line: line - 1, character: character - 1 };
46
+ return toLspPosition(line, character);
50
47
  }
51
48
 
52
49
  /**
@@ -146,16 +143,12 @@ export async function resolveSymbolTarget(
146
143
  exportedOnly?: boolean;
147
144
  },
148
145
  ): Promise<TargetResolutionResult> {
149
- const lspState = getSessionLspService(cwd);
146
+ const lspState = await getSemanticServiceState(cwd, { waitForReady: true });
150
147
 
151
148
  if (lspState.kind === "ready") {
152
149
  return resolveSymbolViaLsp(symbol, cwd, lspState.service, options);
153
150
  }
154
151
 
155
- if (lspState.kind === "pending") {
156
- // In v1, we may wait for LSP. For now, try structural fallback.
157
- }
158
-
159
152
  // Structural fallback via text search
160
153
  return resolveSymbolViaSearch(symbol, cwd, options);
161
154
  }
@@ -336,10 +329,10 @@ async function resolveFileTargetsViaLsp(
336
329
  cwd: string,
337
330
  structuralTargets: ResolvedTarget[] | null,
338
331
  ): Promise<ResolvedTarget[] | null> {
339
- const lspState = getSessionLspService(cwd);
340
- if (lspState.kind !== "ready") return null;
332
+ const lsp = await getSemanticService(cwd, { waitForReady: true });
333
+ if (!lsp) return null;
341
334
 
342
- const symbols = await lspState.service.documentSymbols(resolvedFile);
335
+ const symbols = await lsp.documentSymbols(resolvedFile);
343
336
  if (!symbols || symbols.length === 0) {
344
337
  return structuralTargets;
345
338
  }
@@ -378,30 +371,28 @@ async function resolveFileTargetsViaTreeSitter(
378
371
  resolvedFile: string,
379
372
  cwd: string,
380
373
  ): Promise<ResolvedTarget[] | null> {
381
- let tsSession: ReturnType<typeof createTreeSitterSession> | null = null;
382
374
  try {
383
- tsSession = createTreeSitterSession(cwd);
384
- const exportsResult = await tsSession.exports(relPath);
385
- if (exportsResult.kind !== "success" || exportsResult.data.length === 0) {
386
- return null;
387
- }
375
+ return await withStructuralSession(cwd, async (tsSession) => {
376
+ const exportsResult = await tsSession.exports(relPath);
377
+ if (exportsResult.kind !== "success" || exportsResult.data.length === 0) {
378
+ return null;
379
+ }
388
380
 
389
- return dedupeTargets(
390
- exportsResult.data.map((record) =>
391
- createResolvedTarget({
392
- file: resolvedFile,
393
- line: record.range.startLine,
394
- character: record.range.startCharacter,
395
- name: record.name,
396
- kind: record.kind,
397
- confidence: "structural",
398
- }),
399
- ),
400
- );
381
+ return dedupeTargets(
382
+ exportsResult.data.map((record) =>
383
+ createResolvedTarget({
384
+ file: resolvedFile,
385
+ line: record.range.startLine,
386
+ character: record.range.startCharacter,
387
+ name: record.name,
388
+ kind: record.kind,
389
+ confidence: "structural",
390
+ }),
391
+ ),
392
+ );
393
+ });
401
394
  } catch {
402
395
  return null;
403
- } finally {
404
- tsSession?.dispose();
405
396
  }
406
397
  }
407
398
 
@@ -0,0 +1,21 @@
1
+ // Prompt guidance and tool description for the code_intel tool.
2
+
3
+ export const toolDescription = `Code intelligence tool — codebase orientation, semantic relationships, impact analysis, and scoped search.
4
+
5
+ Actions: brief, callers, callees, implementations, affected, pattern, index.
6
+
7
+ Use code_intel to localize relevant files before precise drill-down: summarize a project/package/file, find callers/callees/implementations, estimate blast radius, or search within a scope. Prefer lsp_lookup, lsp_document_symbols, lsp_workspace_symbols, lsp_diagnostics, lsp_refactor, and lsp_recover for semantic drill-down once the target is known; use tree_sitter for exact syntax and read/rg once you know the file. line and character are 1-based and require file. pattern is literal unless regex is true; kind supports definition, export, or import. Relative paths resolve from cwd, and leading @ on path/file is stripped.`;
8
+
9
+ export const promptGuidelines = [
10
+ 'Use code_intel with `action: "brief"` for a project, package, directory, file, or anchored-position brief before opening more files.',
11
+ 'Use code_intel with `action: "index"` for a project map, top-level directories, language mix, or landmark files.',
12
+ 'Use code_intel with `action: "callers"` or `action: "implementations"` to find who invokes a symbol or which concrete types implement a declaration.',
13
+ 'Use code_intel with `action: "callees"` for outgoing calls from a function or method at a known `file`, `line`, and `character`.',
14
+ 'Use code_intel with `action: "affected"` before edits for blast radius, downstream modules, risk, and likely follow-up checks or tests.',
15
+ 'Use code_intel with `action: "pattern"` for bounded search within a path; `pattern` is literal by default, set `regex: true` for regex, and use `kind: "definition" | "export" | "import"` for structured search.',
16
+ "Use code_intel with `file`, `line`, and `character` for anchored positions; do not pair `line` or `character` with `path`.",
17
+ "Use code_intel first when the area is not yet localized; switch to lsp_lookup, lsp_document_symbols, lsp_workspace_symbols, lsp_diagnostics, lsp_refactor, or lsp_recover for semantic drill-down once code_intel narrows the target.",
18
+ ];
19
+
20
+ export const promptSnippet =
21
+ "code_intel — codebase orientation, callers/callees, blast radius, and scoped search before file drill-down";