| from abc import ABC, abstractmethod |
| import importlib |
| import os |
| from typing import Dict, List, Type |
|
|
| |
| 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) |
|
|
|
|
| |
| class VSCodeSnippetPlugin(Plugin): |
| name = "vscode_snippets" |
| description = "Provides VSCode snippet generation and insertion into editor." |
|
|
| def initialize(self, config: Dict) -> None: |
| |
| self.snippet_dir = config.get('snippet_dir', './snippets') |
|
|
| def execute(self, **kwargs) -> Dict: |
| |
| language = kwargs.get('language', 'python') |
| snippet_name = kwargs.get('snippet_name') |
| |
| 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 } |
|
|
| |
| plugin_manager = PluginManager() |
| plugin_manager.discover() |
| plugin_manager.initialize_all() |
|
|