registry.tsā¢8.56 kB
import { glob } from 'glob';
import * as fs from 'fs-extra';
import * as path from 'path';
import { WorksonaAgent, AgentSuggestion, AgentStats } from './types';
export class AgentRegistry {
private agents = new Map<string, WorksonaAgent>();
private categories = new Map<string, string[]>();
private loadingPromise: Promise<void> | null = null;
constructor(private worksonaAgentsPath: string) {
// Validate path exists
if (!fs.existsSync(worksonaAgentsPath)) {
throw new Error(`Worksona agents path does not exist: ${worksonaAgentsPath}`);
}
}
async loadAllAgents(): Promise<void> {
if (this.loadingPromise) {
return this.loadingPromise;
}
this.loadingPromise = this.performLoad();
return this.loadingPromise;
}
private async performLoad(): Promise<void> {
console.error('š Discovering Worksona agents...');
try {
// Find all metadata.json files
const metadataFiles = await glob('**/metadata.json', {
cwd: this.worksonaAgentsPath,
absolute: true,
ignore: ['**/node_modules/**', '**/dist/**', '**/.git/**']
});
console.error(`š Found ${metadataFiles.length} agent definitions`);
let loadedCount = 0;
for (const metadataPath of metadataFiles) {
try {
await this.loadAgent(metadataPath);
loadedCount++;
} catch (error) {
console.error(`ā ļø Failed to load agent from ${metadataPath}:`, error);
}
}
console.error(`ā
Loaded ${loadedCount} agents across ${this.categories.size} categories`);
this.printAgentSummary();
} catch (error) {
console.error('ā Failed to load agents:', error);
throw error;
}
}
private async loadAgent(metadataPath: string): Promise<void> {
const metadata = await fs.readJSON(metadataPath);
const agentDir = path.dirname(metadataPath);
const agentMdPath = path.join(agentDir, 'agent.md');
// Load agent prompt content
let prompt = '';
if (await fs.pathExists(agentMdPath)) {
prompt = await fs.readFile(agentMdPath, 'utf-8');
}
// Normalize metadata structure - handle both array and object formats
let capabilities;
if (Array.isArray(metadata.capabilities)) {
capabilities = {
primary: metadata.capabilities,
secondary: []
};
} else if (metadata.capabilities && typeof metadata.capabilities === 'object') {
capabilities = {
primary: metadata.capabilities.primary || [],
secondary: metadata.capabilities.secondary || [],
frameworks: metadata.capabilities.frameworks
};
} else {
capabilities = {
primary: [],
secondary: []
};
}
// Create agent definition
const agent: WorksonaAgent = {
name: metadata.name,
version: metadata.version || '1.0.0',
description: metadata.description || 'No description available',
category: this.extractCategory(agentDir),
subcategory: metadata.subcategory,
capabilities,
requirements: metadata.requirements || { tools: [] },
coordination: metadata.coordination,
prompt,
examples: metadata.examples,
triggers: metadata.triggers,
quality: metadata.quality
};
// Register agent
this.agents.set(agent.name, agent);
// Update category index
if (!this.categories.has(agent.category)) {
this.categories.set(agent.category, []);
}
this.categories.get(agent.category)!.push(agent.name);
}
private extractCategory(agentPath: string): string {
// Extract category from path: /path/to/agents/software-engineering/frontend-developer
const parts = agentPath.split(path.sep);
const agentsIndex = parts.findIndex(part => part === 'agents');
return parts[agentsIndex + 1] || 'uncategorized';
}
getAgent(name: string): WorksonaAgent | undefined {
return this.agents.get(name);
}
getAllAgents(): WorksonaAgent[] {
return Array.from(this.agents.values());
}
getAgentsByCategory(category: string): WorksonaAgent[] {
const agentNames = this.categories.get(category) || [];
return agentNames.map(name => this.agents.get(name)!).filter(Boolean);
}
getCategories(): string[] {
return Array.from(this.categories.keys());
}
findAgentsByCapability(capability: string): WorksonaAgent[] {
return this.getAllAgents().filter(agent =>
agent.capabilities.primary.includes(capability) ||
agent.capabilities.secondary?.includes(capability)
);
}
suggestAgentsForRequest(request: string, maxSuggestions = 5): AgentSuggestion[] {
const requestWords = request.toLowerCase().split(/\\s+/);
const suggestions: AgentSuggestion[] = [];
for (const agent of this.getAllAgents()) {
let score = 0;
let reasons: string[] = [];
// Check trigger keywords
if (agent.triggers?.keywords) {
for (const keyword of agent.triggers.keywords) {
if (requestWords.some(word => word.includes(keyword.toLowerCase()) || keyword.toLowerCase().includes(word))) {
score += 15;
reasons.push(`matches keyword "${keyword}"`);
}
}
}
// Check trigger patterns
if (agent.triggers?.patterns) {
for (const pattern of agent.triggers.patterns) {
const regex = new RegExp(pattern.replace('*', '.*'), 'i');
if (regex.test(request)) {
score += 20;
reasons.push(`matches pattern "${pattern}"`);
}
}
}
// Check description and capability words
const agentText = (agent.description + ' ' + agent.capabilities.primary.join(' ') + ' ' + (agent.capabilities.secondary?.join(' ') || '')).toLowerCase();
const agentWords = agentText.split(/\\s+/);
let wordMatches = 0;
for (const word of requestWords) {
if (word.length > 3 && agentWords.some(agentWord => agentWord.includes(word) || word.includes(agentWord))) {
wordMatches++;
}
}
if (wordMatches > 0) {
score += wordMatches * 3;
reasons.push(`${wordMatches} word match(es)`);
}
// Enterprise grade agents get slight boost
if (agent.quality?.enterpriseGrade) {
score += 2;
}
if (score > 5) {
suggestions.push({
agent,
score,
reason: reasons.join(', ')
});
}
}
return suggestions
.sort((a, b) => b.score - a.score)
.slice(0, maxSuggestions);
}
getAgentStats(): AgentStats {
const categoryCounts: Record<string, number> = {};
const capabilityCounts: Record<string, number> = {};
for (const [category, agents] of this.categories.entries()) {
categoryCounts[category] = agents.length;
}
for (const agent of this.getAllAgents()) {
// Safely iterate over capabilities
if (agent.capabilities.primary && Array.isArray(agent.capabilities.primary)) {
for (const capability of agent.capabilities.primary) {
capabilityCounts[capability] = (capabilityCounts[capability] || 0) + 1;
}
}
if (agent.capabilities.secondary && Array.isArray(agent.capabilities.secondary)) {
for (const capability of agent.capabilities.secondary) {
capabilityCounts[capability] = (capabilityCounts[capability] || 0) + 1;
}
}
}
return {
totalAgents: this.agents.size,
categoryCounts,
capabilityCounts
};
}
// Find agents that work well together
findCoordinatingAgents(primaryAgent: string): WorksonaAgent[] {
const agent = this.getAgent(primaryAgent);
if (!agent?.coordination?.worksWellWith) {
return [];
}
return agent.coordination.worksWellWith
.map(name => this.getAgent(name))
.filter(Boolean) as WorksonaAgent[];
}
private printAgentSummary(): void {
console.error('\\nš Agent Registry Summary:');
for (const [category, agents] of this.categories.entries()) {
console.error(` ${category}: ${agents.length} agents`);
}
}
// Validation method
validateAgent(agent: WorksonaAgent): string[] {
const errors: string[] = [];
if (!agent.name) errors.push('Agent name is required');
if (!agent.description) errors.push('Agent description is required');
if (!agent.capabilities.primary.length) errors.push('At least one primary capability is required');
if (!agent.prompt) errors.push('Agent prompt is required');
return errors;
}
}