| "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 | ||
| }; |
@@ -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"; |
+5
-5
@@ -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; |
+4
-4
@@ -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, |
+4
-2
| { | ||
| "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; |
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Shell access
Supply chain riskThis module accesses the system shell. Accessing the system shell increases the risk of executing arbitrary code.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
396521
0.08%7839
0.27%21
-8.7%