create_directory_tool.rs

 1use crate::schema::json_schema_for;
 2use anyhow::{Context as _, Result, anyhow};
 3use assistant_tool::{ActionLog, Tool, ToolResult};
 4use gpui::AnyWindowHandle;
 5use gpui::{App, Entity, Task};
 6use language_model::{LanguageModel, LanguageModelRequest, LanguageModelToolSchemaFormat};
 7use project::Project;
 8use schemars::JsonSchema;
 9use serde::{Deserialize, Serialize};
10use std::sync::Arc;
11use ui::IconName;
12use util::markdown::MarkdownInlineCode;
13
14#[derive(Debug, Serialize, Deserialize, JsonSchema)]
15pub struct CreateDirectoryToolInput {
16    /// The path of the new directory.
17    ///
18    /// <example>
19    /// If the project has the following structure:
20    ///
21    /// - directory1/
22    /// - directory2/
23    ///
24    /// You can create a new directory by providing a path of "directory1/new_directory"
25    /// </example>
26    pub path: String,
27}
28
29pub struct CreateDirectoryTool;
30
31impl Tool for CreateDirectoryTool {
32    type Input = CreateDirectoryToolInput;
33
34    fn name(&self) -> String {
35        "create_directory".into()
36    }
37
38    fn description(&self) -> String {
39        include_str!("./create_directory_tool/description.md").into()
40    }
41
42    fn needs_confirmation(&self, _: &Self::Input, _: &App) -> bool {
43        false
44    }
45
46    fn may_perform_edits(&self) -> bool {
47        false
48    }
49
50    fn icon(&self) -> IconName {
51        IconName::Folder
52    }
53
54    fn input_schema(&self, format: LanguageModelToolSchemaFormat) -> Result<serde_json::Value> {
55        json_schema_for::<CreateDirectoryToolInput>(format)
56    }
57
58    fn ui_text(&self, input: &Self::Input) -> String {
59        format!("Create directory {}", MarkdownInlineCode(&input.path))
60    }
61
62    fn run(
63        self: Arc<Self>,
64        input: Self::Input,
65        _request: Arc<LanguageModelRequest>,
66        project: Entity<Project>,
67        _action_log: Entity<ActionLog>,
68        _model: Arc<dyn LanguageModel>,
69        _window: Option<AnyWindowHandle>,
70        cx: &mut App,
71    ) -> ToolResult {
72        let project_path = match project.read(cx).find_project_path(&input.path, cx) {
73            Some(project_path) => project_path,
74            None => {
75                return Task::ready(Err(anyhow!("Path to create was outside the project"))).into();
76            }
77        };
78        let destination_path: Arc<str> = input.path.as_str().into();
79
80        cx.spawn(async move |cx| {
81            project
82                .update(cx, |project, cx| {
83                    project.create_entry(project_path.clone(), true, cx)
84                })?
85                .await
86                .with_context(|| format!("Creating directory {destination_path}"))?;
87
88            Ok(format!("Created directory {destination_path}").into())
89        })
90        .into()
91    }
92}