Files
flynn/src/skills/registry.ts
T
2026-02-12 17:30:23 -08:00

75 lines
2.0 KiB
TypeScript

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<string, Skill> = 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: <name>
* <instructions>
* ```
*
* 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);
}
}