// Skill Loader — reads .md skill files from skills/data/, caches in memory import { readFileSync } from 'fs'; import { join } from 'path'; import { SKILL_REGISTRY, type SkillName } from './registry'; const SKILLS_DIR = join(__dirname, 'data'); // In-memory cache: filename → content const fileCache = new Map(); // Combined skill cache: skill name → concatenated content const skillCache = new Map(); function loadFile(filename: string): string { const cached = fileCache.get(filename); if (cached) return cached; const filePath = join(SKILLS_DIR, filename); const content = readFileSync(filePath, 'utf-8'); fileCache.set(filename, content); return content; } /** * Get the combined skill content for a named skill. * Multi-file skills (builder, designer) are concatenated with separators. */ export function getSkill(name: string): string { const cached = skillCache.get(name); if (cached) return cached; const mapping = SKILL_REGISTRY[name as SkillName]; if (!mapping) { throw new Error( `Unknown skill "${name}". Available: ${Object.keys(SKILL_REGISTRY).join(', ')}` ); } const parts = mapping.files.map((file) => { const content = loadFile(file); return `\n${content}`; }); const combined = parts.join('\n\n---\n\n'); skillCache.set(name, combined); return combined; } /** * Get a single raw skill file by filename. */ export function getSkillFile(filename: string): string { return loadFile(filename); } /** * Preload all skill files into cache. Call at startup for faster first requests. */ export function preloadSkills(): void { for (const mapping of Object.values(SKILL_REGISTRY)) { for (const file of mapping.files) { loadFile(file); } } // Also build combined caches for (const name of Object.keys(SKILL_REGISTRY)) { getSkill(name); } } /** * Clear all caches. Useful for development hot-reload. */ export function clearSkillCache(): void { fileCache.clear(); skillCache.clear(); }