import { promises as fs } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { normalizeAgentUrlKey } from "@rudderhq/shared";
import { unprocessable } from "../../errors.js";
import { PROJECT_ROOT_SKILL_SUBDIRECTORIES, PROJECT_SCAN_DIRECTORY_ROOTS, asString, classifyInventoryKind, deriveCanonicalSkillKey, deriveTrustLevel, isPlainRecord, normalizeGitHubSkillDirectory, normalizePackageFileMap, normalizePortablePath, normalizeSkillDescription, normalizeSkillSlug, readCanonicalSkillKey, statPath, } from "./organization-skills.catalog.js";
export function prepareYamlLines(raw) {
    return raw
        .split("\n")
        .map((line) => ({
        indent: line.match(/^ */)?.[0].length ?? 0,
        content: line.trim(),
    }))
        .filter((line) => line.content.length > 0 && !line.content.startsWith("#"));
}
export function parseYamlScalar(rawValue) {
    const trimmed = rawValue.trim();
    if (trimmed === "")
        return "";
    if (trimmed === "null" || trimmed === "~")
        return null;
    if (trimmed === "true")
        return true;
    if (trimmed === "false")
        return false;
    if (trimmed === "[]")
        return [];
    if (trimmed === "{}")
        return {};
    if (/^-?\d+(\.\d+)?$/.test(trimmed))
        return Number(trimmed);
    if (trimmed.startsWith("\"") || trimmed.startsWith("[") || trimmed.startsWith("{")) {
        try {
            return JSON.parse(trimmed);
        }
        catch {
            return trimmed;
        }
    }
    return trimmed;
}
export function parseYamlBlockScalar(lines, startIndex, indentLevel, style) {
    const parts = [];
    let index = startIndex;
    while (index < lines.length) {
        const line = lines[index];
        if (line.indent < indentLevel)
            break;
        parts.push(line.content);
        index += 1;
    }
    if (style === ">") {
        return { value: parts.join(" "), nextIndex: index };
    }
    return { value: parts.join("\n"), nextIndex: index };
}
export function parseYamlBlock(lines, startIndex, indentLevel) {
    let index = startIndex;
    while (index < lines.length && lines[index].content.length === 0)
        index += 1;
    if (index >= lines.length || lines[index].indent < indentLevel) {
        return { value: {}, nextIndex: index };
    }
    const isArray = lines[index].indent === indentLevel && lines[index].content.startsWith("-");
    if (isArray) {
        const values = [];
        while (index < lines.length) {
            const line = lines[index];
            if (line.indent < indentLevel)
                break;
            if (line.indent !== indentLevel || !line.content.startsWith("-"))
                break;
            const remainder = line.content.slice(1).trim();
            index += 1;
            if (!remainder) {
                const nested = parseYamlBlock(lines, index, indentLevel + 2);
                values.push(nested.value);
                index = nested.nextIndex;
                continue;
            }
            const inlineObjectSeparator = remainder.indexOf(":");
            if (inlineObjectSeparator > 0 &&
                !remainder.startsWith("\"") &&
                !remainder.startsWith("{") &&
                !remainder.startsWith("[")) {
                const key = remainder.slice(0, inlineObjectSeparator).trim();
                const rawValue = remainder.slice(inlineObjectSeparator + 1).trim();
                const nextObject = {
                    [key]: parseYamlScalar(rawValue),
                };
                if (index < lines.length && lines[index].indent > indentLevel) {
                    const nested = parseYamlBlock(lines, index, indentLevel + 2);
                    if (isPlainRecord(nested.value)) {
                        Object.assign(nextObject, nested.value);
                    }
                    index = nested.nextIndex;
                }
                values.push(nextObject);
                continue;
            }
            values.push(parseYamlScalar(remainder));
        }
        return { value: values, nextIndex: index };
    }
    const record = {};
    while (index < lines.length) {
        const line = lines[index];
        if (line.indent < indentLevel)
            break;
        if (line.indent !== indentLevel) {
            index += 1;
            continue;
        }
        const separatorIndex = line.content.indexOf(":");
        if (separatorIndex <= 0) {
            index += 1;
            continue;
        }
        const key = line.content.slice(0, separatorIndex).trim();
        const remainder = line.content.slice(separatorIndex + 1).trim();
        index += 1;
        if (/^[>|][+-]?$/.test(remainder)) {
            const blockScalar = parseYamlBlockScalar(lines, index, indentLevel + 2, remainder.startsWith(">") ? ">" : "|");
            record[key] = blockScalar.value;
            index = blockScalar.nextIndex;
            continue;
        }
        if (!remainder) {
            const nested = parseYamlBlock(lines, index, indentLevel + 2);
            record[key] = nested.value;
            index = nested.nextIndex;
            continue;
        }
        record[key] = parseYamlScalar(remainder);
    }
    return { value: record, nextIndex: index };
}
export function parseYamlFrontmatter(raw) {
    const prepared = prepareYamlLines(raw);
    if (prepared.length === 0)
        return {};
    const parsed = parseYamlBlock(prepared, 0, prepared[0].indent);
    return isPlainRecord(parsed.value) ? parsed.value : {};
}
export function parseFrontmatterMarkdown(raw) {
    const normalized = raw.replace(/\r\n/g, "\n");
    if (!normalized.startsWith("---\n")) {
        return { frontmatter: {}, body: normalized.trim() };
    }
    const closing = normalized.indexOf("\n---\n", 4);
    if (closing < 0) {
        return { frontmatter: {}, body: normalized.trim() };
    }
    const frontmatterRaw = normalized.slice(4, closing).trim();
    const body = normalized.slice(closing + 5).trim();
    return {
        frontmatter: parseYamlFrontmatter(frontmatterRaw),
        body,
    };
}
export async function fetchText(url) {
    const response = await fetch(url);
    if (!response.ok) {
        throw unprocessable(`Failed to fetch ${url}: ${response.status}`);
    }
    return response.text();
}
export async function fetchJson(url) {
    const response = await fetch(url, {
        headers: {
            accept: "application/vnd.github+json",
        },
    });
    if (!response.ok) {
        throw unprocessable(`Failed to fetch ${url}: ${response.status}`);
    }
    return response.json();
}
export async function resolveGitHubDefaultBranch(owner, repo) {
    const response = await fetchJson(`https://api.github.com/repos/${owner}/${repo}`);
    return asString(response.default_branch) ?? "main";
}
export async function resolveGitHubCommitSha(owner, repo, ref) {
    const response = await fetchJson(`https://api.github.com/repos/${owner}/${repo}/commits/${encodeURIComponent(ref)}`);
    const sha = asString(response.sha);
    if (!sha) {
        throw unprocessable(`Failed to resolve GitHub ref ${ref}`);
    }
    return sha;
}
export function parseGitHubSourceUrl(rawUrl) {
    const url = new URL(rawUrl);
    if (url.hostname !== "github.com") {
        throw unprocessable("GitHub source must use github.com URL");
    }
    const parts = url.pathname.split("/").filter(Boolean);
    if (parts.length < 2) {
        throw unprocessable("Invalid GitHub URL");
    }
    const owner = parts[0];
    const repo = parts[1].replace(/\.git$/i, "");
    let ref = "main";
    let basePath = "";
    let filePath = null;
    let explicitRef = false;
    if (parts[2] === "tree") {
        ref = parts[3] ?? "main";
        basePath = parts.slice(4).join("/");
        explicitRef = true;
    }
    else if (parts[2] === "blob") {
        ref = parts[3] ?? "main";
        filePath = parts.slice(4).join("/");
        basePath = filePath ? path.posix.dirname(filePath) : "";
        explicitRef = true;
    }
    return { owner, repo, ref, basePath, filePath, explicitRef };
}
export async function resolveGitHubPinnedRef(parsed) {
    if (/^[0-9a-f]{40}$/i.test(parsed.ref.trim())) {
        return {
            pinnedRef: parsed.ref,
            trackingRef: parsed.explicitRef ? parsed.ref : null,
        };
    }
    const trackingRef = parsed.explicitRef
        ? parsed.ref
        : await resolveGitHubDefaultBranch(parsed.owner, parsed.repo);
    const pinnedRef = await resolveGitHubCommitSha(parsed.owner, parsed.repo, trackingRef);
    return { pinnedRef, trackingRef };
}
export function resolveRawGitHubUrl(owner, repo, ref, filePath) {
    return `https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${filePath.replace(/^\/+/, "")}`;
}
export function extractCommandTokens(raw) {
    const matches = raw.match(/"[^"]*"|'[^']*'|\S+/g) ?? [];
    return matches.map((token) => token.replace(/^['"]|['"]$/g, ""));
}
export function parseSkillImportSourceInput(rawInput) {
    const trimmed = rawInput.trim();
    if (!trimmed) {
        throw unprocessable("Skill source is required.");
    }
    const warnings = [];
    let source = trimmed;
    let requestedSkillSlug = null;
    if (/^npx\s+skills\s+add\s+/i.test(trimmed)) {
        const tokens = extractCommandTokens(trimmed);
        const addIndex = tokens.findIndex((token, index) => token === "add"
            && index > 0
            && tokens[index - 1]?.toLowerCase() === "skills");
        if (addIndex >= 0) {
            source = tokens[addIndex + 1] ?? "";
            for (let index = addIndex + 2; index < tokens.length; index += 1) {
                const token = tokens[index];
                if (token === "--skill") {
                    requestedSkillSlug = normalizeSkillSlug(tokens[index + 1] ?? null);
                    index += 1;
                    continue;
                }
                if (token.startsWith("--skill=")) {
                    requestedSkillSlug = normalizeSkillSlug(token.slice("--skill=".length));
                }
            }
        }
    }
    const normalizedSource = source.trim();
    if (!normalizedSource) {
        throw unprocessable("Skill source is required.");
    }
    // Key-style imports (org/repo/skill) originate from the skills.sh registry
    if (!/^https?:\/\//i.test(normalizedSource) && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(normalizedSource)) {
        const [owner, repo, skillSlugRaw] = normalizedSource.split("/");
        return {
            resolvedSource: `https://github.com/${owner}/${repo}`,
            requestedSkillSlug: normalizeSkillSlug(skillSlugRaw),
            originalSkillsShUrl: `https://skills.sh/${owner}/${repo}/${skillSlugRaw}`,
            warnings,
        };
    }
    if (!/^https?:\/\//i.test(normalizedSource) && /^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(normalizedSource)) {
        return {
            resolvedSource: `https://github.com/${normalizedSource}`,
            requestedSkillSlug,
            originalSkillsShUrl: null,
            warnings,
        };
    }
    // Detect skills.sh URLs and resolve to GitHub: https://skills.sh/org/repo/skill → org/repo/skill key
    const skillsShMatch = normalizedSource.match(/^https?:\/\/(?:www\.)?skills\.sh\/([A-Za-z0-9_.-]+)\/([A-Za-z0-9_.-]+)(?:\/([A-Za-z0-9_.-]+))?(?:[?#].*)?$/i);
    if (skillsShMatch) {
        const [, owner, repo, skillSlugRaw] = skillsShMatch;
        return {
            resolvedSource: `https://github.com/${owner}/${repo}`,
            requestedSkillSlug: skillSlugRaw ? normalizeSkillSlug(skillSlugRaw) : requestedSkillSlug,
            originalSkillsShUrl: normalizedSource,
            warnings,
        };
    }
    return {
        resolvedSource: normalizedSource,
        requestedSkillSlug,
        originalSkillsShUrl: null,
        warnings,
    };
}
export function resolveBundledSkillsRoot() {
    const moduleDir = path.dirname(fileURLToPath(import.meta.url));
    return [
        path.resolve(moduleDir, "../../../resources/bundled-skills"),
        path.resolve(process.cwd(), "server/resources/bundled-skills"),
    ];
}
export async function readCommunityPresetFallbackImport(orgId, slug, skillKey, sourceUrl) {
    for (const skillsRoot of resolveCommunityPresetSkillsRoot()) {
        const stats = await fs.stat(skillsRoot).catch(() => null);
        if (!stats?.isDirectory())
            continue;
        const skillDir = path.join(skillsRoot, slug);
        const skillStats = await fs.stat(skillDir).catch(() => null);
        if (!skillStats?.isDirectory())
            continue;
        const imported = await readLocalSkillImportFromDirectory(orgId, skillDir, {
            metadata: {
                sourceKind: "community_preset",
                skillKey,
            },
        }).catch(() => null);
        if (!imported)
            continue;
        return {
            ...imported,
            key: skillKey,
            slug,
            sourceType: "github",
            sourceLocator: sourceUrl,
            sourceRef: imported.sourceRef ?? null,
            metadata: {
                ...(imported.metadata ?? {}),
                sourceKind: "community_preset",
                skillKey,
            },
        };
    }
    return null;
}
export function resolveCommunityPresetSkillsRoot() {
    const moduleDir = path.dirname(fileURLToPath(import.meta.url));
    return [
        path.resolve(moduleDir, "../../../resources/community-skills"),
        path.resolve(process.cwd(), "server/resources/community-skills"),
    ];
}
export function matchesRequestedSkill(relativeSkillPath, requestedSkillSlug) {
    if (!requestedSkillSlug)
        return true;
    const skillDir = path.posix.dirname(relativeSkillPath);
    return normalizeSkillSlug(path.posix.basename(skillDir)) === requestedSkillSlug;
}
export function deriveImportedSkillSlug(frontmatter, fallback) {
    return normalizeSkillSlug(asString(frontmatter.slug))
        ?? normalizeSkillSlug(asString(frontmatter.name))
        ?? normalizeAgentUrlKey(fallback)
        ?? "skill";
}
export function deriveImportedSkillSource(frontmatter, fallbackSlug) {
    const metadata = isPlainRecord(frontmatter.metadata) ? frontmatter.metadata : null;
    const canonicalKey = readCanonicalSkillKey(frontmatter, metadata);
    const rawSources = metadata && Array.isArray(metadata.sources) ? metadata.sources : [];
    const sourceEntry = rawSources.find((entry) => isPlainRecord(entry));
    const kind = asString(sourceEntry?.kind);
    if (kind === "github-dir" || kind === "github-file") {
        const repo = asString(sourceEntry?.repo);
        const repoPath = asString(sourceEntry?.path);
        const commit = asString(sourceEntry?.commit);
        const trackingRef = asString(sourceEntry?.trackingRef);
        const url = asString(sourceEntry?.url)
            ?? (repo
                ? `https://github.com/${repo}${repoPath ? `/tree/${trackingRef ?? commit ?? "main"}/${repoPath}` : ""}`
                : null);
        const [owner, repoName] = (repo ?? "").split("/");
        if (repo && owner && repoName) {
            return {
                sourceType: "github",
                sourceLocator: url,
                sourceRef: commit,
                metadata: {
                    ...(canonicalKey ? { skillKey: canonicalKey } : {}),
                    sourceKind: "github",
                    owner,
                    repo: repoName,
                    ref: commit,
                    trackingRef,
                    repoSkillDir: repoPath ?? `skills/${fallbackSlug}`,
                },
            };
        }
    }
    if (kind === "url") {
        const url = asString(sourceEntry?.url) ?? asString(sourceEntry?.rawUrl);
        if (url) {
            return {
                sourceType: "url",
                sourceLocator: url,
                sourceRef: null,
                metadata: {
                    ...(canonicalKey ? { skillKey: canonicalKey } : {}),
                    sourceKind: "url",
                },
            };
        }
    }
    return {
        sourceType: "catalog",
        sourceLocator: null,
        sourceRef: null,
        metadata: {
            ...(canonicalKey ? { skillKey: canonicalKey } : {}),
            sourceKind: "catalog",
        },
    };
}
export function readInlineSkillImports(orgId, files) {
    const normalizedFiles = normalizePackageFileMap(files);
    const skillPaths = Object.keys(normalizedFiles).filter((entry) => path.posix.basename(entry).toLowerCase() === "skill.md");
    const imports = [];
    for (const skillPath of skillPaths) {
        const dir = path.posix.dirname(skillPath);
        const skillDir = dir === "." ? "" : dir;
        const slugFallback = path.posix.basename(skillDir || path.posix.dirname(skillPath));
        const markdown = normalizedFiles[skillPath];
        const parsed = parseFrontmatterMarkdown(markdown);
        const slug = deriveImportedSkillSlug(parsed.frontmatter, slugFallback);
        const source = deriveImportedSkillSource(parsed.frontmatter, slug);
        const inventory = Object.keys(normalizedFiles)
            .filter((entry) => entry === skillPath || (skillDir ? entry.startsWith(`${skillDir}/`) : false))
            .map((entry) => {
            const relative = entry === skillPath ? "SKILL.md" : entry.slice(skillDir.length + 1);
            return {
                path: normalizePortablePath(relative),
                kind: classifyInventoryKind(relative),
            };
        })
            .sort((left, right) => left.path.localeCompare(right.path));
        imports.push({
            key: "",
            slug,
            name: asString(parsed.frontmatter.name) ?? slug,
            description: normalizeSkillDescription(parsed.frontmatter.description),
            markdown,
            packageDir: skillDir,
            sourceType: source.sourceType,
            sourceLocator: source.sourceLocator,
            sourceRef: source.sourceRef,
            trustLevel: deriveTrustLevel(inventory),
            compatibility: "compatible",
            fileInventory: inventory,
            metadata: source.metadata,
        });
        imports[imports.length - 1].key = deriveCanonicalSkillKey(orgId, imports[imports.length - 1]);
    }
    return imports;
}
export async function walkLocalFiles(root, current, out) {
    const entries = await fs.readdir(current, { withFileTypes: true });
    for (const entry of entries) {
        if (entry.name === ".git" || entry.name === "node_modules")
            continue;
        const absolutePath = path.join(current, entry.name);
        if (entry.isDirectory()) {
            await walkLocalFiles(root, absolutePath, out);
            continue;
        }
        if (!entry.isFile())
            continue;
        out.push(normalizePortablePath(path.relative(root, absolutePath)));
    }
}
export async function collectLocalSkillInventory(skillDir, mode = "full") {
    const skillFilePath = path.join(skillDir, "SKILL.md");
    const skillFileStat = await statPath(skillFilePath);
    if (!skillFileStat?.isFile()) {
        throw unprocessable(`No SKILL.md file was found in ${skillDir}.`);
    }
    const allFiles = new Set(["SKILL.md"]);
    if (mode === "full") {
        const discoveredFiles = [];
        await walkLocalFiles(skillDir, skillDir, discoveredFiles);
        for (const relativePath of discoveredFiles) {
            allFiles.add(relativePath);
        }
    }
    else {
        for (const relativeDir of PROJECT_ROOT_SKILL_SUBDIRECTORIES) {
            const absoluteDir = path.join(skillDir, relativeDir);
            const dirStat = await statPath(absoluteDir);
            if (!dirStat?.isDirectory())
                continue;
            const discoveredFiles = [];
            await walkLocalFiles(skillDir, absoluteDir, discoveredFiles);
            for (const relativePath of discoveredFiles) {
                allFiles.add(relativePath);
            }
        }
    }
    return Array.from(allFiles)
        .map((relativePath) => ({
        path: normalizePortablePath(relativePath),
        kind: classifyInventoryKind(relativePath),
    }))
        .sort((left, right) => left.path.localeCompare(right.path));
}
export async function readLocalSkillImportFromDirectory(orgId, skillDir, options) {
    const resolvedSkillDir = path.resolve(skillDir);
    const skillFilePath = path.join(resolvedSkillDir, "SKILL.md");
    const markdown = await fs.readFile(skillFilePath, "utf8");
    const parsed = parseFrontmatterMarkdown(markdown);
    const slug = deriveImportedSkillSlug(parsed.frontmatter, path.basename(resolvedSkillDir));
    const parsedMetadata = isPlainRecord(parsed.frontmatter.metadata) ? parsed.frontmatter.metadata : null;
    const skillKey = readCanonicalSkillKey(parsed.frontmatter, parsedMetadata);
    const metadata = {
        ...(skillKey ? { skillKey } : {}),
        ...(parsedMetadata ?? {}),
        sourceKind: "local_path",
        ...(options?.metadata ?? {}),
    };
    const inventory = await collectLocalSkillInventory(resolvedSkillDir, options?.inventoryMode ?? "full");
    return {
        key: deriveCanonicalSkillKey(orgId, {
            slug,
            sourceType: "local_path",
            sourceLocator: resolvedSkillDir,
            metadata,
        }),
        slug,
        name: asString(parsed.frontmatter.name) ?? slug,
        description: normalizeSkillDescription(parsed.frontmatter.description),
        markdown,
        packageDir: resolvedSkillDir,
        sourceType: "local_path",
        sourceLocator: resolvedSkillDir,
        sourceRef: null,
        trustLevel: deriveTrustLevel(inventory),
        compatibility: "compatible",
        fileInventory: inventory,
        metadata,
    };
}
export async function discoverProjectWorkspaceSkillDirectories(target) {
    const discovered = new Map();
    const rootSkillPath = path.join(target.workspaceCwd, "SKILL.md");
    if ((await statPath(rootSkillPath))?.isFile()) {
        discovered.set(path.resolve(target.workspaceCwd), "project_root");
    }
    for (const relativeRoot of PROJECT_SCAN_DIRECTORY_ROOTS) {
        const absoluteRoot = path.join(target.workspaceCwd, relativeRoot);
        const rootStat = await statPath(absoluteRoot);
        if (!rootStat?.isDirectory())
            continue;
        const entries = await fs.readdir(absoluteRoot, { withFileTypes: true }).catch(() => []);
        for (const entry of entries) {
            if (!entry.isDirectory())
                continue;
            const absoluteSkillDir = path.resolve(absoluteRoot, entry.name);
            if (!(await statPath(path.join(absoluteSkillDir, "SKILL.md")))?.isFile())
                continue;
            discovered.set(absoluteSkillDir, "full");
        }
    }
    return Array.from(discovered.entries())
        .map(([skillDir, inventoryMode]) => ({ skillDir, inventoryMode }))
        .sort((left, right) => left.skillDir.localeCompare(right.skillDir));
}
export async function readLocalSkillImports(orgId, sourcePath) {
    const resolvedPath = path.resolve(sourcePath);
    const stat = await fs.stat(resolvedPath).catch(() => null);
    if (!stat) {
        throw unprocessable(`Skill source path does not exist: ${sourcePath}`);
    }
    if (stat.isFile()) {
        if (path.basename(resolvedPath).toLowerCase() !== "skill.md") {
            throw unprocessable("Local skill imports must point at SKILL.md or a directory that contains skill folders.");
        }
        return [await readLocalSkillImportFromDirectory(orgId, path.dirname(resolvedPath))];
    }
    const discovered = new Set();
    if ((await statPath(path.join(resolvedPath, "SKILL.md")))?.isFile()) {
        discovered.add(resolvedPath);
    }
    for (const candidateRoot of [resolvedPath, path.join(resolvedPath, "skills")]) {
        const candidateStat = await statPath(candidateRoot);
        if (!candidateStat?.isDirectory())
            continue;
        const entries = await fs.readdir(candidateRoot, { withFileTypes: true }).catch(() => []);
        for (const entry of entries) {
            if (!entry.isDirectory())
                continue;
            const skillDir = path.join(candidateRoot, entry.name);
            if ((await statPath(path.join(skillDir, "SKILL.md")))?.isFile()) {
                discovered.add(path.resolve(skillDir));
            }
        }
    }
    if (discovered.size === 0) {
        throw unprocessable("No SKILL.md files were found in the provided path.");
    }
    return Promise.all(Array.from(discovered)
        .sort((left, right) => left.localeCompare(right))
        .map((skillDir) => readLocalSkillImportFromDirectory(orgId, skillDir)));
}
export async function readUrlSkillImports(orgId, sourceUrl, requestedSkillSlug = null) {
    const url = sourceUrl.trim();
    const warnings = [];
    if (url.includes("github.com/")) {
        const parsed = parseGitHubSourceUrl(url);
        const { pinnedRef, trackingRef } = await resolveGitHubPinnedRef(parsed);
        let ref = pinnedRef;
        const tree = await fetchJson(`https://api.github.com/repos/${parsed.owner}/${parsed.repo}/git/trees/${ref}?recursive=1`).catch(() => {
            throw unprocessable(`Failed to read GitHub tree for ${url}`);
        });
        const allPaths = (tree.tree ?? [])
            .filter((entry) => entry.type === "blob")
            .map((entry) => entry.path)
            .filter((entry) => typeof entry === "string");
        const basePrefix = parsed.basePath ? `${parsed.basePath.replace(/^\/+|\/+$/g, "")}/` : "";
        const scopedPaths = basePrefix
            ? allPaths.filter((entry) => entry.startsWith(basePrefix))
            : allPaths;
        const relativePaths = scopedPaths.map((entry) => basePrefix ? entry.slice(basePrefix.length) : entry);
        const filteredPaths = parsed.filePath
            ? relativePaths.filter((entry) => entry === path.posix.relative(parsed.basePath || ".", parsed.filePath))
            : relativePaths;
        const skillPaths = filteredPaths.filter((entry) => path.posix.basename(entry).toLowerCase() === "skill.md");
        if (skillPaths.length === 0) {
            throw unprocessable("No SKILL.md files were found in the provided GitHub source.");
        }
        const skills = [];
        for (const relativeSkillPath of skillPaths) {
            const repoSkillPath = basePrefix ? `${basePrefix}${relativeSkillPath}` : relativeSkillPath;
            const markdown = await fetchText(resolveRawGitHubUrl(parsed.owner, parsed.repo, ref, repoSkillPath));
            const parsedMarkdown = parseFrontmatterMarkdown(markdown);
            const skillDir = path.posix.dirname(relativeSkillPath);
            const slug = deriveImportedSkillSlug(parsedMarkdown.frontmatter, path.posix.basename(skillDir));
            const skillKey = readCanonicalSkillKey(parsedMarkdown.frontmatter, isPlainRecord(parsedMarkdown.frontmatter.metadata) ? parsedMarkdown.frontmatter.metadata : null);
            if (requestedSkillSlug && !matchesRequestedSkill(relativeSkillPath, requestedSkillSlug) && slug !== requestedSkillSlug) {
                continue;
            }
            const metadata = {
                ...(skillKey ? { skillKey } : {}),
                sourceKind: "github",
                owner: parsed.owner,
                repo: parsed.repo,
                ref: ref,
                trackingRef,
                repoSkillDir: normalizeGitHubSkillDirectory(basePrefix ? `${basePrefix}${skillDir}` : skillDir, slug),
            };
            const inventory = filteredPaths
                .filter((entry) => entry === relativeSkillPath || entry.startsWith(`${skillDir}/`))
                .map((entry) => ({
                path: entry === relativeSkillPath ? "SKILL.md" : entry.slice(skillDir.length + 1),
                kind: classifyInventoryKind(entry === relativeSkillPath ? "SKILL.md" : entry.slice(skillDir.length + 1)),
            }))
                .sort((left, right) => left.path.localeCompare(right.path));
            skills.push({
                key: deriveCanonicalSkillKey(orgId, {
                    slug,
                    sourceType: "github",
                    sourceLocator: sourceUrl,
                    metadata,
                }),
                slug,
                name: asString(parsedMarkdown.frontmatter.name) ?? slug,
                description: normalizeSkillDescription(parsedMarkdown.frontmatter.description),
                markdown,
                sourceType: "github",
                sourceLocator: sourceUrl,
                sourceRef: ref,
                trustLevel: deriveTrustLevel(inventory),
                compatibility: "compatible",
                fileInventory: inventory,
                metadata,
            });
        }
        if (skills.length === 0) {
            throw unprocessable(requestedSkillSlug
                ? `Skill ${requestedSkillSlug} was not found in the provided GitHub source.`
                : "No SKILL.md files were found in the provided GitHub source.");
        }
        return { skills, warnings };
    }
    if (url.startsWith("http://") || url.startsWith("https://")) {
        const markdown = await fetchText(url);
        const parsedMarkdown = parseFrontmatterMarkdown(markdown);
        const urlObj = new URL(url);
        const fileName = path.posix.basename(urlObj.pathname);
        const slug = deriveImportedSkillSlug(parsedMarkdown.frontmatter, fileName.replace(/\.md$/i, ""));
        const skillKey = readCanonicalSkillKey(parsedMarkdown.frontmatter, isPlainRecord(parsedMarkdown.frontmatter.metadata) ? parsedMarkdown.frontmatter.metadata : null);
        const metadata = {
            ...(skillKey ? { skillKey } : {}),
            sourceKind: "url",
        };
        const inventory = [{ path: "SKILL.md", kind: "skill" }];
        return {
            skills: [{
                    key: deriveCanonicalSkillKey(orgId, {
                        slug,
                        sourceType: "url",
                        sourceLocator: url,
                        metadata,
                    }),
                    slug,
                    name: asString(parsedMarkdown.frontmatter.name) ?? slug,
                    description: normalizeSkillDescription(parsedMarkdown.frontmatter.description),
                    markdown,
                    sourceType: "url",
                    sourceLocator: url,
                    sourceRef: null,
                    trustLevel: deriveTrustLevel(inventory),
                    compatibility: "compatible",
                    fileInventory: inventory,
                    metadata,
                }],
            warnings,
        };
    }
    throw unprocessable("Unsupported skill source. Use a local path or URL.");
}
//# sourceMappingURL=organization-skills.sources.js.map