|
| 1 | +import { spinner } from '@clack/prompts' |
| 2 | +import { join } from 'pathe' |
| 3 | +import { x } from 'tinyexec' |
| 4 | + |
| 5 | +// Types from @nuxt/schema (PR 1) - defined locally until schema is updated |
| 6 | +interface ModuleAgentSkillsConfig { |
| 7 | + url: string |
| 8 | + skills?: string | string[] |
| 9 | +} |
| 10 | + |
| 11 | +interface ModuleAgentsConfig { |
| 12 | + skills?: ModuleAgentSkillsConfig |
| 13 | +} |
| 14 | + |
| 15 | +interface ModuleMeta { |
| 16 | + name?: string |
| 17 | + agents?: ModuleAgentsConfig |
| 18 | +} |
| 19 | + |
| 20 | +export interface ModuleSkillInfo { |
| 21 | + url: string |
| 22 | + skills?: string | string[] |
| 23 | + moduleName: string |
| 24 | +} |
| 25 | + |
| 26 | +export async function detectModuleSkills(moduleNames: string[], cwd: string): Promise<ModuleSkillInfo[]> { |
| 27 | + const result: ModuleSkillInfo[] = [] |
| 28 | + |
| 29 | + for (const pkgName of moduleNames) { |
| 30 | + const meta = await getModuleMeta(pkgName, cwd) |
| 31 | + if (meta?.agents?.skills?.url) { |
| 32 | + result.push({ |
| 33 | + url: meta.agents.skills.url, |
| 34 | + skills: meta.agents.skills.skills, |
| 35 | + moduleName: pkgName, |
| 36 | + }) |
| 37 | + } |
| 38 | + } |
| 39 | + return result |
| 40 | +} |
| 41 | + |
| 42 | +async function getModuleMeta(pkgName: string, cwd: string): Promise<ModuleMeta | null> { |
| 43 | + try { |
| 44 | + const modulePath = join(cwd, 'node_modules', pkgName) |
| 45 | + const mod = await import(modulePath) |
| 46 | + return await mod?.default?.getMeta?.() |
| 47 | + } |
| 48 | + catch { |
| 49 | + return null |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +export async function installSkills(infos: ModuleSkillInfo[], cwd: string): Promise<void> { |
| 54 | + for (const info of infos) { |
| 55 | + const skillList = info.skills ? (Array.isArray(info.skills) ? info.skills : [info.skills]) : [] |
| 56 | + const label = skillList.length > 0 ? `Installing ${skillList.join(', ')}...` : `Installing skills from ${info.url}...` |
| 57 | + |
| 58 | + const s = spinner() |
| 59 | + s.start(label) |
| 60 | + |
| 61 | + try { |
| 62 | + const args = ['skills', 'add', info.url, '-y'] |
| 63 | + if (skillList.length > 0) { |
| 64 | + args.push('--skill', ...skillList) |
| 65 | + } |
| 66 | + |
| 67 | + await x('npx', args, { |
| 68 | + nodeOptions: { cwd, stdio: 'pipe' }, |
| 69 | + }) |
| 70 | + |
| 71 | + s.stop('Installed to detected agents') |
| 72 | + } |
| 73 | + catch (error: unknown) { |
| 74 | + const msg = error instanceof Error ? error.message : String(error) |
| 75 | + s.stop('Failed to install skills') |
| 76 | + console.warn(`Skill installation failed: ${msg}`) |
| 77 | + } |
| 78 | + } |
| 79 | +} |
| 80 | + |
| 81 | +export function getSkillNames(infos: ModuleSkillInfo[]): string { |
| 82 | + return infos |
| 83 | + .flatMap((i) => { |
| 84 | + if (!i.skills) { |
| 85 | + return 'all' |
| 86 | + } |
| 87 | + return Array.isArray(i.skills) ? i.skills : [i.skills] |
| 88 | + }) |
| 89 | + .join(', ') |
| 90 | +} |
0 commit comments