| 51 | } |
| 52 | |
| 53 | async fn execute(&self, args: &serde_json::Value, ctx: &ToolContext) -> Result<ToolOutput> { |
| 54 | let file_path = match args.get("file_path").and_then(|v| v.as_str()) { |
| 55 | Some(p) => p, |
| 56 | None => return Ok(ToolOutput::error("file_path parameter is required")), |
| 57 | }; |
| 58 | |
| 59 | let offset = args.get("offset").and_then(|v| v.as_u64()).unwrap_or(0) as usize; |
| 60 | |
| 61 | let limit = args |
| 62 | .get("limit") |
| 63 | .and_then(|v| v.as_u64()) |
| 64 | .map(|v| v as usize) |
| 65 | .unwrap_or(MAX_READ_LINES); |
| 66 | |
| 67 | let workspace_path = match ctx.resolve_workspace_path(file_path) { |
| 68 | Ok(p) => p, |
| 69 | Err(e) => return Ok(ToolOutput::error(format!("Failed to resolve path: {}", e))), |
| 70 | }; |
| 71 | |
| 72 | let fs = ctx.workspace_services.fs(); |
| 73 | let path_for_read = workspace_path.clone(); |
| 74 | let content = match ctx |
| 75 | .workspace_services |
| 76 | .run_with_timeout( |
| 77 | "read_text", |
| 78 | async move { fs.read_text(&path_for_read).await }, |
| 79 | ) |
| 80 | .await |
| 81 | { |
| 82 | Ok(c) => c, |
| 83 | Err(e) => { |
| 84 | return Ok(ToolOutput::error(format!( |
| 85 | "Failed to read file {}: {}", |
| 86 | ctx.workspace_services.display_path(&workspace_path), |
| 87 | e |
| 88 | ))) |
| 89 | } |
| 90 | }; |
| 91 | |
| 92 | let lines: Vec<&str> = content.lines().collect(); |
| 93 | let total_lines = lines.len(); |
| 94 | |
| 95 | if offset >= total_lines && total_lines > 0 { |
| 96 | return Ok(ToolOutput::error(format!( |
| 97 | "Offset {} exceeds file length ({} lines)", |
| 98 | offset, total_lines |
| 99 | ))); |
| 100 | } |
| 101 | |
| 102 | let end = (offset + limit).min(total_lines); |
| 103 | let selected = &lines[offset..end]; |
| 104 | |
| 105 | let mut output = String::new(); |
| 106 | for (i, line) in selected.iter().enumerate() { |
| 107 | let line_num = offset + i + 1; // 1-indexed |
| 108 | let truncated = truncate_utf8(line, MAX_LINE_LENGTH); |
| 109 | output.push_str(&format!("{:>6}\t{}\n", line_num, truncated)); |
| 110 | } |