/** * SkillRegistry - Global singleton registry for discovering and loading skills. * * Matches Python's `skills/registry.py:SkillRegistry`: the registry stores * **class references** keyed by each class's static `SKILL_NAME`, and reads * metadata (description, version, required packages/env vars, * multi-instance support) by direct static-attribute access. * * Registration via `register(SkillClass)` validates that the class defines * `SKILL_NAME`, that `getParameterSchema()` is callable and returns a * non-empty object (Python `register_skill` at `registry.py:132-194`). * * Supports the `SIGNALWIRE_SKILL_PATHS` environment variable for custom * skill directories, matching Python's discovery search path. */ import { SkillBase, type SkillConfig, type ParameterSchemaEntry } from './SkillBase.js'; /** * Metadata exposed for a registered skill. Shape matches Python's * `SkillRegistry.list_skills()` / `get_all_skills_schema()` return values * (`skills/registry.py:205-227`, `229-296`). */ export interface SkillSchemaInfo { /** The skill's registered name (from `SkillBase.SKILL_NAME`). */ name: string; /** Human-readable description (from `SkillBase.SKILL_DESCRIPTION`). */ description: string; /** Semantic version string (from `SkillBase.SKILL_VERSION`). */ version: string; /** Whether this skill supports multiple simultaneous instances. */ supportsMultipleInstances: boolean; /** Environment variables required by the skill. */ requiredEnvVars: string[]; /** NPM packages required by the skill. */ requiredPackages: string[]; /** Full parameter schema with types, defaults, and constraints. */ parameters: Record; /** Optional source category for grouping (e.g., "builtin", "external"). */ source?: string; } /** * Global singleton registry for registering and instantiating skills. * * Skills can be registered programmatically via `register(SkillClass)`. * Matches Python's `skill_registry` global (`skills/registry.py:481`). */ export declare class SkillRegistry { /** * Map from `SKILL_NAME` to class reference. Matches Python's * `self._skills: Dict[str, Type[SkillBase]]` (`registry.py:26`). */ private registry; private lockedNames; private searchPaths; /** * External skill directories registered via {@link addSkillDirectory}. * Mirrors Python's `SkillRegistry._external_paths` (`registry.py:373`). */ private externalPaths; constructor(); /** * Get the global singleton instance, creating it on first access. * @returns The shared SkillRegistry instance. */ static getInstance(): SkillRegistry; /** * Reset the global singleton (for testing). */ static resetInstance(): void; /** * Register a skill class. The skill name is read from the class's static * `SKILL_NAME`. Mirrors Python's `register_skill(skill_class)` * (`skills/registry.py:132-194`) — including the schema-non-empty check * and the protection against overwriting locked skills. * * @param SkillClass - A concrete subclass of `SkillBase` with a * non-empty `SKILL_NAME`. */ register(SkillClass: typeof SkillBase): void; /** * Lock one or more skill names to prevent overwriting. * If called with no arguments, locks all currently registered skills. * @param names - Skill names to lock; if omitted, all current names are locked. */ lock(names?: string[]): void; /** * Unregister a skill by name, removing it from the registry. * @param name - The skill name to unregister. * @returns True if the skill was found and removed. */ unregister(name: string): boolean; /** * Create a new skill instance by looking up its class in the registry. * Matches Python's `skill_manager.load_skill(name)` class-lookup + instantiate * flow (`skill_manager.py:97`: `skill_instance = skill_class(self.agent, params)`). * * @param name - The registered skill name. * @param config - Optional configuration to pass to the skill constructor. * @returns A new skill instance, or null if the name is not registered. */ create(name: string, config?: SkillConfig): SkillBase | null; /** * Get the registered skill class by name. Matches Python's * `get_skill_class(skill_name)` (`registry.py:196-203`). * @param name - The registered skill name. * @returns The skill class reference, or undefined if not registered. */ getSkillClass(name: string): typeof SkillBase | undefined; /** * Check if a skill name is registered. * @param name - The skill name to check. * @returns True if the skill is registered. */ has(name: string): boolean; /** * List all registered skill names. * @returns Array of registered skill name strings. */ listRegistered(): string[]; /** * List all registered skills with their full metadata. Matches Python's * `list_skills()` shape (`registry.py:205-227`) plus TS-idiomatic * camelCase keys. * @returns Array of skill metadata objects. */ listSkills(): SkillSchemaInfo[]; /** * Add a directory path to search during skill discovery. * @param path - Absolute path to a directory containing skill files. */ addSearchPath(path: string): void; /** * Add a directory to search for skills. * * Mirrors Python's * `signalwire.skills.registry.SkillRegistry.add_skill_directory` * (`registry.py:350-375`): validate that the path exists and is a * directory, then append it (de-duplicated) to `externalPaths`. Throws * `Error` (the JS analog of Python's `ValueError`) for non-existent * paths or non-directories. Distinct from `addSearchPath`, which * silently accepts anything; `addSkillDirectory` is the strict * Python-parity surface and the recommended entry point for * registering third-party skill directories. * * @param path - Absolute or relative path to a directory containing * skill subdirectories. * @throws Error when the path doesn't exist or isn't a directory. */ addSkillDirectory(path: string): void; /** * Returns a copy of the external skill directories registered via * {@link addSkillDirectory}. Parity surface for Python's * `_external_paths`. */ getExternalPaths(): string[]; /** * Get all configured search paths for skill discovery. * @returns Copy of the search paths array. */ getSearchPaths(): string[]; /** * Discover and register skills from a directory by importing each file. * Looks for SkillBase subclass exports and registers them. * @param dirPath - Absolute path to the directory to scan. * @returns Array of newly discovered skill names. */ discoverFromDirectory(dirPath: string): Promise; /** * Discover and register skills from all configured search paths. * @returns Array of all newly discovered skill names. */ discoverAll(): Promise; /** * Get the combined schema info for a registered skill. * Matches Python `get_all_skills_schema` per-skill shape * (`registry.py:287-295`). * @param name - The registered skill name to query. * @returns The skill's schema info, or undefined if not found. */ getSkillSchema(name: string): SkillSchemaInfo | undefined; /** * Get combined schema info for all registered skills. * @returns Record mapping skill names to their schema info. */ getAllSkillsSchema(): Record; /** * Group registered skill names by source category. Matches Python's * `list_all_skill_sources` (`skills/registry.py:436-478`). * * Current TS implementation treats every registered skill as "registered" * (the only category that fits — filesystem-based discovery is optional * and entry-points don't apply to Node the way they do to Python). * * @returns Record mapping source categories to arrays of skill names. */ listAllSkillSources(): Record; /** * Get the number of registered skills. * @returns The count of registered skills. */ get size(): number; /** * Clear all registrations. */ clear(): void; }