import { createRequire as __createRequire } from "node:module"; const require = __createRequire(import.meta.url);
import {
  classifyShellCommand,
  createCodeIntelTool,
  createLightweightCodeIntelProvider,
  createSharedWebFetchTool,
  createSharedWebResearchTool,
  createSharedWebSearchTool,
  interpretShellExit,
  requestInteraction
} from "./chunk-SW6G4XW2.js";
import {
  Type
} from "./chunk-D7NAXU7F.js";

// src/config.ts
import { existsSync, mkdirSync, readFileSync, renameSync } from "fs";
import { homedir } from "os";
import { dirname, join } from "path";
import { fileURLToPath } from "url";
var __filename = fileURLToPath(import.meta.url);
var __dirname = dirname(__filename);
var isBunRuntime = !!process.versions.bun;
function getPackageDir() {
  const envDir = process.env.PIE_PACKAGE_DIR;
  if (envDir) {
    if (envDir === "~") return homedir();
    if (envDir.startsWith("~/")) return homedir() + envDir.slice(1);
    return envDir;
  }
  let dir = __dirname;
  while (dir !== dirname(dir)) {
    if (existsSync(join(dir, "package.json"))) {
      return dir;
    }
    dir = dirname(dir);
  }
  return __dirname;
}
function getThemesDir() {
  const packageDir = getPackageDir();
  const srcOrDist = existsSync(join(packageDir, "src")) ? "src" : "dist";
  return join(packageDir, srcOrDist, "theme");
}
function getBuiltinDir() {
  const packageDir = getPackageDir();
  const distBuiltinDir = join(packageDir, "dist", "builtin");
  if (existsSync(distBuiltinDir)) {
    return distBuiltinDir;
  }
  return join(packageDir, "builtin");
}
function getPackageJsonPath() {
  return join(getPackageDir(), "package.json");
}
var pkg = JSON.parse(readFileSync(getPackageJsonPath(), "utf-8"));
var CONFIG_DIR_NAME = ".pie";
var VERSION = pkg.version;
var ENV_AGENT_DIR = "PIE_CODING_AGENT_DIR";
function getConfigDir() {
  const envDir = process.env[ENV_AGENT_DIR];
  if (envDir) {
    if (envDir === "~") return homedir();
    if (envDir.startsWith("~/")) return homedir() + envDir.slice(1);
    return envDir;
  }
  return join(homedir(), CONFIG_DIR_NAME);
}
function getAgentDir() {
  return getConfigDir();
}
function getCustomThemesDir() {
  return join(getConfigDir(), "themes");
}
function getSettingsPath() {
  return join(getConfigDir(), "settings.json");
}
function getSessionsDir() {
  return join(getConfigDir(), "sessions");
}
function getLogsDir() {
  return join(getConfigDir(), "logs");
}
function getRuntimeLogPath() {
  return join(getLogsDir(), "pie-cli.log");
}
function migrateConfigFromAgentDir() {
  const oldAgentDir = join(homedir(), CONFIG_DIR_NAME, "agent");
  const newConfigDir = getConfigDir();
  if (!existsSync(oldAgentDir)) {
    return;
  }
  if (!existsSync(newConfigDir)) {
    mkdirSync(newConfigDir, { recursive: true });
  }
  const filesToMigrate = [
    "keybindings.json",
    "settings.json",
    "pie-debug.log"
  ];
  for (const file of filesToMigrate) {
    const oldPath = join(oldAgentDir, file);
    const newPath = file === "pie-debug.log" ? getRuntimeLogPath() : join(newConfigDir, file);
    if (existsSync(oldPath) && !existsSync(newPath)) {
      try {
        if (file === "pie-debug.log") {
          mkdirSync(getLogsDir(), { recursive: true });
        }
        renameSync(oldPath, newPath);
        console.log(`[Config] Migrated ${file} to ${newPath}`);
      } catch (e) {
      }
    }
  }
  const oldThemesDir = join(oldAgentDir, "themes");
  const newThemesDir = getCustomThemesDir();
  if (existsSync(oldThemesDir) && !existsSync(newThemesDir)) {
    try {
      renameSync(oldThemesDir, newThemesDir);
      console.log(`[Config] Migrated themes to ${newThemesDir}`);
    } catch (e) {
    }
  }
}

// src/capabilities/bash/index.ts
import { spawn } from "child_process";
import { createWriteStream, existsSync as existsSync2, statSync, writeFileSync } from "fs";
import { tmpdir } from "os";
import { join as join2 } from "path";
import { randomBytes } from "crypto";

