UNPKG

claude-flow

Version:

Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration

605 lines (531 loc) 16.6 kB
/** * V3 Plugin Registry * Domain-Driven Design - Plugin-Based Architecture (ADR-004) * * Central registry for tracking plugin state, metadata, and registrations */ import type { ClaudeFlowPlugin, PluginInfo, PluginLifecycleState, PluginContext, AgentTypeDefinition, TaskTypeDefinition, MCPToolDefinition, CLICommandDefinition, MemoryBackendFactory, } from './plugin-interface.js'; import { PluginError } from './plugin-interface.js'; /** * Plugin registry for managing plugin lifecycle and registrations */ export class PluginRegistry { private plugins = new Map<string, PluginInfo>(); private agentTypes = new Map<string, { plugin: string; definition: AgentTypeDefinition }>(); private taskTypes = new Map<string, { plugin: string; definition: TaskTypeDefinition }>(); private mcpTools = new Map<string, { plugin: string; definition: MCPToolDefinition }>(); private cliCommands = new Map<string, { plugin: string; definition: CLICommandDefinition }>(); private memoryBackends = new Map<string, { plugin: string; factory: MemoryBackendFactory }>(); /** * Register a plugin in the registry */ registerPlugin( plugin: ClaudeFlowPlugin, initialState: PluginLifecycleState, context: PluginContext ): void { if (this.plugins.has(plugin.name)) { throw new PluginError( `Plugin '${plugin.name}' is already registered`, plugin.name, 'DUPLICATE_PLUGIN' ); } const info: PluginInfo = { plugin, state: initialState, context, metrics: { agentTypesRegistered: 0, taskTypesRegistered: 0, mcpToolsRegistered: 0, cliCommandsRegistered: 0, memoryBackendsRegistered: 0, }, }; this.plugins.set(plugin.name, info); } /** * Unregister a plugin from the registry */ unregisterPlugin(pluginName: string): boolean { const info = this.plugins.get(pluginName); if (!info) { return false; } // Unregister all plugin's registrations this.unregisterPluginAgentTypes(pluginName); this.unregisterPluginTaskTypes(pluginName); this.unregisterPluginMCPTools(pluginName); this.unregisterPluginCLICommands(pluginName); this.unregisterPluginMemoryBackends(pluginName); // Remove plugin return this.plugins.delete(pluginName); } /** * Get a plugin by name */ getPlugin(pluginName: string): PluginInfo | undefined { return this.plugins.get(pluginName); } /** * Get all registered plugins */ getAllPlugins(): Map<string, PluginInfo> { return new Map(this.plugins); } /** * Get all plugin names */ getPluginNames(): string[] { return Array.from(this.plugins.keys()); } /** * Check if a plugin is registered */ hasPlugin(pluginName: string): boolean { return this.plugins.has(pluginName); } /** * Get plugins by state */ getPluginsByState(state: PluginLifecycleState): PluginInfo[] { return Array.from(this.plugins.values()).filter((info) => info.state === state); } /** * Get plugin count */ getPluginCount(): number { return this.plugins.size; } /** * Update plugin state */ updatePluginState(pluginName: string, state: PluginLifecycleState, error?: Error): void { const info = this.plugins.get(pluginName); if (!info) { throw new PluginError( `Plugin '${pluginName}' not found`, pluginName, 'INVALID_PLUGIN' ); } info.state = state; if (state === 'initialized') { info.initializedAt = new Date(); } else if (state === 'shutdown') { info.shutdownAt = new Date(); } else if (state === 'error') { info.error = error; } } /** * Collect and update plugin metrics */ collectPluginMetrics(pluginName: string): void { const info = this.plugins.get(pluginName); if (!info || !info.metrics) { return; } const plugin = info.plugin; // Count registered items info.metrics.agentTypesRegistered = plugin.registerAgentTypes?.()?.length || 0; info.metrics.taskTypesRegistered = plugin.registerTaskTypes?.()?.length || 0; info.metrics.mcpToolsRegistered = plugin.registerMCPTools?.()?.length || 0; info.metrics.cliCommandsRegistered = plugin.registerCLICommands?.()?.length || 0; info.metrics.memoryBackendsRegistered = plugin.registerMemoryBackends?.()?.length || 0; } /** * Get plugin status summary */ getStatusSummary(): PluginRegistryStatus { const states = Array.from(this.plugins.values()).reduce((acc, info) => { acc[info.state] = (acc[info.state] || 0) + 1; return acc; }, {} as Record<PluginLifecycleState, number>); return { totalPlugins: this.plugins.size, states, agentTypesRegistered: this.agentTypes.size, taskTypesRegistered: this.taskTypes.size, mcpToolsRegistered: this.mcpTools.size, cliCommandsRegistered: this.cliCommands.size, memoryBackendsRegistered: this.memoryBackends.size, }; } // ============================================================================= // Agent Type Registry // ============================================================================= /** * Register agent types from a plugin */ registerAgentTypes(pluginName: string): void { const info = this.plugins.get(pluginName); if (!info) { throw new PluginError( `Plugin '${pluginName}' not found`, pluginName, 'INVALID_PLUGIN' ); } const agentTypes = info.plugin.registerAgentTypes?.(); if (!agentTypes || agentTypes.length === 0) { return; } for (const definition of agentTypes) { if (this.agentTypes.has(definition.type)) { throw new PluginError( `Agent type '${definition.type}' is already registered by plugin '${this.agentTypes.get(definition.type)?.plugin}'`, pluginName, 'DUPLICATE_PLUGIN' ); } this.agentTypes.set(definition.type, { plugin: pluginName, definition }); } } /** * Unregister agent types from a plugin */ unregisterPluginAgentTypes(pluginName: string): void { for (const [type, entry] of Array.from(this.agentTypes.entries())) { if (entry.plugin === pluginName) { this.agentTypes.delete(type); } } } /** * Get agent type definition */ getAgentType(type: string): AgentTypeDefinition | undefined { return this.agentTypes.get(type)?.definition; } /** * Get all agent types */ getAllAgentTypes(): AgentTypeDefinition[] { return Array.from(this.agentTypes.values()).map((entry) => entry.definition); } /** * Get agent types by plugin */ getAgentTypesByPlugin(pluginName: string): AgentTypeDefinition[] { return Array.from(this.agentTypes.values()) .filter((entry) => entry.plugin === pluginName) .map((entry) => entry.definition); } // ============================================================================= // Task Type Registry // ============================================================================= /** * Register task types from a plugin */ registerTaskTypes(pluginName: string): void { const info = this.plugins.get(pluginName); if (!info) { throw new PluginError( `Plugin '${pluginName}' not found`, pluginName, 'INVALID_PLUGIN' ); } const taskTypes = info.plugin.registerTaskTypes?.(); if (!taskTypes || taskTypes.length === 0) { return; } for (const definition of taskTypes) { if (this.taskTypes.has(definition.type)) { throw new PluginError( `Task type '${definition.type}' is already registered by plugin '${this.taskTypes.get(definition.type)?.plugin}'`, pluginName, 'DUPLICATE_PLUGIN' ); } this.taskTypes.set(definition.type, { plugin: pluginName, definition }); } } /** * Unregister task types from a plugin */ unregisterPluginTaskTypes(pluginName: string): void { for (const [type, entry] of Array.from(this.taskTypes.entries())) { if (entry.plugin === pluginName) { this.taskTypes.delete(type); } } } /** * Get task type definition */ getTaskType(type: string): TaskTypeDefinition | undefined { return this.taskTypes.get(type)?.definition; } /** * Get all task types */ getAllTaskTypes(): TaskTypeDefinition[] { return Array.from(this.taskTypes.values()).map((entry) => entry.definition); } /** * Get task types by plugin */ getTaskTypesByPlugin(pluginName: string): TaskTypeDefinition[] { return Array.from(this.taskTypes.values()) .filter((entry) => entry.plugin === pluginName) .map((entry) => entry.definition); } // ============================================================================= // MCP Tool Registry // ============================================================================= /** * Register MCP tools from a plugin */ registerMCPTools(pluginName: string): void { const info = this.plugins.get(pluginName); if (!info) { throw new PluginError( `Plugin '${pluginName}' not found`, pluginName, 'INVALID_PLUGIN' ); } const mcpTools = info.plugin.registerMCPTools?.(); if (!mcpTools || mcpTools.length === 0) { return; } for (const definition of mcpTools) { if (this.mcpTools.has(definition.name)) { throw new PluginError( `MCP tool '${definition.name}' is already registered by plugin '${this.mcpTools.get(definition.name)?.plugin}'`, pluginName, 'DUPLICATE_PLUGIN' ); } // Add plugin metadata definition.pluginName = pluginName; this.mcpTools.set(definition.name, { plugin: pluginName, definition }); } } /** * Unregister MCP tools from a plugin */ unregisterPluginMCPTools(pluginName: string): void { for (const [name, entry] of Array.from(this.mcpTools.entries())) { if (entry.plugin === pluginName) { this.mcpTools.delete(name); } } } /** * Get MCP tool definition */ getMCPTool(name: string): MCPToolDefinition | undefined { return this.mcpTools.get(name)?.definition; } /** * Get all MCP tools */ getAllMCPTools(): MCPToolDefinition[] { return Array.from(this.mcpTools.values()).map((entry) => entry.definition); } /** * Get MCP tools by plugin */ getMCPToolsByPlugin(pluginName: string): MCPToolDefinition[] { return Array.from(this.mcpTools.values()) .filter((entry) => entry.plugin === pluginName) .map((entry) => entry.definition); } // ============================================================================= // CLI Command Registry // ============================================================================= /** * Register CLI commands from a plugin */ registerCLICommands(pluginName: string): void { const info = this.plugins.get(pluginName); if (!info) { throw new PluginError( `Plugin '${pluginName}' not found`, pluginName, 'INVALID_PLUGIN' ); } const cliCommands = info.plugin.registerCLICommands?.(); if (!cliCommands || cliCommands.length === 0) { return; } for (const definition of cliCommands) { if (this.cliCommands.has(definition.name)) { throw new PluginError( `CLI command '${definition.name}' is already registered by plugin '${this.cliCommands.get(definition.name)?.plugin}'`, pluginName, 'DUPLICATE_PLUGIN' ); } this.cliCommands.set(definition.name, { plugin: pluginName, definition }); // Register aliases if (definition.aliases) { for (const alias of definition.aliases) { if (this.cliCommands.has(alias)) { throw new PluginError( `CLI command alias '${alias}' is already registered`, pluginName, 'DUPLICATE_PLUGIN' ); } this.cliCommands.set(alias, { plugin: pluginName, definition }); } } } } /** * Unregister CLI commands from a plugin */ unregisterPluginCLICommands(pluginName: string): void { for (const [name, entry] of Array.from(this.cliCommands.entries())) { if (entry.plugin === pluginName) { this.cliCommands.delete(name); } } } /** * Get CLI command definition */ getCLICommand(name: string): CLICommandDefinition | undefined { return this.cliCommands.get(name)?.definition; } /** * Get all CLI commands */ getAllCLICommands(): CLICommandDefinition[] { const seen = new Set<CLICommandDefinition>(); return Array.from(this.cliCommands.values()) .map((entry) => entry.definition) .filter((def) => { if (seen.has(def)) return false; seen.add(def); return true; }); } /** * Get CLI commands by plugin */ getCLICommandsByPlugin(pluginName: string): CLICommandDefinition[] { const seen = new Set<CLICommandDefinition>(); return Array.from(this.cliCommands.values()) .filter((entry) => entry.plugin === pluginName) .map((entry) => entry.definition) .filter((def) => { if (seen.has(def)) return false; seen.add(def); return true; }); } // ============================================================================= // Memory Backend Registry // ============================================================================= /** * Register memory backends from a plugin */ registerMemoryBackends(pluginName: string): void { const info = this.plugins.get(pluginName); if (!info) { throw new PluginError( `Plugin '${pluginName}' not found`, pluginName, 'INVALID_PLUGIN' ); } const memoryBackends = info.plugin.registerMemoryBackends?.(); if (!memoryBackends || memoryBackends.length === 0) { return; } for (const factory of memoryBackends) { if (this.memoryBackends.has(factory.name)) { throw new PluginError( `Memory backend '${factory.name}' is already registered by plugin '${this.memoryBackends.get(factory.name)?.plugin}'`, pluginName, 'DUPLICATE_PLUGIN' ); } this.memoryBackends.set(factory.name, { plugin: pluginName, factory }); } } /** * Unregister memory backends from a plugin */ unregisterPluginMemoryBackends(pluginName: string): void { for (const [name, entry] of Array.from(this.memoryBackends.entries())) { if (entry.plugin === pluginName) { this.memoryBackends.delete(name); } } } /** * Get memory backend factory */ getMemoryBackend(name: string): MemoryBackendFactory | undefined { return this.memoryBackends.get(name)?.factory; } /** * Get all memory backends */ getAllMemoryBackends(): MemoryBackendFactory[] { return Array.from(this.memoryBackends.values()).map((entry) => entry.factory); } /** * Get memory backends by plugin */ getMemoryBackendsByPlugin(pluginName: string): MemoryBackendFactory[] { return Array.from(this.memoryBackends.values()) .filter((entry) => entry.plugin === pluginName) .map((entry) => entry.factory); } // ============================================================================= // Bulk Registration // ============================================================================= /** * Register all extension points from a plugin */ registerAllFromPlugin(pluginName: string): void { this.registerAgentTypes(pluginName); this.registerTaskTypes(pluginName); this.registerMCPTools(pluginName); this.registerCLICommands(pluginName); this.registerMemoryBackends(pluginName); this.collectPluginMetrics(pluginName); } /** * Clear all registrations (for testing) */ clear(): void { this.plugins.clear(); this.agentTypes.clear(); this.taskTypes.clear(); this.mcpTools.clear(); this.cliCommands.clear(); this.memoryBackends.clear(); } } /** * Plugin registry status summary */ export interface PluginRegistryStatus { totalPlugins: number; states: Record<PluginLifecycleState, number>; agentTypesRegistered: number; taskTypesRegistered: number; mcpToolsRegistered: number; cliCommandsRegistered: number; memoryBackendsRegistered: number; }