Skip to main content

cersei_tools/
file_read.rs

1//! File read tool.
2
3use super::*;
4use crate::tool_primitives::fs as pfs;
5use serde::Deserialize;
6
7pub struct FileReadTool;
8
9#[async_trait]
10impl Tool for FileReadTool {
11    fn name(&self) -> &str {
12        "Read"
13    }
14    fn description(&self) -> &str {
15        "Read a file from the filesystem."
16    }
17    fn permission_level(&self) -> PermissionLevel {
18        PermissionLevel::ReadOnly
19    }
20    fn category(&self) -> ToolCategory {
21        ToolCategory::FileSystem
22    }
23
24    fn input_schema(&self) -> Value {
25        serde_json::json!({
26            "type": "object",
27            "properties": {
28                "file_path": { "type": "string", "description": "Absolute path to the file" },
29                "offset": { "type": "integer", "description": "Line number to start reading from" },
30                "limit": { "type": "integer", "description": "Number of lines to read" }
31            },
32            "required": ["file_path"]
33        })
34    }
35
36    async fn execute(&self, input: Value, _ctx: &ToolContext) -> ToolResult {
37        #[derive(Deserialize)]
38        struct Input {
39            file_path: String,
40            offset: Option<usize>,
41            limit: Option<usize>,
42        }
43
44        let input: Input = match serde_json::from_value(input) {
45            Ok(i) => i,
46            Err(e) => return ToolResult::error(format!("Invalid input: {}", e)),
47        };
48
49        let path = std::path::Path::new(&input.file_path);
50        if !path.exists() {
51            return ToolResult::error(format!("File not found: {}", input.file_path));
52        }
53
54        let offset = input.offset.unwrap_or(0);
55        let limit = input.limit.unwrap_or(2000);
56
57        match pfs::read_file(path, offset, limit).await {
58            Ok(fc) => ToolResult::success(fc.content),
59            Err(e) => ToolResult::error(format!("Failed to read file: {}", e)),
60        }
61    }
62}