2026-07-12 11:28:39 +07:00
|
|
|
//! Tool: `delete` — remove a file or empty directory relative to a workspace root.
|
|
|
|
|
|
2026-07-11 20:44:15 +07:00
|
|
|
use std::fs;
|
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use serde_json::{json, Value};
|
|
|
|
|
use anyhow::{Result, anyhow};
|
|
|
|
|
use super::super::Tool;
|
|
|
|
|
use super::super::ToolCtx;
|
|
|
|
|
use super::super::resolve_path;
|
|
|
|
|
use super::helpers::arg_str;
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// Tool: delete a file or empty directory. Refuses non-empty directories.
|
2026-07-11 20:44:15 +07:00
|
|
|
pub struct Delete;
|
|
|
|
|
|
|
|
|
|
impl Tool for Delete {
|
|
|
|
|
fn name(&self) -> &'static str {
|
|
|
|
|
"delete"
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn description(&self) -> &'static str {
|
|
|
|
|
"Delete a file or empty directory. Will not delete non-empty directories."
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn parameters(&self) -> Value {
|
|
|
|
|
json!({
|
|
|
|
|
"type": "object",
|
|
|
|
|
"properties": {
|
|
|
|
|
"path": {
|
|
|
|
|
"type": "string",
|
|
|
|
|
"description": "Path to the file or directory to delete (relative to workspace root)"
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"required": ["path"]
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
|
2026-07-12 11:28:39 +07:00
|
|
|
/// Delete a file or empty directory. Returns success message or errors on failure.
|
|
|
|
|
///
|
|
|
|
|
/// Flow: resolve path → check existence → check dir/file → remove.
|
|
|
|
|
/// Only empty directories are deletable (non-empty returns an error).
|
2026-07-11 20:44:15 +07:00
|
|
|
fn run(&self, ctx: &ToolCtx, args: &Value) -> Result<String> {
|
|
|
|
|
let rel = arg_str(args, "path")?;
|
|
|
|
|
let path: PathBuf = resolve_path(&ctx.workspaces, &rel)?;
|
|
|
|
|
|
|
|
|
|
if !path.exists() {
|
|
|
|
|
return Ok(format!("path '{}' does not exist (resolved to {})", rel, path.display()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let metadata = path.metadata()
|
|
|
|
|
.map_err(|e| anyhow!("failed to read metadata for '{}': {}", rel, e))?;
|
|
|
|
|
|
|
|
|
|
if metadata.is_dir() {
|
|
|
|
|
let is_empty = fs::read_dir(&path)
|
|
|
|
|
.map_err(|e| anyhow!("failed to read directory '{}': {}", rel, e))?
|
|
|
|
|
.next()
|
|
|
|
|
.is_none();
|
|
|
|
|
if is_empty {
|
|
|
|
|
fs::remove_dir(&path)
|
|
|
|
|
.map_err(|e| anyhow!("failed to remove directory '{}': {}", rel, e))?;
|
|
|
|
|
Ok(format!("removed empty directory {}", rel))
|
|
|
|
|
} else {
|
|
|
|
|
anyhow::bail!("directory '{}' is not empty (refusing to delete)", rel);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
fs::remove_file(&path)
|
|
|
|
|
.map_err(|e| anyhow!("failed to delete '{}': {}", rel, e))?;
|
|
|
|
|
Ok(format!("deleted {}", rel))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|