// engine/marketplace-fetcher/index.ts — SPEC-681: Orchestrate skill fetching from all sources
// Fetches skills from Superpowers, Anthropic, local ~/.claude/skills/, and community registry.
// Per-source failures are isolated via Promise.allSettled — never throws.
import { writeSkillRegistryEntry } from '../../storage/skill-registry-storage.js';
import { DEFAULT_CACHE_TTL_MS } from './cache.js';
import { appendToEvolutionLog } from './evolution-log.js';
import { fetchLocalSkills } from './local-source.js';
import { fetchAnthropicSkills } from './anthropic-source.js';
import { fetchSuperpowersSkills } from './superpowers-source.js';
import { fetchCommunitySkills } from './community-source.js';
// ---------------------------------------------------------------------------
// Constants (exported — single source of truth, no per-call literals)
// ---------------------------------------------------------------------------
/** Default per-request timeout in ms. Matches FETCH_TIMEOUT_MS in registry-fetcher.ts. */
export const FETCH_TIMEOUT_MS = 5000;
/** Default cache TTL in ms (24h). Re-exported from cache.ts for consumers. */
export { DEFAULT_CACHE_TTL_MS };
/** All supported sources in priority order. */
const ALL_SOURCES = ['local', 'superpowers', 'anthropic', 'community'];
// ---------------------------------------------------------------------------
// Source dispatcher
// ---------------------------------------------------------------------------
async function fetchFromSource(source, projectPath, options) {
    switch (source) {
        case 'local':
            // Local source ignores skillNames/timeout — reads entire ~/.claude/skills/ dir
            return { fetched: await fetchLocalSkills(projectPath), errors: [] };
        case 'anthropic':
            return fetchAnthropicSkills(projectPath, options);
        case 'superpowers':
            return fetchSuperpowersSkills(projectPath, options);
        case 'community':
            return fetchCommunitySkills(projectPath, options);
        default:
            return { fetched: [], errors: [] };
    }
}
// ---------------------------------------------------------------------------
// Registry update helpers (SPEC-680 integration)
// ---------------------------------------------------------------------------
async function recordInRegistry(projectPath, skill, errors) {
    try {
        await writeSkillRegistryEntry(projectPath, {
            skillName: skill.skillName,
            source: skill.source,
            version: null,
            adaptedAt: new Date().toISOString(),
            ttlDays: 30,
        });
    }
    catch (err) {
        // Scenario 7: registry write fails — skill file already written, just log
        const message = err instanceof Error ? err.message : String(err);
        errors.push({
            source: skill.source,
            skillName: skill.skillName,
            message: `registry-write-failed: ${message}`,
            kind: 'registry-write-failed',
        });
        await appendToEvolutionLog(projectPath, `[marketplace-fetcher] registry-write-failed "${skill.skillName}" (${skill.source}): ${message}`);
    }
}
// ---------------------------------------------------------------------------
// Deduplication: local-wins rule (Scenario 5)
// ---------------------------------------------------------------------------
function deduplicateWithLocalWins(all) {
    const map = new Map();
    for (const skill of all) {
        const existing = map.get(skill.skillName);
        if (!existing || skill.source === 'local') {
            map.set(skill.skillName, skill);
        }
    }
    return Array.from(map.values());
}
// ---------------------------------------------------------------------------
// Main orchestrator
// ---------------------------------------------------------------------------
/**
 * Fetch skills from all (or selected) sources in parallel.
 * Per-source failures are isolated — never throws.
 */
export async function fetchSkillsFromAllSources(projectPath, options = {}) {
    const sources = options.sources ?? ALL_SOURCES;
    if (sources.length === 0) {
        return { fetched: [], errors: [] };
    }
    const resolvedOptions = {
        timeoutMs: FETCH_TIMEOUT_MS,
        cacheTtlMs: DEFAULT_CACHE_TTL_MS,
        ...options,
    };
    // Fetch all sources concurrently — per-source failures are isolated (Scenario 1, 3)
    const settlements = await Promise.allSettled(sources.map((source) => fetchFromSource(source, projectPath, resolvedOptions)));
    const allFetched = [];
    const allErrors = [];
    for (const [i, settlement] of settlements.entries()) {
        const source = sources[i] ?? 'community';
        if (settlement.status === 'fulfilled') {
            const fulfilled = settlement;
            allFetched.push(...fulfilled.value.fetched);
            allErrors.push(...fulfilled.value.errors);
            // Log network/timeout errors to evolution-log (Scenario 3 — per-skill and source-wide)
            for (const fetchErr of fulfilled.value.errors) {
                if (fetchErr.kind === 'network' ||
                    fetchErr.kind === 'timeout' ||
                    fetchErr.kind === 'unknown') {
                    await appendToEvolutionLog(projectPath, `[marketplace-fetcher] source="${fetchErr.source}" skill="${fetchErr.skillName ?? 'unknown'}" kind="${fetchErr.kind}": ${fetchErr.message}`);
                }
            }
        }
        else {
            // Source-wide failure (Scenario 3)
            const rejected = settlement;
            const message = rejected.reason instanceof Error ? rejected.reason.message : String(rejected.reason);
            const kind = classifySourceError(message);
            allErrors.push({ source, skillName: null, message, kind });
            await appendToEvolutionLog(projectPath, `[marketplace-fetcher] source-failure source="${source}" kind="${kind}": ${message}`);
        }
    }
    // Apply local-wins deduplication (Scenario 5)
    const deduped = deduplicateWithLocalWins(allFetched);
    // Update registry for each successfully fetched skill (SPEC-680 integration)
    for (const skill of deduped) {
        await recordInRegistry(projectPath, skill, allErrors);
    }
    return { fetched: deduped, errors: allErrors };
}
// ---------------------------------------------------------------------------
// Error classification
// ---------------------------------------------------------------------------
function classifySourceError(message) {
    const lower = message.toLowerCase();
    if (lower.includes('timeout') || lower.includes('abort')) {
        return 'timeout';
    }
    if (lower.includes('not-found') || lower.includes('404')) {
        return 'not-found';
    }
    if (lower.includes('malformed')) {
        return 'malformed';
    }
    if (lower.includes('network') || lower.includes('econnrefused') || lower.includes('fetch')) {
        return 'network';
    }
    if (lower.includes('registry-write-failed')) {
        return 'registry-write-failed';
    }
    return 'unknown';
}
//# sourceMappingURL=index.js.map