// Transformative summary of fetched page text via a Claude Code subprocess.
//
//   VALIDATE TEXT ──▶ SPAWN claude --print --model <haiku> --max-turns 1 ──▶ STDOUT
//        │                       │                                              │
//        ├─ empty → throw        ├─ ENOENT (claude not in PATH) → throw         │
//        │                       ├─ non-zero exit → throw (stderr + stdout)     │
//        │                       ├─ timeout → SIGTERM → throw                   │
//        │                       └─ empty stdout → throw                        │
//        └───────────────────────────────────────────────────────────▶ return text
//
// Claude Code handles its own OAuth internally — no API key, no SDK. This is
// the same mechanism the workflows plugin uses for LLM steps. The summary is
// transformative, so it does not trip WebFetch's copyright refusal; the
// verbatim path keeps the model out entirely.
import { spawn as realSpawn } from "node:child_process";
/** Haiku is the right tier for a one-shot summary (cheap, fast). Overridable
 *  via env for forward-compat with model id changes. */
const SUMMARY_MODEL = process.env.URL_GET_SUMMARY_MODEL ?? "claude-haiku-4-5";
const SUMMARY_TIMEOUT_MS = 60_000;
const MAX_STDERR_BYTES = 4096;
/** Builds the summarisation prompt. A single principled instruction, not a
 *  checklist of conditions — per the project's LLM-classification doctrine,
 *  a prescriptive rule list turns the model into a brittle pattern matcher. */
function buildPrompt(url, text) {
    return (`Summarise the page below for a reader deciding whether to open the full text. ` +
        `Capture what the page is, its main claims, and anything that would matter to ` +
        `someone searching for it later. Write only the summary — no preamble.\n\n` +
        `Source: ${url}\n\n---\n${text}`);
}
/** Run the summary. Throws on every failure mode (the caller decides whether a
 *  summary failure should degrade gracefully — the verbatim file is already on
 *  disk by the time this runs). */
export async function summarise(url, text, _spawn = realSpawn) {
    if (!text.trim()) {
        throw new Error("nothing to summarise — cleaned text is empty");
    }
    const prompt = buildPrompt(url, text);
    const args = [
        "--print",
        "--model",
        SUMMARY_MODEL,
        "--max-turns",
        "1",
        "--permission-mode",
        "dontAsk",
        prompt,
    ];
    return new Promise((resolve, reject) => {
        const proc = _spawn("claude", args, { stdio: ["ignore", "pipe", "pipe"] });
        let stdout = "";
        let stderr = "";
        proc.stdout?.on("data", (chunk) => {
            stdout += chunk.toString("utf-8");
        });
        proc.stderr?.on("data", (chunk) => {
            if (stderr.length < MAX_STDERR_BYTES) {
                stderr += chunk.toString("utf-8").slice(0, MAX_STDERR_BYTES - stderr.length);
            }
        });
        const timer = setTimeout(() => {
            proc.kill("SIGTERM");
            reject(new Error(`summary timed out after ${SUMMARY_TIMEOUT_MS}ms`));
        }, SUMMARY_TIMEOUT_MS);
        proc.on("close", (code) => {
            clearTimeout(timer);
            if (code !== 0) {
                const tail = stderr.trim() ? ` stderr: ${stderr.trim()}` : "";
                reject(new Error(`claude exited ${code}${tail}`));
                return;
            }
            if (!stdout.trim()) {
                reject(new Error("claude returned an empty summary"));
                return;
            }
            resolve(stdout.trim());
        });
        proc.on("error", (err) => {
            clearTimeout(timer);
            reject(new Error(`failed to spawn claude: ${err.message}`));
        });
    });
}
//# sourceMappingURL=summarise.js.map