adk_devtools/tools/
write.rs1use std::sync::Arc;
4
5use adk_core::{Result, Tool, ToolContext};
6use async_trait::async_trait;
7use serde_json::{Value, json};
8
9use crate::error::DevToolError;
10use crate::tools::read::require_str;
11use crate::workspace::Workspace;
12
13pub struct WriteFileTool {
15 workspace: Workspace,
16}
17
18impl WriteFileTool {
19 pub fn new(workspace: Workspace) -> Self {
21 Self { workspace }
22 }
23}
24
25#[async_trait]
26impl Tool for WriteFileTool {
27 fn name(&self) -> &str {
28 "write_file"
29 }
30
31 fn description(&self) -> &str {
32 "Create a new file or overwrite an existing one with the given content. \
33 Parent directories are created automatically."
34 }
35
36 fn parameters_schema(&self) -> Option<Value> {
37 Some(json!({
38 "type": "object",
39 "properties": {
40 "path": { "type": "string", "description": "Path relative to the workspace root." },
41 "content": { "type": "string", "description": "The full file content to write." }
42 },
43 "required": ["path", "content"]
44 }))
45 }
46
47 async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
48 if !self.workspace.is_writable() {
49 return Err(DevToolError::ReadOnly("write_file").into());
50 }
51 let path = require_str(&args, "path")?;
52 let content = require_str(&args, "content")?;
53 let resolved = self.workspace.resolve(&path)?;
54
55 if let Some(parent) = resolved.parent() {
56 tokio::fs::create_dir_all(parent).await.map_err(DevToolError::from)?;
57 }
58 tokio::fs::write(&resolved, &content).await.map_err(DevToolError::from)?;
59 self.workspace.mark_read(&resolved);
61
62 Ok(json!({
63 "path": self.workspace.display(&resolved),
64 "bytes_written": content.len(),
65 "message": format!("Wrote {} bytes to {}", content.len(), self.workspace.display(&resolved)),
66 }))
67 }
68}