@wrongstack/tools 0.77.0 → 0.82.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/dist/audit.d.ts +4 -4
  2. package/dist/audit.js +10 -2
  3. package/dist/audit.js.map +1 -1
  4. package/dist/{background-indexer-C70RD7LU.d.ts → background-indexer-DYm1FUxK.d.ts} +19 -19
  5. package/dist/bash.d.ts +4 -4
  6. package/dist/bash.js +18 -4
  7. package/dist/bash.js.map +1 -1
  8. package/dist/batch-tool-use.d.ts +4 -4
  9. package/dist/batch-tool-use.js.map +1 -1
  10. package/dist/builtin.js +173 -70
  11. package/dist/builtin.js.map +1 -1
  12. package/dist/circuit-breaker.d.ts +6 -6
  13. package/dist/circuit-breaker.js.map +1 -1
  14. package/dist/codebase-index/index.d.ts +12 -12
  15. package/dist/codebase-index/index.js +79 -41
  16. package/dist/codebase-index/index.js.map +1 -1
  17. package/dist/diff.d.ts +7 -7
  18. package/dist/diff.js.map +1 -1
  19. package/dist/document.d.ts +6 -6
  20. package/dist/document.js.map +1 -1
  21. package/dist/edit.d.ts +1 -1
  22. package/dist/edit.js.map +1 -1
  23. package/dist/exec.d.ts +3 -3
  24. package/dist/exec.js +15 -3
  25. package/dist/exec.js.map +1 -1
  26. package/dist/fetch.d.ts +1 -1
  27. package/dist/fetch.js +3 -1
  28. package/dist/fetch.js.map +1 -1
  29. package/dist/format.d.ts +4 -4
  30. package/dist/format.js +18 -4
  31. package/dist/format.js.map +1 -1
  32. package/dist/git.d.ts +10 -10
  33. package/dist/git.js +8 -2
  34. package/dist/git.js.map +1 -1
  35. package/dist/glob.d.ts +2 -2
  36. package/dist/glob.js.map +1 -1
  37. package/dist/grep.d.ts +6 -6
  38. package/dist/grep.js +10 -2
  39. package/dist/grep.js.map +1 -1
  40. package/dist/index.d.ts +7 -7
  41. package/dist/index.js +173 -70
  42. package/dist/index.js.map +1 -1
  43. package/dist/install.d.ts +5 -5
  44. package/dist/install.js +18 -4
  45. package/dist/install.js.map +1 -1
  46. package/dist/json.d.ts +8 -8
  47. package/dist/json.js.map +1 -1
  48. package/dist/lint.d.ts +4 -4
  49. package/dist/lint.js +18 -4
  50. package/dist/lint.js.map +1 -1
  51. package/dist/logs.d.ts +8 -8
  52. package/dist/logs.js.map +1 -1
  53. package/dist/memory.d.ts +2 -2
  54. package/dist/memory.js.map +1 -1
  55. package/dist/mode.d.ts +3 -3
  56. package/dist/mode.js.map +1 -1
  57. package/dist/outdated.d.ts +4 -4
  58. package/dist/outdated.js.map +1 -1
  59. package/dist/pack.js +173 -70
  60. package/dist/pack.js.map +1 -1
  61. package/dist/patch.d.ts +3 -3
  62. package/dist/patch.js.map +1 -1
  63. package/dist/process-registry.d.ts +3 -3
  64. package/dist/process-registry.js +7 -1
  65. package/dist/process-registry.js.map +1 -1
  66. package/dist/read.d.ts +2 -2
  67. package/dist/read.js.map +1 -1
  68. package/dist/replace.d.ts +4 -4
  69. package/dist/replace.js +8 -2
  70. package/dist/replace.js.map +1 -1
  71. package/dist/scaffold.d.ts +2 -2
  72. package/dist/scaffold.js.map +1 -1
  73. package/dist/search.d.ts +2 -2
  74. package/dist/search.js +16 -8
  75. package/dist/search.js.map +1 -1
  76. package/dist/test.d.ts +7 -7
  77. package/dist/test.js +18 -4
  78. package/dist/test.js.map +1 -1
  79. package/dist/tool-help.d.ts +4 -4
  80. package/dist/tool-help.js.map +1 -1
  81. package/dist/tool-search.d.ts +5 -5
  82. package/dist/tool-search.js.map +1 -1
  83. package/dist/tool-use.d.ts +2 -2
  84. package/dist/tool-use.js.map +1 -1
  85. package/dist/tree.d.ts +7 -7
  86. package/dist/tree.js +10 -2
  87. package/dist/tree.js.map +1 -1
  88. package/dist/typecheck.d.ts +4 -4
  89. package/dist/typecheck.js +18 -4
  90. package/dist/typecheck.js.map +1 -1
  91. package/dist/write.d.ts +1 -1
  92. package/dist/write.js.map +1 -1
  93. package/package.json +2 -2
