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