UNPKG

@haxtheweb/create

Version:

CLI for all things HAX the web

212 lines (206 loc) 8.19 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.copySkillDir = copySkillDir; exports.installSkills = installSkills; exports.listBundledSkills = listBundledSkills; exports.skillsActions = skillsActions; exports.skillsCommandDetected = skillsCommandDetected; var fs = _interopRequireWildcard(require("node:fs")); var path = _interopRequireWildcard(require("node:path")); var p = _interopRequireWildcard(require("@clack/prompts")); var _picocolors = _interopRequireDefault(require("picocolors")); var _logging = require("../logging.js"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); } /** * @description Actions available for the skills command * @returns Array of action objects */ function skillsActions() { return [{ value: 'list', label: 'List bundled agent skills' }, { value: 'install', label: 'Install bundled skills into .agents/skills/' }]; } /** * @description Returns the path to the bundled skills directory (dist/skills/) * @returns string path to bundled skills */ function getBundledSkillsPath() { // __dirname is dist/lib/programs/ in the built CLI, so skills are at ../../skills/ return path.resolve(__dirname, '../../skills'); } /** * @description Lists all bundled skills by reading SKILL.md frontmatter from dist/skills/ * @returns Array of skill objects with name, description, version */ function listBundledSkills() { const skillsPath = getBundledSkillsPath(); let skills = []; if (fs.existsSync(skillsPath)) { const dirs = fs.readdirSync(skillsPath).filter(d => fs.statSync(path.join(skillsPath, d)).isDirectory() && fs.existsSync(path.join(skillsPath, d, 'SKILL.md'))); for (const dir of dirs) { const skillPath = path.join(skillsPath, dir, 'SKILL.md'); const content = fs.readFileSync(skillPath, 'utf-8'); // parse YAML frontmatter const fmMatch = content.match(/^---\n([\s\S]*?)\n---/); let skill = { name: dir, dir, path: skillPath }; if (fmMatch) { const fm = fmMatch[1]; const nameMatch = fm.match(/^name:\s*(.+)/m); const versionMatch = fm.match(/^version:\s*(.+)/m); const descMatch = fm.match(/description:\s*>\s*\n([\s\S]*?)(?=\n[a-z]|\n---)/m); if (nameMatch) skill.name = nameMatch[1].trim(); if (versionMatch) skill.version = versionMatch[1].trim(); if (descMatch) { skill.description = descMatch[1].replace(/^\s+/gm, ' ').trim(); } } skills.push(skill); } } return skills; } /** * @description Copies a skill directory recursively to a target * @param src source skill directory * @param dest destination skill directory */ function copySkillDir(src, dest) { if (!fs.existsSync(dest)) { fs.mkdirSync(dest, { recursive: true }); } const entries = fs.readdirSync(src, { withFileTypes: true }); for (const entry of entries) { const srcPath = path.join(src, entry.name); const destPath = path.join(dest, entry.name); if (entry.isDirectory()) { copySkillDir(srcPath, destPath); } else { fs.copyFileSync(srcPath, destPath); } } } /** * @description Installs one or all bundled skills into a target .agents/skills/ directory * @param skills list of bundled skill objects * @param skillName name of skill to install, or 'all' for all * @param targetPath target directory (default ~/.agents/skills/) */ function installSkills(skills, skillName, targetPath) { const bundledPath = getBundledSkillsPath(); if (!targetPath) { targetPath = path.join(process.env.HOME || process.env.USERPROFILE, '.agents', 'skills'); } if (!fs.existsSync(targetPath)) { fs.mkdirSync(targetPath, { recursive: true }); } let toInstall = skills; if (skillName && skillName !== 'all') { toInstall = skills.filter(s => s.name === skillName || s.dir === skillName); if (toInstall.length === 0) { (0, _logging.log)(`Skill "${skillName}" not found in bundled skills`, 'error'); return []; } } let installed = []; for (const skill of toInstall) { const srcDir = path.join(bundledPath, skill.dir); const destDir = path.join(targetPath, skill.dir); copySkillDir(srcDir, destDir); installed.push(skill); if (!process.env.haxquiet) { p.intro(`${_picocolors.default.bgGreen(_picocolors.default.black(` ✓ Installed: ${skill.name}${destDir} `))}`); } } return installed; } /** * @description Runs the skills command, called when `hax skills` is run * @param commandRun CLI command state */ async function skillsCommandDetected(commandRun) { const action = commandRun.arguments && commandRun.arguments.action; const skills = listBundledSkills(); if (skills.length === 0) { const bundledPath = getBundledSkillsPath(); if (!process.env.haxquiet) { p.intro(`${_picocolors.default.bgBlack(_picocolors.default.white(` No bundled skills found at ${bundledPath} `))}`); } return; } if (action === 'list') { if (commandRun.options && commandRun.options.format === 'json') { const out = skills.map(s => ({ name: s.name, version: s.version, description: s.description })); console.log(JSON.stringify(out, null, 2)); } else { p.intro(`${_picocolors.default.bgBlack(_picocolors.default.white(` Bundled agent skills (${skills.length}) `))}`); for (const skill of skills) { p.intro(`${_picocolors.default.cyan(skill.name)} v${skill.version || '?'}${skill.description || ''}`); } p.outro(`Install with: ${_picocolors.default.bold('hax skills install --all')} or ${_picocolors.default.bold('hax skills install <name>')}`); } } else if (action === 'install') { let skillName = commandRun.options && commandRun.options.skillName; const targetPath = commandRun.options && commandRun.options.path; if (!skillName) { if (commandRun.options && commandRun.options.y) { skillName = 'all'; } else if (commandRun.options && commandRun.options.i) { (0, _logging.log)('Must specify --skill-name <name> or --all for non-interactive install', 'error'); return; } else { const choices = [{ value: 'all', label: 'All bundled skills' }, ...skills.map(s => ({ value: s.name, label: `${s.name} v${s.version || '?'}` }))]; skillName = await p.select({ message: 'Which skill(s) to install?', options: choices }); } } const installed = installSkills(skills, skillName, targetPath); if (!process.env.haxquiet) { p.outro(`${_picocolors.default.green(installed.length)} skill(s) installed`); } } else { // no action specified — show list if (commandRun.options && commandRun.options.format === 'json') { const out = skills.map(s => ({ name: s.name, version: s.version, description: s.description })); console.log(JSON.stringify(out, null, 2)); } else { p.intro(`${_picocolors.default.bgBlack(_picocolors.default.white(` Bundled agent skills (${skills.length}) `))}`); for (const skill of skills) { p.intro(`${_picocolors.default.cyan(skill.name)} v${skill.version || '?'}${skill.description || ''}`); } p.outro(`Install with: ${_picocolors.default.bold('hax skills install --all')} or ${_picocolors.default.bold('hax skills install <name>')}`); } } }