"use strict";
/**
 * AgentCreator — authoring system for standalone agents under `.catalog/agents/`.
 *
 * Story 5.5 — OpenLife v1.8 (M5 of Hit-10 sprint).
 *
 * Replaces the 6-line placeholder previously inlined in
 * `aiobuilder create-agent` with a real authoring pipeline that produces
 * an AGENT.md with complete frontmatter + structured sections (Goal,
 * Commands, Dependencies, Persona, Guardrails). Mirrors SkillCreator
 * (single-file output) rather than SquadCreator (multi-file directory),
 * since standalone agents live as one AGENT.md inside their own directory.
 *
 * Methods:
 *   - create(proposal)   — render full AGENT.md from a proposal
 *   - validate(id)       — semantic frontmatter + cross-ref check
 *   - list(filter?)      — direct read of `.catalog/agents/`
 *   - analyze(id)        — surface missing/orphan references
 */
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentCreator = void 0;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const AtomicWriter_1 = require("./util/AtomicWriter");
const ID_PATTERN = /^[a-z0-9][a-z0-9._-]+$/i;
const VALID_STATUSES = new Set(['active', 'draft', 'archived']);
class AgentCreator {
    catalogRoot;
    skillsCatalogRoot;
    squadsCatalogRoot;
    constructor(opts = {}) {
        const cwd = process.cwd();
        this.catalogRoot = opts.catalogRoot || path.join(cwd, '.catalog', 'agents');
        this.skillsCatalogRoot = opts.skillsCatalogRoot || path.join(cwd, '.catalog', 'skills');
        this.squadsCatalogRoot = opts.squadsCatalogRoot || path.join(cwd, '.catalog', 'squads');
    }
    // ─────────────────────────────────────────────────────────
    // create
    // ─────────────────────────────────────────────────────────
    create(proposal) {
        if (!proposal.id || !ID_PATTERN.test(proposal.id)) {
            return { ok: false, error: 'invalid_agent_id', detail: 'must match /^[a-z0-9][a-z0-9._-]+$/i' };
        }
        if (!proposal.name?.trim()) {
            return { ok: false, error: 'missing_name' };
        }
        if (!proposal.role?.trim()) {
            return { ok: false, error: 'missing_role' };
        }
        if (proposal.status && !VALID_STATUSES.has(proposal.status)) {
            return { ok: false, error: 'invalid_status', detail: `must be one of: ${Array.from(VALID_STATUSES).join(', ')}` };
        }
        const agentDir = path.join(this.catalogRoot, proposal.id);
        if (fs.existsSync(agentDir)) {
            return { ok: false, error: 'agent_already_exists', detail: agentDir };
        }
        try {
            fs.mkdirSync(agentDir, { recursive: true });
            const content = this.renderAgentMd(proposal);
            const filePath = path.join(agentDir, 'AGENT.md');
            (0, AtomicWriter_1.writeStringAtomic)(filePath, content);
            return { ok: true, agentId: proposal.id, agentDir, filePath };
        }
        catch (err) {
            try {
                fs.rmSync(agentDir, { recursive: true, force: true });
            }
            catch { /* ignore */ }
            return { ok: false, error: 'render_failed', detail: err instanceof Error ? err.message : String(err) };
        }
    }
    // ─────────────────────────────────────────────────────────
    // validate
    // ─────────────────────────────────────────────────────────
    validate(agentId) {
        const errors = [];
        const warnings = [];
        const agentDir = path.join(this.catalogRoot, agentId);
        const agentFile = path.join(agentDir, 'AGENT.md');
        if (!fs.existsSync(agentDir)) {
            errors.push(`agent directory not found: ${agentDir}`);
            return { ok: false, agentId, errors, warnings };
        }
        if (!fs.existsSync(agentFile)) {
            errors.push('AGENT.md missing');
            return { ok: false, agentId, errors, warnings };
        }
        const content = fs.readFileSync(agentFile, 'utf-8');
        const fm = this.parseFrontmatter(content);
        if (!fm.id)
            errors.push('frontmatter missing id');
        if (!fm.name)
            errors.push('frontmatter missing name');
        if (!fm.role)
            errors.push('frontmatter missing role');
        if (fm.id && fm.id !== agentId) {
            errors.push(`frontmatter id '${fm.id}' does not match directory '${agentId}'`);
        }
        if (fm.status && !VALID_STATUSES.has(fm.status)) {
            errors.push(`invalid status '${fm.status}' (must be active|draft|archived)`);
        }
        // Cross-reference: parent squad
        if (fm.parentSquad) {
            const squadPath = path.join(this.squadsCatalogRoot, fm.parentSquad);
            if (!fs.existsSync(squadPath)) {
                warnings.push(`parent squad '${fm.parentSquad}' not found in catalog`);
            }
        }
        // Cross-reference: primary skills
        const skillRefs = this.parseListField(content, 'primarySkills');
        for (const skillId of skillRefs) {
            const skillPath = path.join(this.skillsCatalogRoot, skillId);
            if (!fs.existsSync(skillPath)) {
                warnings.push(`primary skill '${skillId}' not found in catalog`);
            }
        }
        return { ok: errors.length === 0, agentId, errors, warnings };
    }
    // ─────────────────────────────────────────────────────────
    // list
    // ─────────────────────────────────────────────────────────
    list(filter) {
        if (!fs.existsSync(this.catalogRoot))
            return [];
        const out = [];
        for (const entry of fs.readdirSync(this.catalogRoot)) {
            const agentFile = path.join(this.catalogRoot, entry, 'AGENT.md');
            if (!fs.existsSync(agentFile))
                continue;
            const fm = this.parseFrontmatter(fs.readFileSync(agentFile, 'utf-8'));
            const item = {
                id: fm.id || entry,
                name: fm.name || entry,
                role: fm.role || 'unknown',
                status: fm.status || 'unknown',
                parentSquad: fm.parentSquad || null,
            };
            if (filter?.status && item.status !== filter.status)
                continue;
            if (filter?.squad && item.parentSquad !== filter.squad)
                continue;
            out.push(item);
        }
        return out;
    }
    // ─────────────────────────────────────────────────────────
    // analyze
    // ─────────────────────────────────────────────────────────
    analyze(agentId) {
        const agentDir = path.join(this.catalogRoot, agentId);
        const agentFile = path.join(agentDir, 'AGENT.md');
        if (!fs.existsSync(agentFile))
            return null;
        const content = fs.readFileSync(agentFile, 'utf-8');
        const fm = this.parseFrontmatter(content);
        const hasPersona = /^## Persona\b/m.test(content);
        const commandLines = content.match(/^\s*-\s*name:\s*[\w-]+/gm) || [];
        const skillRefs = this.parseListField(content, 'primarySkills');
        const orphanSkills = [];
        for (const skillId of skillRefs) {
            if (!fs.existsSync(path.join(this.skillsCatalogRoot, skillId))) {
                orphanSkills.push(skillId);
            }
        }
        const squadReference = fm.parentSquad || null;
        const squadExists = squadReference
            ? fs.existsSync(path.join(this.squadsCatalogRoot, squadReference))
            : null;
        const suggestions = [];
        if (!hasPersona)
            suggestions.push('add a Persona section to clarify behavior under load');
        if (commandLines.length === 0)
            suggestions.push('declare at least one command so operators know how to invoke');
        if (orphanSkills.length > 0)
            suggestions.push(`${orphanSkills.length} skill reference(s) not in catalog`);
        if (squadReference && squadExists === false)
            suggestions.push(`parent squad '${squadReference}' not in catalog`);
        return {
            agentId,
            hasPersona,
            commandCount: commandLines.length,
            skillReferences: skillRefs.length,
            orphanSkills,
            squadReference,
            squadExists,
            suggestions,
        };
    }
    // ─────────────────────────────────────────────────────────
    // render — AGENT.md body
    // ─────────────────────────────────────────────────────────
    renderAgentMd(p) {
        const status = p.status || 'draft';
        const version = p.version || '0.1.0';
        const source = p.source || 'aiobuilder';
        const createdAt = new Date().toISOString();
        const frontmatterLines = [
            '---',
            `id: ${p.id}`,
            `name: ${p.name}`,
            `role: ${p.role}`,
        ];
        if (p.domain)
            frontmatterLines.push(`domain: ${p.domain}`);
        if (p.parentSquad)
            frontmatterLines.push(`parentSquad: ${p.parentSquad}`);
        if (p.expertise?.length) {
            frontmatterLines.push('expertise:');
            for (const tag of p.expertise)
                frontmatterLines.push(`  - ${tag}`);
        }
        if (p.primarySkills?.length) {
            frontmatterLines.push('primarySkills:');
            for (const skillId of p.primarySkills)
                frontmatterLines.push(`  - ${skillId}`);
        }
        frontmatterLines.push(`status: ${status}`);
        frontmatterLines.push(`version: ${version}`);
        frontmatterLines.push(`source: ${source}`);
        frontmatterLines.push(`createdAt: ${createdAt}`);
        frontmatterLines.push('---');
        const commands = p.commands?.length
            ? p.commands
            : [{ name: 'help', description: 'Show available commands and capabilities' }];
        const dependencies = p.dependencies?.length
            ? p.dependencies
            : ['(none declared yet — list skills, tools, or services this agent relies on)'];
        const guardrails = p.guardrails?.length
            ? p.guardrails
            : [
                'Respect toolset boundaries from parent squad policies',
                'Emit audit events for state-changing actions',
                'Defer to human approval for actions outside autonomy budget',
            ];
        const sections = [];
        sections.push(`# ${p.name}`);
        sections.push('');
        sections.push(`> Role: ${p.role}${p.domain ? `  ·  Domain: ${p.domain}` : ''}`);
        sections.push('');
        sections.push('## Goal');
        sections.push('');
        sections.push(p.whenToUse || `Define and execute work in the ${p.role} role.`);
        sections.push('');
        sections.push('## Commands');
        sections.push('');
        sections.push('```yaml');
        sections.push('commands:');
        for (const c of commands) {
            sections.push(`  - name: ${c.name}`);
            sections.push(`    description: ${JSON.stringify(c.description)}`);
        }
        sections.push('```');
        sections.push('');
        sections.push('## Dependencies');
        sections.push('');
        for (const d of dependencies)
            sections.push(`- ${d}`);
        sections.push('');
        sections.push('## Persona');
        sections.push('');
        if (p.identity || p.style || p.focus || p.persona) {
            if (p.identity)
                sections.push(`- **Identity:** ${p.identity}`);
            if (p.style)
                sections.push(`- **Style:** ${p.style}`);
            if (p.focus)
                sections.push(`- **Focus:** ${p.focus}`);
            if (p.persona) {
                sections.push('');
                sections.push(p.persona);
            }
        }
        else {
            sections.push(`Embodies the ${p.role} discipline with rigor and clarity. Speaks in concrete steps,`);
            sections.push('not abstractions. Defers to operator judgment on irreversible decisions.');
        }
        sections.push('');
        sections.push('## Guardrails');
        sections.push('');
        for (const g of guardrails)
            sections.push(`- ${g}`);
        sections.push('');
        sections.push('---');
        sections.push('*Generated by OpenLife AgentCreator.*');
        return frontmatterLines.join('\n') + '\n\n' + sections.join('\n') + '\n';
    }
    // ─────────────────────────────────────────────────────────
    // parsing helpers
    // ─────────────────────────────────────────────────────────
    parseFrontmatter(content) {
        const match = content.match(/^---\n([\s\S]*?)\n---/);
        if (!match)
            return {};
        const out = {};
        for (const line of match[1].split('\n')) {
            const m = line.match(/^([a-zA-Z][a-zA-Z0-9_]*):\s*(.+)$/);
            if (m)
                out[m[1]] = m[2].trim();
        }
        return out;
    }
    parseListField(content, field) {
        const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
        if (!fmMatch)
            return [];
        const lines = fmMatch[1].split('\n');
        const out = [];
        let capturing = false;
        const fieldRe = new RegExp(`^${field}:\\s*$`);
        for (const line of lines) {
            if (fieldRe.test(line)) {
                capturing = true;
                continue;
            }
            if (capturing) {
                const m = line.match(/^\s+-\s+(.+)$/);
                if (m) {
                    out.push(m[1].trim());
                    continue;
                }
                // any non-list line ends the capture
                if (/^[a-zA-Z]/.test(line))
                    capturing = false;
            }
        }
        return out;
    }
}
exports.AgentCreator = AgentCreator;
