Skip to main content

kota/kota_code/tools/
create_directory.rs

1use super::FileToolError;
2use colored::*;
3use rig::{completion::ToolDefinition, tool::Tool};
4use serde::{Deserialize, Serialize};
5use std::fs;
6use std::path::Path;
7
8#[derive(Deserialize)]
9pub struct CreateDirectoryArgs {
10    pub dir_path: String,
11}
12
13#[derive(Serialize, Debug)]
14pub struct CreateDirectoryOutput {
15    pub dir_path: String,
16    pub success: bool,
17    pub message: String,
18    pub created_parents: bool,
19}
20
21#[derive(Deserialize, Serialize, Default)]
22pub struct CreateDirectoryTool;
23
24impl Tool for CreateDirectoryTool {
25    const NAME: &'static str = "make_dir";
26
27    type Error = FileToolError;
28    type Args = CreateDirectoryArgs;
29    type Output = CreateDirectoryOutput;
30
31    async fn definition(&self, _prompt: String) -> ToolDefinition {
32        ToolDefinition {
33            name: "make_dir".to_string(),
34            description: "Create a directory and all necessary parent directories. If the directory already exists, the operation succeeds.".to_string(),
35            parameters: serde_json::json!({
36                "type": "object",
37                "properties": {
38                    "dir_path": {
39                        "type": "string",
40                        "description": "The path of the directory to create (relative or absolute). Examples: 'new_folder', 'src/components', '/path/to/new/dir'"
41                    }
42                },
43                "required": ["dir_path"]
44            })
45        }
46    }
47
48    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
49        let dir_path = &args.dir_path;
50        let path = Path::new(dir_path);
51
52        // Check if directory already exists
53        if path.exists() {
54            if path.is_dir() {
55                return Ok(CreateDirectoryOutput {
56                    dir_path: dir_path.clone(),
57                    success: true,
58                    message: format!("Directory '{}' already exists", dir_path),
59                    created_parents: false,
60                });
61            } else {
62                return Err(FileToolError::NotAFile(format!(
63                    "Path '{}' exists but is not a directory",
64                    dir_path
65                )));
66            }
67        }
68
69        // Check if we need to create parent directories
70        let needs_parents = path.parent().is_some_and(|parent| !parent.exists());
71
72        // Create the directory and all parent directories
73        match fs::create_dir_all(dir_path) {
74            Ok(()) => Ok(CreateDirectoryOutput {
75                dir_path: dir_path.clone(),
76                success: true,
77                message: if needs_parents {
78                    format!(
79                        "Successfully created directory '{}' and parent directories",
80                        dir_path
81                    )
82                } else {
83                    format!("Successfully created directory '{}'", dir_path)
84                },
85                created_parents: needs_parents,
86            }),
87            Err(e) => match e.kind() {
88                std::io::ErrorKind::PermissionDenied => {
89                    Err(FileToolError::PermissionDenied(dir_path.clone()))
90                }
91                _ => Err(FileToolError::Io(e)),
92            },
93        }
94    }
95}
96
97#[derive(Deserialize, Serialize, Default)]
98pub struct WrappedCreateDirectoryTool {
99    inner: CreateDirectoryTool,
100}
101
102impl WrappedCreateDirectoryTool {
103    pub fn new() -> Self {
104        Self {
105            inner: CreateDirectoryTool,
106        }
107    }
108}
109
110impl Tool for WrappedCreateDirectoryTool {
111    const NAME: &'static str = "make_dir";
112
113    type Error = FileToolError;
114    type Args = <CreateDirectoryTool as Tool>::Args;
115    type Output = <CreateDirectoryTool as Tool>::Output;
116
117    async fn definition(&self, prompt: String) -> ToolDefinition {
118        self.inner.definition(prompt).await
119    }
120
121    async fn call(&self, args: Self::Args) -> Result<Self::Output, Self::Error> {
122        println!("\n{} CreateDir({})", "●".bright_green(), args.dir_path);
123
124        let result = self.inner.call(args).await;
125
126        match &result {
127            Ok(output) => {
128                if output.created_parents {
129                    println!("  └─ {} (with parents)", "Directory created".dimmed());
130                } else {
131                    println!("  └─ {}", "Directory created".dimmed());
132                }
133            }
134            Err(e) => {
135                println!("  └─ {}", format!("Error: {}", e).red());
136            }
137        }
138        println!();
139        result
140    }
141}