edit_file

This commit is contained in:
Dhanji Prasanna
2025-09-30 13:50:02 +10:00
parent 69fc3e90dc
commit 270cbae1e6
2 changed files with 161 additions and 3 deletions

View File

@@ -137,6 +137,44 @@ pub async fn run() -> Result<()> {
}
async fn run_interactive(mut agent: Agent, show_prompt: bool, show_code: bool) -> Result<()> {
// Display the flying goose ASCII art logo
println!(r#"
___
.-' `'.
/ \
| |
| ;
| /
_.._ '. .'
.-' `'-.__`\ /`
/ | `"`
| |
| |
___..--.. | |
_.--""` `"-. | |
.-' `. | |
.' `. | |
/ ___... \| |
/ _.-"" `\ | |
| _.-' | | |
| .-' | | |
| .-' | '. .'
| .-' / |`'--...--'`|
|-' / | |
/ / | G3 |
/ ___ / | |
| __..--' `'-. | | CODING |
| __.--' `| | AGENT |
| .--' | | |
|' '. | |
| | `'--...--'--'
| |
| |
'. /
`'--..__ __.-'
``''------''``
"#);
println!();
println!("🤖 G3 AI Coding Agent - Interactive Mode");
println!(
"I solve problems by writing and executing code. Tell me what you need to accomplish!"
@@ -479,4 +517,4 @@ Keep your response concise and focused on actionable items.",
Ok(())
}
use std::io::Write;
use std::io::Write;

View File

@@ -625,8 +625,8 @@ The tool will execute immediately and you'll receive the result (success or erro
- Example: {\"tool\": \"write_file\", \"args\": {\"file_path\": \"src/lib.rs\", \"content\": \"pub fn hello() {}\"}}
- **edit_file**: Edit a specific range of lines in a file
- Format: {\"tool\": \"edit_file\", \"args\": {\"file_path\": \"path/to/file\", \"start_line\": 1, \"end_line\": 3, \"new_text\": \"replacement text\"}}
- Example: {\"tool\": \"edit_file\", \"args\": {\"file_path\": \"src/main.rs\", \"start_line\": 5, \"end_line\": 7, \"new_text\": \"println!(\\\"Hello, world!\\\");\"}}
- Format: {\"tool\": \"edit_file\", \"args\": {\"file_path\": \"path/to/file\", \"content\": \"replacement text\", \"start_of_range\": 1, \"end_of_range\": 3}}
- Example: {\"tool\": \"edit_file\", \"args\": {\"file_path\": \"src/main.rs\", \"content\": \"println!(\\\"Hello, world!\\\");\", \"start_of_range\": 5, \"end_of_range\": 7}}
- **final_output**: Signal task completion with a detailed summary of work done in markdown format
- Format: {\"tool\": \"final_output\", \"args\": {\"summary\": \"what_was_accomplished\"}}
@@ -884,6 +884,32 @@ The tool will execute immediately and you'll receive the result (success or erro
"required": ["file_path", "content"]
}),
},
Tool {
name: "edit_file".to_string(),
description: "Edit a specific range of lines in a file. Replaces lines from start_of_range to end_of_range (inclusive, 1-indexed) with new content.".to_string(),
input_schema: json!({
"type": "object",
"properties": {
"file_path": {
"type": "string",
"description": "The path to the file to edit"
},
"content": {
"type": "string",
"description": "The new content to replace the specified range"
},
"start_of_range": {
"type": "integer",
"description": "The starting line number (1-indexed, inclusive)"
},
"end_of_range": {
"type": "integer",
"description": "The ending line number (1-indexed, inclusive)"
}
},
"required": ["file_path", "content", "start_of_range", "end_of_range"]
}),
},
Tool {
name: "final_output".to_string(),
description: "Signal task completion with a detailed summary".to_string(),
@@ -1575,6 +1601,100 @@ The tool will execute immediately and you'll receive the result (success or erro
))
}
}
"edit_file" => {
debug!("Processing edit_file tool call");
// Extract arguments
let args_obj = tool_call.args.as_object();
if args_obj.is_none() {
return Ok("❌ Invalid arguments: expected object".to_string());
}
let args_obj = args_obj.unwrap();
// Get file_path
let file_path = args_obj.get("file_path")
.and_then(|v| v.as_str());
if file_path.is_none() {
return Ok("❌ Missing file_path argument".to_string());
}
let file_path = file_path.unwrap();
// Get content
let content = args_obj.get("content")
.and_then(|v| v.as_str());
if content.is_none() {
return Ok("❌ Missing content argument".to_string());
}
let content = content.unwrap();
// Get start_of_range
let start_of_range = args_obj.get("start_of_range")
.and_then(|v| v.as_i64())
.map(|v| v as usize);
if start_of_range.is_none() {
return Ok("❌ Missing or invalid start_of_range argument".to_string());
}
let start_of_range = start_of_range.unwrap();
// Get end_of_range
let end_of_range = args_obj.get("end_of_range")
.and_then(|v| v.as_i64())
.map(|v| v as usize);
if end_of_range.is_none() {
return Ok("❌ Missing or invalid end_of_range argument".to_string());
}
let end_of_range = end_of_range.unwrap();
// Validate range
if start_of_range < 1 {
return Ok("❌ start_of_range must be >= 1 (lines are 1-indexed)".to_string());
}
if end_of_range < start_of_range {
return Ok("❌ end_of_range must be >= start_of_range".to_string());
}
// Read the existing file
let existing_content = match std::fs::read_to_string(file_path) {
Ok(content) => content,
Err(e) => return Ok(format!("❌ Failed to read file '{}': {}", file_path, e)),
};
// Split into lines
let mut lines: Vec<String> = existing_content.lines().map(|s| s.to_string()).collect();
// Check if range is valid
if start_of_range > lines.len() + 1 {
return Ok(format!("❌ start_of_range {} exceeds file length ({} lines)", start_of_range, lines.len()));
}
// Prepare new content lines
let new_lines: Vec<String> = content.lines().map(|s| s.to_string()).collect();
// Calculate the actual end of range (capped at file length)
let actual_end = end_of_range.min(lines.len());
// Replace the range with new content
// Convert to 0-indexed for vector operations
let start_idx = start_of_range - 1;
let end_idx = actual_end;
// Remove old lines and insert new ones
lines.splice(start_idx..end_idx, new_lines.clone());
// Write back to file
let new_content = lines.join("\n");
match std::fs::write(file_path, &new_content) {
Ok(()) => {
let lines_replaced = end_idx - start_idx;
let lines_added = new_lines.len();
Ok(format!(
"✅ Successfully edited '{}': replaced {} lines ({}-{}) with {} lines. File now has {} lines",
file_path, lines_replaced, start_of_range, actual_end, lines_added, lines.len()
))
}
Err(e) => Ok(format!("❌ Failed to write to file '{}': {}", file_path, e)),
}
}
"final_output" => {
if let Some(summary) = tool_call.args.get("summary") {
if let Some(summary_str) = summary.as_str() {