import importlib.util import os import sys from typing import Callable, Dict, List from pr.core.logging import get_logger logger = get_logger("plugins") PLUGINS_DIR = os.path.expanduser("~/.pr/plugins") class PluginLoader: def __init__(self): self.loaded_plugins = {} self.plugin_tools = [] os.makedirs(PLUGINS_DIR, exist_ok=True) def load_plugins(self) -> List[Dict]: if not os.path.exists(PLUGINS_DIR): logger.info("No plugins directory found") return [] plugin_files = [f for f in os.listdir(PLUGINS_DIR) if f.endswith(".py")] for plugin_file in plugin_files: try: self._load_plugin_file(plugin_file) except Exception as e: logger.error(f"Error loading plugin {plugin_file}: {e}") return self.plugin_tools def _load_plugin_file(self, filename: str): plugin_path = os.path.join(PLUGINS_DIR, filename) plugin_name = filename[:-3] spec = importlib.util.spec_from_file_location(plugin_name, plugin_path) if spec is None or spec.loader is None: logger.error(f"Could not load spec for {filename}") return module = importlib.util.module_from_spec(spec) sys.modules[plugin_name] = module spec.loader.exec_module(module) if hasattr(module, "register_tools"): tools = module.register_tools() if isinstance(tools, list): self.plugin_tools.extend(tools) self.loaded_plugins[plugin_name] = module logger.info(f"Loaded plugin: {plugin_name} ({len(tools)} tools)") else: logger.warning(f"Plugin {plugin_name} register_tools() did not return a list") else: logger.warning(f"Plugin {plugin_name} does not have register_tools() function") def get_plugin_function(self, tool_name: str) -> Callable: for plugin_name, module in self.loaded_plugins.items(): if hasattr(module, tool_name): return getattr(module, tool_name) raise ValueError(f"Plugin function not found: {tool_name}") def list_loaded_plugins(self) -> List[str]: return list(self.loaded_plugins.keys()) def create_example_plugin(): example_plugin = os.path.join(PLUGINS_DIR, "example_plugin.py") if os.path.exists(example_plugin): return example_code = '''""" Example plugin for PR Assistant This plugin demonstrates how to create custom tools. """ def my_custom_tool(argument: str) -> str: """ A custom tool that does something useful. Args: argument: Some input Returns: A result string """ return f"Custom tool processed: {argument}" def register_tools(): """ Register tools with the PR assistant. Returns: List of tool definitions """ return [ { "type": "function", "function": { "name": "my_custom_tool", "description": "A custom tool that processes input", "parameters": { "type": "object", "properties": { "argument": { "type": "string", "description": "The input to process" } }, "required": ["argument"] } } } ] ''' try: os.makedirs(PLUGINS_DIR, exist_ok=True) with open(example_plugin, "w") as f: f.write(example_code) logger.info(f"Created example plugin at {example_plugin}") except Exception as e: logger.error(f"Error creating example plugin: {e}")