@mrclrchtr/supi-code-intelligence 1.5.0 → 1.7.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 (80) hide show
  1. package/README.md +42 -24
  2. package/node_modules/@mrclrchtr/supi-core/package.json +6 -2
  3. package/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
  4. package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
  5. package/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
  6. package/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
  7. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +6 -2
  8. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
  9. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
  10. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
  11. package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
  12. package/node_modules/@mrclrchtr/supi-lsp/package.json +10 -3
  13. package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +8 -5
  14. package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +79 -190
  15. package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +38 -0
  16. package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +61 -387
  17. package/node_modules/@mrclrchtr/supi-lsp/src/format.ts +16 -8
  18. package/node_modules/@mrclrchtr/supi-lsp/src/lsp.ts +2 -2
  19. package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +1 -1
  20. package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +11 -184
  21. package/node_modules/@mrclrchtr/supi-lsp/src/session/lsp-state.ts +1 -1
  22. package/node_modules/@mrclrchtr/supi-lsp/src/tool/guidance.ts +1 -1
  23. package/node_modules/@mrclrchtr/supi-lsp/src/tool/tool-specs.ts +1 -1
  24. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +6 -2
  25. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
  26. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
  27. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
  28. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
  29. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/LICENSE +21 -0
  30. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/README.md +265 -0
  31. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs +4661 -0
  32. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs.map +7 -0
  33. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js +4605 -0
  34. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js.map +7 -0
  35. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
  36. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +57 -0
  37. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +100 -0
  38. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs +4063 -0
  39. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs.map +7 -0
  40. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts +1025 -0
  41. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts.map +58 -0
  42. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts +1025 -0
  43. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +58 -0
  44. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js +4007 -0
  45. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js.map +7 -0
  46. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
  47. package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +55 -0
  48. package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +2 -2
  49. package/package.json +4 -4
  50. package/src/actions/affected-action.ts +67 -54
  51. package/src/actions/brief-action.ts +142 -5
  52. package/src/actions/callees-action.ts +1 -1
  53. package/src/actions/callers-action.ts +38 -67
  54. package/src/actions/implementations-action.ts +27 -63
  55. package/src/actions/map-action.ts +206 -0
  56. package/src/actions/pattern-action.ts +1 -1
  57. package/src/api.ts +1 -0
  58. package/src/brief-focused.ts +5 -5
  59. package/src/brief.ts +3 -3
  60. package/src/code-intelligence.ts +6 -75
  61. package/src/index.ts +1 -0
  62. package/src/pattern-structured.ts +1 -1
  63. package/src/prioritization-signals.ts +13 -26
  64. package/src/query-params.ts +15 -0
  65. package/src/resolve-target.ts +2 -2
  66. package/src/search-helpers.ts +2 -2
  67. package/src/target-resolution.ts +27 -102
  68. package/src/tool/execute-affected.ts +25 -0
  69. package/src/tool/execute-brief.ts +25 -0
  70. package/src/tool/execute-map.ts +32 -0
  71. package/src/tool/execute-pattern.ts +26 -0
  72. package/src/tool/execute-relations.ts +48 -0
  73. package/src/tool/guidance.ts +24 -13
  74. package/src/tool/register-tools.ts +32 -0
  75. package/src/tool/tool-specs.ts +184 -0
  76. package/src/tool/validation.ts +43 -0
  77. package/src/types.ts +10 -0
  78. package/src/actions/index-action.ts +0 -187
  79. package/src/tool/action-specs.ts +0 -66
  80. package/src/tool-actions.ts +0 -100
@@ -1,20 +1,14 @@
1
- // Implementations action — find concrete implementations via LSP or heuristic.
1
+ // Implementations action — find concrete implementations via LSP.
2
2
 
3
3
  import * as path from "node:path";
4
4
  import { getSemanticService } from "../providers/semantic-provider.ts";
5
+ import type { CodeQueryParams as ActionParams } from "../query-params.ts";
5
6
  import { resolveTarget } from "../resolve-target.ts";
