| 41 | } |
| 42 | |
| 43 | async fn execute(&self, args: &serde_json::Value, ctx: &ToolContext) -> Result<ToolOutput> { |
| 44 | let file_path = match args.get("file_path").and_then(|v| v.as_str()) { |
| 45 | Some(p) => p, |
| 46 | None => return Ok(ToolOutput::error("file_path parameter is required")), |
| 47 | }; |
| 48 | |
| 49 | let content = match args.get("content").and_then(|v| v.as_str()) { |
| 50 | Some(c) => c, |
| 51 | None => return Ok(ToolOutput::error("content parameter is required")), |
| 52 | }; |
| 53 | |
| 54 | let workspace_path = match ctx.resolve_workspace_path(file_path) { |
| 55 | Ok(p) => p, |
| 56 | Err(e) => return Ok(ToolOutput::error(format!("Failed to resolve path: {}", e))), |
| 57 | }; |
| 58 | |
| 59 | // Read existing content for diff metadata (if file exists) |
| 60 | let fs = ctx.workspace_services.fs(); |
| 61 | let path_for_before = workspace_path.clone(); |
| 62 | let fs_for_before = fs.clone(); |
| 63 | let before_content = ctx |
| 64 | .workspace_services |
| 65 | .run_with_timeout("read_text", async move { |
| 66 | fs_for_before.read_text(&path_for_before).await |
| 67 | }) |
| 68 | .await |
| 69 | .ok(); |
| 70 | |
| 71 | let path_for_write = workspace_path.clone(); |
| 72 | let content_for_write = content.to_string(); |
| 73 | match ctx |
| 74 | .workspace_services |
| 75 | .run_with_timeout("write_text", async move { |
| 76 | fs.write_text(&path_for_write, &content_for_write).await |
| 77 | }) |
| 78 | .await |
| 79 | { |
| 80 | Ok(outcome) => { |
| 81 | // Attach diff metadata |
| 82 | let mut metadata = serde_json::Map::new(); |
| 83 | metadata.insert("file_path".to_string(), serde_json::json!(file_path)); |
| 84 | metadata.insert("after".to_string(), serde_json::json!(content)); |
| 85 | if let Some(before) = before_content { |
| 86 | metadata.insert("before".to_string(), serde_json::json!(before)); |
| 87 | } |
| 88 | |
| 89 | Ok(ToolOutput::success(format!( |
| 90 | "Wrote {} bytes ({} lines) to {}", |
| 91 | outcome.bytes, |
| 92 | outcome.lines, |
| 93 | ctx.workspace_services.display_path(&workspace_path) |
| 94 | )) |
| 95 | .with_metadata(serde_json::Value::Object(metadata))) |
| 96 | } |
| 97 | Err(e) => Ok(ToolOutput::error(format!( |
| 98 | "Failed to write file {}: {}", |
| 99 | ctx.workspace_services.display_path(&workspace_path), |
| 100 | e |