// tools/read.rs — Read file contents use anyhow::{Context, Result}; use serde_json::json; use crate::types::ToolDef; pub fn definition() -> ToolDef { ToolDef::new( "read_file", "Read the contents of a file. Returns the file contents with line numbers.", json!({ "type": "object", "properties": { "file_path": { "type": "string", "description": "Absolute path to the file to read" }, "offset": { "type": "integer", "description": "Line number to start reading from (1-based). Optional." }, "limit": { "type": "integer", "description": "Maximum number of lines to read. Optional." } }, "required": ["file_path"] }), ) } pub fn read_file(args: &serde_json::Value) -> Result { let path = args["file_path"] .as_str() .context("file_path is required")?; let content = std::fs::read_to_string(path).with_context(|| format!("Failed to read {}", path))?; let lines: Vec<&str> = content.lines().collect(); let offset = args["offset"].as_u64().unwrap_or(1).max(1) as usize - 1; let limit = args["limit"].as_u64().unwrap_or(lines.len() as u64) as usize; let mut output = String::new(); for (i, line) in lines.iter().skip(offset).take(limit).enumerate() { let line_num = offset + i + 1; output.push_str(&format!("{:>6}\t{}\n", line_num, line)); } if output.is_empty() { output = "(empty file)\n".to_string(); } Ok(output) }