use clap::{Parser, Subcommand};
use std::io::{self, Write};
use crate::config::AgentConfig;
use crate::models::LanguageModel;
#[derive(Parser)]
#[command(name = "ai-agent")]
#[command(about = "AI-Native Code Assistant")]
#[command(version)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
Task {
task: String,
#[arg(short, long, default_value = "config.toml")]
config: String,
#[arg(short, long, default_value = "text")]
output: String,
},
Interactive {
#[arg(short, long, default_value = "config.toml")]
config: String,
},
Tools {
#[arg(short, long, default_value = "config.toml")]
config: String,
},
Config {
#[arg(short, long, default_value = "config.toml")]
config: String,
},
}
impl Cli {
pub async fn run(self) -> anyhow::Result<()> {
match self.command {
Commands::Task { task, config, output } => {
Self::handle_task(task, config, output).await
}
Commands::Interactive { config } => {
Self::handle_interactive(config).await
}
Commands::Tools { config } => {
Self::handle_tools(config).await
}
Commands::Config { config } => {
Self::handle_config(config).await
}
}
}
async fn handle_task(task: String, config_path: String, output: String) -> anyhow::Result<()> {
println!("🚀 Starting AI Agent Task Execution");
println!("====================================");
println!("📝 Task: {}", task);
println!("⏰ Started at: {}", chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"));
println!("");
tracing::info!("Processing task: {}", task);
println!("🔧 Loading configuration...");
let config = AgentConfig::load_with_fallback(&config_path)
.map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
println!("✅ Configuration loaded successfully");
println!("🤖 Initializing AI agent...");
let mut agent = create_agent(&config).await?;
let tools = agent.get_tools().await;
let tool_count = tools.lock().await.get_tool_names().len();
println!("✅ Agent initialized with {} tools", tool_count);
println!("🧠 Processing task with AI model...");
println!("📋 Creating task plan...");
let start_time = std::time::Instant::now();
let result = agent.process_task(&task).await;
let duration = start_time.elapsed();
println!("🏁 Task execution completed in {:.2}s", duration.as_secs_f32());
println!("====================================");
match result {
Ok(task_result) => {
println!("✅ Task Status: SUCCESS");
println!("⏱️ Execution Time: {}s", task_result.execution_time.unwrap_or(0));
match output.as_str() {
"json" => {
println!("📄 Output (JSON format):");
println!("{}", serde_json::to_string_pretty(&task_result)?);
}
"verbose" => {
println!("📋 Task Plan:");
if let Some(plan) = &task_result.task_plan {
println!(" 🧠 Understanding: {}", plan.understanding);
println!(" 🛠️ Approach: {}", plan.approach);
println!(" 📊 Complexity: {:?}", plan.complexity);
println!(" 🔢 Estimated Steps: {}", plan.estimated_steps.unwrap_or(0));
if !plan.requirements.is_empty() {
println!(" 📋 Requirements: {}", plan.requirements.join(", "));
}
}
println!("");
println!("📋 Summary:");
println!(" {}", task_result.summary);
if let Some(details) = task_result.details {
println!("");
println!("🔍 Detailed Results:");
println!(" {}", details.replace('\n', "\n "));
}
if task_result.execution_time.is_some() {
println!("");
println!("📊 Performance Metrics:");
println!(" • Internal execution time: {}s", task_result.execution_time.unwrap_or(0));
println!(" • Total wall-clock time: {:.2}s", duration.as_secs_f32());
}
}
_ => {
println!("📋 Result:");
println!("{}", task_result.summary);
if let Some(details) = task_result.details {
println!("");
println!("🔍 Details:");
println!("{}", details);
}
}
}
}
Err(e) => {
println!("❌ Task Status: FAILED");
println!("🚨 Error Details:");
println!(" {}", e);
}
}
println!("====================================");
println!("🏁 Task execution finished at: {}", chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"));
println!("");
Ok(())
}
async fn handle_interactive(config_path: String) -> anyhow::Result<()> {
let config = AgentConfig::load_with_fallback(&config_path)
.map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
let mut agent = create_agent(&config).await?;
println!("AI-Native Code Agent - Interactive Mode");
println!("Type 'exit' or 'quit' to exit");
println!("Type 'help' for available commands");
println!();
loop {
print!("> ");
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
let input = input.trim();
if input.is_empty() {
continue;
}
match input {
"exit" | "quit" => {
println!("Goodbye!");
break;
}
"help" => {
Self::print_help();
}
"tools" => {
Self::print_available_tools(&agent).await;
}
_ => {
let result = agent.process_task(input).await;
match result {
Ok(task_result) => {
println!("✅ {}", task_result.summary);
if let Some(details) = task_result.details {
println!("\nDetails:\n{}", details);
}
}
Err(e) => {
println!("❌ Error: {}", e);
}
}
println!();
}
}
}
Ok(())
}
async fn handle_tools(config_path: String) -> anyhow::Result<()> {
let config = AgentConfig::load_with_fallback(&config_path)
.map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
let agent = create_agent(&config).await?;
Self::print_available_tools(&agent).await;
Ok(())
}
async fn handle_config(config_path: String) -> anyhow::Result<()> {
let config = AgentConfig::load_with_fallback(&config_path)
.map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
println!("Configuration:");
println!("{:#?}", config);
Ok(())
}
fn print_help() {
println!("Available commands:");
println!(" exit, quit - Exit the program");
println!(" help - Show this help message");
println!(" tools - List available tools");
println!(" config - Show current configuration");
println!(" Any other text will be processed as a task");
println!();
}
async fn print_available_tools(agent: &crate::agent::CodeAgent) {
let tools = agent.get_tools().await;
let tool_names = tools.lock().await.get_tool_names();
println!("Available tools:");
for tool_name in tool_names {
if let Some(tool) = tools.lock().await.get_tool(&tool_name) {
println!(" • {} - {}", tool.name(), tool.description());
}
}
println!();
}
}
async fn create_agent(config: &AgentConfig) -> anyhow::Result<crate::agent::CodeAgent> {
let model: Box<dyn LanguageModel> = match &config.model.provider {
crate::config::ModelProvider::OpenAI => {
let api_key = config.model.api_key.clone()
.ok_or_else(|| anyhow::anyhow!("OpenAI API key not found"))?;
Box::new(crate::models::OpenAIModel::new(api_key, config.model.model_name.clone()))
}
crate::config::ModelProvider::Anthropic => {
let api_key = config.model.api_key.clone()
.ok_or_else(|| anyhow::anyhow!("Anthropic API key not found"))?;
Box::new(crate::models::AnthropicModel::new(api_key, config.model.model_name.clone()))
}
crate::config::ModelProvider::Zhipu => {
let api_key = config.model.api_key.clone()
.ok_or_else(|| anyhow::anyhow!("Zhipu API key not found"))?;
Box::new(crate::models::ZhipuModel::new(api_key, config.model.model_name.clone(), config.model.endpoint.clone()))
}
crate::config::ModelProvider::Local(ref endpoint) => {
Box::new(crate::models::LocalModel::new(endpoint.clone(), config.model.model_name.clone()))
}
};
let mut agent = crate::agent::CodeAgent::new(model, config.clone());
agent.register_tool(crate::tools::ReadFileTool).await;
agent.register_tool(crate::tools::WriteFileTool).await;
agent.register_tool(crate::tools::RunCommandTool).await;
agent.register_tool(crate::tools::ListFilesTool).await;
Ok(agent)
}