/**
 * A preset's display metadata: the name and description a picker shows.
 *
 * It lives in its own file because the composition is a top-level list of
 * plugin rows — YAML cannot carry sibling keys beside it, and faking a
 * metadata row would hand the Loader something to load. Keeping it separate
 * also keeps the composition exactly what its name says: a Cordis file the
 * loader owns and the cordis preset can author.
 *
 * The file carries display text ONLY. `id` is the directory name and `trust`
 * comes from the root a preset was discovered under, so neither is writable
 * here — otherwise a locally authored preset could claim to be a shipped one.
 *
 * Every read failure degrades to no metadata. A preset whose display text is
 * missing, malformed, or unreadable still mounts: presentation is not a
 * capability, and a broken name must never become an agent that cannot start.
 * @module @deepseek-ai/dsh-agent-presets/metadata
 */
import { readFile } from 'node:fs/promises';
import { join } from 'node:path';
import yaml from 'js-yaml';
/** The optional display-metadata file beside a preset's composition. */
export const METADATA_FILE = 'preset.yml';
/** A non-empty trimmed string, or undefined for anything else. */
function text(value) {
    if (typeof value !== 'string')
        return undefined;
    const trimmed = value.trim();
    return trimmed === '' ? undefined : trimmed;
}
/**
 * Read one preset directory's display metadata.
 *
 * Absent, unparsable, and wrongly-shaped files are all the same answer —
 * empty metadata — because the caller renders a picker, not a diagnostic.
 * @param directory - the preset directory.
 * @returns the display text the preset published, possibly empty.
 */
export async function readPresetMetadata(directory) {
    let raw;
    try {
        raw = await readFile(join(directory, METADATA_FILE), 'utf8');
    }
    catch {
        // Absent is the common case: metadata is optional and most presets,
        // including every one authored by duplicating another, carry none.
        return {};
    }
    let parsed;
    try {
        parsed = yaml.load(raw);
    }
    catch {
        // Malformed display text is not worth failing discovery over; the picker
        // falls back to the id, and the composition still mounts.
        return {};
    }
    if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed))
        return {};
    const record = parsed;
    const name = text(record.name);
    const description = text(record.description);
    const order = typeof record.order === 'number' && Number.isFinite(record.order)
        ? record.order
        : undefined;
    return {
        ...name === undefined ? {} : { name },
        ...description === undefined ? {} : { description },
        ...order === undefined ? {} : { order },
    };
}
/**
 * Render display metadata as the file's contents.
 *
 * Absent fields are omitted rather than written empty, so a preset with no
 * description does not ship a key that reads as an intentional blank.
 * @param metadata - the display text to store.
 * @returns the YAML document, or undefined when there is nothing to store.
 */
export function renderPresetMetadata(metadata) {
    const name = text(metadata.name);
    const description = text(metadata.description);
    const { order } = metadata;
    if (name === undefined && description === undefined && order === undefined)
        return undefined;
    return yaml.dump({
        ...name === undefined ? {} : { name },
        ...description === undefined ? {} : { description },
        ...order === undefined ? {} : { order },
    }, { lineWidth: -1 });
}
//# sourceMappingURL=metadata.js.map