Major enhancements to Clover CLI: ✨ New Features: - AI agent with multi-turn conversation capabilities - Tool calling system with 11+ tools for file operations, Git, linting, etc. - Step-by-step AI assistance with play-by-play commentary - Enhanced interactive mode with better UX 🔧 Core Components Added: - ai_agent.py: Main AI agent with conversation management - models/: API client and model management system - Comprehensive tool system for development tasks 🛠️ Tools Available: - File operations (create, read, update, delete) - Command execution with safety checks - Git operations (status, diff, commit, push) - Code linting and formatting - Project structure analysis - Security scanning and dependency management 💡 User Experience: - Real-time tool execution summaries - File creation with full path visibility - Error handling and retry mechanisms - Clean conversation flow until task completion 🧹 Repository Cleanup: - Added comprehensive .gitignore - Removed __pycache__ directories and build artifacts - Organized project structure The AI can now actually create files, run commands, and work through complex development tasks step-by-step with full transparency.
179 lines
6.5 KiB
Python
179 lines
6.5 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Clover - A CLI tool for working with AI models to build and manage projects.
|
|
Interactive mode where you can run commands without prefixing 'clover'.
|
|
"""
|
|
|
|
import argparse
|
|
import os
|
|
import sys
|
|
|
|
# Add the current directory to Python path
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
|
|
|
from cli.commands import handle_command
|
|
from cli.parser import parse_args, print_help
|
|
|
|
|
|
def interactive_mode():
|
|
"""Run Clover in interactive mode"""
|
|
print("🍀 Clover Interactive AI Development Assistant")
|
|
print("=" * 60)
|
|
print("Welcome to Clover! An AI-powered development assistant that can:")
|
|
print(" • Create, edit, and manage files")
|
|
print(" • Execute commands and test code")
|
|
print(" • Work through problems step-by-step")
|
|
print(" • Use tools to solve complex development tasks")
|
|
print("")
|
|
print("🔧 Available Commands:")
|
|
print("- /init : Initialize project summary file")
|
|
print("- /list : List available AI models")
|
|
print("- /timeout SECS : Set timeout duration")
|
|
print("- /threads N : Set thread limit")
|
|
print("- /git_status : Check Git repository status")
|
|
print("- /lint_file FILE : Lint a specific file")
|
|
print("- /reset : Reset AI conversation history")
|
|
print("- /summary : Show AI conversation summary")
|
|
print("- /help : Show this help")
|
|
print("- /quit or /exit: Exit interactive mode")
|
|
print("")
|
|
print("💡 Just describe what you want to build and the AI will:")
|
|
print(" → Break down the task into steps")
|
|
print(" → Create and test files as needed")
|
|
print(" → Continue until the task is complete")
|
|
print("=" * 60)
|
|
print("🚀 Ready! Enter your development request below:")
|
|
|
|
while True:
|
|
try:
|
|
# Get user input
|
|
user_input = input("\n🍀 > ").strip()
|
|
|
|
# Handle exit commands
|
|
if user_input.lower() in ["/quit", "/exit", "exit", "quit"]:
|
|
print("\n👋 Thanks for using Clover! Goodbye!")
|
|
break
|
|
|
|
# Handle help command
|
|
if user_input.lower() == "/help":
|
|
print_help()
|
|
continue
|
|
|
|
# If input is empty, continue
|
|
if not user_input:
|
|
continue
|
|
|
|
# Parse and handle the command (prefix with clover for parsing)
|
|
# We'll create a fake args object for command handling in interactive mode
|
|
if user_input.startswith("/"):
|
|
# This is already a special command - no prefix needed
|
|
args = argparse.Namespace()
|
|
if user_input == "/init":
|
|
args.init = True
|
|
args.list = False
|
|
args.timeout = None
|
|
args.threads = None
|
|
args.prompt = None
|
|
elif user_input == "/list":
|
|
args.init = False
|
|
args.list = True
|
|
args.timeout = None
|
|
args.threads = None
|
|
args.prompt = None
|
|
elif user_input.startswith("/timeout "):
|
|
try:
|
|
seconds = int(user_input.split()[1])
|
|
args.init = False
|
|
args.list = False
|
|
args.timeout = seconds
|
|
args.threads = None
|
|
args.prompt = None
|
|
except (ValueError, IndexError):
|
|
print("Usage: /timeout <seconds>")
|
|
continue
|
|
elif user_input.startswith("/threads "):
|
|
try:
|
|
count = int(user_input.split()[1])
|
|
args.init = False
|
|
args.list = False
|
|
args.timeout = None
|
|
args.threads = count
|
|
args.prompt = None
|
|
except (ValueError, IndexError):
|
|
print("Usage: /threads <count>")
|
|
continue
|
|
elif user_input == "/git_status":
|
|
# For git commands we might need to handle differently
|
|
args.init = False
|
|
args.list = False
|
|
args.timeout = None
|
|
args.threads = None
|
|
args.prompt = "/git_status"
|
|
elif user_input.startswith("/lint_file "):
|
|
args.init = False
|
|
args.list = False
|
|
args.timeout = None
|
|
args.threads = None
|
|
args.prompt = user_input
|
|
elif user_input == "/reset":
|
|
args.init = False
|
|
args.list = False
|
|
args.timeout = None
|
|
args.threads = None
|
|
args.prompt = "/reset"
|
|
elif user_input == "/summary":
|
|
args.init = False
|
|
args.list = False
|
|
args.timeout = None
|
|
args.threads = None
|
|
args.prompt = "/summary"
|
|
else:
|
|
# Treat as regular command for now
|
|
args = argparse.Namespace()
|
|
args.init = False
|
|
args.list = False
|
|
args.timeout = None
|
|
args.threads = None
|
|
args.prompt = user_input
|
|
|
|
try:
|
|
handle_command(args)
|
|
except Exception as e:
|
|
print(f"Error executing command: {e}")
|
|
else:
|
|
# Treat as regular query
|
|
args = argparse.Namespace()
|
|
args.init = False
|
|
args.list = False
|
|
args.timeout = None
|
|
args.threads = None
|
|
args.prompt = user_input
|
|
try:
|
|
handle_command(args)
|
|
except Exception as e:
|
|
print(f"Error executing command: {e}")
|
|
|
|
except KeyboardInterrupt:
|
|
print("\n\n🛑 Session interrupted. Thanks for using Clover!")
|
|
break
|
|
except EOFError:
|
|
print("\n👋 Thanks for using Clover! Goodbye!")
|
|
break
|
|
|
|
|
|
def main():
|
|
"""Main entry point"""
|
|
# Parse arguments
|
|
args = parse_args()
|
|
|
|
# Check if running in interactive mode (no arguments provided)
|
|
if len(sys.argv) == 1:
|
|
interactive_mode()
|
|
else:
|
|
# Run single command mode
|
|
handle_command(args)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|