@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.
- package/README.md +42 -24
- package/node_modules/@mrclrchtr/supi-core/package.json +6 -2
- package/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
- package/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
- package/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
- package/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/package.json +6 -2
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
- package/node_modules/@mrclrchtr/supi-lsp/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
- package/node_modules/@mrclrchtr/supi-lsp/package.json +10 -3
- package/node_modules/@mrclrchtr/supi-lsp/src/client/client.ts +8 -5
- package/node_modules/@mrclrchtr/supi-lsp/src/client/transport.ts +79 -190
- package/node_modules/@mrclrchtr/supi-lsp/src/config/server-config.ts +38 -0
- package/node_modules/@mrclrchtr/supi-lsp/src/config/types.ts +61 -387
- package/node_modules/@mrclrchtr/supi-lsp/src/format.ts +16 -8
- package/node_modules/@mrclrchtr/supi-lsp/src/lsp.ts +2 -2
- package/node_modules/@mrclrchtr/supi-lsp/src/manager/manager-project-info.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/pattern-matcher.ts +11 -184
- package/node_modules/@mrclrchtr/supi-lsp/src/session/lsp-state.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/tool/guidance.ts +1 -1
- package/node_modules/@mrclrchtr/supi-lsp/src/tool/tool-specs.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/package.json +6 -2
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/api.ts +12 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/config/config.ts +1 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/index.ts +12 -1
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/@mrclrchtr/supi-core/src/tool-framework.ts +116 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/LICENSE +21 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/README.md +265 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs +4661 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.cjs.map +7 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js +4605 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.js.map +7 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/debug/web-tree-sitter.wasm.map +57 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/package.json +100 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs +4063 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.cjs.map +7 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts +1025 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.cts.map +58 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts +1025 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.d.ts.map +58 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js +4007 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.js.map +7 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm +0 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/node_modules/web-tree-sitter/web-tree-sitter.wasm.map +55 -0
- package/node_modules/@mrclrchtr/supi-tree-sitter/package.json +2 -2
- package/package.json +4 -4
- package/src/actions/affected-action.ts +67 -54
- package/src/actions/brief-action.ts +142 -5
- package/src/actions/callees-action.ts +1 -1
- package/src/actions/callers-action.ts +38 -67
- package/src/actions/implementations-action.ts +27 -63
- package/src/actions/map-action.ts +206 -0
- package/src/actions/pattern-action.ts +1 -1
- package/src/api.ts +1 -0
- package/src/brief-focused.ts +5 -5
- package/src/brief.ts +3 -3
- package/src/code-intelligence.ts +6 -75
- package/src/index.ts +1 -0
- package/src/pattern-structured.ts +1 -1
- package/src/prioritization-signals.ts +13 -26
- package/src/query-params.ts +15 -0
- package/src/resolve-target.ts +2 -2
- package/src/search-helpers.ts +2 -2
- package/src/target-resolution.ts +27 -102
- package/src/tool/execute-affected.ts +25 -0
- package/src/tool/execute-brief.ts +25 -0
- package/src/tool/execute-map.ts +32 -0
- package/src/tool/execute-pattern.ts +26 -0
- package/src/tool/execute-relations.ts +48 -0
- package/src/tool/guidance.ts +24 -13
- package/src/tool/register-tools.ts +32 -0
- package/src/tool/tool-specs.ts +184 -0
- package/src/tool/validation.ts +43 -0
- package/src/types.ts +10 -0
- package/src/actions/index-action.ts +0 -187
- package/src/tool/action-specs.ts +0 -66
- package/src/tool-actions.ts +0 -100
|
@@ -1,20 +1,14 @@
|
|
|
1
|
-
// Implementations action — find concrete implementations via LSP
|
|
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
|
|
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
|
-
"`
|
|
72
|
-
"`
|
|
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
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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:
|
|
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
package/src/brief-focused.ts
CHANGED
|
@@ -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("`
|
|
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
|
-
`\`
|
|
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
|
-
`\`
|
|
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
|
-
`\`
|
|
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
|
-
`\`
|
|
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 `
|
|
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
|
-
`\`
|
|
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("`
|
|
215
|
+
nextQueries.push("`code_affected` before modifying shared exports");
|
|
216
216
|
}
|
|
217
217
|
return nextQueries;
|
|
218
218
|
}
|
package/src/code-intelligence.ts
CHANGED
|
@@ -1,22 +1,16 @@
|
|
|
1
|
-
// Code Intelligence extension entry point — registers the
|
|
2
|
-
// Provides architecture briefs,
|
|
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 {
|
|
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
|
|
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
|
|
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
|
@@ -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 "./
|
|
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
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
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
|
+
}
|
package/src/resolve-target.ts
CHANGED
|
@@ -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:
|
|
107
|
+
`Example: rerun with \`file: "${first.file}"\`, \`line: ${first.line}\`, and \`character: ${first.character}\`.`,
|
|
108
108
|
);
|
|
109
109
|
}
|
|
110
110
|
|
package/src/search-helpers.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Shared search helpers for
|
|
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
|
|
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.
|