/**
 * Reviewer-side actions on skill proposals: replace the live skill, install as
 * a new suffixed version, or reject (delete the proposal). These are explicit
 * user-initiated operations from the /proposal command and live outside the
 * forked-agent loop entirely.
 */
import { existsSync, readdirSync, readFileSync } from 'node:fs';
import { mkdir, readFile, rename, rm, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { PROPOSAL_KIND } from './catalog.js';
import { initializeSkillProposalRoots, liveSkillDir, proposalDir, validateSkillName } from './proposal-fs.js';
/** Read a proposal's name, kind (create/update), and rationale into a single review-time snapshot. */
export function readSkillProposalReviewSnapshot(roots, name) {
    initializeSkillProposalRoots(roots);
    const targetProposalDir = proposalDir(roots, name);
    if (!existsSync(targetProposalDir)) {
        throw new Error(`Proposal does not exist: ${name}`);
    }
    const liveDir = liveSkillDir(roots, name);
    const kind = existsSync(liveDir) ? PROPOSAL_KIND.UPDATE : PROPOSAL_KIND.CREATE;
    return { name, kind, rationale: readProposalRationale(roots, name) };
}
/** Read RATIONALE.md from the proposal folder, or return an empty string if absent or unreadable. */
function readProposalRationale(roots, name) {
    try {
        return readFileSync(join(proposalDir(roots, name), 'RATIONALE.md'), 'utf-8').trim();
    }
    catch {
        return '';
    }
}
/** Reviewer action: delete the proposal folder. Live skills are untouched. */
export async function rejectSkillProposal(roots, name) {
    initializeSkillProposalRoots(roots);
    const targetProposalDir = proposalDir(roots, name);
    if (!existsSync(targetProposalDir)) {
        throw new Error(`Proposal does not exist: ${name}`);
    }
    await rm(targetProposalDir, { recursive: true, force: true });
}
/**
 * Reviewer action: replace the unsuffixed live skill with the proposal.
 * If a live skill exists, it is moved to ~/.shortcut/agent/skill-backups/<name>-<timestamp>/
 * before the proposal moves into place. The user can manually restore it from there.
 */
export async function replaceSkillWithProposal(roots, name) {
    initializeSkillProposalRoots(roots);
    const targetProposalDir = proposalDir(roots, name);
    const targetLiveSkillDir = liveSkillDir(roots, name);
    if (!existsSync(targetProposalDir)) {
        throw new Error(`Proposal does not exist: ${name}`);
    }
    assertSkillMdPresent(targetProposalDir, name);
    if (existsSync(targetLiveSkillDir)) {
        await mkdir(roots.skillBackupsRoot, { recursive: true });
        const backupDir = join(roots.skillBackupsRoot, `${name}-${new Date().toISOString().replace(/[:.]/g, '-')}`);
        await rename(targetLiveSkillDir, backupDir);
    }
    await rename(targetProposalDir, targetLiveSkillDir);
}
/** Reviewer action: install the proposal as the next suffixed live skill version. */
export async function saveProposalAsVersion(roots, name) {
    initializeSkillProposalRoots(roots);
    const targetProposalDir = proposalDir(roots, name);
    if (!existsSync(targetProposalDir)) {
        throw new Error(`Proposal does not exist: ${name}`);
    }
    assertSkillMdPresent(targetProposalDir, name);
    const versionName = nextVersionName(roots, name);
    const versionDir = liveSkillDir(roots, versionName);
    await rewriteSkillName(join(targetProposalDir, 'SKILL.md'), versionName);
    await rename(targetProposalDir, versionDir);
    return versionName;
}
/** Return the next available `<name>_<N>` suffix not already present in the live skills root. */
export function nextVersionName(roots, name) {
    validateSkillName(name);
    const prefix = `${name}_`;
    let highest = 1;
    if (existsSync(roots.liveSkillsRoot)) {
        for (const entry of readdirSync(roots.liveSkillsRoot)) {
            if (!entry.startsWith(prefix))
                continue;
            const suffix = Number(entry.slice(prefix.length));
            if (Number.isInteger(suffix) && suffix > highest) {
                highest = suffix;
            }
        }
    }
    return `${name}_${highest + 1}`;
}
/** Rewrite the `name:` field in a proposal SKILL.md frontmatter to match a new (versioned) folder name. */
async function rewriteSkillName(skillPath, skillName) {
    // Save-as-version installs to <name>_<N>; the loader requires the SKILL.md
    // frontmatter `name` to match the folder.
    const content = await readFile(skillPath, 'utf-8');
    const match = /^(---\r?\n)([\s\S]*?)(\r?\n---)(\r?\n|$)/.exec(content);
    if (!match) {
        throw new Error(`Proposal SKILL.md must include frontmatter before saving "${skillName}"`);
    }
    const [, openingFence, frontmatterBody, closingFence, fenceTerminator] = match;
    const body = content.slice(match[0].length);
    const nextFrontmatterBody = /^name:\s*.*$/m.test(frontmatterBody)
        ? frontmatterBody.replace(/^name:\s*.*$/m, `name: ${skillName}`)
        : `name: ${skillName}\n${frontmatterBody}`;
    await writeFile(skillPath, `${openingFence}${nextFrontmatterBody}${closingFence}${fenceTerminator}${body}`, 'utf-8');
}
/** Throw if the proposal folder is missing the required SKILL.md file. */
function assertSkillMdPresent(dir, name) {
    if (!existsSync(join(dir, 'SKILL.md'))) {
        throw new Error(`Proposal must include SKILL.md for "${name}"`);
    }
}
//# sourceMappingURL=review.js.map