Big News: Socket raises $60M Series C at a $1B valuation to secure software supply chains for AI-driven development.Announcement
Sign In

agentool

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

agentool - npm Package Compare versions

Comparing version
1.4.0
to
1.4.1
+120
dist/chunk-4KNQPKGH.cjs
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/shared/ripgrep.ts
var _child_process = require('child_process');
var MAX_BUFFER_SIZE = 2e7;
var DEFAULT_TIMEOUT = 2e4;
var INSTALL_URL = "https://github.com/BurntSushi/ripgrep#installation";
var RipgrepNotFoundError = class extends Error {
constructor(message) {
super(
_nullishCoalesce(message, () => ( `ripgrep (rg) not found on PATH. Install it from: ${INSTALL_URL}`))
);
this.name = "RipgrepNotFoundError";
}
};
var RipgrepTimeoutError = class extends Error {
/** Lines captured from stdout before the process was killed. */
constructor(message, partialResults) {
super(message);
this.name = "RipgrepTimeoutError";
this.partialResults = partialResults;
}
};
function findRg() {
try {
const result = _child_process.execFileSync.call(void 0, "which", ["rg"], {
encoding: "utf-8",
timeout: 5e3
});
const rgPath = result.trim();
if (!rgPath) {
throw new RipgrepNotFoundError();
}
return rgPath;
} catch (error) {
if (error instanceof RipgrepNotFoundError) {
throw error;
}
throw new RipgrepNotFoundError();
}
}
function isEagainError(stderr) {
return stderr.includes("os error 11") || stderr.includes("Resource temporarily unavailable");
}
function parseStdout(stdout) {
return stdout.trim().split("\n").map((line) => line.replace(/\r$/, "")).filter(Boolean);
}
async function executeRipgrep(args, target, options) {
const rgPath = findRg();
const timeout = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.timeout]), () => ( DEFAULT_TIMEOUT));
return new Promise((resolve, reject) => {
const handleResult = (error, stdout, stderr, isRetry) => {
if (!error) {
resolve(parseStdout(stdout));
return;
}
if (error.code === 1) {
resolve([]);
return;
}
const CRITICAL_CODES = ["ENOENT", "EACCES", "EPERM"];
if (CRITICAL_CODES.includes(error.code)) {
reject(error);
return;
}
if (!isRetry && isEagainError(stderr)) {
_child_process.execFile.call(void 0,
rgPath,
["-j", "1", ...args, target],
{
maxBuffer: MAX_BUFFER_SIZE,
signal: _optionalChain([options, 'optionalAccess', _2 => _2.signal]),
timeout,
killSignal: "SIGKILL"
},
(retryErr, retryStdout, retryStderr) => {
handleResult(retryErr, retryStdout, retryStderr, true);
}
);
return;
}
const hasOutput = stdout && stdout.trim().length > 0;
const isTimeout = error.signal === "SIGTERM" || error.signal === "SIGKILL" || error.code === "ABORT_ERR";
const isBufferOverflow = error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
let lines = [];
if (hasOutput) {
lines = parseStdout(stdout);
if (lines.length > 0 && (isTimeout || isBufferOverflow)) {
lines = lines.slice(0, -1);
}
}
if (isTimeout && lines.length === 0) {
reject(
new RipgrepTimeoutError(
`Ripgrep search timed out after ${timeout / 1e3} seconds. Try a more specific path or pattern.`,
lines
)
);
return;
}
resolve(lines);
};
_child_process.execFile.call(void 0,
rgPath,
[...args, target],
{
maxBuffer: MAX_BUFFER_SIZE,
signal: _optionalChain([options, 'optionalAccess', _3 => _3.signal]),
timeout,
killSignal: "SIGKILL"
},
(error, stdout, stderr) => {
handleResult(error, stdout, stderr, false);
}
);
});
}
exports.executeRipgrep = executeRipgrep;
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
var _chunk4KNQPKGHcjs = require('./chunk-4KNQPKGH.cjs');
var _chunkQZ5GS6HWcjs = require('./chunk-QZ5GS6HW.cjs');
var _chunkKONXT2SFcjs = require('./chunk-KONXT2SF.cjs');
// src/glob/index.ts
var _ai = require('ai');
var _zod = require('zod');
// src/shared/glob.ts
var _path = require('path');
function extractGlobBaseDirectory(pattern) {
const globChars = /[*?[{]/;
const match = pattern.match(globChars);
if (!match || match.index === void 0) {
const dir = _path.dirname.call(void 0, pattern);
const file = _path.basename.call(void 0, pattern);
return { baseDir: dir, relativePattern: file };
}
const staticPrefix = pattern.slice(0, match.index);
const lastSepIndex = Math.max(
staticPrefix.lastIndexOf("/"),
staticPrefix.lastIndexOf(_path.sep)
);
if (lastSepIndex === -1) {
return { baseDir: "", relativePattern: pattern };
}
let baseDir = staticPrefix.slice(0, lastSepIndex);
const relativePattern = pattern.slice(lastSepIndex + 1);
if (baseDir === "" && lastSepIndex === 0) {
baseDir = "/";
}
return { baseDir, relativePattern };
}
async function glob(pattern, cwd, options) {
const limit = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.limit]), () => ( 100));
const offset = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.offset]), () => ( 0));
let searchDir = cwd;
let searchPattern = pattern;
if (_path.isAbsolute.call(void 0, pattern)) {
const { baseDir, relativePattern } = extractGlobBaseDirectory(pattern);
if (baseDir) {
searchDir = baseDir;
searchPattern = relativePattern;
}
}
const args = [
"--files",
"--glob",
searchPattern,
"--sort=modified",
"--hidden",
"--no-ignore"
];
const allPaths = await _chunk4KNQPKGHcjs.executeRipgrep.call(void 0, args, searchDir, {
signal: _optionalChain([options, 'optionalAccess', _3 => _3.signal])
});
const absolutePaths = allPaths.map(
(p) => _path.isAbsolute.call(void 0, p) ? p : _path.join.call(void 0, searchDir, p)
);
const truncated = absolutePaths.length > offset + limit;
const files = absolutePaths.slice(offset, offset + limit);
return { files, truncated };
}
// src/glob/prompt.ts
function getPrompt() {
return `Fast file pattern matching tool that works with any codebase size.
- Supports glob patterns like "**/*.js" or "src/**/*.ts"
- Returns matching file paths sorted by modification time
- Use this tool when you need to find files by name patterns
- Use the content search tool when you need to search inside files`;
}
// src/glob/index.ts
function createGlob(config = {}) {
const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
const pathStyle = _nullishCoalesce(config.pathStyle, () => ( "relative"));
return _ai.tool.call(void 0, {
description: _nullishCoalesce(config.description, () => ( getPrompt())),
inputSchema: _zod.z.object({
pattern: _zod.z.string().describe("Glob pattern to match files against"),
path: _zod.z.string().optional().describe("Directory to search in. Defaults to the working directory.")
}),
execute: async ({ pattern, path }) => {
try {
const searchDir = path ? _chunkQZ5GS6HWcjs.expandPath.call(void 0, path, cwd) : cwd;
const { files, truncated } = await glob(pattern, searchDir);
if (files.length === 0) {
return "No files found";
}
const header = truncated ? `Found ${files.length}+ files (results truncated)` : `Found ${files.length} files`;
const outputFiles = pathStyle === "absolute" ? files : files.map((file) => _chunkQZ5GS6HWcjs.toRelativePath.call(void 0, file, cwd));
return `${header}
${outputFiles.join("\n")}`;
} catch (error) {
const message = _chunkKONXT2SFcjs.extractErrorMessage.call(void 0, error);
return `Error [glob]: Failed to search for files: ${message}`;
}
}
});
}
var glob2 = createGlob();
exports.getPrompt = getPrompt; exports.createGlob = createGlob; exports.glob = glob2;
import {
executeRipgrep
} from "./chunk-XNTMPBZJ.js";
import {
getFileStats
} from "./chunk-PWBVB6MN.js";
import {
expandPath,
toRelativePath
} from "./chunk-I3ONDY7P.js";
import {
extractErrorMessage
} from "./chunk-X6ZY2KFU.js";
// src/grep/index.ts
import { tool } from "ai";
import { z } from "zod";
// src/grep/prompt.ts
function getPrompt() {
return `Search file contents using ripgrep.
Usage:
- Use this tool for search tasks instead of shell grep or rg
- Supports ripgrep regex syntax, e.g. "log.*Error" or "function\\s+\\w+"
- Filter files with glob (e.g. "*.js", "**/*.tsx") or type (e.g. "js", "py", "rust")
- output_mode: "files_with_matches" returns paths (default), "content" returns matching lines, "count" returns match counts
- Default head_limit is 250 entries. Pass head_limit: 0 for unlimited
- Use offset to paginate large result sets
- For cross-line patterns like \`struct \\{[\\s\\S]*?field\`, use multiline: true`;
}
// src/grep/index.ts
var VCS_DIRS = [".git", ".svn", ".hg", ".bzr", ".jj", ".sl"];
var DEFAULT_HEAD_LIMIT = 250;
function applyHeadLimit(items, limit, offset = 0) {
if (limit === 0) return { items: items.slice(offset), appliedLimit: void 0 };
const cap = limit ?? DEFAULT_HEAD_LIMIT;
const sliced = items.slice(offset, offset + cap);
const truncated = items.length - offset > cap;
return { items: sliced, appliedLimit: truncated ? cap : void 0 };
}
function truncationSuffix(appliedLimit, offset) {
const parts = [];
if (appliedLimit !== void 0) parts.push(`limit: ${appliedLimit}`);
if (offset > 0) parts.push(`offset: ${offset}`);
return parts.length > 0 ? `
[Results truncated. ${parts.join(", ")}]` : "";
}
function relativizeLine(line, baseCwd, last = false) {
const idx = last ? line.lastIndexOf(":") : line.indexOf(":");
if (idx > 0) {
return toRelativePath(line.substring(0, idx), baseCwd) + line.substring(idx);
}
return line;
}
function parseGlobPatterns(globFilter) {
const patterns = [];
for (const raw of globFilter.split(/\s+/)) {
if (raw.includes("{") && raw.includes("}")) {
patterns.push(raw);
} else {
patterns.push(...raw.split(",").filter(Boolean));
}
}
return patterns;
}
function createGrep(config = {}) {
const cwd = config.cwd ?? process.cwd();
return tool({
description: config.description ?? getPrompt(),
inputSchema: z.object({
pattern: z.string().describe("The regular expression pattern to search for in file contents"),
path: z.string().optional().describe("File or directory to search in (rg PATH). Defaults to current working directory."),
glob: z.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}") - maps to rg --glob'),
output_mode: z.enum(["content", "files_with_matches", "count"]).optional().describe('Output mode: "content" shows matching lines (supports -A/-B/-C context, -n line numbers, head_limit), "files_with_matches" shows file paths (supports head_limit), "count" shows match counts (supports head_limit). Defaults to "files_with_matches".'),
"-B": z.number().optional().describe('Number of lines to show before each match (rg -B). Requires output_mode: "content", ignored otherwise.'),
"-A": z.number().optional().describe('Number of lines to show after each match (rg -A). Requires output_mode: "content", ignored otherwise.'),
"-C": z.number().optional().describe("Alias for context."),
context: z.number().optional().describe('Number of lines to show before and after each match (rg -C). Requires output_mode: "content", ignored otherwise.'),
"-n": z.boolean().optional().describe('Show line numbers in output (rg -n). Requires output_mode: "content", ignored otherwise. Defaults to true.'),
"-i": z.boolean().optional().describe("Case insensitive search (rg -i)"),
type: z.string().optional().describe("File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types."),
head_limit: z.number().optional().describe('Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 250 when unspecified. Pass 0 for unlimited (use sparingly \u2014 large result sets waste context).'),
offset: z.number().optional().describe('Skip first N lines/entries before applying head_limit, equivalent to "| tail -n +N | head -N". Works across all output modes. Defaults to 0.'),
multiline: z.boolean().optional().describe("Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.")
}),
execute: async (input) => {
try {
const {
pattern,
path,
glob: globFilter,
type: typeFilter,
output_mode: outputMode = "files_with_matches",
"-B": ctxBefore,
"-A": ctxAfter,
"-C": ctxC,
context: ctxAlias,
"-n": showLineNumbers = true,
"-i": caseInsensitive = false,
head_limit: headLimit,
offset = 0,
multiline = false
} = input;
const absolutePath = path ? expandPath(path, cwd) : cwd;
const args = ["--hidden"];
for (const dir of VCS_DIRS) args.push("--glob", `!${dir}`);
args.push("--max-columns", "500");
if (multiline) args.push("-U", "--multiline-dotall");
if (caseInsensitive) args.push("-i");
if (outputMode === "files_with_matches") args.push("-l");
else if (outputMode === "count") args.push("-c");
if (showLineNumbers && outputMode === "content") args.push("-n");
if (outputMode === "content") {
if (ctxAlias !== void 0) args.push("-C", ctxAlias.toString());
else if (ctxC !== void 0) args.push("-C", ctxC.toString());
else {
if (ctxBefore !== void 0) args.push("-B", ctxBefore.toString());
if (ctxAfter !== void 0) args.push("-A", ctxAfter.toString());
}
}
if (pattern.startsWith("-")) args.push("-e", pattern);
else args.push(pattern);
if (typeFilter) args.push("--type", typeFilter);
if (globFilter) {
for (const gp of parseGlobPatterns(globFilter)) args.push("--glob", gp);
}
const results = await executeRipgrep(args, absolutePath);
if (results.length === 0) return "No matches found";
if (outputMode === "content") {
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
const lines = items2.map((l) => relativizeLine(l, cwd));
return lines.join("\n") + truncationSuffix(appliedLimit2, offset);
}
if (outputMode === "count") {
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
const lines = items2.map((l) => relativizeLine(l, cwd, true));
let totalMatches = 0;
let fileCount = 0;
for (const line of lines) {
const idx = line.lastIndexOf(":");
if (idx > 0) {
const n = parseInt(line.substring(idx + 1), 10);
if (!isNaN(n)) {
totalMatches += n;
fileCount += 1;
}
}
}
return lines.join("\n") + `
Total: ${totalMatches} matches in ${fileCount} files` + truncationSuffix(appliedLimit2, offset);
}
const stats = await Promise.allSettled(results.map((f) => getFileStats(f)));
const sorted = results.map((fp, i) => {
const r = stats[i];
const mt = r.status === "fulfilled" ? r.value.mtimeMs ?? 0 : 0;
return [fp, mt];
}).sort((a, b) => {
const d = b[1] - a[1];
return d !== 0 ? d : a[0].localeCompare(b[0]);
}).map((e) => e[0]);
const { items, appliedLimit } = applyHeadLimit(sorted, headLimit, offset);
const relative = items.map((f) => toRelativePath(f, cwd));
return relative.join("\n") + truncationSuffix(appliedLimit, offset);
} catch (error) {
const msg = extractErrorMessage(error);
return `Error [grep]: Failed to search: ${msg}`;
}
}
});
}
var grep = createGrep();
export {
getPrompt,
createGrep,
grep
};
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
var _chunkKONXT2SFcjs = require('./chunk-KONXT2SF.cjs');
// src/bash/index.ts
var _ai = require('ai');
var _zod = require('zod');
// src/shared/shell.ts
var _child_process = require('child_process');
var MAX_BUFFER_BYTES = 10 * 1024 * 1024;
var DEFAULT_SHELL_OUTPUT_CHARS = 3e4;
var MAX_SHELL_OUTPUT_CHARS = 15e4;
var DEFAULT_TIMEOUT_MS = 12e4;
var SIGKILL_GRACE_MS = 5e3;
function resolveShellOutputChars(value) {
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
return DEFAULT_SHELL_OUTPUT_CHARS;
}
return Math.min(Math.floor(value), MAX_SHELL_OUTPUT_CHARS);
}
function truncateShellOutput(output, maxOutputChars = DEFAULT_SHELL_OUTPUT_CHARS) {
if (output.length <= maxOutputChars) {
return output;
}
const omitted = output.length - maxOutputChars;
return `${output.slice(0, maxOutputChars)}
... [output truncated - ${omitted} chars removed]`;
}
async function executeShell(command, options) {
const shellBin = _nullishCoalesce(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.shell]), () => ( process.env.SHELL)), () => ( "/bin/bash"));
const timeout = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.timeout]), () => ( DEFAULT_TIMEOUT_MS));
return new Promise((resolve, reject) => {
const child = _child_process.spawn.call(void 0, shellBin, ["-c", command], {
cwd: _optionalChain([options, 'optionalAccess', _3 => _3.cwd]),
stdio: ["pipe", "pipe", "pipe"],
// Prevent visible console window on Windows (no-op elsewhere)
windowsHide: true
});
let stdout = "";
let stderr = "";
let stdoutBytes = 0;
let stderrBytes = 0;
let settled = false;
let timeoutId;
let graceId;
function cleanup() {
if (timeoutId !== void 0) {
clearTimeout(timeoutId);
timeoutId = void 0;
}
if (graceId !== void 0) {
clearTimeout(graceId);
graceId = void 0;
}
if (abortHandler && _optionalChain([options, 'optionalAccess', _4 => _4.signal])) {
options.signal.removeEventListener("abort", abortHandler);
}
}
function settle(result) {
if (settled) return;
settled = true;
cleanup();
resolve(result);
}
child.stdout.on("data", (chunk) => {
if (stdoutBytes >= MAX_BUFFER_BYTES) return;
const str = chunk.toString();
const remaining = MAX_BUFFER_BYTES - stdoutBytes;
if (chunk.length > remaining) {
stdout += str.slice(0, remaining);
stdoutBytes = MAX_BUFFER_BYTES;
} else {
stdout += str;
stdoutBytes += chunk.length;
}
});
child.stderr.on("data", (chunk) => {
if (stderrBytes >= MAX_BUFFER_BYTES) return;
const str = chunk.toString();
const remaining = MAX_BUFFER_BYTES - stderrBytes;
if (chunk.length > remaining) {
stderr += str.slice(0, remaining);
stderrBytes = MAX_BUFFER_BYTES;
} else {
stderr += str;
stderrBytes += chunk.length;
}
});
child.on("exit", (code, signal) => {
const exitCode = code !== null ? code : signal === "SIGKILL" ? 137 : signal === "SIGTERM" ? 143 : 1;
settle({ stdout, stderr, exitCode });
});
child.on("error", (err) => {
if (settled) return;
settled = true;
cleanup();
reject(err);
});
if (timeout > 0) {
timeoutId = setTimeout(() => {
timeoutId = void 0;
if (settled) return;
child.kill("SIGTERM");
graceId = setTimeout(() => {
graceId = void 0;
if (settled) return;
child.kill("SIGKILL");
}, SIGKILL_GRACE_MS);
}, timeout);
}
const abortHandler = _optionalChain([options, 'optionalAccess', _5 => _5.signal]) ? () => {
if (settled) return;
child.kill("SIGTERM");
} : void 0;
if (abortHandler && _optionalChain([options, 'optionalAccess', _6 => _6.signal])) {
if (options.signal.aborted) {
child.kill("SIGTERM");
} else {
options.signal.addEventListener("abort", abortHandler, { once: true });
}
}
});
}
// src/bash/prompt.ts
function getPrompt(config = {}) {
const timeout = _nullishCoalesce(config.timeout, () => ( 12e4));
const timeoutMin = timeout / 6e4;
const shell = _nullishCoalesce(config.shell, () => ( "$SHELL or /bin/bash"));
const maxOutputChars = resolveShellOutputChars(config.maxOutputChars);
return `Execute a shell command and return its output (stdout, stderr, exit code).
Runs the command in ${shell} with \`-c\`. The working directory persists between calls.
## When to Use
- Build commands, git operations, system administration, installing packages
- Running scripts, compiling code, process management
- Any shell task that doesn't have a dedicated tool available
## When NOT to Use
- Reading file contents \u2014 use the dedicated file reading tool instead
- Searching file contents \u2014 use the dedicated content search tool instead
- Finding files by name/pattern \u2014 use the dedicated file search tool instead
- Editing files \u2014 use the dedicated file editing tool instead
- Writing new files \u2014 use the dedicated file writing tool instead
Prefer dedicated tools over shell equivalents (e.g., don't use cat, head, tail, sed, awk, grep, find, or echo when a dedicated tool exists). Dedicated tools provide better output formatting and permission handling.
## Usage Guidelines
- Default timeout: ${timeout}ms (${timeoutMin} minutes). Override with the timeout parameter.
- Timeout escalation: SIGTERM first, then SIGKILL after 5-second grace period.
- Output returned to the model is capped at ${maxOutputChars} characters. Raw stdout/stderr collection is capped at 10 MB per stream.
- Always quote file paths containing spaces with double quotes.
- When issuing multiple commands:
- Independent commands: make separate tool calls in parallel.
- Sequential with dependency: chain with \`&&\`.
- Sequential ignoring failures: chain with \`;\`.
- Do NOT use newlines to separate commands.
- Avoid unnecessary \`sleep\` commands:
- Don't sleep between commands that can run immediately.
- Don't retry failing commands in a sleep loop \u2014 diagnose the root cause.
- If you must sleep, keep it short (1-5 seconds).
- For git commands:
- Prefer creating new commits rather than amending existing ones.
- Never skip hooks (--no-verify) unless the user explicitly requests it.
- Before destructive operations (reset --hard, push --force), consider safer alternatives.`;
}
// src/bash/index.ts
function createBash(config = {}) {
const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
const timeout = _nullishCoalesce(config.timeout, () => ( 12e4));
const maxOutputChars = resolveShellOutputChars(config.maxOutputChars);
return _ai.tool.call(void 0, {
description: _nullishCoalesce(config.description, () => ( getPrompt(config))),
inputSchema: _zod.z.object({
command: _zod.z.string().describe("The shell command to execute"),
timeout: _zod.z.number().optional().describe("Timeout in milliseconds (default: 120000)"),
description: _zod.z.string().optional().describe("Human-readable description of what the command does")
}),
execute: async ({ command, timeout: cmdTimeout }) => {
try {
const result = await executeShell(command, {
cwd,
timeout: _nullishCoalesce(cmdTimeout, () => ( timeout)),
shell: config.shell
});
const outputParts = [];
if (result.stdout) outputParts.push(result.stdout);
if (result.stderr) outputParts.push(`STDERR:
${result.stderr}`);
const output = truncateShellOutput(
outputParts.join("\n"),
maxOutputChars
);
const parts = [];
if (output) parts.push(output);
if (result.exitCode !== 0) parts.push(`Exit code: ${result.exitCode}`);
return parts.length > 0 ? parts.join("\n") : `Command completed with exit code ${result.exitCode}`;
} catch (error) {
const msg = _chunkKONXT2SFcjs.extractErrorMessage.call(void 0, error);
return `Error [bash]: Failed to execute command: ${msg}`;
}
}
});
}
var bash = createBash();
exports.getPrompt = getPrompt; exports.createBash = createBash; exports.bash = bash;
import {
extractErrorMessage
} from "./chunk-X6ZY2KFU.js";
// src/bash/index.ts
import { tool } from "ai";
import { z } from "zod";
// src/shared/shell.ts
import { spawn } from "child_process";
var MAX_BUFFER_BYTES = 10 * 1024 * 1024;
var DEFAULT_SHELL_OUTPUT_CHARS = 3e4;
var MAX_SHELL_OUTPUT_CHARS = 15e4;
var DEFAULT_TIMEOUT_MS = 12e4;
var SIGKILL_GRACE_MS = 5e3;
function resolveShellOutputChars(value) {
if (value === void 0 || !Number.isFinite(value) || value <= 0) {
return DEFAULT_SHELL_OUTPUT_CHARS;
}
return Math.min(Math.floor(value), MAX_SHELL_OUTPUT_CHARS);
}
function truncateShellOutput(output, maxOutputChars = DEFAULT_SHELL_OUTPUT_CHARS) {
if (output.length <= maxOutputChars) {
return output;
}
const omitted = output.length - maxOutputChars;
return `${output.slice(0, maxOutputChars)}
... [output truncated - ${omitted} chars removed]`;
}
async function executeShell(command, options) {
const shellBin = options?.shell ?? process.env.SHELL ?? "/bin/bash";
const timeout = options?.timeout ?? DEFAULT_TIMEOUT_MS;
return new Promise((resolve, reject) => {
const child = spawn(shellBin, ["-c", command], {
cwd: options?.cwd,
stdio: ["pipe", "pipe", "pipe"],
// Prevent visible console window on Windows (no-op elsewhere)
windowsHide: true
});
let stdout = "";
let stderr = "";
let stdoutBytes = 0;
let stderrBytes = 0;
let settled = false;
let timeoutId;
let graceId;
function cleanup() {
if (timeoutId !== void 0) {
clearTimeout(timeoutId);
timeoutId = void 0;
}
if (graceId !== void 0) {
clearTimeout(graceId);
graceId = void 0;
}
if (abortHandler && options?.signal) {
options.signal.removeEventListener("abort", abortHandler);
}
}
function settle(result) {
if (settled) return;
settled = true;
cleanup();
resolve(result);
}
child.stdout.on("data", (chunk) => {
if (stdoutBytes >= MAX_BUFFER_BYTES) return;
const str = chunk.toString();
const remaining = MAX_BUFFER_BYTES - stdoutBytes;
if (chunk.length > remaining) {
stdout += str.slice(0, remaining);
stdoutBytes = MAX_BUFFER_BYTES;
} else {
stdout += str;
stdoutBytes += chunk.length;
}
});
child.stderr.on("data", (chunk) => {
if (stderrBytes >= MAX_BUFFER_BYTES) return;
const str = chunk.toString();
const remaining = MAX_BUFFER_BYTES - stderrBytes;
if (chunk.length > remaining) {
stderr += str.slice(0, remaining);
stderrBytes = MAX_BUFFER_BYTES;
} else {
stderr += str;
stderrBytes += chunk.length;
}
});
child.on("exit", (code, signal) => {
const exitCode = code !== null ? code : signal === "SIGKILL" ? 137 : signal === "SIGTERM" ? 143 : 1;
settle({ stdout, stderr, exitCode });
});
child.on("error", (err) => {
if (settled) return;
settled = true;
cleanup();
reject(err);
});
if (timeout > 0) {
timeoutId = setTimeout(() => {
timeoutId = void 0;
if (settled) return;
child.kill("SIGTERM");
graceId = setTimeout(() => {
graceId = void 0;
if (settled) return;
child.kill("SIGKILL");
}, SIGKILL_GRACE_MS);
}, timeout);
}
const abortHandler = options?.signal ? () => {
if (settled) return;
child.kill("SIGTERM");
} : void 0;
if (abortHandler && options?.signal) {
if (options.signal.aborted) {
child.kill("SIGTERM");
} else {
options.signal.addEventListener("abort", abortHandler, { once: true });
}
}
});
}
// src/bash/prompt.ts
function getPrompt(config = {}) {
const timeout = config.timeout ?? 12e4;
const timeoutMin = timeout / 6e4;
const shell = config.shell ?? "$SHELL or /bin/bash";
const maxOutputChars = resolveShellOutputChars(config.maxOutputChars);
return `Execute a shell command and return its output (stdout, stderr, exit code).
Runs the command in ${shell} with \`-c\`. The working directory persists between calls.
## When to Use
- Build commands, git operations, system administration, installing packages
- Running scripts, compiling code, process management
- Any shell task that doesn't have a dedicated tool available
## When NOT to Use
- Reading file contents \u2014 use the dedicated file reading tool instead
- Searching file contents \u2014 use the dedicated content search tool instead
- Finding files by name/pattern \u2014 use the dedicated file search tool instead
- Editing files \u2014 use the dedicated file editing tool instead
- Writing new files \u2014 use the dedicated file writing tool instead
Prefer dedicated tools over shell equivalents (e.g., don't use cat, head, tail, sed, awk, grep, find, or echo when a dedicated tool exists). Dedicated tools provide better output formatting and permission handling.
## Usage Guidelines
- Default timeout: ${timeout}ms (${timeoutMin} minutes). Override with the timeout parameter.
- Timeout escalation: SIGTERM first, then SIGKILL after 5-second grace period.
- Output returned to the model is capped at ${maxOutputChars} characters. Raw stdout/stderr collection is capped at 10 MB per stream.
- Always quote file paths containing spaces with double quotes.
- When issuing multiple commands:
- Independent commands: make separate tool calls in parallel.
- Sequential with dependency: chain with \`&&\`.
- Sequential ignoring failures: chain with \`;\`.
- Do NOT use newlines to separate commands.
- Avoid unnecessary \`sleep\` commands:
- Don't sleep between commands that can run immediately.
- Don't retry failing commands in a sleep loop \u2014 diagnose the root cause.
- If you must sleep, keep it short (1-5 seconds).
- For git commands:
- Prefer creating new commits rather than amending existing ones.
- Never skip hooks (--no-verify) unless the user explicitly requests it.
- Before destructive operations (reset --hard, push --force), consider safer alternatives.`;
}
// src/bash/index.ts
function createBash(config = {}) {
const cwd = config.cwd ?? process.cwd();
const timeout = config.timeout ?? 12e4;
const maxOutputChars = resolveShellOutputChars(config.maxOutputChars);
return tool({
description: config.description ?? getPrompt(config),
inputSchema: z.object({
command: z.string().describe("The shell command to execute"),
timeout: z.number().optional().describe("Timeout in milliseconds (default: 120000)"),
description: z.string().optional().describe("Human-readable description of what the command does")
}),
execute: async ({ command, timeout: cmdTimeout }) => {
try {
const result = await executeShell(command, {
cwd,
timeout: cmdTimeout ?? timeout,
shell: config.shell
});
const outputParts = [];
if (result.stdout) outputParts.push(result.stdout);
if (result.stderr) outputParts.push(`STDERR:
${result.stderr}`);
const output = truncateShellOutput(
outputParts.join("\n"),
maxOutputChars
);
const parts = [];
if (output) parts.push(output);
if (result.exitCode !== 0) parts.push(`Exit code: ${result.exitCode}`);
return parts.length > 0 ? parts.join("\n") : `Command completed with exit code ${result.exitCode}`;
} catch (error) {
const msg = extractErrorMessage(error);
return `Error [bash]: Failed to execute command: ${msg}`;
}
}
});
}
var bash = createBash();
export {
getPrompt,
createBash,
bash
};
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
var _chunk4KNQPKGHcjs = require('./chunk-4KNQPKGH.cjs');
var _chunkBIAODQ2Pcjs = require('./chunk-BIAODQ2P.cjs');
var _chunkQZ5GS6HWcjs = require('./chunk-QZ5GS6HW.cjs');
var _chunkKONXT2SFcjs = require('./chunk-KONXT2SF.cjs');
// src/grep/index.ts
var _ai = require('ai');
var _zod = require('zod');
// src/grep/prompt.ts
function getPrompt() {
return `Search file contents using ripgrep.
Usage:
- Use this tool for search tasks instead of shell grep or rg
- Supports ripgrep regex syntax, e.g. "log.*Error" or "function\\s+\\w+"
- Filter files with glob (e.g. "*.js", "**/*.tsx") or type (e.g. "js", "py", "rust")
- output_mode: "files_with_matches" returns paths (default), "content" returns matching lines, "count" returns match counts
- Default head_limit is 250 entries. Pass head_limit: 0 for unlimited
- Use offset to paginate large result sets
- For cross-line patterns like \`struct \\{[\\s\\S]*?field\`, use multiline: true`;
}
// src/grep/index.ts
var VCS_DIRS = [".git", ".svn", ".hg", ".bzr", ".jj", ".sl"];
var DEFAULT_HEAD_LIMIT = 250;
function applyHeadLimit(items, limit, offset = 0) {
if (limit === 0) return { items: items.slice(offset), appliedLimit: void 0 };
const cap = _nullishCoalesce(limit, () => ( DEFAULT_HEAD_LIMIT));
const sliced = items.slice(offset, offset + cap);
const truncated = items.length - offset > cap;
return { items: sliced, appliedLimit: truncated ? cap : void 0 };
}
function truncationSuffix(appliedLimit, offset) {
const parts = [];
if (appliedLimit !== void 0) parts.push(`limit: ${appliedLimit}`);
if (offset > 0) parts.push(`offset: ${offset}`);
return parts.length > 0 ? `
[Results truncated. ${parts.join(", ")}]` : "";
}
function relativizeLine(line, baseCwd, last = false) {
const idx = last ? line.lastIndexOf(":") : line.indexOf(":");
if (idx > 0) {
return _chunkQZ5GS6HWcjs.toRelativePath.call(void 0, line.substring(0, idx), baseCwd) + line.substring(idx);
}
return line;
}
function parseGlobPatterns(globFilter) {
const patterns = [];
for (const raw of globFilter.split(/\s+/)) {
if (raw.includes("{") && raw.includes("}")) {
patterns.push(raw);
} else {
patterns.push(...raw.split(",").filter(Boolean));
}
}
return patterns;
}
function createGrep(config = {}) {
const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
return _ai.tool.call(void 0, {
description: _nullishCoalesce(config.description, () => ( getPrompt())),
inputSchema: _zod.z.object({
pattern: _zod.z.string().describe("The regular expression pattern to search for in file contents"),
path: _zod.z.string().optional().describe("File or directory to search in (rg PATH). Defaults to current working directory."),
glob: _zod.z.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}") - maps to rg --glob'),
output_mode: _zod.z.enum(["content", "files_with_matches", "count"]).optional().describe('Output mode: "content" shows matching lines (supports -A/-B/-C context, -n line numbers, head_limit), "files_with_matches" shows file paths (supports head_limit), "count" shows match counts (supports head_limit). Defaults to "files_with_matches".'),
"-B": _zod.z.number().optional().describe('Number of lines to show before each match (rg -B). Requires output_mode: "content", ignored otherwise.'),
"-A": _zod.z.number().optional().describe('Number of lines to show after each match (rg -A). Requires output_mode: "content", ignored otherwise.'),
"-C": _zod.z.number().optional().describe("Alias for context."),
context: _zod.z.number().optional().describe('Number of lines to show before and after each match (rg -C). Requires output_mode: "content", ignored otherwise.'),
"-n": _zod.z.boolean().optional().describe('Show line numbers in output (rg -n). Requires output_mode: "content", ignored otherwise. Defaults to true.'),
"-i": _zod.z.boolean().optional().describe("Case insensitive search (rg -i)"),
type: _zod.z.string().optional().describe("File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types."),
head_limit: _zod.z.number().optional().describe('Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 250 when unspecified. Pass 0 for unlimited (use sparingly \u2014 large result sets waste context).'),
offset: _zod.z.number().optional().describe('Skip first N lines/entries before applying head_limit, equivalent to "| tail -n +N | head -N". Works across all output modes. Defaults to 0.'),
multiline: _zod.z.boolean().optional().describe("Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.")
}),
execute: async (input) => {
try {
const {
pattern,
path,
glob: globFilter,
type: typeFilter,
output_mode: outputMode = "files_with_matches",
"-B": ctxBefore,
"-A": ctxAfter,
"-C": ctxC,
context: ctxAlias,
"-n": showLineNumbers = true,
"-i": caseInsensitive = false,
head_limit: headLimit,
offset = 0,
multiline = false
} = input;
const absolutePath = path ? _chunkQZ5GS6HWcjs.expandPath.call(void 0, path, cwd) : cwd;
const args = ["--hidden"];
for (const dir of VCS_DIRS) args.push("--glob", `!${dir}`);
args.push("--max-columns", "500");
if (multiline) args.push("-U", "--multiline-dotall");
if (caseInsensitive) args.push("-i");
if (outputMode === "files_with_matches") args.push("-l");
else if (outputMode === "count") args.push("-c");
if (showLineNumbers && outputMode === "content") args.push("-n");
if (outputMode === "content") {
if (ctxAlias !== void 0) args.push("-C", ctxAlias.toString());
else if (ctxC !== void 0) args.push("-C", ctxC.toString());
else {
if (ctxBefore !== void 0) args.push("-B", ctxBefore.toString());
if (ctxAfter !== void 0) args.push("-A", ctxAfter.toString());
}
}
if (pattern.startsWith("-")) args.push("-e", pattern);
else args.push(pattern);
if (typeFilter) args.push("--type", typeFilter);
if (globFilter) {
for (const gp of parseGlobPatterns(globFilter)) args.push("--glob", gp);
}
const results = await _chunk4KNQPKGHcjs.executeRipgrep.call(void 0, args, absolutePath);
if (results.length === 0) return "No matches found";
if (outputMode === "content") {
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
const lines = items2.map((l) => relativizeLine(l, cwd));
return lines.join("\n") + truncationSuffix(appliedLimit2, offset);
}
if (outputMode === "count") {
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
const lines = items2.map((l) => relativizeLine(l, cwd, true));
let totalMatches = 0;
let fileCount = 0;
for (const line of lines) {
const idx = line.lastIndexOf(":");
if (idx > 0) {
const n = parseInt(line.substring(idx + 1), 10);
if (!isNaN(n)) {
totalMatches += n;
fileCount += 1;
}
}
}
return lines.join("\n") + `
Total: ${totalMatches} matches in ${fileCount} files` + truncationSuffix(appliedLimit2, offset);
}
const stats = await Promise.allSettled(results.map((f) => _chunkBIAODQ2Pcjs.getFileStats.call(void 0, f)));
const sorted = results.map((fp, i) => {
const r = stats[i];
const mt = r.status === "fulfilled" ? _nullishCoalesce(r.value.mtimeMs, () => ( 0)) : 0;
return [fp, mt];
}).sort((a, b) => {
const d = b[1] - a[1];
return d !== 0 ? d : a[0].localeCompare(b[0]);
}).map((e) => e[0]);
const { items, appliedLimit } = applyHeadLimit(sorted, headLimit, offset);
const relative = items.map((f) => _chunkQZ5GS6HWcjs.toRelativePath.call(void 0, f, cwd));
return relative.join("\n") + truncationSuffix(appliedLimit, offset);
} catch (error) {
const msg = _chunkKONXT2SFcjs.extractErrorMessage.call(void 0, error);
return `Error [grep]: Failed to search: ${msg}`;
}
}
});
}
var grep = createGrep();
exports.getPrompt = getPrompt; exports.createGrep = createGrep; exports.grep = grep;
import {
executeRipgrep
} from "./chunk-XNTMPBZJ.js";
import {
expandPath,
toRelativePath
} from "./chunk-I3ONDY7P.js";
import {
extractErrorMessage
} from "./chunk-X6ZY2KFU.js";
// src/glob/index.ts
import { tool } from "ai";
import { z } from "zod";
// src/shared/glob.ts
import { isAbsolute, join, basename, dirname, sep } from "path";
function extractGlobBaseDirectory(pattern) {
const globChars = /[*?[{]/;
const match = pattern.match(globChars);
if (!match || match.index === void 0) {
const dir = dirname(pattern);
const file = basename(pattern);
return { baseDir: dir, relativePattern: file };
}
const staticPrefix = pattern.slice(0, match.index);
const lastSepIndex = Math.max(
staticPrefix.lastIndexOf("/"),
staticPrefix.lastIndexOf(sep)
);
if (lastSepIndex === -1) {
return { baseDir: "", relativePattern: pattern };
}
let baseDir = staticPrefix.slice(0, lastSepIndex);
const relativePattern = pattern.slice(lastSepIndex + 1);
if (baseDir === "" && lastSepIndex === 0) {
baseDir = "/";
}
return { baseDir, relativePattern };
}
async function glob(pattern, cwd, options) {
const limit = options?.limit ?? 100;
const offset = options?.offset ?? 0;
let searchDir = cwd;
let searchPattern = pattern;
if (isAbsolute(pattern)) {
const { baseDir, relativePattern } = extractGlobBaseDirectory(pattern);
if (baseDir) {
searchDir = baseDir;
searchPattern = relativePattern;
}
}
const args = [
"--files",
"--glob",
searchPattern,
"--sort=modified",
"--hidden",
"--no-ignore"
];
const allPaths = await executeRipgrep(args, searchDir, {
signal: options?.signal
});
const absolutePaths = allPaths.map(
(p) => isAbsolute(p) ? p : join(searchDir, p)
);
const truncated = absolutePaths.length > offset + limit;
const files = absolutePaths.slice(offset, offset + limit);
return { files, truncated };
}
// src/glob/prompt.ts
function getPrompt() {
return `Fast file pattern matching tool that works with any codebase size.
- Supports glob patterns like "**/*.js" or "src/**/*.ts"
- Returns matching file paths sorted by modification time
- Use this tool when you need to find files by name patterns
- Use the content search tool when you need to search inside files`;
}
// src/glob/index.ts
function createGlob(config = {}) {
const cwd = config.cwd ?? process.cwd();
const pathStyle = config.pathStyle ?? "relative";
return tool({
description: config.description ?? getPrompt(),
inputSchema: z.object({
pattern: z.string().describe("Glob pattern to match files against"),
path: z.string().optional().describe("Directory to search in. Defaults to the working directory.")
}),
execute: async ({ pattern, path }) => {
try {
const searchDir = path ? expandPath(path, cwd) : cwd;
const { files, truncated } = await glob(pattern, searchDir);
if (files.length === 0) {
return "No files found";
}
const header = truncated ? `Found ${files.length}+ files (results truncated)` : `Found ${files.length} files`;
const outputFiles = pathStyle === "absolute" ? files : files.map((file) => toRelativePath(file, cwd));
return `${header}
${outputFiles.join("\n")}`;
} catch (error) {
const message = extractErrorMessage(error);
return `Error [glob]: Failed to search for files: ${message}`;
}
}
});
}
var glob2 = createGlob();
export {
getPrompt,
createGlob,
glob2 as glob
};
// src/shared/ripgrep.ts
import { execFile, execFileSync, spawn } from "child_process";
var MAX_BUFFER_SIZE = 2e7;
var DEFAULT_TIMEOUT = 2e4;
var INSTALL_URL = "https://github.com/BurntSushi/ripgrep#installation";
var RipgrepNotFoundError = class extends Error {
constructor(message) {
super(
message ?? `ripgrep (rg) not found on PATH. Install it from: ${INSTALL_URL}`
);
this.name = "RipgrepNotFoundError";
}
};
var RipgrepTimeoutError = class extends Error {
/** Lines captured from stdout before the process was killed. */
partialResults;
constructor(message, partialResults) {
super(message);
this.name = "RipgrepTimeoutError";
this.partialResults = partialResults;
}
};
function findRg() {
try {
const result = execFileSync("which", ["rg"], {
encoding: "utf-8",
timeout: 5e3
});
const rgPath = result.trim();
if (!rgPath) {
throw new RipgrepNotFoundError();
}
return rgPath;
} catch (error) {
if (error instanceof RipgrepNotFoundError) {
throw error;
}
throw new RipgrepNotFoundError();
}
}
function isEagainError(stderr) {
return stderr.includes("os error 11") || stderr.includes("Resource temporarily unavailable");
}
function parseStdout(stdout) {
return stdout.trim().split("\n").map((line) => line.replace(/\r$/, "")).filter(Boolean);
}
async function executeRipgrep(args, target, options) {
const rgPath = findRg();
const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
return new Promise((resolve, reject) => {
const handleResult = (error, stdout, stderr, isRetry) => {
if (!error) {
resolve(parseStdout(stdout));
return;
}
if (error.code === 1) {
resolve([]);
return;
}
const CRITICAL_CODES = ["ENOENT", "EACCES", "EPERM"];
if (CRITICAL_CODES.includes(error.code)) {
reject(error);
return;
}
if (!isRetry && isEagainError(stderr)) {
execFile(
rgPath,
["-j", "1", ...args, target],
{
maxBuffer: MAX_BUFFER_SIZE,
signal: options?.signal,
timeout,
killSignal: "SIGKILL"
},
(retryErr, retryStdout, retryStderr) => {
handleResult(retryErr, retryStdout, retryStderr, true);
}
);
return;
}
const hasOutput = stdout && stdout.trim().length > 0;
const isTimeout = error.signal === "SIGTERM" || error.signal === "SIGKILL" || error.code === "ABORT_ERR";
const isBufferOverflow = error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
let lines = [];
if (hasOutput) {
lines = parseStdout(stdout);
if (lines.length > 0 && (isTimeout || isBufferOverflow)) {
lines = lines.slice(0, -1);
}
}
if (isTimeout && lines.length === 0) {
reject(
new RipgrepTimeoutError(
`Ripgrep search timed out after ${timeout / 1e3} seconds. Try a more specific path or pattern.`,
lines
)
);
return;
}
resolve(lines);
};
execFile(
rgPath,
[...args, target],
{
maxBuffer: MAX_BUFFER_SIZE,
signal: options?.signal,
timeout,
killSignal: "SIGKILL"
},
(error, stdout, stderr) => {
handleResult(error, stdout, stderr, false);
}
);
});
}
export {
executeRipgrep
};
+2
-2

@@ -5,3 +5,3 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true});

