/**
 * Directory-derived view of the skill-proposals folder. The catalog never
 * mutates the filesystem; whether a proposal is a "create" or "update" is
 * inferred by checking for a same-named live skill, not by per-proposal
 * metadata. Used by status, prompt, and reviewer code paths.
 */
import { lstatSync, readdirSync } from 'node:fs';
import { join } from 'node:path';
import { initializeSkillProposalRoots, liveSkillDir, proposalDir } from './proposal-fs.js';
const SKILL_NAME_PATTERN = /^[a-z0-9](?:[a-z0-9-]{0,62}[a-z0-9])?$/;
export const PROPOSAL_KIND = {
    CREATE: 'create',
    UPDATE: 'update'
};
/** Passive discovery for status/prompt/reviewer; it must not mutate proposal folders. */
export function listSkillProposals(roots) {
    initializeSkillProposalRoots(roots);
    return readdirSync(roots.proposalsRoot, { withFileTypes: true })
        .filter((entry) => isValidProposalDirectoryEntry(roots, entry))
        .map((entry) => {
        const targetLiveSkillDir = liveSkillDir(roots, entry.name);
        const kind = pathExistsForLstat(targetLiveSkillDir)
            ? PROPOSAL_KIND.UPDATE
            : PROPOSAL_KIND.CREATE;
        return {
            name: entry.name,
            proposalDir: proposalDir(roots, entry.name),
            liveSkillDir: targetLiveSkillDir,
            kind
        };
    })
        .sort((a, b) => a.name.localeCompare(b.name));
}
/** True if lstat succeeds on `path` (any filesystem entry, including symlinks). */
function pathExistsForLstat(path) {
    try {
        lstatSync(path);
        return true;
    }
    catch {
        return false;
    }
}
/** Return the number of valid proposal directories on disk. */
export function countSkillProposals(roots) {
    initializeSkillProposalRoots(roots);
    return readdirSync(roots.proposalsRoot, { withFileTypes: true }).filter((entry) => isValidProposalDirectoryEntry(roots, entry)).length;
}
/** Like countSkillProposals but returns an error message instead of throwing. */
export function tryCountSkillProposals(roots) {
    try {
        return { count: countSkillProposals(roots) };
    }
    catch (error) {
        return { count: 0, message: error instanceof Error ? error.message : String(error) };
    }
}
/** True if a dirent is a well-named proposal directory; throws on invalid directory names or stray files. */
function isValidProposalDirectoryEntry(roots, entry) {
    if (entry.name.startsWith('.'))
        return false;
    if (!SKILL_NAME_PATTERN.test(entry.name)) {
        if (entry.isDirectory()) {
            throw new Error(`Skill proposal folder name is invalid: ${join(roots.proposalsRoot, entry.name)}`);
        }
        return false;
    }
    if (entry.isDirectory())
        return true;
    throw new Error(`Skill proposal entry must be a directory: ${proposalDir(roots, entry.name)}`);
}
//# sourceMappingURL=catalog.js.map