#!/usr/bin/env node
// catalog.mjs — generate the agent catalog from @agent-manifest headers.
//
// Scans agents/scripts/**/*.mjs plus the runtime PHP agents listed below, extracts each
// fenced `@agent-manifest … @end` block, and emits:
//   agents/AGENTS.md    — human catalog, one table per class (GENERATED — do not edit)
//   agents/agents.json  — machine manifest; clones diff this to find missing deps
//
// Run after adding/editing an agent:  node agents/catalog.mjs
// (also runs automatically at the end of scripts/configure.mjs)
import { readFileSync, writeFileSync, readdirSync } from "node:fs";
import { dirname, join, relative } from "node:path";
import { fileURLToPath } from "node:url";

const HERE = dirname(fileURLToPath(import.meta.url));
const ROOT = join(HERE, "..");

// Runtime agents living outside agents/ (PHP, served over HTTP) — included explicitly.
const RUNTIME_FILES = [];  // no runtime PHP agents in the seed base yet

const CLASSES = ["content", "operational", "runtime", "plumbing"];
const LIST_KEYS = ["prompt", "skills", "tools", "reads", "writes"];

function parseManifest(src) {
  const m = src.match(/@agent-manifest\s*\n([\s\S]*?)\n\s*\*?\s*@end/);
  if (!m) return null;
  const out = {};
  for (const raw of m[1].split("\n")) {
    const line = raw.replace(/^\s*\*\s?/, ""); // strip comment leader
    const kv = line.match(/^([a-z-]+):\s*(.*)$/);
    if (kv) out[kv[1]] = kv[2].trim();
  }
  for (const k of LIST_KEYS) {
    const v = out[k];
    out[k] = !v || v === "-" ? [] : v.split(",").map((s) => s.trim()).filter(Boolean);
  }
  return out;
}

const files = readdirSync(join(HERE, "scripts"))
  .filter((f) => f.endsWith(".mjs"))
  .map((f) => join("agents", "scripts", f))
  .concat(RUNTIME_FILES);

const agents = [];
for (const rel of files.sort()) {
  const manifest = parseManifest(readFileSync(join(ROOT, rel), "utf8"));
  if (!manifest) continue; // no manifest → not an agent (e.g. future helpers)
  if (!manifest.name || !manifest.class) {
    console.error(`[catalog] ${rel}: manifest missing name/class — skipped`);
    continue;
  }
  if (!CLASSES.includes(manifest.class)) {
    console.error(`[catalog] ${rel}: unknown class "${manifest.class}" — skipped`);
    continue;
  }
  agents.push({ file: rel, ...manifest });
}
agents.sort((a, b) => a.name.localeCompare(b.name));

// ---- agents.json (machine manifest) ----
writeFileSync(join(HERE, "agents.json"), JSON.stringify({ agents }, null, 2) + "\n");

// ---- AGENTS.md (human catalog) ----
const esc = (s) => String(s).replace(/\|/g, "\\|");
const classBlurb = {
  content: "Cron/manual-triggered producers of gated drafts (events auto-publishes).",
  operational: "Event/queue-triggered reviewers and responders (gated + logged).",
  runtime: "HTTP-triggered agents answering live user requests.",
  plumbing: "Deterministic helpers — not agents (no LLM), catalogued for completeness.",
};

let md = `# Agent catalog

> **GENERATED — do not edit.** Regenerate with \`node agents/catalog.mjs\`
> (source of truth: the \`@agent-manifest\` header in each script).

An **agent** = an LLM orchestrator script + its role prompt (\`prompt:\`) + its skills
(MCP servers, \`skills:\`) + its tools (\`api/cli/*.php\` bridges and \`lib/*.mjs\`, \`tools:\`).
To port an agent to another SeedProject clone, satisfy its manifest: copy its prompts, ensure
its tools exist, migrate each table in \`reads:\`/\`writes:\`, configure its MCP servers.
\`agents.json\` is the machine-readable version — diff it against a clone to find gaps.
`;

for (const cls of CLASSES) {
  const group = agents.filter((a) => a.class === cls);
  if (!group.length) continue;
  md += `\n## ${cls}\n\n${classBlurb[cls]}\n\n`;
  md += `| agent | trigger | model | skills | tools | reads → writes | description |\n`;
  md += `|---|---|---|---|---|---|---|\n`;
  for (const a of group) {
    const rw = `${a.reads.join(", ") || "—"} → ${a.writes.join(", ") || "—"}`;
    md += `| **${esc(a.name)}** ([${esc(a.file)}](../${a.file})) | ${esc(a.trigger || "—")} | ${esc(a.model || "—")} | ${esc(a.skills.join(", ") || "—")} | ${esc(a.tools.join(", ") || "—")} | ${esc(rw)} | ${esc(a.description || "")} |\n`;
  }
}

writeFileSync(join(HERE, "AGENTS.md"), md);
console.log(`[catalog] ${agents.length} manifests → agents/AGENTS.md + agents/agents.json`);
