import type { Skill } from './types.js'; /** * SkillRegistry holds loaded skills and generates system prompt additions. * * Skills are keyed by name. Managed/workspace skills may override bundled * skills by registering with the same name. */ export class SkillRegistry { private skills: Map = new Map(); /** Replace all registered skills with the provided set. */ reset(skills: Skill[]): void { this.skills.clear(); for (const skill of skills) { this.skills.set(skill.manifest.name, skill); } } /** Register a skill. Replaces any existing skill with the same name. */ register(skill: Skill): void { this.skills.set(skill.manifest.name, skill); console.log( `Skill '${skill.manifest.name}' registered (${skill.manifest.tier}, ${skill.available ? 'available' : 'unavailable'})`, ); } /** Unregister a skill by name. Returns true if the skill existed. */ unregister(name: string): boolean { return this.skills.delete(name); } /** Look up a skill by name. */ get(name: string): Skill | undefined { return this.skills.get(name); } /** Return all registered skills. */ list(): Skill[] { return Array.from(this.skills.values()); } /** Return only skills whose requirements are met (available === true). */ listAvailable(): Skill[] { return this.list().filter(skill => skill.available); } /** * Generate system prompt additions from all available skills. * * Each skill's SKILL.md content is formatted as a markdown section: * ``` * ## Skill: * * ``` * * Returns an empty string if no skills are available. */ getSystemPromptAdditions(): string { const available = this.listAvailable(); if (available.length === 0) { return ''; } return available .map(skill => `## Skill: ${skill.manifest.name}\n${skill.instructions}`) .join('\n\n'); } /** Return the names of all available skills. */ getSkillNames(): string[] { return this.listAvailable().map(skill => skill.manifest.name); } }