// src/capabilities/bash/truncate.ts
var DEFAULT_MAX_LINES = 2e3;
var DEFAULT_MAX_BYTES = 50 * 1024;
function formatSize(bytes) {
  if (bytes < 1024) {
    return `${bytes}B`;
  } else if (bytes < 1024 * 1024) {
    return `${(bytes / 1024).toFixed(1)}KB`;
  } else {
    return `${(bytes / (1024 * 1024)).toFixed(1)}MB`;
  }
}
function truncateTail(content, options = {}) {
  const maxLines = options.maxLines ?? DEFAULT_MAX_LINES;
  const maxBytes = options.maxBytes ?? DEFAULT_MAX_BYTES;
  const totalBytes = Buffer.byteLength(content, "utf-8");
  const lines = content.split("\n");
  const totalLines = lines.length;
  if (totalLines <= maxLines && totalBytes <= maxBytes) {
    return {
      content,
      truncated: false,
      truncatedBy: null,
      totalLines,
      totalBytes,
      outputLines: totalLines,
      outputBytes: totalBytes,
      lastLinePartial: false,
      maxLines,
      maxBytes
    };
  }
  const outputLinesArr = [];
  let outputBytesCount = 0;
  let truncatedBy = "lines";
  let lastLinePartial = false;
  for (let i = lines.length - 1; i >= 0 && outputLinesArr.length < maxLines; i--) {
    const line = lines[i];
    const lineBytes = Buffer.byteLength(line, "utf-8") + (outputLinesArr.length > 0 ? 1 : 0);
    if (outputBytesCount + lineBytes > maxBytes) {
      truncatedBy = "bytes";
      if (outputLinesArr.length === 0) {
        const truncatedLine = truncateStringToBytesFromEnd(line, maxBytes);
        outputLinesArr.unshift(truncatedLine);
        outputBytesCount = Buffer.byteLength(truncatedLine, "utf-8");
        lastLinePartial = true;
      }
      break;
    }
    outputLinesArr.unshift(line);
    outputBytesCount += lineBytes;
  }
  if (outputLinesArr.length >= maxLines && outputBytesCount <= maxBytes) {
    truncatedBy = "lines";
  }
  const outputContent = outputLinesArr.join("\n");
  const finalOutputBytes = Buffer.byteLength(outputContent, "utf-8");
  return {
    content: outputContent,
    truncated: true,
    truncatedBy,
    totalLines,
    totalBytes,
    outputLines: outputLinesArr.length,
    outputBytes: finalOutputBytes,
    lastLinePartial,
    maxLines,
    maxBytes
  };
}
function truncateStringToBytesFromEnd(str, maxBytes) {
  const buf = Buffer.from(str, "utf-8");
  if (buf.length <= maxBytes) {
    return str;
  }
  let start = buf.length - maxBytes;
  while (start < buf.length && (buf[start] & 192) === 128) {
    start++;
  }
  return buf.slice(start).toString("utf-8");
}

