// tools/warp_grep/agent/parser.ts
var VALID_COMMANDS = ["list_directory", "grep", "read", "finish"];
function isValidCommand(name) {
  return VALID_COMMANDS.includes(name);
}
function getXmlElementText(xml, tagName) {
  const regex = new RegExp(`<${tagName}>([\\s\\S]*?)</${tagName}>`, "i");
  const match = xml.match(regex);
  return match ? match[1].trim() : null;
}
function parseNestedXmlTools(text) {
  const tools = [];
  const toolRegex = /<([a-z_][a-z0-9_]*)>([\s\S]*?)<\/\1>/gi;
  let match;
  while ((match = toolRegex.exec(text)) !== null) {
    const rawToolName = match[1].toLowerCase();
    const content = match[2];
    if (!isValidCommand(rawToolName)) continue;
    const toolName = rawToolName;
    if (toolName === "list_directory") {
      const path = getXmlElementText(content, "path");
      const pattern = getXmlElementText(content, "pattern");
      if (path) {
        tools.push({ name: "list_directory", arguments: { path, pattern } });
      }
    } else if (toolName === "grep") {
      const pattern = getXmlElementText(content, "pattern");
      const subDir = getXmlElementText(content, "sub_dir");
      const glob = getXmlElementText(content, "glob");
      if (pattern) {
        tools.push({
          name: "grep",
          arguments: {
            pattern,
            path: subDir || ".",
            ...glob && { glob }
          }
        });
      }
    } else if (toolName === "read") {
      const path = getXmlElementText(content, "path");
      const linesStr = getXmlElementText(content, "lines");
      if (path) {
        const args = { path };
        if (linesStr) {
          const ranges = [];
          for (const rangeStr of linesStr.split(",")) {
            const trimmed = rangeStr.trim();
            if (!trimmed) continue;
            const [s, e] = trimmed.split("-").map((v) => parseInt(v.trim(), 10));
            if (Number.isFinite(s) && Number.isFinite(e)) {
              ranges.push([s, e]);
            } else if (Number.isFinite(s)) {
              ranges.push([s, s]);
            }
          }
          if (ranges.length === 1) {
            args.start = ranges[0][0];
            args.end = ranges[0][1];
          } else if (ranges.length > 1) {
            args.lines = ranges;
          }
        }
        tools.push({ name: "read", arguments: args });
      }
    } else if (toolName === "finish") {
      const fileRegex = /<file>([\s\S]*?)<\/file>/gi;
      const files = [];
      let fileMatch;
      while ((fileMatch = fileRegex.exec(content)) !== null) {
        const fileContent = fileMatch[1];
        const filePath = getXmlElementText(fileContent, "path");
        const linesStr = getXmlElementText(fileContent, "lines");
        if (filePath) {
          if (!linesStr || linesStr.trim() === "*") {
            files.push({ path: filePath, lines: "*" });
          } else {
            const ranges = [];
            for (const rangeStr of linesStr.split(",")) {
              const [s, e] = rangeStr.split("-").map((v) => parseInt(v.trim(), 10));
              if (Number.isFinite(s) && Number.isFinite(e)) {
                ranges.push([s, e]);
              }
            }
            if (ranges.length > 0) {
              files.push({ path: filePath, lines: ranges });
            } else {
              files.push({ path: filePath, lines: "*" });
            }
          }
        }
      }
      if (files.length > 0) {
        tools.push({ name: "finish", arguments: { files } });
      }
    }
  }
  return tools;
}
function preprocessText(text) {
  const processed = text.replace(/<think>[\s\S]*?<\/think>/gi, "").replace(/<\/?tool_call>/gi, "");
  const nestedTools = parseNestedXmlTools(processed);
  const toolCallLines = [];
  const allLines = processed.split(/\r?\n/).map((l) => l.trim());
  for (const line of allLines) {
    if (!line) continue;
    if (line.startsWith("<")) continue;
    const firstWord = line.split(/\s/)[0];
    if (VALID_COMMANDS.includes(firstWord)) {
      if (!toolCallLines.includes(line)) {
        toolCallLines.push(line);
      }
    }
  }
  return { lines: toolCallLines, nestedTools };
}
var LLMResponseParser = class {
  finishSpecSplitRe = /,(?=[^,\s]+:)/;
  parse(text) {
    if (typeof text !== "string") {
      throw new TypeError("Command text must be a string.");
    }
    const { lines, nestedTools } = preprocessText(text);
    const commands = [...nestedTools];
    let finishAccumulator = null;
    lines.forEach((line) => {
      if (!line || line.startsWith("#")) return;
      const parts = this.splitLine(line);
      if (parts.length === 0) return;
      const cmd = parts[0];
      switch (cmd) {
        case "list_directory":
          this.handleListDirectory(parts, line, commands);
          break;
        case "grep":
          this.handleGrep(parts, line, commands);
          break;
        case "read":
          this.handleRead(parts, line, commands);
          break;
        case "finish":
          finishAccumulator = this.handleFinish(parts, line, commands, finishAccumulator);
          break;
        default:
          break;
      }
    });
    if (finishAccumulator) {
      const map = finishAccumulator;
      const entries = [...map.entries()];
      const filesPayload = entries.map(([path, ranges]) => ({
        path,
        lines: [...ranges].sort((a, b) => a[0] - b[0])
      }));
      commands.push({ name: "finish", arguments: { files: filesPayload } });
    }
    return commands;
  }
  splitLine(line) {
    const parts = [];
    let current = "";
    let inSingle = false;
    for (let i = 0; i < line.length; i++) {
      const ch = line[i];
      if (ch === "'" && line[i - 1] !== "\\") {
        inSingle = !inSingle;
        current += ch;
      } else if (!inSingle && /\s/.test(ch)) {
        if (current) {
          parts.push(current);
          current = "";
        }
      } else {
        current += ch;
      }
    }
    if (current) parts.push(current);
    return parts;
  }
  /** Helper to create a _skip tool call with an error message */
  skip(message) {
    return { name: "_skip", arguments: { message } };
  }
  handleListDirectory(parts, rawLine, commands) {
    if (parts.length < 2) {
      commands.push(this.skip(
        `[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: list_directory <path> [pattern]. Example: list_directory src/`
      ));
      return;
    }
    const path = parts[1];
    const pattern = parts[2]?.replace(/^"|"$/g, "") ?? null;
    commands.push({ name: "list_directory", arguments: { path, pattern } });
  }
  handleGrep(parts, rawLine, commands) {
    if (parts.length < 3) {
      commands.push(this.skip(
        `[SKIPPED] Your command "${rawLine}" is missing arguments. Correct format: grep '<pattern>' <path>. Example: grep 'TODO' src/`
      ));
      return;
    }
    let pat = parts[1];
    if (pat.startsWith("'") && pat.endsWith("'")) {
      pat = pat.slice(1, -1);
    }
    if (!pat) {
      commands.push(this.skip(
        `[SKIPPED] Your command "${rawLine}" has an empty pattern. Provide a non-empty search pattern. Example: grep 'function' src/`
      ));
      return;
    }
    commands.push({ name: "grep", arguments: { pattern: pat, path: parts[2] } });
  }
  handleRead(parts, rawLine, commands) {
    if (parts.length < 2) {
      commands.push(this.skip(
        `[SKIPPED] Your command "${rawLine}" is missing a path. Correct format: read <path> or read <path>:<start>-<end>. Example: read src/index.ts:1-50`
      ));
      return;
    }
    const spec = parts[1];
    const rangeIdx = spec.indexOf(":");
    if (rangeIdx === -1) {
      commands.push({ name: "read", arguments: { path: spec } });
      return;
    }
    const filePath = spec.slice(0, rangeIdx);
    const range = spec.slice(rangeIdx + 1);
    const [s, e] = range.split("-").map((v) => parseInt(v, 10));
    if (!Number.isFinite(s) || !Number.isFinite(e)) {
      commands.push({ name: "read", arguments: { path: filePath } });
      return;
    }
    commands.push({ name: "read", arguments: { path: filePath, start: s, end: e } });
  }
  handleFinish(parts, rawLine, commands, acc) {
    const map = acc ?? /* @__PURE__ */ new Map();
    const args = parts.slice(1);
    for (const token of args) {
      const [filePath, rangesText] = token.split(":", 2);
      if (!filePath || !rangesText) {
        commands.push(this.skip(
          `[SKIPPED] Invalid finish token "${token}". Correct format: finish <path>:<start>-<end>. Example: finish src/index.ts:1-50`
        ));
        continue;
      }
      const rangeSpecs = rangesText.split(",").filter(Boolean);
      for (const spec of rangeSpecs) {
        const [s, e] = spec.split("-").map((v) => parseInt(v, 10));
        if (!Number.isFinite(s) || !Number.isFinite(e) || e < s) {
          commands.push(this.skip(
            `[SKIPPED] Invalid range "${spec}" in "${token}". Ranges must be <start>-<end> where start <= end. Example: 1-50`
          ));
          continue;
        }
        const arr = map.get(filePath) ?? [];
        arr.push([s, e]);
        map.set(filePath, arr);
      }
    }
    return map;
  }
};

export {
  LLMResponseParser
};
//# sourceMappingURL=chunk-SNGGSPYJ.js.map