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>
154 lines
4.5 KiB
Python
154 lines
4.5 KiB
Python
"""
|
|
Built-in formatter plugins for MarkiTect.
|
|
|
|
These formatters provide various output formats for MarkiTect data.
|
|
"""
|
|
|
|
import json
|
|
import yaml
|
|
from typing import Any
|
|
|
|
from ..base import FormatterPlugin, PluginMetadata, PluginType
|
|
from ..decorators import register_plugin
|
|
|
|
|
|
@register_plugin("json_formatter")
|
|
class JsonFormatter(FormatterPlugin):
|
|
"""JSON output formatter."""
|
|
|
|
@property
|
|
def metadata(self) -> PluginMetadata:
|
|
return PluginMetadata(
|
|
name="json_formatter",
|
|
version="1.0.0",
|
|
description="Format output as JSON",
|
|
author="MarkiTect Team",
|
|
plugin_type=PluginType.FORMATTER
|
|
)
|
|
|
|
def format(self, data: Any, **kwargs) -> str:
|
|
"""Format data as JSON."""
|
|
indent = kwargs.get('indent', 2)
|
|
ensure_ascii = kwargs.get('ensure_ascii', False)
|
|
|
|
return json.dumps(data, indent=indent, ensure_ascii=ensure_ascii)
|
|
|
|
def get_file_extension(self) -> str:
|
|
"""Get JSON file extension."""
|
|
return '.json'
|
|
|
|
|
|
@register_plugin("yaml_formatter")
|
|
class YamlFormatter(FormatterPlugin):
|
|
"""YAML output formatter."""
|
|
|
|
@property
|
|
def metadata(self) -> PluginMetadata:
|
|
return PluginMetadata(
|
|
name="yaml_formatter",
|
|
version="1.0.0",
|
|
description="Format output as YAML",
|
|
author="MarkiTect Team",
|
|
plugin_type=PluginType.FORMATTER
|
|
)
|
|
|
|
def format(self, data: Any, **kwargs) -> str:
|
|
"""Format data as YAML."""
|
|
default_flow_style = kwargs.get('default_flow_style', False)
|
|
indent = kwargs.get('indent', 2)
|
|
|
|
return yaml.dump(data, default_flow_style=default_flow_style, indent=indent)
|
|
|
|
def get_file_extension(self) -> str:
|
|
"""Get YAML file extension."""
|
|
return '.yaml'
|
|
|
|
|
|
@register_plugin("table_formatter")
|
|
class TableFormatter(FormatterPlugin):
|
|
"""Table output formatter for structured data."""
|
|
|
|
@property
|
|
def metadata(self) -> PluginMetadata:
|
|
return PluginMetadata(
|
|
name="table_formatter",
|
|
version="1.0.0",
|
|
description="Format output as ASCII table",
|
|
author="MarkiTect Team",
|
|
plugin_type=PluginType.FORMATTER
|
|
)
|
|
|
|
def format(self, data: Any, **kwargs) -> str:
|
|
"""Format data as ASCII table."""
|
|
if not isinstance(data, (list, tuple)):
|
|
return str(data)
|
|
|
|
if not data:
|
|
return "No data"
|
|
|
|
# Handle list of dictionaries (most common case)
|
|
if isinstance(data[0], dict):
|
|
return self._format_dict_table(data, **kwargs)
|
|
|
|
# Handle simple list
|
|
return self._format_simple_table(data, **kwargs)
|
|
|
|
def _format_dict_table(self, data: list, **kwargs) -> str:
|
|
"""Format list of dictionaries as table."""
|
|
if not data:
|
|
return "No data"
|
|
|
|
# Get all unique keys
|
|
all_keys = set()
|
|
for item in data:
|
|
if isinstance(item, dict):
|
|
all_keys.update(item.keys())
|
|
|
|
headers = sorted(all_keys)
|
|
|
|
# Calculate column widths
|
|
col_widths = {}
|
|
for header in headers:
|
|
col_widths[header] = len(str(header))
|
|
for item in data:
|
|
if isinstance(item, dict) and header in item:
|
|
col_widths[header] = max(col_widths[header], len(str(item[header])))
|
|
|
|
# Build table
|
|
lines = []
|
|
|
|
# Header
|
|
header_line = "| " + " | ".join(h.ljust(col_widths[h]) for h in headers) + " |"
|
|
lines.append(header_line)
|
|
|
|
# Separator
|
|
sep_line = "|-" + "-|-".join("-" * col_widths[h] for h in headers) + "-|"
|
|
lines.append(sep_line)
|
|
|
|
# Data rows
|
|
for item in data:
|
|
if isinstance(item, dict):
|
|
row_line = "| " + " | ".join(
|
|
str(item.get(h, "")).ljust(col_widths[h]) for h in headers
|
|
) + " |"
|
|
lines.append(row_line)
|
|
|
|
return "\n".join(lines)
|
|
|
|
def _format_simple_table(self, data: list, **kwargs) -> str:
|
|
"""Format simple list as single-column table."""
|
|
max_width = max(len(str(item)) for item in data)
|
|
max_width = max(max_width, len("Value"))
|
|
|
|
lines = []
|
|
lines.append(f"| {'Value'.ljust(max_width)} |")
|
|
lines.append(f"|-{'-' * max_width}-|")
|
|
|
|
for item in data:
|
|
lines.append(f"| {str(item).ljust(max_width)} |")
|
|
|
|
return "\n".join(lines)
|
|
|
|
def get_file_extension(self) -> str:
|
|
"""Get table file extension."""
|
|
return '.txt' |