create_directory_tool.rs

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