/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import * as fs from 'node:fs/promises';
import * as path from 'node:path';
import { glob } from 'glob';
import { load } from 'js-yaml';
import { debugLogger } from '../utils/debugLogger.js';
import { coreEvents } from '../utils/events.js';
export const FRONTMATTER_REGEX = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?/;
/**
 * Parses frontmatter content using YAML with a fallback to simple key-value parsing.
 * This handles cases where description contains colons that would break YAML parsing.
 */
function parseFrontmatter(content) {
    try {
        const parsed = load(content);
        if (parsed && typeof parsed === 'object') {
            // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
            const { name, description } = parsed;
            if (typeof name === 'string' && typeof description === 'string') {
                return { name, description };
            }
        }
    }
    catch (yamlError) {
        debugLogger.debug('YAML frontmatter parsing failed, falling back to simple parser:', yamlError);
    }
    return parseSimpleFrontmatter(content);
}
/**
 * Simple frontmatter parser that extracts name and description fields.
 * Handles cases where values contain colons that would break YAML parsing.
 */
function parseSimpleFrontmatter(content) {
    const lines = content.split(/\r?\n/);
    let name;
    let description;
    for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        // Match "name:" at the start of the line (optional whitespace)
        const nameMatch = line.match(/^\s*name:\s*(.*)$/);
        if (nameMatch) {
            name = nameMatch[1].trim();
            continue;
        }
        // Match "description:" at the start of the line (optional whitespace)
        const descMatch = line.match(/^\s*description:\s*(.*)$/);
        if (descMatch) {
            const descLines = [descMatch[1].trim()];
            // Check for multi-line description (indented continuation lines)
            while (i + 1 < lines.length) {
                const nextLine = lines[i + 1];
                // If next line is indented, it's a continuation of the description
                if (nextLine.match(/^[ \t]+\S/)) {
                    descLines.push(nextLine.trim());
                    i++;
                }
                else {
                    break;
                }
            }
            description = descLines.filter(Boolean).join(' ');
            continue;
        }
    }
    if (name !== undefined && description !== undefined) {
        return { name, description };
    }
    return null;
}
/**
 * Discovers and loads all skills in the provided directory.
 */
export async function loadSkillsFromDir(dir) {
    const discoveredSkills = [];
    try {
        const absoluteSearchPath = path.resolve(dir);
        const stats = await fs.stat(absoluteSearchPath).catch(() => null);
        if (!stats || !stats.isDirectory()) {
            return [];
        }
        const pattern = ['SKILL.md', '*/SKILL.md'];
        const skillFiles = await glob(pattern, {
            cwd: absoluteSearchPath,
            absolute: true,
            nodir: true,
            ignore: ['**/node_modules/**', '**/.git/**'],
        });
        for (const skillFile of skillFiles) {
            const metadata = await loadSkillFromFile(skillFile);
            if (metadata) {
                discoveredSkills.push(metadata);
            }
        }
        if (discoveredSkills.length === 0) {
            const files = await fs.readdir(absoluteSearchPath);
            if (files.length > 0) {
                debugLogger.debug(`Failed to load skills from ${absoluteSearchPath}. The directory is not empty but no valid skills were discovered. Please ensure SKILL.md files are present in subdirectories and have valid frontmatter.`);
            }
        }
    }
    catch (error) {
        coreEvents.emitFeedback('warning', `Error discovering skills in ${dir}:`, error);
    }
    return discoveredSkills;
}
/**
 * Loads a single skill from a SKILL.md file.
 */
export async function loadSkillFromFile(filePath) {
    try {
        const content = await fs.readFile(filePath, 'utf-8');
        const match = content.match(FRONTMATTER_REGEX);
        if (!match) {
            return null;
        }
        const frontmatter = parseFrontmatter(match[1]);
        if (!frontmatter) {
            return null;
        }
        // Sanitize name for use as a filename/directory name (e.g. replace ':' with '-')
        const sanitizedName = frontmatter.name.replace(/[:\\/<>*?"|]/g, '-');
        return {
            name: sanitizedName,
            description: frontmatter.description,
            location: filePath,
            body: match[2]?.trim() ?? '',
        };
    }
    catch (error) {
        debugLogger.log(`Error parsing skill file ${filePath}:`, error);
        return null;
    }
}
//# sourceMappingURL=skillLoader.js.map