#!/usr/bin/env python3 """ OpenCode Slash Commands for Git Agent Provides custom slash commands for Git operations in the Uniswap Auto CLP project """ import os import sys import subprocess import json from datetime import datetime from typing import Dict, Any, List, Optional class GitSlashCommand: """Base class for Git-related slash commands""" def __init__(self): self.project_root = "K:\\Projects\\uniswap_auto_clp" self.git_agent_path = os.path.join(self.project_root, "tools", "git_agent.py") def run_git_agent(self, args: List[str]) -> Dict[str, Any]: """Execute git_agent.py with specified arguments""" try: cmd = ["python", self.git_agent_path] + args result = subprocess.run( cmd, cwd=self.project_root, capture_output=True, text=True, check=False ) return { "success": result.returncode == 0, "stdout": result.stdout.strip(), "stderr": result.stderr.strip(), "returncode": result.returncode } except Exception as e: return { "success": False, "stdout": "", "stderr": str(e), "returncode": -1 } def get_backup_branches(self) -> List[str]: """Get list of backup branches for restore functionality""" try: result = subprocess.run( ["git", "branch", "-a"], cwd=self.project_root, capture_output=True, text=True, check=False ) if result.returncode == 0: branches = [] for line in result.stdout.strip().split('\n'): branch = line.strip().replace('* ', '').replace('remotes/origin/', '') if branch.startswith('backup-'): branches.append(branch) branches.sort(key=lambda x: x.replace('backup-', ''), reverse=True) return branches return [] except Exception: return [] def format_backup_time(self, time_input: str) -> Optional[str]: """Convert user time input to backup branch name format""" try: # Handle different input formats if len(time_input) == 10: # YYYY-MM-DD return f"backup-{time_input}" elif len(time_input) == 13: # YYYY-MM-DD-HH return f"backup-{time_input}" elif len(time_input) == 8: # MM-DD-HH current_year = datetime.now().year return f"backup-{current_year}-{time_input}" else: # Try to match partial patterns if '-' in time_input: parts = time_input.split('-') if len(parts) == 2 and len(parts[1]) == 2: # MM-DD current_year = datetime.now().year return f"backup-{current_year}-{parts[0]}-{parts[1]}-00" return None except Exception: return None class GitBackupCommand(GitSlashCommand): """Handle /git-backup command""" def execute(self) -> str: result = self.run_git_agent(["--backup"]) if result["success"]: return "āœ… **Backup completed successfully**\n\nAutomated backup created and pushed to remote repository." else: error_msg = result["stderr"] or result["stdout"] or "Unknown error" return f"āŒ **Backup failed**\n\nError: {error_msg}" class GitStatusCommand(GitSlashCommand): """Handle /git-status command""" def execute(self) -> str: result = self.run_git_agent(["--status"]) if result["success"]: return f"šŸ“Š **Git Agent Status**\n\n```\n{result['stdout']}\n```" else: error_msg = result["stderr"] or result["stdout"] or "Unknown error" return f"āŒ **Status check failed**\n\nError: {error_msg}" class GitCleanupCommand(GitSlashCommand): """Handle /git-cleanup command""" def execute(self) -> str: result = self.run_git_agent(["--cleanup"]) if result["success"]: return "āœ… **Cleanup completed**\n\nOld backup branches have been removed according to retention policy." else: error_msg = result["stderr"] or result["stdout"] or "Unknown error" return f"āŒ **Cleanup failed**\n\nError: {error_msg}" class GitRestoreCommand(GitSlashCommand): """Handle /git-restore command""" def execute(self, time_input: str = None) -> str: if not time_input: # Show available backups branches = self.get_backup_branches() if not branches: return "šŸ“‚ **No backup branches found**\n\nUse `/git-backup` to create a backup first." response = "šŸ“‚ **Available Backups**\n\nChoose a backup to restore:\n" for i, branch in enumerate(branches[:10]): # Show last 10 # Extract timestamp from branch name timestamp = branch.replace('backup-', '') formatted_time = self.format_timestamp_display(timestamp) response += f"• `{timestamp}` - {formatted_time}\n" if len(branches) > 10: response += f"\n... and {len(branches) - 10} more backups" response += "\n\n**Usage:** `/git-restore ` (e.g., `2025-12-19-14`)" return response # Try to restore specific backup branch_name = self.format_backup_time(time_input) if not branch_name: return f"āŒ **Invalid time format**\n\nExpected format: `YYYY-MM-DD-HH` (e.g., `2025-12-19-14`)" # Check if branch exists branches = self.get_backup_branches() matching_branches = [b for b in branches if branch_name in b] if not matching_branches: return f"āŒ **Backup not found**\n\nNo backup found for: `{time_input}`\n\nUse `/git-restore` to see available backups." # Use the most recent matching branch target_branch = matching_branches[0] try: # Checkout the backup branch result = subprocess.run( ["git", "checkout", target_branch], cwd=self.project_root, capture_output=True, text=True, check=False ) if result.returncode == 0: timestamp = target_branch.replace('backup-', '') formatted_time = self.format_timestamp_display(timestamp) return f"āœ… **Restored to backup**\n\nBranch: `{target_branch}`\nTime: {formatted_time}\n\nāš ļø **Note:** You're now on a backup branch. Use `git checkout main` to return to the main branch when done." else: return f"āŒ **Restore failed**\n\nError: {result.stderr.strip()}" except Exception as e: return f"āŒ **Restore failed**\n\nError: {str(e)}" def format_timestamp_display(self, timestamp: str) -> str: """Format backup timestamp for display""" try: if len(timestamp) >= 10: date_part = timestamp[:10] if len(timestamp) >= 13: time_part = timestamp[11:13] + ":00" return f"{date_part} {time_part} UTC" return f"{date_part}" return timestamp except Exception: return timestamp # Command registry COMMAND_HANDLERS = { "git-backup": GitBackupCommand, "git-status": GitStatusCommand, "git-cleanup": GitCleanupCommand, "git-restore": GitRestoreCommand, } def execute_command(command_name: str, args: List[str] = None) -> str: """Execute a slash command and return the response""" if args is None: args = [] handler_class = COMMAND_HANDLERS.get(command_name) if not handler_class: return f"āŒ **Unknown command:** `{command_name}`" handler = handler_class() if command_name == "git-restore": return handler.execute(args[0] if args else None) else: return handler.execute() if __name__ == "__main__": # Test functionality if len(sys.argv) > 1: command = sys.argv[1] command_args = sys.argv[2:] if len(sys.argv) > 2 else [] print(execute_command(command, command_args)) else: print("Available commands:") for cmd in COMMAND_HANDLERS.keys(): print(f" /{cmd}")