#!/usr/bin/env node
/**
 * @agent-manifest
 * name:        reviser
 * title:       Draft Reviser
 * class:       content
 * trigger:     manual <slug> (auto-invoked when the SEO gate fails and autoRevise is on)
 * description: Feed the SEO audit's fixes back to the writer and re-audit until the draft passes or attempts run out.
 * model:       writer=claude-sonnet-4-6
 * prompt:      prompts/reviser.system.md
 * skills:      -
 * tools:       lib/claude.mjs
 * reads:       -
 * writes:      -
 * created:     2026-07-04
 * @end
 */
// Auto-revise loop: feed the SEO audit's fixes back to the writer, re-audit, repeat until the
// draft passes the gate or maxReviseAttempts is reached.
// Usage: node agents/scripts/revise.mjs <slug>
import { existsSync } from "node:fs";
import { dirname, resolve, join } from "node:path";
import { fileURLToPath } from "node:url";
import { runClaude } from "./lib/claude.mjs";
import { loadJson } from "./lib/util.mjs";
import { runSeoReview } from "./seo-review.mjs";

const HERE = dirname(fileURLToPath(import.meta.url));
const PIPELINE = resolve(HERE, "..");
const cfg = loadJson(join(PIPELINE, "config.json"));
const root = cfg.paths.projectRoot;

const failing = (audit, minScore) =>
  !audit || audit.verdict === "fail" || (audit.overall ?? 0) < minScore;

/**
 * Revise a draft until it passes the SEO gate (or attempts run out).
 * @returns {Promise<{audit:object|null, attempts:number}>}
 */
export async function runRevise(slug, { maxAttempts } = {}) {
  const max = maxAttempts ?? cfg.seo?.maxReviseAttempts ?? 2;
  const minScore = cfg.seo?.minScore ?? 85;
  const draftDir = join(root, cfg.paths.draftsDir, slug);
  const mdxPath = join(draftDir, "index.mdx");
  const auditPath = join(draftDir, "seo-review.json");
  if (!existsSync(mdxPath)) {
    console.error(`[revise] no draft at ${mdxPath}`);
    return { audit: null, attempts: 0 };
  }

  const calPathForEntry = join(root, cfg.paths.calendar);
  const calEntry = existsSync(calPathForEntry)
    ? loadJson(calPathForEntry).find((e) => e.slug === slug)
    : null;
  const firsthandNote = calEntry?.authorFirsthand
    ? `\n\nIMPORTANT — authorFirsthand piece: a real, named author (${cfg.author?.name || "the site author"}) wrote this personally. Do NOT strip or neutralize the first-person voice and subjective opinions — that is authentic Experience and must be kept. Only add citations for objective facts (addresses, prices, dates, hours) or frame them honestly.`
    : "";

  let audit = existsSync(auditPath) ? loadJson(auditPath) : await runSeoReview(slug, { published: false });

  let attempts = 0;
  while (failing(audit, minScore) && attempts < max) {
    attempts++;
    const keyword = audit?.primaryKeyword || slug.replace(/-/g, " ");
    console.log(`[revise] attempt ${attempts}/${max} on ${slug} (current: ${audit?.verdict} ${audit?.overall})`);

    const prompt = `Revise this draft to pass the SEO/EEAT gate (target ≥ ${minScore}).

- Draft: ${mdxPath}
- Citations file: ${join(draftDir, "sources.json")}
- Audit to address (work through blocking + topFixes): ${auditPath}
- Primary keyword: ${keyword}
- Existing published posts (for internal links): ${join(root, cfg.paths.blogContentDir)}${firsthandNote}

Follow the reviser instructions in your system prompt exactly. Edit the files in place.`;

    await runClaude({
      prompt,
      systemPromptFile: join(PIPELINE, "prompts/reviser.system.md"),
      model: cfg.models.writer,
      allowedTools: "Read Write Edit Glob Grep WebSearch",
      addDirs: [root],
      cwd: root,
      logFile: join(root, cfg.paths.logsDir, `revise-${slug}.log`),
    });

    audit = await runSeoReview(slug, { published: false });
  }

  const ok = !failing(audit, minScore);
  console.log(
    `[revise] ${slug}: ${ok ? "PASS" : "still failing"} after ${attempts} attempt(s) — ` +
      `${audit?.verdict} ${audit?.overall}`
  );
  return { audit, attempts };
}

// CLI
if (import.meta.url === `file://${process.argv[1]}`) {
  const slug = process.argv[2];
  if (!slug) {
    console.error("Usage: revise.mjs <slug>");
    process.exit(1);
  }
  const { audit } = await runRevise(slug);
  process.exit(audit && audit.verdict === "pass" ? 0 : 2);
}