var _chunkCXBWF5ONcjs = require('../chunk-CXBWF5ON.cjs');
var _chunkDKN6WTRYcjs = require('../chunk-DKN6WTRY.cjs');
require('../chunk-KONXT2SF.cjs');

@@ -12,2 +12,2 @@

exports.bash = _chunkCXBWF5ONcjs.bash; exports.bashPrompt = _chunkCXBWF5ONcjs.getPrompt; exports.createBash = _chunkCXBWF5ONcjs.createBash;
exports.bash = _chunkDKN6WTRYcjs.bash; exports.bashPrompt = _chunkDKN6WTRYcjs.getPrompt; exports.createBash = _chunkDKN6WTRYcjs.createBash;

@@ -13,3 +13,3 @@ import * as ai from 'ai';

*/
declare function getPrompt(config?: Pick<BashConfig, 'timeout' | 'shell'>): string;
declare function getPrompt(config?: Pick<BashConfig, 'timeout' | 'shell' | 'maxOutputChars'>): string;

@@ -29,2 +29,7 @@ /**

shell?: string;
/**
* Maximum model-facing output characters to return before truncating.
* Defaults to 30 000. Values above 150 000 are capped.
*/
maxOutputChars?: number;
/** Override the default tool description. */

@@ -31,0 +36,0 @@ description?: string;

