Files
markitect-main/markitect/plugins
tegwick 096017b93f
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
feat: reorganize tests by capability with separate test targets
Separate capability-specific tests from core system tests to establish clear
test organization and separation of concerns.

## Test Reorganization:
- **markitect-content tests**: Moved 6 tests to capabilities/markitect-content/tests/
- **markitect-finance tests**: Moved 7 tests to markitect/finance/tests/
- **markitect-query tests**: Moved 1 test to markitect/query_paradigms/tests/
- **markitect-graphql tests**: Moved 2 tests to markitect/graphql/tests/
- **markitect-plugins tests**: Moved 2 tests to markitect/plugins/tests/

## Makefile Updates:
- **make test**: Excludes capability tests, runs only core system tests
- **make test-capabilities**: Runs all capability tests
- **make test-capability-***: Individual capability test targets
- Updated all test targets (test-red, test-green, test-ultra-fast, test-perf)
- Added capability test targets to help documentation

## Benefits:
- Clear separation between core system tests and capability-specific tests
- Faster core test execution (capability tests not run by default)
- Individual capability testing for focused development
- Supports future capability extraction workflow
- Maintains capability test independence

Test verification:
- Core tests: 1291 tests (capability tests excluded)
- Finance capability: 143 tests working independently
- Content capability: 79 tests working independently

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-25 02:37:45 +02:00
..

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

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

# 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

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:

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

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

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

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

@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:

@register_plugin("plugin_name")
class MyPlugin(BasePlugin):
    # Implementation

Or register manually:

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:

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

# 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:

PluginMetadata(
    # ...
    dependencies=["requests", "pyyaml"],
    markitect_version=">=0.2.0"
)

Plugin Initialization and Cleanup

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

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:

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

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

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

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:

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:

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:

markitect --verbose plugin-list
markitect --verbose plugin-load my_plugin

Plugin Validation

# Validate specific plugin
markitect plugin-info my_plugin

# Discover and validate all plugins
markitect plugin-discover --refresh