// src/capabilities/bash/index.ts
var bashSchema = Type.Object({
  command: Type.String({ description: "The bash command to execute" }),
  timeout: Type.Optional(
    Type.Number({
      description: "Timeout in seconds. Default: 120s (2 minutes). For longer tasks, explicitly set a higher value: { timeout: 600 } for npm install, { timeout: 600 } for cargo build, etc."
    })
  ),
  cwd: Type.Optional(Type.String({ description: "Working directory for the command" })),
  shell: Type.Optional(Type.Union([
    Type.Literal("auto"),
    Type.Literal("bash"),
    Type.Literal("powershell"),
    Type.Literal("cmd")
  ], { description: "Shell to use. Defaults to bash on macOS/Linux and PowerShell on Windows. Use this only for platform-specific commands." }))
});
var DEFAULT_TIMEOUT_SECONDS = 120;
function validateBashCommand(command) {
  const value = typeof command === "string" ? command : "";
  const trimmed = value.trim();
  if (!trimmed) {
    throw new Error("Command cannot be empty. Pass a real shell command in the command argument.");
  }
  if (/^:\s/.test(trimmed) || /^:\s*(?:\d+[\.)]?)?\s*$/.test(trimmed) || /^:\s*\d+[\.)]/.test(trimmed)) {
    throw new Error(`Invalid bash command "${value}". Do not pass a no-op, numbered plan item, or plan fragment; pass the actual shell command to run.`);
  }
  if (trimmed.includes("<|tool")) {
    throw new Error("Invalid bash command. The command argument must be only a real shell command, not tool-call markup.");
  }
}
function buildEffectiveCommand(command, options) {
  const prefix = options?.commandPrefix?.trim();
  if (!prefix) {
    return command;
  }
  validateBashCommand(prefix);
  return `${prefix} && ${command}`;
}
function resolveShell(shell) {
  if (!shell || shell === "auto") {
    return process.platform === "win32" ? "powershell" : "bash";
  }
  return shell;
}
function buildSpawnCommand(shell, command) {
  if (shell === "powershell") {
    return { file: process.platform === "win32" ? "powershell.exe" : "pwsh", args: ["-NoProfile", "-NonInteractive", "-Command", command], shell: false };
  }
  if (shell === "cmd") {
    return { file: "cmd.exe", args: ["/d", "/s", "/c", command], shell: false };
  }
  return { file: command, args: [], shell: true };
}
async function requestHighRiskConfirmation(request) {
  const response = await requestInteraction({
    type: "confirm",
    id: `bash:${Date.now()}`,
    prompt: `Run high-risk ${request.shell} command?`,
    detail: [
      `Class: ${request.commandClass}`,
      `Warnings: ${request.riskWarnings.join("; ")}`,
      "",
      request.command
    ].join("\n"),
    timeoutMs: 12e4
  });
  return response.type === "confirm" && response.confirmed === true;
}
function validateWorkingDirectory(cwd) {
  if (!existsSync2(cwd)) {
    throw new Error(`Invalid bash cwd: ${cwd} does not exist. Use the current workspace path or pass an existing directory in cwd.`);
  }
  let stat;
  try {
    stat = statSync(cwd);
  } catch (error) {
    throw new Error(`Invalid bash cwd: cannot access ${cwd}: ${error instanceof Error ? error.message : String(error)}`);
  }
  if (!stat.isDirectory()) {
    throw new Error(`Invalid bash cwd: ${cwd} is not a directory. Pass an existing directory in cwd.`);
  }
}
function normalizeWorkingDirectory(cwd, defaultCwd) {
  const requestedCwd = typeof cwd === "string" && cwd.trim().length > 0 ? cwd.trim() : void 0;
  return requestedCwd ?? defaultCwd ?? process.cwd();
}
function getTempFilePath() {
  const id = randomBytes(8).toString("hex");
  return join2(tmpdir(), `pie-bash-${id}.log`);
}
function finishTempFileStream(stream) {
  if (!stream) return Promise.resolve();
  return new Promise((resolve3, reject) => {
    const onError = (error) => {
      stream.off("error", onError);
      reject(error);
    };
    stream.once("error", onError);
    stream.end(() => {
      stream.off("error", onError);
      resolve3();
    });
  });
}
function buildBashRecoveryHint(command, output) {
  if (/\bnpm ERR!|\bnpm error\b/i.test(output) && /\b(?:ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|fetch failed|socket hang up|registry|proxy)\b/i.test(output)) {
    return "npm could not reach the registry. Treat this as an environment/network/proxy issue, not a source-code failure; retry npm install/npm ci when network is available or check npm proxy/registry settings.";
  }
  if (/\bnpm\s+run\b/.test(command) && (/(?:^|\n)\s*(?:sh|bash|zsh):\s+[\w@./-]+:\s+command not found\b/i.test(output) || /(?:^|\r?\n)\s*'[^']+'\s+is not recognized as an internal or external command\b/i.test(output))) {
    return "This npm script could not find a local executable. Check whether dependencies were installed successfully in cwd, whether node_modules exists, and whether a prior npm install/npm ci failed before treating this as a code failure.";
  }
  return void 0;
}
function killProcessTree(pid) {
  if (process.platform === "win32") {
    try {
      spawn("taskkill", ["/F", "/T", "/PID", String(pid)], {
        stdio: "ignore",
        detached: true
      });
    } catch {
    }
  } else {
    try {
      process.kill(-pid, "SIGKILL");
    } catch {
      try {
        process.kill(pid, "SIGKILL");
      } catch {
      }
    }
  }
}
function createBashTool(defaultCwd, options) {
  return {
    name: "bash",
    label: "bash",
    description: `Execute a shell command in the current working directory. The public tool name remains bash for compatibility; on Windows shell:auto uses PowerShell, otherwise bash. Returns stdout and stderr. Output is truncated to last ${DEFAULT_MAX_LINES} lines or ${DEFAULT_MAX_BYTES / 1024}KB (whichever is hit first). If truncated, full output is saved to a temp file. Default timeout: 120s. Use timeout parameter for longer tasks (e.g., npm install -> 600).`,
    parameters: bashSchema,
    execute: async (_toolCallId, { command, timeout, cwd, shell }, signal, onUpdate) => {
      validateBashCommand(command);
      const resolvedShell = resolveShell(shell);
      const effectiveCommand = buildEffectiveCommand(command, options);
      const workingDir = normalizeWorkingDirectory(cwd, defaultCwd);
      validateWorkingDirectory(workingDir);
      const risk = classifyShellCommand(effectiveCommand, resolvedShell);
      if (risk.destructive && !options?.isYoloMode?.()) {
        const approved = await (options?.confirm ?? requestHighRiskConfirmation)({
          command: effectiveCommand,
          shell: resolvedShell,
          commandClass: risk.commandClass,
          riskWarnings: risk.riskWarnings
        }).catch(() => false);
        if (!approved) {
          return {
            content: [{
              type: "text",
              text: `Blocked high-risk shell command (${risk.commandClass}): ${risk.riskWarnings.join("; ")}. Ask the user for explicit confirmation or use a safer diagnostic command first.`
            }],
            details: {
              exitCode: null,
              signal: null,
              timedOut: false,
              shell: resolvedShell,
              commandClass: risk.commandClass,
              riskWarnings: risk.riskWarnings,
              permissionDecision: {
                allowed: false,
                reason: "destructive command blocked by shell policy",
                requiresPermission: true
              }
            },
            isError: true
          };
        }
      }
      const timeoutMs = (timeout ?? DEFAULT_TIMEOUT_SECONDS) * 1e3;
      return new Promise((resolve3, reject) => {
        if (signal?.aborted) {
          reject(new Error("Operation aborted"));
          return;
        }
        let tempFilePath;
        let tempFileStream;
        let totalBytes = 0;
        const chunks = [];
        let chunksBytes = 0;
        const maxChunksBytes = DEFAULT_MAX_BYTES * 2;
        let timedOut = false;
        const spawnCommand = buildSpawnCommand(resolvedShell, effectiveCommand);
        const child = spawn(spawnCommand.file, spawnCommand.args, {
          shell: spawnCommand.shell,
          cwd: workingDir,
          env: process.env,
          detached: process.platform !== "win32"
        });
        const timeoutId = setTimeout(() => {
          timedOut = true;
          if (child.pid) {
            killProcessTree(child.pid);
          }
        }, timeoutMs);
        const onAbort = () => {
          clearTimeout(timeoutId);
          if (child.pid) {
            killProcessTree(child.pid);
          }
          reject(new Error("Operation aborted"));
        };
        signal?.addEventListener("abort", onAbort, { once: true });
        child.stdout?.on("data", (data) => {
          totalBytes += data.length;
          if (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {
            tempFilePath = getTempFilePath();
            tempFileStream = createWriteStream(tempFilePath);
            for (const chunk of chunks) {
              tempFileStream.write(chunk);
            }
          }
          if (tempFileStream) {
            tempFileStream.write(data);
          }
          chunks.push(data);
          chunksBytes += data.length;
          while (chunksBytes > maxChunksBytes && chunks.length > 1) {
            const removed = chunks.shift();
            chunksBytes -= removed.length;
          }
          if (onUpdate) {
            const fullBuffer = Buffer.concat(chunks);
            const fullText = fullBuffer.toString("utf-8");
            const truncation = truncateTail(fullText);
            onUpdate({
              content: [{ type: "text", text: truncation.content || "" }],
              details: {
                exitCode: null,
                signal: null,
                timedOut: false,
                truncation: truncation.truncated ? {
                  truncated: true,
                  truncatedBy: truncation.truncatedBy,
                  totalLines: truncation.totalLines,
                  outputLines: truncation.outputLines
                } : void 0,
                fullOutputPath: tempFilePath,
                shell: resolvedShell,
                commandClass: risk.commandClass,
                riskWarnings: risk.riskWarnings,
                permissionDecision: {
                  allowed: true,
                  requiresPermission: risk.requiresPermission
                }
              }
            });
          }
        });
        child.stderr?.on("data", (data) => {
          totalBytes += data.length;
          if (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {
            tempFilePath = getTempFilePath();
            tempFileStream = createWriteStream(tempFilePath);
            for (const chunk of chunks) {
              tempFileStream.write(chunk);
            }
          }
          if (tempFileStream) {
            tempFileStream.write(data);
          }
          chunks.push(data);
          chunksBytes += data.length;
          while (chunksBytes > maxChunksBytes && chunks.length > 1) {
            const removed = chunks.shift();
            chunksBytes -= removed.length;
          }
        });
        child.on("close", async (code, sig) => {
          try {
            clearTimeout(timeoutId);
            signal?.removeEventListener("abort", onAbort);
            const fullBuffer = Buffer.concat(chunks);
            let fullOutput = fullBuffer.toString("utf-8");
            const truncation = truncateTail(fullOutput);
            let outputText = truncation.content || "(no output)";
            if (truncation.truncated && !tempFilePath) {
              tempFilePath = getTempFilePath();
              writeFileSync(tempFilePath, fullOutput, "utf-8");
            }
            await finishTempFileStream(tempFileStream);
            const details = {
              exitCode: code,
              signal: sig?.toString() ?? null,
              timedOut,
              shell: resolvedShell,
              commandClass: risk.commandClass,
              riskWarnings: risk.riskWarnings,
              permissionDecision: {
                allowed: true,
                requiresPermission: risk.requiresPermission
              }
            };
            const semantic = interpretShellExit(effectiveCommand, code);
            details.semanticExitMeaning = semantic.semanticExitMeaning;
            if (resolvedShell === "powershell" && code === 0 && /^\s*(?:diff|Compare-Object)\b/i.test(effectiveCommand) && /\bSideIndicator\b/.test(fullOutput)) {
              details.semanticExitMeaning = "files differ";
            }
            if (truncation.truncated) {
              details.truncation = {
                truncated: true,
                truncatedBy: truncation.truncatedBy,
                totalLines: truncation.totalLines,
                outputLines: truncation.outputLines
              };
              details.fullOutputPath = tempFilePath;
              const startLine = truncation.totalLines - truncation.outputLines + 1;
              const endLine = truncation.totalLines;
              if (truncation.lastLinePartial) {
                const lastLineSize = formatSize(
                  Buffer.byteLength(fullOutput.split("\n").pop() || "", "utf-8")
                );
                outputText += `

[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;
              } else if (truncation.truncatedBy === "lines") {
                outputText += `

[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${tempFilePath}]`;
              } else {
                outputText += `

[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${tempFilePath}]`;
              }
            }
            if (timedOut) {
              outputText += `

[Command timed out after ${timeout ?? DEFAULT_TIMEOUT_SECONDS} seconds]`;
            }
            const recoveryHint = buildBashRecoveryHint(command, outputText);
            if (recoveryHint) {
              outputText += `

[Recovery hint: ${recoveryHint}]`;
            }
            const content = [{ type: "text", text: outputText }];
            resolve3({ content, details, isError: timedOut || semantic.isError });
          } catch (error) {
            reject(error);
          }
        });
        child.on("error", (err) => {
          clearTimeout(timeoutId);
          signal?.removeEventListener("abort", onAbort);
          if (tempFileStream) {
            tempFileStream.end();
          }
          reject(new Error(`Failed to start bash command in ${workingDir}: ${err.message}. Check that the command is executable and cwd exists.`));
        });
      });
    }
  };
}

// src/capabilities/code-intel.ts
import * as fs from "node:fs";
import * as path from "node:path";
var projectCache = /* @__PURE__ */ new Map();
var typescriptModulePromise;
function loadTypeScript() {
  typescriptModulePromise ??= import("./typescript-GSKWJIO4.js");
  return typescriptModulePromise;
}
function findTsconfig(startPath, projectRoot) {
  let dir = fs.existsSync(startPath) && fs.statSync(startPath).isDirectory() ? startPath : path.dirname(startPath);
  const root = path.resolve(projectRoot);
  while (dir.startsWith(root)) {
    const candidate = path.join(dir, "tsconfig.json");
    if (fs.existsSync(candidate)) return candidate;
    const parent = path.dirname(dir);
    if (parent === dir) break;
    dir = parent;
  }
  const rootCandidate = path.join(root, "tsconfig.json");
  return fs.existsSync(rootCandidate) ? rootCandidate : void 0;
}
function readConfig(ts, configPath) {
  const config = ts.readConfigFile(configPath, ts.sys.readFile);
  if (config.error) {
    throw new Error(ts.flattenDiagnosticMessageText(config.error.messageText, "\n"));
  }
  const parsed = ts.parseJsonConfigFileContent(config.config, ts.sys, path.dirname(configPath));
  return { fileNames: parsed.fileNames, options: parsed.options };
}
function walkJsTs(root, limit = 1e3) {
  const skip = /* @__PURE__ */ new Set(["node_modules", "dist", "build", ".git", "Library", "Temp", "obj"]);
  const out = [];
  const visit = (dir) => {
    if (out.length >= limit) return;
    for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
      if (skip.has(entry.name)) continue;
      const full = path.join(dir, entry.name);
      if (entry.isDirectory()) visit(full);
      else if (/\.[cm]?[jt]sx?$/.test(entry.name)) out.push(full);
      if (out.length >= limit) return;
    }
  };
  if (fs.existsSync(root)) visit(root);
  return out;
}
function createProject(ts, projectRoot, focusPath) {
  const configPath = findTsconfig(focusPath ?? projectRoot, projectRoot);
  const key = configPath ?? `loose:${projectRoot}`;
  const cached = projectCache.get(key);
  if (cached) return cached;
  const config = configPath ? readConfig(ts, configPath) : {
    fileNames: walkJsTs(projectRoot),
    options: {
      allowJs: true,
      checkJs: true,
      moduleResolution: ts.ModuleResolutionKind.NodeJs,
      target: ts.ScriptTarget.ES2022,
      module: ts.ModuleKind.ESNext,
      jsx: ts.JsxEmit.ReactJSX
    }
  };
  const versions = new Map(config.fileNames.map((file) => [path.resolve(file), "0"]));
  const host = {
    getScriptFileNames: () => [...versions.keys()],
    getScriptVersion: (fileName) => versions.get(path.resolve(fileName)) ?? "0",
    getScriptSnapshot: (fileName) => {
      if (!fs.existsSync(fileName)) return void 0;
      return ts.ScriptSnapshot.fromString(fs.readFileSync(fileName, "utf8"));
    },
    getCurrentDirectory: () => configPath ? path.dirname(configPath) : projectRoot,
    getCompilationSettings: () => config.options,
    getDefaultLibFileName: (options) => ts.getDefaultLibFilePath(options),
    fileExists: ts.sys.fileExists,
    readFile: ts.sys.readFile,
    readDirectory: ts.sys.readDirectory,
    directoryExists: ts.sys.directoryExists,
    getDirectories: ts.sys.getDirectories
  };
  const project = {
    configPath,
    service: ts.createLanguageService(host),
    fileNames: [...versions.keys()],
    options: config.options
  };
  projectCache.set(key, project);
  return project;
}
function offsetOf(ts, sourceFile, line, character) {
  if (!line || !character) throw new Error("operation requires path, line, and character");
  return ts.getPositionOfLineAndCharacter(sourceFile, Math.max(0, line - 1), Math.max(0, character - 1));
}
function loc(fileName, start = 0) {
  const text = fs.existsSync(fileName) ? fs.readFileSync(fileName, "utf8").slice(0, start) : "";
  const lines = text.split(/\r?\n/);
  return `${fileName}:${lines.length}:${lines[lines.length - 1].length + 1}`;
}
function diagnosticText(ts, d) {
  const where = d.file && typeof d.start === "number" ? `${loc(d.file.fileName, d.start)} ` : "";
  return `${where}${ts.flattenDiagnosticMessageText(d.messageText, "\n")}`;
}
function navItems(items, fileName) {
  const out = [];
  const visit = (item, prefix = "") => {
    const span = item.spans[0];
    out.push(`${prefix}${item.text} (${item.kind}) ${span ? loc(fileName, span.start) : fileName}`);
    for (const child of item.childItems ?? []) visit(child, `${prefix}${item.text}.`);
  };
  for (const item of items) visit(item);
  return out;
}
function createTypeScriptCodeIntelProvider() {
  const lightweight = createLightweightCodeIntelProvider();
  return {
    async run(input, context) {
      try {
        const ts = await loadTypeScript();
        const filePath = context.resolvePath(input.path);
        const project = createProject(ts, context.projectRoot, filePath);
        const files = filePath ? [filePath] : project.fileNames;
        if (input.operation === "diagnostics") {
          const diagnostics = files.flatMap((file) => [
            ...project.service.getSyntacticDiagnostics(file),
            ...project.service.getSemanticDiagnostics(file)
          ]);
          return {
            text: diagnostics.length ? diagnostics.map((diagnostic) => diagnosticText(ts, diagnostic)).join("\n") : "No TypeScript diagnostics found.",
            details: {
              operation: input.operation,
              projectRoot: context.projectRoot,
              path: filePath,
              provider: "typescript_service",
              resultCount: diagnostics.length,
              diagnosticCount: diagnostics.length,
              tsconfigPath: project.configPath
            }
          };
        }
        if (input.operation === "symbols") {
          const query = input.query?.toLowerCase();
          const symbols = files.flatMap((file) => navItems(project.service.getNavigationBarItems(file), file)).filter((line) => !query || line.toLowerCase().includes(query));
          return {
            text: symbols.length ? symbols.join("\n") : "No symbols found.",
            details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: symbols.length, tsconfigPath: project.configPath }
          };
        }
        if (!filePath) throw new Error(`${input.operation} requires path`);
        const source = project.service.getProgram()?.getSourceFile(filePath);
        if (!source) throw new Error(`File is not part of the TypeScript project: ${input.path}`);
        const offset = offsetOf(ts, source, input.line, input.character);
        if (input.operation === "definition") {
          const defs = project.service.getDefinitionAtPosition(filePath, offset) ?? [];
          return {
            text: defs.length ? defs.map((def) => `${def.name} ${loc(def.fileName, def.textSpan.start)}`).join("\n") : "No definition found.",
            details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: defs.length, tsconfigPath: project.configPath }
          };
        }
        if (input.operation === "references") {
          const refs = project.service.getReferencesAtPosition(filePath, offset) ?? [];
          return {
            text: refs.length ? refs.map((ref) => loc(ref.fileName, ref.textSpan.start)).join("\n") : "No references found.",
            details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: refs.length, tsconfigPath: project.configPath }
          };
        }
        const quickInfo = project.service.getQuickInfoAtPosition(filePath, offset);
        const text = quickInfo ? ts.displayPartsToString(quickInfo.displayParts ?? []) + (quickInfo.documentation?.length ? `
${ts.displayPartsToString(quickInfo.documentation)}` : "") : "No hover information found.";
        return {
          text,
          details: { operation: input.operation, projectRoot: context.projectRoot, path: filePath, provider: "typescript_service", resultCount: quickInfo ? 1 : 0, tsconfigPath: project.configPath }
        };
      } catch (error) {
        const fallback = await lightweight.run(input, context);
        return {
          ...fallback,
          details: {
            ...fallback.details,
            provider: "lightweight_fallback",
            fallbackReason: error instanceof Error ? error.message : String(error)
          }
        };
      }
    }
  };
}
function createCodeIntelTool2(options) {
  return createCodeIntelTool({
    ...options,
    provider: options.provider ?? createTypeScriptCodeIntelProvider()
  });
}

// src/capabilities/rendered-page-reader.ts
import { spawn as spawn2 } from "node:child_process";
import { createRequire } from "node:module";
var require2 = createRequire(import.meta.url);
function resolvePlaywrightCli() {
  return require2.resolve("@playwright/cli/playwright-cli.js");
}
function runPlaywright(args, timeoutMs = 6e4) {
  return new Promise((resolve3, reject) => {
    const child = spawn2(process.execPath, [resolvePlaywrightCli(), ...args], {
      stdio: ["ignore", "pipe", "pipe"],
      shell: false
    });
    let stdout = "";
    let stderr = "";
    const timer = setTimeout(() => {
      child.kill("SIGTERM");
      reject(new Error(`Playwright CLI timed out after ${Math.ceil(timeoutMs / 1e3)} seconds`));
    }, timeoutMs);
    child.stdout.setEncoding("utf8");
    child.stderr.setEncoding("utf8");
    child.stdout.on("data", (chunk) => {
      stdout += chunk;
    });
    child.stderr.on("data", (chunk) => {
      stderr += chunk;
    });
    child.on("error", (error) => {
      clearTimeout(timer);
      reject(error);
    });
    child.on("close", (code) => {
      clearTimeout(timer);
      if (code === 0) resolve3(stdout.trim());
      else reject(new Error((stderr || stdout || `Playwright CLI exited with ${code}`).trim()));
    });
  });
}
function linesOf(output, max = 50) {
  return output.split(/\r?\n/).map((line) => line.trim()).filter(Boolean).slice(0, max);
}
function parseScreenshotPath(output) {
  const match = output.match(/\(([^)]+\.png)\)/i) || output.match(/([^\s]+\.png)/i);
  return match?.[1];
}
function parsePageInfo(raw) {
  const trimmed = raw.trim();
  if (!trimmed) return {};
  try {
    const decoded = trimmed.startsWith('"') ? JSON.parse(trimmed) : trimmed;
    const parsed = typeof decoded === "string" ? JSON.parse(decoded) : decoded;
    if (!parsed || typeof parsed !== "object") return {};
    return {
      finalUrl: typeof parsed.href === "string" ? parsed.href : void 0,
      title: typeof parsed.title === "string" ? parsed.title : void 0,
      text: typeof parsed.text === "string" ? parsed.text : void 0
    };
  } catch {
    return {};
  }
}
function createCliRenderedPageReader() {
  return async ({ url }) => {
    const session = `pie-web-research-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
    try {
      await runPlaywright([`-s=${session}`, "open", "about:blank"]);
      await runPlaywright([`-s=${session}`, "goto", url]);
      const [snapshot, pageInfoRaw, screenshotOutput, consoleOutput, networkOutput] = await Promise.all([
        runPlaywright([`-s=${session}`, "snapshot"]),
        runPlaywright([`-s=${session}`, "--raw", "eval", "() => JSON.stringify({ title: document.title, href: location.href, text: document.body ? document.body.innerText : '' })"]),
        runPlaywright([`-s=${session}`, "--raw", "screenshot"]).catch((error) => `screenshot_failed: ${error instanceof Error ? error.message : String(error)}`),
        runPlaywright([`-s=${session}`, "--raw", "console"]).catch((error) => `console_failed: ${error instanceof Error ? error.message : String(error)}`),
        runPlaywright([`-s=${session}`, "--raw", "network"]).catch((error) => `network_failed: ${error instanceof Error ? error.message : String(error)}`)
      ]);
      const pageInfo = parsePageInfo(pageInfoRaw);
      const text = pageInfo.text?.trim() || snapshot.trim();
      if (!text) {
        throw new Error("Playwright snapshot returned no text.");
      }
      return {
        url,
        finalUrl: pageInfo.finalUrl || url,
        title: pageInfo.title,
        text,
        citationAnchors: [{ url: pageInfo.finalUrl || url, title: pageInfo.title, startOffset: 0, endOffset: Math.min(120, text.length) }],
        screenshotPath: parseScreenshotPath(screenshotOutput),
        consoleMessages: linesOf(consoleOutput),
        networkRequests: linesOf(networkOutput)
      };
    } finally {
      runPlaywright([`-s=${session}`, "close"], 1e4).catch(() => void 0);
    }
  };
}

// src/capabilities/web-research-health.ts
import fs2 from "node:fs";
import path2 from "node:path";
function isRecord(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}
function parseHealthEntry(value) {
  if (!isRecord(value)) return void 0;
  const routeKey = typeof value.routeKey === "string" ? value.routeKey : "";
  if (!routeKey) return void 0;
  return {
    routeKey,
    attempts: Number(value.attempts) || 0,
    successes: Number(value.successes) || 0,
    failures: Number(value.failures) || 0,
    lowQuality: Number(value.lowQuality) || 0,
    rateLimited: Number(value.rateLimited) || 0,
    timeouts: Number(value.timeouts) || 0,
    totalDurationMs: Number(value.totalDurationMs) || 0,
    totalSources: Number(value.totalSources) || 0,
    updatedAt: Number(value.updatedAt) || 0
  };
}
function createCliWebResearchHealthStore(configDir) {
  const filePath = path2.join(configDir, "web-research-health.json");
  return {
    loadProviderHealth: () => {
      try {
        const parsed = JSON.parse(fs2.readFileSync(filePath, "utf8"));
        if (!Array.isArray(parsed)) return [];
        return parsed.map(parseHealthEntry).filter((entry) => !!entry);
      } catch {
        return [];
      }
    },
    saveProviderHealth: (entries) => {
      try {
        fs2.mkdirSync(path2.dirname(filePath), { recursive: true });
        fs2.writeFileSync(filePath, JSON.stringify(entries, null, 2));
      } catch {
      }
    }
  };
}

// src/capabilities/index.ts
function createCliHostCapabilities(cwd, options = {}) {
  const bashTool = createBashTool(cwd);
  const healthStore = createCliWebResearchHealthStore(options.configDir ?? getConfigDir());
  const webSearchTool = createSharedWebSearchTool({
    getModel: options.getModel ?? (() => void 0),
    getApiKey: options.getApiKey ?? (() => void 0),
    getMode: options.getWebSearchMode,
    resolveToolModel: options.resolveToolModel,
    resolveToolModelCandidates: options.resolveToolModelCandidates
  });
  const webFetchTool = createSharedWebFetchTool({
    getModel: options.getModel,
    getApiKey: options.getApiKey
  });
  const webResearchTool = createSharedWebResearchTool({
    getModel: options.getModel ?? (() => void 0),
    getApiKey: options.getApiKey ?? (() => void 0),
    getMode: options.getWebSearchMode,
    resolveToolModel: options.resolveToolModel,
    resolveToolModelCandidates: (purpose) => options.resolveToolModelCandidates?.(purpose),
    timeoutMs: 6e4,
    searchAttemptTimeoutMs: 45e3,
    fetchAttemptTimeoutMs: 3e4,
    browserAttemptTimeoutMs: 45e3,
    getCurrentDateContext: () => ({
      currentDate: (/* @__PURE__ */ new Date()).toISOString().slice(0, 10),
      timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
    }),
    webFetchTool,
    renderedPageReader: createCliRenderedPageReader(),
    loadProviderHealth: healthStore.loadProviderHealth,
    saveProviderHealth: healthStore.saveProviderHealth
  });
  const codeIntelTool = createCodeIntelTool2({ projectRoot: cwd });
  const capabilities = [
    {
      id: "bash",
      description: "Node/TUI host capability for executing shell commands in the local workspace.",
      tools: [bashTool]
    },
    {
      id: "web_research",
      description: "End-to-end web research that searches, fetches credible sources, and returns citations.",
      tools: [webResearchTool]
    },
    {
      id: "web_search",
      description: "Provider-native web search for current information outside the local workspace.",
      tools: [webSearchTool]
    },
    {
      id: "web_fetch",
      description: "Fetch and read a specific public web URL after a source has been found.",
      tools: [webFetchTool]
    },
    {
      id: "code_intel",
      description: "Read-only JS/TS code intelligence for diagnostics, definitions, references, symbols, and hover.",
      tools: [codeIntelTool]
    }
  ];
  return {
    capabilities,
    tools: capabilities.flatMap((capability) => capability.tools)
  };
}

export {
  getPackageDir,
  getThemesDir,
  getBuiltinDir,
  getConfigDir,
  getAgentDir,
  getCustomThemesDir,
  getSettingsPath,
  getSessionsDir,
  getLogsDir,
  getRuntimeLogPath,
  migrateConfigFromAgentDir,
  createCliWebResearchHealthStore,
  createCliHostCapabilities
};
