""" Command handling module for Clover - A terminal assistant for AI-powered project management """ import os import re import sys from pathlib import Path # Add the current directory to Python path sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from ai_agent import AIAgent from config.settings import load_config from models.model_manager import ModelManager from tools.commandline_tool import commandline, safe_execute from tools.file_tools import create_file, delete_file, read_file, update_file from tools.git_tools import git_commit, git_diff, git_push, git_status from tools.lint_format_tools import format_code, lint_code from tools.project_tools import ( aggregate_summaries, get_project_structure, summarize_file, ) # Global AI agent instance ai_agent = None def reset_ai_agent(): """Reset the global AI agent to reload configuration""" global ai_agent ai_agent = None def handle_command(args): """ Handle the parsed CLI arguments and execute corresponding commands """ # Load configuration config = load_config() # Initialize AI agent (always reload to get fresh config) global ai_agent ai_agent = AIAgent() try: # Check if this is an interactive prompt (not a special command) if ( args.prompt and not args.init and not args.list and not args.timeout and not args.threads ): # Handle regular prompts - use AI agent for multi-turn conversation with tools print("\n" + "=" * 60) print("šŸ¤– AI DEVELOPMENT SESSION STARTING") print("=" * 60) print(f"šŸ“ Your Request: {args.prompt}") print("\n🧠 AI is analyzing your request and planning the approach...") print( "šŸ’” The AI will use tools to create files, run commands, and solve the task step-by-step" ) print("šŸ“Š You'll see detailed summaries of each action the AI takes") print("-" * 60) try: # Use AI agent for intelligent conversation with tool usage response = ai_agent.chat(args.prompt) print(f"\nšŸŽÆ Final Status: {response}") except Exception as e: print(f"\nāŒ Error during AI session: {e}") print("šŸ”§ Try rephrasing your request or check the system status") elif args.init: # Handle /init command init_project() elif args.list: # Handle /list command list_models(ai_agent) elif args.timeout is not None: # Handle /timeout command set_timeout(args.timeout) elif args.threads is not None: # Handle /threads command set_threads(args.threads) elif args.prompt and args.prompt == "/git_status": # Handle git status command from interactive mode print("Repository Status:") result = git_status() if "error" in result: print(f"Error: {result['error']}") else: print(f"Staged files: {len(result['staged'])}") print(f"Unstaged files: {len(result['unstaged'])}") print(f"Untracked files: {len(result['untracked'])}") elif args.prompt and args.prompt.startswith("/lint_file "): # Handle linting command try: file_path = args.prompt.split(" ", 2)[2] result = lint_code([file_path]) print(f"Linting results for {file_path}:") if "error" in result: print(f"Error: {result['error']}") else: print("Return code:", result.get("return_code")) print("Success:", result.get("success")) except Exception as e: print(f"Error linting file: {e}") elif args.prompt and args.prompt == "/reset": # Reset AI conversation if ai_agent: ai_agent.reset_conversation() else: print("No active AI session to reset.") elif args.prompt and args.prompt == "/summary": # Show conversation summary if ai_agent: summary = ai_agent.get_conversation_summary() print("šŸ“‹ Conversation Summary:") print(summary) else: print("No active AI session.") else: # No specific command, just show help for now from cli.parser import print_help print_help() except Exception as e: print(f"Error executing command: {e}") sys.exit(1) def init_project(): """Initialize project with a summary file""" try: # Create or update clover.md file project_summary = """ # Project Summary This is the project summary file for the Clover CLI tool. All project information and progress will be tracked here. ## Current Status - Project initialized - Basic structure created - Configuration loaded ## Next Steps 1. Review existing files 2. Define project goals 3. Begin implementation tasks """ with open("clover.md", "w") as f: f.write(project_summary) print("Project initialized! Created clover.md file.") # Create structure.md if it doesn't exist if not os.path.exists("structure.md"): # In a real implementation, this would call the LLM to generate structure with open("structure.md", "w") as f: f.write( "# Project Structure\n\nThis is a placeholder for the project structure generated by LLM.\n" ) print("Created structure.md file.") except Exception as e: print(f"Error initializing project: {e}") def list_models(ai_agent_instance): """List available models on the server""" try: result = ai_agent_instance.model_manager.list_models() if "error" in result: print(f"Error listing models: {result['error']}") return models = result.get("models", []) print("Available models:") for model in models: name = model.get("name", "unknown") print(f"- {name}") if not models: # Fallback to default models print("No models found, fallback to defaults:") default_models = [ "gpt-4", "gpt-3.5-turbo", "claude-3-opus", "claude-3-sonnet", "llama2-70b", "qwen3-coder:30b", ] for model in default_models: print(f"- {model}") print(f"Active model: {result.get('active_model', 'gpt-4')}") print(f"Base URL: {result.get('base_url', 'http://192.168.8.223:11434')}") except Exception as e: print(f"Error listing models: {e}") def set_timeout(seconds): """Set timeout duration for AI operations""" try: config = load_config() config["timeout"] = seconds # In a full implementation, save to config file print(f"Timeout set to {seconds} seconds") except Exception as e: print(f"Error setting timeout: {e}") def set_threads(count): """Set maximum number of threads for concurrent operations""" try: config = load_config() config["threads"] = count # In a full implementation, save to config file print(f"Thread limit set to {count}") except Exception as e: print(f"Error setting thread limit: {e}") def execute_command(cmd): """Execute a system command with permission prompt""" try: response = commandline(cmd) print(response) except Exception as e: print(f"Error executing command: {e}") def extract_code_blocks(text): """ Extract code blocks from AI response text Args: text (str): The AI response text Returns: list: List of dictionaries with 'language' and 'code' keys """ code_blocks = [] # Pattern to match code blocks with optional language specification pattern = r"```(\w+)?\n(.*?)\n```" matches = re.findall(pattern, text, re.DOTALL) for match in matches: language = match[0] if match[0] else "text" code = match[1].strip() if code: # Only add non-empty code blocks code_blocks.append({"language": language, "code": code}) return code_blocks def suggest_filename(code, language): """ Suggest a filename based on code content and language Args: code (str): The code content language (str): Programming language Returns: str: Suggested filename """ # Extract potential class names, function names, or descriptive words if language.lower() == "python": # Look for class definitions class_match = re.search(r"class\s+(\w+)", code) if class_match: return f"{class_match.group(1).lower()}.py" # Look for function definitions func_match = re.search(r"def\s+(\w+)", code) if func_match: return f"{func_match.group(1).lower()}.py" return "script.py" elif language.lower() in ["javascript", "js"]: return "script.js" elif language.lower() in ["html"]: return "index.html" elif language.lower() in ["css"]: return "styles.css" elif language.lower() in ["bash", "shell", "sh"]: return "script.sh" elif language.lower() in ["json"]: return "data.json" elif language.lower() in ["yaml", "yml"]: return "config.yml" else: return f"code.{language.lower()}" if language != "text" else "code.txt" def handle_code_blocks(response_text): """ Handle code blocks in AI response - extract and offer to save them Args: response_text (str): The AI response containing potential code blocks """ code_blocks = extract_code_blocks(response_text) if not code_blocks: return print(f"\nšŸ“ Found {len(code_blocks)} code block(s) in the response.") for i, block in enumerate(code_blocks, 1): language = block["language"] code = block["code"] suggested_name = suggest_filename(code, language) print(f"\n--- Code Block {i} ({language}) ---") print(f"Suggested filename: {suggested_name}") print("Preview:") # Show first few lines lines = code.split("\n") preview_lines = lines[:3] for line in preview_lines: print(f" {line}") if len(lines) > 3: print(f" ... ({len(lines) - 3} more lines)") try: save_choice = ( input(f"\nSave this code block? [y/N/c=custom filename]: ") .strip() .lower() ) if save_choice in ["y", "yes"]: filename = suggested_name elif save_choice in ["c", "custom"]: filename = input("Enter filename: ").strip() if not filename: print("Skipping - no filename provided") continue else: print("Skipping code block") continue # Create the file if create_file(filename, code): print(f"āœ… Created file: {filename}") else: print(f"āŒ Failed to create file: {filename}") except KeyboardInterrupt: print("\nSkipping remaining code blocks") break except EOFError: print("\nSkipping remaining code blocks") break