Skip to main content
Glama
index.ts5.65 kB
/** * Plugin Loader and Registry * Dynamically loads and manages all prompt plugins * Modern v4.2 architecture - plugins handle their own context management */ import { IPromptPlugin } from './types.js'; import { BasePlugin } from './base-plugin.js'; import path from 'path'; import { promises as fs } from 'fs'; import { pathToFileURL } from 'url'; export class PluginLoader { private plugins: Map<string, IPromptPlugin> = new Map(); private categories: Map<string, IPromptPlugin[]> = new Map(); constructor() { // Initialize category maps this.categories.set('analyze', []); this.categories.set('generate', []); this.categories.set('custom', []); this.categories.set('system', []); this.categories.set('fun', []); } /** * Load all plugins from the prompts directory */ async loadPlugins(promptsDir: string): Promise<void> { const categories = ['analyze', 'generate', 'custom', 'system', 'fun']; for (const category of categories) { const categoryPath = path.join(promptsDir, category); try { const files = await fs.readdir(categoryPath); for (const file of files) { if (file.endsWith('.js') && !file.includes('.test.')) { // Only load .js files, skip .d.ts and tests await this.loadPlugin(path.join(categoryPath, file), category as any); } } } catch (error) { // Silent error handling to avoid JSON-RPC interference // console.error(`Error loading plugins from ${category}:`, error); } } // Load system plugins from shared (cache management) await this.loadSystemPlugins(path.join(promptsDir, 'shared')); } /** * Load a single plugin file */ private async loadPlugin(filePath: string, category: 'analyze' | 'generate' | 'custom' | 'system'): Promise<void> { try { // Skip .d.ts files (TypeScript declaration files) if (filePath.endsWith('.d.ts')) { return; } // Use pathToFileURL for proper ES module loading on Windows const fileUrl = pathToFileURL(filePath).href; const module = await import(fileUrl); const PluginClass = module.default; if (PluginClass && typeof PluginClass === 'function') { const plugin = new PluginClass(); if (plugin instanceof BasePlugin) { this.registerPlugin(plugin); // Removed console.log to avoid JSON-RPC interference } } } catch (error) { // Silent error handling to avoid JSON-RPC interference // console.error(`Error loading plugin from ${filePath}:`, error); } } /** * Load system plugins (cache management, health check) */ private async loadSystemPlugins(sharedDir: string): Promise<void> { // Load cache management plugins try { const cacheManagerPath = path.join(sharedDir, 'cache-manager.js'); // Convert Windows path to proper file:// URL const fileUrl = `file:///${cacheManagerPath.replace(/\\/g, '/')}`; const cacheModule = await import(fileUrl); if (cacheModule.ClearCachePlugin) { this.registerPlugin(new cacheModule.ClearCachePlugin()); } if (cacheModule.CacheStatisticsPlugin) { this.registerPlugin(new cacheModule.CacheStatisticsPlugin()); } } catch (error) { // Silent error handling to avoid JSON-RPC interference // console.error('Error loading cache management plugins:', error); } } /** * Register a plugin */ registerPlugin(plugin: IPromptPlugin): void { // Validate plugin has required properties if (!plugin.name || !plugin.category || !plugin.execute) { throw new Error('Invalid plugin: missing required properties'); } // Register in main map this.plugins.set(plugin.name, plugin); // Register in category map const categoryPlugins = this.categories.get(plugin.category) || []; categoryPlugins.push(plugin); this.categories.set(plugin.category, categoryPlugins); } /** * Get a plugin by name */ getPlugin(name: string): IPromptPlugin | undefined { return this.plugins.get(name); } /** * Get all plugins */ getPlugins(): IPromptPlugin[] { return Array.from(this.plugins.values()); } /** * Get plugins by category */ getPluginsByCategory(category: 'analyze' | 'generate' | 'system'): IPromptPlugin[] { return this.categories.get(category) || []; } /** * Get plugin names */ getPluginNames(): string[] { return Array.from(this.plugins.keys()); } /** * Execute a plugin by name * Modern v4.2: Each plugin handles its own context management with ThreeStagePromptManager */ async executePlugin(name: string, params: any, llmClient: any): Promise<any> { const plugin = this.getPlugin(name); if (!plugin) { throw new Error(`Plugin not found: ${name}`); } // Apply defaults and validate parameters if (plugin instanceof BasePlugin) { params = plugin.applyDefaults(params); plugin.validateParams(params); } // Execute plugin - let each plugin handle its own context management // Modern plugins use ThreeStagePromptManager internally for intelligent chunking return await plugin.execute(params, llmClient); } } export class PluginRegistry { private static instance: PluginLoader; static getInstance(): PluginLoader { if (!this.instance) { this.instance = new PluginLoader(); } return this.instance; } } export default { PluginLoader, PluginRegistry };

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/houtini-ai/lm'

If you have feedback or need assistance with the MCP directory API, please join our Discord server