cleanup
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
use clap::{Parser, Subcommand};
|
||||
use clap::Parser;
|
||||
use g3_core::Agent;
|
||||
use g3_config::Config;
|
||||
use anyhow::Result;
|
||||
@@ -9,9 +9,6 @@ use tracing::{info, error};
|
||||
#[command(about = "A modular, composable AI coding agent")]
|
||||
#[command(version)]
|
||||
pub struct Cli {
|
||||
#[command(subcommand)]
|
||||
pub command: Option<Commands>,
|
||||
|
||||
/// Enable verbose logging
|
||||
#[arg(short, long)]
|
||||
pub verbose: bool,
|
||||
@@ -27,79 +24,9 @@ pub struct Cli {
|
||||
/// Configuration file path
|
||||
#[arg(short, long)]
|
||||
pub config: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Subcommand)]
|
||||
pub enum Commands {
|
||||
/// Solve any task by writing and executing code
|
||||
Task {
|
||||
/// Description of the task to accomplish
|
||||
description: String,
|
||||
/// Programming language to prefer (auto-detect if not specified)
|
||||
#[arg(short, long)]
|
||||
language: Option<String>,
|
||||
/// Execute the generated code automatically (default: ask for approval)
|
||||
#[arg(short, long)]
|
||||
execute: bool,
|
||||
},
|
||||
/// Create automation scripts for recurring tasks
|
||||
Automate {
|
||||
/// Description of the workflow to automate
|
||||
workflow: String,
|
||||
/// Output file for the automation script
|
||||
#[arg(short, long)]
|
||||
output: Option<String>,
|
||||
},
|
||||
/// Process and analyze data with code
|
||||
Data {
|
||||
/// Description of the data processing task
|
||||
operation: String,
|
||||
/// Input file or data source
|
||||
#[arg(short, long)]
|
||||
input: Option<String>,
|
||||
/// Output format (json, csv, text)
|
||||
#[arg(short = 'f', long, default_value = "text")]
|
||||
format: String,
|
||||
},
|
||||
/// Web-related tasks (scraping, APIs, downloads)
|
||||
Web {
|
||||
/// Description of the web task
|
||||
task: String,
|
||||
/// Target URL (if applicable)
|
||||
#[arg(short, long)]
|
||||
url: Option<String>,
|
||||
},
|
||||
/// File system operations and management
|
||||
File {
|
||||
/// Description of the file operation
|
||||
operation: String,
|
||||
/// Target path
|
||||
#[arg(short, long)]
|
||||
path: Option<String>,
|
||||
},
|
||||
/// Legacy: Analyze code and provide insights
|
||||
Analyze {
|
||||
/// Path to analyze
|
||||
path: String,
|
||||
/// Output format (json, text)
|
||||
#[arg(short, long, default_value = "text")]
|
||||
format: String,
|
||||
},
|
||||
/// Legacy: Generate code based on description
|
||||
Generate {
|
||||
/// Description of what to generate
|
||||
description: String,
|
||||
/// Output file path
|
||||
#[arg(short, long)]
|
||||
output: Option<String>,
|
||||
},
|
||||
/// Legacy: Review code and suggest improvements
|
||||
Review {
|
||||
/// Path to review
|
||||
path: String,
|
||||
},
|
||||
/// Interactive mode (default)
|
||||
Interactive,
|
||||
/// Task to execute (if provided, runs in single-shot mode instead of interactive)
|
||||
pub task: Option<String>,
|
||||
}
|
||||
|
||||
pub async fn run() -> Result<()> {
|
||||
@@ -124,80 +51,24 @@ pub async fn run() -> Result<()> {
|
||||
// Initialize agent
|
||||
let agent = Agent::new(config).await?;
|
||||
|
||||
// Execute command - default to Interactive if no command provided
|
||||
match cli.command.unwrap_or(Commands::Interactive) {
|
||||
Commands::Task { description, language, execute } => {
|
||||
info!("Executing task: {}", description);
|
||||
let result = agent.execute_task(&description, language.as_deref(), execute).await?;
|
||||
// Execute task or start interactive mode
|
||||
if let Some(task) = cli.task {
|
||||
// Single-shot mode
|
||||
info!("Executing task: {}", task);
|
||||
let result = agent.execute_task_with_timing(&task, None, false, cli.show_prompt, cli.show_code, true).await?;
|
||||
println!("{}", result);
|
||||
}
|
||||
Commands::Automate { workflow, output } => {
|
||||
info!("Creating automation: {}", workflow);
|
||||
let result = agent.create_automation(&workflow).await?;
|
||||
|
||||
if let Some(output_path) = output {
|
||||
std::fs::write(&output_path, &result)?;
|
||||
println!("Automation script written to: {}", output_path);
|
||||
} else {
|
||||
println!("{}", result);
|
||||
}
|
||||
}
|
||||
Commands::Data { operation, input, format } => {
|
||||
info!("Processing data: {}", operation);
|
||||
let result = agent.process_data(&operation, input.as_deref()).await?;
|
||||
|
||||
match format.as_str() {
|
||||
"json" => println!("{}", serde_json::to_string_pretty(&result)?),
|
||||
_ => println!("{}", result),
|
||||
}
|
||||
}
|
||||
Commands::Web { task, url } => {
|
||||
info!("Web task: {}", task);
|
||||
let result = agent.execute_web_task(&task, url.as_deref()).await?;
|
||||
println!("{}", result);
|
||||
}
|
||||
Commands::File { operation, path } => {
|
||||
info!("File operation: {}", operation);
|
||||
let result = agent.execute_file_operation(&operation, path.as_deref()).await?;
|
||||
println!("{}", result);
|
||||
}
|
||||
Commands::Analyze { path, format } => {
|
||||
info!("Analyzing: {}", path);
|
||||
let result = agent.analyze(&path).await?;
|
||||
|
||||
match format.as_str() {
|
||||
"json" => println!("{}", serde_json::to_string_pretty(&result)?),
|
||||
_ => println!("{}", result),
|
||||
}
|
||||
}
|
||||
Commands::Generate { description, output } => {
|
||||
info!("Generating code: {}", description);
|
||||
let result = agent.generate(&description).await?;
|
||||
|
||||
if let Some(output_path) = output {
|
||||
std::fs::write(&output_path, &result)?;
|
||||
println!("Generated code written to: {}", output_path);
|
||||
} else {
|
||||
println!("{}", result);
|
||||
}
|
||||
}
|
||||
Commands::Review { path } => {
|
||||
info!("Reviewing: {}", path);
|
||||
let result = agent.review(&path).await?;
|
||||
println!("{}", result);
|
||||
}
|
||||
Commands::Interactive => {
|
||||
// Interactive mode (default)
|
||||
info!("Starting interactive mode");
|
||||
run_interactive(agent, cli.show_prompt, cli.show_code).await?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn run_interactive(agent: Agent, show_prompt: bool, show_code: bool) -> Result<()> {
|
||||
println!("🤖 G3 General Purpose AI Agent - Interactive Mode");
|
||||
println!("I solve problems by writing code. Tell me what you need to accomplish!");
|
||||
println!("🤖 G3 AI Coding Agent - Interactive Mode");
|
||||
println!("I solve problems by writing and executing code. Tell me what you need to accomplish!");
|
||||
println!();
|
||||
println!("Type 'exit' or 'quit' to exit");
|
||||
println!();
|
||||
@@ -219,32 +90,7 @@ async fn run_interactive(agent: Agent, show_prompt: bool, show_code: bool) -> Re
|
||||
continue;
|
||||
}
|
||||
|
||||
// Handle legacy commands
|
||||
if let Some(path) = input.strip_prefix("analyze ") {
|
||||
match agent.analyze(path).await {
|
||||
Ok(result) => println!("{}", result),
|
||||
Err(e) => error!("Error analyzing {}: {}", path, e),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(description) = input.strip_prefix("generate ") {
|
||||
match agent.generate(description).await {
|
||||
Ok(result) => println!("{}", result),
|
||||
Err(e) => error!("Error generating code: {}", e),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(path) = input.strip_prefix("review ") {
|
||||
match agent.review(path).await {
|
||||
Ok(result) => println!("{}", result),
|
||||
Err(e) => error!("Error reviewing {}: {}", path, e),
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Default to task execution (code-first approach)
|
||||
// Execute task (code-first approach)
|
||||
match agent.execute_task_with_timing(input, None, false, show_prompt, show_code, true).await {
|
||||
Ok(response) => println!("{}", response),
|
||||
Err(e) => error!("Error: {}", e),
|
||||
|
||||
@@ -13,43 +13,6 @@ pub struct Agent {
|
||||
config: Config,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct AnalysisResult {
|
||||
pub summary: String,
|
||||
pub issues: Vec<Issue>,
|
||||
pub suggestions: Vec<Suggestion>,
|
||||
pub metrics: CodeMetrics,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Issue {
|
||||
pub severity: IssueSeverity,
|
||||
pub message: String,
|
||||
pub line: Option<u32>,
|
||||
pub column: Option<u32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum IssueSeverity {
|
||||
Error,
|
||||
Warning,
|
||||
Info,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Suggestion {
|
||||
pub description: String,
|
||||
pub code_example: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct CodeMetrics {
|
||||
pub lines_of_code: u32,
|
||||
pub complexity_score: f32,
|
||||
pub maintainability_index: f32,
|
||||
}
|
||||
|
||||
impl Agent {
|
||||
pub async fn new(config: Config) -> Result<Self> {
|
||||
let mut providers = ProviderRegistry::new();
|
||||
@@ -91,101 +54,6 @@ impl Agent {
|
||||
Ok(Self { providers, config })
|
||||
}
|
||||
|
||||
pub async fn analyze(&self, path: &str) -> Result<AnalysisResult> {
|
||||
info!("Analyzing path: {}", path);
|
||||
|
||||
let content = self.read_file_or_directory(path)?;
|
||||
let provider = self.providers.get(None)?;
|
||||
|
||||
let messages = vec![
|
||||
Message {
|
||||
role: MessageRole::System,
|
||||
content: "You are a code analysis expert. Analyze the provided code and return a detailed analysis including issues, suggestions, and metrics.".to_string(),
|
||||
},
|
||||
Message {
|
||||
role: MessageRole::User,
|
||||
content: format!("Please analyze this code:\n\n{}", content),
|
||||
},
|
||||
];
|
||||
|
||||
let request = CompletionRequest {
|
||||
messages,
|
||||
max_tokens: Some(2048),
|
||||
temperature: Some(0.1),
|
||||
stream: false,
|
||||
};
|
||||
|
||||
let response = provider.complete(request).await?;
|
||||
|
||||
// For now, return a simplified analysis
|
||||
// In a real implementation, we'd parse the LLM response into structured data
|
||||
Ok(AnalysisResult {
|
||||
summary: response.content,
|
||||
issues: vec![],
|
||||
suggestions: vec![],
|
||||
metrics: CodeMetrics {
|
||||
lines_of_code: content.lines().count() as u32,
|
||||
complexity_score: 1.0,
|
||||
maintainability_index: 85.0,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn generate(&self, description: &str) -> Result<String> {
|
||||
info!("Generating code for: {}", description);
|
||||
|
||||
let provider = self.providers.get(None)?;
|
||||
|
||||
let messages = vec![
|
||||
Message {
|
||||
role: MessageRole::System,
|
||||
content: "You are a code generation expert. Generate clean, well-documented code based on the user's description.".to_string(),
|
||||
},
|
||||
Message {
|
||||
role: MessageRole::User,
|
||||
content: description.to_string(),
|
||||
},
|
||||
];
|
||||
|
||||
let request = CompletionRequest {
|
||||
messages,
|
||||
max_tokens: Some(2048),
|
||||
temperature: Some(0.2),
|
||||
stream: false,
|
||||
};
|
||||
|
||||
let response = provider.complete(request).await?;
|
||||
Ok(response.content)
|
||||
}
|
||||
|
||||
pub async fn review(&self, path: &str) -> Result<String> {
|
||||
info!("Reviewing path: {}", path);
|
||||
|
||||
let content = self.read_file_or_directory(path)?;
|
||||
let provider = self.providers.get(None)?;
|
||||
|
||||
let messages = vec![
|
||||
Message {
|
||||
role: MessageRole::System,
|
||||
content: "You are a code review expert. Review the provided code and suggest improvements focusing on best practices, performance, and maintainability.".to_string(),
|
||||
},
|
||||
Message {
|
||||
role: MessageRole::User,
|
||||
content: format!("Please review this code:\n\n{}", content),
|
||||
},
|
||||
];
|
||||
|
||||
let request = CompletionRequest {
|
||||
messages,
|
||||
max_tokens: Some(2048),
|
||||
temperature: Some(0.1),
|
||||
stream: false,
|
||||
};
|
||||
|
||||
let response = provider.complete(request).await?;
|
||||
Ok(response.content)
|
||||
}
|
||||
|
||||
pub async fn execute_task(
|
||||
&self,
|
||||
description: &str,
|
||||
@@ -240,19 +108,19 @@ When given a task:
|
||||
4. Add error handling where appropriate
|
||||
5. EXECUTE the code immediately to solve the user's problem
|
||||
|
||||
Prefer these languages for different tasks (in order of preference):
|
||||
- Bash/Shell: File operations, system administration, simple tasks, process management, text processing
|
||||
- Python: Complex data processing, web scraping, APIs, when libraries are needed
|
||||
Prefer these languages:
|
||||
- Bash/Shell: File operations, system administration, simple tasks
|
||||
- Python: Complex data processing, when libraries are needed
|
||||
- Rust: Performance-critical tasks, system programming
|
||||
|
||||
For simple tasks like listing files, checking processes, basic text manipulation, etc. - prefer bash/shell.
|
||||
Only use Rust/Python when you need libraries or complex logic that bash can't handle easily.
|
||||
|
||||
Format your response as:
|
||||
```[language]
|
||||
[code]
|
||||
```
|
||||
Then execute it and show the output.",
|
||||
|
||||
with nothing afterwards.",
|
||||
if let Some(lang) = language {
|
||||
format!(" (prefer {})", lang)
|
||||
} else {
|
||||
@@ -291,13 +159,6 @@ Then execute it and show the output.",
|
||||
let response = provider.complete(request).await?;
|
||||
let llm_duration = llm_start.elapsed();
|
||||
|
||||
// Log the LLM response before passing to CodeExecutor
|
||||
// debug!(
|
||||
// "LLM Response received ({} chars): {}\n=== END ===",
|
||||
// response.content.len(),
|
||||
// response.content
|
||||
// );
|
||||
|
||||
// Time the code execution
|
||||
let exec_start = Instant::now();
|
||||
let executor = CodeExecutor::new();
|
||||
@@ -310,10 +171,10 @@ Then execute it and show the output.",
|
||||
|
||||
if show_timing {
|
||||
let timing_summary = format!(
|
||||
"\n⏱️ Task Summary:\n LLM call: {}\n Code execution: {}\n Total time: {}",
|
||||
"\n{} [💡: {} ⚡️: {}]",
|
||||
Self::format_duration(total_duration),
|
||||
Self::format_duration(llm_duration),
|
||||
Self::format_duration(exec_duration),
|
||||
Self::format_duration(total_duration)
|
||||
Self::format_duration(exec_duration)
|
||||
);
|
||||
Ok(format!("{}\n{}", result, timing_summary))
|
||||
} else {
|
||||
@@ -335,205 +196,10 @@ Then execute it and show the output.",
|
||||
format!("{}m {:.1}s", minutes, remaining_seconds)
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn create_automation(&self, workflow: &str) -> Result<String> {
|
||||
info!("Creating automation for: {}", workflow);
|
||||
|
||||
let provider = self.providers.get(None)?;
|
||||
|
||||
let messages = vec![
|
||||
Message {
|
||||
role: MessageRole::System,
|
||||
content: "You are G3, a code-first AI agent. Create automation scripts that can be saved and reused. Focus on creating robust, well-documented scripts with error handling and logging.".to_string(),
|
||||
},
|
||||
Message {
|
||||
role: MessageRole::User,
|
||||
content: format!("Create an automation script for: {}", workflow),
|
||||
},
|
||||
];
|
||||
|
||||
let request = CompletionRequest {
|
||||
messages,
|
||||
max_tokens: Some(2048),
|
||||
temperature: Some(0.1),
|
||||
stream: false,
|
||||
};
|
||||
|
||||
let response = provider.complete(request).await?;
|
||||
Ok(response.content)
|
||||
}
|
||||
|
||||
pub async fn process_data(&self, operation: &str, input_file: Option<&str>) -> Result<String> {
|
||||
info!("Processing data: {}", operation);
|
||||
|
||||
let provider = self.providers.get(None)?;
|
||||
|
||||
let context = if let Some(file) = input_file {
|
||||
format!("Operation: {}\nInput file: {}", operation, file)
|
||||
} else {
|
||||
format!("Operation: {}", operation)
|
||||
};
|
||||
|
||||
let messages = vec![
|
||||
Message {
|
||||
role: MessageRole::System,
|
||||
content: "You are G3, a code-first AI agent specializing in data processing. Write Python code using pandas, numpy, or other appropriate libraries to process and analyze data. Always include data validation and error handling.".to_string(),
|
||||
},
|
||||
Message {
|
||||
role: MessageRole::User,
|
||||
content: context,
|
||||
},
|
||||
];
|
||||
|
||||
let request = CompletionRequest {
|
||||
messages,
|
||||
max_tokens: Some(2048),
|
||||
temperature: Some(0.1),
|
||||
stream: false,
|
||||
};
|
||||
|
||||
let response = provider.complete(request).await?;
|
||||
Ok(response.content)
|
||||
}
|
||||
|
||||
pub async fn execute_web_task(&self, task: &str, url: Option<&str>) -> Result<String> {
|
||||
info!("Executing web task: {}", task);
|
||||
|
||||
let provider = self.providers.get(None)?;
|
||||
|
||||
let context = if let Some(url) = url {
|
||||
format!("Task: {}\nURL: {}", task, url)
|
||||
} else {
|
||||
format!("Task: {}", task)
|
||||
};
|
||||
|
||||
let messages = vec![
|
||||
Message {
|
||||
role: MessageRole::System,
|
||||
content: "You are G3, a code-first AI agent for web tasks. Write code for web scraping, API calls, downloads, or web automation. Use appropriate libraries like requests, BeautifulSoup, selenium, or similar. Always respect robots.txt and rate limits.".to_string(),
|
||||
},
|
||||
Message {
|
||||
role: MessageRole::User,
|
||||
content: context,
|
||||
},
|
||||
];
|
||||
|
||||
let request = CompletionRequest {
|
||||
messages,
|
||||
max_tokens: Some(2048),
|
||||
temperature: Some(0.2),
|
||||
stream: false,
|
||||
};
|
||||
|
||||
let response = provider.complete(request).await?;
|
||||
Ok(response.content)
|
||||
}
|
||||
|
||||
pub async fn execute_file_operation(
|
||||
&self,
|
||||
operation: &str,
|
||||
path: Option<&str>,
|
||||
) -> Result<String> {
|
||||
info!("Executing file operation: {}", operation);
|
||||
|
||||
let provider = self.providers.get(None)?;
|
||||
|
||||
let context = if let Some(path) = path {
|
||||
format!("Operation: {}\nPath: {}", operation, path)
|
||||
} else {
|
||||
format!("Operation: {}", operation)
|
||||
};
|
||||
|
||||
let messages = vec![
|
||||
Message {
|
||||
role: MessageRole::System,
|
||||
content: "You are G3, a code-first AI agent for file operations. Write scripts (bash, Python, or other appropriate languages) for file management, organization, backup, compression, format conversion, etc. Always include safety checks and confirmation prompts for destructive operations.".to_string(),
|
||||
},
|
||||
Message {
|
||||
role: MessageRole::User,
|
||||
content: context,
|
||||
},
|
||||
];
|
||||
|
||||
let request = CompletionRequest {
|
||||
messages,
|
||||
max_tokens: Some(2048),
|
||||
temperature: Some(0.1),
|
||||
stream: false,
|
||||
};
|
||||
|
||||
let response = provider.complete(request).await?;
|
||||
Ok(response.content)
|
||||
}
|
||||
|
||||
fn read_file_or_directory(&self, path: &str) -> Result<String> {
|
||||
let path = Path::new(path);
|
||||
|
||||
if path.is_file() {
|
||||
Ok(std::fs::read_to_string(path)?)
|
||||
} else if path.is_dir() {
|
||||
// For directories, read multiple files and combine them
|
||||
let mut content = String::new();
|
||||
self.read_directory_recursive(path, &mut content)?;
|
||||
Ok(content)
|
||||
} else {
|
||||
anyhow::bail!("Path does not exist: {}", path.display())
|
||||
}
|
||||
}
|
||||
|
||||
fn read_directory_recursive(&self, dir: &Path, content: &mut String) -> Result<()> {
|
||||
for entry in std::fs::read_dir(dir)? {
|
||||
let entry = entry?;
|
||||
let path = entry.path();
|
||||
|
||||
if path.is_file() {
|
||||
if let Some(ext) = path.extension() {
|
||||
// Only read common code files
|
||||
if matches!(
|
||||
ext.to_str(),
|
||||
Some("rs" | "py" | "js" | "ts" | "go" | "java" | "cpp" | "c" | "h")
|
||||
) {
|
||||
content.push_str(&format!("\n--- {} ---\n", path.display()));
|
||||
if let Ok(file_content) = std::fs::read_to_string(&path) {
|
||||
content.push_str(&file_content);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if path.is_dir() && !path.file_name().unwrap().to_str().unwrap().starts_with('.')
|
||||
{
|
||||
self.read_directory_recursive(&path, content)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for AnalysisResult {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
writeln!(f, "Code Analysis Results")?;
|
||||
writeln!(f, "====================")?;
|
||||
writeln!(f)?;
|
||||
writeln!(f, "Summary:")?;
|
||||
writeln!(f, "{}", self.summary)?;
|
||||
writeln!(f)?;
|
||||
writeln!(f, "Metrics:")?;
|
||||
writeln!(f, "- Lines of Code: {}", self.metrics.lines_of_code)?;
|
||||
writeln!(
|
||||
f,
|
||||
"- Complexity Score: {:.2}",
|
||||
self.metrics.complexity_score
|
||||
)?;
|
||||
writeln!(
|
||||
f,
|
||||
"- Maintainability Index: {:.2}",
|
||||
self.metrics.maintainability_index
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub mod providers {
|
||||
pub mod anthropic;
|
||||
pub mod openai;
|
||||
pub mod embedded;
|
||||
pub mod openai;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user