matrixcode_core/tools/
write.rs1use anyhow::Result;
2use async_trait::async_trait;
3use serde_json::{Value, json};
4
5use super::{Tool, ToolDefinition};
6use crate::approval::RiskLevel;
7use crate::path_validator::{validate_path, validate_content_size};
8
9pub struct WriteTool;
10
11#[async_trait]
12impl Tool for WriteTool {
13 fn definition(&self) -> ToolDefinition {
14 ToolDefinition {
15 name: "write".to_string(),
16 description: "向文件写入内容,若文件不存在则创建。自动验证路径安全性,限制单次写入最大10MB".to_string(),
17 parameters: json!({
18 "type": "object",
19 "properties": {
20 "path": {
21 "type": "string",
22 "description": "要写入的文件路径(会自动验证安全性,阻止路径穿越和系统文件写入)"
23 },
24 "content": {
25 "type": "string",
26 "description": "要写入的内容(单次写入最大10MB,超大内容请分批写入)"
27 }
28 },
29 "required": ["path", "content"]
30 }),
31 }
32 }
33
34 async fn execute(&self, params: Value) -> Result<String> {
35 let path_str = params["path"]
36 .as_str()
37 .ok_or_else(|| anyhow::anyhow!("missing 'path'"))?;
38 let content = params["content"]
39 .as_str()
40 .ok_or_else(|| anyhow::anyhow!("missing 'content'"))?;
41
42 validate_content_size(content)?;
44
45 let validated_path = validate_path(path_str, None, true)?;
48
49 if let Some(parent) = validated_path.parent() {
51 tokio::fs::create_dir_all(parent).await?;
52 }
53
54 let total_bytes = content.len();
56 let size_mb = total_bytes as f64 / 1_000_000.0;
57
58 tokio::fs::write(&validated_path, content).await?;
60
61 let size_feedback = if size_mb > 1.0 {
63 format!(
64 " ({:.2} MB - large file written successfully. \
65 Consider splitting if this causes performance issues)",
66 size_mb
67 )
68 } else if size_mb > 0.1 {
69 format!(" ({:.2} MB)", size_mb)
70 } else {
71 format!(" ({:.2} KB)", total_bytes as f64 / 1_000.0)
72 };
73
74 Ok(format!(
75 "Successfully wrote {} bytes{} to {}\nPath validated: {}",
76 total_bytes, size_feedback, path_str, validated_path.display()
77 ))
78 }
79
80 fn risk_level(&self) -> RiskLevel {
81 RiskLevel::Mutating
82 }
83}