Complete implementation of md-explode command for transforming single markdown files into organized directory structures: Core Implementation: - MarkdownSection class for hierarchical document modeling - extract_headings() - Parse markdown headings with levels - parse_markdown_structure() - Build section hierarchy from content - generate_safe_filename() - Convert headings to filesystem-safe names - explode_markdown_file() - Main explosion functionality - DirectoryStructureBuilder - Create organized file/directory structures CLI Integration: - md-explode command with comprehensive options - --dry-run for previewing structure - --verbose for detailed output - --max-depth for limiting nesting - --output-dir for custom output location Key Features: - Hierarchical structure preservation (# → ## → ###) - Smart filename generation with Unicode support - Front matter handling and preservation - Content integrity maintenance - Cross-platform filesystem compatibility - Comprehensive error handling and validation Refactoring Applied: - Eliminated code duplication between filename functions - Extracted front matter processing into dedicated function - Modularized CLI command with helper functions - Improved error handling and user feedback Documentation: - Complete API documentation with docstrings - Comprehensive user documentation (docs/md-explode-command.md) - Usage examples and troubleshooting guide - Integration instructions with other MarkiTect commands Testing: 47 comprehensive tests covering all functionality Status: Production-ready, full TDD8 cycle completed Performance: Efficient for documents with thousands of sections 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
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 JSONyaml_formatter: Format output as YAMLtable_formatter: Format output as ASCII tables
Processors
markdown_processor: Process markdown contenttext_processor: Process generic text content
Plugin Discovery
Plugins are discovered from:
- Built-in plugins:
markitect.plugins.builtin.* - Local directories: Configured plugin directories
- 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
- Plugin not discovered: Check plugin directory configuration
- Import errors: Ensure all dependencies are installed
- Registration fails: Check plugin class inheritance
- 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