package/dist/grep.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/_regex.ts","../src/_util.ts","../src/grep.ts"],"names":["resolve","path2","stat"],"mappings":"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","file":"grep.js","sourcesContent":["/**\n * Compile a user-supplied regex with conservative bounds against ReDoS.\n *\n * Node's regex engine (V8) is backtracking-based and cannot interrupt a\n * synchronous match — a pattern like `(a+)+$` against a sufficiently long\n * line will pin a worker for seconds. The executor's outer `timeoutMs` only\n * fires between async boundaries, so a long regex eval inside a sync loop\n * is uninterruptible.\n *\n * We can't fully prevent ReDoS without an alternative engine (re2-wasm), but\n * we can sharply limit the blast radius:\n *\n * 1. Cap pattern length — practically all legitimate user patterns are\n * under 256 characters. A 4 KB pattern is almost certainly malicious\n * or a copy-paste accident.\n * 2. Reject patterns containing the most obvious super-linear structures.\n * This is a coarse filter (false-positives are likely; we accept that\n * for hostile-input contexts).\n *\n * Callers should additionally bound the *subject* length (e.g. by capping\n * line size before matching).\n */\n\nconst MAX_PATTERN_LEN = 256;\n\n// Heuristics for catastrophic-backtracking constructs. Not exhaustive; bias\n// toward false-positives in tools that accept LLM-generated input.\nconst DANGEROUS_PATTERNS: ReadonlyArray<RegExp> = [\n // (a+)+, (.*)+, etc — nested quantifier on a group with internal quantifier\n /(\\([^)]*[+*][^)]*\\))[+*]/,\n /(\\(\\?:[^)]*[+*][^)]*\\))[+*]/,\n // Adjacent quantifiers: a++ a*+\n /[+*]{2,}/,\n // Quantifier on alternation with length 2+\n /\\([^|)]+\\|[^)]+\\)[+*][+*]/,\n // Greedy quantifier inside lookahead/lookbehind — (?!.*a+)\n /[\\(\\[][^)\\]]*[+*][^)\\]]*[\\)\\]][^)]*\\?\\??/,\n];\n\nexport interface CompileResult {\n ok: true;\n regex: RegExp;\n}\n\nexport interface CompileFail {\n ok: false;\n reason: string;\n}\n\nexport function compileUserRegex(pattern: string, flags: string): CompileResult | CompileFail {\n if (typeof pattern !== 'string') {\n return { ok: false, reason: 'pattern must be a string' };\n }\n if (pattern.length === 0) {\n return { ok: false, reason: 'pattern is empty' };\n }\n if (pattern.length > MAX_PATTERN_LEN) {\n return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };\n }\n for (const rx of DANGEROUS_PATTERNS) {\n if (rx.test(pattern)) {\n return {\n ok: false,\n reason:\n 'pattern looks vulnerable to catastrophic backtracking — rewrite without nested quantifiers',\n };\n }\n }\n try {\n return { ok: true, regex: new RegExp(pattern, flags) };\n } catch (err) {\n return {\n ok: false,\n reason: err instanceof Error ? err.message : 'invalid regex',\n };\n }\n}\n\n/**\n * Truncate a subject line to a safe length for synchronous regex eval.\n * The cap is conservative; tools that need exact-line matching against very\n * long lines should use ripgrep externally rather than the native walker.\n */\nexport const MAX_SUBJECT_LEN = 64 * 1024;\n\nexport function capSubject(line: string): string {\n return line.length > MAX_SUBJECT_LEN ? line.slice(0, MAX_SUBJECT_LEN) : line;\n}\n","import * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core';\nimport type { Context } from '@wrongstack/core';\n\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm → yarn → npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const root = path.resolve(ctx.projectRoot);\n const target = path.resolve(absPath);\n const rel = path.relative(root, target);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(`Path \"${absPath}\" is outside project root \"${root}\"`);\n }\n return target;\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root→out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink — macOS `/var`→`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n const realRoot = await fsp.realpath(ctx.projectRoot).catch(() => path.resolve(ctx.projectRoot));\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n const rel = path.relative(realRoot, real);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoot}\"`,\n );\n }\n return;\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n…[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]…\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// ─── Command-output normalization (token-saving) ────────────────────────────\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) — never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only — it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(lines[i]!, `… ⟨repeated ${run}×⟩`);\n } else {\n for (let k = i; k < j; k++) out.push(lines[k]!);\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends — the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n…[truncated ${total - kept} bytes]…\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI → collapse\n * carriage-return progress → trim trailing whitespace → collapse identical\n * consecutive lines → squeeze blank-line runs → head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines → 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n","import { spawn } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { Tool, ToolStreamEvent } from '@wrongstack/core';\nimport { buildChildEnv, compileGlob } from '@wrongstack/core';\nimport { capSubject, compileUserRegex } from './_regex.js';\nimport { isBinaryBuffer, safeResolve } from './_util.js';\n\ninterface GrepInput {\n pattern: string;\n path?: string;\n glob?: string;\n output_mode?: 'content' | 'files_with_matches' | 'count';\n context_lines?: number;\n case_insensitive?: boolean;\n limit?: number;\n}\n\ninterface GrepOutput {\n matches: string[];\n count: number;\n truncated: boolean;\n used: 'rg' | 'native';\n}\n\nconst DEFAULT_IGNORE = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage'];\n\nexport const grepTool: Tool<GrepInput, GrepOutput> = {\n name: 'grep',\n category: 'Search',\n description:\n 'Search across files using a regular expression. This is one of the primary code search tools. ' +\n 'Prefers ripgrep for speed and features when available.',\n usageHint:\n 'POWERFUL CODE SEARCH TOOL:\\n\\n' +\n '- `pattern` is a regular expression.\\n' +\n '- Use `output_mode: \"content\"` (default) to get matching lines with context.\\n' +\n '- Use `\"files_with_matches\"` when you only need the list of files.\\n' +\n '- Use `\"count\"` for quick statistics.\\n' +\n '- `glob` and `path` let you narrow the search scope significantly.\\n' +\n '- Always prefer this over `bash grep` when searching code.',\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n maxOutputBytes: 131_072,\n timeoutMs: 10_000,\n inputSchema: {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'Regular expression pattern to search for in file contents.',\n },\n path: {\n type: 'string',\n description: 'Limit search to this directory or file (relative to project root).',\n },\n glob: {\n type: 'string',\n description: 'Glob filter for which files to include (e.g. \"**/*.ts\", \"src/**\").',\n },\n output_mode: {\n type: 'string',\n enum: ['content', 'files_with_matches', 'count'],\n description: 'Return style: detailed matches, just file list, or count only.',\n },\n context_lines: {\n type: 'integer',\n description: 'How many lines of surrounding context to include with each match.',\n },\n case_insensitive: {\n type: 'boolean',\n description: 'Ignore case when matching.',\n },\n limit: {\n type: 'integer',\n description: 'Maximum number of matches to return.',\n },\n },\n required: ['pattern'],\n },\n async execute(input, ctx, opts) {\n let final: GrepOutput | undefined;\n for await (const ev of grepTool.executeStream!(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('grep: stream ended without final event');\n return final;\n },\n async *executeStream(input, ctx, opts): AsyncGenerator<ToolStreamEvent<GrepOutput>> {\n if (!input?.pattern) throw new Error('grep: pattern is required');\n const base = input.path ? safeResolve(input.path, ctx) : ctx.cwd;\n const mode = input.output_mode ?? 'content';\n const limit = Math.max(1, Math.min(input.limit ?? 200, 2000));\n const validation = compileUserRegex(input.pattern, input.case_insensitive ? 'i' : '');\n if (!validation.ok) {\n throw new Error(`grep: ${validation.reason}`);\n }\n\n const rgAvailable = await detectRg(opts.signal);\n if (rgAvailable) {\n try {\n yield* runRgStream(input, base, mode, limit, opts.signal);\n return;\n } catch {\n // fall through to native\n }\n }\n yield { type: 'log', text: 'Falling back to native grep…' };\n const out = await runNative(input, base, mode, limit, opts.signal);\n yield { type: 'final', output: out };\n },\n};\n\nasync function detectRg(signal: AbortSignal): Promise<boolean> {\n return new Promise((resolve) => {\n try {\n const p = spawn('rg', ['--version'], { env: buildChildEnv(), stdio: 'ignore', signal });\n p.on('error', () => resolve(false));\n p.on('close', (code) => resolve(code === 0));\n } catch {\n resolve(false);\n }\n });\n}\n\nasync function* runRgStream(\n input: GrepInput,\n base: string,\n mode: 'content' | 'files_with_matches' | 'count',\n limit: number,\n signal: AbortSignal,\n): AsyncGenerator<ToolStreamEvent<GrepOutput>> {\n const args: string[] = ['--no-heading'];\n if (input.case_insensitive) args.push('-i');\n if (mode === 'files_with_matches') args.push('-l');\n if (mode === 'count') args.push('-c');\n if (mode === 'content') {\n args.push('-n');\n if (input.context_lines) args.push('-C', String(input.context_lines));\n }\n for (const ignored of DEFAULT_IGNORE) {\n args.push('--glob', `!${ignored}/**`, '--glob', `!**/${ignored}/**`);\n }\n if (input.glob) args.push('--glob', input.glob);\n args.push('--', input.pattern, base);\n\n const matches: string[] = [];\n let buf = '';\n let totalLines = 0;\n let totalCount = 0;\n let batchSinceFlush = 0;\n const FLUSH_AT = 16; // yield a partial_output every 16 matches\n // Cap on the in-progress line buffer. Without this, a single huge \"line\"\n // (e.g. a file with no newlines under a symlink) plus a fast producer\n // would let `buf` grow unbounded. 1 MB comfortably holds any realistic\n // grep hit; beyond that we kill the child and surface a truncation.\n const MAX_BUF_BYTES = 1_000_000;\n let bufOverflow = false;\n\n const child = spawn('rg', args, { signal, env: buildChildEnv(), stdio: ['ignore', 'pipe', 'pipe'] });\n\n type Chunk = { kind: 'out' | 'close' | 'error'; data: string };\n const queue: Chunk[] = [];\n let waiter: (() => void) | undefined;\n const wake = () => {\n if (waiter) {\n const w = waiter;\n waiter = undefined;\n w();\n }\n };\n child.stdout?.on('data', (c) => {\n queue.push({ kind: 'out', data: c.toString() });\n wake();\n });\n child.on('error', (e) => {\n queue.push({ kind: 'error', data: e.message });\n wake();\n });\n child.on('close', () => {\n queue.push({ kind: 'close', data: '' });\n wake();\n });\n\n let pendingBatch: string[] = [];\n let errored = false;\n for (;;) {\n while (queue.length === 0) {\n await new Promise<void>((r) => {\n waiter = r;\n });\n }\n const c = queue.shift()!;\n if (c.kind === 'error') {\n errored = true;\n continue;\n }\n if (c.kind === 'close') break;\n buf += c.data;\n // Guard against a pathological producer (e.g. matching a huge binary\n // without newlines) pinning memory. Kill the child and mark the result\n // truncated; whatever we already captured stays intact.\n if (buf.length > MAX_BUF_BYTES && !bufOverflow) {\n bufOverflow = true;\n buf = buf.slice(-MAX_BUF_BYTES);\n try {\n child.kill('SIGTERM');\n } catch {\n /* ignore */\n }\n }\n const idx = buf.lastIndexOf('\\n');\n if (idx === -1) continue;\n const ready = buf.slice(0, idx);\n buf = buf.slice(idx + 1);\n for (const line of ready.split('\\n')) {\n if (!line) continue;\n totalLines++;\n if (mode === 'count') totalCount += parseRgCountLine(line);\n if (matches.length < limit) {\n matches.push(line);\n pendingBatch.push(line);\n batchSinceFlush++;\n }\n }\n if (batchSinceFlush >= FLUSH_AT) {\n yield {\n type: 'partial_output',\n text: pendingBatch.join('\\n'),\n data: { matches_so_far: matches.length },\n };\n pendingBatch = [];\n batchSinceFlush = 0;\n }\n }\n\n if (buf.trim()) {\n for (const line of buf.split('\\n')) {\n if (!line) continue;\n totalLines++;\n if (mode === 'count') totalCount += parseRgCountLine(line);\n if (matches.length < limit) {\n matches.push(line);\n pendingBatch.push(line);\n }\n }\n }\n if (pendingBatch.length > 0) {\n yield {\n type: 'partial_output',\n text: pendingBatch.join('\\n'),\n data: { matches_so_far: matches.length },\n };\n }\n if (errored) throw new Error('rg: spawn error');\n\n yield {\n type: 'final',\n output: {\n matches,\n count: mode === 'count' ? totalCount : totalLines,\n truncated: totalLines > limit || bufOverflow,\n used: 'rg',\n },\n };\n}\n\nfunction parseRgCountLine(line: string): number {\n const idx = line.lastIndexOf(':');\n if (idx === -1) return 0;\n const n = Number.parseInt(line.slice(idx + 1), 10);\n return Number.isFinite(n) ? n : 0;\n}\n\nasync function runNative(\n input: GrepInput,\n base: string,\n mode: 'content' | 'files_with_matches' | 'count',\n limit: number,\n signal: AbortSignal,\n): Promise<GrepOutput> {\n const flags = input.case_insensitive ? 'i' : '';\n const compiled = compileUserRegex(input.pattern, flags);\n if (!compiled.ok) {\n throw new Error(`grep: ${compiled.reason}`);\n }\n const re = compiled.regex;\n const globRe = input.glob ? compileGlob(input.glob) : null;\n const matches: string[] = [];\n const fileMatches = new Map<string, number>();\n let total = 0;\n let stopped = false;\n\n const walk = async (dir: string): Promise<void> => {\n if (stopped || signal.aborted) return;\n let entries: import('node:fs').Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (stopped) return;\n if (DEFAULT_IGNORE.includes(e.name)) continue;\n // Skip symlinks entirely. fs.Dirent.isDirectory/isFile return the\n // symlink's TYPE without resolving, but following the link into\n // arbitrary places (e.g. ~/.ssh) is the security concern. Tools\n // that genuinely need to traverse symlinks should opt in explicitly.\n if (e.isSymbolicLink()) continue;\n const full = path.join(dir, e.name);\n if (e.isDirectory()) {\n await walk(full);\n } else if (e.isFile()) {\n if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;\n if (globRe) globRe.lastIndex = 0;\n try {\n const stat = await fs.stat(full);\n if (stat.size > 1_000_000) continue;\n const head = await fs.readFile(full);\n if (isBinaryBuffer(head)) continue;\n const text = head.toString('utf8');\n const lines = text.split(/\\r?\\n/);\n let fileHits = 0;\n for (let i = 0; i < lines.length; i++) {\n const ln = capSubject(lines[i] ?? '');\n re.lastIndex = 0;\n if (re.test(ln)) {\n fileHits++;\n total++;\n if (mode === 'content' && matches.length < limit) {\n matches.push(`${full}:${i + 1}:${ln}`);\n }\n }\n }\n if (fileHits > 0) {\n fileMatches.set(full, fileHits);\n if (mode === 'files_with_matches' && matches.length < limit) {\n matches.push(full);\n }\n if (mode === 'count' && matches.length < limit) {\n matches.push(`${full}:${fileHits}`);\n }\n }\n if (matches.length >= limit) stopped = true;\n } catch {\n // skip read errors\n }\n }\n }\n };\n await walk(base);\n\n return {\n matches,\n count: total,\n truncated: stopped,\n used: 'native',\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/_regex.ts","../src/_util.ts","../src/grep.ts"],"names":["resolve","path2","stat"],"mappings":";;;;;;;;AAuBA,IAAM,eAAA,GAAkB,GAAA;AAIxB,IAAM,kBAAA,GAA4C;AAAA;AAAA,EAEhD,0BAAA;AAAA,EACA,6BAAA;AAAA;AAAA,EAEA,UAAA;AAAA;AAAA,EAEA,2BAAA;AAAA;AAAA,EAEA;AACF,CAAA;AAYO,SAAS,gBAAA,CAAiB,SAAiB,KAAA,EAA4C;AAC5F,EAAA,IAAI,OAAO,YAAY,QAAA,EAAU;AAC/B,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,0BAAA,EAA2B;AAAA,EACzD;AACA,EAAA,IAAI,OAAA,CAAQ,WAAW,CAAA,EAAG;AACxB,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,kBAAA,EAAmB;AAAA,EACjD;AACA,EAAA,IAAI,OAAA,CAAQ,SAAS,eAAA,EAAiB;AACpC,IAAA,OAAO,EAAE,EAAA,EAAI,KAAA,EAAO,MAAA,EAAQ,CAAA,gBAAA,EAAmB,eAAe,CAAA,WAAA,CAAA,EAAc;AAAA,EAC9E;AACA,EAAA,KAAA,MAAW,MAAM,kBAAA,EAAoB;AACnC,IAAA,IAAI,EAAA,CAAG,IAAA,CAAK,OAAO,CAAA,EAAG;AACpB,MAAA,OAAO;AAAA,QACL,EAAA,EAAI,KAAA;AAAA,QACJ,MAAA,EACE;AAAA,OACJ;AAAA,IACF;AAAA,EACF;AACA,EAAA,IAAI;AACF,IAAA,OAAO,EAAE,IAAI,IAAA,EAAM,KAAA,EAAO,IAAI,MAAA,CAAO,OAAA,EAAS,KAAK,CAAA,EAAE;AAAA,EACvD,SAAS,GAAA,EAAK;AACZ,IAAA,OAAO;AAAA,MACL,EAAA,EAAI,KAAA;AAAA,MACJ,MAAA,EAAQ,GAAA,YAAe,KAAA,GAAQ,GAAA,CAAI,OAAA,GAAU;AAAA,KAC/C;AAAA,EACF;AACF;AAOO,IAAM,kBAAkB,EAAA,GAAK,IAAA;AAE7B,SAAS,WAAW,IAAA,EAAsB;AAC/C,EAAA,OAAO,KAAK,MAAA,GAAS,eAAA,GAAkB,KAAK,KAAA,CAAM,CAAA,EAAG,eAAe,CAAA,GAAI,IAAA;AAC1E;AC/CO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAY,IAAA,CAAA,UAAA,CAAW,KAAK,CAAA,GAAS,IAAA,CAAA,SAAA,CAAU,KAAK,CAAA,GAAS,IAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,GAAA,EAAK,KAAK,CAAA;AACrF;AAEO,SAAS,gBAAA,CAAiB,SAAiB,GAAA,EAAsB;AACtE,EAAA,MAAM,IAAA,GAAY,IAAA,CAAA,OAAA,CAAQ,GAAA,CAAI,WAAW,CAAA;AACzC,EAAA,MAAM,MAAA,GAAc,aAAQ,OAAO,CAAA;AACnC,EAAA,MAAM,GAAA,GAAW,IAAA,CAAA,QAAA,CAAS,IAAA,EAAM,MAAM,CAAA;AACtC,EAAA,IAAI,IAAI,UAAA,CAAW,IAAI,CAAA,IAAU,IAAA,CAAA,UAAA,CAAW,GAAG,CAAA,EAAG;AAChD,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,OAAO,CAAA,2BAAA,EAA8B,IAAI,CAAA,CAAA,CAAG,CAAA;AAAA,EACvE;AACA,EAAA,OAAO,MAAA;AACT;AAEO,SAAS,WAAA,CAAY,OAAe,GAAA,EAAsB;AAC/D,EAAA,OAAO,gBAAA,CAAiB,WAAA,CAAY,KAAA,EAAO,GAAG,GAAG,GAAG,CAAA;AACtD;AA2DO,SAAS,eAAe,GAAA,EAAsB;AACnD,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,GAAA,CAAI,GAAA,CAAI,QAAQ,IAAI,CAAA;AACrC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,EAAK,CAAA,EAAA,EAAK;AAC5B,IAAA,IAAI,GAAA,CAAI,CAAC,CAAA,KAAM,CAAA,EAAG,OAAO,IAAA;AAAA,EAC3B;AACA,EAAA,OAAO,KAAA;AACT;;;AC/GA,SAAS,cAAiB,KAAA,EAAgC;AACxD,EAAA,IAAI,KAAA,KAAU,IAAA,IAAQ,KAAA,KAAU,MAAA,EAAW;AACzC,IAAA,MAAM,IAAI,MAAM,8BAA8B,CAAA;AAAA,EAChD;AACA,EAAA,OAAO,KAAA;AACT;AAmBA,IAAM,iBAAiB,CAAC,cAAA,EAAgB,QAAQ,MAAA,EAAQ,OAAA,EAAS,SAAS,UAAU,CAAA;AAE7E,IAAM,QAAA,GAAwC;AAAA,EACnD,IAAA,EAAM,MAAA;AAAA,EACN,QAAA,EAAU,QAAA;AAAA,EACV,WAAA,EACE,sJAAA;AAAA,EAEF,SAAA,EACE,6XAAA;AAAA,EAOF,UAAA,EAAY,MAAA;AAAA,EACZ,QAAA,EAAU,KAAA;AAAA,EACV,YAAA,EAAc,CAAC,SAAS,CAAA;AAAA,EACxB,cAAA,EAAgB,MAAA;AAAA,EAChB,SAAA,EAAW,GAAA;AAAA,EACX,WAAA,EAAa;AAAA,IACX,IAAA,EAAM,QAAA;AAAA,IACN,UAAA,EAAY;AAAA,MACV,OAAA,EAAS;AAAA,QACP,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,IAAA,EAAM;AAAA,QACJ,IAAA,EAAM,QAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,WAAA,EAAa;AAAA,QACX,IAAA,EAAM,QAAA;AAAA,QACN,IAAA,EAAM,CAAC,SAAA,EAAW,oBAAA,EAAsB,OAAO,CAAA;AAAA,QAC/C,WAAA,EAAa;AAAA,OACf;AAAA,MACA,aAAA,EAAe;AAAA,QACb,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,gBAAA,EAAkB;AAAA,QAChB,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa;AAAA,OACf;AAAA,MACA,KAAA,EAAO;AAAA,QACL,IAAA,EAAM,SAAA;AAAA,QACN,WAAA,EAAa;AAAA;AACf,KACF;AAAA,IACA,QAAA,EAAU,CAAC,SAAS;AAAA,GACtB;AAAA,EACA,MAAM,OAAA,CAAQ,KAAA,EAAO,GAAA,EAAK,IAAA,EAAM;AAC9B,IAAA,IAAI,KAAA;AACJ,IAAA,MAAM,gBAAgB,QAAA,CAAS,aAAA;AAC/B,IAAA,IAAI,CAAC,aAAA,EAAe,MAAM,IAAI,MAAM,wCAAwC,CAAA;AAC5E,IAAA,WAAA,MAAiB,EAAA,IAAM,aAAA,CAAc,KAAA,EAAO,GAAA,EAAK,IAAI,CAAA,EAAG;AACtD,MAAA,IAAI,EAAA,CAAG,IAAA,KAAS,OAAA,EAAS,KAAA,GAAQ,EAAA,CAAG,MAAA;AAAA,IACtC;AACA,IAAA,IAAI,CAAC,KAAA,EAAO,MAAM,IAAI,MAAM,wCAAwC,CAAA;AACpE,IAAA,OAAO,KAAA;AAAA,EACT,CAAA;AAAA,EACA,OAAO,aAAA,CAAc,KAAA,EAAO,GAAA,EAAK,IAAA,EAAmD;AAClF,IAAA,IAAI,CAAC,KAAA,EAAO,OAAA,EAAS,MAAM,IAAI,MAAM,2BAA2B,CAAA;AAChE,IAAA,MAAM,IAAA,GAAO,MAAM,IAAA,GAAO,WAAA,CAAY,MAAM,IAAA,EAAM,GAAG,IAAI,GAAA,CAAI,GAAA;AAC7D,IAAA,MAAM,IAAA,GAAO,MAAM,WAAA,IAAe,SAAA;AAClC,IAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,IAAA,CAAK,IAAI,KAAA,CAAM,KAAA,IAAS,GAAA,EAAK,GAAI,CAAC,CAAA;AAC5D,IAAA,MAAM,aAAa,gBAAA,CAAiB,KAAA,CAAM,SAAS,KAAA,CAAM,gBAAA,GAAmB,MAAM,EAAE,CAAA;AACpF,IAAA,IAAI,CAAC,WAAW,EAAA,EAAI;AAClB,MAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,UAAA,CAAW,MAAM,CAAA,CAAE,CAAA;AAAA,IAC9C;AAEA,IAAA,MAAM,WAAA,GAAc,MAAM,QAAA,CAAS,IAAA,CAAK,MAAM,CAAA;AAC9C,IAAA,IAAI,WAAA,EAAa;AACf,MAAA,IAAI;AACF,QAAA,OAAO,YAAY,KAAA,EAAO,IAAA,EAAM,IAAA,EAAM,KAAA,EAAO,KAAK,MAAM,CAAA;AACxD,QAAA;AAAA,MACF,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,MAAM,EAAE,IAAA,EAAM,KAAA,EAAO,IAAA,EAAM,mCAAA,EAA+B;AAC1D,IAAA,MAAM,GAAA,GAAM,MAAM,SAAA,CAAU,KAAA,EAAO,MAAM,IAAA,EAAM,KAAA,EAAO,KAAK,MAAM,CAAA;AACjE,IAAA,MAAM,EAAE,IAAA,EAAM,OAAA,EAAS,MAAA,EAAQ,GAAA,EAAI;AAAA,EACrC;AACF;AAEA,eAAe,SAAS,MAAA,EAAuC;AAC7D,EAAA,OAAO,IAAI,OAAA,CAAQ,CAACA,QAAAA,KAAY;AAC9B,IAAA,IAAI;AACF,MAAA,MAAM,CAAA,GAAI,KAAA,CAAM,IAAA,EAAM,CAAC,WAAW,CAAA,EAAG,EAAE,GAAA,EAAK,aAAA,EAAc,EAAG,KAAA,EAAO,QAAA,EAAU,QAAQ,CAAA;AACtF,MAAA,CAAA,CAAE,EAAA,CAAG,OAAA,EAAS,MAAMA,QAAAA,CAAQ,KAAK,CAAC,CAAA;AAClC,MAAA,CAAA,CAAE,GAAG,OAAA,EAAS,CAAC,SAASA,QAAAA,CAAQ,IAAA,KAAS,CAAC,CAAC,CAAA;AAAA,IAC7C,CAAA,CAAA,MAAQ;AACN,MAAAA,SAAQ,KAAK,CAAA;AAAA,IACf;AAAA,EACF,CAAC,CAAA;AACH;AAEA,gBAAgB,WAAA,CACd,KAAA,EACA,IAAA,EACA,IAAA,EACA,OACA,MAAA,EAC6C;AAC7C,EAAA,MAAM,IAAA,GAAiB,CAAC,cAAc,CAAA;AACtC,EAAA,IAAI,KAAA,CAAM,gBAAA,EAAkB,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA;AAC1C,EAAA,IAAI,IAAA,KAAS,oBAAA,EAAsB,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA;AACjD,EAAA,IAAI,IAAA,KAAS,OAAA,EAAS,IAAA,CAAK,IAAA,CAAK,IAAI,CAAA;AACpC,EAAA,IAAI,SAAS,SAAA,EAAW;AACtB,IAAA,IAAA,CAAK,KAAK,IAAI,CAAA;AACd,IAAA,IAAI,KAAA,CAAM,eAAe,IAAA,CAAK,IAAA,CAAK,MAAM,MAAA,CAAO,KAAA,CAAM,aAAa,CAAC,CAAA;AAAA,EACtE;AACA,EAAA,KAAA,MAAW,WAAW,cAAA,EAAgB;AACpC,IAAA,IAAA,CAAK,IAAA,CAAK,UAAU,CAAA,CAAA,EAAI,OAAO,OAAO,QAAA,EAAU,CAAA,IAAA,EAAO,OAAO,CAAA,GAAA,CAAK,CAAA;AAAA,EACrE;AACA,EAAA,IAAI,MAAM,IAAA,EAAM,IAAA,CAAK,IAAA,CAAK,QAAA,EAAU,MAAM,IAAI,CAAA;AAC9C,EAAA,IAAA,CAAK,IAAA,CAAK,IAAA,EAAM,KAAA,CAAM,OAAA,EAAS,IAAI,CAAA;AAEnC,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,IAAI,GAAA,GAAM,EAAA;AACV,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,UAAA,GAAa,CAAA;AACjB,EAAA,IAAI,eAAA,GAAkB,CAAA;AACtB,EAAA,MAAM,QAAA,GAAW,EAAA;AAKjB,EAAA,MAAM,aAAA,GAAgB,GAAA;AACtB,EAAA,IAAI,WAAA,GAAc,KAAA;AAElB,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,IAAA,EAAM,IAAA,EAAM,EAAE,MAAA,EAAQ,GAAA,EAAK,aAAA,EAAc,EAAG,OAAO,CAAC,QAAA,EAAU,MAAA,EAAQ,MAAM,GAAG,CAAA;AAGnG,EAAA,MAAM,QAAiB,EAAC;AACxB,EAAA,IAAI,MAAA;AACJ,EAAA,MAAM,OAAO,MAAM;AACjB,IAAA,IAAI,MAAA,EAAQ;AACV,MAAA,MAAM,CAAA,GAAI,MAAA;AACV,MAAA,MAAA,GAAS,MAAA;AACT,MAAA,CAAA,EAAE;AAAA,IACJ;AAAA,EACF,CAAA;AACA,EAAA,KAAA,CAAM,MAAA,EAAQ,EAAA,CAAG,MAAA,EAAQ,CAAC,CAAA,KAAM;AAC9B,IAAA,KAAA,CAAM,IAAA,CAAK,EAAE,IAAA,EAAM,KAAA,EAAO,MAAM,CAAA,CAAE,QAAA,IAAY,CAAA;AAC9C,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,KAAA,CAAM,EAAA,CAAG,OAAA,EAAS,CAAC,CAAA,KAAM;AACvB,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,SAAS,IAAA,EAAM,CAAA,CAAE,SAAS,CAAA;AAC7C,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AACD,EAAA,KAAA,CAAM,EAAA,CAAG,SAAS,MAAM;AACtB,IAAA,KAAA,CAAM,KAAK,EAAE,IAAA,EAAM,OAAA,EAAS,IAAA,EAAM,IAAI,CAAA;AACtC,IAAA,IAAA,EAAK;AAAA,EACP,CAAC,CAAA;AAED,EAAA,IAAI,eAAyB,EAAC;AAC9B,EAAA,IAAI,OAAA,GAAU,KAAA;AACd,EAAA,WAAS;AACP,IAAA,OAAO,KAAA,CAAM,WAAW,CAAA,EAAG;AACzB,MAAA,MAAM,IAAI,OAAA,CAAc,CAAC,CAAA,KAAM;AAC7B,QAAA,MAAA,GAAS,CAAA;AAAA,MACX,CAAC,CAAA;AAAA,IACH;AACA,IAAA,MAAM,CAAA,GAAI,aAAA,CAAc,KAAA,CAAM,KAAA,EAAO,CAAA;AACrC,IAAA,IAAI,CAAA,CAAE,SAAS,OAAA,EAAS;AACtB,MAAA,OAAA,GAAU,IAAA;AACV,MAAA;AAAA,IACF;AACA,IAAA,IAAI,CAAA,CAAE,SAAS,OAAA,EAAS;AACxB,IAAA,GAAA,IAAO,CAAA,CAAE,IAAA;AAIT,IAAA,IAAI,GAAA,CAAI,MAAA,GAAS,aAAA,IAAiB,CAAC,WAAA,EAAa;AAC9C,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,GAAA,GAAM,GAAA,CAAI,KAAA,CAAM,CAAC,aAAa,CAAA;AAC9B,MAAA,IAAI;AACF,QAAA,KAAA,CAAM,KAAK,SAAS,CAAA;AAAA,MACtB,CAAA,CAAA,MAAQ;AAAA,MAER;AAAA,IACF;AACA,IAAA,MAAM,GAAA,GAAM,GAAA,CAAI,WAAA,CAAY,IAAI,CAAA;AAChC,IAAA,IAAI,QAAQ,EAAA,EAAI;AAChB,IAAA,MAAM,KAAA,GAAQ,GAAA,CAAI,KAAA,CAAM,CAAA,EAAG,GAAG,CAAA;AAC9B,IAAA,GAAA,GAAM,GAAA,CAAI,KAAA,CAAM,GAAA,GAAM,CAAC,CAAA;AACvB,IAAA,KAAA,MAAW,IAAA,IAAQ,KAAA,CAAM,KAAA,CAAM,IAAI,CAAA,EAAG;AACpC,MAAA,IAAI,CAAC,IAAA,EAAM;AACX,MAAA,UAAA,EAAA;AACA,MAAA,IAAI,IAAA,KAAS,OAAA,EAAS,UAAA,IAAc,gBAAA,CAAiB,IAAI,CAAA;AACzD,MAAA,IAAI,OAAA,CAAQ,SAAS,KAAA,EAAO;AAC1B,QAAA,OAAA,CAAQ,KAAK,IAAI,CAAA;AACjB,QAAA,YAAA,CAAa,KAAK,IAAI,CAAA;AACtB,QAAA,eAAA,EAAA;AAAA,MACF;AAAA,IACF;AACA,IAAA,IAAI,mBAAmB,QAAA,EAAU;AAC/B,MAAA,MAAM;AAAA,QACJ,IAAA,EAAM,gBAAA;AAAA,QACN,IAAA,EAAM,YAAA,CAAa,IAAA,CAAK,IAAI,CAAA;AAAA,QAC5B,IAAA,EAAM,EAAE,cAAA,EAAgB,OAAA,CAAQ,MAAA;AAAO,OACzC;AACA,MAAA,YAAA,GAAe,EAAC;AAChB,MAAA,eAAA,GAAkB,CAAA;AAAA,IACpB;AAAA,EACF;AAEA,EAAA,IAAI,GAAA,CAAI,MAAK,EAAG;AACd,IAAA,KAAA,MAAW,IAAA,IAAQ,GAAA,CAAI,KAAA,CAAM,IAAI,CAAA,EAAG;AAClC,MAAA,IAAI,CAAC,IAAA,EAAM;AACX,MAAA,UAAA,EAAA;AACA,MAAA,IAAI,IAAA,KAAS,OAAA,EAAS,UAAA,IAAc,gBAAA,CAAiB,IAAI,CAAA;AACzD,MAAA,IAAI,OAAA,CAAQ,SAAS,KAAA,EAAO;AAC1B,QAAA,OAAA,CAAQ,KAAK,IAAI,CAAA;AACjB,QAAA,YAAA,CAAa,KAAK,IAAI,CAAA;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AACA,EAAA,IAAI,YAAA,CAAa,SAAS,CAAA,EAAG;AAC3B,IAAA,MAAM;AAAA,MACJ,IAAA,EAAM,gBAAA;AAAA,MACN,IAAA,EAAM,YAAA,CAAa,IAAA,CAAK,IAAI,CAAA;AAAA,MAC5B,IAAA,EAAM,EAAE,cAAA,EAAgB,OAAA,CAAQ,MAAA;AAAO,KACzC;AAAA,EACF;AACA,EAAA,IAAI,OAAA,EAAS,MAAM,IAAI,KAAA,CAAM,iBAAiB,CAAA;AAE9C,EAAA,MAAM;AAAA,IACJ,IAAA,EAAM,OAAA;AAAA,IACN,MAAA,EAAQ;AAAA,MACN,OAAA;AAAA,MACA,KAAA,EAAO,IAAA,KAAS,OAAA,GAAU,UAAA,GAAa,UAAA;AAAA,MACvC,SAAA,EAAW,aAAa,KAAA,IAAS,WAAA;AAAA,MACjC,IAAA,EAAM;AAAA;AACR,GACF;AACF;AAEA,SAAS,iBAAiB,IAAA,EAAsB;AAC9C,EAAA,MAAM,GAAA,GAAM,IAAA,CAAK,WAAA,CAAY,GAAG,CAAA;AAChC,EAAA,IAAI,GAAA,KAAQ,IAAI,OAAO,CAAA;AACvB,EAAA,MAAM,CAAA,GAAI,OAAO,QAAA,CAAS,IAAA,CAAK,MAAM,GAAA,GAAM,CAAC,GAAG,EAAE,CAAA;AACjD,EAAA,OAAO,MAAA,CAAO,QAAA,CAAS,CAAC,CAAA,GAAI,CAAA,GAAI,CAAA;AAClC;AAEA,eAAe,SAAA,CACb,KAAA,EACA,IAAA,EACA,IAAA,EACA,OACA,MAAA,EACqB;AACrB,EAAA,MAAM,KAAA,GAAQ,KAAA,CAAM,gBAAA,GAAmB,GAAA,GAAM,EAAA;AAC7C,EAAA,MAAM,QAAA,GAAW,gBAAA,CAAiB,KAAA,CAAM,OAAA,EAAS,KAAK,CAAA;AACtD,EAAA,IAAI,CAAC,SAAS,EAAA,EAAI;AAChB,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,MAAA,EAAS,QAAA,CAAS,MAAM,CAAA,CAAE,CAAA;AAAA,EAC5C;AACA,EAAA,MAAM,KAAK,QAAA,CAAS,KAAA;AACpB,EAAA,MAAM,SAAS,KAAA,CAAM,IAAA,GAAO,WAAA,CAAY,KAAA,CAAM,IAAI,CAAA,GAAI,IAAA;AACtD,EAAA,MAAM,UAAoB,EAAC;AAC3B,EAAA,MAAM,WAAA,uBAAkB,GAAA,EAAoB;AAC5C,EAAA,IAAI,KAAA,GAAQ,CAAA;AACZ,EAAA,IAAI,OAAA,GAAU,KAAA;AAEd,EAAA,MAAM,IAAA,GAAO,OAAO,GAAA,KAA+B;AACjD,IAAA,IAAI,OAAA,IAAW,OAAO,OAAA,EAAS;AAC/B,IAAA,IAAI,OAAA;AACJ,IAAA,IAAI;AACF,MAAA,OAAA,GAAU,MAAS,EAAA,CAAA,OAAA,CAAQ,GAAA,EAAK,EAAE,aAAA,EAAe,MAAM,CAAA;AAAA,IACzD,CAAA,CAAA,MAAQ;AACN,MAAA;AAAA,IACF;AACA,IAAA,KAAA,MAAW,KAAK,OAAA,EAAS;AACvB,MAAA,IAAI,OAAA,EAAS;AACb,MAAA,IAAI,cAAA,CAAe,QAAA,CAAS,CAAA,CAAE,IAAI,CAAA,EAAG;AAKrC,MAAA,IAAI,CAAA,CAAE,gBAAe,EAAG;AACxB,MAAA,MAAM,IAAA,GAAYC,IAAA,CAAA,IAAA,CAAK,GAAA,EAAK,CAAA,CAAE,IAAI,CAAA;AAClC,MAAA,IAAI,CAAA,CAAE,aAAY,EAAG;AACnB,QAAA,MAAM,KAAK,IAAI,CAAA;AAAA,MACjB,CAAA,MAAA,IAAW,CAAA,CAAE,MAAA,EAAO,EAAG;AACrB,QAAA,IAAI,MAAA,IAAU,CAAC,MAAA,CAAO,IAAA,CAAK,CAAA,CAAE,IAAI,CAAA,IAAK,CAAC,MAAA,CAAO,IAAA,CAAK,IAAI,CAAA,EAAG;AAC1D,QAAA,IAAI,MAAA,SAAe,SAAA,GAAY,CAAA;AAC/B,QAAA,IAAI;AACF,UAAA,MAAMC,KAAAA,GAAO,MAAS,EAAA,CAAA,IAAA,CAAK,IAAI,CAAA;AAC/B,UAAA,IAAIA,KAAAA,CAAK,OAAO,GAAA,EAAW;AAC3B,UAAA,MAAM,IAAA,GAAO,MAAS,EAAA,CAAA,QAAA,CAAS,IAAI,CAAA;AACnC,UAAA,IAAI,cAAA,CAAe,IAAI,CAAA,EAAG;AAC1B,UAAA,MAAM,IAAA,GAAO,IAAA,CAAK,QAAA,CAAS,MAAM,CAAA;AACjC,UAAA,MAAM,KAAA,GAAQ,IAAA,CAAK,KAAA,CAAM,OAAO,CAAA;AAChC,UAAA,IAAI,QAAA,GAAW,CAAA;AACf,UAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,KAAA,CAAM,QAAQ,CAAA,EAAA,EAAK;AACrC,YAAA,MAAM,EAAA,GAAK,UAAA,CAAW,KAAA,CAAM,CAAC,KAAK,EAAE,CAAA;AACpC,YAAA,EAAA,CAAG,SAAA,GAAY,CAAA;AACf,YAAA,IAAI,EAAA,CAAG,IAAA,CAAK,EAAE,CAAA,EAAG;AACf,cAAA,QAAA,EAAA;AACA,cAAA,KAAA,EAAA;AACA,cAAA,IAAI,IAAA,KAAS,SAAA,IAAa,OAAA,CAAQ,MAAA,GAAS,KAAA,EAAO;AAChD,gBAAA,OAAA,CAAQ,IAAA,CAAK,GAAG,IAAI,CAAA,CAAA,EAAI,IAAI,CAAC,CAAA,CAAA,EAAI,EAAE,CAAA,CAAE,CAAA;AAAA,cACvC;AAAA,YACF;AAAA,UACF;AACA,UAAA,IAAI,WAAW,CAAA,EAAG;AAChB,YAAA,WAAA,CAAY,GAAA,CAAI,MAAM,QAAQ,CAAA;AAC9B,YAAA,IAAI,IAAA,KAAS,oBAAA,IAAwB,OAAA,CAAQ,MAAA,GAAS,KAAA,EAAO;AAC3D,cAAA,OAAA,CAAQ,KAAK,IAAI,CAAA;AAAA,YACnB;AACA,YAAA,IAAI,IAAA,KAAS,OAAA,IAAW,OAAA,CAAQ,MAAA,GAAS,KAAA,EAAO;AAC9C,cAAA,OAAA,CAAQ,IAAA,CAAK,CAAA,EAAG,IAAI,CAAA,CAAA,EAAI,QAAQ,CAAA,CAAE,CAAA;AAAA,YACpC;AAAA,UACF;AACA,UAAA,IAAI,OAAA,CAAQ,MAAA,IAAU,KAAA,EAAO,OAAA,GAAU,IAAA;AAAA,QACzC,CAAA,CAAA,MAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF,CAAA;AACA,EAAA,MAAM,KAAK,IAAI,CAAA;AAEf,EAAA,OAAO;AAAA,IACL,OAAA;AAAA,IACA,KAAA,EAAO,KAAA;AAAA,IACP,SAAA,EAAW,OAAA;AAAA,IACX,IAAA,EAAM;AAAA,GACR;AACF","file":"grep.js","sourcesContent":["/**\n * Compile a user-supplied regex with conservative bounds against ReDoS.\n *\n * Node's regex engine (V8) is backtracking-based and cannot interrupt a\n * synchronous match — a pattern like `(a+)+$` against a sufficiently long\n * line will pin a worker for seconds. The executor's outer `timeoutMs` only\n * fires between async boundaries, so a long regex eval inside a sync loop\n * is uninterruptible.\n *\n * We can't fully prevent ReDoS without an alternative engine (re2-wasm), but\n * we can sharply limit the blast radius:\n *\n * 1. Cap pattern length — practically all legitimate user patterns are\n * under 256 characters. A 4 KB pattern is almost certainly malicious\n * or a copy-paste accident.\n * 2. Reject patterns containing the most obvious super-linear structures.\n * This is a coarse filter (false-positives are likely; we accept that\n * for hostile-input contexts).\n *\n * Callers should additionally bound the *subject* length (e.g. by capping\n * line size before matching).\n */\n\nconst MAX_PATTERN_LEN = 256;\n\n// Heuristics for catastrophic-backtracking constructs. Not exhaustive; bias\n// toward false-positives in tools that accept LLM-generated input.\nconst DANGEROUS_PATTERNS: ReadonlyArray<RegExp> = [\n // (a+)+, (.*)+, etc — nested quantifier on a group with internal quantifier\n /(\\([^)]*[+*][^)]*\\))[+*]/,\n /(\\(\\?:[^)]*[+*][^)]*\\))[+*]/,\n // Adjacent quantifiers: a++ a*+\n /[+*]{2,}/,\n // Quantifier on alternation with length 2+\n /\\([^|)]+\\|[^)]+\\)[+*][+*]/,\n // Greedy quantifier inside lookahead/lookbehind — (?!.*a+)\n /[\\(\\[][^)\\]]*[+*][^)\\]]*[\\)\\]][^)]*\\?\\??/,\n];\n\nexport interface CompileResult {\n ok: true;\n regex: RegExp;\n}\n\nexport interface CompileFail {\n ok: false;\n reason: string;\n}\n\nexport function compileUserRegex(pattern: string, flags: string): CompileResult | CompileFail {\n if (typeof pattern !== 'string') {\n return { ok: false, reason: 'pattern must be a string' };\n }\n if (pattern.length === 0) {\n return { ok: false, reason: 'pattern is empty' };\n }\n if (pattern.length > MAX_PATTERN_LEN) {\n return { ok: false, reason: `pattern exceeds ${MAX_PATTERN_LEN} characters` };\n }\n for (const rx of DANGEROUS_PATTERNS) {\n if (rx.test(pattern)) {\n return {\n ok: false,\n reason:\n 'pattern looks vulnerable to catastrophic backtracking — rewrite without nested quantifiers',\n };\n }\n }\n try {\n return { ok: true, regex: new RegExp(pattern, flags) };\n } catch (err) {\n return {\n ok: false,\n reason: err instanceof Error ? err.message : 'invalid regex',\n };\n }\n}\n\n/**\n * Truncate a subject line to a safe length for synchronous regex eval.\n * The cap is conservative; tools that need exact-line matching against very\n * long lines should use ripgrep externally rather than the native walker.\n */\nexport const MAX_SUBJECT_LEN = 64 * 1024;\n\nexport function capSubject(line: string): string {\n return line.length > MAX_SUBJECT_LEN ? line.slice(0, MAX_SUBJECT_LEN) : line;\n}\n","import * as fsp from 'node:fs/promises';\nimport * as path from 'node:path';\nimport * as Core from '@wrongstack/core';\nimport type { Context } from '@wrongstack/core';\n\n\n\nfunction expectDefined<T>(value: T | null | undefined): T {\n if (value === null || value === undefined) {\n throw new Error('Expected value to be defined');\n }\n return value;\n}\n\n/** Detected package manager for a project directory. */\nexport type PackageManager = 'pnpm' | 'yarn' | 'npm';\n\n/**\n * Detect the project's package manager by inspecting lockfiles in `cwd`.\n * Order: pnpm → yarn → npm (default). Missing or unreadable directories fall\n * back to `npm` rather than throwing, so a `safeResolve`-checked cwd that\n * happens to be empty never aborts the tool.\n */\nexport async function detectPackageManager(cwd: string): Promise<PackageManager> {\n const { stat } = await import('node:fs/promises');\n try {\n await stat(`${cwd}/pnpm-lock.yaml`);\n return 'pnpm';\n } catch {\n /* not pnpm */\n }\n try {\n await stat(`${cwd}/yarn.lock`);\n return 'yarn';\n } catch {\n /* not yarn */\n }\n return 'npm';\n}\n\nexport function resolvePath(input: string, ctx: Context): string {\n return path.isAbsolute(input) ? path.normalize(input) : path.resolve(ctx.cwd, input);\n}\n\nexport function ensureInsideRoot(absPath: string, ctx: Context): string {\n const root = path.resolve(ctx.projectRoot);\n const target = path.resolve(absPath);\n const rel = path.relative(root, target);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(`Path \"${absPath}\" is outside project root \"${root}\"`);\n }\n return target;\n}\n\nexport function safeResolve(input: string, ctx: Context): string {\n return ensureInsideRoot(resolvePath(input, ctx), ctx);\n}\n\n/**\n * Defense against in-root→out-of-root symlink escape (CWE-59). `safeResolve`\n * only does a syntactic `../` check, so a symlink that lives *inside* the\n * project root but points outside still passes it. This resolves the path\n * through `fs.realpath` and re-verifies containment against the realpath of\n * the project root (comparing like-for-like, since the root itself may be a\n * symlink — macOS `/var`→`/private/var`, Windows 8.3 short names). For a path\n * that does not exist yet (e.g. a `write` to a new file) the nearest existing\n * ancestor directory is checked instead. Throws if the real target escapes.\n *\n * Mirrors the per-file guard already used in `replace.ts`/`grep.ts`; applied\n * to single-file `read`/`edit`/`write` it throws (rather than skips) because\n * the caller named exactly one file.\n */\nexport async function assertRealInsideRoot(absPath: string, ctx: Context): Promise<void> {\n const realRoot = await fsp.realpath(ctx.projectRoot).catch(() => path.resolve(ctx.projectRoot));\n let probe = absPath;\n for (;;) {\n let real: string;\n try {\n real = await fsp.realpath(probe);\n } catch (err) {\n if ((err as NodeJS.ErrnoException).code === 'ENOENT') {\n const parent = path.dirname(probe);\n if (parent === probe) return; // reached fs root without escaping\n probe = parent;\n continue;\n }\n throw err;\n }\n const rel = path.relative(realRoot, real);\n if (rel.startsWith('..') || path.isAbsolute(rel)) {\n throw new Error(\n `Path \"${absPath}\" resolves through a symlink outside project root \"${realRoot}\"`,\n );\n }\n return;\n }\n}\n\n/** `safeResolve` + symlink realpath containment check. Async. */\nexport async function safeResolveReal(input: string, ctx: Context): Promise<string> {\n const abs = safeResolve(input, ctx);\n await assertRealInsideRoot(abs, ctx);\n return abs;\n}\n\nexport function truncateMiddle(s: string, max: number): string {\n if (Buffer.byteLength(s, 'utf8') <= max) return s;\n const half = Math.floor(max / 2);\n return (\n s.slice(0, half) +\n `\\n…[truncated ${Buffer.byteLength(s, 'utf8') - max} bytes from middle]…\\n` +\n s.slice(-half)\n );\n}\n\nexport function isBinaryBuffer(buf: Buffer): boolean {\n const len = Math.min(buf.length, 8192);\n for (let i = 0; i < len; i++) {\n if (buf[i] === 0) return true;\n }\n return false;\n}\n\n// ─── Command-output normalization (token-saving) ────────────────────────────\n//\n// Raw process output is full of tokens the model gains nothing from: ANSI\n// escapes, carriage-return progress spam, runs of identical warning lines, and\n// huge tails of build noise. These helpers strip that noise before the output\n// reaches the LLM. They are scoped to COMMAND tools (bash/git/exec and the\n// _spawn-stream consumers) — never applied to structured/code outputs.\n\n/** Unified byte cap for all command tool output fed to the model. */\nexport const COMMAND_OUTPUT_MAX_BYTES = 32_768;\n\n/** Runs of >= this many identical consecutive lines are collapsed. */\nconst REPEAT_RUN_THRESHOLD = 3;\n\n/**\n * Collapse carriage-return overwrites the way a terminal would: `\\r\\n` becomes\n * `\\n`, and a bare `\\r` (progress redraw) keeps only the text after the LAST\n * `\\r` on its physical line. Without this, a single progress bar that redraws\n * 200 times explodes into 200 lines.\n */\nexport function collapseCarriageReturns(text: string): string {\n const lf = text.replace(/\\r\\n/g, '\\n');\n if (!lf.includes('\\r')) return lf;\n return lf\n .split('\\n')\n .map((line) => (line.includes('\\r') ? line.slice(line.lastIndexOf('\\r') + 1) : line))\n .join('\\n');\n}\n\n/**\n * Collapse a run of `minRun`+ identical consecutive lines into the line once\n * plus a marker. Consecutive-only — it never reorders or dedups non-adjacent\n * lines, so diffs/source stay intact.\n */\nexport function collapseConsecutiveDuplicates(text: string, minRun = REPEAT_RUN_THRESHOLD): string {\n const lines = text.split('\\n');\n const out: string[] = [];\n let i = 0;\n while (i < lines.length) {\n let j = i + 1;\n while (j < lines.length && lines[j] === lines[i]) j++;\n const run = j - i;\n if (run >= minRun) {\n out.push(expectDefined(lines[i]), `… ⟨repeated ${run}×⟩`);\n } else {\n for (let k = i; k < j; k++) out.push(expectDefined(lines[k]));\n }\n i = j;\n }\n return out.join('\\n');\n}\n\n/** Largest prefix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeHeadBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(0, mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(0, lo);\n}\n\n/** Largest suffix of `s` whose UTF-8 byte length is <= `maxBytes`. */\nfunction takeTailBytes(s: string, maxBytes: number): string {\n if (maxBytes <= 0) return '';\n if (Buffer.byteLength(s, 'utf8') <= maxBytes) return s;\n let lo = 0;\n let hi = s.length;\n while (lo < hi) {\n const mid = Math.ceil((lo + hi) / 2);\n if (Buffer.byteLength(s.slice(s.length - mid), 'utf8') <= maxBytes) lo = mid;\n else hi = mid - 1;\n }\n return s.slice(s.length - lo);\n}\n\n/**\n * Truncate to `maxBytes` keeping BOTH ends — the head (what ran / early context)\n * and the tail (errors and summaries usually land last), biased ~45/55 toward\n * the tail. The result never exceeds `maxBytes`.\n */\nexport function truncateHeadTail(s: string, maxBytes: number): string {\n const total = Buffer.byteLength(s, 'utf8');\n if (total <= maxBytes) return s;\n // Reserve a fixed allowance for the marker so the final string can't exceed\n // the cap even though the dropped-byte count's digit width varies.\n const MARKER_RESERVE = 64;\n const avail = Math.max(0, maxBytes - MARKER_RESERVE);\n const headBudget = Math.floor(avail * 0.45);\n const head = takeHeadBytes(s, headBudget);\n const tail = takeTailBytes(s, avail - Buffer.byteLength(head, 'utf8'));\n const kept = Buffer.byteLength(head, 'utf8') + Buffer.byteLength(tail, 'utf8');\n return `${head}\\n…[truncated ${total - kept} bytes]…\\n${tail}`;\n}\n\n/**\n * Full token-saving pipeline for command tool output: strip ANSI → collapse\n * carriage-return progress → trim trailing whitespace → collapse identical\n * consecutive lines → squeeze blank-line runs → head+tail truncate to the cap.\n */\nexport function normalizeCommandOutput(\n raw: string,\n opts: { maxBytes?: number | undefined } = {},\n): string {\n if (!raw) return raw;\n let text = Core.stripAnsi(raw);\n text = collapseCarriageReturns(text);\n text = text.replace(/[ \\t]+$/gm, ''); // trailing whitespace per line\n text = collapseConsecutiveDuplicates(text);\n text = text.replace(/\\n{3,}/g, '\\n\\n'); // >=2 blank lines → 1\n return truncateHeadTail(text, opts.maxBytes ?? COMMAND_OUTPUT_MAX_BYTES);\n}\n","import { spawn } from 'node:child_process';\nimport * as fs from 'node:fs/promises';\nimport * as path from 'node:path';\nimport type { Tool, ToolStreamEvent } from '@wrongstack/core';\nimport { buildChildEnv, compileGlob } from '@wrongstack/core';\nimport { capSubject, compileUserRegex } from './_regex.js';\nimport { isBinaryBuffer, safeResolve } from './_util.js';\n\n\n\nfunction expectDefined<T>(value: T | null | undefined): T {\n if (value === null || value === undefined) {\n throw new Error('Expected value to be defined');\n }\n return value;\n}\n\ninterface GrepInput {\n pattern: string;\n path?: string | undefined;\n glob?: string | undefined;\n output_mode?: 'content' | 'files_with_matches' | 'count' | undefined;\n context_lines?: number | undefined;\n case_insensitive?: boolean | undefined;\n limit?: number | undefined;\n}\n\ninterface GrepOutput {\n matches: string[];\n count: number;\n truncated: boolean;\n used: 'rg' | 'native';\n}\n\nconst DEFAULT_IGNORE = ['node_modules', '.git', 'dist', 'build', '.next', 'coverage'];\n\nexport const grepTool: Tool<GrepInput, GrepOutput> = {\n name: 'grep',\n category: 'Search',\n description:\n 'Search across files using a regular expression. This is one of the primary code search tools. ' +\n 'Prefers ripgrep for speed and features when available.',\n usageHint:\n 'POWERFUL CODE SEARCH TOOL:\\n\\n' +\n '- `pattern` is a regular expression.\\n' +\n '- Use `output_mode: \"content\"` (default) to get matching lines with context.\\n' +\n '- Use `\"files_with_matches\"` when you only need the list of files.\\n' +\n '- Use `\"count\"` for quick statistics.\\n' +\n '- `glob` and `path` let you narrow the search scope significantly.\\n' +\n '- Always prefer this over `bash grep` when searching code.',\n permission: 'auto',\n mutating: false,\n capabilities: ['fs.read'],\n maxOutputBytes: 131_072,\n timeoutMs: 10_000,\n inputSchema: {\n type: 'object',\n properties: {\n pattern: {\n type: 'string',\n description: 'Regular expression pattern to search for in file contents.',\n },\n path: {\n type: 'string',\n description: 'Limit search to this directory or file (relative to project root).',\n },\n glob: {\n type: 'string',\n description: 'Glob filter for which files to include (e.g. \"**/*.ts\", \"src/**\").',\n },\n output_mode: {\n type: 'string',\n enum: ['content', 'files_with_matches', 'count'],\n description: 'Return style: detailed matches, just file list, or count only.',\n },\n context_lines: {\n type: 'integer',\n description: 'How many lines of surrounding context to include with each match.',\n },\n case_insensitive: {\n type: 'boolean',\n description: 'Ignore case when matching.',\n },\n limit: {\n type: 'integer',\n description: 'Maximum number of matches to return.',\n },\n },\n required: ['pattern'],\n },\n async execute(input, ctx, opts) {\n let final: GrepOutput | undefined;\n const executeStream = grepTool.executeStream;\n if (!executeStream) throw new Error('grepTool: stream execution unavailable');\n for await (const ev of executeStream(input, ctx, opts)) {\n if (ev.type === 'final') final = ev.output;\n }\n if (!final) throw new Error('grep: stream ended without final event');\n return final;\n },\n async *executeStream(input, ctx, opts): AsyncGenerator<ToolStreamEvent<GrepOutput>> {\n if (!input?.pattern) throw new Error('grep: pattern is required');\n const base = input.path ? safeResolve(input.path, ctx) : ctx.cwd;\n const mode = input.output_mode ?? 'content';\n const limit = Math.max(1, Math.min(input.limit ?? 200, 2000));\n const validation = compileUserRegex(input.pattern, input.case_insensitive ? 'i' : '');\n if (!validation.ok) {\n throw new Error(`grep: ${validation.reason}`);\n }\n\n const rgAvailable = await detectRg(opts.signal);\n if (rgAvailable) {\n try {\n yield* runRgStream(input, base, mode, limit, opts.signal);\n return;\n } catch {\n // fall through to native\n }\n }\n yield { type: 'log', text: 'Falling back to native grep…' };\n const out = await runNative(input, base, mode, limit, opts.signal);\n yield { type: 'final', output: out };\n },\n};\n\nasync function detectRg(signal: AbortSignal): Promise<boolean> {\n return new Promise((resolve) => {\n try {\n const p = spawn('rg', ['--version'], { env: buildChildEnv(), stdio: 'ignore', signal });\n p.on('error', () => resolve(false));\n p.on('close', (code) => resolve(code === 0));\n } catch {\n resolve(false);\n }\n });\n}\n\nasync function* runRgStream(\n input: GrepInput,\n base: string,\n mode: 'content' | 'files_with_matches' | 'count',\n limit: number,\n signal: AbortSignal,\n): AsyncGenerator<ToolStreamEvent<GrepOutput>> {\n const args: string[] = ['--no-heading'];\n if (input.case_insensitive) args.push('-i');\n if (mode === 'files_with_matches') args.push('-l');\n if (mode === 'count') args.push('-c');\n if (mode === 'content') {\n args.push('-n');\n if (input.context_lines) args.push('-C', String(input.context_lines));\n }\n for (const ignored of DEFAULT_IGNORE) {\n args.push('--glob', `!${ignored}/**`, '--glob', `!**/${ignored}/**`);\n }\n if (input.glob) args.push('--glob', input.glob);\n args.push('--', input.pattern, base);\n\n const matches: string[] = [];\n let buf = '';\n let totalLines = 0;\n let totalCount = 0;\n let batchSinceFlush = 0;\n const FLUSH_AT = 16; // yield a partial_output every 16 matches\n // Cap on the in-progress line buffer. Without this, a single huge \"line\"\n // (e.g. a file with no newlines under a symlink) plus a fast producer\n // would let `buf` grow unbounded. 1 MB comfortably holds any realistic\n // grep hit; beyond that we kill the child and surface a truncation.\n const MAX_BUF_BYTES = 1_000_000;\n let bufOverflow = false;\n\n const child = spawn('rg', args, { signal, env: buildChildEnv(), stdio: ['ignore', 'pipe', 'pipe'] });\n\n type Chunk = { kind: 'out' | 'close' | 'error'; data: string };\n const queue: Chunk[] = [];\n let waiter: (() => void) | undefined;\n const wake = () => {\n if (waiter) {\n const w = waiter;\n waiter = undefined;\n w();\n }\n };\n child.stdout?.on('data', (c) => {\n queue.push({ kind: 'out', data: c.toString() });\n wake();\n });\n child.on('error', (e) => {\n queue.push({ kind: 'error', data: e.message });\n wake();\n });\n child.on('close', () => {\n queue.push({ kind: 'close', data: '' });\n wake();\n });\n\n let pendingBatch: string[] = [];\n let errored = false;\n for (;;) {\n while (queue.length === 0) {\n await new Promise<void>((r) => {\n waiter = r;\n });\n }\n const c = expectDefined(queue.shift());\n if (c.kind === 'error') {\n errored = true;\n continue;\n }\n if (c.kind === 'close') break;\n buf += c.data;\n // Guard against a pathological producer (e.g. matching a huge binary\n // without newlines) pinning memory. Kill the child and mark the result\n // truncated; whatever we already captured stays intact.\n if (buf.length > MAX_BUF_BYTES && !bufOverflow) {\n bufOverflow = true;\n buf = buf.slice(-MAX_BUF_BYTES);\n try {\n child.kill('SIGTERM');\n } catch {\n /* ignore */\n }\n }\n const idx = buf.lastIndexOf('\\n');\n if (idx === -1) continue;\n const ready = buf.slice(0, idx);\n buf = buf.slice(idx + 1);\n for (const line of ready.split('\\n')) {\n if (!line) continue;\n totalLines++;\n if (mode === 'count') totalCount += parseRgCountLine(line);\n if (matches.length < limit) {\n matches.push(line);\n pendingBatch.push(line);\n batchSinceFlush++;\n }\n }\n if (batchSinceFlush >= FLUSH_AT) {\n yield {\n type: 'partial_output',\n text: pendingBatch.join('\\n'),\n data: { matches_so_far: matches.length },\n };\n pendingBatch = [];\n batchSinceFlush = 0;\n }\n }\n\n if (buf.trim()) {\n for (const line of buf.split('\\n')) {\n if (!line) continue;\n totalLines++;\n if (mode === 'count') totalCount += parseRgCountLine(line);\n if (matches.length < limit) {\n matches.push(line);\n pendingBatch.push(line);\n }\n }\n }\n if (pendingBatch.length > 0) {\n yield {\n type: 'partial_output',\n text: pendingBatch.join('\\n'),\n data: { matches_so_far: matches.length },\n };\n }\n if (errored) throw new Error('rg: spawn error');\n\n yield {\n type: 'final',\n output: {\n matches,\n count: mode === 'count' ? totalCount : totalLines,\n truncated: totalLines > limit || bufOverflow,\n used: 'rg',\n },\n };\n}\n\nfunction parseRgCountLine(line: string): number {\n const idx = line.lastIndexOf(':');\n if (idx === -1) return 0;\n const n = Number.parseInt(line.slice(idx + 1), 10);\n return Number.isFinite(n) ? n : 0;\n}\n\nasync function runNative(\n input: GrepInput,\n base: string,\n mode: 'content' | 'files_with_matches' | 'count',\n limit: number,\n signal: AbortSignal,\n): Promise<GrepOutput> {\n const flags = input.case_insensitive ? 'i' : '';\n const compiled = compileUserRegex(input.pattern, flags);\n if (!compiled.ok) {\n throw new Error(`grep: ${compiled.reason}`);\n }\n const re = compiled.regex;\n const globRe = input.glob ? compileGlob(input.glob) : null;\n const matches: string[] = [];\n const fileMatches = new Map<string, number>();\n let total = 0;\n let stopped = false;\n\n const walk = async (dir: string): Promise<void> => {\n if (stopped || signal.aborted) return;\n let entries: import('node:fs').Dirent[];\n try {\n entries = await fs.readdir(dir, { withFileTypes: true });\n } catch {\n return;\n }\n for (const e of entries) {\n if (stopped) return;\n if (DEFAULT_IGNORE.includes(e.name)) continue;\n // Skip symlinks entirely. fs.Dirent.isDirectory/isFile return the\n // symlink's TYPE without resolving, but following the link into\n // arbitrary places (e.g. ~/.ssh) is the security concern. Tools\n // that genuinely need to traverse symlinks should opt in explicitly.\n if (e.isSymbolicLink()) continue;\n const full = path.join(dir, e.name);\n if (e.isDirectory()) {\n await walk(full);\n } else if (e.isFile()) {\n if (globRe && !globRe.test(e.name) && !globRe.test(full)) continue;\n if (globRe) globRe.lastIndex = 0;\n try {\n const stat = await fs.stat(full);\n if (stat.size > 1_000_000) continue;\n const head = await fs.readFile(full);\n if (isBinaryBuffer(head)) continue;\n const text = head.toString('utf8');\n const lines = text.split(/\\r?\\n/);\n let fileHits = 0;\n for (let i = 0; i < lines.length; i++) {\n const ln = capSubject(lines[i] ?? '');\n re.lastIndex = 0;\n if (re.test(ln)) {\n fileHits++;\n total++;\n if (mode === 'content' && matches.length < limit) {\n matches.push(`${full}:${i + 1}:${ln}`);\n }\n }\n }\n if (fileHits > 0) {\n fileMatches.set(full, fileHits);\n if (mode === 'files_with_matches' && matches.length < limit) {\n matches.push(full);\n }\n if (mode === 'count' && matches.length < limit) {\n matches.push(`${full}:${fileHits}`);\n }\n }\n if (matches.length >= limit) stopped = true;\n } catch {\n // skip read errors\n }\n }\n }\n };\n await walk(base);\n\n return {\n matches,\n count: total,\n truncated: stopped,\n used: 'native',\n };\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -33,7 +33,7 @@ export { forgetTool, rememberTool } from './memory.js';
33
33
  export { createModeTool } from './mode.js';
