kota/kota_code/tools/
create_directory.rs1use 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 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 let needs_parents = path.parent().is_some_and(|parent| !parent.exists());
71
72 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}