@@ -13,3 +13,3 @@ import * as ai from 'ai';

*/
declare function getPrompt(config?: Pick<BashConfig, 'timeout' | 'shell'>): string;
declare function getPrompt(config?: Pick<BashConfig, 'timeout' | 'shell' | 'maxOutputChars'>): string;

@@ -29,2 +29,7 @@ /**

shell?: string;
/**
* Maximum model-facing output characters to return before truncating.
* Defaults to 30 000. Values above 150 000 are capped.
*/
maxOutputChars?: number;
/** Override the default tool description. */

@@ -31,0 +36,0 @@ description?: string;

@@ -5,3 +5,3 @@ import {

getPrompt
} from "../chunk-TXZ3BMMR.js";
} from "../chunk-EIS2BRBZ.js";
import "../chunk-X6ZY2KFU.js";

@@ -8,0 +8,0 @@ export {

@@ -5,4 +5,4 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true});

var _chunkYCWJVQYOcjs = require('../chunk-YCWJVQYO.cjs');
require('../chunk-MIYA7TNR.cjs');
var _chunkBNICFVN5cjs = require('../chunk-BNICFVN5.cjs');
require('../chunk-4KNQPKGH.cjs');
require('../chunk-QZ5GS6HW.cjs');

@@ -14,2 +14,2 @@ require('../chunk-KONXT2SF.cjs');

