UNPKG

erosolar-cli

Version:

Unified AI agent framework for the command line - Multi-provider support with schema-driven tools, code intelligence, and transparent reasoning

178 lines 7.12 kB
export function createSkillTools(options) { const repository = options.repository; return [ { name: 'ListSkills', description: 'List Claude Code compatible skills discovered in the workspace, ~/.claude/skills, and ~/.erosolar/skills directories.', parameters: { type: 'object', properties: { query: { type: 'string', description: 'Optional substring to filter skills by name, namespace, or description.', }, refresh_cache: { type: 'boolean', description: 'When true, force a re-scan of skill directories before listing.', }, }, }, handler: async (args) => { if (args && typeof args['refresh_cache'] === 'boolean' && args['refresh_cache']) { repository.refresh(); } const query = typeof args?.['query'] === 'string' ? args['query'].trim().toLowerCase() : ''; const skills = repository .listSkills() .filter((skill) => skillMatches(skill, query)) .sort((a, b) => a.name.localeCompare(b.name)); if (!skills.length) { return query ? `No skills matched "${query}". Add SKILL.md files under skills/ or ~/.claude/skills and rerun the command.` : 'No skills found. Create a skills/ directory with SKILL.md files or import Claude Code plugin skills.'; } const lines = []; lines.push(`Discovered ${skills.length} skill${skills.length === 1 ? '' : 's'}:`); for (const skill of skills) { lines.push(formatSkillSummary(skill)); } return lines.join('\n'); }, }, { name: 'Skill', description: 'Load a Claude Skill package by name, slug, or path. Returns metadata, documentation body, and optional resource listings.', parameters: { type: 'object', properties: { skill: { type: 'string', description: 'Skill name, slug (kebab-case), namespace-qualified id (e.g. plugin-dev:skill-development), or path to SKILL.md.', }, sections: { type: 'array', description: 'Optional list of sections to include. Defaults to all sections.', items: { type: 'string', enum: ['metadata', 'body', 'references', 'scripts', 'assets'], }, }, refresh_cache: { type: 'boolean', description: 'When true, force a re-scan of skills before loading.', }, }, required: ['skill'], }, handler: async (args) => { if (args && typeof args['refresh_cache'] === 'boolean' && args['refresh_cache']) { repository.refresh(); } const identifier = String(args?.['skill'] ?? '').trim(); if (!identifier) { return 'Skill identifier is required.'; } const skill = repository.getSkill(identifier); if (!skill) { return `Skill "${identifier}" not found. Run ListSkills to inspect available skills.`; } const sections = normalizeSections(args?.['sections']); // Use simplified format for basic Skill type const output = formatBasicSkillDetail(skill, sections); return output || `Skill "${skill.name}" has no content.`; }, }, ]; } function skillMatches(skill, query) { if (!query) { return true; } const haystack = [ skill.id, skill.slug, skill.name, skill.description, skill.namespace ?? '', skill.relativeLocation ?? '', ] .join(' ') .toLowerCase(); return haystack.includes(query); } function normalizeSections(value) { if (!Array.isArray(value) || !value.length) { return new Set(['metadata', 'body', 'references', 'scripts', 'assets']); } const normalized = new Set(); for (const entry of value) { if (typeof entry !== 'string') { continue; } const key = entry.trim().toLowerCase(); if (key === 'metadata' || key === 'body' || key === 'references' || key === 'scripts' || key === 'assets') { normalized.add(key); } } if (!normalized.size) { normalized.add('metadata'); normalized.add('body'); } return normalized; } function formatSkillSummary(skill) { const namespace = skill.namespace ? `${skill.namespace}:` : ''; const label = `${namespace}${skill.slug}`; const location = skill.relativeLocation ?? skill.location; const resourceStatus = [ `Body ${skill.hasBody ? '✅' : '—'}`, `References ${skill.hasReferences ? '✅' : '—'}`, `Scripts ${skill.hasScripts ? '✅' : '—'}`, `Assets ${skill.hasAssets ? '✅' : '—'}`, ].join(' | '); const lines = [ `- ${label}${skill.description}`, ` Source: ${skill.sourceLabel} • Path: ${location}`, ` ${resourceStatus}`, ]; return lines.join('\n'); } /** * Simplified skill detail formatter for the basic Skill type from skillRepository */ function formatBasicSkillDetail(skill, sections) { const lines = []; lines.push(`# Skill: ${skill.name}`); lines.push(`ID: ${skill.id}`); lines.push(''); if (sections.has('metadata')) { lines.push('## Metadata'); lines.push(`- Description: ${skill.description}`); lines.push(`- Category: ${skill.category}`); lines.push(`- Tags: ${skill.tags.join(', ')}`); lines.push(''); } if (sections.has('body')) { lines.push('## Implementation'); lines.push(`Pattern: ${skill.implementation.pattern}`); if (skill.implementation.template) { lines.push(''); lines.push('Template:'); lines.push(skill.implementation.template); } lines.push(''); } if (sections.has('references') && skill.examples.length > 0) { lines.push('## Examples'); for (const example of skill.examples) { lines.push(`### ${example.name}`); lines.push(example.description); lines.push('```'); lines.push(example.code); lines.push('```'); lines.push(''); } } return lines.join('\n').trim(); } //# sourceMappingURL=skillTools.js.map