34
34
  export { KillOpts, RegistryStats, TrackedProcess, _resetProcessRegistry, getProcessRegistry } from './process-registry.js';
35
35
  export { CircuitBreaker, CircuitBreakerConfig, CircuitBreakerSnapshot } from './circuit-breaker.js';
36
- export { g as cancelPendingReindexes, h as codebaseIndexTool, i as codebaseSearchTool, j as codebaseStatsTool, k as enqueueReindex, l as getIndexState, m as isIndexReady, n as isIndexableFile, o as isIndexing, p as onIndexStateChange, r as runStartupIndex } from './background-indexer-C70RD7LU.js';
36
+ export { g as cancelPendingReindexes, h as codebaseIndexTool, i as codebaseSearchTool, j as codebaseStatsTool, k as enqueueReindex, l as getIndexState, m as isIndexReady, n as isIndexableFile, o as isIndexing, p as onIndexStateChange, r as runStartupIndex } from './background-indexer-DYm1FUxK.js';
37
37
  export { builtinTools } from './builtin.js';
38
38
  export { builtinToolsPack } from './pack.js';
39
39
  import 'node:child_process';
@@ -54,15 +54,15 @@ import 'node:child_process';
54
54
  interface PlanInput {
55
55
  action: 'show' | 'add' | 'start' | 'done' | 'remove' | 'promote' | 'derive' | 'template_use' | 'clear';
56
56
  /** Required for add. */
57
- title?: string;
57
+ title?: string | undefined;
58
58
  /** Optional detail line for add. */
59
- details?: string;
59
+ details?: string | undefined;
60
60
  /** Required for start/done/remove/promote/derive — accepts plan item id OR 1-based index OR title substring. */
61
- target?: string;
61
+ target?: string | undefined;
62
62
  /** Optional subtasks for promote/derive. If omitted, a single todo is created from the plan item title. */
63
- subtasks?: string[];
63
+ subtasks?: string[] | undefined;
64
64
  /** Required for template_use — the template name (e.g. "new-feature", "bug-fix"). */
65
- template?: string;
65
+ template?: string | undefined;
66
66
  }
67
67
  interface PlanOutput {
68
68
  ok: boolean;
@@ -78,7 +78,7 @@ interface PlanOutput {
78
78
  id: string;
79
79
  content: string;
80
80
  status: string;
81
- activeForm?: string;
81
+ activeForm?: string | undefined;
82
82
  }>;
83
83
  }
84
84
  declare const planTool: Tool<PlanInput, PlanOutput>;