exports.createGlob = _chunkYCWJVQYOcjs.createGlob; exports.glob = _chunkYCWJVQYOcjs.glob; exports.globPrompt = _chunkYCWJVQYOcjs.getPrompt;
exports.createGlob = _chunkBNICFVN5cjs.createGlob; exports.glob = _chunkBNICFVN5cjs.glob; exports.globPrompt = _chunkBNICFVN5cjs.getPrompt;

@@ -15,3 +15,5 @@ import * as ai from 'ai';

* Uses ripgrep `--files --glob` under the hood for fast file matching.
* Returns absolute paths sorted by modification time (newest first).
* Returns paths sorted by modification time (newest first). Paths under the
* configured cwd are relative by default, matching Claude Code's model-facing
* output.
* Execute never throws; errors are returned as descriptive strings.

@@ -50,2 +52,7 @@ *

type GlobConfig = BaseToolConfig & {
/**
* Path style for model-facing results.
* Defaults to "relative" to match Claude Code.
*/
pathStyle?: 'relative' | 'absolute';
/** Override the default tool description. */

@@ -52,0 +59,0 @@ description?: string;

@@ -15,3 +15,5 @@ import * as ai from 'ai';

* Uses ripgrep `--files --glob` under the hood for fast file matching.
* Returns absolute paths sorted by modification time (newest first).
* Returns paths sorted by modification time (newest first). Paths under the
* configured cwd are relative by default, matching Claude Code's model-facing
* output.
* Execute never throws; errors are returned as descriptive strings.

@@ -50,2 +52,7 @@ *

type GlobConfig = BaseToolConfig & {
/**
* Path style for model-facing results.
* Defaults to "relative" to match Claude Code.
*/
pathStyle?: 'relative' | 'absolute';
/** Override the default tool description. */

@@ -52,0 +59,0 @@ description?: string;

@@ -5,4 +5,4 @@ import {

glob
} from "../chunk-IBC4QCGR.js";
import "../chunk-MJCAXASI.js";
} from "../chunk-QLWIBPMH.js";
import "../chunk-XNTMPBZJ.js";
import "../chunk-I3ONDY7P.js";

@@ -9,0 +9,0 @@ import "../chunk-X6ZY2KFU.js";

@@ -5,4 +5,4 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true});

var _chunkRIGL3JTScjs = require('../chunk-RIGL3JTS.cjs');
require('../chunk-MIYA7TNR.cjs');
var _chunkNRU2KZIMcjs = require('../chunk-NRU2KZIM.cjs');
require('../chunk-4KNQPKGH.cjs');
require('../chunk-BIAODQ2P.cjs');

@@ -15,2 +15,2 @@ require('../chunk-QZ5GS6HW.cjs');

exports.createGrep = _chunkRIGL3JTScjs.createGrep; exports.grep = _chunkRIGL3JTScjs.grep; exports.grepPrompt = _chunkRIGL3JTScjs.getPrompt;
exports.createGrep = _chunkNRU2KZIMcjs.createGrep; exports.grep = _chunkNRU2KZIMcjs.grep; exports.grepPrompt = _chunkNRU2KZIMcjs.getPrompt;

@@ -5,4 +5,4 @@ import {

grep
} from "../chunk-2WSZCOJP.js";
import "../chunk-MJCAXASI.js";
} from "../chunk-D5JJPGTP.js";
import "../chunk-XNTMPBZJ.js";
import "../chunk-PWBVB6MN.js";

@@ -9,0 +9,0 @@ import "../chunk-I3ONDY7P.js";

@@ -79,3 +79,3 @@ "use strict";Object.defineProperty(exports, "__esModule", {value: true});

var _chunkCXBWF5ONcjs = require('./chunk-CXBWF5ON.cjs');
var _chunkDKN6WTRYcjs = require('./chunk-DKN6WTRY.cjs');

@@ -85,3 +85,3 @@

var _chunkRIGL3JTScjs = require('./chunk-RIGL3JTS.cjs');
var _chunkNRU2KZIMcjs = require('./chunk-NRU2KZIM.cjs');

@@ -91,4 +91,4 @@

var _chunkYCWJVQYOcjs = require('./chunk-YCWJVQYO.cjs');
require('./chunk-MIYA7TNR.cjs');
var _chunkBNICFVN5cjs = require('./chunk-BNICFVN5.cjs');
require('./chunk-4KNQPKGH.cjs');

@@ -183,2 +183,2 @@

