create_directory_tool.rs

 1use agent_client_protocol::ToolKind;
 2use anyhow::{Context as _, Result, anyhow};
 3use futures::FutureExt as _;
 4use gpui::{App, Entity, SharedString, Task};
 5use project::Project;
 6use schemars::JsonSchema;
 7use serde::{Deserialize, Serialize};
 8use std::sync::Arc;
 9use util::markdown::MarkdownInlineCode;
10
11use crate::{AgentTool, ToolCallEventStream};
12
13/// Creates a new directory at the specified path within the project. Returns confirmation that the directory was created.
14///
15/// This tool creates a directory and all necessary parent directories. It should be used whenever you need to create new directories within the project.
16#[derive(Debug, Serialize, Deserialize, JsonSchema)]
17pub struct CreateDirectoryToolInput {
18    /// The path of the new directory.
19    ///
20    /// <example>
21    /// If the project has the following structure:
22    ///
23    /// - directory1/
24    /// - directory2/
25    ///
26    /// You can create a new directory by providing a path of "directory1/new_directory"
27    /// </example>
28    pub path: String,
29}
30
31pub struct CreateDirectoryTool {
32    project: Entity<Project>,
33}
34
35impl CreateDirectoryTool {
36    pub fn new(project: Entity<Project>) -> Self {
37        Self { project }
38    }
39}
40
41impl AgentTool for CreateDirectoryTool {
42    type Input = CreateDirectoryToolInput;
43    type Output = String;
44
45    fn name() -> &'static str {
46        "create_directory"
47    }
48
49    fn kind() -> ToolKind {
50        ToolKind::Read
51    }
52
53    fn initial_title(
54        &self,
55        input: Result<Self::Input, serde_json::Value>,
56        _cx: &mut App,
57    ) -> SharedString {
58        if let Ok(input) = input {
59            format!("Create directory {}", MarkdownInlineCode(&input.path)).into()
60        } else {
61            "Create directory".into()
62        }
63    }
64
65    fn run(
66        self: Arc<Self>,
67        input: Self::Input,
68        event_stream: ToolCallEventStream,
69        cx: &mut App,
70    ) -> Task<Result<Self::Output>> {
71        let project_path = match self.project.read(cx).find_project_path(&input.path, cx) {
72            Some(project_path) => project_path,
73            None => {
74                return Task::ready(Err(anyhow!("Path to create was outside the project")));
75            }
76        };
77        let destination_path: Arc<str> = input.path.as_str().into();
78
79        let create_entry = self.project.update(cx, |project, cx| {
80            project.create_entry(project_path.clone(), true, cx)
81        });
82
83        cx.spawn(async move |_cx| {
84            futures::select! {
85                result = create_entry.fuse() => {
86                    result.with_context(|| format!("Creating directory {destination_path}"))?;
87                }
88                _ = event_stream.cancelled_by_user().fuse() => {
89                    anyhow::bail!("Create directory cancelled by user");
90                }
91            }
92
93            Ok(format!("Created directory {destination_path}"))
94        })
95    }
96}