Complete plugin system implementation providing extensible architecture for MarkiTect: 🏗️ **Core Plugin Architecture**: - BasePlugin abstract class with lifecycle management (initialize/cleanup) - Specialized plugin types: ProcessorPlugin, FormatterPlugin, ValidatorPlugin, ExporterPlugin, CommandPlugin - PluginMetadata system with version, dependencies, and type information - Plugin initialization and configuration validation 🔍 **Plugin Discovery & Management**: - PluginManager with automatic discovery from built-in modules and directories - PluginRegistry for centralized plugin registration and lifecycle management - Support for plugin loading, unloading, and reloading with configuration - Plugin discovery from multiple sources (built-in, directories, packages) 🛠️ **CLI Integration**: - markitect plugin-list: List all available plugins with metadata - markitect plugin-load: Load plugins with optional configuration - markitect plugin-unload: Unload plugins and cleanup resources - markitect plugin-info: Show detailed plugin information - markitect plugin-discover: Discover and refresh plugin catalog 📦 **Built-in Plugins**: - JSON/YAML/Table formatters for output formatting - Markdown/Text processors for content processing - Auto-registered via @register_plugin decorator - Comprehensive configuration options 🔧 **Developer Experience**: - @register_plugin decorator for easy plugin registration - Plugin configuration validation and error handling - Comprehensive API documentation with examples - Plugin development guide and best practices 📋 **Example Plugins**: - Advanced text processor with case conversion and pattern replacement - XML/CSV formatters demonstrating custom output formats - Complete examples showing plugin development patterns 🧪 **Test Coverage**: - 59 comprehensive tests covering all plugin functionality - Tests for plugin lifecycle, registration, discovery, and CLI integration - Error handling and edge case coverage - Built-in plugin validation Technical Implementation: - Plugin types: processor, formatter, validator, exporter, generator, importer, transformer, extension, backend, command - Configuration-driven plugin management with YAML/JSON support - Graceful error handling and plugin isolation - Plugin dependency validation and compatibility checking 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
175 lines
5.3 KiB
Python
175 lines
5.3 KiB
Python
"""
|
|
Example processor plugin for MarkiTect.
|
|
|
|
This demonstrates how to create a custom processor plugin.
|
|
"""
|
|
|
|
import re
|
|
from markitect.plugins.base import ProcessorPlugin, PluginMetadata, PluginType
|
|
from markitect.plugins.decorators import register_plugin
|
|
|
|
|
|
@register_plugin("example_processor")
|
|
class ExampleProcessor(ProcessorPlugin):
|
|
"""
|
|
Example processor that demonstrates various text processing capabilities.
|
|
|
|
This processor can:
|
|
- Convert text to different cases
|
|
- Clean up whitespace
|
|
- Remove or replace patterns
|
|
- Add prefixes/suffixes to lines
|
|
"""
|
|
|
|
@property
|
|
def metadata(self) -> PluginMetadata:
|
|
return PluginMetadata(
|
|
name="example_processor",
|
|
version="1.0.0",
|
|
description="Example processor demonstrating text transformations",
|
|
author="MarkiTect Team",
|
|
plugin_type=PluginType.PROCESSOR
|
|
)
|
|
|
|
def process(self, content: str, **kwargs) -> str:
|
|
"""
|
|
Process content with various transformations.
|
|
|
|
Args:
|
|
content: Input content to process
|
|
**kwargs: Processing options:
|
|
- case: 'upper', 'lower', 'title', 'sentence'
|
|
- clean_whitespace: bool
|
|
- remove_pattern: regex pattern to remove
|
|
- replace_pattern: dict with 'pattern' and 'replacement'
|
|
- line_prefix: string to add to start of each line
|
|
- line_suffix: string to add to end of each line
|
|
|
|
Returns:
|
|
Processed content
|
|
"""
|
|
if not isinstance(content, str):
|
|
return content
|
|
|
|
result = content
|
|
|
|
# Case transformations
|
|
case = kwargs.get('case', '').lower()
|
|
if case == 'upper':
|
|
result = result.upper()
|
|
elif case == 'lower':
|
|
result = result.lower()
|
|
elif case == 'title':
|
|
result = result.title()
|
|
elif case == 'sentence':
|
|
result = self._sentence_case(result)
|
|
|
|
# Clean whitespace
|
|
if kwargs.get('clean_whitespace', False):
|
|
result = self._clean_whitespace(result)
|
|
|
|
# Remove pattern
|
|
remove_pattern = kwargs.get('remove_pattern')
|
|
if remove_pattern:
|
|
result = re.sub(remove_pattern, '', result)
|
|
|
|
# Replace pattern
|
|
replace_pattern = kwargs.get('replace_pattern')
|
|
if replace_pattern and isinstance(replace_pattern, dict):
|
|
pattern = replace_pattern.get('pattern')
|
|
replacement = replace_pattern.get('replacement', '')
|
|
if pattern:
|
|
result = re.sub(pattern, replacement, result)
|
|
|
|
# Line prefix/suffix
|
|
line_prefix = kwargs.get('line_prefix', '')
|
|
line_suffix = kwargs.get('line_suffix', '')
|
|
if line_prefix or line_suffix:
|
|
lines = result.split('\n')
|
|
lines = [f"{line_prefix}{line}{line_suffix}" for line in lines]
|
|
result = '\n'.join(lines)
|
|
|
|
return result
|
|
|
|
def can_process(self, content: str, **kwargs) -> bool:
|
|
"""Check if content can be processed (any string content)."""
|
|
return isinstance(content, str)
|
|
|
|
def _sentence_case(self, text: str) -> str:
|
|
"""Convert text to sentence case (first letter capitalized)."""
|
|
if not text:
|
|
return text
|
|
return text[0].upper() + text[1:].lower()
|
|
|
|
def _clean_whitespace(self, text: str) -> str:
|
|
"""Clean up whitespace in text."""
|
|
# Remove trailing whitespace from each line
|
|
lines = [line.rstrip() for line in text.split('\n')]
|
|
|
|
# Remove multiple consecutive empty lines
|
|
cleaned_lines = []
|
|
prev_empty = False
|
|
for line in lines:
|
|
if line.strip():
|
|
cleaned_lines.append(line)
|
|
prev_empty = False
|
|
elif not prev_empty:
|
|
cleaned_lines.append(line)
|
|
prev_empty = True
|
|
|
|
return '\n'.join(cleaned_lines)
|
|
|
|
def validate_config(self) -> list:
|
|
"""Validate plugin configuration."""
|
|
errors = []
|
|
|
|
case = self.config.get('case', '').lower()
|
|
if case and case not in ['upper', 'lower', 'title', 'sentence']:
|
|
errors.append(f"Invalid case option: {case}")
|
|
|
|
replace_pattern = self.config.get('replace_pattern')
|
|
if replace_pattern and not isinstance(replace_pattern, dict):
|
|
errors.append("replace_pattern must be a dictionary with 'pattern' and 'replacement' keys")
|
|
|
|
return errors
|
|
|
|
|
|
# Example usage:
|
|
if __name__ == '__main__':
|
|
# Test the processor
|
|
processor = ExampleProcessor()
|
|
|
|
test_content = """ Hello World
|
|
This is a test.
|
|
|
|
|
|
Multiple empty lines above. """
|
|
|
|
# Test case conversion
|
|
result = processor.process(test_content, case='upper')
|
|
print("Upper case:")
|
|
print(result)
|
|
print()
|
|
|
|
# Test whitespace cleaning
|
|
result = processor.process(test_content, clean_whitespace=True)
|
|
print("Cleaned whitespace:")
|
|
print(repr(result))
|
|
print()
|
|
|
|
# Test pattern replacement
|
|
result = processor.process(
|
|
test_content,
|
|
replace_pattern={'pattern': r'\s+', 'replacement': ' '}
|
|
)
|
|
print("Normalized spaces:")
|
|
print(repr(result))
|
|
print()
|
|
|
|
# Test line prefix
|
|
result = processor.process(
|
|
"Line 1\nLine 2\nLine 3",
|
|
line_prefix=">> "
|
|
)
|
|
print("With prefix:")
|
|
print(result) |