/** * Skill Catalog Service * * Scans skill directories (orchestrator and agent), reads SKILL.md * (YAML frontmatter + markdown body) from each, and generates formatted * catalog Markdown files at ~/.crewly/skills/. * * Generates two catalogs: * - SKILLS_CATALOG.md for orchestrator skills * - AGENT_SKILLS_CATALOG.md for agent skills * * Each catalog provides a human-readable and LLM-readable reference of all * available skills grouped by category, including usage examples and parameter * documentation extracted from each skill's SKILL.md body. * * @module services/skill/skill-catalog.service */ /** * Result of the catalog generation process. */ export interface CatalogGenerationResult { /** Whether the catalog was successfully generated */ success: boolean; /** Absolute path to the generated catalog file */ catalogPath: string; /** Total number of skills included in the catalog */ skillCount: number; /** Number of distinct categories in the catalog */ categoryCount: number; /** Error message if generation failed */ error?: string; } /** * Service for generating a Markdown skills catalog from orchestrator skill directories. * * Scans config/skills/orchestrator/ for subdirectories (skipping _common), * reads skill.json and instructions.md from each, groups skills by category, * and writes a formatted SKILLS_CATALOG.md to ~/.crewly/skills/. * * Uses a singleton pattern consistent with other Crewly services. * * @example * ```typescript * const catalogService = SkillCatalogService.getInstance('/path/to/project'); * const result = await catalogService.generateCatalog(); * console.log(`Generated catalog at ${result.catalogPath} with ${result.skillCount} skills`); * ``` */ export declare class SkillCatalogService { private static instance; private readonly logger; private readonly projectRoot; private readonly catalogDir; /** * Create a new SkillCatalogService instance. * * @param projectRoot - Absolute path to the project root directory. * Defaults to the current working directory if not provided. */ constructor(projectRoot?: string); /** * #222: Resolve the Crewly install directory from __dirname or CWD. * * @returns Absolute path to the Crewly install directory */ private resolveInstallDir; /** * Get the singleton SkillCatalogService instance. * * Creates a new instance on first call with the provided project root, * or returns the existing instance on subsequent calls. * * @param projectRoot - Absolute path to the project root directory * @returns The shared SkillCatalogService instance */ static getInstance(projectRoot?: string): SkillCatalogService; /** * Reset the singleton instance. * * Used primarily for testing to ensure a clean state between test runs. */ static clearInstance(): void; /** * Generate the skills catalog by scanning skill directories and writing * the formatted Markdown file. * * This method performs the following steps: * 1. Ensures the output directory (~/.crewly/skills/) exists * 2. Scans config/skills/orchestrator/ for skill subdirectories * 3. Reads skill.json and instructions.md from each valid directory * 4. Groups loaded skills by their category field * 5. Renders a Markdown catalog document * 6. Writes the catalog to ~/.crewly/skills/SKILLS_CATALOG.md * * @returns A result object with success status, path, and counts * * @example * ```typescript * const result = await catalogService.generateCatalog(); * if (result.success) { * console.log(`Catalog written to ${result.catalogPath}`); * } else { * console.error(`Catalog generation failed: ${result.error}`); * } * ``` */ generateCatalog(): Promise; /** * Generate the agent skills catalog by scanning agent skill directories * and writing a formatted Markdown file. * * This method mirrors generateCatalog() but targets config/skills/agent/ * and writes to ~/.crewly/skills/AGENT_SKILLS_CATALOG.md. * * @returns A result object with success status, path, and counts * * @example * ```typescript * const result = await catalogService.generateAgentCatalog(); * if (result.success) { * console.log(`Agent catalog written to ${result.catalogPath}`); * } * ``` */ generateAgentCatalog(): Promise; /** * Get the absolute path where the orchestrator catalog file will be written. * * @returns Absolute path to ~/.crewly/skills/SKILLS_CATALOG.md */ getCatalogPath(): string; /** * Get the absolute path where the agent catalog file will be written. * * @returns Absolute path to ~/.crewly/skills/AGENT_SKILLS_CATALOG.md */ getAgentCatalogPath(): string; /** * Ensure the catalog output directory exists, creating it recursively * if necessary. * * Uses synchronous mkdirSync since this is a one-time setup operation * that must complete before writing the catalog. */ private ensureCatalogDirectory; /** * Scan the orchestrator skills directory for valid skill subdirectories, * plus agent skills whose `assignableRoles` includes `orchestrator`. * * Pipeline-#4 fix (spec 2026-05-05-request-decompose-pipeline-gap.md, Patch C * zero-LOC alternative): some materialisation skills (e.g. * `agent/core/break-down-request`) are role-shared between agents and the * orchestrator and live under `config/skills/agent/core/`. Without this * cross-scan, the orchestrator's catalog would not surface them and the orc * would have no discoverable path to materialise WorkItems for a Request, * even though the prompt at prompt-builder.service.ts:374 instructs it to. * * Skills already present under `config/skills/orchestrator/` win on * collisions (deduped by name), so adding an explicit orchestrator-side * skill later transparently shadows the agent-side one. * * @returns Array of loaded skills with their definitions and instructions */ private scanSkillDirectories; /** * Scan a skills directory for valid skill subdirectories. * * Reads each subdirectory (skipping entries in SKIP_DIRECTORIES), * loads skill.json and instructions.md from each, and returns an array * of successfully loaded skills. Directories that fail to load are * logged as warnings but do not cause the entire scan to fail. * * When a subdirectory has no skill.json, it is treated as a category * directory (e.g. core/, marketplace/) and its children are scanned. * * @param pathOrRelative - Path to the skills directory. Joined with * projectRoot by default; used directly when `options.absolute` is true. * @param options - Optional flags to control path resolution and basePath tagging * @returns Array of loaded skills with their definitions and instructions */ private scanSkillDirectoriesAt; /** * Load a single skill from a directory by reading its SKILL.md file * (YAML frontmatter + markdown body). Falls back to legacy skill.json * + instructions.md format for backward compatibility. * * @param parentDir - Absolute path to the parent skills directory * @param dirName - Name of the skill subdirectory * @returns A LoadedSkill object, or null if loading fails */ private loadSkillFromDirectory; /** * Load a skill from SKILL.md format (YAML frontmatter + markdown body). * * @param skillDir - Absolute path to the skill directory * @param skillMdPath - Path to the SKILL.md file * @param dirName - Name of the skill subdirectory * @returns A LoadedSkill object, or null if loading fails */ private loadSkillFromSkillMd; /** * Load a skill from legacy skill.json + instructions.md format. * * @param skillDir - Absolute path to the skill directory * @param skillJsonPath - Path to the skill.json file * @param dirName - Name of the skill subdirectory * @returns A LoadedSkill object, or null if loading fails */ private loadSkillFromLegacyJson; /** * Group loaded skills by their category field. * * Categories are title-cased for display (e.g., "management" becomes "Management"). * Skills within each category are maintained in their current sorted order. * * @param skills - Array of loaded skills to group * @returns Map of category display name to array of skills in that category */ private groupByCategory; /** * Format a raw category string into a display-friendly title. * * Capitalizes the first letter of each word and replaces hyphens/underscores * with spaces (e.g., "task-management" becomes "Task Management"). * * @param category - Raw category string from skill.json * @returns Formatted category display name */ private formatCategoryName; /** * Render the complete Markdown catalog document from grouped skills. * * Delegates to renderCatalogWithConfig() with orchestrator defaults. * * @param groupedSkills - Map of category name to skills in that category * @returns Complete Markdown document as a string */ private renderCatalog; /** * Render the complete Markdown catalog document from grouped skills * with configurable title and path prefix. * * Produces the full catalog including header, usage instructions, * category sections, and individual skill entries with descriptions * and parameter documentation extracted from instructions.md. * * @param groupedSkills - Map of category name to skills in that category * @param config - Catalog rendering configuration * @param config.title - Catalog title for the Markdown heading * @param config.skillsRelativePath - Relative path prefix for skill usage commands * @returns Complete Markdown document as a string */ private renderCatalogWithConfig; /** * Render a single skill entry as Markdown lines. * * Includes the skill name, description, usage command, and any * Parameters section extracted from the skill's instructions.md. * * @param skill - The loaded skill to render * @param skillsRelativePath - Relative path prefix for usage command * @returns Array of Markdown lines for this skill entry */ private renderSkillEntry; /** * Extract the Parameters section from an instructions.md file's content. * * Uses a line-based approach to find the "## Parameters" heading and capture * all content until the next heading of the same or higher level (# or ##) * or the end of the file. Returns null if no Parameters section is found. * * @param instructionsContent - Full content of the instructions.md file * @returns The Parameters section content (without the heading), or null */ private extractParametersSection; } //# sourceMappingURL=skill-catalog.service.d.ts.map