@kenkaiiii/ggcoder 5.38.0 → 5.39.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/dist/core/agent-session-tool-tiers.test.d.ts +2 -0
- package/dist/core/agent-session-tool-tiers.test.d.ts.map +1 -0
- package/dist/core/agent-session-tool-tiers.test.js +138 -0
- package/dist/core/agent-session-tool-tiers.test.js.map +1 -0
- package/dist/core/agent-session.d.ts +19 -3
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +47 -6
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/code-retrieval-chunkers.d.ts +27 -0
- package/dist/core/code-retrieval-chunkers.d.ts.map +1 -0
- package/dist/core/code-retrieval-chunkers.js +264 -0
- package/dist/core/code-retrieval-chunkers.js.map +1 -0
- package/dist/core/code-retrieval-chunkers.test.d.ts +2 -0
- package/dist/core/code-retrieval-chunkers.test.d.ts.map +1 -0
- package/dist/core/code-retrieval-chunkers.test.js +203 -0
- package/dist/core/code-retrieval-chunkers.test.js.map +1 -0
- package/dist/core/code-retrieval.d.ts +6 -0
- package/dist/core/code-retrieval.d.ts.map +1 -1
- package/dist/core/code-retrieval.js +38 -1
- package/dist/core/code-retrieval.js.map +1 -1
- package/dist/core/lsp/client-navigation.test.d.ts +2 -0
- package/dist/core/lsp/client-navigation.test.d.ts.map +1 -0
- package/dist/core/lsp/client-navigation.test.js +119 -0
- package/dist/core/lsp/client-navigation.test.js.map +1 -0
- package/dist/core/lsp/client.d.ts +62 -0
- package/dist/core/lsp/client.d.ts.map +1 -1
- package/dist/core/lsp/client.js +147 -0
- package/dist/core/lsp/client.js.map +1 -1
- package/dist/core/lsp/manager-navigation.test.d.ts +2 -0
- package/dist/core/lsp/manager-navigation.test.d.ts.map +1 -0
- package/dist/core/lsp/manager-navigation.test.js +119 -0
- package/dist/core/lsp/manager-navigation.test.js.map +1 -0
- package/dist/core/lsp/manager.d.ts +35 -1
- package/dist/core/lsp/manager.d.ts.map +1 -1
- package/dist/core/lsp/manager.js +92 -0
- package/dist/core/lsp/manager.js.map +1 -1
- package/dist/core/mcp/deferred-catalog.d.ts +1 -1
- package/dist/core/mcp/deferred-catalog.js +1 -1
- package/dist/core/settings-manager.d.ts +2 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +10 -1
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/system-prompt.d.ts +6 -1
- package/dist/system-prompt.d.ts.map +1 -1
- package/dist/system-prompt.js +28 -5
- package/dist/system-prompt.js.map +1 -1
- package/dist/system-prompt.tiering.test.d.ts +2 -0
- package/dist/system-prompt.tiering.test.d.ts.map +1 -0
- package/dist/system-prompt.tiering.test.js +103 -0
- package/dist/system-prompt.tiering.test.js.map +1 -0
- package/dist/tools/code-nav.d.ts +30 -0
- package/dist/tools/code-nav.d.ts.map +1 -0
- package/dist/tools/code-nav.js +247 -0
- package/dist/tools/code-nav.js.map +1 -0
- package/dist/tools/code-nav.test.d.ts +2 -0
- package/dist/tools/code-nav.test.d.ts.map +1 -0
- package/dist/tools/code-nav.test.js +109 -0
- package/dist/tools/code-nav.test.js.map +1 -0
- package/dist/tools/grep-parity.test.d.ts +2 -0
- package/dist/tools/grep-parity.test.d.ts.map +1 -0
- package/dist/tools/grep-parity.test.js +92 -0
- package/dist/tools/grep-parity.test.js.map +1 -0
- package/dist/tools/grep-recall.test.d.ts +2 -0
- package/dist/tools/grep-recall.test.d.ts.map +1 -0
- package/dist/tools/grep-recall.test.js +114 -0
- package/dist/tools/grep-recall.test.js.map +1 -0
- package/dist/tools/grep-spawn.test.d.ts +2 -0
- package/dist/tools/grep-spawn.test.d.ts.map +1 -0
- package/dist/tools/grep-spawn.test.js +66 -0
- package/dist/tools/grep-spawn.test.js.map +1 -0
- package/dist/tools/grep.d.ts +13 -3
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +340 -40
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/grep.test.js +11 -8
- package/dist/tools/grep.test.js.map +1 -1
- package/dist/tools/index.d.ts +6 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +4 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/prompt-hints.d.ts.map +1 -1
- package/dist/tools/prompt-hints.js +23 -8
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/search-code.d.ts.map +1 -1
- package/dist/tools/search-code.js +7 -6
- package/dist/tools/search-code.js.map +1 -1
- package/dist/tools/search-code.test.js +8 -2
- package/dist/tools/search-code.test.js.map +1 -1
- package/dist/tools/tool-search.d.ts.map +1 -1
- package/dist/tools/tool-search.js +2 -1
- package/dist/tools/tool-search.js.map +1 -1
- package/dist/tools/tool-tiers.d.ts +42 -0
- package/dist/tools/tool-tiers.d.ts.map +1 -0
- package/dist/tools/tool-tiers.js +77 -0
- package/dist/tools/tool-tiers.js.map +1 -0
- package/dist/tools/tool-tiers.test.d.ts +2 -0
- package/dist/tools/tool-tiers.test.d.ts.map +1 -0
- package/dist/tools/tool-tiers.test.js +48 -0
- package/dist/tools/tool-tiers.test.js.map +1 -0
- package/dist/ui/hooks/useAgentLoop.js +1 -1
- package/dist/ui/hooks/useAgentLoop.js.map +1 -1
- package/package.json +4 -4
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { fileURLToPath } from "node:url";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { resolvePath, toPosixPath } from "./path-utils.js";
|
|
5
|
+
import { localOperations } from "./operations.js";
|
|
6
|
+
const CodeNavParams = z.object({
|
|
7
|
+
op: z
|
|
8
|
+
.enum(["definition", "references", "symbols", "hover"])
|
|
9
|
+
.describe("definition = where a symbol is declared; references = every use of it; " +
|
|
10
|
+
"symbols = outline of one file; hover = its type/signature"),
|
|
11
|
+
file: z.string().describe("File containing the symbol (relative to cwd or absolute)"),
|
|
12
|
+
line: z
|
|
13
|
+
.number()
|
|
14
|
+
.int()
|
|
15
|
+
.min(1)
|
|
16
|
+
.optional()
|
|
17
|
+
.describe("1-based line of the symbol. Required for every op except `symbols`."),
|
|
18
|
+
column: z
|
|
19
|
+
.number()
|
|
20
|
+
.int()
|
|
21
|
+
.min(1)
|
|
22
|
+
.optional()
|
|
23
|
+
.describe("1-based column of the symbol; inferred from `symbol` when omitted"),
|
|
24
|
+
symbol: z
|
|
25
|
+
.string()
|
|
26
|
+
.optional()
|
|
27
|
+
.describe("Symbol name — used to locate the column on `line`, and to filter `symbols`"),
|
|
28
|
+
max_results: z
|
|
29
|
+
.number()
|
|
30
|
+
.int()
|
|
31
|
+
.min(1)
|
|
32
|
+
.optional()
|
|
33
|
+
.describe("Maximum locations to return (default: 60)"),
|
|
34
|
+
});
|
|
35
|
+
const DEFAULT_MAX_RESULTS = 60;
|
|
36
|
+
const MAX_SNIPPET_LENGTH = 160;
|
|
37
|
+
const MAX_HOVER_LENGTH = 1200;
|
|
38
|
+
/**
|
|
39
|
+
* LSP SymbolKind, for the outline. Numbers are the wire values; the names are
|
|
40
|
+
* what a reader of the tool output actually wants to see.
|
|
41
|
+
*/
|
|
42
|
+
const SYMBOL_KIND_NAMES = {
|
|
43
|
+
1: "file",
|
|
44
|
+
2: "module",
|
|
45
|
+
3: "namespace",
|
|
46
|
+
4: "package",
|
|
47
|
+
5: "class",
|
|
48
|
+
6: "method",
|
|
49
|
+
7: "property",
|
|
50
|
+
8: "field",
|
|
51
|
+
9: "constructor",
|
|
52
|
+
10: "enum",
|
|
53
|
+
11: "interface",
|
|
54
|
+
12: "function",
|
|
55
|
+
13: "variable",
|
|
56
|
+
14: "constant",
|
|
57
|
+
15: "string",
|
|
58
|
+
16: "number",
|
|
59
|
+
17: "boolean",
|
|
60
|
+
18: "array",
|
|
61
|
+
19: "object",
|
|
62
|
+
20: "key",
|
|
63
|
+
21: "null",
|
|
64
|
+
22: "enum-member",
|
|
65
|
+
23: "struct",
|
|
66
|
+
24: "event",
|
|
67
|
+
25: "operator",
|
|
68
|
+
26: "type-parameter",
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Semantic code navigation, backed by the project's language server.
|
|
72
|
+
*
|
|
73
|
+
* One tool with an `op` enum rather than four tools: each tool in the live set
|
|
74
|
+
* costs a full JSON schema on every request, and four near-identical schemas
|
|
75
|
+
* would spend the budget this harness is trying to save.
|
|
76
|
+
*
|
|
77
|
+
* Rename is deliberately absent. A workspace-wide rename that half-applies is
|
|
78
|
+
* worse than no rename at all, and nothing here can roll one back.
|
|
79
|
+
*/
|
|
80
|
+
export function createCodeNavTool(cwd, lspManager, ops = localOperations) {
|
|
81
|
+
return {
|
|
82
|
+
name: "code_nav",
|
|
83
|
+
description: "Resolve a symbol with the language server: `definition` (where it is declared), " +
|
|
84
|
+
"`references` (every use), `symbols` (outline of a file), `hover` (type/signature). " +
|
|
85
|
+
"Exact and cross-file — prefer it over grep for 'who calls this' and 'where is this " +
|
|
86
|
+
"defined'. Reports explicitly when no language server can answer.",
|
|
87
|
+
parameters: CodeNavParams,
|
|
88
|
+
async execute({ op, file, line, column, symbol, max_results }) {
|
|
89
|
+
if (!lspManager) {
|
|
90
|
+
return "code_nav is unavailable: language-server support is disabled for this session. Use grep or code_search instead.";
|
|
91
|
+
}
|
|
92
|
+
const maxResults = max_results ?? DEFAULT_MAX_RESULTS;
|
|
93
|
+
const absolute = resolvePath(cwd, file);
|
|
94
|
+
let content;
|
|
95
|
+
try {
|
|
96
|
+
content = await ops.readFile(absolute);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
return `Cannot read ${toPosixPath(path.relative(cwd, absolute))}: ${error instanceof Error ? error.message : String(error)}`;
|
|
100
|
+
}
|
|
101
|
+
const lines = content.split("\n");
|
|
102
|
+
if (op === "symbols") {
|
|
103
|
+
const outcome = await lspManager.documentSymbols(absolute, content);
|
|
104
|
+
if (outcome.kind !== "ok")
|
|
105
|
+
return describeFailure(outcome, cwd, op);
|
|
106
|
+
return formatSymbols(outcome.value, symbol, maxResults);
|
|
107
|
+
}
|
|
108
|
+
const position = resolvePosition(lines, line, column, symbol);
|
|
109
|
+
if (typeof position === "string")
|
|
110
|
+
return position;
|
|
111
|
+
if (op === "hover") {
|
|
112
|
+
const outcome = await lspManager.hover(absolute, content, position);
|
|
113
|
+
if (outcome.kind !== "ok")
|
|
114
|
+
return describeFailure(outcome, cwd, op);
|
|
115
|
+
const text = outcome.value.trim();
|
|
116
|
+
if (!text)
|
|
117
|
+
return `No hover information at ${describeSite(cwd, absolute, position)}.`;
|
|
118
|
+
return text.length > MAX_HOVER_LENGTH
|
|
119
|
+
? `${text.slice(0, MAX_HOVER_LENGTH)}\n… [hover truncated]`
|
|
120
|
+
: text;
|
|
121
|
+
}
|
|
122
|
+
const outcome = op === "definition"
|
|
123
|
+
? await lspManager.definition(absolute, content, position)
|
|
124
|
+
: await lspManager.references(absolute, content, position);
|
|
125
|
+
if (outcome.kind !== "ok")
|
|
126
|
+
return describeFailure(outcome, cwd, op);
|
|
127
|
+
return formatLocations(outcome.value, cwd, ops, maxResults, op, absolute, position);
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Turn a 1-based line (+ optional column or symbol name) into an LSP position.
|
|
133
|
+
* Returning a string means the request cannot be built at all, which is a
|
|
134
|
+
* clearer answer than sending the server a position that resolves to nothing.
|
|
135
|
+
*/
|
|
136
|
+
function resolvePosition(lines, line, column, symbol) {
|
|
137
|
+
if (line === undefined) {
|
|
138
|
+
return '`line` is required for this op. Pass the 1-based line the symbol appears on (op: "symbols" needs no position).';
|
|
139
|
+
}
|
|
140
|
+
if (line > lines.length) {
|
|
141
|
+
return `Line ${line} is past the end of the file (${lines.length} lines).`;
|
|
142
|
+
}
|
|
143
|
+
const text = lines[line - 1] ?? "";
|
|
144
|
+
if (column !== undefined)
|
|
145
|
+
return { line: line - 1, character: column - 1 };
|
|
146
|
+
if (symbol) {
|
|
147
|
+
const index = text.indexOf(symbol);
|
|
148
|
+
if (index === -1) {
|
|
149
|
+
return `\`${symbol}\` does not appear on line ${line}. Pass \`column\`, or the line where it does appear.`;
|
|
150
|
+
}
|
|
151
|
+
return { line: line - 1, character: index };
|
|
152
|
+
}
|
|
153
|
+
// No column and no name: aim at the first non-whitespace character, which is
|
|
154
|
+
// the identifier far more often than column 1 is.
|
|
155
|
+
const firstNonSpace = text.search(/\S/);
|
|
156
|
+
return { line: line - 1, character: firstNonSpace === -1 ? 0 : firstNonSpace };
|
|
157
|
+
}
|
|
158
|
+
function describeSite(cwd, absolute, position) {
|
|
159
|
+
return `${toPosixPath(path.relative(cwd, absolute))}:${position.line + 1}:${position.character + 1}`;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Render a non-`ok` outcome as an instruction, not an absence. "No references
|
|
163
|
+
* found" and "no language server installed" are opposite facts, and conflating
|
|
164
|
+
* them makes the model confidently delete live code.
|
|
165
|
+
*/
|
|
166
|
+
function describeFailure(outcome, cwd, op) {
|
|
167
|
+
const rel = toPosixPath(path.relative(cwd, outcome.filePath)) || outcome.filePath;
|
|
168
|
+
switch (outcome.kind) {
|
|
169
|
+
case "unsupported":
|
|
170
|
+
return outcome.serverId
|
|
171
|
+
? `The ${outcome.serverId} language server does not implement \`${op}\`. Fall back to code_search or grep.`
|
|
172
|
+
: `No language server is configured for ${rel}. Fall back to code_search or grep.`;
|
|
173
|
+
case "unavailable":
|
|
174
|
+
return `No language server is installed for ${rel}${outcome.serverId ? ` (expected: ${outcome.serverId})` : ""}. Install it to enable code_nav, or fall back to code_search or grep.`;
|
|
175
|
+
case "timeout":
|
|
176
|
+
return `The language server did not answer \`${op}\` for ${rel} in time. It may still be indexing — retry once, then fall back to grep.`;
|
|
177
|
+
default:
|
|
178
|
+
return `The language server failed on \`${op}\` for ${rel}${outcome.message ? `: ${outcome.message}` : ""}. Fall back to code_search or grep.`;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
function formatSymbols(symbols, filter, maxResults) {
|
|
182
|
+
const matched = filter
|
|
183
|
+
? symbols.filter((s) => s.name.toLowerCase().includes(filter.toLowerCase()))
|
|
184
|
+
: symbols;
|
|
185
|
+
if (matched.length === 0) {
|
|
186
|
+
return filter ? `No symbol matching \`${filter}\` in this file.` : "No symbols in this file.";
|
|
187
|
+
}
|
|
188
|
+
const shown = matched.slice(0, maxResults);
|
|
189
|
+
const lines = shown.map((entry) => {
|
|
190
|
+
const qualified = [...entry.containers, entry.name].join(".");
|
|
191
|
+
const kind = SYMBOL_KIND_NAMES[entry.kind] ?? "symbol";
|
|
192
|
+
const detail = entry.detail ? ` ${entry.detail}` : "";
|
|
193
|
+
return `${entry.range.start.line + 1}:${entry.range.start.character + 1} — ${kind} ${qualified}${detail}`;
|
|
194
|
+
});
|
|
195
|
+
if (matched.length > shown.length) {
|
|
196
|
+
lines.push(`… ${matched.length - shown.length} more symbol(s) not shown`);
|
|
197
|
+
}
|
|
198
|
+
return lines.join("\n");
|
|
199
|
+
}
|
|
200
|
+
async function formatLocations(locations, cwd, ops, maxResults, op, originFile, position) {
|
|
201
|
+
if (locations.length === 0) {
|
|
202
|
+
return op === "definition"
|
|
203
|
+
? `No definition found for the symbol at ${describeSite(cwd, originFile, position)}.`
|
|
204
|
+
: `No references found for the symbol at ${describeSite(cwd, originFile, position)}.`;
|
|
205
|
+
}
|
|
206
|
+
const shown = locations.slice(0, maxResults);
|
|
207
|
+
// One read per distinct file, not per location: a hot symbol produces many
|
|
208
|
+
// hits in the same file and re-reading it per hit is the whole cost.
|
|
209
|
+
const sourceCache = new Map();
|
|
210
|
+
const lines = [];
|
|
211
|
+
for (const location of shown) {
|
|
212
|
+
const absolute = fileFromUri(location.uri);
|
|
213
|
+
const rel = toPosixPath(path.relative(cwd, absolute)) || absolute;
|
|
214
|
+
let source = sourceCache.get(absolute);
|
|
215
|
+
if (source === undefined) {
|
|
216
|
+
try {
|
|
217
|
+
source = (await ops.readFile(absolute)).split("\n");
|
|
218
|
+
}
|
|
219
|
+
catch {
|
|
220
|
+
source = null;
|
|
221
|
+
}
|
|
222
|
+
sourceCache.set(absolute, source);
|
|
223
|
+
}
|
|
224
|
+
const snippetRaw = source?.[location.range.start.line]?.trim() ?? "";
|
|
225
|
+
const snippet = snippetRaw.length > MAX_SNIPPET_LENGTH
|
|
226
|
+
? `${snippetRaw.slice(0, MAX_SNIPPET_LENGTH)}…`
|
|
227
|
+
: snippetRaw;
|
|
228
|
+
const site = `${rel}:${location.range.start.line + 1}:${location.range.start.character + 1}`;
|
|
229
|
+
lines.push(snippet ? `${site} — ${snippet}` : site);
|
|
230
|
+
}
|
|
231
|
+
if (locations.length > shown.length) {
|
|
232
|
+
lines.push(`… ${locations.length - shown.length} more result(s) not shown — raise max_results to see them`);
|
|
233
|
+
}
|
|
234
|
+
return lines.join("\n");
|
|
235
|
+
}
|
|
236
|
+
/** `file://` URI → filesystem path, tolerating servers that send a bare path. */
|
|
237
|
+
function fileFromUri(uri) {
|
|
238
|
+
if (!uri.startsWith("file://"))
|
|
239
|
+
return uri;
|
|
240
|
+
try {
|
|
241
|
+
return fileURLToPath(uri);
|
|
242
|
+
}
|
|
243
|
+
catch {
|
|
244
|
+
return decodeURIComponent(uri.replace(/^file:\/\//, ""));
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
//# sourceMappingURL=code-nav.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-nav.js","sourceRoot":"","sources":["../../src/tools/code-nav.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-nav.test.d.ts","sourceRoot":"","sources":["../../src/tools/code-nav.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { createCodeNavTool } from "./code-nav.js";
|
|
7
|
+
import { LspManager } from "../core/lsp/manager.js";
|
|
8
|
+
import { removeWhenReleased } from "../core/lsp/test-support.js";
|
|
9
|
+
const FIXTURE = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "./__fixtures__/fake-lsp-server.mjs");
|
|
10
|
+
function fakeSpec(serverArgs = []) {
|
|
11
|
+
return {
|
|
12
|
+
id: "fake",
|
|
13
|
+
extensions: [".fake"],
|
|
14
|
+
rootMarkers: ["fake-root.json"],
|
|
15
|
+
languageIdFor: () => "fake",
|
|
16
|
+
resolveCommand: () => ({ command: process.execPath, args: [FIXTURE, ...serverArgs] }),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
const SOURCE = "class Widget {\n render() {}\n}\nconst widget = new Widget();\n";
|
|
20
|
+
function context() {
|
|
21
|
+
return { signal: new AbortController().signal, toolCallId: "test" };
|
|
22
|
+
}
|
|
23
|
+
describe("code_nav", () => {
|
|
24
|
+
let tmpDir;
|
|
25
|
+
let managers;
|
|
26
|
+
beforeEach(async () => {
|
|
27
|
+
tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "code-nav-test-"));
|
|
28
|
+
await fs.writeFile(path.join(tmpDir, "fake-root.json"), "{}");
|
|
29
|
+
await fs.writeFile(path.join(tmpDir, "widget.fake"), SOURCE);
|
|
30
|
+
managers = [];
|
|
31
|
+
});
|
|
32
|
+
afterEach(async () => {
|
|
33
|
+
for (const manager of managers)
|
|
34
|
+
manager.shutdownAll();
|
|
35
|
+
await removeWhenReleased(tmpDir);
|
|
36
|
+
});
|
|
37
|
+
function tool(spec = fakeSpec(), budgetMs = 5000) {
|
|
38
|
+
const manager = new LspManager(tmpDir, {
|
|
39
|
+
catalog: [spec],
|
|
40
|
+
warmBudgetMs: budgetMs,
|
|
41
|
+
firstBudgetMs: budgetMs,
|
|
42
|
+
});
|
|
43
|
+
managers.push(manager);
|
|
44
|
+
return createCodeNavTool(tmpDir, manager);
|
|
45
|
+
}
|
|
46
|
+
it("resolves a definition to path:line:col with a snippet", async () => {
|
|
47
|
+
const result = await tool().execute({ op: "definition", file: "widget.fake", line: 4, symbol: "Widget" }, context());
|
|
48
|
+
expect(String(result)).toBe("widget.fake:1:7 — class Widget {");
|
|
49
|
+
});
|
|
50
|
+
it("lists references", async () => {
|
|
51
|
+
const result = String(await tool().execute({ op: "references", file: "widget.fake", line: 1, symbol: "Widget" }, context()));
|
|
52
|
+
expect(result).toContain("widget.fake:1:7 — class Widget {");
|
|
53
|
+
expect(result).toContain("widget.fake:3:3 — }");
|
|
54
|
+
});
|
|
55
|
+
it("outlines a file's symbols with containers and kinds", async () => {
|
|
56
|
+
const result = String(await tool().execute({ op: "symbols", file: "widget.fake" }, context()));
|
|
57
|
+
expect(result).toContain("class Widget");
|
|
58
|
+
expect(result).toContain("method Widget.render (): void");
|
|
59
|
+
});
|
|
60
|
+
it("filters the outline by symbol name", async () => {
|
|
61
|
+
const result = String(await tool().execute({ op: "symbols", file: "widget.fake", symbol: "render" }, context()));
|
|
62
|
+
expect(result).toContain("render");
|
|
63
|
+
expect(result).not.toContain("class Widget\n");
|
|
64
|
+
});
|
|
65
|
+
it("returns hover text", async () => {
|
|
66
|
+
const result = String(await tool().execute({ op: "hover", file: "widget.fake", line: 4, symbol: "widget" }, context()));
|
|
67
|
+
expect(result).toContain("const widget: Widget");
|
|
68
|
+
});
|
|
69
|
+
it("caps results and says how many were withheld", async () => {
|
|
70
|
+
const result = String(await tool().execute({ op: "references", file: "widget.fake", line: 1, symbol: "Widget", max_results: 1 }, context()));
|
|
71
|
+
expect(result).toContain("widget.fake:1:7");
|
|
72
|
+
expect(result).toContain("1 more result(s) not shown");
|
|
73
|
+
});
|
|
74
|
+
it("says a server is missing instead of reporting no results", async () => {
|
|
75
|
+
const result = String(await tool({ ...fakeSpec(), resolveCommand: () => null }).execute({ op: "references", file: "widget.fake", line: 1, symbol: "Widget" }, context()));
|
|
76
|
+
expect(result).toContain("No language server is installed");
|
|
77
|
+
expect(result).not.toContain("No references found");
|
|
78
|
+
});
|
|
79
|
+
it("says the capability is missing when the server lacks it", async () => {
|
|
80
|
+
const result = String(await tool(fakeSpec(["--no-nav"])).execute({ op: "definition", file: "widget.fake", line: 1, symbol: "Widget" }, context()));
|
|
81
|
+
expect(result).toContain("does not implement `definition`");
|
|
82
|
+
});
|
|
83
|
+
it("says it timed out rather than returning nothing", async () => {
|
|
84
|
+
const result = String(await tool(fakeSpec(["--nav-silent"]), 200).execute({ op: "references", file: "widget.fake", line: 1, symbol: "Widget" }, context()));
|
|
85
|
+
expect(result).toContain("did not answer");
|
|
86
|
+
});
|
|
87
|
+
it("reports an unsupported file type", async () => {
|
|
88
|
+
await fs.writeFile(path.join(tmpDir, "notes.unknown"), "text\n");
|
|
89
|
+
const result = String(await tool().execute({ op: "symbols", file: "notes.unknown" }, context()));
|
|
90
|
+
expect(result).toContain("No language server is configured");
|
|
91
|
+
});
|
|
92
|
+
it("requires a line for position ops", async () => {
|
|
93
|
+
const result = String(await tool().execute({ op: "hover", file: "widget.fake" }, context()));
|
|
94
|
+
expect(result).toContain("`line` is required");
|
|
95
|
+
});
|
|
96
|
+
it("rejects a symbol that is not on the given line", async () => {
|
|
97
|
+
const result = String(await tool().execute({ op: "definition", file: "widget.fake", line: 2, symbol: "Widget" }, context()));
|
|
98
|
+
expect(result).toContain("does not appear on line 2");
|
|
99
|
+
});
|
|
100
|
+
it("reports a missing file instead of throwing", async () => {
|
|
101
|
+
const result = String(await tool().execute({ op: "symbols", file: "nope.fake" }, context()));
|
|
102
|
+
expect(result).toContain("Cannot read nope.fake");
|
|
103
|
+
});
|
|
104
|
+
it("states plainly when language-server support is off", async () => {
|
|
105
|
+
const result = String(await createCodeNavTool(tmpDir, undefined).execute({ op: "symbols", file: "widget.fake" }, context()));
|
|
106
|
+
expect(result).toContain("language-server support is disabled");
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
//# sourceMappingURL=code-nav.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"code-nav.test.js","sourceRoot":"","sources":["../../src/tools/code-nav.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grep-parity.test.d.ts","sourceRoot":"","sources":["../../src/tools/grep-parity.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { beforeAll, describe, expect, it } from "vitest";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { createGrepTool, detectExternalScanner } from "./grep.js";
|
|
6
|
+
function context() {
|
|
7
|
+
return { signal: new AbortController().signal, toolCallId: "test" };
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* One fixture covering the inputs where two regex engines and two file walkers
|
|
11
|
+
* are most likely to disagree: dot-directories, ignored paths, non-ASCII text,
|
|
12
|
+
* CRLF endings, a line past the truncation cap, and a binary blob.
|
|
13
|
+
*/
|
|
14
|
+
async function makeFixture() {
|
|
15
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "gg-grep-parity-"));
|
|
16
|
+
const files = {
|
|
17
|
+
".gitignore": "ignored/\n*.min.js\n",
|
|
18
|
+
".github/workflows/ci.yml": "name: ci\njobs:\n build:\n runs-on: ubuntu-latest\n",
|
|
19
|
+
"src/alpha.ts": "export const alpha = 1;\nconst TOKEN = 'alpha';\n// alpha again\n",
|
|
20
|
+
"src/beta.ts": "export function beta() {\n return 'ALPHA';\n}\n",
|
|
21
|
+
// A nested ignore file must NOT take effect: only the search root's
|
|
22
|
+
// .gitignore applies, on both scanner paths.
|
|
23
|
+
"src/nested/.gitignore": "deep/\n",
|
|
24
|
+
"src/nested/deep/gamma.ts": "// alpha in a deep file\nexport const gamma = 2;\n",
|
|
25
|
+
"src/unicode.ts": "const caf\u00e9 = '\u00e9l\u00e8ve \u2014 alpha';\nconst \u65e5\u672c = 'alpha \u6f22\u5b57';\n",
|
|
26
|
+
"src/crlf.ts": "const a = 1;\r\nconst alpha = 2;\r\nconst c = 3;\r\n",
|
|
27
|
+
"src/long.ts": `const long = "${"alpha ".repeat(120)}";\n`,
|
|
28
|
+
"ignored/hidden.ts": "export const alpha = 'should not be searched';\n",
|
|
29
|
+
"vendor/bundle.min.js": "var alpha=1;\n",
|
|
30
|
+
"docs/notes.md": "Alpha, beta and gamma.\n",
|
|
31
|
+
};
|
|
32
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
33
|
+
const abs = path.join(dir, rel);
|
|
34
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
35
|
+
await fs.writeFile(abs, content);
|
|
36
|
+
}
|
|
37
|
+
await fs
|
|
38
|
+
.writeFile(path.join(dir, "assets", "blob.bin"), Buffer.from([0, 1, 2, 97, 108, 112, 104, 97, 0, 3]))
|
|
39
|
+
.catch(async () => {
|
|
40
|
+
await fs.mkdir(path.join(dir, "assets"), { recursive: true });
|
|
41
|
+
await fs.writeFile(path.join(dir, "assets", "blob.bin"), Buffer.from([0, 1, 2, 97, 108, 112, 104, 97, 0, 3]));
|
|
42
|
+
});
|
|
43
|
+
return dir;
|
|
44
|
+
}
|
|
45
|
+
const queries = [
|
|
46
|
+
{ label: "literal", args: { pattern: "alpha", max_results: 200 } },
|
|
47
|
+
{
|
|
48
|
+
label: "case-insensitive",
|
|
49
|
+
args: { pattern: "alpha", case_insensitive: true, max_results: 200 },
|
|
50
|
+
},
|
|
51
|
+
{ label: "inline (?i) flag", args: { pattern: "(?i)ALPHA", max_results: 200 } },
|
|
52
|
+
{ label: "anchored", args: { pattern: "^export ", max_results: 200 } },
|
|
53
|
+
{ label: "character class", args: { pattern: "const [a-z]+ =", max_results: 200 } },
|
|
54
|
+
{ label: "include filter", args: { pattern: "alpha", include: "*.ts", max_results: 200 } },
|
|
55
|
+
{ label: "unicode literal", args: { pattern: "\u6f22\u5b57", max_results: 200 } },
|
|
56
|
+
{ label: "result cap", args: { pattern: "alpha", max_results: 3 } },
|
|
57
|
+
{ label: "no matches", args: { pattern: "zzz-not-present-zzz", max_results: 200 } },
|
|
58
|
+
];
|
|
59
|
+
let externalAvailable = false;
|
|
60
|
+
beforeAll(async () => {
|
|
61
|
+
externalAvailable = (await detectExternalScanner()) !== undefined;
|
|
62
|
+
});
|
|
63
|
+
describe("grep scanner parity", () => {
|
|
64
|
+
it.each(queries)("returns identical output for $label", async ({ args }) => {
|
|
65
|
+
if (!externalAvailable) {
|
|
66
|
+
// `rg` is optional. Skipping loudly beats a silently green run.
|
|
67
|
+
console.warn("skipping grep parity: the external scanner `rg` is not on PATH");
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const dir = await makeFixture();
|
|
71
|
+
const inProcess = await createGrepTool(dir, undefined, {
|
|
72
|
+
useExternalScanner: () => false,
|
|
73
|
+
}).execute(args, context());
|
|
74
|
+
const external = await createGrepTool(dir, undefined, {
|
|
75
|
+
useExternalScanner: () => true,
|
|
76
|
+
}).execute(args, context());
|
|
77
|
+
expect(String(external)).toBe(String(inProcess));
|
|
78
|
+
});
|
|
79
|
+
it("falls back in-process for lookahead, which the external engine cannot run", async () => {
|
|
80
|
+
const dir = await makeFixture();
|
|
81
|
+
const args = { pattern: "alpha(?= again)", max_results: 200 };
|
|
82
|
+
const forced = await createGrepTool(dir, undefined, {
|
|
83
|
+
useExternalScanner: () => true,
|
|
84
|
+
}).execute(args, context());
|
|
85
|
+
const reference = await createGrepTool(dir, undefined, {
|
|
86
|
+
useExternalScanner: () => false,
|
|
87
|
+
}).execute(args, context());
|
|
88
|
+
expect(String(forced)).toBe(String(reference));
|
|
89
|
+
expect(String(forced)).toContain("alpha again");
|
|
90
|
+
});
|
|
91
|
+
});
|
|
92
|
+
//# sourceMappingURL=grep-parity.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grep-parity.test.js","sourceRoot":"","sources":["../../src/tools/grep-parity.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grep-recall.test.d.ts","sourceRoot":"","sources":["../../src/tools/grep-recall.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import os from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import readline from "node:readline";
|
|
6
|
+
import { createReadStream } from "node:fs";
|
|
7
|
+
import { createGrepTool } from "./grep.js";
|
|
8
|
+
function context() {
|
|
9
|
+
return { signal: new AbortController().signal, toolCallId: "test" };
|
|
10
|
+
}
|
|
11
|
+
async function makeFixture(files) {
|
|
12
|
+
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "gg-grep-recall-"));
|
|
13
|
+
for (const [rel, content] of Object.entries(files)) {
|
|
14
|
+
const abs = path.join(dir, rel);
|
|
15
|
+
await fs.mkdir(path.dirname(abs), { recursive: true });
|
|
16
|
+
await fs.writeFile(abs, content);
|
|
17
|
+
}
|
|
18
|
+
return dir;
|
|
19
|
+
}
|
|
20
|
+
/** Both scanner paths, so recall is asserted for whichever one a host uses. */
|
|
21
|
+
const scanners = [
|
|
22
|
+
{ label: "in-process", useExternalScanner: () => false },
|
|
23
|
+
{ label: "external", useExternalScanner: () => true },
|
|
24
|
+
];
|
|
25
|
+
describe.each(scanners)("grep recall ($label)", ({ useExternalScanner }) => {
|
|
26
|
+
const grep = (dir) => createGrepTool(dir, undefined, { useExternalScanner });
|
|
27
|
+
it("searches dot-directories", async () => {
|
|
28
|
+
const dir = await makeFixture({
|
|
29
|
+
".github/workflows/ci.yml": "jobs:\n build:\n runs-on: ubuntu-latest\n",
|
|
30
|
+
"README.md": "no jobs here\n",
|
|
31
|
+
});
|
|
32
|
+
const result = await grep(dir).execute({ pattern: "runs-on" }, context());
|
|
33
|
+
expect(result).toContain(path.join(".github", "workflows", "ci.yml") + ":3:");
|
|
34
|
+
});
|
|
35
|
+
it("skips files matched by .gitignore", async () => {
|
|
36
|
+
const dir = await makeFixture({
|
|
37
|
+
".gitignore": "dist/\n*.log\n",
|
|
38
|
+
"src/app.ts": "export const marker = 1;\n",
|
|
39
|
+
"dist/app.js": "export const marker = 1;\n",
|
|
40
|
+
"debug.log": "marker\n",
|
|
41
|
+
});
|
|
42
|
+
const result = await grep(dir).execute({ pattern: "marker" }, context());
|
|
43
|
+
expect(result).toContain(path.join("src", "app.ts") + ":1:");
|
|
44
|
+
expect(result).not.toContain("dist");
|
|
45
|
+
expect(result).not.toContain("debug.log");
|
|
46
|
+
});
|
|
47
|
+
it("still searches an ignored directory when it is the explicit path", async () => {
|
|
48
|
+
const dir = await makeFixture({
|
|
49
|
+
".gitignore": "dist/\n",
|
|
50
|
+
"dist/app.js": "export const marker = 1;\n",
|
|
51
|
+
});
|
|
52
|
+
const result = await grep(dir).execute({ pattern: "marker", path: "dist" }, context());
|
|
53
|
+
expect(result).toContain("app.js:1:");
|
|
54
|
+
});
|
|
55
|
+
it("never scans node_modules or .git", async () => {
|
|
56
|
+
const dir = await makeFixture({
|
|
57
|
+
"node_modules/dep/index.js": "marker\n",
|
|
58
|
+
".git/COMMIT_EDITMSG": "marker\n",
|
|
59
|
+
"src/keep.ts": "marker\n",
|
|
60
|
+
});
|
|
61
|
+
const result = await grep(dir).execute({ pattern: "marker" }, context());
|
|
62
|
+
expect(result).toContain(path.join("src", "keep.ts") + ":1:");
|
|
63
|
+
expect(result).not.toContain("node_modules");
|
|
64
|
+
expect(result).not.toContain(".git");
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
describe("grep scan ordering", () => {
|
|
68
|
+
it("matches a sequential scan over the same candidate list", async () => {
|
|
69
|
+
const files = {};
|
|
70
|
+
// Enough files to fill several scan waves, with matches spread across them.
|
|
71
|
+
for (let i = 0; i < 50; i++) {
|
|
72
|
+
files[`pkg${String(i).padStart(2, "0")}/mod.ts`] =
|
|
73
|
+
`line one\nconst marker${i} = ${i};\nmarker tail\n`;
|
|
74
|
+
}
|
|
75
|
+
const dir = await makeFixture(files);
|
|
76
|
+
const parallel = await createGrepTool(dir, undefined, {
|
|
77
|
+
useExternalScanner: () => false,
|
|
78
|
+
}).execute({ pattern: "marker", max_results: 500 }, context());
|
|
79
|
+
const expected = await sequentialScan(dir, /marker/g);
|
|
80
|
+
expect(String(parallel).split("\n\n")[0]).toBe(expected.join("\n"));
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
/**
|
|
84
|
+
* Reference implementation: one file at a time, candidates in sorted order.
|
|
85
|
+
*
|
|
86
|
+
* fast-glob always yields POSIX-separated paths, while the tool reports
|
|
87
|
+
* `path.relative` output — native separators, so backslashes on Windows.
|
|
88
|
+
* Normalizing here keeps this test about ORDER and CONTENT; asserting the raw
|
|
89
|
+
* glob strings instead silently made it a path-format test that only failed on
|
|
90
|
+
* Windows.
|
|
91
|
+
*/
|
|
92
|
+
async function sequentialScan(dir, regex) {
|
|
93
|
+
const fg = await import("fast-glob");
|
|
94
|
+
const entries = (await fg.default("**/*", { cwd: dir, dot: true, onlyFiles: true, suppressErrors: true }))
|
|
95
|
+
.sort()
|
|
96
|
+
.map((entry) => path.normalize(entry));
|
|
97
|
+
const results = [];
|
|
98
|
+
for (const entry of entries) {
|
|
99
|
+
const rl = readline.createInterface({
|
|
100
|
+
input: createReadStream(path.join(dir, entry), "utf-8"),
|
|
101
|
+
crlfDelay: Infinity,
|
|
102
|
+
});
|
|
103
|
+
let lineNum = 0;
|
|
104
|
+
for await (const line of rl) {
|
|
105
|
+
lineNum++;
|
|
106
|
+
regex.lastIndex = 0;
|
|
107
|
+
if (regex.test(line))
|
|
108
|
+
results.push(`${entry}:${lineNum}:${line}`);
|
|
109
|
+
}
|
|
110
|
+
rl.close();
|
|
111
|
+
}
|
|
112
|
+
return results;
|
|
113
|
+
}
|
|
114
|
+
//# sourceMappingURL=grep-recall.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grep-recall.test.js","sourceRoot":"","sources":["../../src/tools/grep-recall.test.ts"],"names":[],"mappings":"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"}
|