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_content_size, validate_path};
8
9pub struct WriteTool;
10
11impl Default for WriteTool {
12 fn default() -> Self {
13 Self::new()
14 }
15}
16
17impl WriteTool {
18 pub fn new() -> Self {
20 Self
21 }
22
23 pub fn with_verification_strategy(_strategy: crate::tools::code_quality_hook::VerificationStrategy) -> Self {
25 Self }
27}
28
29#[async_trait]
30impl Tool for WriteTool {
31 fn definition(&self) -> ToolDefinition {
32 ToolDefinition {
33 name: "write".to_string(),
34 description: "向文件写入内容,若文件不存在则创建。
35
36【重要】写入现有文件前必须先读取:
37- 如果文件已存在,必须先用 read 工具读取当前内容
38- 如果没先读文件,此工具会失败
39- 了解现有内容可防止意外覆盖重要信息
40
41【代码质量验证】写入代码文件后 Agent 会自动验证:
42- 根据 verify_strategy 配置决定验证时机
43- 'pre' 策略:写入前验证,失败则阻止写入并返回错误给 AI 纠正
44- 'post' 策略:写入后验证,结果附加在输出中
45- 支持的验证:cargo check / tsc / python -m py_compile / go vet
46
47优先用 edit 工具修改现有文件(只发送 diff)
48只在以下情况使用此工具:
49- 创建新文件
50- 完整重写文件(用户明确要求)
51
52路径安全:自动验证路径安全性,阻止路径穿越和系统文件写入"
53 .to_string(),
54 parameters: json!({
55 "type": "object",
56 "properties": {
57 "path": {
58 "type": "string",
59 "description": "要写入的文件路径(会自动验证安全性,阻止路径穿越和系统文件写入)"
60 },
61 "content": {
62 "type": "string",
63 "description": "要写入的内容(单次写入最大10MB,超大内容请分批写入)"
64 }
65 },
66 "required": ["path", "content"]
67 }),
68 ..Default::default()
69 }
70 }
71
72 async fn execute(&self, params: Value) -> Result<String> {
73 let path_str = params["path"]
74 .as_str()
75 .ok_or_else(|| anyhow::anyhow!("missing 'path'"))?;
76
77 let content = params["content"]
79 .as_str()
80 .ok_or_else(|| anyhow::anyhow!("missing 'content'"))?;
81 validate_content_size(content)?;
82
83 let validated_path = validate_path(path_str, None, true)?;
85
86 if let Some(parent) = validated_path.parent() {
88 tokio::fs::create_dir_all(parent).await?;
89 }
90
91 let total_bytes = content.len();
93 tokio::fs::write(&validated_path, content).await?;
94
95 let size_feedback = if total_bytes > 1_000_000 {
97 format!(
98 " ({:.2} MB - large file written successfully. \
99 Consider splitting if this causes performance issues)",
100 total_bytes as f64 / 1_000_000.0
101 )
102 } else if total_bytes > 100_000 {
103 format!(" ({:.2} MB)", total_bytes as f64 / 1_000_000.0)
104 } else {
105 format!(" ({:.2} KB)", total_bytes as f64 / 1_000.0)
106 };
107
108 Ok(format!(
109 "Successfully wrote {} bytes{} to {}\nPath validated: {}",
110 total_bytes,
111 size_feedback,
112 path_str,
113 validated_path.display()
114 ))
115 }
116
117 fn risk_level(&self) -> RiskLevel {
118 RiskLevel::Mutating
119 }
120}