//! Read a file from the workspace. use crate::tools::{resolve_path, Tool, ToolCtx}; use anyhow::Result; use serde_json::{json, Value}; use std::fs; use tracing::{info, instrument, warn}; /// Read the contents of a file. /// /// Flow: resolve path → check existence and type → read file content → return. pub struct Read; impl Tool for Read { fn name(&self) -> &'static str { "read" } fn description(&self) -> &'static str { "Read the contents of a file" } fn parameters(&self) -> Value { json!({ "type": "object", "properties": { "path": { "type": "string", "description": "Path to the file to read (relative to workspace root)" } }, "required": ["path"] }) } #[instrument(skip(self, ctx, args))] fn run(&self, ctx: &ToolCtx, args: &Value) -> Result { let rel = crate::tools::arg_str(args, "path")?; let path = resolve_path(&ctx.workspaces, &rel)?; if !path.exists() { warn!(rel = %rel, "read target does not exist"); anyhow::bail!("file '{rel}' does not exist"); } if !path.is_file() { warn!(rel = %rel, "read target is not a file"); anyhow::bail!("'{rel}' is not a file"); } let content = fs::read_to_string(&path)?; info!(rel = %rel, bytes = content.len(), "file read"); Ok(content) } }