File size: 3,840 Bytes
4179782
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
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()