exports.askUser = _chunkKUFZFNPTcjs.askUser; exports.askUserPrompt = _chunkKUFZFNPTcjs.getPrompt; exports.bash = _chunkCXBWF5ONcjs.bash; exports.bashPrompt = _chunkCXBWF5ONcjs.getPrompt; exports.compactMessages = _chunkB76NYX22cjs.compactMessages; exports.createAskUser = _chunkKUFZFNPTcjs.createAskUser; exports.createBash = _chunkCXBWF5ONcjs.createBash; exports.createDiff = _chunkOYLTQJXTcjs.createDiff; exports.createEdit = _chunk6ULQG2W2cjs.createEdit; exports.createGlob = _chunkYCWJVQYOcjs.createGlob; exports.createGrep = _chunkRIGL3JTScjs.createGrep; exports.createHttpRequest = _chunk5T3SQYI4cjs.createHttpRequest; exports.createLsp = _chunkNQIV6LBHcjs.createLsp; exports.createMemory = _chunkLNAR3NJQcjs.createMemory; exports.createMultiEdit = _chunkVPV6WG5Vcjs.createMultiEdit; exports.createOutputValidator = _chunkYRUGHL6Scjs.createOutputValidator; exports.createRead = _chunkHG5T47NAcjs.createRead; exports.createSleep = _chunkJYTOARJVcjs.createSleep; exports.createTaskCreate = _chunkSFDZRLSXcjs.createTaskCreate; exports.createTaskGet = _chunkXGDE7S2Dcjs.createTaskGet; exports.createTaskList = _chunk3FT4ZPB2cjs.createTaskList; exports.createTaskUpdate = _chunkT6STO7PScjs.createTaskUpdate; exports.createToolSearch = _chunk2JBLVFB7cjs.createToolSearch; exports.createWebFetch = _chunkG6ZVJA4Vcjs.createWebFetch; exports.createWebSearch = _chunkCM3VRCNXcjs.createWebSearch; exports.createWrite = _chunkABXTBB2Ncjs.createWrite; exports.diff = _chunkOYLTQJXTcjs.diff; exports.diffPrompt = _chunkOYLTQJXTcjs.getPrompt; exports.edit = _chunk6ULQG2W2cjs.edit; exports.editPrompt = _chunk6ULQG2W2cjs.getPrompt; exports.glob = _chunkYCWJVQYOcjs.glob; exports.globPrompt = _chunkYCWJVQYOcjs.getPrompt; exports.grep = _chunkRIGL3JTScjs.grep; exports.grepPrompt = _chunkRIGL3JTScjs.getPrompt; exports.httpRequest = _chunk5T3SQYI4cjs.httpRequest; exports.httpRequestPrompt = _chunk5T3SQYI4cjs.getPrompt; exports.lsp = _chunkNQIV6LBHcjs.lsp; exports.lspPrompt = _chunkNQIV6LBHcjs.getPrompt; exports.memory = _chunkLNAR3NJQcjs.memory; exports.memoryPrompt = _chunkLNAR3NJQcjs.getPrompt; exports.multiEdit = _chunkVPV6WG5Vcjs.multiEdit; exports.multiEditPrompt = _chunkVPV6WG5Vcjs.getPrompt; exports.outputValidator = _chunkYRUGHL6Scjs.outputValidator; exports.outputValidatorPrompt = _chunkYRUGHL6Scjs.getPrompt; exports.read = _chunkHG5T47NAcjs.read; exports.readPrompt = _chunkHG5T47NAcjs.getPrompt; exports.sleep = _chunkJYTOARJVcjs.sleep; exports.sleepPrompt = _chunkJYTOARJVcjs.getPrompt; exports.taskCreate = _chunkSFDZRLSXcjs.taskCreate; exports.taskCreatePrompt = _chunkSFDZRLSXcjs.getPrompt; exports.taskGet = _chunkXGDE7S2Dcjs.taskGet; exports.taskGetPrompt = _chunkXGDE7S2Dcjs.getPrompt; exports.taskList = _chunk3FT4ZPB2cjs.taskList; exports.taskListPrompt = _chunk3FT4ZPB2cjs.getPrompt; exports.taskUpdate = _chunkT6STO7PScjs.taskUpdate; exports.taskUpdatePrompt = _chunkT6STO7PScjs.getPrompt; exports.toolSearch = _chunk2JBLVFB7cjs.toolSearch; exports.toolSearchPrompt = _chunk2JBLVFB7cjs.getPrompt; exports.webFetch = _chunkG6ZVJA4Vcjs.webFetch; exports.webFetchPrompt = _chunkG6ZVJA4Vcjs.getPrompt; exports.webSearch = _chunkCM3VRCNXcjs.webSearch; exports.webSearchPrompt = _chunkCM3VRCNXcjs.getPrompt; exports.write = _chunkABXTBB2Ncjs.write; exports.writePrompt = _chunkABXTBB2Ncjs.getPrompt;
exports.askUser = _chunkKUFZFNPTcjs.askUser; exports.askUserPrompt = _chunkKUFZFNPTcjs.getPrompt; exports.bash = _chunkDKN6WTRYcjs.bash; exports.bashPrompt = _chunkDKN6WTRYcjs.getPrompt; exports.compactMessages = _chunkB76NYX22cjs.compactMessages; exports.createAskUser = _chunkKUFZFNPTcjs.createAskUser; exports.createBash = _chunkDKN6WTRYcjs.createBash; exports.createDiff = _chunkOYLTQJXTcjs.createDiff; exports.createEdit = _chunk6ULQG2W2cjs.createEdit; exports.createGlob = _chunkBNICFVN5cjs.createGlob; exports.createGrep = _chunkNRU2KZIMcjs.createGrep; exports.createHttpRequest = _chunk5T3SQYI4cjs.createHttpRequest; exports.createLsp = _chunkNQIV6LBHcjs.createLsp; exports.createMemory = _chunkLNAR3NJQcjs.createMemory; exports.createMultiEdit = _chunkVPV6WG5Vcjs.createMultiEdit; exports.createOutputValidator = _chunkYRUGHL6Scjs.createOutputValidator; exports.createRead = _chunkHG5T47NAcjs.createRead; exports.createSleep = _chunkJYTOARJVcjs.createSleep; exports.createTaskCreate = _chunkSFDZRLSXcjs.createTaskCreate; exports.createTaskGet = _chunkXGDE7S2Dcjs.createTaskGet; exports.createTaskList = _chunk3FT4ZPB2cjs.createTaskList; exports.createTaskUpdate = _chunkT6STO7PScjs.createTaskUpdate; exports.createToolSearch = _chunk2JBLVFB7cjs.createToolSearch; exports.createWebFetch = _chunkG6ZVJA4Vcjs.createWebFetch; exports.createWebSearch = _chunkCM3VRCNXcjs.createWebSearch; exports.createWrite = _chunkABXTBB2Ncjs.createWrite; exports.diff = _chunkOYLTQJXTcjs.diff; exports.diffPrompt = _chunkOYLTQJXTcjs.getPrompt; exports.edit = _chunk6ULQG2W2cjs.edit; exports.editPrompt = _chunk6ULQG2W2cjs.getPrompt; exports.glob = _chunkBNICFVN5cjs.glob; exports.globPrompt = _chunkBNICFVN5cjs.getPrompt; exports.grep = _chunkNRU2KZIMcjs.grep; exports.grepPrompt = _chunkNRU2KZIMcjs.getPrompt; exports.httpRequest = _chunk5T3SQYI4cjs.httpRequest; exports.httpRequestPrompt = _chunk5T3SQYI4cjs.getPrompt; exports.lsp = _chunkNQIV6LBHcjs.lsp; exports.lspPrompt = _chunkNQIV6LBHcjs.getPrompt; exports.memory = _chunkLNAR3NJQcjs.memory; exports.memoryPrompt = _chunkLNAR3NJQcjs.getPrompt; exports.multiEdit = _chunkVPV6WG5Vcjs.multiEdit; exports.multiEditPrompt = _chunkVPV6WG5Vcjs.getPrompt; exports.outputValidator = _chunkYRUGHL6Scjs.outputValidator; exports.outputValidatorPrompt = _chunkYRUGHL6Scjs.getPrompt; exports.read = _chunkHG5T47NAcjs.read; exports.readPrompt = _chunkHG5T47NAcjs.getPrompt; exports.sleep = _chunkJYTOARJVcjs.sleep; exports.sleepPrompt = _chunkJYTOARJVcjs.getPrompt; exports.taskCreate = _chunkSFDZRLSXcjs.taskCreate; exports.taskCreatePrompt = _chunkSFDZRLSXcjs.getPrompt; exports.taskGet = _chunkXGDE7S2Dcjs.taskGet; exports.taskGetPrompt = _chunkXGDE7S2Dcjs.getPrompt; exports.taskList = _chunk3FT4ZPB2cjs.taskList; exports.taskListPrompt = _chunk3FT4ZPB2cjs.getPrompt; exports.taskUpdate = _chunkT6STO7PScjs.taskUpdate; exports.taskUpdatePrompt = _chunkT6STO7PScjs.getPrompt; exports.toolSearch = _chunk2JBLVFB7cjs.toolSearch; exports.toolSearchPrompt = _chunk2JBLVFB7cjs.getPrompt; exports.webFetch = _chunkG6ZVJA4Vcjs.webFetch; exports.webFetchPrompt = _chunkG6ZVJA4Vcjs.getPrompt; exports.webSearch = _chunkCM3VRCNXcjs.webSearch; exports.webSearchPrompt = _chunkCM3VRCNXcjs.getPrompt; exports.write = _chunkABXTBB2Ncjs.write; exports.writePrompt = _chunkABXTBB2Ncjs.getPrompt;

