""" Example Integration: Adding Legacy Support to MarkiTect CLI Commands This file demonstrates how to integrate the legacy compatibility system into existing MarkiTect CLI commands, showing practical patterns for: 1. Adding legacy switches to existing commands 2. Creating compatibility adapters for breaking changes 3. Registering legacy interfaces and deprecation timelines 4. Setting up automated legacy management This serves as both documentation and a working example. """ import click import json from datetime import datetime, timedelta from pathlib import Path # Import the legacy system components from .legacy import ( LegacyRegistry, LegacySwitch, LegacyAgent, DeprecationManager, GitStateTracker, CompatibilityLayer, LegacyStatus, legacy_option, with_legacy_support ) from .legacy.compatibility import InterfaceAdapter, ParameterMapping # Example 1: Adding legacy support to the existing 'query' command # This shows how to modify an existing command to support legacy versions @click.command() @click.argument('sql', type=str) @click.option('--format', '-f', type=click.Choice(['table', 'json', 'yaml', 'simple']), default='simple', help='Output format') @legacy_option('v1.0', 'Use v1.0 legacy query behavior (deprecated)') @legacy_option('v2.0', 'Use v2.0 legacy query behavior (deprecated)') @with_legacy_support('query') def query_with_legacy(sql, format, legacy_v1_0=False, legacy_v2_0=False): """ Execute SQL query with legacy support. This demonstrates how an existing command can be enhanced with legacy support without breaking existing functionality. """ # The @with_legacy_support decorator handles legacy routing automatically # If no legacy flags are set, this executes the modern implementation # Modern implementation return execute_modern_query(sql, format) def execute_modern_query(sql: str, format: str): """Modern query implementation.""" # This would be your current implementation return f"Modern query result for: {sql} in {format} format" # Example 2: Setting up legacy compatibility adapters # This shows how to handle breaking changes between versions def setup_query_legacy_adapters(): """Setup compatibility adapters for query command legacy versions.""" compatibility = CompatibilityLayer() # Adapter for v1.0 - handles parameter name changes v1_adapter = InterfaceAdapter( legacy_version='v1.0', parameter_mappings=[ ParameterMapping( legacy_name='sql_query', # Old parameter name modern_name='sql', # New parameter name required=True ), ParameterMapping( legacy_name='output_format', # Old parameter name modern_name='format', # New parameter name transformer=lambda x: { # Handle format value changes 'pretty': 'table', 'raw': 'simple', 'structured': 'json' }.get(x, x), default_value='simple' ), ParameterMapping( legacy_name='verbose_output', # Boolean flag converted to format modern_name='format', transformer=lambda x: 'table' if x else 'simple', required=False ) ], return_transformer=legacy_v1_return_format, # Transform output format compatibility_mode=CompatibilityLayer.CompatibilityMode.ADAPTIVE ) # Adapter for v2.0 - handles different breaking changes v2_adapter = InterfaceAdapter( legacy_version='v2.0', parameter_mappings=[ ParameterMapping( legacy_name='database_query', # Another old name modern_name='sql', required=True ), ParameterMapping( legacy_name='response_format', modern_name='format', transformer=lambda x: x.lower(), # Simple case conversion default_value='simple' ) ], return_transformer=legacy_v2_return_format, compatibility_mode=CompatibilityLayer.CompatibilityMode.STRICT ) compatibility.register_adapter('query', v1_adapter) compatibility.register_adapter('query', v2_adapter) return compatibility def legacy_v1_return_format(result): """Transform modern query results to v1.0 expected format.""" if isinstance(result, str) and 'Modern query result' in result: # v1.0 expected results wrapped in a specific structure return { 'status': 'success', 'query_result': result, 'format_version': 'v1.0', 'timestamp': datetime.now().isoformat() } return result def legacy_v2_return_format(result): """Transform modern query results to v2.0 expected format.""" if isinstance(result, str): # v2.0 expected a different wrapper structure return { 'success': True, 'data': result, 'api_version': 'v2.0' } return result # Example 3: Registering legacy interfaces with the registry # This shows how to formally register legacy versions def register_query_legacy_versions(): """Register legacy versions of the query command.""" registry = LegacyRegistry() git_tracker = GitStateTracker() # Register v1.0 as deprecated (90 days ago) deprecated_date = (datetime.now() - timedelta(days=90)).isoformat() registry.register_legacy_interface( command='query', version='v1.0', git_commit='a1b2c3d4', # Actual commit where v1.0 was current status=LegacyStatus.DEPRECATED, deprecated_date=deprecated_date, removal_date=(datetime.now() + timedelta(days=60)).isoformat(), description='Legacy query interface with sql_query parameter', breaking_changes=[ 'Parameter sql_query renamed to sql', 'Output format values changed (pretty->table, raw->simple)', 'Return structure modified for consistency' ], migration_guide=''' Migration from query v1.0 to current: 1. Change parameter names: --sql_query → --sql (or use sql as positional argument) --output_format → --format 2. Update format values: --output_format=pretty → --format=table --output_format=raw → --format=simple --output_format=structured → --format=json 3. Update result parsing: - v1.0 returned: {"status": "success", "query_result": "...", ...} - Current returns: direct result string or structured data Example: Old: markitect query --sql_query "SELECT * FROM files" --output_format=pretty New: markitect query "SELECT * FROM files" --format=table ''', implementation=legacy_v1_query_implementation ) # Register v2.0 as legacy (requires flag) registry.register_legacy_interface( command='query', version='v2.0', git_commit='e5f6g7h8', # Actual commit where v2.0 was current status=LegacyStatus.LEGACY, deprecated_date=(datetime.now() - timedelta(days=30)).isoformat(), removal_date=(datetime.now() + timedelta(days=90)).isoformat(), description='Legacy query interface with database_query parameter', breaking_changes=[ 'Parameter database_query renamed to sql', 'Response format structure simplified' ], migration_guide=''' Migration from query v2.0 to current: 1. Change parameter names: --database_query → positional sql argument 2. Update result parsing: - v2.0 returned: {"success": true, "data": "...", "api_version": "v2.0"} - Current returns: direct result Example: Old: markitect query --database_query "SELECT * FROM files" New: markitect query "SELECT * FROM files" ''', implementation=legacy_v2_query_implementation ) # Bind versions to git commits for precise restoration git_tracker.bind_version_to_commit( command='query', version='v1.0', commit_hash='a1b2c3d4', description='Query v1.0 implementation with sql_query parameter', validation_files=['markitect/cli.py', 'markitect/database.py'] ) git_tracker.bind_version_to_commit( command='query', version='v2.0', commit_hash='e5f6g7h8', description='Query v2.0 implementation with database_query parameter', validation_files=['markitect/cli.py', 'markitect/database.py'] ) def legacy_v1_query_implementation(*args, **kwargs): """Legacy v1.0 query implementation.""" # Extract legacy parameters sql_query = kwargs.get('sql_query') or args[0] if args else None output_format = kwargs.get('output_format', 'simple') verbose_output = kwargs.get('verbose_output', False) if not sql_query: raise ValueError("sql_query parameter is required for v1.0") # Transform to modern parameters modern_format = { 'pretty': 'table', 'raw': 'simple', 'structured': 'json' }.get(output_format, output_format) if verbose_output: modern_format = 'table' # Execute modern implementation result = execute_modern_query(sql_query, modern_format) # Return in v1.0 expected format return { 'status': 'success', 'query_result': result, 'format_version': 'v1.0', 'timestamp': datetime.now().isoformat() } def legacy_v2_query_implementation(*args, **kwargs): """Legacy v2.0 query implementation.""" database_query = kwargs.get('database_query') or args[0] if args else None response_format = kwargs.get('response_format', 'simple').lower() if not database_query: raise ValueError("database_query parameter is required for v2.0") # Execute modern implementation result = execute_modern_query(database_query, response_format) # Return in v2.0 expected format return { 'success': True, 'data': result, 'api_version': 'v2.0' } # Example 4: Setting up automated legacy management # This shows how to configure the legacy agent for automation def setup_legacy_automation(): """Setup automated legacy management for MarkiTect.""" # Configure agent with custom settings from .legacy.agent import AgentConfig config = AgentConfig( auto_progression=True, # Automatically progress deprecations cleanup_unused_days=180, # Clean up after 6 months of no usage migration_guide_auto_generation=True, # Generate migration guides notification_threshold_days=30, # Notify 30 days before removal max_concurrent_migrations=3, # Limit concurrent migration assistance backup_before_cleanup=True # Always backup before cleanup ) agent = LegacyAgent(config=config) # Schedule regular maintenance (this would typically be done via cron/systemd) maintenance_summary = agent.run_maintenance() return agent, maintenance_summary # Example 5: CLI commands for legacy management # This shows how to add CLI commands for managing legacy interfaces @click.group('legacy') def legacy_management(): """Manage legacy interface compatibility and lifecycle.""" pass @legacy_management.command('status') @click.option('--format', '-f', type=click.Choice(['table', 'json', 'yaml']), default='table', help='Output format') def legacy_status(format): """Show status of all legacy interfaces.""" registry = LegacyRegistry() # Get all legacy interfaces interfaces = [] for command in registry._interfaces: for version, interface in registry._interfaces[command].items(): interfaces.append({ 'command': interface.command, 'version': interface.version, 'status': interface.status.value, 'deprecated_date': interface.deprecated_date, 'removal_date': interface.removal_date, 'git_commit': interface.git_commit[:8] if interface.git_commit else 'N/A' }) if format == 'json': click.echo(json.dumps(interfaces, indent=2)) elif format == 'yaml': import yaml click.echo(yaml.dump(interfaces, default_flow_style=False)) else: # Table format if interfaces: from tabulate import tabulate headers = ['Command', 'Version', 'Status', 'Deprecated', 'Removal', 'Commit'] rows = [[i['command'], i['version'], i['status'], i['deprecated_date'][:10] if i['deprecated_date'] else 'N/A', i['removal_date'][:10] if i['removal_date'] else 'N/A', i['git_commit']] for i in interfaces] click.echo(tabulate(rows, headers=headers, tablefmt='grid')) else: click.echo("No legacy interfaces found.") @legacy_management.command('migrate') @click.argument('command') @click.argument('version') def legacy_migrate(command, version): """Get migration guidance for a legacy version.""" registry = LegacyRegistry() interface = registry.get_legacy_interface(command, version) if not interface: click.echo(f"Legacy version {command} {version} not found.", err=True) return migration = registry.get_migration_path(command, version) click.echo(f"Migration Guide for {command} {version}") click.echo("=" * 50) if interface.migration_guide: click.echo(interface.migration_guide) else: click.echo("No specific migration guide available.") if migration['breaking_changes']: click.echo("\nBreaking Changes:") for change in migration['breaking_changes']: click.echo(f"• {change}") @legacy_management.command('cleanup') @click.argument('command') @click.argument('version') @click.option('--force', is_flag=True, help='Force cleanup without confirmation') def legacy_cleanup(command, version, force): """Clean up a specific legacy version.""" agent = LegacyAgent() if not force: interface = agent.registry.get_legacy_interface(command, version) if interface: click.echo(f"About to clean up {command} {version}") click.echo(f"Status: {interface.status.value}") if interface.removal_date: click.echo(f"Scheduled removal: {interface.removal_date}") if not click.confirm("Are you sure you want to proceed?"): click.echo("Cleanup cancelled.") return success = agent.force_cleanup(command, version) if success: click.echo(f"✅ Successfully cleaned up {command} {version}") else: click.echo(f"❌ Failed to clean up {command} {version}", err=True) @legacy_management.command('agent-status') def legacy_agent_status(): """Show legacy agent status and statistics.""" agent = LegacyAgent() status = agent.get_agent_status() click.echo("Legacy Agent Status") click.echo("=" * 30) click.echo(f"Data Directory: {status['data_directory']}") click.echo(f"Total Tasks: {status['tasks']['total']}") click.echo(f"Pending Tasks: {status['tasks']['pending']}") click.echo(f"Completed Tasks: {status['tasks']['completed']}") if status['next_maintenance']: click.echo(f"Next Maintenance: {status['next_maintenance']}") click.echo("\nRegistry Statistics:") for stat_name, stat_value in status['registry_stats'].items(): click.echo(f" {stat_name}: {stat_value}") # Example 6: Integration with existing CLI structure # This shows how to add legacy support to the main CLI def add_legacy_support_to_main_cli(): """ Example of how to integrate legacy support into the main CLI module. This would typically be added to markitect/cli.py """ # 1. Import legacy components at the top of cli.py # from .legacy import LegacyRegistry, with_legacy_support, legacy_option # 2. Initialize legacy system in the main CLI group def initialize_legacy_system(): # Setup registry and compatibility adapters setup_query_legacy_adapters() register_query_legacy_versions() # Setup agent for automation setup_legacy_automation() # 3. Add legacy support to existing commands (example for query command) def enhance_existing_query_command(): """ This shows how to modify the existing query command in cli.py to add legacy support without breaking changes. """ # Original command would be modified from: # @cli.command() # @click.argument('sql', type=str) # @click.option('--format', '-f', ...) # def query(sql, format): # # existing implementation # To: # @cli.command() # @click.argument('sql', type=str) # @click.option('--format', '-f', ...) # @legacy_option('v1.0', 'Use v1.0 legacy behavior') # @legacy_option('v2.0', 'Use v2.0 legacy behavior') # @with_legacy_support('query') # def query(sql, format, legacy_v1_0=False, legacy_v2_0=False): # # The @with_legacy_support decorator handles routing # # Original implementation stays the same # return original_query_implementation(sql, format) pass # 4. Add legacy management commands to main CLI def add_legacy_commands_to_cli(): """Add legacy management commands to main CLI.""" # This would be added to the main cli group: # @cli.group() # def legacy(): # """Legacy interface management commands.""" # pass # # Then add all the legacy_management commands as subcommands pass if __name__ == '__main__': """ Demonstration of the complete legacy system setup. This shows how all components work together. """ click.echo("Setting up MarkiTect Legacy Compatibility System...") # 1. Setup compatibility adapters click.echo("1. Setting up compatibility adapters...") compatibility = setup_query_legacy_adapters() # 2. Register legacy versions click.echo("2. Registering legacy interfaces...") register_query_legacy_versions() # 3. Setup automation click.echo("3. Setting up legacy automation...") agent, summary = setup_legacy_automation() # 4. Test legacy functionality click.echo("4. Testing legacy compatibility...") # Test parameter adaptation test_result = compatibility.test_compatibility( 'query', 'v1.0', {'sql_query': 'SELECT * FROM test', 'output_format': 'pretty'} ) if test_result['success']: click.echo(" ✅ Parameter adaptation working") click.echo(f" Adapted: {test_result['adapted_parameters']}") else: click.echo(" ❌ Parameter adaptation failed") # Test registry functionality registry = LegacyRegistry() interface = registry.get_legacy_interface('query', 'v1.0') if interface: click.echo(" ✅ Legacy interface registry working") click.echo(f" Found: {interface.command} {interface.version} ({interface.status.value})") else: click.echo(" ❌ Legacy interface registry failed") click.echo("\n✅ Legacy compatibility system setup complete!") click.echo("\nNext steps:") click.echo("1. Integrate legacy_option decorators into existing CLI commands") click.echo("2. Add legacy management commands to main CLI") click.echo("3. Schedule regular agent maintenance") click.echo("4. Monitor legacy usage and plan migrations")