// engine/skill-discovery/ranker.ts — SPEC-606: Rank universal skill discovery results
// Combines stack compatibility, download count, source reliability, and recency
// into a composite score used to surface the most relevant skills first.
// Weight breakdown (must sum to 1.0)
const WEIGHT_STACK = 0.4;
const WEIGHT_DOWNLOADS = 0.2;
const WEIGHT_SOURCE = 0.2;
const WEIGHT_RECENCY = 0.2;
// Source reliability order: builtin > anthropic > skills.sh > github > npm
const SOURCE_SCORES = {
    builtin: 1.0,
    anthropic: 0.85,
    'skills.sh': 0.65,
    github: 0.45,
    npm: 0.3,
};
/**
 * Compute the stack compatibility sub-score.
 * Returns 1.0 if any of the skill's stacks matches the requested stack,
 * 0.5 if no stack filter was requested (neutral), 0.0 if it explicitly mismatches.
 */
function stackScore(skill, targetStack) {
    if (!targetStack) {
        return 0.5; // neutral — no preference expressed
    }
    const target = targetStack.toLowerCase();
    const hasMatch = skill.stacks.some((s) => s.toLowerCase().includes(target));
    return hasMatch ? 1.0 : 0.0;
}
/**
 * Compute the download count sub-score (log-normalised, capped at 1M).
 */
function downloadScore(downloads) {
    if (downloads === undefined || downloads <= 0) {
        return 0;
    }
    // log10(1_000_000) = 6 — normalise to 0-1
    return Math.min(Math.log10(downloads + 1) / 6, 1);
}
/**
 * Compute the source reliability sub-score.
 */
function sourceScore(source) {
    return SOURCE_SCORES[source];
}
/**
 * Compute the recency sub-score (placeholder — 0.5 neutral until sources supply dates).
 */
function recencyScore() {
    return 0.5;
}
/**
 * Compute composite rank score for a single skill result.
 */
export function computeRankScore(skill, targetStack) {
    const score = WEIGHT_STACK * stackScore(skill, targetStack) +
        WEIGHT_DOWNLOADS * downloadScore(skill.downloadCount) +
        WEIGHT_SOURCE * sourceScore(skill.source) +
        WEIGHT_RECENCY * recencyScore();
    return Math.min(score, 1);
}
/**
 * Deduplicate results by name, keeping the entry with the highest rank score.
 */
function deduplicateByName(results) {
    const best = new Map();
    for (const r of results) {
        const existing = best.get(r.name);
        if (!existing || r.rankScore > existing.rankScore) {
            best.set(r.name, r);
        }
    }
    return Array.from(best.values());
}
/**
 * Rank and deduplicate an array of universal skill results.
 *
 * Steps:
 *  1. Compute rank score for each result.
 *  2. Deduplicate by name (keep highest score).
 *  3. Sort descending by rank score.
 */
export function rankResults(results, targetStack) {
    const ranked = results.map((r) => ({
        ...r,
        rankScore: computeRankScore(r, targetStack),
    }));
    const deduped = deduplicateByName(ranked);
    deduped.sort((a, b) => b.rankScore - a.rankScore);
    return deduped;
}
//# sourceMappingURL=ranker.js.map