@kenkaiiii/ggcoder 5.38.0 → 5.39.1

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 (102) hide show
  1. package/dist/core/agent-session-tool-tiers.test.d.ts +2 -0
  2. package/dist/core/agent-session-tool-tiers.test.d.ts.map +1 -0
  3. package/dist/core/agent-session-tool-tiers.test.js +138 -0
  4. package/dist/core/agent-session-tool-tiers.test.js.map +1 -0
  5. package/dist/core/agent-session.d.ts +19 -3
  6. package/dist/core/agent-session.d.ts.map +1 -1
  7. package/dist/core/agent-session.js +47 -6
  8. package/dist/core/agent-session.js.map +1 -1
  9. package/dist/core/code-retrieval-chunkers.d.ts +27 -0
  10. package/dist/core/code-retrieval-chunkers.d.ts.map +1 -0
  11. package/dist/core/code-retrieval-chunkers.js +264 -0
  12. package/dist/core/code-retrieval-chunkers.js.map +1 -0
  13. package/dist/core/code-retrieval-chunkers.test.d.ts +2 -0
  14. package/dist/core/code-retrieval-chunkers.test.d.ts.map +1 -0
  15. package/dist/core/code-retrieval-chunkers.test.js +203 -0
  16. package/dist/core/code-retrieval-chunkers.test.js.map +1 -0
  17. package/dist/core/code-retrieval.d.ts +6 -0
  18. package/dist/core/code-retrieval.d.ts.map +1 -1
  19. package/dist/core/code-retrieval.js +38 -1
  20. package/dist/core/code-retrieval.js.map +1 -1
  21. package/dist/core/lsp/client-navigation.test.d.ts +2 -0
  22. package/dist/core/lsp/client-navigation.test.d.ts.map +1 -0
  23. package/dist/core/lsp/client-navigation.test.js +135 -0
  24. package/dist/core/lsp/client-navigation.test.js.map +1 -0
  25. package/dist/core/lsp/client.d.ts +69 -0
  26. package/dist/core/lsp/client.d.ts.map +1 -1
  27. package/dist/core/lsp/client.js +149 -0
  28. package/dist/core/lsp/client.js.map +1 -1
  29. package/dist/core/lsp/manager-navigation.test.d.ts +2 -0
  30. package/dist/core/lsp/manager-navigation.test.d.ts.map +1 -0
  31. package/dist/core/lsp/manager-navigation.test.js +121 -0
  32. package/dist/core/lsp/manager-navigation.test.js.map +1 -0
  33. package/dist/core/lsp/manager.d.ts +35 -1
  34. package/dist/core/lsp/manager.d.ts.map +1 -1
  35. package/dist/core/lsp/manager.js +92 -0
  36. package/dist/core/lsp/manager.js.map +1 -1
  37. package/dist/core/mcp/deferred-catalog.d.ts +1 -1
  38. package/dist/core/mcp/deferred-catalog.js +1 -1
  39. package/dist/core/settings-manager.d.ts +2 -0
  40. package/dist/core/settings-manager.d.ts.map +1 -1
  41. package/dist/core/settings-manager.js +10 -1
  42. package/dist/core/settings-manager.js.map +1 -1
  43. package/dist/system-prompt.d.ts +6 -1
  44. package/dist/system-prompt.d.ts.map +1 -1
  45. package/dist/system-prompt.js +28 -5
  46. package/dist/system-prompt.js.map +1 -1
  47. package/dist/system-prompt.tiering.test.d.ts +2 -0
  48. package/dist/system-prompt.tiering.test.d.ts.map +1 -0
  49. package/dist/system-prompt.tiering.test.js +103 -0
  50. package/dist/system-prompt.tiering.test.js.map +1 -0
  51. package/dist/tools/code-nav.d.ts +30 -0
  52. package/dist/tools/code-nav.d.ts.map +1 -0
  53. package/dist/tools/code-nav.js +320 -0
  54. package/dist/tools/code-nav.js.map +1 -0
  55. package/dist/tools/code-nav.test.d.ts +2 -0
  56. package/dist/tools/code-nav.test.d.ts.map +1 -0
  57. package/dist/tools/code-nav.test.js +136 -0
  58. package/dist/tools/code-nav.test.js.map +1 -0
  59. package/dist/tools/grep-parity.test.d.ts +2 -0
  60. package/dist/tools/grep-parity.test.d.ts.map +1 -0
  61. package/dist/tools/grep-parity.test.js +92 -0
  62. package/dist/tools/grep-parity.test.js.map +1 -0
  63. package/dist/tools/grep-recall.test.d.ts +2 -0
  64. package/dist/tools/grep-recall.test.d.ts.map +1 -0
  65. package/dist/tools/grep-recall.test.js +114 -0
  66. package/dist/tools/grep-recall.test.js.map +1 -0
  67. package/dist/tools/grep-spawn.test.d.ts +2 -0
  68. package/dist/tools/grep-spawn.test.d.ts.map +1 -0
  69. package/dist/tools/grep-spawn.test.js +66 -0
  70. package/dist/tools/grep-spawn.test.js.map +1 -0
  71. package/dist/tools/grep.d.ts +13 -3
  72. package/dist/tools/grep.d.ts.map +1 -1
  73. package/dist/tools/grep.js +340 -40
  74. package/dist/tools/grep.js.map +1 -1
  75. package/dist/tools/grep.test.js +11 -8
  76. package/dist/tools/grep.test.js.map +1 -1
  77. package/dist/tools/index.d.ts +6 -0
  78. package/dist/tools/index.d.ts.map +1 -1
  79. package/dist/tools/index.js +4 -1
  80. package/dist/tools/index.js.map +1 -1
  81. package/dist/tools/prompt-hints.d.ts.map +1 -1
  82. package/dist/tools/prompt-hints.js +23 -8
  83. package/dist/tools/prompt-hints.js.map +1 -1
  84. package/dist/tools/search-code.d.ts.map +1 -1
  85. package/dist/tools/search-code.js +7 -6
  86. package/dist/tools/search-code.js.map +1 -1
  87. package/dist/tools/search-code.test.js +8 -2
  88. package/dist/tools/search-code.test.js.map +1 -1
  89. package/dist/tools/tool-search.d.ts.map +1 -1
  90. package/dist/tools/tool-search.js +2 -1
  91. package/dist/tools/tool-search.js.map +1 -1
  92. package/dist/tools/tool-tiers.d.ts +42 -0
  93. package/dist/tools/tool-tiers.d.ts.map +1 -0
  94. package/dist/tools/tool-tiers.js +77 -0
  95. package/dist/tools/tool-tiers.js.map +1 -0
  96. package/dist/tools/tool-tiers.test.d.ts +2 -0
  97. package/dist/tools/tool-tiers.test.d.ts.map +1 -0
  98. package/dist/tools/tool-tiers.test.js +48 -0
  99. package/dist/tools/tool-tiers.test.js.map +1 -0
  100. package/dist/ui/hooks/useAgentLoop.js +1 -1
  101. package/dist/ui/hooks/useAgentLoop.js.map +1 -1
  102. package/package.json +4 -4
