1use clap::{Parser, Subcommand};
4use std::io::{self, Write};
5use crate::config::AgentConfig;
6use crate::models::LanguageModel;
7
8#[derive(Parser)]
9#[command(name = "ai-agent")]
10#[command(about = "AI-Native Code Assistant")]
11#[command(version)]
12pub struct Cli {
13 #[command(subcommand)]
14 pub command: Commands,
15}
16
17#[derive(Subcommand)]
18pub enum Commands {
19 Task {
21 task: String,
23 #[arg(short, long, default_value = "config.toml")]
25 config: String,
26 #[arg(short, long, default_value = "text")]
28 output: String,
29 },
30 Interactive {
32 #[arg(short, long, default_value = "config.toml")]
34 config: String,
35 },
36 Tools {
38 #[arg(short, long, default_value = "config.toml")]
40 config: String,
41 },
42 Config {
44 #[arg(short, long, default_value = "config.toml")]
46 config: String,
47 },
48}
49
50impl Cli {
51 pub async fn run(self) -> anyhow::Result<()> {
53 match self.command {
54 Commands::Task { task, config, output } => {
55 Self::handle_task(task, config, output).await
56 }
57 Commands::Interactive { config } => {
58 Self::handle_interactive(config).await
59 }
60 Commands::Tools { config } => {
61 Self::handle_tools(config).await
62 }
63 Commands::Config { config } => {
64 Self::handle_config(config).await
65 }
66 }
67 }
68
69 async fn handle_task(task: String, config_path: String, output: String) -> anyhow::Result<()> {
70 println!("🚀 Starting AI Agent Task Execution");
71 println!("====================================");
72 println!("📝 Task: {}", task);
73 println!("⏰ Started at: {}", chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"));
74 println!("");
75
76 tracing::info!("Processing task: {}", task);
77
78 println!("🔧 Loading configuration...");
79 let config = AgentConfig::load_with_fallback(&config_path)
80 .map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
81 println!("✅ Configuration loaded successfully");
82
83 println!("🤖 Initializing AI agent...");
84 let mut agent = create_agent(&config).await?;
85 let tools = agent.get_tools().await;
86 let tool_count = tools.lock().await.get_tool_names().len();
87 println!("✅ Agent initialized with {} tools", tool_count);
88
89 println!("🧠 Processing task with AI model...");
90 println!("📋 Creating task plan...");
91 let start_time = std::time::Instant::now();
92 let result = agent.process_task(&task).await;
93 let duration = start_time.elapsed();
94
95 println!("🏁 Task execution completed in {:.2}s", duration.as_secs_f32());
96 println!("====================================");
97
98 match result {
99 Ok(task_result) => {
100 println!("✅ Task Status: SUCCESS");
101 println!("⏱️ Execution Time: {}s", task_result.execution_time.unwrap_or(0));
102
103 match output.as_str() {
104 "json" => {
105 println!("📄 Output (JSON format):");
106 println!("{}", serde_json::to_string_pretty(&task_result)?);
107 }
108 "verbose" => {
109 println!("📋 Task Plan:");
110 if let Some(plan) = &task_result.task_plan {
111 println!(" 🧠 Understanding: {}", plan.understanding);
112 println!(" 🛠️ Approach: {}", plan.approach);
113 println!(" 📊 Complexity: {:?}", plan.complexity);
114 println!(" 🔢 Estimated Steps: {}", plan.estimated_steps.unwrap_or(0));
115 if !plan.requirements.is_empty() {
116 println!(" 📋 Requirements: {}", plan.requirements.join(", "));
117 }
118 }
119 println!("");
120 println!("📋 Summary:");
121 println!(" {}", task_result.summary);
122 if let Some(details) = task_result.details {
123 println!("");
124 println!("🔍 Detailed Results:");
125 println!(" {}", details.replace('\n', "\n "));
126 }
127 if task_result.execution_time.is_some() {
128 println!("");
129 println!("📊 Performance Metrics:");
130 println!(" • Internal execution time: {}s", task_result.execution_time.unwrap_or(0));
131 println!(" • Total wall-clock time: {:.2}s", duration.as_secs_f32());
132 }
133 }
134 _ => {
135 println!("📋 Result:");
136 println!("{}", task_result.summary);
137 if let Some(details) = task_result.details {
138 println!("");
139 println!("🔍 Details:");
140 println!("{}", details);
141 }
142 }
143 }
144 }
145 Err(e) => {
146 println!("❌ Task Status: FAILED");
147 println!("🚨 Error Details:");
148 println!(" {}", e);
149 }
150 }
151
152 println!("====================================");
153 println!("🏁 Task execution finished at: {}", chrono::Utc::now().format("%Y-%m-%d %H:%M:%S UTC"));
154 println!("");
155
156 Ok(())
157 }
158
159 async fn handle_interactive(config_path: String) -> anyhow::Result<()> {
160 let config = AgentConfig::load_with_fallback(&config_path)
161 .map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
162 let mut agent = create_agent(&config).await?;
163
164 println!("AI-Native Code Agent - Interactive Mode");
165 println!("Type 'exit' or 'quit' to exit");
166 println!("Type 'help' for available commands");
167 println!();
168
169 loop {
170 print!("> ");
171 io::stdout().flush()?;
172
173 let mut input = String::new();
174 io::stdin().read_line(&mut input)?;
175
176 let input = input.trim();
177 if input.is_empty() {
178 continue;
179 }
180
181 match input {
182 "exit" | "quit" => {
183 println!("Goodbye!");
184 break;
185 }
186 "help" => {
187 Self::print_help();
188 }
189 "tools" => {
190 Self::print_available_tools(&agent).await;
191 }
192 _ => {
193 let result = agent.process_task(input).await;
194 match result {
195 Ok(task_result) => {
196 println!("✅ {}", task_result.summary);
197 if let Some(details) = task_result.details {
198 println!("\nDetails:\n{}", details);
199 }
200 }
201 Err(e) => {
202 println!("❌ Error: {}", e);
203 }
204 }
205 println!();
206 }
207 }
208 }
209
210 Ok(())
211 }
212
213 async fn handle_tools(config_path: String) -> anyhow::Result<()> {
214 let config = AgentConfig::load_with_fallback(&config_path)
215 .map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
216 let agent = create_agent(&config).await?;
217
218 Self::print_available_tools(&agent).await;
219 Ok(())
220 }
221
222 async fn handle_config(config_path: String) -> anyhow::Result<()> {
223 let config = AgentConfig::load_with_fallback(&config_path)
224 .map_err(|e| anyhow::anyhow!("Failed to load config: {}", e))?;
225 println!("Configuration:");
226 println!("{:#?}", config);
227 Ok(())
228 }
229
230 fn print_help() {
231 println!("Available commands:");
232 println!(" exit, quit - Exit the program");
233 println!(" help - Show this help message");
234 println!(" tools - List available tools");
235 println!(" config - Show current configuration");
236 println!(" Any other text will be processed as a task");
237 println!();
238 }
239
240 async fn print_available_tools(agent: &crate::agent::CodeAgent) {
241 let tools = agent.get_tools().await;
242 let tool_names = tools.lock().await.get_tool_names();
243
244 println!("Available tools:");
245 for tool_name in tool_names {
246 if let Some(tool) = tools.lock().await.get_tool(&tool_name) {
247 println!(" • {} - {}", tool.name(), tool.description());
248 }
249 }
250 println!();
251 }
252}
253
254async fn create_agent(config: &AgentConfig) -> anyhow::Result<crate::agent::CodeAgent> {
256 let model: Box<dyn LanguageModel> = match &config.model.provider {
258 crate::config::ModelProvider::OpenAI => {
259 let api_key = config.model.api_key.clone()
260 .ok_or_else(|| anyhow::anyhow!("OpenAI API key not found"))?;
261 Box::new(crate::models::OpenAIModel::new(api_key, config.model.model_name.clone()))
262 }
263 crate::config::ModelProvider::Anthropic => {
264 let api_key = config.model.api_key.clone()
265 .ok_or_else(|| anyhow::anyhow!("Anthropic API key not found"))?;
266 Box::new(crate::models::AnthropicModel::new(api_key, config.model.model_name.clone()))
267 }
268 crate::config::ModelProvider::Zhipu => {
269 let api_key = config.model.api_key.clone()
270 .ok_or_else(|| anyhow::anyhow!("Zhipu API key not found"))?;
271 Box::new(crate::models::ZhipuModel::new(api_key, config.model.model_name.clone(), config.model.endpoint.clone()))
272 }
273 crate::config::ModelProvider::Local(ref endpoint) => {
274 Box::new(crate::models::LocalModel::new(endpoint.clone(), config.model.model_name.clone()))
275 }
276 };
277
278 let mut agent = crate::agent::CodeAgent::new(model, config.clone());
279
280 agent.register_tool(crate::tools::ReadFileTool).await;
282 agent.register_tool(crate::tools::WriteFileTool).await;
283 agent.register_tool(crate::tools::RunCommandTool).await;
284 agent.register_tool(crate::tools::ListFilesTool).await;
285
286 Ok(agent)
287}