@@ -79,3 +79,3 @@ import {

getPrompt
} from "./chunk-TXZ3BMMR.js";
} from "./chunk-EIS2BRBZ.js";
import {

@@ -85,3 +85,3 @@ createGrep,

grep
} from "./chunk-2WSZCOJP.js";
} from "./chunk-D5JJPGTP.js";
import {

@@ -91,4 +91,4 @@ createGlob,

glob
} from "./chunk-IBC4QCGR.js";
import "./chunk-MJCAXASI.js";
} from "./chunk-QLWIBPMH.js";
import "./chunk-XNTMPBZJ.js";
import {

@@ -95,0 +95,0 @@ createRead,

{
"name": "agentool",
"version": "1.4.0",
"version": "1.4.1",
"type": "module",

@@ -157,4 +157,6 @@ "description": "22 AI agent tools + context-compaction helper as standalone Vercel AI SDK modules",

"test": "vitest run --project unit",
"test:coverage": "vitest run --project unit --exclude tests/unit/build.test.ts --coverage",
"test:functional": "vitest run --project functional",
"lint": "eslint src/"
"lint": "eslint src/",
"precommit": "npm run lint && npm run typecheck && npm run test:coverage"
},

@@ -161,0 +163,0 @@ "engines": {

import {
executeRipgrep
} from "./chunk-MJCAXASI.js";
import {
getFileStats
} from "./chunk-PWBVB6MN.js";
import {
expandPath,
toRelativePath
} from "./chunk-I3ONDY7P.js";
import {
extractErrorMessage
} from "./chunk-X6ZY2KFU.js";
// src/grep/index.ts
import { tool } from "ai";
import { z } from "zod";
// src/grep/prompt.ts
function getPrompt() {
return `Search file contents using ripgrep. Supports regex patterns, context lines, and multiple output modes.
## When to Use
- To find where a function, variable, string, or pattern is used across files
- To search for specific code patterns, error messages, or configuration values
- To count occurrences of a pattern across a codebase
## When NOT to Use
- To find files by name/extension \u2014 use the dedicated file search tool instead
- To read a specific file \u2014 use the file reading tool instead
- Don't use shell commands (grep, rg) for content search when this tool is available
## Output Modes
- \`files_with_matches\` (default): Returns file paths containing matches, sorted by modification time. Best for discovering which files contain a pattern.
- \`content\`: Returns matching lines with optional context. Supports \`-A\` (after), \`-B\` (before), \`-C\` (context) for surrounding lines and \`-n\` for line numbers (default: true).
- \`count\`: Returns match counts per file with totals.
## Usage Guidelines
- Uses ripgrep regex syntax (not grep). Literal braces need escaping: use \`interface\\{\\}\` to find \`interface{}\`
- Filter files with the \`glob\` parameter (e.g., "*.js", "*.{ts,tsx}") or \`type\` parameter (e.g., "js", "py")
- Default head_limit is 250 entries. Pass \`head_limit: 0\` for unlimited (use sparingly \u2014 large results waste context)
- Use \`offset\` to paginate through large result sets
- Enable \`multiline: true\` for patterns that span lines (e.g., \`struct \\{[\\s\\S]*?field\`)
- Use \`-i: true\` for case-insensitive search`;
}
// src/grep/index.ts
var VCS_DIRS = [".git", ".svn", ".hg", ".bzr", ".jj", ".sl"];
var DEFAULT_HEAD_LIMIT = 250;
function applyHeadLimit(items, limit, offset = 0) {
if (limit === 0) return { items: items.slice(offset), appliedLimit: void 0 };
const cap = limit ?? DEFAULT_HEAD_LIMIT;
const sliced = items.slice(offset, offset + cap);
const truncated = items.length - offset > cap;
return { items: sliced, appliedLimit: truncated ? cap : void 0 };
}
function truncationSuffix(appliedLimit, offset) {
const parts = [];
if (appliedLimit !== void 0) parts.push(`limit: ${appliedLimit}`);
if (offset > 0) parts.push(`offset: ${offset}`);
return parts.length > 0 ? `
[Results truncated. ${parts.join(", ")}]` : "";
}
function relativizeLine(line, baseCwd, last = false) {
const idx = last ? line.lastIndexOf(":") : line.indexOf(":");
if (idx > 0) {
return toRelativePath(line.substring(0, idx), baseCwd) + line.substring(idx);
}
return line;
}
function parseGlobPatterns(globFilter) {
const patterns = [];
for (const raw of globFilter.split(/\s+/)) {
if (raw.includes("{") && raw.includes("}")) {
patterns.push(raw);
} else {
patterns.push(...raw.split(",").filter(Boolean));
}
}
return patterns;
}
function createGrep(config = {}) {
const cwd = config.cwd ?? process.cwd();
return tool({
description: config.description ?? getPrompt(),
inputSchema: z.object({
pattern: z.string().describe("The regular expression pattern to search for in file contents"),
path: z.string().optional().describe("File or directory to search in (rg PATH). Defaults to current working directory."),
glob: z.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}") - maps to rg --glob'),
output_mode: z.enum(["content", "files_with_matches", "count"]).optional().describe('Output mode: "content" shows matching lines (supports -A/-B/-C context, -n line numbers, head_limit), "files_with_matches" shows file paths (supports head_limit), "count" shows match counts (supports head_limit). Defaults to "files_with_matches".'),
"-B": z.number().optional().describe('Number of lines to show before each match (rg -B). Requires output_mode: "content", ignored otherwise.'),
"-A": z.number().optional().describe('Number of lines to show after each match (rg -A). Requires output_mode: "content", ignored otherwise.'),
"-C": z.number().optional().describe("Alias for context."),
context: z.number().optional().describe('Number of lines to show before and after each match (rg -C). Requires output_mode: "content", ignored otherwise.'),
"-n": z.boolean().optional().describe('Show line numbers in output (rg -n). Requires output_mode: "content", ignored otherwise. Defaults to true.'),
"-i": z.boolean().optional().describe("Case insensitive search (rg -i)"),
type: z.string().optional().describe("File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types."),
head_limit: z.number().optional().describe('Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 250 when unspecified. Pass 0 for unlimited (use sparingly \u2014 large result sets waste context).'),
offset: z.number().optional().describe('Skip first N lines/entries before applying head_limit, equivalent to "| tail -n +N | head -N". Works across all output modes. Defaults to 0.'),
multiline: z.boolean().optional().describe("Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.")
}),
execute: async (input) => {
try {
const {
pattern,
path,
glob: globFilter,
type: typeFilter,
output_mode: outputMode = "files_with_matches",
"-B": ctxBefore,
"-A": ctxAfter,
"-C": ctxC,
context: ctxAlias,
"-n": showLineNumbers = true,
"-i": caseInsensitive = false,
head_limit: headLimit,
offset = 0,
multiline = false
} = input;
const absolutePath = path ? expandPath(path, cwd) : cwd;
const args = ["--hidden"];
for (const dir of VCS_DIRS) args.push("--glob", `!${dir}`);
args.push("--max-columns", "500");
if (multiline) args.push("-U", "--multiline-dotall");
if (caseInsensitive) args.push("-i");
if (outputMode === "files_with_matches") args.push("-l");
else if (outputMode === "count") args.push("-c");
if (showLineNumbers && outputMode === "content") args.push("-n");
if (outputMode === "content") {
if (ctxAlias !== void 0) args.push("-C", ctxAlias.toString());
else if (ctxC !== void 0) args.push("-C", ctxC.toString());
else {
if (ctxBefore !== void 0) args.push("-B", ctxBefore.toString());
if (ctxAfter !== void 0) args.push("-A", ctxAfter.toString());
}
}
if (pattern.startsWith("-")) args.push("-e", pattern);
else args.push(pattern);
if (typeFilter) args.push("--type", typeFilter);
if (globFilter) {
for (const gp of parseGlobPatterns(globFilter)) args.push("--glob", gp);
}
const results = await executeRipgrep(args, absolutePath);
if (results.length === 0) return "No matches found";
if (outputMode === "content") {
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
const lines = items2.map((l) => relativizeLine(l, cwd));
return lines.join("\n") + truncationSuffix(appliedLimit2, offset);
}
if (outputMode === "count") {
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
const lines = items2.map((l) => relativizeLine(l, cwd, true));
let totalMatches = 0;
let fileCount = 0;
for (const line of lines) {
const idx = line.lastIndexOf(":");
if (idx > 0) {
const n = parseInt(line.substring(idx + 1), 10);
if (!isNaN(n)) {
totalMatches += n;
fileCount += 1;
}
}
}
return lines.join("\n") + `
Total: ${totalMatches} matches in ${fileCount} files` + truncationSuffix(appliedLimit2, offset);
}
const stats = await Promise.allSettled(results.map((f) => getFileStats(f)));
const sorted = results.map((fp, i) => {
const r = stats[i];
const mt = r.status === "fulfilled" ? r.value.mtimeMs ?? 0 : 0;
return [fp, mt];
}).sort((a, b) => {
const d = b[1] - a[1];
return d !== 0 ? d : a[0].localeCompare(b[0]);
}).map((e) => e[0]);
const { items, appliedLimit } = applyHeadLimit(sorted, headLimit, offset);
const relative = items.map((f) => toRelativePath(f, cwd));
return relative.join("\n") + truncationSuffix(appliedLimit, offset);
} catch (error) {
const msg = extractErrorMessage(error);
return `Error [grep]: Failed to search: ${msg}`;
}
}
});
}
var grep = createGrep();
export {
getPrompt,
createGrep,
grep
};
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
var _chunkKONXT2SFcjs = require('./chunk-KONXT2SF.cjs');
// src/bash/index.ts
var _ai = require('ai');
var _zod = require('zod');
// src/shared/shell.ts
var _child_process = require('child_process');
var MAX_BUFFER_BYTES = 10 * 1024 * 1024;
var DEFAULT_TIMEOUT_MS = 12e4;
var SIGKILL_GRACE_MS = 5e3;
async function executeShell(command, options) {
const shellBin = _nullishCoalesce(_nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.shell]), () => ( process.env.SHELL)), () => ( "/bin/bash"));
const timeout = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.timeout]), () => ( DEFAULT_TIMEOUT_MS));
return new Promise((resolve, reject) => {
const child = _child_process.spawn.call(void 0, shellBin, ["-c", command], {
cwd: _optionalChain([options, 'optionalAccess', _3 => _3.cwd]),
stdio: ["pipe", "pipe", "pipe"],
// Prevent visible console window on Windows (no-op elsewhere)
windowsHide: true
});
let stdout = "";
let stderr = "";
let stdoutBytes = 0;
let stderrBytes = 0;
let settled = false;
let timeoutId;
let graceId;
function cleanup() {
if (timeoutId !== void 0) {
clearTimeout(timeoutId);
timeoutId = void 0;
}
if (graceId !== void 0) {
clearTimeout(graceId);
graceId = void 0;
}
if (abortHandler && _optionalChain([options, 'optionalAccess', _4 => _4.signal])) {
options.signal.removeEventListener("abort", abortHandler);
}
}
function settle(result) {
if (settled) return;
settled = true;
cleanup();
resolve(result);
}
child.stdout.on("data", (chunk) => {
if (stdoutBytes >= MAX_BUFFER_BYTES) return;
const str = chunk.toString();
const remaining = MAX_BUFFER_BYTES - stdoutBytes;
if (chunk.length > remaining) {
stdout += str.slice(0, remaining);
stdoutBytes = MAX_BUFFER_BYTES;
} else {
stdout += str;
stdoutBytes += chunk.length;
}
});
child.stderr.on("data", (chunk) => {
if (stderrBytes >= MAX_BUFFER_BYTES) return;
const str = chunk.toString();
const remaining = MAX_BUFFER_BYTES - stderrBytes;
if (chunk.length > remaining) {
stderr += str.slice(0, remaining);
stderrBytes = MAX_BUFFER_BYTES;
} else {
stderr += str;
stderrBytes += chunk.length;
}
});
child.on("exit", (code, signal) => {
const exitCode = code !== null ? code : signal === "SIGKILL" ? 137 : signal === "SIGTERM" ? 143 : 1;
settle({ stdout, stderr, exitCode });
});
child.on("error", (err) => {
if (settled) return;
settled = true;
cleanup();
reject(err);
});
if (timeout > 0) {
timeoutId = setTimeout(() => {
timeoutId = void 0;
if (settled) return;
child.kill("SIGTERM");
graceId = setTimeout(() => {
graceId = void 0;
if (settled) return;
child.kill("SIGKILL");
}, SIGKILL_GRACE_MS);
}, timeout);
}
const abortHandler = _optionalChain([options, 'optionalAccess', _5 => _5.signal]) ? () => {
if (settled) return;
child.kill("SIGTERM");
} : void 0;
if (abortHandler && _optionalChain([options, 'optionalAccess', _6 => _6.signal])) {
if (options.signal.aborted) {
child.kill("SIGTERM");
} else {
options.signal.addEventListener("abort", abortHandler, { once: true });
}
}
});
}
// src/bash/prompt.ts
function getPrompt(config = {}) {
const timeout = _nullishCoalesce(config.timeout, () => ( 12e4));
const timeoutMin = timeout / 6e4;
const shell = _nullishCoalesce(config.shell, () => ( "$SHELL or /bin/bash"));
return `Execute a shell command and return its output (stdout, stderr, exit code).
Runs the command in ${shell} with \`-c\`. The working directory persists between calls.
## When to Use
- Build commands, git operations, system administration, installing packages
- Running scripts, compiling code, process management
- Any shell task that doesn't have a dedicated tool available
## When NOT to Use
- Reading file contents \u2014 use the dedicated file reading tool instead
- Searching file contents \u2014 use the dedicated content search tool instead
- Finding files by name/pattern \u2014 use the dedicated file search tool instead
- Editing files \u2014 use the dedicated file editing tool instead
- Writing new files \u2014 use the dedicated file writing tool instead
Prefer dedicated tools over shell equivalents (e.g., don't use cat, head, tail, sed, awk, grep, find, or echo when a dedicated tool exists). Dedicated tools provide better output formatting and permission handling.
## Usage Guidelines
- Default timeout: ${timeout}ms (${timeoutMin} minutes). Override with the timeout parameter.
- Timeout escalation: SIGTERM first, then SIGKILL after 5-second grace period.
- Output is capped at 10 MB per stream (stdout/stderr).
- Always quote file paths containing spaces with double quotes.
- When issuing multiple commands:
- Independent commands: make separate tool calls in parallel.
- Sequential with dependency: chain with \`&&\`.
- Sequential ignoring failures: chain with \`;\`.
- Do NOT use newlines to separate commands.
- Avoid unnecessary \`sleep\` commands:
- Don't sleep between commands that can run immediately.
- Don't retry failing commands in a sleep loop \u2014 diagnose the root cause.
- If you must sleep, keep it short (1-5 seconds).
- For git commands:
- Prefer creating new commits rather than amending existing ones.
- Never skip hooks (--no-verify) unless the user explicitly requests it.
- Before destructive operations (reset --hard, push --force), consider safer alternatives.`;
}
// src/bash/index.ts
function createBash(config = {}) {
const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
const timeout = _nullishCoalesce(config.timeout, () => ( 12e4));
return _ai.tool.call(void 0, {
description: _nullishCoalesce(config.description, () => ( getPrompt(config))),
inputSchema: _zod.z.object({
command: _zod.z.string().describe("The shell command to execute"),
timeout: _zod.z.number().optional().describe("Timeout in milliseconds (default: 120000)"),
description: _zod.z.string().optional().describe("Human-readable description of what the command does")
}),
execute: async ({ command, timeout: cmdTimeout }) => {
try {
const result = await executeShell(command, {
cwd,
timeout: _nullishCoalesce(cmdTimeout, () => ( timeout)),
shell: config.shell
});
const parts = [];
if (result.stdout) parts.push(result.stdout);
if (result.stderr) parts.push(`STDERR:
${result.stderr}`);
if (result.exitCode !== 0) parts.push(`Exit code: ${result.exitCode}`);
return parts.length > 0 ? parts.join("\n") : `Command completed with exit code ${result.exitCode}`;
} catch (error) {
const msg = _chunkKONXT2SFcjs.extractErrorMessage.call(void 0, error);
return `Error [bash]: Failed to execute command: ${msg}`;
}
}
});
}
var bash = createBash();
exports.getPrompt = getPrompt; exports.createBash = createBash; exports.bash = bash;
import {
executeRipgrep
} from "./chunk-MJCAXASI.js";
import {
expandPath
} from "./chunk-I3ONDY7P.js";
import {
extractErrorMessage
} from "./chunk-X6ZY2KFU.js";
// src/glob/index.ts
import { tool } from "ai";
import { z } from "zod";
// src/shared/glob.ts
import { isAbsolute, join, basename, dirname, sep } from "path";
function extractGlobBaseDirectory(pattern) {
const globChars = /[*?[{]/;
const match = pattern.match(globChars);
if (!match || match.index === void 0) {
const dir = dirname(pattern);
const file = basename(pattern);
return { baseDir: dir, relativePattern: file };
}
const staticPrefix = pattern.slice(0, match.index);
const lastSepIndex = Math.max(
staticPrefix.lastIndexOf("/"),
staticPrefix.lastIndexOf(sep)
);
if (lastSepIndex === -1) {
return { baseDir: "", relativePattern: pattern };
}
let baseDir = staticPrefix.slice(0, lastSepIndex);
const relativePattern = pattern.slice(lastSepIndex + 1);
if (baseDir === "" && lastSepIndex === 0) {
baseDir = "/";
}
return { baseDir, relativePattern };
}
async function glob(pattern, cwd, options) {
const limit = options?.limit ?? 100;
const offset = options?.offset ?? 0;
let searchDir = cwd;
let searchPattern = pattern;
if (isAbsolute(pattern)) {
const { baseDir, relativePattern } = extractGlobBaseDirectory(pattern);
if (baseDir) {
searchDir = baseDir;
searchPattern = relativePattern;
}
}
const args = [
"--files",
"--glob",
searchPattern,
"--sort=modified",
"--hidden",
"--no-ignore"
];
const allPaths = await executeRipgrep(args, searchDir, {
signal: options?.signal
});
const absolutePaths = allPaths.map(
(p) => isAbsolute(p) ? p : join(searchDir, p)
);
const truncated = absolutePaths.length > offset + limit;
const files = absolutePaths.slice(offset, offset + limit);
return { files, truncated };
}
// src/glob/prompt.ts
function getPrompt() {
return `Find files matching a glob pattern. Returns absolute file paths sorted by modification time (newest first).
Fast file pattern matching powered by ripgrep. Supports patterns like "**/*.ts", "src/**/*.js", or "*.json".
## When to Use
- To find files by name or extension across a codebase
- To locate configuration files, test files, or specific file types
- To discover project structure and file organization
## When NOT to Use
- To search file *contents* \u2014 use the dedicated content search tool instead
- To read a specific file whose path you already know \u2014 use the file reading tool directly
## Usage Guidelines
- Results are sorted by modification time (most recently modified first)
- The optional \`path\` parameter lets you narrow the search to a specific directory
- Results may be truncated for very large result sets`;
}
// src/glob/index.ts
function createGlob(config = {}) {
const cwd = config.cwd ?? process.cwd();
return tool({
description: config.description ?? getPrompt(),
inputSchema: z.object({
pattern: z.string().describe("Glob pattern to match files against"),
path: z.string().optional().describe("Directory to search in. Defaults to the working directory.")
}),
execute: async ({ pattern, path }) => {
try {
const searchDir = path ? expandPath(path, cwd) : cwd;
const { files, truncated } = await glob(pattern, searchDir);
if (files.length === 0) {
return "No files found";
}
const header = truncated ? `Found ${files.length}+ files (results truncated)` : `Found ${files.length} files`;
return `${header}
${files.join("\n")}`;
} catch (error) {
const message = extractErrorMessage(error);
return `Error [glob]: Failed to search for files: ${message}`;
}
}
});
}
var glob2 = createGlob();
export {
getPrompt,
createGlob,
glob2 as glob
};
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }// src/shared/ripgrep.ts
var _child_process = require('child_process');
var MAX_BUFFER_SIZE = 2e7;
var DEFAULT_TIMEOUT = 2e4;
var INSTALL_URL = "https://github.com/BurntSushi/ripgrep#installation";
var RipgrepNotFoundError = class extends Error {
constructor(message) {
super(
_nullishCoalesce(message, () => ( `ripgrep (rg) not found on PATH. Install it from: ${INSTALL_URL}`))
);
this.name = "RipgrepNotFoundError";
}
};
var RipgrepTimeoutError = class extends Error {
/**
* Lines captured from stdout before the process was killed.
* May be empty if no output arrived before the timeout.
*/
constructor(message, partialResults) {
super(message);
this.name = "RipgrepTimeoutError";
this.partialResults = partialResults;
}
};
function findRg() {
try {
const result = _child_process.execFileSync.call(void 0, "which", ["rg"], {
encoding: "utf-8",
timeout: 5e3
});
const rgPath = result.trim();
if (!rgPath) {
throw new RipgrepNotFoundError();
}
return rgPath;
} catch (error) {
if (error instanceof RipgrepNotFoundError) {
throw error;
}
throw new RipgrepNotFoundError();
}
}
function isEagainError(stderr) {
return stderr.includes("os error 11") || stderr.includes("Resource temporarily unavailable");
}
function parseStdout(stdout) {
return stdout.trim().split("\n").map((line) => line.replace(/\r$/, "")).filter(Boolean);
}
async function executeRipgrep(args, target, options) {
const rgPath = findRg();
const timeout = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.timeout]), () => ( DEFAULT_TIMEOUT));
return new Promise((resolve, reject) => {
const handleResult = (error, stdout, stderr, isRetry) => {
if (!error) {
resolve(parseStdout(stdout));
return;
}
if (error.code === 1) {
resolve([]);
return;
}
const CRITICAL_CODES = ["ENOENT", "EACCES", "EPERM"];
if (CRITICAL_CODES.includes(error.code)) {
reject(error);
return;
}
if (!isRetry && isEagainError(stderr)) {
_child_process.execFile.call(void 0,
rgPath,
["-j", "1", ...args, target],
{
maxBuffer: MAX_BUFFER_SIZE,
signal: _optionalChain([options, 'optionalAccess', _2 => _2.signal]),
timeout,
killSignal: "SIGKILL"
},
(retryErr, retryStdout, retryStderr) => {
handleResult(retryErr, retryStdout, retryStderr, true);
}
);
return;
}
const hasOutput = stdout && stdout.trim().length > 0;
const isTimeout = error.signal === "SIGTERM" || error.signal === "SIGKILL" || error.code === "ABORT_ERR";
const isBufferOverflow = error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
let lines = [];
if (hasOutput) {
lines = parseStdout(stdout);
if (lines.length > 0 && (isTimeout || isBufferOverflow)) {
lines = lines.slice(0, -1);
}
}
if (isTimeout && lines.length === 0) {
reject(
new RipgrepTimeoutError(
`Ripgrep search timed out after ${timeout / 1e3} seconds. Try a more specific path or pattern.`,
lines
)
);
return;
}
resolve(lines);
};
_child_process.execFile.call(void 0,
rgPath,
[...args, target],
{
maxBuffer: MAX_BUFFER_SIZE,
signal: _optionalChain([options, 'optionalAccess', _3 => _3.signal]),
timeout,
killSignal: "SIGKILL"
},
(error, stdout, stderr) => {
handleResult(error, stdout, stderr, false);
}
);
});
}
exports.executeRipgrep = executeRipgrep;
// src/shared/ripgrep.ts
import { execFile, execFileSync } from "child_process";
var MAX_BUFFER_SIZE = 2e7;
var DEFAULT_TIMEOUT = 2e4;
var INSTALL_URL = "https://github.com/BurntSushi/ripgrep#installation";
var RipgrepNotFoundError = class extends Error {
constructor(message) {
super(
message ?? `ripgrep (rg) not found on PATH. Install it from: ${INSTALL_URL}`
);
this.name = "RipgrepNotFoundError";
}
};
var RipgrepTimeoutError = class extends Error {
/**
* Lines captured from stdout before the process was killed.
* May be empty if no output arrived before the timeout.
*/
partialResults;
constructor(message, partialResults) {
super(message);
this.name = "RipgrepTimeoutError";
this.partialResults = partialResults;
}
};
function findRg() {
try {
const result = execFileSync("which", ["rg"], {
encoding: "utf-8",
timeout: 5e3
});
const rgPath = result.trim();
if (!rgPath) {
throw new RipgrepNotFoundError();
}
return rgPath;
} catch (error) {
if (error instanceof RipgrepNotFoundError) {
throw error;
}
throw new RipgrepNotFoundError();
}
}
function isEagainError(stderr) {
return stderr.includes("os error 11") || stderr.includes("Resource temporarily unavailable");
}
function parseStdout(stdout) {
return stdout.trim().split("\n").map((line) => line.replace(/\r$/, "")).filter(Boolean);
}
async function executeRipgrep(args, target, options) {
const rgPath = findRg();
const timeout = options?.timeout ?? DEFAULT_TIMEOUT;
return new Promise((resolve, reject) => {
const handleResult = (error, stdout, stderr, isRetry) => {
if (!error) {
resolve(parseStdout(stdout));
return;
}
if (error.code === 1) {
resolve([]);
return;
}
const CRITICAL_CODES = ["ENOENT", "EACCES", "EPERM"];
if (CRITICAL_CODES.includes(error.code)) {
reject(error);
return;
}
if (!isRetry && isEagainError(stderr)) {
execFile(
rgPath,
["-j", "1", ...args, target],
{
maxBuffer: MAX_BUFFER_SIZE,
signal: options?.signal,
timeout,
killSignal: "SIGKILL"
},
(retryErr, retryStdout, retryStderr) => {
handleResult(retryErr, retryStdout, retryStderr, true);
}
);
return;
}
const hasOutput = stdout && stdout.trim().length > 0;
const isTimeout = error.signal === "SIGTERM" || error.signal === "SIGKILL" || error.code === "ABORT_ERR";
const isBufferOverflow = error.code === "ERR_CHILD_PROCESS_STDIO_MAXBUFFER";
let lines = [];
if (hasOutput) {
lines = parseStdout(stdout);
if (lines.length > 0 && (isTimeout || isBufferOverflow)) {
lines = lines.slice(0, -1);
}
}
if (isTimeout && lines.length === 0) {
reject(
new RipgrepTimeoutError(
`Ripgrep search timed out after ${timeout / 1e3} seconds. Try a more specific path or pattern.`,
lines
)
);
return;
}
resolve(lines);
};
execFile(
rgPath,
[...args, target],
{
maxBuffer: MAX_BUFFER_SIZE,
signal: options?.signal,
timeout,
killSignal: "SIGKILL"
},
(error, stdout, stderr) => {
handleResult(error, stdout, stderr, false);
}
);
});
}
export {
executeRipgrep
};
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
var _chunkMIYA7TNRcjs = require('./chunk-MIYA7TNR.cjs');
var _chunkBIAODQ2Pcjs = require('./chunk-BIAODQ2P.cjs');
var _chunkQZ5GS6HWcjs = require('./chunk-QZ5GS6HW.cjs');
var _chunkKONXT2SFcjs = require('./chunk-KONXT2SF.cjs');
// src/grep/index.ts
var _ai = require('ai');
var _zod = require('zod');
// src/grep/prompt.ts
function getPrompt() {
return `Search file contents using ripgrep. Supports regex patterns, context lines, and multiple output modes.
## When to Use
- To find where a function, variable, string, or pattern is used across files
- To search for specific code patterns, error messages, or configuration values
- To count occurrences of a pattern across a codebase
## When NOT to Use
- To find files by name/extension \u2014 use the dedicated file search tool instead
- To read a specific file \u2014 use the file reading tool instead
- Don't use shell commands (grep, rg) for content search when this tool is available
## Output Modes
- \`files_with_matches\` (default): Returns file paths containing matches, sorted by modification time. Best for discovering which files contain a pattern.
- \`content\`: Returns matching lines with optional context. Supports \`-A\` (after), \`-B\` (before), \`-C\` (context) for surrounding lines and \`-n\` for line numbers (default: true).
- \`count\`: Returns match counts per file with totals.
## Usage Guidelines
- Uses ripgrep regex syntax (not grep). Literal braces need escaping: use \`interface\\{\\}\` to find \`interface{}\`
- Filter files with the \`glob\` parameter (e.g., "*.js", "*.{ts,tsx}") or \`type\` parameter (e.g., "js", "py")
- Default head_limit is 250 entries. Pass \`head_limit: 0\` for unlimited (use sparingly \u2014 large results waste context)
- Use \`offset\` to paginate through large result sets
- Enable \`multiline: true\` for patterns that span lines (e.g., \`struct \\{[\\s\\S]*?field\`)
- Use \`-i: true\` for case-insensitive search`;
}
// src/grep/index.ts
var VCS_DIRS = [".git", ".svn", ".hg", ".bzr", ".jj", ".sl"];
var DEFAULT_HEAD_LIMIT = 250;
function applyHeadLimit(items, limit, offset = 0) {
if (limit === 0) return { items: items.slice(offset), appliedLimit: void 0 };
const cap = _nullishCoalesce(limit, () => ( DEFAULT_HEAD_LIMIT));
const sliced = items.slice(offset, offset + cap);
const truncated = items.length - offset > cap;
return { items: sliced, appliedLimit: truncated ? cap : void 0 };
}
function truncationSuffix(appliedLimit, offset) {
const parts = [];
if (appliedLimit !== void 0) parts.push(`limit: ${appliedLimit}`);
if (offset > 0) parts.push(`offset: ${offset}`);
return parts.length > 0 ? `
[Results truncated. ${parts.join(", ")}]` : "";
}
function relativizeLine(line, baseCwd, last = false) {
const idx = last ? line.lastIndexOf(":") : line.indexOf(":");
if (idx > 0) {
return _chunkQZ5GS6HWcjs.toRelativePath.call(void 0, line.substring(0, idx), baseCwd) + line.substring(idx);
}
return line;
}
function parseGlobPatterns(globFilter) {
const patterns = [];
for (const raw of globFilter.split(/\s+/)) {
if (raw.includes("{") && raw.includes("}")) {
patterns.push(raw);
} else {
patterns.push(...raw.split(",").filter(Boolean));
}
}
return patterns;
}
function createGrep(config = {}) {
const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
return _ai.tool.call(void 0, {
description: _nullishCoalesce(config.description, () => ( getPrompt())),
inputSchema: _zod.z.object({
pattern: _zod.z.string().describe("The regular expression pattern to search for in file contents"),
path: _zod.z.string().optional().describe("File or directory to search in (rg PATH). Defaults to current working directory."),
glob: _zod.z.string().optional().describe('Glob pattern to filter files (e.g. "*.js", "*.{ts,tsx}") - maps to rg --glob'),
output_mode: _zod.z.enum(["content", "files_with_matches", "count"]).optional().describe('Output mode: "content" shows matching lines (supports -A/-B/-C context, -n line numbers, head_limit), "files_with_matches" shows file paths (supports head_limit), "count" shows match counts (supports head_limit). Defaults to "files_with_matches".'),
"-B": _zod.z.number().optional().describe('Number of lines to show before each match (rg -B). Requires output_mode: "content", ignored otherwise.'),
"-A": _zod.z.number().optional().describe('Number of lines to show after each match (rg -A). Requires output_mode: "content", ignored otherwise.'),
"-C": _zod.z.number().optional().describe("Alias for context."),
context: _zod.z.number().optional().describe('Number of lines to show before and after each match (rg -C). Requires output_mode: "content", ignored otherwise.'),
"-n": _zod.z.boolean().optional().describe('Show line numbers in output (rg -n). Requires output_mode: "content", ignored otherwise. Defaults to true.'),
"-i": _zod.z.boolean().optional().describe("Case insensitive search (rg -i)"),
type: _zod.z.string().optional().describe("File type to search (rg --type). Common types: js, py, rust, go, java, etc. More efficient than include for standard file types."),
head_limit: _zod.z.number().optional().describe('Limit output to first N lines/entries, equivalent to "| head -N". Works across all output modes: content (limits output lines), files_with_matches (limits file paths), count (limits count entries). Defaults to 250 when unspecified. Pass 0 for unlimited (use sparingly \u2014 large result sets waste context).'),
offset: _zod.z.number().optional().describe('Skip first N lines/entries before applying head_limit, equivalent to "| tail -n +N | head -N". Works across all output modes. Defaults to 0.'),
multiline: _zod.z.boolean().optional().describe("Enable multiline mode where . matches newlines and patterns can span lines (rg -U --multiline-dotall). Default: false.")
}),
execute: async (input) => {
try {
const {
pattern,
path,
glob: globFilter,
type: typeFilter,
output_mode: outputMode = "files_with_matches",
"-B": ctxBefore,
"-A": ctxAfter,
"-C": ctxC,
context: ctxAlias,
"-n": showLineNumbers = true,
"-i": caseInsensitive = false,
head_limit: headLimit,
offset = 0,
multiline = false
} = input;
const absolutePath = path ? _chunkQZ5GS6HWcjs.expandPath.call(void 0, path, cwd) : cwd;
const args = ["--hidden"];
for (const dir of VCS_DIRS) args.push("--glob", `!${dir}`);
args.push("--max-columns", "500");
if (multiline) args.push("-U", "--multiline-dotall");
if (caseInsensitive) args.push("-i");
if (outputMode === "files_with_matches") args.push("-l");
else if (outputMode === "count") args.push("-c");
if (showLineNumbers && outputMode === "content") args.push("-n");
if (outputMode === "content") {
if (ctxAlias !== void 0) args.push("-C", ctxAlias.toString());
else if (ctxC !== void 0) args.push("-C", ctxC.toString());
else {
if (ctxBefore !== void 0) args.push("-B", ctxBefore.toString());
if (ctxAfter !== void 0) args.push("-A", ctxAfter.toString());
}
}
if (pattern.startsWith("-")) args.push("-e", pattern);
else args.push(pattern);
if (typeFilter) args.push("--type", typeFilter);
if (globFilter) {
for (const gp of parseGlobPatterns(globFilter)) args.push("--glob", gp);
}
const results = await _chunkMIYA7TNRcjs.executeRipgrep.call(void 0, args, absolutePath);
if (results.length === 0) return "No matches found";
if (outputMode === "content") {
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
const lines = items2.map((l) => relativizeLine(l, cwd));
return lines.join("\n") + truncationSuffix(appliedLimit2, offset);
}
if (outputMode === "count") {
const { items: items2, appliedLimit: appliedLimit2 } = applyHeadLimit(results, headLimit, offset);
const lines = items2.map((l) => relativizeLine(l, cwd, true));
let totalMatches = 0;
let fileCount = 0;
for (const line of lines) {
const idx = line.lastIndexOf(":");
if (idx > 0) {
const n = parseInt(line.substring(idx + 1), 10);
if (!isNaN(n)) {
totalMatches += n;
fileCount += 1;
}
}
}
return lines.join("\n") + `
Total: ${totalMatches} matches in ${fileCount} files` + truncationSuffix(appliedLimit2, offset);
}
const stats = await Promise.allSettled(results.map((f) => _chunkBIAODQ2Pcjs.getFileStats.call(void 0, f)));
const sorted = results.map((fp, i) => {
const r = stats[i];
const mt = r.status === "fulfilled" ? _nullishCoalesce(r.value.mtimeMs, () => ( 0)) : 0;
return [fp, mt];
}).sort((a, b) => {
const d = b[1] - a[1];
return d !== 0 ? d : a[0].localeCompare(b[0]);
}).map((e) => e[0]);
const { items, appliedLimit } = applyHeadLimit(sorted, headLimit, offset);
const relative = items.map((f) => _chunkQZ5GS6HWcjs.toRelativePath.call(void 0, f, cwd));
return relative.join("\n") + truncationSuffix(appliedLimit, offset);
} catch (error) {
const msg = _chunkKONXT2SFcjs.extractErrorMessage.call(void 0, error);
return `Error [grep]: Failed to search: ${msg}`;
}
}
});
}
var grep = createGrep();
exports.getPrompt = getPrompt; exports.createGrep = createGrep; exports.grep = grep;
import {
extractErrorMessage
} from "./chunk-X6ZY2KFU.js";
// src/bash/index.ts
import { tool } from "ai";
import { z } from "zod";
// src/shared/shell.ts
import { spawn } from "child_process";
var MAX_BUFFER_BYTES = 10 * 1024 * 1024;
var DEFAULT_TIMEOUT_MS = 12e4;
var SIGKILL_GRACE_MS = 5e3;
async function executeShell(command, options) {
const shellBin = options?.shell ?? process.env.SHELL ?? "/bin/bash";
const timeout = options?.timeout ?? DEFAULT_TIMEOUT_MS;
return new Promise((resolve, reject) => {
const child = spawn(shellBin, ["-c", command], {
cwd: options?.cwd,
stdio: ["pipe", "pipe", "pipe"],
// Prevent visible console window on Windows (no-op elsewhere)
windowsHide: true
});
let stdout = "";
let stderr = "";
let stdoutBytes = 0;
let stderrBytes = 0;
let settled = false;
let timeoutId;
let graceId;
function cleanup() {
if (timeoutId !== void 0) {
clearTimeout(timeoutId);
timeoutId = void 0;
}
if (graceId !== void 0) {
clearTimeout(graceId);
graceId = void 0;
}
if (abortHandler && options?.signal) {
options.signal.removeEventListener("abort", abortHandler);
}
}
function settle(result) {
if (settled) return;
settled = true;
cleanup();
resolve(result);
}
child.stdout.on("data", (chunk) => {
if (stdoutBytes >= MAX_BUFFER_BYTES) return;
const str = chunk.toString();
const remaining = MAX_BUFFER_BYTES - stdoutBytes;
if (chunk.length > remaining) {
stdout += str.slice(0, remaining);
stdoutBytes = MAX_BUFFER_BYTES;
} else {
stdout += str;
stdoutBytes += chunk.length;
}
});
child.stderr.on("data", (chunk) => {
if (stderrBytes >= MAX_BUFFER_BYTES) return;
const str = chunk.toString();
const remaining = MAX_BUFFER_BYTES - stderrBytes;
if (chunk.length > remaining) {
stderr += str.slice(0, remaining);
stderrBytes = MAX_BUFFER_BYTES;
} else {
stderr += str;
stderrBytes += chunk.length;
}
});
child.on("exit", (code, signal) => {
const exitCode = code !== null ? code : signal === "SIGKILL" ? 137 : signal === "SIGTERM" ? 143 : 1;
settle({ stdout, stderr, exitCode });
});
child.on("error", (err) => {
if (settled) return;
settled = true;
cleanup();
reject(err);
});
if (timeout > 0) {
timeoutId = setTimeout(() => {
timeoutId = void 0;
if (settled) return;
child.kill("SIGTERM");
graceId = setTimeout(() => {
graceId = void 0;
if (settled) return;
child.kill("SIGKILL");
}, SIGKILL_GRACE_MS);
}, timeout);
}
const abortHandler = options?.signal ? () => {
if (settled) return;
child.kill("SIGTERM");
} : void 0;
if (abortHandler && options?.signal) {
if (options.signal.aborted) {
child.kill("SIGTERM");
} else {
options.signal.addEventListener("abort", abortHandler, { once: true });
}
}
});
}
// src/bash/prompt.ts
function getPrompt(config = {}) {
const timeout = config.timeout ?? 12e4;
const timeoutMin = timeout / 6e4;
const shell = config.shell ?? "$SHELL or /bin/bash";
return `Execute a shell command and return its output (stdout, stderr, exit code).
Runs the command in ${shell} with \`-c\`. The working directory persists between calls.
## When to Use
- Build commands, git operations, system administration, installing packages
- Running scripts, compiling code, process management
- Any shell task that doesn't have a dedicated tool available
## When NOT to Use
- Reading file contents \u2014 use the dedicated file reading tool instead
- Searching file contents \u2014 use the dedicated content search tool instead
- Finding files by name/pattern \u2014 use the dedicated file search tool instead
- Editing files \u2014 use the dedicated file editing tool instead
- Writing new files \u2014 use the dedicated file writing tool instead
Prefer dedicated tools over shell equivalents (e.g., don't use cat, head, tail, sed, awk, grep, find, or echo when a dedicated tool exists). Dedicated tools provide better output formatting and permission handling.
## Usage Guidelines
- Default timeout: ${timeout}ms (${timeoutMin} minutes). Override with the timeout parameter.
- Timeout escalation: SIGTERM first, then SIGKILL after 5-second grace period.
- Output is capped at 10 MB per stream (stdout/stderr).
- Always quote file paths containing spaces with double quotes.
- When issuing multiple commands:
- Independent commands: make separate tool calls in parallel.
- Sequential with dependency: chain with \`&&\`.
- Sequential ignoring failures: chain with \`;\`.
- Do NOT use newlines to separate commands.
- Avoid unnecessary \`sleep\` commands:
- Don't sleep between commands that can run immediately.
- Don't retry failing commands in a sleep loop \u2014 diagnose the root cause.
- If you must sleep, keep it short (1-5 seconds).
- For git commands:
- Prefer creating new commits rather than amending existing ones.
- Never skip hooks (--no-verify) unless the user explicitly requests it.
- Before destructive operations (reset --hard, push --force), consider safer alternatives.`;
}
// src/bash/index.ts
function createBash(config = {}) {
const cwd = config.cwd ?? process.cwd();
const timeout = config.timeout ?? 12e4;
return tool({
description: config.description ?? getPrompt(config),
inputSchema: z.object({
command: z.string().describe("The shell command to execute"),
timeout: z.number().optional().describe("Timeout in milliseconds (default: 120000)"),
description: z.string().optional().describe("Human-readable description of what the command does")
}),
execute: async ({ command, timeout: cmdTimeout }) => {
try {
const result = await executeShell(command, {
cwd,
timeout: cmdTimeout ?? timeout,
shell: config.shell
});
const parts = [];
if (result.stdout) parts.push(result.stdout);
if (result.stderr) parts.push(`STDERR:
${result.stderr}`);
if (result.exitCode !== 0) parts.push(`Exit code: ${result.exitCode}`);
return parts.length > 0 ? parts.join("\n") : `Command completed with exit code ${result.exitCode}`;
} catch (error) {
const msg = extractErrorMessage(error);
return `Error [bash]: Failed to execute command: ${msg}`;
}
}
});
}
var bash = createBash();
export {
getPrompt,
createBash,
bash
};
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
var _chunkMIYA7TNRcjs = require('./chunk-MIYA7TNR.cjs');
var _chunkQZ5GS6HWcjs = require('./chunk-QZ5GS6HW.cjs');
var _chunkKONXT2SFcjs = require('./chunk-KONXT2SF.cjs');
// src/glob/index.ts
var _ai = require('ai');
var _zod = require('zod');
// src/shared/glob.ts
var _path = require('path');
function extractGlobBaseDirectory(pattern) {
const globChars = /[*?[{]/;
const match = pattern.match(globChars);
if (!match || match.index === void 0) {
const dir = _path.dirname.call(void 0, pattern);
const file = _path.basename.call(void 0, pattern);
return { baseDir: dir, relativePattern: file };
}
const staticPrefix = pattern.slice(0, match.index);
const lastSepIndex = Math.max(
staticPrefix.lastIndexOf("/"),
staticPrefix.lastIndexOf(_path.sep)
);
if (lastSepIndex === -1) {
return { baseDir: "", relativePattern: pattern };
}
let baseDir = staticPrefix.slice(0, lastSepIndex);
const relativePattern = pattern.slice(lastSepIndex + 1);
if (baseDir === "" && lastSepIndex === 0) {
baseDir = "/";
}
return { baseDir, relativePattern };
}
async function glob(pattern, cwd, options) {
const limit = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _ => _.limit]), () => ( 100));
const offset = _nullishCoalesce(_optionalChain([options, 'optionalAccess', _2 => _2.offset]), () => ( 0));
let searchDir = cwd;
let searchPattern = pattern;
if (_path.isAbsolute.call(void 0, pattern)) {
const { baseDir, relativePattern } = extractGlobBaseDirectory(pattern);
if (baseDir) {
searchDir = baseDir;
searchPattern = relativePattern;
}
}
const args = [
"--files",
"--glob",
searchPattern,
"--sort=modified",
"--hidden",
"--no-ignore"
];
const allPaths = await _chunkMIYA7TNRcjs.executeRipgrep.call(void 0, args, searchDir, {
signal: _optionalChain([options, 'optionalAccess', _3 => _3.signal])
});
const absolutePaths = allPaths.map(
(p) => _path.isAbsolute.call(void 0, p) ? p : _path.join.call(void 0, searchDir, p)
);
const truncated = absolutePaths.length > offset + limit;
const files = absolutePaths.slice(offset, offset + limit);
return { files, truncated };
}
// src/glob/prompt.ts
function getPrompt() {
return `Find files matching a glob pattern. Returns absolute file paths sorted by modification time (newest first).
Fast file pattern matching powered by ripgrep. Supports patterns like "**/*.ts", "src/**/*.js", or "*.json".
## When to Use
- To find files by name or extension across a codebase
- To locate configuration files, test files, or specific file types
- To discover project structure and file organization
## When NOT to Use
- To search file *contents* \u2014 use the dedicated content search tool instead
- To read a specific file whose path you already know \u2014 use the file reading tool directly
## Usage Guidelines
- Results are sorted by modification time (most recently modified first)
- The optional \`path\` parameter lets you narrow the search to a specific directory
- Results may be truncated for very large result sets`;
}
// src/glob/index.ts
function createGlob(config = {}) {
const cwd = _nullishCoalesce(config.cwd, () => ( process.cwd()));
return _ai.tool.call(void 0, {
description: _nullishCoalesce(config.description, () => ( getPrompt())),
inputSchema: _zod.z.object({
pattern: _zod.z.string().describe("Glob pattern to match files against"),
path: _zod.z.string().optional().describe("Directory to search in. Defaults to the working directory.")
}),
execute: async ({ pattern, path }) => {
try {
const searchDir = path ? _chunkQZ5GS6HWcjs.expandPath.call(void 0, path, cwd) : cwd;
const { files, truncated } = await glob(pattern, searchDir);
if (files.length === 0) {
return "No files found";
}
const header = truncated ? `Found ${files.length}+ files (results truncated)` : `Found ${files.length} files`;
return `${header}
${files.join("\n")}`;
} catch (error) {
const message = _chunkKONXT2SFcjs.extractErrorMessage.call(void 0, error);
return `Error [glob]: Failed to search for files: ${message}`;
}
}
});
}
var glob2 = createGlob();
exports.getPrompt = getPrompt; exports.createGlob = createGlob; exports.glob = glob2;