Upgrade candidates (all 12 done): - #1 parallel read-only tool execution (runToolBatch, 4 loop sites) - #2 configurable retry policy (maxRetries + exponential backoff via SDK) - #3 script-aware token estimation (CJK/symbol/structure-aware heuristic) - #4 head+tail output capping (truncate.ts), auto-applies to bash/git/etc - #5 partial-history compaction (preserve recent tail, summarize older prefix) - #6 dynamic max_tokens (resolveMaxTokens) - #7 richer tool descriptions with "use when" guidance - #8 MCP reconnect retry + /mcp reconnect command + session toolset refresh - #9 context-window cache TTL (cachedAt timestamp, default 7 days) - #10 edit_file similar-match suggestion on old_string miss (bounded Levenshtein) - #11 git_status output head+tail (resolved via #4) - #12 auto-accept now approves all mutating tools; auto-edit stays edit-only Other: - DEFAULT_MAX_ITERATIONS 50 -> 100 (local models issue one tool call per step) - MaxIterationsError message guides resume + config override - plus prior known-issues work (grep -e/--, session id sanitization, MCP content types, 12 hook events, plugin collisions, skill references, git ops expansion, configurable autoCompactThreshold, bashGuard, pathGuard, FilePanel, replay)
44 lines
1.9 KiB
TypeScript
44 lines
1.9 KiB
TypeScript
import path from "node:path";
|
|
import { rgPath } from "@vscode/ripgrep";
|
|
import { execa } from "execa";
|
|
import { z } from "zod";
|
|
import type { ToolDef } from "./types.js";
|
|
|
|
const schema = z.object({
|
|
pattern: z.string().describe("Regular expression to search for."),
|
|
path: z.string().optional().describe("File or directory to search, relative to the working directory."),
|
|
glob: z.string().optional().describe("Restrict search to files matching this glob, e.g. '*.ts'."),
|
|
case_insensitive: z.boolean().optional(),
|
|
max_results: z.number().int().min(1).max(500).optional(),
|
|
});
|
|
|
|
export const grepTool: ToolDef<z.infer<typeof schema>> = {
|
|
name: "grep",
|
|
description: "Search file contents for a regular expression pattern using ripgrep. Use to find where a symbol/function/word is used across the codebase, or to locate files containing specific text. Faster than reading files one by one.",
|
|
schema,
|
|
mutating: false,
|
|
handler: async ({ pattern, path: searchPath, glob, case_insensitive, max_results }, ctx) => {
|
|
const args = ["--line-number", "--no-heading", "--color", "never"];
|
|
if (case_insensitive) args.push("--ignore-case");
|
|
if (glob) args.push("--glob", glob);
|
|
// Use -e for the pattern and -- before the path so a pattern beginning with "-"
|
|
// (or "--") is never parsed as a ripgrep flag.
|
|
args.push("-e", pattern, "--");
|
|
args.push(searchPath ? path.resolve(ctx.cwd, searchPath) : ctx.cwd);
|
|
|
|
try {
|
|
const { stdout } = await execa(rgPath, args);
|
|
const lines = stdout.split("\n").filter(Boolean);
|
|
const limit = max_results ?? 100;
|
|
return { matches: lines.slice(0, limit), truncated: lines.length > limit };
|
|
} catch (err) {
|
|
const exitCode = (err as { exitCode?: number }).exitCode;
|
|
if (exitCode === 1) {
|
|
// ripgrep exit code 1 means "no matches found", not an error
|
|
return { matches: [], truncated: false };
|
|
}
|
|
throw err;
|
|
}
|
|
},
|
|
};
|