import {
  readAllLines
} from "./chunk-G2RSY56Q.js";
import {
  fixPathRepetition,
  isSymlink,
  isTextualFile,
  resolveUnderRepo,
  toRepoRelative
} from "./chunk-YPKNMYD4.js";
import {
  runRipgrep
} from "./chunk-TPP2UGQP.js";
import {
  AGENT_CONFIG,
  DEFAULT_EXCLUDES
} from "./chunk-I3J46TSB.js";

// tools/warp_grep/providers/local.ts
import fs from "fs/promises";
import path from "path";
var SKIP_NAMES = /* @__PURE__ */ new Set([
  // Version control
  ".git",
  ".svn",
  ".hg",
  ".bzr",
  // Dependencies
  "node_modules",
  "bower_components",
  ".pnpm",
  ".yarn",
  "vendor",
  "Pods",
  ".bundle",
  // Python
  "__pycache__",
  ".pytest_cache",
  ".mypy_cache",
  ".ruff_cache",
  ".venv",
  "venv",
  ".tox",
  ".nox",
  ".eggs",
  // Build outputs
  "dist",
  "build",
  "out",
  "output",
  "target",
  "_build",
  ".next",
  ".nuxt",
  ".output",
  ".vercel",
  ".netlify",
  // Cache
  ".cache",
  ".parcel-cache",
  ".turbo",
  ".nx",
  ".gradle",
  // IDE
  ".idea",
  ".vscode",
  ".vs",
  // Coverage
  "coverage",
  ".coverage",
  "htmlcov",
  ".nyc_output",
  // Temp
  "tmp",
  "temp",
  ".tmp",
  ".temp",
  // Lock files
  "package-lock.json",
  "yarn.lock",
  "pnpm-lock.yaml",
  "bun.lockb",
  "Cargo.lock",
  "Gemfile.lock",
  "poetry.lock"
]);
var SKIP_EXTENSIONS = /* @__PURE__ */ new Set([
  ".min.js",
  ".min.css",
  ".bundle.js",
  ".wasm",
  ".so",
  ".dll",
  ".pyc",
  ".map",
  ".js.map"
]);
function shouldSkip(name, allowNames) {
  if (allowNames?.has(name)) return false;
  if (SKIP_NAMES.has(name)) return true;
  if (name.startsWith(".")) return true;
  for (const ext of SKIP_EXTENSIONS) {
    if (name.endsWith(ext)) return true;
  }
  return false;
}
var LocalRipgrepProvider = class {
  constructor(repoRoot, excludes = DEFAULT_EXCLUDES, options) {
    this.repoRoot = repoRoot;
    this.excludes = excludes;
    if (options?.allowNames?.length) {
      this.allowNames = new Set(options.allowNames);
    }
  }
  allowNames;
  async grep(params) {
    let abs;
    try {
      abs = resolveUnderRepo(this.repoRoot, params.path);
    } catch (err) {
      return {
        lines: [],
        error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
      };
    }
    const stat = await fs.stat(abs).catch(() => null);
    if (!stat) return { lines: [] };
    const targetArg = abs === path.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
    const contextLines = params.context_lines !== void 0 ? String(params.context_lines) : "1";
    const args = [
      "--no-config",
      "--no-heading",
      "--with-filename",
      "--line-number",
      "--color=never",
      "--trim",
      "--max-columns=400",
      "-C",
      contextLines,
      ...params.case_sensitive === false ? ["--ignore-case"] : [],
      ...params.glob ? ["--glob", params.glob] : [],
      ...this.excludes.filter((e) => !this.allowNames?.has(e)).flatMap((e) => ["-g", `!${e}`]),
      params.pattern,
      targetArg || "."
    ];
    const res = await runRipgrep(args, { cwd: this.repoRoot });
    if (res.exitCode === -1) {
      return {
        lines: [],
        error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required but failed to execute. Please install it:
  \u2022 macOS: brew install ripgrep
  \u2022 Ubuntu/Debian: apt install ripgrep
  \u2022 Windows: choco install ripgrep
  \u2022 Or visit: https://github.com/BurntSushi/ripgrep#installation
Exit code: ${res.exitCode}${res.stderr ? `
Details: ${res.stderr}` : ""}`
      };
    }
    if (res.exitCode !== 0 && res.exitCode !== 1) {
      return {
        lines: [],
        error: `[RIPGREP ERROR] grep failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}`
      };
    }
    const lines = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
    if (lines.length > AGENT_CONFIG.MAX_OUTPUT_LINES) {
      const truncated = lines.slice(0, AGENT_CONFIG.MAX_OUTPUT_LINES);
      truncated.push(`... (output truncated at ${AGENT_CONFIG.MAX_OUTPUT_LINES} of ${lines.length} lines)`);
      return { lines: truncated };
    }
    return { lines };
  }
  async read(params) {
    let abs;
    try {
      abs = resolveUnderRepo(this.repoRoot, params.path);
    } catch (err) {
      return {
        lines: [],
        error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}`
      };
    }
    let stat = await fs.stat(abs).catch(() => null);
    if (!stat || !stat.isFile()) {
      const fixedPath = fixPathRepetition(abs);
      if (fixedPath) {
        const fixedStat = await fs.stat(fixedPath).catch(() => null);
        if (fixedStat?.isFile()) {
          abs = fixedPath;
          stat = fixedStat;
        }
      }
    }
    if (!stat || !stat.isFile()) {
      return {
        lines: [],
        error: `[FILE NOT FOUND] You tried to read "${params.path}" but there is no file at this path. Double-check the path exists and is spelled correctly.`
      };
    }
    if (isSymlink(abs)) {
      return {
        lines: [],
        error: `[SYMLINK] You tried to read "${params.path}" but this is a symlink. Try reading the actual file it points to instead.`
      };
    }
    if (!isTextualFile(abs)) {
      return {
        lines: [],
        error: `[UNREADABLE FILE] You tried to read "${params.path}" but this file is either too large or not a text file, so it cannot be read. Try a different file.`
      };
    }
    let lines;
    try {
      lines = await readAllLines(abs);
    } catch (err) {
      return {
        lines: [],
        error: `[READ ERROR] Failed to read "${params.path}": ${err instanceof Error ? err.message : String(err)}`
      };
    }
    const total = lines.length;
    const rawStart = params.start;
    const rawEnd = params.end;
    let s = 1;
    let e = total;
    const startValid = rawStart === void 0 || Number.isFinite(rawStart) && rawStart > 0;
    const endValid = rawEnd === void 0 || Number.isFinite(rawEnd) && rawEnd > 0;
    if (startValid && endValid) {
      s = rawStart ?? 1;
      e = Math.min(rawEnd ?? total, total);
      if (s > total && total > 0 || s > e) {
        s = 1;
        e = total;
      }
    }
    const out = [];
    for (let i = s; i <= e; i += 1) {
      const content = lines[i - 1] ?? "";
      out.push(`${i}|${content}`);
    }
    if (out.length > AGENT_CONFIG.MAX_READ_LINES) {
      const truncated = out.slice(0, AGENT_CONFIG.MAX_READ_LINES);
      truncated.push(`... (output truncated at ${AGENT_CONFIG.MAX_READ_LINES} of ${out.length} lines)`);
      return { lines: truncated };
    }
    return { lines: out };
  }
  async listDirectory(params) {
    let abs;
    try {
      abs = resolveUnderRepo(this.repoRoot, params.path);
    } catch {
      return [];
    }
    const stat = await fs.stat(abs).catch(() => null);
    if (!stat || !stat.isDirectory()) {
      return [];
    }
    const maxResults = params.maxResults ?? AGENT_CONFIG.MAX_OUTPUT_LINES;
    const maxDepth = params.maxDepth ?? AGENT_CONFIG.MAX_LIST_DEPTH;
    const regex = params.pattern ? new RegExp(params.pattern) : null;
    const repoRoot = this.repoRoot;
    const allowNames = this.allowNames;
    const results = [];
    let timedOut = false;
    const startTime = Date.now();
    async function walk(dir, depth) {
      if (Date.now() - startTime > AGENT_CONFIG.LIST_TIMEOUT_MS) {
        timedOut = true;
        return;
      }
      if (depth > maxDepth || results.length >= maxResults) return;
      let entries;
      try {
        entries = await fs.readdir(dir, { withFileTypes: true });
      } catch {
        return;
      }
      for (const entry of entries) {
        if (timedOut || results.length >= maxResults) break;
        if (shouldSkip(entry.name, allowNames)) continue;
        if (regex && !regex.test(entry.name)) continue;
        const full = path.join(dir, entry.name);
        const isDir = entry.isDirectory();
        results.push({
          name: entry.name,
          path: toRepoRelative(repoRoot, full),
          type: isDir ? "dir" : "file",
          depth
        });
        if (isDir) {
          await walk(full, depth + 1);
        }
      }
    }
    await walk(abs, 0);
    return results;
  }
  async glob(params) {
    let abs;
    try {
      abs = params.path ? resolveUnderRepo(this.repoRoot, params.path) : this.repoRoot;
    } catch (err) {
      return { files: [], searchDir: this.repoRoot, totalFound: 0, error: `[PATH ERROR] ${err instanceof Error ? err.message : String(err)}` };
    }
    const stat = await fs.stat(abs).catch(() => null);
    if (!stat || !stat.isDirectory()) {
      return { files: [], searchDir: abs, totalFound: 0, error: `[PATH ERROR] Directory not found: ${params.path || "."}` };
    }
    const targetArg = abs === path.resolve(this.repoRoot) ? "." : toRepoRelative(this.repoRoot, abs);
    const args = [
      "--no-config",
      "--files",
      "--color=never",
      "-g",
      params.pattern,
      ...this.excludes.filter((e) => !this.allowNames?.has(e)).flatMap((e) => ["-g", `!${e}`]),
      targetArg || "."
    ];
    const res = await runRipgrep(args, { cwd: this.repoRoot });
    if (res.exitCode === -1) {
      return { files: [], searchDir: abs, totalFound: 0, error: `[RIPGREP NOT AVAILABLE] ripgrep (rg) is required for glob search.` };
    }
    if (res.exitCode !== 0 && res.exitCode !== 1) {
      return { files: [], searchDir: abs, totalFound: 0, error: `[GLOB ERROR] glob failed with exit code ${res.exitCode}${res.stderr ? `: ${res.stderr}` : ""}` };
    }
    const absRoot = path.resolve(this.repoRoot);
    const relFiles = (res.stdout || "").trim().split(/\r?\n/).filter((l) => l.length > 0);
    const absFiles = relFiles.map((f) => path.resolve(absRoot, f));
    const withMtime = [];
    for (const f of absFiles) {
      const s = await fs.stat(f).catch(() => null);
      withMtime.push({ file: f, mtime: s?.mtimeMs ?? 0 });
    }
    withMtime.sort((a, b) => b.mtime - a.mtime);
    const totalFound = withMtime.length;
    return { files: withMtime.slice(0, 100).map((f) => f.file), searchDir: abs, totalFound };
  }
};

export {
  LocalRipgrepProvider
};
//# sourceMappingURL=chunk-XNBQJSLI.js.map