"use strict";
// =============================================================================
// Pure Agent - Skill Utilities
// Ported from Hermes agent/skill_utils.py
// Frontmatter parsing, skill discovery, platform matching
// =============================================================================
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.EXCLUDED_SKILL_DIRS = exports.PLATFORM_MAP = void 0;
exports.parseFrontmatter = parseFrontmatter;
exports.skillMatchesPlatform = skillMatchesPlatform;
exports.getSkillsDir = getSkillsDir;
exports.getBundledSkillsDir = getBundledSkillsDir;
exports.getAllSkillsDirs = getAllSkillsDirs;
exports.isExcludedSkillDir = isExcludedSkillDir;
exports.iterSkillIndexFiles = iterSkillIndexFiles;
exports.getCategoryFromPath = getCategoryFromPath;
exports.discoverAllSkills = discoverAllSkills;
exports.skillView = skillView;
exports.normalizeSkillName = normalizeSkillName;
const fs = __importStar(require("fs"));
const path = __importStar(require("path"));
const os = __importStar(require("os"));
const constants_1 = require("./constants");
const logger_1 = require("./logger");
// ---------------------------------------------------------------------------
// Constants
// ---------------------------------------------------------------------------
exports.PLATFORM_MAP = {
    macos: 'darwin',
    linux: 'linux',
    windows: 'win32',
};
exports.EXCLUDED_SKILL_DIRS = new Set([
    '.git', '.github', '.hub', '.archive', '.venv', 'venv',
    'node_modules', 'site-packages', '__pycache__',
    '.tox', '.nox', '.pytest_cache', '.mypy_cache', '.ruff_cache',
]);
const INJECTION_PATTERNS = [
    'ignore previous instructions',
    'ignore all previous',
    'you are now',
    'disregard your',
    'forget your instructions',
    'new instructions:',
    'system prompt:',
    '<system>',
    ']]>',
];
const MAX_NAME_LENGTH = 64;
const MAX_DESCRIPTION_LENGTH = 1024;
// ---------------------------------------------------------------------------
// Frontmatter parsing
// ---------------------------------------------------------------------------
function parseFrontmatter(content) {
    if (!content.startsWith('---')) {
        return [{}, content];
    }
    const endMatch = content.match(/\n---\s*\n/);
    if (!endMatch || endMatch.index === undefined) {
        return [{}, content];
    }
    const yamlStr = content.slice(3, endMatch.index + 1);
    const body = content.slice(endMatch.index + 4); // skip "\n---\n"
    try {
        const parsed = simpleYamlParse(yamlStr);
        return [parsed || {}, body];
    }
    catch {
        return [{}, body];
    }
}
// ---------------------------------------------------------------------------
// Simple YAML parser (handles common SKILL.md frontmatter patterns)
// ---------------------------------------------------------------------------
function simpleYamlParse(yamlStr) {
    const result = {};
    const lines = yamlStr.split('\n');
    let currentKey = '';
    let currentList = null;
    let indent = 0;
    for (const rawLine of lines) {
        const line = rawLine.replace(/\r$/, '');
        if (line.trim() === '' || line.trim().startsWith('#'))
            continue;
        const lineIndent = line.search(/\S/);
        const trimmed = line.trim();
        // List item
        if (trimmed.startsWith('- ')) {
            const value = trimmed.slice(2).trim().replace(/^["']|["']$/g, '');
            if (currentList) {
                currentList.push(value);
            }
            else if (currentKey) {
                currentList = [value];
                result[currentKey] = currentList;
            }
            continue;
        }
        // Key-value pair
        const kvMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:\s*(.*)$/);
        if (kvMatch) {
            const key = kvMatch[1];
            let value = kvMatch[2].trim().replace(/^["']|["']$/g, '');
            // Empty value = could be a list (items follow)
            if (value === '') {
                currentKey = key;
                currentList = null;
                indent = lineIndent;
                continue;
            }
            // Parse value
            value = parseYamlValue(value);
            result[key] = value;
            currentKey = key;
            currentList = null;
            indent = lineIndent;
            continue;
        }
        // Inline object (key: { ... })
        if (trimmed.includes(':') && trimmed.includes('{')) {
            const inlineMatch = trimmed.match(/^([a-zA-Z_][a-zA-Z0-9_-]*)\s*:\s*\{(.*)\}$/);
            if (inlineMatch) {
                try {
                    result[inlineMatch[1]] = JSON.parse('{' + inlineMatch[2] + '}');
                }
                catch {
                    result[inlineMatch[1]] = inlineMatch[2].trim();
                }
            }
        }
    }
    return result;
}
function parseYamlValue(value) {
    if (value === '' || value === 'null' || value === '~')
        return null;
    if (value === 'true')
        return true;
    if (value === 'false')
        return false;
    if (/^\d+$/.test(value))
        return parseInt(value, 10);
    if (/^\d+\.\d+$/.test(value))
        return parseFloat(value);
    // Inline array: [a, b, c]
    if (value.startsWith('[') && value.endsWith(']')) {
        try {
            return JSON.parse(value.replace(/'/g, '"'));
        }
        catch {
            return value.slice(1, -1).split(',').map(s => s.trim().replace(/^["']|["']$/g, ''));
        }
    }
    return value;
}
// ---------------------------------------------------------------------------
// Platform matching
// ---------------------------------------------------------------------------
function skillMatchesPlatform(frontmatter) {
    const platforms = frontmatter.platforms;
    if (!platforms || !Array.isArray(platforms) || platforms.length === 0)
        return true;
    const currentPlatform = os.platform();
    return platforms.some((p) => {
        const normalized = exports.PLATFORM_MAP[p.toLowerCase()] || p.toLowerCase();
        return currentPlatform.startsWith(normalized);
    });
}
// ---------------------------------------------------------------------------
// Skill directory resolution
// ---------------------------------------------------------------------------
function getSkillsDir() {
    return path.join((0, constants_1.getPureAgentHome)(), 'skills');
}
function getBundledSkillsDir() {
    // Look for bundled skills relative to the project root
    const candidates = [
        path.join(process.cwd(), 'skills'),
        path.join(__dirname, '..', '..', '..', 'skills'),
    ];
    for (const dir of candidates) {
        if (fs.existsSync(path.join(dir, 'system')) || fs.existsSync(path.join(dir, 'preset'))) {
            return dir;
        }
    }
    return null;
}
function getAllSkillsDirs() {
    const dirs = [];
    // Primary: ~/.pure-agent/skills/
    const primary = getSkillsDir();
    if (!dirs.includes(primary))
        dirs.push(primary);
    // Bundled: project skills/ directory
    const bundled = getBundledSkillsDir();
    if (bundled && !dirs.includes(bundled))
        dirs.push(bundled);
    return dirs;
}
// ---------------------------------------------------------------------------
// Skill file iteration
// ---------------------------------------------------------------------------
function isExcludedSkillDir(name) {
    return exports.EXCLUDED_SKILL_DIRS.has(name) || name.startsWith('.');
}
function* iterSkillIndexFiles(skillsDir, filename) {
    if (!fs.existsSync(skillsDir))
        return;
    const walk = function* (dir) {
        let entries;
        try {
            entries = fs.readdirSync(dir, { withFileTypes: true });
        }
        catch {
            return;
        }
        // Sort for deterministic order
        entries.sort((a, b) => a.name.localeCompare(b.name));
        for (const entry of entries) {
            if (entry.isDirectory()) {
                if (isExcludedSkillDir(entry.name))
                    continue;
                yield* walk(path.join(dir, entry.name));
            }
            else if (entry.name === filename) {
                yield path.join(dir, entry.name);
            }
        }
    };
    yield* walk(skillsDir);
}
// ---------------------------------------------------------------------------
// Category extraction
// ---------------------------------------------------------------------------
function getCategoryFromPath(skillPath, skillsDir) {
    const relative = path.relative(skillsDir, skillPath);
    const parts = relative.split(path.sep);
    // If skill is at skillsDir/skillname/SKILL.md, category is null
    // If skill is at skillsDir/category/skillname/SKILL.md, category is "category"
    if (parts.length >= 3) {
        return parts[0];
    }
    return null;
}
function discoverAllSkills() {
    const skills = [];
    const seenNames = new Set();
    const skillsDirs = getAllSkillsDirs();
    for (const skillsDir of skillsDirs) {
        for (const skillPath of iterSkillIndexFiles(skillsDir, 'SKILL.md')) {
            try {
                const content = fs.readFileSync(skillPath, 'utf-8').slice(0, 4000);
                const [fm] = parseFrontmatter(content);
                const name = fm.name || path.basename(path.dirname(skillPath));
                if (seenNames.has(name))
                    continue;
                // Platform check
                if (!skillMatchesPlatform(fm))
                    continue;
                // Injection check
                const threat = checkInjection(content);
                if (threat) {
                    (0, logger_1.getLogger)().warn(`Skill "${name}" blocked: ${threat}`);
                    continue;
                }
                seenNames.add(name);
                skills.push({
                    name: name.slice(0, MAX_NAME_LENGTH),
                    description: (fm.description || '').slice(0, MAX_DESCRIPTION_LENGTH),
                    category: getCategoryFromPath(skillPath, skillsDir),
                    skillPath,
                    skillDir: path.dirname(skillPath),
                    frontmatter: fm,
                });
            }
            catch (err) {
                (0, logger_1.getLogger)().warn(`Failed to read skill at ${skillPath}: ${err.message}`);
            }
        }
    }
    return skills;
}
function skillView(name) {
    const skills = discoverAllSkills();
    // Strategy 1: exact name match
    const exact = skills.filter(s => s.name === name);
    if (exact.length === 1) {
        return loadSkillContent(exact[0]);
    }
    if (exact.length > 1) {
        return {
            success: false,
            name,
            error: `Multiple skills named "${name}" found: ${exact.map(s => s.skillPath).join(', ')}`,
        };
    }
    // Strategy 2: partial match / fuzzy
    const partial = skills.filter(s => s.name.includes(name) || name.includes(s.name));
    if (partial.length === 1) {
        return loadSkillContent(partial[0]);
    }
    if (partial.length > 1) {
        return {
            success: false,
            name,
            error: `Ambiguous skill name "${name}". Matches: ${partial.map(s => s.name).join(', ')}`,
        };
    }
    // Strategy 3: direct path
    const skillsDirs = getAllSkillsDirs();
    for (const dir of skillsDirs) {
        const directPath = path.join(dir, name, 'SKILL.md');
        if (fs.existsSync(directPath)) {
            const skillDir = path.dirname(directPath);
            return loadSkillFromPath(name, directPath, skillDir);
        }
        const flatPath = path.join(dir, name + '.md');
        if (fs.existsSync(flatPath)) {
            return loadSkillFromPath(name, flatPath, dir);
        }
    }
    return {
        success: false,
        name,
        error: `Skill "${name}" not found. Available skills: ${skills.map(s => s.name).join(', ')}`,
    };
}
function loadSkillContent(skill) {
    return loadSkillFromPath(skill.name, skill.skillPath, skill.skillDir);
}
function loadSkillFromPath(name, skillPath, skillDir) {
    try {
        const rawContent = fs.readFileSync(skillPath, 'utf-8');
        const [fm, body] = parseFrontmatter(rawContent);
        // Injection check on full content
        const threat = checkInjection(body);
        if (threat) {
            return { success: false, name, error: `Security: ${threat}` };
        }
        // Discover linked files
        const linkedFiles = discoverLinkedFiles(skillDir);
        return {
            success: true,
            name: fm.name || name,
            description: fm.description,
            content: body.trim(),
            path: skillPath,
            skillDir,
            linkedFiles,
            tags: fm.metadata?.hermes?.tags || [],
        };
    }
    catch (err) {
        return { success: false, name, error: `Failed to load: ${err.message}` };
    }
}
// ---------------------------------------------------------------------------
// Linked file discovery
// ---------------------------------------------------------------------------
function discoverLinkedFiles(skillDir) {
    const result = {};
    const subdirs = ['references', 'templates', 'scripts', 'assets'];
    for (const subdir of subdirs) {
        const dirPath = path.join(skillDir, subdir);
        if (!fs.existsSync(dirPath))
            continue;
        try {
            const entries = fs.readdirSync(dirPath, { withFileTypes: true });
            const files = entries
                .filter(e => e.isFile())
                .map(e => e.name)
                .sort();
            if (files.length > 0) {
                result[subdir] = files;
            }
        }
        catch { /* skip */ }
    }
    return result;
}
// ---------------------------------------------------------------------------
// Injection detection
// ---------------------------------------------------------------------------
function checkInjection(content) {
    const lower = content.toLowerCase();
    for (const pattern of INJECTION_PATTERNS) {
        if (lower.includes(pattern)) {
            return `contains injection pattern: "${pattern}"`;
        }
    }
    return null;
}
// ---------------------------------------------------------------------------
// Skill name normalization
// ---------------------------------------------------------------------------
function normalizeSkillName(name) {
    return name
        .toLowerCase()
        .replace(/\s+/g, '-')
        .replace(/_/g, '-')
        .replace(/[^a-z0-9-]/g, '')
        .replace(/-{2,}/g, '-')
        .replace(/^-+|-+$/g, '');
}
//# sourceMappingURL=skill-utils.js.map