import { fileURLToPath as __fileURLToPath } from 'url';
import { dirname as __pathDirname } from 'path';
const __filename = __fileURLToPath(import.meta.url);
const __dirname = __pathDirname(__filename);
import { callClaude, callCodexCLI, implementWithClaude } from "./providers.js";
import * as fs from "fs";
import * as path from "path";
import { FrameManager } from "../../core/context/index.js";
import { deriveProjectId } from "./utils.js";
function heuristicPlan(input) {
  return {
    summary: `Plan for: ${input.task}`,
    steps: [
      {
        id: "step-1",
        title: "Analyze requirements",
        rationale: "Understand the task scope and constraints",
        acceptanceCriteria: [
          "Requirements clearly defined",
          "Edge cases identified"
        ]
      },
      {
        id: "step-2",
        title: "Implement core changes",
        rationale: "Make the minimal changes needed to complete the task",
        acceptanceCriteria: [
          "Code compiles without errors",
          "Core functionality works"
        ]
      },
      {
        id: "step-3",
        title: "Verify and test",
        rationale: "Ensure changes work correctly",
        acceptanceCriteria: ["Tests pass", "No regressions introduced"]
      }
    ],
    risks: [
      "API key not configured - using heuristic plan",
      "May need manual review of generated code"
    ]
  };
}
async function runSpike(input, options = {}) {
  const plannerSystem = `You write concise, actionable implementation plans. Output raw JSON only (no markdown code fences). Schema: { "summary": "string", "steps": [{ "id": "step-1", "title": "string", "rationale": "string", "acceptanceCriteria": ["string"] }], "risks": ["string"] }`;
  const contextSummary = getLocalContextSummary(input.repoPath);
  const plannerPrompt = `Task: ${input.task}
Repo: ${input.repoPath}
Notes: ${input.contextNotes || "(none)"}
${contextSummary}
Constraints: Keep the plan minimal and implementable in a single PR.`;
  let plan;
  try {
    const raw = await callClaude(plannerPrompt, {
      model: options.plannerModel,
      system: plannerSystem
    });
    try {
      const cleaned = raw.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "").trim();
      plan = JSON.parse(cleaned);
    } catch {
      plan = heuristicPlan(input);
    }
  } catch {
    plan = heuristicPlan(input);
  }
  const implementer = options.implementer || "codex";
  const maxIters = Math.max(1, options.maxIters ?? 2);
  const iterations = [];
  let approved = false;
  let lastCommand = "";
  let lastOutput = "";
  let lastCritique = {
    approved: true,
    issues: [],
    suggestions: []
  };
  for (let i = 0; i < maxIters; i++) {
    const stepsList = plan.steps.map((s, idx) => `${idx + 1}. ${s.title}`).join("\n");
    const basePrompt = `Implement the following plan:
${stepsList}

Keep changes minimal and focused. Avoid unrelated edits.`;
    const refine = i === 0 ? "" : `
Incorporate reviewer suggestions: ${lastCritique.suggestions.join("; ")}`;
    const implPrompt = basePrompt + refine;
    let ok = false;
    if (implementer === "codex") {
      const impl = callCodexCLI(
        implPrompt,
        [],
        options.dryRun !== false,
        input.repoPath
      );
      ok = impl.ok;
      lastCommand = impl.command;
      lastOutput = impl.output;
    } else {
      const impl = await implementWithClaude(implPrompt, {
        model: options.plannerModel
      });
      ok = impl.ok;
      lastCommand = `claude:${options.plannerModel || "sonnet"} prompt`;
      lastOutput = impl.output;
    }
    const criticSystem = `You are a strict code reviewer. Return a JSON object: { approved: boolean, issues: string[], suggestions: string[] }`;
    const criticPrompt = `Plan: ${plan.summary}
Attempt ${i + 1}/${maxIters}
Command: ${lastCommand}
Output: ${lastOutput.slice(0, 2e3)}`;
    try {
      const raw = await callClaude(criticPrompt, {
        model: options.reviewerModel,
        system: criticSystem
      });
      const cleaned = raw.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "").trim();
      lastCritique = JSON.parse(cleaned);
    } catch {
      lastCritique = {
        approved: ok,
        issues: ok ? [] : ["Critique failed"],
        suggestions: []
      };
    }
    iterations.push({
      command: lastCommand,
      ok,
      outputPreview: lastOutput.slice(0, 400),
      critique: lastCritique
    });
    if (lastCritique.approved) {
      approved = true;
      break;
    }
  }
  try {
    const dir = options.auditDir || path.join(input.repoPath, ".stackmemory", "build");
    fs.mkdirSync(dir, { recursive: true });
    const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
    const file = path.join(dir, `spike-${stamp}.json`);
    fs.writeFileSync(
      file,
      JSON.stringify(
        {
          input,
          options: { ...options, auditDir: void 0 },
          plan,
          iterations
        },
        null,
        2
      )
    );
  } catch {
  }
  if (options.record) {
    void recordContext(
      input.repoPath,
      "decision",
      `Plan: ${plan.summary}`,
      0.8
    );
    void recordContext(
      input.repoPath,
      "decision",
      `Critique: ${lastCritique.approved ? "approved" : "needs_changes"}`,
      0.6
    );
  }
  if (options.recordFrame) {
    void recordAsFrame(
      input.repoPath,
      input.task,
      plan,
      lastCritique,
      iterations
    );
  }
  return {
    plan,
    implementation: {
      success: approved,
      summary: approved ? "Implementation approved by critic" : "Implementation not approved",
      commands: iterations.map((it) => it.command)
    },
    critique: lastCritique,
    iterations
  };
}
const runPlanAndCode = runSpike;
async function recordContext(repoPath, type, content, importance = 0.6) {
  try {
    const dbPath = path.join(repoPath, ".stackmemory", "context.db");
    if (!fs.existsSync(path.dirname(dbPath)))
      fs.mkdirSync(path.dirname(dbPath), { recursive: true });
    const { default: Database } = await import("better-sqlite3");
    const db = new Database(dbPath);
    db.exec(`
      CREATE TABLE IF NOT EXISTS contexts (
        id TEXT PRIMARY KEY,
        type TEXT NOT NULL,
        content TEXT NOT NULL,
        importance REAL DEFAULT 0.5,
        created_at INTEGER DEFAULT (unixepoch()),
        last_accessed INTEGER DEFAULT (unixepoch()),
        access_count INTEGER DEFAULT 1
      );
    `);
    const id = `ctx_${Date.now()}_${Math.random().toString(36).slice(2, 8)}`;
    const stmt = db.prepare(
      "INSERT OR REPLACE INTO contexts (id, type, content, importance) VALUES (?, ?, ?, ?)"
    );
    stmt.run(id, type, content, importance);
    db.close();
  } catch {
  }
}
async function recordAsFrame(repoPath, task, plan, critique, iterations) {
  try {
    const dbPath = path.join(repoPath, ".stackmemory", "context.db");
    fs.mkdirSync(path.dirname(dbPath), { recursive: true });
    const { default: Database } = await import("better-sqlite3");
    const db = new Database(dbPath);
    const projectId = deriveProjectId(repoPath);
    const fm = new FrameManager(db, projectId);
    const frameId = fm.createFrame({
      type: "task",
      name: `Plan & Code: ${task}`,
      inputs: { plan }
    });
    fm.addAnchor("DECISION", plan.summary, 8, { source: "build" }, frameId);
    const commands = iterations.map((it) => it.command).filter(Boolean);
    if (commands.length) {
      fm.addAnchor(
        "FACT",
        `Commands: ${commands.join(" | ")}`,
        5,
        { commands },
        frameId
      );
    }
    if (critique.issues?.length) {
      critique.issues.slice(0, 5).forEach((issue) => fm.addAnchor("RISK", issue, 6, {}, frameId));
    }
    if (critique.suggestions?.length) {
      critique.suggestions.slice(0, 5).forEach(
        (s) => fm.addAnchor("TODO", s, 5, { from: "critic" }, frameId)
      );
    }
    fm.closeFrame(frameId, { approved: critique.approved });
    db.close();
  } catch {
  }
}
async function runPlanOnly(input, options = {}) {
  const plannerSystem = `You write concise, actionable implementation plans. Output raw JSON only (no markdown code fences). Schema: { "summary": "string", "steps": [{ "id": "step-1", "title": "string", "rationale": "string", "acceptanceCriteria": ["string"] }], "risks": ["string"] }`;
  const contextSummary = getLocalContextSummary(input.repoPath);
  const plannerPrompt = `Task: ${input.task}
Repo: ${input.repoPath}
Notes: ${input.contextNotes || "(none)"}
${contextSummary}
Constraints: Keep the plan minimal and implementable in a single PR.`;
  try {
    const raw = await callClaude(plannerPrompt, {
      model: options.plannerModel,
      system: plannerSystem
    });
    try {
      return JSON.parse(raw);
    } catch {
      return heuristicPlan(input);
    }
  } catch {
    return heuristicPlan(input);
  }
}
function getLocalContextSummary(repoPath) {
  try {
    const dbPath = path.join(repoPath, ".stackmemory", "context.db");
    if (!fs.existsSync(dbPath)) return "Project context: (no local DB found)";
    return "Project context: (available \u2014 DB present)";
  } catch {
    return "Project context: (unavailable \u2014 local DB not ready)";
  }
}
export {
  runPlanAndCode,
  runPlanOnly,
  runSpike
};
//# sourceMappingURL=harness.js.map
