import { Inject, Injectable, Logger } from '@nestjs/common'; import type { ToolDefinition } from '@mariozechner/pi-coding-agent'; import { SkillsService } from '../skills/skills.service.js'; import { createSkillTools } from './tools/skill-tools.js'; export interface LoadedSkills { /** Meta-tools: skill_list + skill_invoke */ metaTools: ToolDefinition[]; /** * System prompt additions from enabled prompt-type skills. * Callers may prepend these to the session system prompt. */ promptAdditions: string[]; } /** * SkillLoaderService is responsible for: * 1. Providing the skill meta-tools (skill_list, skill_invoke) to agent sessions. * 2. Collecting system-prompt additions from enabled prompt-type skills. */ @Injectable() export class SkillLoaderService { private readonly logger = new Logger(SkillLoaderService.name); constructor(@Inject(SkillsService) private readonly skillsService: SkillsService) {} /** * Load enabled skills and return tools + prompt additions for a new session. */ async loadForSession(): Promise { const metaTools = createSkillTools(this.skillsService); let promptAdditions: string[] = []; try { const enabledSkills = await this.skillsService.findEnabled(); promptAdditions = enabledSkills.flatMap((skill) => { const config = (skill.config ?? {}) as Record; const skillType = (config['type'] as string | undefined) ?? 'prompt'; if (skillType === 'prompt') { const addition = (config['prompt'] as string | undefined) ?? skill.description; return addition ? [addition] : []; } return []; }); this.logger.log( `Loaded ${enabledSkills.length} enabled skill(s), ` + `${promptAdditions.length} prompt addition(s)`, ); } catch (err) { // Non-fatal: log and continue without prompt additions this.logger.warn( `Failed to load skill prompt additions: ${err instanceof Error ? err.message : String(err)}`, ); } return { metaTools, promptAdditions }; } }