@@ -0,0 +1,320 @@
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. Optional when `symbol` is given; unused by `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. Enough on its own for definition/references/hover — no `line` needed. Filters the `symbols` outline."),
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
+ /**
137
+ * First line where `symbol` appears as a whole word, preferring real code.
138
+ *
139
+ * Comment lines are skipped: a doc comment mentioning the name carries no
140
+ * resolvable symbol, so the server answers nothing there and the caller would
141
+ * read that empty reply as "no references". A comment-only hit is still
142
+ * returned as a last resort, which beats claiming the name is absent.
143
+ */
144
+ function findSymbolLine(lines, symbol) {
145
+ const word = new RegExp(`\\b${symbol.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\b`);
146
+ let commentHit;
147
+ for (let i = 0; i < lines.length; i++) {
148
+ if (!word.test(lines[i]))
149
+ continue;
150
+ const trimmed = lines[i].trim();
151
+ if (/^(\/\/|\/\*|\*|#)/.test(trimmed)) {
152
+ commentHit ??= i + 1;
153
+ continue;
154
+ }
155
+ return i + 1;
156
+ }
157
+ return commentHit;
158
+ }
159
+ function resolvePosition(lines, line, column, symbol) {
160
+ if (line === undefined) {
161
+ // You almost always know the NAME and not the line. Requiring both forced a
162
+ // grep round-trip before every single call, so `symbol` alone is enough:
163
+ // any occurrence resolves, since the server walks from a reference to its
164
+ // declaration anyway.
165
+ if (!symbol) {
166
+ return 'Pass `symbol` (its name) or `line` (1-based) so I know what to resolve. op: "symbols" needs neither.';
167
+ }
168
+ const found = findSymbolLine(lines, symbol);
169
+ if (found === undefined) {
170
+ return `\`${symbol}\` does not appear in this file. Check the name, or pass \`line\` directly.`;
171
+ }
172
+ line = found;
173
+ }
174
+ if (line > lines.length) {
175
+ return `Line ${line} is past the end of the file (${lines.length} lines).`;
176
+ }
177
+ const text = lines[line - 1] ?? "";
178
+ if (column !== undefined)
179
+ return { line: line - 1, character: column - 1 };
180
+ if (symbol) {
181
+ const index = text.indexOf(symbol);
182
+ if (index === -1) {
183
+ return `\`${symbol}\` does not appear on line ${line}. Pass \`column\`, or the line where it does appear.`;
184
+ }
185
+ return { line: line - 1, character: index };
186
+ }
187
+ // No column and no name: aim at the first non-whitespace character, which is
188
+ // the identifier far more often than column 1 is.
189
+ const firstNonSpace = text.search(/\S/);
190
+ return { line: line - 1, character: firstNonSpace === -1 ? 0 : firstNonSpace };
191
+ }
192
+ function describeSite(cwd, absolute, position) {
193
+ return `${toPosixPath(path.relative(cwd, absolute))}:${position.line + 1}:${position.character + 1}`;
194
+ }
195
+ /**
196
+ * Render a non-`ok` outcome as an instruction, not an absence. "No references
197
+ * found" and "no language server installed" are opposite facts, and conflating
198
+ * them makes the model confidently delete live code.
199
+ */
200
+ function describeFailure(outcome, cwd, op) {
201
+ const rel = toPosixPath(path.relative(cwd, outcome.filePath)) || outcome.filePath;
202
+ switch (outcome.kind) {
203
+ case "unsupported":
204
+ return outcome.serverId
205
+ ? `The ${outcome.serverId} language server does not implement \`${op}\`. Fall back to code_search or grep.`
206
+ : `No language server is configured for ${rel}. Fall back to code_search or grep.`;
207
+ case "unavailable":
208
+ 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.`;
209
+ case "timeout":
210
+ return `The language server did not answer \`${op}\` for ${rel} in time. It may still be indexing — retry once, then fall back to grep.`;
211
+ default:
212
+ return `The language server failed on \`${op}\` for ${rel}${outcome.message ? `: ${outcome.message}` : ""}. Fall back to code_search or grep.`;
213
+ }
214
+ }
215
+ /**
216
+ * Ancestor kinds that make a nested symbol part of a file's STRUCTURE rather
217
+ * than an implementation detail: a method inside a class belongs in an outline,
218
+ * a `const` inside a function body does not.
219
+ */
220
+ const STRUCTURAL_CONTAINER_KINDS = new Set([
221
+ 1, // file
222
+ 2, // module
223
+ 3, // namespace
224
+ 4, // package
225
+ 5, // class
226
+ 10, // enum
227
+ 11, // interface
228
+ 23, // struct
229
+ ]);
230
+ /**
231
+ * Is this symbol part of the file's outline, or noise from inside a body?
232
+ *
233
+ * Language servers return the FULL symbol tree, so an unfiltered outline buries
234
+ * the eight declarations you wanted under every loop counter, local `const` and
235
+ * nested object-literal key in the file. Measured on a real 300-line source
236
+ * file: 89 symbols, with the truncation limit reached before the last real
237
+ * function was ever printed — the outline actively hid what it exists to show.
238
+ *
239
+ * A symbol survives only when every ancestor is structural. Servers that send
240
+ * flat `SymbolInformation` report no ancestor kinds at all; those pass through,
241
+ * since that shape is already a top-level list.
242
+ */
243
+ function isOutlineSymbol(entry) {
244
+ return entry.containerKinds.every((kind) => STRUCTURAL_CONTAINER_KINDS.has(kind));
245
+ }
246
+ function formatSymbols(symbols, filter, maxResults) {
247
+ // Document order, not server order. An outline is read against the file, but
248
+ // tsserver groups its reply by name, so line numbers arrived scrambled
249
+ // (22, 63, 51, 78…) and the outline could not be followed top to bottom.
250
+ const outline = symbols
251
+ .filter(isOutlineSymbol)
252
+ .slice()
253
+ .sort((a, b) => a.range.start.line - b.range.start.line ||
254
+ a.range.start.character - b.range.start.character);
255
+ const matched = filter
256
+ ? outline.filter((s) => s.name.toLowerCase().includes(filter.toLowerCase()))
257
+ : outline;
258
+ if (matched.length === 0) {
259
+ return filter ? `No symbol matching \`${filter}\` in this file.` : "No symbols in this file.";
260
+ }
261
+ const shown = matched.slice(0, maxResults);
262
+ const lines = shown.map((entry) => {
263
+ const qualified = [...entry.containers, entry.name].join(".");
264
+ const kind = SYMBOL_KIND_NAMES[entry.kind] ?? "symbol";
265
+ const detail = entry.detail ? ` ${entry.detail}` : "";
266
+ return `${entry.range.start.line + 1}:${entry.range.start.character + 1} — ${kind} ${qualified}${detail}`;
267
+ });
268
+ if (matched.length > shown.length) {
269
+ lines.push(`… ${matched.length - shown.length} more symbol(s) not shown`);
270
+ }
271
+ return lines.join("\n");
272
+ }
273
+ async function formatLocations(locations, cwd, ops, maxResults, op, originFile, position) {
274
+ if (locations.length === 0) {
275
+ return op === "definition"
276
+ ? `No definition found for the symbol at ${describeSite(cwd, originFile, position)}.`
277
+ : `No references found for the symbol at ${describeSite(cwd, originFile, position)}.`;
278
+ }
279
+ const shown = locations.slice(0, maxResults);
280
+ // One read per distinct file, not per location: a hot symbol produces many
281
+ // hits in the same file and re-reading it per hit is the whole cost.
282
+ const sourceCache = new Map();
283
+ const lines = [];
284
+ for (const location of shown) {
285
+ const absolute = fileFromUri(location.uri);
286
+ const rel = toPosixPath(path.relative(cwd, absolute)) || absolute;
287
+ let source = sourceCache.get(absolute);
288
+ if (source === undefined) {
289
+ try {
290
+ source = (await ops.readFile(absolute)).split("\n");
291
+ }
292
+ catch {
293
+ source = null;
294
+ }
295
+ sourceCache.set(absolute, source);
296
+ }
297
+ const snippetRaw = source?.[location.range.start.line]?.trim() ?? "";
298
+ const snippet = snippetRaw.length > MAX_SNIPPET_LENGTH
299
+ ? `${snippetRaw.slice(0, MAX_SNIPPET_LENGTH)}…`
300
+ : snippetRaw;
301
+ const site = `${rel}:${location.range.start.line + 1}:${location.range.start.character + 1}`;
302
+ lines.push(snippet ? `${site} — ${snippet}` : site);
303
+ }
304
+ if (locations.length > shown.length) {
305
+ lines.push(`… ${locations.length - shown.length} more result(s) not shown — raise max_results to see them`);
306
+ }
307
+ return lines.join("\n");
308
+ }
309
+ /** `file://` URI → filesystem path, tolerating servers that send a bare path. */
310
+ function fileFromUri(uri) {
311
+ if (!uri.startsWith("file://"))
312
+ return uri;
313
+ try {
314
+ return fileURLToPath(uri);
315
+ }
316
+ catch {
317
+ return decodeURIComponent(uri.replace(/^file:\/\//, ""));
318
+ }
319
+ }
320
+ //# 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=code-nav.test.d.ts.map
@@ -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,136 @@
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("lists the outline in document order, not the server's order", async () => {
61
+ const result = String(await tool().execute({ op: "symbols", file: "widget.fake" }, context()));
62
+ const lines = result.split("\n");
63
+ const lineNumbers = lines.map((l) => Number(l.split(":")[0]));
64
+ // The fixture answers grouped by name (helper before Widget) exactly as
65
+ // tsserver does; the outline has to be readable against the file instead.
66
+ expect(lineNumbers).toEqual([...lineNumbers].sort((a, b) => a - b));
67
+ expect(lines[0]).toContain("Widget");
68
+ });
69
+ it("keeps class members but drops locals from inside function bodies", async () => {
70
+ const result = String(await tool().execute({ op: "symbols", file: "widget.fake" }, context()));
71
+ expect(result).toContain("method Widget.render");
72
+ expect(result).toContain("function helper");
73
+ // `tmp` and `i` live inside helper's body — noise, not structure.
74
+ expect(result).not.toContain("helper.tmp");
75
+ expect(result).not.toContain("helper.i");
76
+ });
77
+ it("filters the outline by symbol name", async () => {
78
+ const result = String(await tool().execute({ op: "symbols", file: "widget.fake", symbol: "render" }, context()));
79
+ expect(result).toContain("render");
80
+ expect(result).not.toContain("class Widget\n");
81
+ });
82
+ it("returns hover text", async () => {
83
+ const result = String(await tool().execute({ op: "hover", file: "widget.fake", line: 4, symbol: "widget" }, context()));
84
+ expect(result).toContain("const widget: Widget");
85
+ });
86
+ it("caps results and says how many were withheld", async () => {
87
+ const result = String(await tool().execute({ op: "references", file: "widget.fake", line: 1, symbol: "Widget", max_results: 1 }, context()));
88
+ expect(result).toContain("widget.fake:1:7");
89
+ expect(result).toContain("1 more result(s) not shown");
90
+ });
91
+ it("says a server is missing instead of reporting no results", async () => {
92
+ const result = String(await tool({ ...fakeSpec(), resolveCommand: () => null }).execute({ op: "references", file: "widget.fake", line: 1, symbol: "Widget" }, context()));
93
+ expect(result).toContain("No language server is installed");
94
+ expect(result).not.toContain("No references found");
95
+ });
96
+ it("says the capability is missing when the server lacks it", async () => {
97
+ const result = String(await tool(fakeSpec(["--no-nav"])).execute({ op: "definition", file: "widget.fake", line: 1, symbol: "Widget" }, context()));
98
+ expect(result).toContain("does not implement `definition`");
99
+ });
100
+ it("says it timed out rather than returning nothing", async () => {
101
+ const result = String(await tool(fakeSpec(["--nav-silent"]), 200).execute({ op: "references", file: "widget.fake", line: 1, symbol: "Widget" }, context()));
102
+ expect(result).toContain("did not answer");
103
+ });
104
+ it("reports an unsupported file type", async () => {
105
+ await fs.writeFile(path.join(tmpDir, "notes.unknown"), "text\n");
106
+ const result = String(await tool().execute({ op: "symbols", file: "notes.unknown" }, context()));
107
+ expect(result).toContain("No language server is configured");
108
+ });
109
+ it("asks for a symbol or a line when given neither", async () => {
110
+ const result = String(await tool().execute({ op: "hover", file: "widget.fake" }, context()));
111
+ expect(result).toContain("Pass `symbol`");
112
+ });
113
+ it("resolves from the symbol name alone, with no line", async () => {
114
+ // The common case: you know what it is called, not where it sits. Needing
115
+ // both forced a grep round-trip before every call.
116
+ const result = String(await tool().execute({ op: "definition", file: "widget.fake", symbol: "Widget" }, context()));
117
+ expect(result).toBe("widget.fake:1:7 — class Widget {");
118
+ });
119
+ it("says so plainly when the symbol is nowhere in the file", async () => {
120
+ const result = String(await tool().execute({ op: "references", file: "widget.fake", symbol: "nonexistentSymbol" }, context()));
121
+ expect(result).toContain("does not appear in this file");
122
+ });
123
+ it("rejects a symbol that is not on the given line", async () => {
124
+ const result = String(await tool().execute({ op: "definition", file: "widget.fake", line: 2, symbol: "Widget" }, context()));
125
+ expect(result).toContain("does not appear on line 2");
126
+ });
127
+ it("reports a missing file instead of throwing", async () => {
128
+ const result = String(await tool().execute({ op: "symbols", file: "nope.fake" }, context()));
129
+ expect(result).toContain("Cannot read nope.fake");
130
+ });
131
+ it("states plainly when language-server support is off", async () => {
132
+ const result = String(await createCodeNavTool(tmpDir, undefined).execute({ op: "symbols", file: "widget.fake" }, context()));
133
+ expect(result).toContain("language-server support is disabled");
134
+ });
135
+ });
136
+ //# 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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=grep-parity.test.d.ts.map
@@ -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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=grep-recall.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grep-recall.test.d.ts","sourceRoot":"","sources":["../../src/tools/grep-recall.test.ts"],"names":[],"mappings":""}