2026-07-11 13:16:10 +07:00
|
|
|
use std::fs;
|
|
|
|
|
use serde_json::{json, Value};
|
|
|
|
|
use anyhow::{Result, anyhow};
|
|
|
|
|
use super::super::Tool;
|
|
|
|
|
use super::super::ToolCtx;
|
|
|
|
|
use super::super::resolve_path;
|
2026-07-11 18:23:01 +07:00
|
|
|
use super::super::check_graduated_checks;
|
2026-07-11 13:16:10 +07:00
|
|
|
use super::helpers::arg_str;
|
|
|
|
|
|
|
|
|
|
pub struct Write;
|
|
|
|
|
|
|
|
|
|
impl Tool for Write {
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
"write"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn description(&self) -> &'static str {
|
|
|
|
|
"Write content to a file, creating parent directories as needed"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parameters(&self) -> Value {
|
|
|
|
|
json!({
|
|
|
|
|
"type": "object",
|
|
|
|
|
"properties": {
|
|
|
|
|
"path": {
|
|
|
|
|
"type": "string",
|
|
|
|
|
"description": "Path to the file to write (relative to workspace root)"
|
|
|
|
|
},
|
|
|
|
|
"content": {
|
|
|
|
|
"type": "string",
|
|
|
|
|
"description": "Content to write to the file"
|
|
|
|
|
},
|
|
|
|
|
"reason": {
|
|
|
|
|
"type": "string",
|
|
|
|
|
"description": "Reason for the change (must be non-empty)"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"required": ["path", "content", "reason"]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
|
|
|
|
|
let rel = arg_str(args, "path")?;
|
|
|
|
|
let content = arg_str(args, "content")?;
|
|
|
|
|
let reason = arg_str(args, "reason")?;
|
|
|
|
|
if reason.trim().is_empty() {
|
|
|
|
|
anyhow::bail!("reason must be a non-empty string");
|
|
|
|
|
}
|
2026-07-11 18:23:01 +07:00
|
|
|
let check_matches = check_graduated_checks(&rel, &content, &ctx.graduated_checks);
|
2026-07-11 13:16:10 +07:00
|
|
|
let path = resolve_path(&ctx.workspaces, &rel)?;
|
|
|
|
|
if let Some(parent) = path.parent() {
|
|
|
|
|
fs::create_dir_all(parent)
|
|
|
|
|
.map_err(|e| anyhow!("failed to create parent directories for '{}': {}", rel, e))?;
|
|
|
|
|
}
|
|
|
|
|
fs::write(&path, &content)
|
|
|
|
|
.map_err(|e| anyhow!("failed to write '{}': {}", rel, e))?;
|
2026-07-11 18:23:01 +07:00
|
|
|
if check_matches.is_empty() {
|
|
|
|
|
Ok(format!("wrote {} bytes to {}", content.len(), rel))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(format!("wrote {} bytes to {}. Graduated checks matched: {}", content.len(), rel, check_matches.join(", ")))
|
|
|
|
|
}
|
2026-07-11 13:16:10 +07:00
|
|
|
}
|
|
|
|
|
}
|