UNPKG

aiwg

Version:

Deployment tool and support utility for AI context. Copies agents, skills, commands, rules, and behaviors into the paths each AI platform reads (Claude Code, Codex, Copilot, Cursor, Warp, OpenClaw, and 6 more) so one source of truth works across 10 platfo

334 lines 12.3 kB
/** * Skills CLI Commands * * Provides CLI interface for multi-registry skill operations: * - search: Search skills across registries * - info: Show detailed skill information * - list: List all available skills * - install: Install a skill from a registry * - publish: Publish a skill to a registry * * @implements #539 */ import { spawn } from 'child_process'; import { searchSkills, listSkills, getSkillInfo, installSkill, publishSkill, getAllAdapters, } from './registry.js'; import { parseAgentSpawnFlags, buildAgentArgs, getProviderConfig, isSpawnableProvider, } from '../cli/agent-spawn.js'; const SUPPORTED_TARGETS = [ 'claude', 'copilot', 'factory', 'cursor', 'codex', 'opencode', 'warp', 'windsurf', 'openclaw', 'hermes', 'generic', ]; /** * Parse --provider and --target flags from args */ function parseFlags(args) { const rest = []; let provider; let target; for (let i = 0; i < args.length; i++) { if (args[i] === '--provider' && i + 1 < args.length) { provider = args[i + 1]; i++; } else if (args[i] === '--target' && i + 1 < args.length) { target = args[i + 1]; i++; } else { rest.push(args[i]); } } return { provider, target, rest }; } /** * Format skill results as a table */ function displaySkillsTable(skills) { if (skills.length === 0) { console.log(' No skills found.'); return; } // Group by source const bySource = new Map(); for (const skill of skills) { const list = bySource.get(skill.source) || []; list.push(skill); bySource.set(skill.source, list); } for (const [source, sourceSkills] of bySource) { console.log(`\n ${source} (${sourceSkills.length} skills):`); console.log(' ' + '-'.repeat(70)); for (const skill of sourceSkills) { const name = skill.name.padEnd(28); const pkg = (skill.package || '').padEnd(18); const desc = skill.description.slice(0, 40); console.log(` ${name} ${pkg} ${desc}`); } } } /** * Handle 'skills search' command */ async function handleSearch(args) { const { provider, rest } = parseFlags(args); const query = rest.join(' '); if (!query) { console.error('Error: Search query required'); console.log('Usage: aiwg skills search <query> [--provider <registry>]'); process.exit(1); } const results = await searchSkills(query, provider); console.log(''); console.log(`Search results for "${query}"${provider ? ` (${provider})` : ''}:`); displaySkillsTable(results); console.log(''); console.log(` ${results.length} skill(s) found`); console.log(''); } /** * Handle 'skills info' command */ async function handleInfo(args) { const { provider, rest } = parseFlags(args); const name = rest[0]; if (!name) { console.error('Error: Skill name required'); console.log('Usage: aiwg skills info <name> [--provider <registry>]'); process.exit(1); } const details = await getSkillInfo(name, provider); if (!details) { console.error(`Error: Skill '${name}' not found`); console.log("Run 'aiwg skills search <query>' to find skills"); process.exit(1); } console.log(''); console.log(`Skill: ${details.name}`); console.log(''); console.log(`Source: ${details.source}`); if (details.package) console.log(`Package: ${details.package}`); if (details.version) console.log(`Version: ${details.version}`); console.log(`Description: ${details.description}`); if (details.platforms && details.platforms.length > 0) { console.log(`Platforms: ${details.platforms.join(', ')}`); } if (details.triggers && details.triggers.length > 0) { console.log(''); console.log('Triggers:'); for (const trigger of details.triggers) { console.log(` - ${trigger}`); } } if (details.scripts && details.scripts.length > 0) { console.log(''); console.log('Scripts:'); for (const script of details.scripts) { console.log(` - ${script}`); } } if (details.path) { console.log(''); console.log(`Path: ${details.path}`); } console.log(''); } /** * Handle 'skills list' command */ async function handleList(args) { const { provider } = parseFlags(args); const results = await listSkills(provider); console.log(''); console.log(`Available skills${provider ? ` (${provider})` : ''}:`); displaySkillsTable(results); console.log(''); console.log(` ${results.length} skill(s) total`); // Show registry availability const adapters = getAllAdapters(); console.log(''); console.log(' Registries:'); for (const adapter of adapters) { const available = await adapter.isAvailable(); const status = available ? '\u2713' : '\u2013'; console.log(` ${status} ${adapter.name} (${adapter.id})`); } console.log(''); } /** * Handle 'skills install' command * * Supports cross-platform deployment: * aiwg skills install <name> --provider <registry> --target <platform> * * --provider: which registry to pull from (local, clawhub, openclaw) * --target: which platform to deploy to (claude, copilot, cursor, etc.) * Defaults to claude. Uses platform-aware path translation. */ async function handleInstall(args) { const { provider, target, rest } = parseFlags(args); const name = rest[0]; if (!name) { console.error('Error: Skill name required'); console.log('Usage: aiwg skills install <name> [--provider <registry>] [--target <platform>]'); console.log(''); console.log('Options:'); console.log(' --provider Registry to pull from (local, clawhub, openclaw). Default: local'); console.log(' --target Platform to deploy to (claude, copilot, cursor, etc.). Default: claude'); console.log(''); console.log('Examples:'); console.log(' aiwg skills install parallel-dispatch'); console.log(' aiwg skills install parallel-dispatch --target copilot'); console.log(' aiwg skills install my-skill --provider clawhub --target cursor'); console.log(''); console.log(`Supported targets: ${SUPPORTED_TARGETS.join(', ')}`); process.exit(1); } const registry = provider || 'local'; const targetPlatform = target || 'claude'; if (target && !SUPPORTED_TARGETS.includes(target)) { console.error(`Error: Unknown target platform '${target}'`); console.log(`Supported: ${SUPPORTED_TARGETS.join(', ')}`); process.exit(1); } try { await installSkill(name, { projectDir: process.cwd(), target: targetPlatform }, registry); console.log(`Installed skill '${name}' from ${registry}${targetPlatform}`); } catch (error) { console.error(`Error: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } } /** * Handle 'skills publish' command */ async function handlePublish(args) { const { provider, rest } = parseFlags(args); const packageDir = rest[0] || process.cwd(); if (!provider) { console.error('Error: --provider required'); console.log('Usage: aiwg skills publish [dir] --provider <registry>'); console.log(''); console.log('Example:'); console.log(' aiwg skills publish --provider clawhub'); process.exit(1); } try { await publishSkill(packageDir, provider); console.log(`Published skill to ${provider}`); } catch (error) { console.error(`Error: ${error instanceof Error ? error.message : String(error)}`); process.exit(1); } } /** * Handle 'skills run' command * * Looks up a skill by name, substitutes $ARGUMENTS, and dispatches * the full skill prompt to the configured provider for execution. * * Always routes through a provider — no local execution fallback. * * @implements roctinam/aiwg#630 */ async function handleRun(args) { const { opts, remaining } = parseAgentSpawnFlags(args); const skillName = remaining[0]; const skillArgs = remaining.slice(1); if (!skillName) { console.error('Error: Skill name required'); console.log('Usage: aiwg skills run <skill-name> [args...] [--provider <provider>]'); console.log(''); console.log('Examples:'); console.log(' aiwg skills run workspace-health'); console.log(' aiwg skills run ralph "Fix failing tests" --completion "npm test passes"'); console.log(' aiwg skills run doc-sync --dry-run --provider claude'); process.exit(1); } const details = await getSkillInfo(skillName); if (!details) { console.error(`Error: Skill '${skillName}' not found`); console.log("Run 'aiwg skills list' to see available skills"); process.exit(1); } if (!details.content) { console.error(`Error: Skill '${skillName}' has no executable content`); process.exit(1); } // Substitute $ARGUMENTS with the provided args const argString = skillArgs.join(' '); const prompt = details.content.replace(/\$ARGUMENTS/g, argString); // Resolve provider — default to claude const providerName = opts.provider ?? 'claude'; if (!isSpawnableProvider(providerName)) { const config = getProviderConfig(providerName); console.error(`Error: Provider '${config.name}' cannot be spawned from the CLI.`); if (config.guidanceMessage) { console.log(''); console.log(config.guidanceMessage); } process.exit(1); } const config = getProviderConfig(providerName); const spawnArgs = buildAgentArgs(prompt, opts); console.log(`Running skill '${skillName}' via ${config.name}...`); console.log(''); const child = spawn(config.binary, spawnArgs, { stdio: 'inherit' }); child.on('error', (err) => { console.error(`Error: Failed to spawn ${config.binary}: ${err.message}`); process.exit(1); }); child.on('close', (code) => { process.exit(code ?? 1); }); } /** * Main skills command router */ export async function main(args) { const subcommand = args[0]; const subcommandArgs = args.slice(1); switch (subcommand) { case 'run': await handleRun(subcommandArgs); break; case 'search': await handleSearch(subcommandArgs); break; case 'info': await handleInfo(subcommandArgs); break; case 'list': await handleList(subcommandArgs); break; case 'install': await handleInstall(subcommandArgs); break; case 'publish': await handlePublish(subcommandArgs); break; case undefined: console.error('Error: Skills subcommand required'); console.log('Available: run, search, info, list, install, publish'); console.log(''); console.log('Examples:'); console.log(' aiwg skills run workspace-health'); console.log(' aiwg skills run ralph "Fix failing tests" --completion "npm test passes"'); console.log(' aiwg skills list'); console.log(' aiwg skills search "parallel"'); console.log(' aiwg skills info parallel-dispatch'); console.log(' aiwg skills search "testing" --provider clawhub'); console.log(' aiwg skills install parallel-dispatch'); console.log(' aiwg skills install parallel-dispatch --target copilot'); console.log(' aiwg skills install my-skill --provider clawhub --target cursor'); process.exit(1); break; default: console.error(`Error: Unknown skills subcommand '${subcommand}'`); console.log('Available: run, search, info, list, install, publish'); process.exit(1); } } //# sourceMappingURL=cli.js.map