from abc import ABC, abstractmethod import importlib import os from typing import Dict, List, Type # Base interface for all plugins defined class Plugin(ABC): """ Base class for Shasha AI plugins. """ name: str description: str @abstractmethod def initialize(self, config: Dict) -> None: """Perform any setup required by the plugin.""" pass @abstractmethod def execute(self, **kwargs) -> Dict: """Run the plugin action and return a dict of results.""" pass class PluginManager: """ Manages discovery, registration, and invocation of plugins. """ def __init__(self, plugins_dir: str = 'plugins'): self.plugins_dir = plugins_dir self._registry: Dict[str, Type[Plugin]] = {} self._instances: Dict[str, Plugin] = {} def discover(self) -> None: """ Auto-import all modules in the plugins directory. """ if not os.path.isdir(self.plugins_dir): return for filename in os.listdir(self.plugins_dir): if not filename.endswith('.py') or filename.startswith('_'): continue module_name = filename[:-3] module_path = f"{self.plugins_dir}.{module_name}" try: module = importlib.import_module(module_path) for attr in dir(module): obj = getattr(module, attr) if isinstance(obj, type) and issubclass(obj, Plugin) and obj is not Plugin: self.register(obj) except Exception as e: print(f"Failed to load plugin module {module_path}: {e}") def register(self, plugin_cls: Type[Plugin]) -> None: """ Register a plugin class by its .name attribute. """ key = plugin_cls.name self._registry[key] = plugin_cls def initialize_all(self, config: Dict = None) -> None: """ Instantiate and initialize all registered plugins. """ for name, cls in self._registry.items(): try: instance = cls() instance.initialize(config or {}) self._instances[name] = instance except Exception as e: print(f"Failed to initialize plugin {name}: {e}") def list_plugins(self) -> List[str]: """ Return the list of registered plugin names. """ return list(self._registry.keys()) def execute(self, name: str, **kwargs) -> Dict: """ Execute a named plugin. """ plugin = self._instances.get(name) if not plugin: raise ValueError(f"Plugin '{name}' is not initialized.") return plugin.execute(**kwargs) # Example plugin for VSCode snippets class VSCodeSnippetPlugin(Plugin): name = "vscode_snippets" description = "Provides VSCode snippet generation and insertion into editor." def initialize(self, config: Dict) -> None: # e.g., set snippet directory from config self.snippet_dir = config.get('snippet_dir', './snippets') def execute(self, **kwargs) -> Dict: # Generate or fetch a snippet language = kwargs.get('language', 'python') snippet_name = kwargs.get('snippet_name') # Load snippet from disk or generate dynamically path = os.path.join(self.snippet_dir, f"{language}.{snippet_name}.json") if os.path.exists(path): with open(path, 'r') as f: content = f.read() else: content = '{ "prefix": "todo", "body": ["// add your snippet here"] }' return { 'plugin': self.name, 'snippet': content } # Initialize default manager for import plugin_manager = PluginManager() plugin_manager.discover() plugin_manager.initialize_all()