1use std::path::Path;
2use std::sync::Arc;
3
4use anyhow::{anyhow, Result};
5use assistant_tool::Tool;
6use gpui::{App, Entity, Task};
7use language_model::LanguageModelRequestMessage;
8use project::{Project, ProjectPath, WorktreeId};
9use schemars::JsonSchema;
10use serde::{Deserialize, Serialize};
11
12#[derive(Debug, Serialize, Deserialize, JsonSchema)]
13pub struct ReadFileToolInput {
14 /// The ID of the worktree in which the file resides.
15 pub worktree_id: usize,
16 /// The path to the file to read.
17 ///
18 /// This path is relative to the worktree root, it must not be an absolute path.
19 pub path: Arc<Path>,
20}
21
22pub struct ReadFileTool;
23
24impl Tool for ReadFileTool {
25 fn name(&self) -> String {
26 "read-file".into()
27 }
28
29 fn description(&self) -> String {
30 "Reads the content of a file specified by a worktree ID and path. Use this tool when you need to access the contents of a file in the project.".into()
31 }
32
33 fn input_schema(&self) -> serde_json::Value {
34 let schema = schemars::schema_for!(ReadFileToolInput);
35 serde_json::to_value(&schema).unwrap()
36 }
37
38 fn run(
39 self: Arc<Self>,
40 input: serde_json::Value,
41 _messages: &[LanguageModelRequestMessage],
42 project: Entity<Project>,
43 cx: &mut App,
44 ) -> Task<Result<String>> {
45 let input = match serde_json::from_value::<ReadFileToolInput>(input) {
46 Ok(input) => input,
47 Err(err) => return Task::ready(Err(anyhow!(err))),
48 };
49
50 let project_path = ProjectPath {
51 worktree_id: WorktreeId::from_usize(input.worktree_id),
52 path: input.path,
53 };
54 cx.spawn(|cx| async move {
55 let buffer = cx
56 .update(|cx| {
57 project.update(cx, |project, cx| project.open_buffer(project_path, cx))
58 })?
59 .await?;
60
61 buffer.read_with(&cx, |buffer, _cx| {
62 if buffer
63 .file()
64 .map_or(false, |file| file.disk_state().exists())
65 {
66 Ok(buffer.text())
67 } else {
68 Err(anyhow!("File does not exist"))
69 }
70 })?
71 })
72 }
73}