6
- import {
7
- escapeRegex,
8
- isInProjectPath,
9
- normalizePath,
10
- runRipgrep,
11
- uriToFile,
12
- } from "../search-helpers.ts";
7
+ import { isInProjectPath, uriToFile } from "../search-helpers.ts";
13
8
  import { isResolvedTargetGroup } from "../semantic-action-helpers.ts";
14
- import type { ActionParams } from "../tool-actions.ts";
15
9
  import type { CodeIntelResult, SearchDetails } from "../types.ts";
16
10
 
17
- // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: implementation lookup has distinct semantic, unsupported-file, and heuristic branches
11
+ // biome-ignore lint/complexity/noExcessiveCognitiveComplexity: implementation lookup keeps semantic and unsupported-file paths explicit for maintainability
18
12
  export async function executeImplementationsAction(
19
13
  params: ActionParams,
20
14
  cwd: string,
@@ -57,7 +51,7 @@ export async function executeImplementationsAction(
57
51
 
58
52
  if (lsp) {
59
53
  const impls = await lsp.implementation(target.file, target.position);
60
- if (impls) {
54
+ if (impls !== null) {
61
55
  const locations = Array.isArray(impls) ? impls : [impls];
62
56
  if (locations.length > 0) {
63
57
  const content = formatSemanticImpls(locations, cwd, params.maxResults ?? 8);
@@ -68,29 +62,35 @@ export async function executeImplementationsAction(
68
62
  candidateCount: projectLocs.length,
69
63
  omittedCount: externalLocs.length,
70
64
  nextQueries: [
71
- "`code_intel affected` before changing implementations",
72
- "`code_intel brief` on containing modules for deeper context",
65
+ "`code_affected` before changing implementations",
66
+ "`code_brief` on containing modules for deeper context",
73
67
  ],
74
68
  };
75
69
  return { content, details: { type: "search" as const, data: searchDetails } };
76
70
  }
77
- }
78
- }
79
71
 
80
- if (target.name) {
81
- const result = formatHeuristicImpls(target.name, params, cwd);
82
- const details: SearchDetails = {
83
- confidence: "heuristic",
84
- scope: params.path ?? null,
85
- candidateCount: result.matchCount,
86
- omittedCount: 0,
87
- nextQueries: ["Enable LSP for semantic implementation resolution"],
88
- };
89
- return { content: result.content, details: { type: "search" as const, data: details } };
72
+ const semanticEmptyDetails: SearchDetails = {
73
+ confidence: "semantic",
74
+ scope: params.path ?? null,
75
+ candidateCount: 0,
76
+ omittedCount: 0,
77
+ nextQueries: [
78
+ "`code_pattern` only if you explicitly want text-search hints for likely implementations",
79
+ ],
80
+ };
81
+ return {
82
+ content: target.name
83
+ ? `No implementations found for \`${target.name}\`.`
84
+ : `No implementations found for ${relPath}:${target.displayLine}:${target.displayCharacter}.`,
85
+ details: { type: "search" as const, data: semanticEmptyDetails },
86
+ };
87
+ }
90
88
  }
91
89
 
92
90
  return {
93
- content: `No implementations found for ${relPath}:${target.displayLine}:${target.displayCharacter}.\n\nLSP implementation lookup may not be available. Try \`code_intel pattern\` with the type name.`,
91
+ content: target.name
92
+ ? `No implementations found for \`${target.name}\`.`
93
+ : `No implementations found for ${relPath}:${target.displayLine}:${target.displayCharacter}.`,
94
94
  details: {
95
95
  type: "search" as const,
96
96
  data: {
@@ -98,9 +98,7 @@ export async function executeImplementationsAction(
98
98
  scope: params.path ?? null,
99
99
  candidateCount: 0,
100
100
  omittedCount: 0,
101
- nextQueries: [
102
- "Enable LSP for semantic implementation resolution, or try `code_intel pattern`",
103
- ],
101
+ nextQueries: ["Enable LSP for semantic implementation resolution."],
104
102
  },
105
103
  },
106
104
  };
@@ -172,37 +170,3 @@ function formatSemanticImpls(
172
170
  lines.push("");
173
171
  return lines.join("\n");
174
172
  }
175
-
176
- function formatHeuristicImpls(
177
- symbol: string,
178
- params: ActionParams,
179
- cwd: string,
180
- ): { content: string; matchCount: number } {
181
- const scopePath = params.path ? normalizePath(params.path, cwd) : cwd;
182
- const pattern = `(implements|extends)\\s+.*\\b${escapeRegex(symbol)}\\b`;
183
- const matches = runRipgrep(pattern, scopePath, cwd, { maxMatches: 10 });
184
-
185
- if (matches.length === 0) {
186
- return {
187
- content: `No implementations found for \`${symbol}\`.\n\nTry \`code_intel pattern\` with the type name.`,
188
- matchCount: 0,
189
- };
190
- }
191
-
192
- const lines: string[] = [];
193
- lines.push(`# Implementations of \`${symbol}\` (heuristic)`);
194
- lines.push("");
195
- lines.push(
196
- `**${matches.length} candidate${matches.length > 1 ? "s" : ""}** — text-search hints, not semantic implementations`,
197
- );
198
- lines.push("");
199
-
200
- for (const m of matches.slice(0, 8)) {
201
- lines.push(`- \`${m.file}\`:${m.line} — \`${m.text.slice(0, 80)}\``);
202
- }
203
- if (matches.length > 8) {
204
- lines.push(`- _+${matches.length - 8} more omitted_`);
205
- }
206
- lines.push("");
207
- return { content: lines.join("\n"), matchCount: matches.length };
208
- }
@@ -0,0 +1,206 @@
1
+ import * as fs from "node:fs";
2
+ import * as path from "node:path";
3
+ import type { CodeIntelResult, MapDetails } from "../types.ts";
4
+
5
+ const SOURCE_EXTENSIONS = new Map([
6
+ [".ts", "TypeScript"],
7
+ [".tsx", "TSX"],
8
+ [".js", "JavaScript"],
9
+ [".jsx", "JSX"],
10
+ [".mts", "TypeScript"],
11
+ [".cts", "TypeScript"],
12
+ [".mjs", "JavaScript"],
13
+ [".cjs", "JavaScript"],
14
+ [".py", "Python"],
15
+ [".pyi", "Python"],
16
+ [".rs", "Rust"],
17
+ [".go", "Go"],
18
+ [".mod", "Go"],
19
+ [".java", "Java"],
20
+ [".kt", "Kotlin"],
21
+ [".kts", "Kotlin"],
22
+ [".rb", "Ruby"],
23
+ [".php", "PHP"],
24
+ [".swift", "Swift"],
25
+ [".cpp", "C++"],
26
+ [".hpp", "C++"],
27
+ [".cc", "C++"],
28
+ [".cxx", "C++"],
29
+ [".hxx", "C++ Header"],
30
+ [".c++", "C++"],
31
+ [".h++", "C++ Header"],
32
+ [".c", "C"],
33
+ [".h", "C Header"],
34
+ [".css", "CSS"],
35
+ [".scss", "SCSS"],
36
+ [".less", "Less"],
37
+ [".html", "HTML"],
38
+ [".htm", "HTML"],
39
+ [".xhtml", "HTML"],
40
+ [".json", "JSON"],
41
+ [".yaml", "YAML"],
42
+ [".yml", "YAML"],
43
+ [".toml", "TOML"],
44
+ [".md", "Markdown"],
45
+ [".sh", "Shell"],
46
+ [".bash", "Shell"],
47
+ [".zsh", "Shell"],
48
+ [".r", "R"],
49
+ [".sql", "SQL"],
50
+ [".cs", "C#"],
51
+ ]);
52
+
53
+ const LANDMARK_FILES = new Set([
54
+ "package.json",
55
+ "tsconfig.json",
56
+ "jsconfig.json",
57
+ "vite.config.ts",
58
+ "vitest.config.ts",
59
+ "jest.config.ts",
60
+ "playwright.config.ts",
61
+ "biome.json",
62
+ "eslint.config.js",
63
+ ".eslintrc.js",
64
+ "deno.json",
65
+ "deno.jsonc",
66
+ "Cargo.toml",
67
+ "go.mod",
68
+ "pyproject.toml",
69
+ "setup.py",
70
+ "requirements.txt",
71
+ "Makefile",
72
+ "justfile",
73
+ "Taskfile.yml",
74
+ "Dockerfile",
75
+ "docker-compose.yml",
76
+ ".env.example",
77
+ ".env.local.example",
78
+ ]);
79
+
80
+ const SKIP_DIRS = new Set(["node_modules", "dist", "build", ".git"]);
81
+
82
+ interface FileStats {
83
+ byExtension: Map<string, number>;
84
+ byChildDir: Map<string, number>;
85
+ landmarkFiles: string[];
86
+ total: number;
87
+ }
88
+
89
+ export function executeMapAction(scopePath: string, cwd: string): CodeIntelResult {
90
+ const stats = gatherStats(scopePath);
91
+ const scope = formatScope(scopePath, cwd);
92
+ const content = formatMap(scope, stats);
93
+ const details: MapDetails = {
94
+ scope,
95
+ totalFiles: stats.total,
96
+ childDirectoryCount: stats.byChildDir.size,
97
+ landmarkCount: stats.landmarkFiles.length,
98
+ nextQueries: ["`code_brief` for prioritized context on this scope"],
99
+ };
100
+
101
+ return { content, details: { type: "map", data: details } };
102
+ }
103
+
104
+ function gatherStats(scopePath: string): FileStats {
105
+ const stats: FileStats = {
106
+ byExtension: new Map<string, number>(),
107
+ byChildDir: new Map<string, number>(),
108
+ landmarkFiles: [],
109
+ total: 0,
110
+ };
111
+
112
+ walkDirectory(scopePath, "", stats);
113
+ return stats;
114
+ }
115
+
116
+ function walkDirectory(dir: string, rel: string, stats: FileStats): void {
117
+ const entries = readEntries(dir);
118
+ if (!entries) return;
119
+
120
+ for (const entry of entries) {
121
+ if (shouldSkipEntry(entry)) continue;
122
+ visitEntry(dir, rel, entry, stats);
123
+ }
124
+ }
125
+
126
+ function readEntries(dir: string): fs.Dirent[] | null {
127
+ try {
128
+ return fs.readdirSync(dir, { withFileTypes: true });
129
+ } catch {
130
+ return null;
131
+ }
132
+ }
133
+
134
+ function shouldSkipEntry(entry: fs.Dirent): boolean {
135
+ return entry.name.startsWith(".") || SKIP_DIRS.has(entry.name);
136
+ }
137
+
138
+ function visitEntry(dir: string, rel: string, entry: fs.Dirent, stats: FileStats): void {
139
+ const entryRel = rel ? `${rel}/${entry.name}` : entry.name;
140
+ const fullPath = path.join(dir, entry.name);
141
+
142
+ if (entry.isDirectory()) {
143
+ walkDirectory(fullPath, entryRel, stats);
144
+ return;
145
+ }
146
+
147
+ recordFileStats(entry.name, entryRel, stats);
148
+ }
149
+
150
+ function recordFileStats(entryName: string, entryRel: string, stats: FileStats): void {
151
+ stats.total++;
152
+ const ext = path.extname(entryName).toLowerCase();
153
+ stats.byExtension.set(ext, (stats.byExtension.get(ext) ?? 0) + 1);
154
+
155
+ const firstSegment = entryRel.split("/")[0];
156
+ if (firstSegment && entryRel.includes("/")) {
157
+ stats.byChildDir.set(firstSegment, (stats.byChildDir.get(firstSegment) ?? 0) + 1);
158
+ }
159
+
160
+ if (LANDMARK_FILES.has(entryName)) {
161
+ stats.landmarkFiles.push(entryRel);
162
+ }
163
+ }
164
+
165
+ function formatMap(scope: string, stats: FileStats): string {
166
+ const lines: string[] = [];
167
+
168
+ lines.push(`# Code Map: ${scope}`);
169
+ lines.push("");
170
+ lines.push(`**Files:** ${stats.total} total`);
171
+ for (const [ext, count] of [...stats.byExtension.entries()]
172
+ .sort((a, b) => b[1] - a[1])
173
+ .slice(0, 10)) {
174
+ const label = SOURCE_EXTENSIONS.get(ext) ?? (ext || "(no extension)");
175
+ lines.push(`- ${label}: ${count}`);
176
+ }
177
+ if (stats.byExtension.size > 10) {
178
+ lines.push(`- _+${stats.byExtension.size - 10} more extensions_`);
179
+ }
180
+ lines.push("");
181
+
182
+ if (stats.byChildDir.size > 0) {
183
+ lines.push("**Child directories:**");
184
+ for (const [dir, count] of [...stats.byChildDir.entries()].sort((a, b) => b[1] - a[1])) {
185
+ lines.push(`- ${dir}/ (${count} file${count !== 1 ? "s" : ""})`);
186
+ }
187
+ lines.push("");
188
+ }
189
+
190
+ if (stats.landmarkFiles.length > 0) {
191
+ lines.push("**Landmark files:**");
192
+ for (const file of stats.landmarkFiles) {
193
+ lines.push(`- \`${file}\``);
194
+ }
195
+ lines.push("");
196
+ }
197
+
198
+ return lines.join("\n");
199
+ }
200
+
201
+ function formatScope(scopePath: string, cwd: string): string {
202
+ const relative = path.relative(cwd, scopePath);
203
+ if (relative === "") return ".";
204
+ if (relative.startsWith(`..${path.sep}`) || relative === "..") return scopePath;
205
+ return relative.replaceAll(path.sep, "/");
206
+ }
@@ -7,6 +7,7 @@ import {
7
7
  type StructuredMatch,
8
8
  type StructuredPatternResult,
9
9
  } from "../pattern-structured.ts";
10
+ import type { CodeQueryParams as ActionParams } from "../query-params.ts";
10
11
  import type { RgMatch } from "../search-helpers.ts";
11
12
  import {
12
13
  escapeRegex,
@@ -15,7 +16,6 @@ import {
15
16
  runRipgrep,
16
17
  runRipgrepDetailed,
17
18
  } from "../search-helpers.ts";
18
- import type { ActionParams } from "../tool-actions.ts";
19
19
  import type { CodeIntelResult, SearchDetails } from "../types.ts";
20
20
 
21
21
  /**
package/src/api.ts CHANGED
@@ -23,5 +23,6 @@ export type {
23
23
  CodeIntelResult,
24
24
  ConfidenceMode,
25
25
  DisambiguationCandidate,
26
+ MapDetails,
26
27
  SearchDetails,
27
28
  } from "./types.ts";
@@ -183,13 +183,13 @@ function addDependentsSection(
183
183
  const depShort = dep.name.replace(/^@[^/]+\//, "");
184
184
  lines.push(`- ${depShort} (\`${dep.relativePath}\`)`);
185
185
  }
186
- nextQueries.push("`code_intel affected` before modifying exports from this module");
186
+ nextQueries.push("`code_affected` before modifying exports from this module");
187
187
  }
188
188
 
189
189
  if (mod.entrypoints.length > 0) {
190
190
  const ep = mod.entrypoints[0];
191
191
  nextQueries.push(
192
- `\`code_intel brief\` with \`file: "${mod.relativePath}/${ep.replace(/^\.\//, "")}"\` for entrypoint details`,
192
+ `\`code_brief\` with \`file: "${mod.relativePath}/${ep.replace(/^\.\//, "")}"\` for entrypoint details`,
193
193
  );
194
194
  }
195
195
  }
@@ -276,7 +276,7 @@ function formatNonModuleDir(ctx: NonModuleDirContext): void {
276
276
  lines.push(`- Exports: ${summary.exportCount}`);
277
277
  lines.push("");
278
278
  nextQueries.push(
279
- `\`code_intel pattern\` with \`path: "${relPath || originalPath}"\` to inspect a specific nested symbol`,
279
+ `\`code_pattern\` with \`path: "${relPath || originalPath}"\` to inspect a specific nested symbol`,
280
280
  );
281
281
  }
282
282
 
@@ -334,11 +334,11 @@ function generateFileBrief(
334
334
  }
335
335
 
336
336
  nextQueries.push(
337
- `\`code_intel callers\` with \`file: "${relPath}"\` and a line/character for call-site analysis`,
337
+ `\`code_relations\` with \`kind: "callers"\`, \`file: "${relPath}"\`, and a line/character for call-site analysis`,
338
338
  );
339
339
  if (mod) {
340
340
  nextQueries.push(
341
- `\`code_intel brief\` with \`path: "${mod.relativePath}"\` for the containing module overview`,
341
+ `\`code_brief\` with \`path: "${mod.relativePath}"\` for the containing module overview`,
342
342
  );
343
343
  }
344
344
 
package/src/brief.ts CHANGED
@@ -64,7 +64,7 @@ export function generateOverview(model: ArchitectureModel): string | null {
64
64
  lines.push(formatGitContext(gitCtx));
65
65
  }
66
66
 
67
- lines.push("_Use `code_intel brief` for deeper context on any module or file._");
67
+ lines.push("_Use `code_brief` for deeper context on any module or file._");
68
68
 
69
69
  return lines.join("\n");
70
70
  }
@@ -208,11 +208,11 @@ function buildNextQueries(model: ArchitectureModel, publicSurfaces: string[]): s
208
208
  if (model.modules.length > 0) {
209
209
  const firstMod = model.modules[0];
210
210
  nextQueries.push(
211
- `\`code_intel brief\` with \`path: "${firstMod.relativePath}"\` for a focused module brief`,
211
+ `\`code_brief\` with \`path: "${firstMod.relativePath}"\` for a focused module brief`,
212
212
  );
213
213
  }
214
214
  if (publicSurfaces.length > 0) {
215
- nextQueries.push("`code_intel affected` before modifying shared exports");
215
+ nextQueries.push("`code_affected` before modifying shared exports");
216
216
  }
217
217
  return nextQueries;
218
218
  }
@@ -1,22 +1,16 @@
1
- // Code Intelligence extension entry point — registers the `code_intel` tool with pi.
2
- // Provides architecture briefs, caller/callee analysis, impact assessment, and pattern search.
1
+ // Code Intelligence extension entry point — registers the focused code-intelligence tools.
2
+ // Provides architecture briefs, project maps, relationship tracing, impact assessment, and pattern search.
3
3
 
4
- import { StringEnum } from "@earendil-works/pi-ai";
5
4
  import type { BeforeAgentStartEventResult, ExtensionAPI } from "@earendil-works/pi-coding-agent";
6
- import { Type } from "typebox";
7
5
  import { buildArchitectureModel } from "./architecture.ts";
8
6
  import { generateOverview } from "./brief.ts";
9
- import { CODE_INTEL_ACTION_NAMES, type CodeIntelAction } from "./tool/action-specs.ts";
10
- import { promptGuidelines, promptSnippet, toolDescription } from "./tool/guidance.ts";
11
- import { executeAction } from "./tool-actions.ts";
7
+ import { registerCodeIntelligenceTools } from "./tool/register-tools.ts";
12
8
 
13
9
  const OVERVIEW_CUSTOM_TYPE = "code-intelligence-overview";
14
10
 
15
- const CodeIntelActionEnum = StringEnum(CODE_INTEL_ACTION_NAMES);
16
-
17
11
  /**
18
- * Register the `code_intel` tool and inject a lightweight architecture overview
19
- * once per session.
12
+ * Register the focused code-intelligence tools and inject a lightweight
13
+ * architecture overview once per session.
20
14
  */
21
15
  export default function codeIntelligenceExtension(pi: ExtensionAPI) {
22
16
  let hasInjectedOverview = false;
@@ -56,68 +50,5 @@ export default function codeIntelligenceExtension(pi: ExtensionAPI) {
56
50
  },
57
51
  );
58
52
 
59
- pi.registerTool({
60
- name: "code_intel",
61
- label: "Code Intelligence",
62
- description: toolDescription,
63
- parameters: Type.Object({
64
- action: CodeIntelActionEnum,
65
- path: Type.Optional(
66
- Type.String({ description: "Scope or focus path (package, directory, or file)" }),
67
- ),
68
- file: Type.Optional(
69
- Type.String({
70
- description:
71
- "Anchored target file (use with line/character) or a file-level semantic target for brief/callers/affected",
72
- }),
73
- ),
74
- line: Type.Optional(Type.Number({ description: "1-based line number for anchored target" })),
75
- character: Type.Optional(
76
- Type.Number({ description: "1-based character column (UTF-16) for anchored target" }),
77
- ),
78
- symbol: Type.Optional(
79
- Type.String({ description: "Symbol name for discovery-based resolution" }),
80
- ),
81
- pattern: Type.Optional(
82
- Type.String({
83
- description: "Text search pattern (pattern action only; literal by default)",
84
- }),
85
- ),
86
- regex: Type.Optional(
87
- Type.Boolean({
88
- description: "Use regex semantics for pattern action (default: false, literal search)",
89
- }),
90
- ),
91
- kind: Type.Optional(
92
- Type.String({
93
- description:
94
- "Symbol kind filter for discovery, or pattern kind (`definition` | `export` | `import`) for structured searches",
95
- }),
96
- ),
97
- exportedOnly: Type.Optional(
98
- Type.Boolean({ description: "Limit discovery to exported symbols" }),
99
- ),
100
- maxResults: Type.Optional(Type.Number({ description: "Maximum results to return" })),
101
- contextLines: Type.Optional(Type.Number({ description: "Context lines around matches" })),
102
- summary: Type.Optional(
103
- Type.Boolean({
104
- description:
105
- "Aggregate counts by directory instead of line-level matches (pattern action only)",
106
- }),
107
- ),
108
- }),
109
- promptSnippet,
110
- promptGuidelines,
111
- // biome-ignore lint/complexity/useMaxParams: pi ToolDefinition.execute signature
112
- execute: async (_toolCallId, params, _signal, _onUpdate, ctx) => {
113
- const { content, details } = await executeAction(
114
- params as unknown as { action: CodeIntelAction } & Record<string, unknown>,
115
- { cwd: ctx.cwd },
116
- );
117
- return {
118
- content: [{ type: "text", text: content }],
119
- details,
120
- };
121
- },
122
- });
53
+ registerCodeIntelligenceTools(pi);
123
54
  }
package/src/index.ts CHANGED
@@ -24,5 +24,6 @@ export type {
24
24
  CodeIntelResult,
25
25
  ConfidenceMode,
26
26
  DisambiguationCandidate,
27
+ MapDetails,
27
28
  SearchDetails,
28
29
  } from "./types.ts";
@@ -1,7 +1,7 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import { withStructuralSession } from "./providers/structural-provider.ts";
4
- import type { ActionParams } from "./tool-actions.ts";
4
+ import type { CodeQueryParams as ActionParams } from "./query-params.ts";
5
5
 
6
6
  export const STRUCTURED_PATTERN_FILE_CAP = 200;
7
7
  const STRUCTURED_PATTERN_TIMEOUT_MS = 10_000;
@@ -1,5 +1,5 @@
1
- import * as fs from "node:fs";
2
1
  import * as path from "node:path";
2
+ import { readJsonFile } from "@mrclrchtr/supi-core/api";
3
3
  import { getSessionLspService } from "@mrclrchtr/supi-lsp/api";
4
4
 
5
5
  export interface PrioritySignalsSummary {
@@ -106,22 +106,18 @@ function loadDiagnostics(
106
106
 
107
107
  function loadCoverageSummary(cwd: string): Map<string, number> {
108
108
  const coveragePath = path.join(cwd, "coverage", "coverage-summary.json");
109
- if (!fs.existsSync(coveragePath)) return new Map();
110
-
111
- try {
112
- const parsed = JSON.parse(fs.readFileSync(coveragePath, "utf-8")) as Record<string, unknown>;
113
- const map = new Map<string, number>();
114
- for (const [file, value] of Object.entries(parsed)) {
115
- if (file === "total" || typeof value !== "object" || value === null) continue;
116
- const linesPct = getPct(value, "lines");
117
- const statementsPct = getPct(value, "statements");
118
- const pct = Math.min(linesPct ?? 100, statementsPct ?? 100);
119
- map.set(path.resolve(cwd, file), pct);
120
- }
121
- return map;
122
- } catch {
123
- return new Map();
109
+ const parsed = readJsonFile(coveragePath);
110
+ if (!parsed) return new Map();
111
+
112
+ const map = new Map<string, number>();
113
+ for (const [file, value] of Object.entries(parsed)) {
114
+ if (file === "total" || typeof value !== "object" || value === null) continue;
115
+ const linesPct = getPct(value, "lines");
116
+ const statementsPct = getPct(value, "statements");
117
+ const pct = Math.min(linesPct ?? 100, statementsPct ?? 100);
118
+ map.set(path.resolve(cwd, file), pct);
124
119
  }
120
+ return map;
125
121
  }
126
122
 
127
123
  function loadUnusedFiles(cwd: string): Set<string> {
@@ -157,16 +153,7 @@ function loadUnusedExports(cwd: string): Array<{ file: string; name: string }> {
157
153
  }
158
154
 
159
155
  function loadKnipJson(cwd: string): Record<string, unknown> | null {
160
- const knipPath = path.join(cwd, "knip.json");
161
- if (!fs.existsSync(knipPath)) return null;
162
- try {
163
- const parsed = JSON.parse(fs.readFileSync(knipPath, "utf-8"));
164
- return typeof parsed === "object" && parsed !== null
165
- ? (parsed as Record<string, unknown>)
166
- : null;
167
- } catch {
168
- return null;
169
- }
156
+ return readJsonFile(path.join(cwd, "knip.json"));
170
157
  }
171
158
 
172
159
  function getPct(value: object, key: string): number | null {
@@ -0,0 +1,15 @@
1
+ /** Shared parameter bag for internal code-intelligence actions. */
2
+ export interface CodeQueryParams {
3
+ path?: string;
4
+ file?: string;
5
+ line?: number;
6
+ character?: number;
7
+ symbol?: string;
8
+ pattern?: string;
9
+ regex?: boolean;
10
+ kind?: string;
11
+ exportedOnly?: boolean;
12
+ maxResults?: number;
13
+ contextLines?: number;
14
+ summary?: boolean;
15
+ }
@@ -1,3 +1,4 @@
1
+ import type { CodeQueryParams as ActionParams } from "./query-params.ts";
1
2
  import {
2
3
  type ResolvedTarget,
3
4
  type ResolvedTargetGroup,
@@ -5,7 +6,6 @@ import {
5
6
  resolveFileTargetGroup,
6
7
  resolveSymbolTarget,
7
8
  } from "./target-resolution.ts";
8
- import type { ActionParams } from "./tool-actions.ts";
9
9
 
10
10
  /**
11
11
  * Resolve a target from action params. Returns either a target, a file-level target group,
@@ -104,7 +104,7 @@ function formatDisambiguation(
104
104
  if (result.candidates.length > 0) {
105
105
  const first = result.candidates[0];
106
106
  lines.push(
107
- `Example: \`{ "action": "${params.action}", "file": "${first.file}", "line": ${first.line}, "character": ${first.character} }\``,
107
+ `Example: rerun with \`file: "${first.file}"\`, \`line: ${first.line}\`, and \`character: ${first.character}\`.`,
108
108
  );
109
109
  }
110
110
 
@@ -1,4 +1,4 @@
1
- // Shared search helpers for code_intel actions.
1
+ // Shared search helpers for code-intelligence search and routing helpers.
2
2
 
3
3
  import { execFileSync } from "node:child_process";
4
4
  import * as path from "node:path";
@@ -71,7 +71,7 @@ export interface RipgrepRunResult {
71
71
  /**
72
72
  * Run ripgrep with JSON output and parse matches, filtering low-signal paths.
73
73
  *
74
- * This helper preserves the historical behavior used by most `code_intel`
74
+ * This helper preserves the historical behavior used by internal
75
75
  * actions: any ripgrep execution failure is treated like an empty match set.
76
76
  * Call `runRipgrepDetailed()` when a caller needs to surface regex parse errors
77
77
  * or other non-no-match failures to the agent.