|
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() |
|
|