UNPKG

erosolar-cli

Version:

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

162 lines 5.8 kB
import { existsSync, readFileSync, readdirSync, statSync } from 'node:fs'; import { join, basename } from 'node:path'; import { homedir } from 'node:os'; import { parseFrontmatter } from '../utils/frontmatter.js'; import { getInlineAgentConfig } from './agentConfig.js'; const PROJECT_AGENT_DIR = '.claude/agents'; const USER_AGENT_DIR = '.claude/agents'; export class AgentRegistry { workingDir; inlineAgents; builtIns; agents = new Map(); constructor(options) { this.workingDir = options.workingDir; this.inlineAgents = options.inlineAgents ?? getInlineAgentConfig(); this.builtIns = options.builtIns ?? []; this.refresh(); } refresh() { this.agents.clear(); // Lowest priority: built-in agents for (const agent of this.builtIns) { this.register(agent); } // User-level agents (~/.claude/agents) const userDir = join(homedir(), USER_AGENT_DIR); this.loadFromDirectory(userDir, 'user'); // Inline agents (CLI/env) if (this.inlineAgents) { this.loadInlineAgents(this.inlineAgents); } // Project-level agents (.claude/agents) const projectDir = join(this.workingDir, PROJECT_AGENT_DIR); this.loadFromDirectory(projectDir, 'project'); return this.list(); } list() { return Array.from(this.agents.values()).sort((a, b) => a.name.localeCompare(b.name)); } resolve(name) { const key = normalizeName(name); return this.agents.get(key) ?? null; } register(agent) { const key = normalizeName(agent.name); if (!key) { return; } this.agents.set(key, { ...agent, name: key, }); } loadFromDirectory(dir, source) { if (!existsSync(dir)) { return; } let entries = []; try { entries = readdirSync(dir); } catch { return; } for (const entry of entries) { const fullPath = join(dir, entry); try { const stats = statSync(fullPath); if (stats.isDirectory()) { this.loadFromDirectory(fullPath, source); continue; } if (!entry.toLowerCase().endsWith('.md')) { continue; } const content = readFileSync(fullPath, 'utf8'); const parsed = parseFrontmatter(content); const name = normalizeName(parsed.attributes.name || basename(entry, '.md')); if (!name) { continue; } const description = extractDescription(parsed); const prompt = parsed.body?.trim() || ''; this.register({ name, description, prompt, tools: normalizeStringList(parsed.attributes.tools), model: parsed.attributes.model ? String(parsed.attributes.model) : undefined, permissionMode: parsed.attributes.permissionMode ? String(parsed.attributes.permissionMode) : undefined, skills: normalizeStringList(parsed.attributes.skills), summary: parsed.attributes.summary ? String(parsed.attributes.summary) : undefined, source, path: fullPath, }); } catch (error) { console.warn(`[agent-registry] Failed to load agent from ${fullPath}:`, error); } } } loadInlineAgents(definitions) { for (const [name, config] of Object.entries(definitions)) { const normalized = normalizeName(name); if (!normalized) { continue; } this.register({ name: normalized, description: config.description || `Inline agent ${normalized}`, prompt: config.prompt || '', tools: Array.isArray(config.tools) ? config.tools : config.tools ? [config.tools] : undefined, model: config.model, permissionMode: config.permissionMode, skills: Array.isArray(config.skills) ? config.skills : config.skills ? [config.skills] : undefined, source: 'inline', }); } } } function normalizeName(value) { if (typeof value !== 'string') { return ''; } const trimmed = value.trim(); if (!trimmed) { return ''; } return trimmed.toLowerCase().replace(/\s+/g, '-'); } function normalizeStringList(value) { if (!value) { return undefined; } if (typeof value === 'string') { const trimmed = value.trim(); if (!trimmed) { return undefined; } return trimmed.split(',').map((entry) => entry.trim()).filter(Boolean); } if (Array.isArray(value)) { const normalized = value .filter((entry) => typeof entry === 'string') .map((entry) => entry.trim()) .filter(Boolean); return normalized.length ? normalized : undefined; } return undefined; } function extractDescription(parsed) { const attrDescription = typeof parsed.attributes['description'] === 'string' ? parsed.attributes['description'].trim() : ''; if (attrDescription) { return attrDescription; } const firstLine = parsed.body?.split('\n').find((line) => line.trim().length > 0); if (firstLine) { return firstLine.trim().slice(0, 200); } return 'Custom subagent'; } //# sourceMappingURL=agentRegistry.js.map