# MarkiTect Plugin System The MarkiTect Plugin System provides a flexible and extensible architecture for adding custom functionality to MarkiTect. Plugins can extend processors, formatters, validators, exporters, and more. ## Plugin Types ### Supported Plugin Types - **Processor**: Content processors (markdown, text transformation, etc.) - **Formatter**: Output formatters (JSON, YAML, tables, etc.) - **Validator**: Content validators (schema validation, lint checking, etc.) - **Exporter**: Export handlers (PDF, HTML, etc.) - **Generator**: Content generators (templates, stubs, etc.) - **Importer**: Import handlers (various formats) - **Transformer**: Content transformers (data manipulation, etc.) - **Extension**: General extensions (any functionality) - **Backend**: Storage/API backends (databases, APIs, etc.) - **Command**: CLI command extensions ## Quick Start ### Creating a Simple Plugin ```python from markitect.plugins import BasePlugin, PluginMetadata, PluginType, register_plugin @register_plugin("my_processor") class MyProcessor(ProcessorPlugin): @property def metadata(self) -> PluginMetadata: return PluginMetadata( name="my_processor", version="1.0.0", description="My custom processor", author="Your Name", plugin_type=PluginType.PROCESSOR ) def process(self, content: str, **kwargs) -> str: # Your processing logic here return content.upper() ``` ### Using Plugins via CLI ```bash # List all available plugins markitect plugin-list # Load a specific plugin markitect plugin-load my_processor # Get plugin information markitect plugin-info my_processor # Discover new plugins markitect plugin-discover --refresh ``` ### Using Plugins Programmatically ```python from markitect.plugins import PluginManager # Initialize plugin manager manager = PluginManager() # Discover and load plugins manager.discover_plugins() processor = manager.load_plugin("my_processor") # Use the plugin if processor: result = processor.process("hello world") print(result) # HELLO WORLD ``` ## Plugin Development Guide ### 1. Choose Base Class Select the appropriate base class for your plugin: ```python from markitect.plugins.base import ( ProcessorPlugin, # For content processing FormatterPlugin, # For output formatting ValidatorPlugin, # For content validation ExporterPlugin, # For export functionality CommandPlugin, # For CLI commands BasePlugin # For general extensions ) ``` ### 2. Implement Required Methods Each plugin type has specific methods you must implement: #### ProcessorPlugin ```python class MyProcessor(ProcessorPlugin): def process(self, content: str, **kwargs) -> str: """Process content and return result.""" return processed_content def can_process(self, content: str, **kwargs) -> bool: """Check if this processor can handle the content.""" return True # or your logic ``` #### FormatterPlugin ```python class MyFormatter(FormatterPlugin): def format(self, data: Any, **kwargs) -> str: """Format data to string representation.""" return formatted_string def get_file_extension(self) -> str: """Get file extension for this format.""" return '.txt' ``` #### ValidatorPlugin ```python class MyValidator(ValidatorPlugin): def validate(self, content: str, **kwargs) -> List[str]: """Validate content and return list of errors.""" errors = [] # Your validation logic return errors ``` ### 3. Add Metadata ```python @property def metadata(self) -> PluginMetadata: return PluginMetadata( name="plugin_name", version="1.0.0", description="Plugin description", author="Your Name", plugin_type=PluginType.PROCESSOR, # Choose appropriate type dependencies=["optional", "list", "of", "dependencies"], markitect_version=">=0.1.0" ) ``` ### 4. Register Plugin Use the decorator for automatic registration: ```python @register_plugin("plugin_name") class MyPlugin(BasePlugin): # Implementation ``` Or register manually: ```python from markitect.plugins import plugin_registry plugin_registry.register(MyPlugin, "plugin_name") ``` ## Configuration ### Plugin Configuration File Create a configuration file (`.markitect/plugins.yml`) to manage plugins: ```yaml plugin_directories: - "plugins" - ".markitect/plugins" - "~/.markitect/plugins" auto_discover: true auto_load_enabled: true plugins: my_processor: enabled: true config: option1: value1 option2: value2 json_formatter: enabled: true config: indent: 4 ``` ### Plugin Configuration in Code ```python # Load plugin with configuration config = {"option1": "value1", "option2": "value2"} plugin = manager.load_plugin("my_processor", config) # Access configuration in plugin class MyProcessor(ProcessorPlugin): def process(self, content: str, **kwargs) -> str: option1 = self.config.get('option1', 'default') # Use configuration return processed_content ``` ## Built-in Plugins MarkiTect includes several built-in plugins: ### Formatters - `json_formatter`: Format output as JSON - `yaml_formatter`: Format output as YAML - `table_formatter`: Format output as ASCII tables ### Processors - `markdown_processor`: Process markdown content - `text_processor`: Process generic text content ## Plugin Discovery Plugins are discovered from: 1. **Built-in plugins**: `markitect.plugins.builtin.*` 2. **Local directories**: Configured plugin directories 3. **Installed packages**: Python packages with entry points (future) ### Plugin Directory Structure ``` plugins/ ├── my_plugin.py # Single file plugin ├── complex_plugin/ # Multi-file plugin │ ├── __init__.py │ ├── main.py │ └── utils.py └── another_plugin.py ``` ## Advanced Features ### Plugin Dependencies Specify dependencies in metadata: ```python PluginMetadata( # ... dependencies=["requests", "pyyaml"], markitect_version=">=0.2.0" ) ``` ### Plugin Initialization and Cleanup ```python class MyPlugin(BasePlugin): def _initialize(self) -> None: """Called when plugin is loaded.""" self.connection = setup_connection() def cleanup(self) -> None: """Called when plugin is unloaded.""" if hasattr(self, 'connection'): self.connection.close() ``` ### Configuration Validation ```python def validate_config(self) -> List[str]: """Validate plugin configuration.""" errors = [] if 'required_option' not in self.config: errors.append("Missing required_option") return errors ``` ### Command Plugins Extend the CLI with custom commands: ```python import click class MyCommandPlugin(CommandPlugin): def get_commands(self) -> Dict[str, Any]: return { 'my-command': self.my_command } @click.command() @click.argument('input_file') def my_command(self, input_file): """My custom command.""" click.echo(f"Processing {input_file}") ``` ## Best Practices ### 1. Error Handling ```python def process(self, content: str, **kwargs) -> str: try: return self._do_processing(content) except Exception as e: # Log error but don't crash self.logger.error(f"Processing failed: {e}") return content # Return original content ``` ### 2. Configuration Defaults ```python def _initialize(self) -> None: # Set defaults for configuration defaults = { 'timeout': 30, 'retries': 3, 'format': 'json' } for key, value in defaults.items(): if key not in self.config: self.config[key] = value ``` ### 3. Graceful Degradation ```python def can_process(self, content: str, **kwargs) -> bool: """Only claim we can process if we actually can.""" try: # Test if content is processable return self._test_content(content) except Exception: return False ``` ### 4. Documentation Always provide clear documentation for your plugins: ```python class MyProcessor(ProcessorPlugin): """ Advanced text processor for MarkiTect. This processor provides advanced text transformation capabilities including normalization, cleanup, and format conversion. Configuration: normalize_whitespace (bool): Normalize whitespace (default: True) remove_comments (bool): Remove comment lines (default: False) encoding (str): Text encoding (default: 'utf-8') Example: processor = MyProcessor({ 'normalize_whitespace': True, 'remove_comments': True }) result = processor.process(content) """ ``` ## Testing Plugins Create comprehensive tests for your plugins: ```python import pytest from markitect.plugins import plugin_registry def test_my_processor(): # Load plugin plugin = plugin_registry.get_plugin("my_processor") assert plugin is not None # Test processing result = plugin.process("test content") assert result == "expected result" # Test error handling result = plugin.process(None) assert result is not None ``` ## Troubleshooting ### Common Issues 1. **Plugin not discovered**: Check plugin directory configuration 2. **Import errors**: Ensure all dependencies are installed 3. **Registration fails**: Check plugin class inheritance 4. **Plugin not loading**: Check `_initialize()` method ### Debug Mode Enable verbose logging to debug plugin issues: ```bash markitect --verbose plugin-list markitect --verbose plugin-load my_plugin ``` ### Plugin Validation ```bash # Validate specific plugin markitect plugin-info my_plugin # Discover and validate all plugins markitect plugin-discover --refresh ```