1use std::path::Path;
2use std::sync::Arc;
3
4use anyhow::{anyhow, Result};
5use assistant_tool::Tool;
6use gpui::{App, Task, WeakEntity, Window};
7use project::{ProjectPath, WorktreeId};
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10use workspace::Workspace;
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 workspace: WeakEntity<Workspace>,
42 _window: &mut Window,
43 cx: &mut App,
44 ) -> Task<Result<String>> {
45 let Some(workspace) = workspace.upgrade() else {
46 return Task::ready(Err(anyhow!("workspace dropped")));
47 };
48
49 let input = match serde_json::from_value::<ReadFileToolInput>(input) {
50 Ok(input) => input,
51 Err(err) => return Task::ready(Err(anyhow!(err))),
52 };
53
54 let project = workspace.read(cx).project().clone();
55 let project_path = ProjectPath {
56 worktree_id: WorktreeId::from_usize(input.worktree_id),
57 path: input.path,
58 };
59 cx.spawn(|cx| async move {
60 let buffer = cx
61 .update(|cx| {
62 project.update(cx, |project, cx| project.open_buffer(project_path, cx))
63 })?
64 .await?;
65
66 cx.update(|cx| buffer.read(cx).text())
67 })
68 }
69}