//! Tool: `delete` — remove a file or empty directory relative to a workspace root. 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; /// Tool: delete a file or empty directory. Refuses non-empty directories. 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"] }) } /// 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). fn run(&self, ctx: &ToolCtx, args: &Value) -> Result { 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 '{rel}' is not empty (refusing to delete)"); } } else { fs::remove_file(&path) .map_err(|e| anyhow!("failed to delete '{rel}': {e}"))?; Ok(format!("deleted {rel}")) } } }