()
| 16 | import { seedBundledSkills } from '../skills/seed.js'; |
| 17 | |
| 18 | export function buildSkillCommand(): Command { |
| 19 | const cmd = new Command('skill'); |
| 20 | cmd.description('Manage installed skills (taste, ui-ux-pro-max, ghost, OODA, etc.)'); |
| 21 | |
| 22 | cmd |
| 23 | .command('list') |
| 24 | .alias('ls') |
| 25 | .description('List every installed skill with origin, version, and enabled state') |
| 26 | .action(async () => { |
| 27 | // Seed bundled skills on first invocation so `qodex skill list` immediately |
| 28 | // surfaces the defaults without forcing the user to launch the REPL first. |
| 29 | await seedBundledSkills(); |
| 30 | await initSkillRegistry(process.cwd()); |
| 31 | const all = await listAllSkillsWithState(process.cwd()); |
| 32 | if (all.length === 0) { |
| 33 | console.log('No skills installed. Try: qodex skill install gh:qodex-skills/taste'); |
| 34 | return; |
| 35 | } |
| 36 | console.log(`${all.length} skill(s):\n`); |
| 37 | for (const s of all) { |
| 38 | const flag = s.enabled ? '●' : '○'; |
| 39 | const ver = s.version ? ` v${s.version}` : ''; |
| 40 | const aliases = s.slashAliases?.length ? ` slash: ${s.slashAliases.map(a => '/' + a).join(' ')}` : ''; |
| 41 | console.log(` ${flag} ${s.name}${ver} [${s.origin}]${aliases}`); |
| 42 | console.log(` ${s.description}`); |
| 43 | } |
| 44 | console.log('\nLegend: ● enabled, ○ disabled'); |
| 45 | }); |
| 46 | |
| 47 | cmd |
| 48 | .command('install <source>') |
| 49 | .description('Install a skill. Sources: ./path, file.tgz, gh:user/repo[@ref], npm:<pkg>') |
| 50 | .option('-f, --force', 'Overwrite if a skill with the same name is already installed') |
| 51 | .action(async (source: string, opts: { force?: boolean }) => { |
| 52 | try { |
| 53 | const result = await installSkill(source, { force: opts.force }); |
| 54 | console.log(`✓ Installed skill "${result.name}" from ${result.source}`); |
| 55 | console.log(` → ${result.installedTo}`); |
| 56 | await refreshSkillRegistry(); |
| 57 | } catch (e: any) { |
| 58 | console.error(`✗ ${e.message}`); |
| 59 | process.exit(1); |
| 60 | } |
| 61 | }); |
| 62 | |
| 63 | cmd |
| 64 | .command('install-all <source>') |
| 65 | .description('Install EVERY skill in a multi-skill repo OR every linked skill in a catalog repo (e.g. gh:abubakarsiddik31/claude-skills-collection)') |
| 66 | .option('-f, --force', 'Overwrite skills already installed') |
| 67 | .option('--max <n>', 'Cap the number of skills installed', (v) => parseInt(v, 10)) |
| 68 | .action(async (source: string, opts: { force?: boolean; max?: number }) => { |
| 69 | try { |
| 70 | const { installAll } = await import('../skills/bulk-installer.js'); |
| 71 | const result = await installAll(source, { |
| 72 | force: opts.force, |
| 73 | maxSkills: opts.max, |
| 74 | onProgress: (m) => console.log(` ${m}`), |
| 75 | }); |
no test coverage detected