// engine/skill-discovery/source-adapter.ts — SPEC-606: Source adapters for universal skill discovery
// Each adapter wraps an existing skill source and normalises results to UniversalSkillResult.
import { searchSkillsSh } from '../skill-registry/skillssh-adapter.js';
import { searchAnthropicSkills } from '../skill-registry/anthropic-adapter.js';
import { searchBuiltInSkills } from '../skill-registry/builtin-catalog-adapter.js';
import { searchAgentSkills } from '../skill-registry/agentskill-adapter.js';
const ADAPTER_TIMEOUT_MS = 5_000;
const DEFAULT_LIMIT = 10;
const GITHUB_SEARCH_URL = 'https://api.github.com/search/repositories';
const USER_AGENT = 'Planu/1.0';
/** Wrap a promise with a timeout; on timeout or error resolves to empty array. */
async function withTimeout(promise, ms) {
    const timeout = new Promise((resolve) => {
        setTimeout(() => {
            resolve([]);
        }, ms);
    });
    try {
        return await Promise.race([promise, timeout]);
    }
    catch {
        return [];
    }
}
/** Map the existing SkillSearchResult source string to SkillDiscoverySource. */
function normaliseSource(raw) {
    const map = {
        anthropic: 'anthropic',
        skillssh: 'skills.sh',
        agentskill: 'skills.sh',
        builtin: 'builtin',
        github: 'github',
        npm: 'npm',
    };
    return map[raw] ?? 'skills.sh';
}
// ---------------------------------------------------------------------------
// SkillsShAdapter
// ---------------------------------------------------------------------------
/** Adapter that fetches skills from skills.sh API. */
export class SkillsShAdapter {
    name = 'skills.sh';
    async search(query, options) {
        const limit = options?.limit ?? DEFAULT_LIMIT;
        const results = await withTimeout(searchSkillsSh(query, options?.stack, limit), ADAPTER_TIMEOUT_MS);
        return results.map((r) => ({
            name: r.name,
            source: 'skills.sh',
            description: r.description,
            stacks: r.tags ?? [],
            downloadCount: r.installs,
            url: r.url,
            format: 'agent-skills-spec',
        }));
    }
}
// ---------------------------------------------------------------------------
// AnthropicAdapter
// ---------------------------------------------------------------------------
/** Adapter that fetches skills from the Anthropic GitHub skills repository. */
export class AnthropicAdapter {
    name = 'anthropic';
    async search(query, options) {
        const limit = options?.limit ?? DEFAULT_LIMIT;
        const results = await withTimeout(searchAnthropicSkills(query, limit), ADAPTER_TIMEOUT_MS);
        return results.map((r) => ({
            name: r.name,
            source: 'anthropic',
            description: r.description,
            stacks: r.tags ?? [],
            downloadCount: r.installs,
            url: r.url,
            format: 'agent-skills-spec',
        }));
    }
}
// ---------------------------------------------------------------------------
// AgentSkillsShAdapter (maps to skills.sh discovery source)
// ---------------------------------------------------------------------------
/** Adapter that fetches skills from agentskill.sh API. */
export class AgentSkillsShAdapter {
    name = 'skills.sh';
    async search(query, options) {
        const limit = options?.limit ?? DEFAULT_LIMIT;
        const results = await withTimeout(searchAgentSkills(query, options?.stack, limit), ADAPTER_TIMEOUT_MS);
        return results.map((r) => ({
            name: r.name,
            source: normaliseSource(r.source),
            description: r.description,
            stacks: r.tags ?? [],
            downloadCount: r.installs,
            url: r.url,
            format: 'agent-skills-spec',
        }));
    }
}
// ---------------------------------------------------------------------------
// GithubAdapter
// ---------------------------------------------------------------------------
/** Adapter that searches GitHub for repos/files containing SKILL.md. */
export class GithubAdapter {
    name = 'github';
    async search(query, options) {
        const limit = options?.limit ?? DEFAULT_LIMIT;
        const searchQuery = buildGithubQuery(query, options?.stack);
        const results = await withTimeout(fetchGithubSkills(searchQuery, limit), ADAPTER_TIMEOUT_MS);
        return results;
    }
}
function buildGithubQuery(query, stack) {
    const parts = [`${query} SKILL.md in:path`];
    if (stack) {
        parts.push(`topic:${stack}`);
    }
    return parts.join(' ');
}
async function fetchGithubSkills(query, limit) {
    try {
        const url = new URL(GITHUB_SEARCH_URL);
        url.searchParams.set('q', query);
        url.searchParams.set('per_page', String(Math.min(limit, 30)));
        url.searchParams.set('sort', 'stars');
        const response = await globalThis.fetch(url.toString(), {
            headers: {
                'User-Agent': USER_AGENT,
                Accept: 'application/vnd.github.v3+json',
            },
            signal: AbortSignal.timeout(ADAPTER_TIMEOUT_MS),
        });
        if (!response.ok) {
            return [];
        }
        const data = (await response.json());
        const items = data.items ?? [];
        return items.slice(0, limit).map((item) => ({
            name: item.name ?? item.full_name ?? 'unknown',
            source: 'github',
            description: item.description ?? `GitHub skill: ${item.full_name ?? ''}`,
            stacks: item.topics ?? [],
            downloadCount: item.stargazers_count,
            url: item.html_url,
            format: 'markdown',
        }));
    }
    catch {
        return [];
    }
}
// ---------------------------------------------------------------------------
// BuiltinAdapter
// ---------------------------------------------------------------------------
/** Adapter that searches the local built-in skill catalog. */
export class BuiltinAdapter {
    name = 'builtin';
    search(query, options) {
        const limit = options?.limit ?? DEFAULT_LIMIT;
        const results = searchBuiltInSkills(query);
        const filtered = options?.stack
            ? results.filter((r) => r.tags?.some((t) => t.toLowerCase().includes((options.stack ?? '').toLowerCase())))
            : results;
        return Promise.resolve(filtered.slice(0, limit).map((r) => ({
            name: r.name,
            source: 'builtin',
            description: r.description,
            stacks: r.tags ?? [],
            url: r.url,
            format: 'agent-skills-spec',
        })));
    }
}
/** Create all default adapters. */
export function createDefaultAdapters() {
    return [new BuiltinAdapter(), new AnthropicAdapter(), new SkillsShAdapter(), new GithubAdapter()];
}
//# sourceMappingURL=source-adapter.js.map