1use std::path::Path;
2
3use serde_json::json;
4
5use vtcode_core::config::constants::tools;
6use vtcode_core::core::interfaces::SessionMode;
7use vtcode_core::llm::provider::ToolDefinition;
8
9pub const TOOL_READ_FILE_DESCRIPTION: &str =
10 "Read the contents of a text file accessible to the IDE workspace";
11pub const TOOL_READ_FILE_URI_ARG: &str = "uri";
12pub const TOOL_READ_FILE_PATH_ARG: &str = "path";
13pub const TOOL_READ_FILE_LINE_ARG: &str = "line";
14pub const TOOL_READ_FILE_LIMIT_ARG: &str = "limit";
15
16pub const TOOL_LIST_FILES_DESCRIPTION: &str = "Explore workspace files in a SUBDIRECTORY (root path is blocked). Requires path like 'src/' or 'vtcode-core/'. For root overview, use shell commands via unified_exec.";
17pub const TOOL_LIST_FILES_PATH_ARG: &str = "path";
18pub const TOOL_LIST_FILES_MODE_ARG: &str = "mode";
19pub const TOOL_LIST_FILES_PAGE_ARG: &str = "page";
20pub const TOOL_LIST_FILES_PER_PAGE_ARG: &str = "per_page";
21pub const TOOL_LIST_FILES_MAX_ITEMS_ARG: &str = "max_items";
22pub const TOOL_LIST_FILES_INCLUDE_HIDDEN_ARG: &str = "include_hidden";
23pub const TOOL_LIST_FILES_RESPONSE_FORMAT_ARG: &str = "response_format";
24pub const TOOL_LIST_FILES_URI_ARG: &str = "uri";
25pub const TOOL_LIST_FILES_NAME_PATTERN_ARG: &str = "name_pattern";
26pub const TOOL_LIST_FILES_CONTENT_PATTERN_ARG: &str = "content_pattern";
27pub const TOOL_LIST_FILES_FILE_EXTENSIONS_ARG: &str = "file_extensions";
28pub const TOOL_LIST_FILES_CASE_SENSITIVE_ARG: &str = "case_sensitive";
29pub const TOOL_LIST_FILES_ITEMS_KEY: &str = "items";
30pub const TOOL_LIST_FILES_MESSAGE_KEY: &str = "message";
31pub const TOOL_LIST_FILES_RESULT_KEY: &str = "result";
32pub const TOOL_LIST_FILES_SUMMARY_MAX_ITEMS: usize = 20;
33
34pub(super) fn build_read_file_definition(workspace_root: &Path) -> ToolDefinition {
35 let workspace_display = workspace_root.display().to_string();
36 let sample_path = workspace_root.join("README.md");
37 let sample_path_string = sample_path.to_string_lossy().into_owned();
38 let sample_uri = format!("file://{}", sample_path_string);
39 let description = format!(
40 "{TOOL_READ_FILE_DESCRIPTION}. Workspace root: {workspace}. Provide {path} or {uri} inside the workspace. Paths must be absolute (see ACP file system spec). Optional {line} and {limit} control slicing.",
41 workspace = workspace_display,
42 path = TOOL_READ_FILE_PATH_ARG,
43 uri = TOOL_READ_FILE_URI_ARG,
44 line = TOOL_READ_FILE_LINE_ARG,
45 limit = TOOL_READ_FILE_LIMIT_ARG,
46 );
47 let examples = vec![
48 json!({
49 TOOL_READ_FILE_PATH_ARG: &sample_path_string,
50 }),
51 json!({
52 TOOL_READ_FILE_PATH_ARG: &sample_path_string,
53 TOOL_READ_FILE_LINE_ARG: 1,
54 TOOL_READ_FILE_LIMIT_ARG: 200,
55 }),
56 json!({
57 TOOL_READ_FILE_URI_ARG: sample_uri,
58 }),
59 ];
60 let schema = json!({
61 "type": "object",
62 "minProperties": 1,
63 "properties": {
64 TOOL_READ_FILE_PATH_ARG: {
65 "type": "string",
66 "description": "Absolute path to the file within the workspace",
67 "minLength": 1,
68 },
69 TOOL_READ_FILE_URI_ARG: {
70 "type": "string",
71 "description": "File URI using file:// or editor-specific schemes",
72 "minLength": 1,
73 },
74 TOOL_READ_FILE_LINE_ARG: {
75 "type": "integer",
76 "minimum": 1,
77 "description": "1-based line number to start reading from",
78 },
79 TOOL_READ_FILE_LIMIT_ARG: {
80 "type": "integer",
81 "minimum": 1,
82 "description": "Maximum number of lines to read",
83 }
84 },
85 "additionalProperties": false,
86 "description": description,
87 "examples": examples,
88 });
89
90 ToolDefinition::function(tools::READ_FILE.to_string(), description, schema)
91}
92
93pub(super) fn build_list_files_definition(workspace_root: &Path) -> ToolDefinition {
94 let description = format!(
95 "{TOOL_LIST_FILES_DESCRIPTION}. Workspace root: {}. Provide {path} (relative) or {uri} inside the workspace. Defaults to '.' when omitted.",
96 workspace_root.display(),
97 path = TOOL_LIST_FILES_PATH_ARG,
98 uri = TOOL_LIST_FILES_URI_ARG,
99 );
100 let workspace_display = workspace_root.display().to_string();
101 let examples = vec![
102 json!({
103 TOOL_LIST_FILES_MODE_ARG: "list",
104 }),
105 json!({
106 TOOL_LIST_FILES_PATH_ARG: "src",
107 TOOL_LIST_FILES_MODE_ARG: "recursive",
108 TOOL_LIST_FILES_PER_PAGE_ARG: 100,
109 }),
110 json!({
111 TOOL_LIST_FILES_URI_ARG: format!("file://{}/src", workspace_display),
112 }),
113 ];
114 let schema = json!({
115 "type": "object",
116 "properties": {
117 TOOL_LIST_FILES_PATH_ARG: {
118 "type": "string",
119 "description": "Directory or file path relative to the workspace root",
120 "default": ".",
121 },
122 TOOL_LIST_FILES_MODE_ARG: {
123 "type": "string",
124 "enum": ["list", "recursive", "find_name", "find_content"],
125 "description": "Listing mode: list (default), recursive, find_name, or find_content",
126 },
127 TOOL_LIST_FILES_PAGE_ARG: {
128 "type": "integer",
129 "minimum": 1,
130 "description": "Page number to return (1-based)",
131 },
132 TOOL_LIST_FILES_PER_PAGE_ARG: {
133 "type": "integer",
134 "minimum": 1,
135 "description": "Items per page (default 50)",
136 },
137 TOOL_LIST_FILES_MAX_ITEMS_ARG: {
138 "type": "integer",
139 "minimum": 1,
140 "description": "Maximum number of items to scan before truncation",
141 },
142 TOOL_LIST_FILES_INCLUDE_HIDDEN_ARG: {
143 "type": "boolean",
144 "description": "Whether to include dotfiles and ignored entries",
145 },
146 TOOL_LIST_FILES_RESPONSE_FORMAT_ARG: {
147 "type": "string",
148 "enum": ["concise", "detailed"],
149 "description": "Choose concise (default) or detailed metadata",
150 },
151 TOOL_LIST_FILES_NAME_PATTERN_ARG: {
152 "type": "string",
153 "description": "Optional filename pattern used by recursive or find_name modes",
154 },
155 TOOL_LIST_FILES_CONTENT_PATTERN_ARG: {
156 "type": "string",
157 "description": "Pattern to search within files when using find_content mode",
158 },
159 TOOL_LIST_FILES_FILE_EXTENSIONS_ARG: {
160 "type": "array",
161 "items": {"type": "string"},
162 "description": "Restrict results to files matching any extension",
163 },
164 TOOL_LIST_FILES_CASE_SENSITIVE_ARG: {
165 "type": "boolean",
166 "description": "Enable case sensitive matching for patterns",
167 },
168 },
169 "additionalProperties": false,
170 "description": description,
171 "examples": examples,
172 });
173
174 ToolDefinition::function(tools::LIST_FILES.to_string(), description, schema)
175}
176
177pub(super) fn build_switch_mode_definition() -> ToolDefinition {
178 let description = format!(
179 "Switch the current session mode. {ask} and {architect} are read-only; {code} enables local implementation tools. Possible modes: {ask}, {architect}, {code}.",
180 ask = SessionMode::Ask.as_str(),
181 architect = SessionMode::Architect.as_str(),
182 code = SessionMode::Code.as_str()
183 );
184 let schema = json!({
185 "type": "object",
186 "required": ["mode_id"],
187 "properties": {
188 "mode_id": {
189 "type": "string",
190 "enum": [
191 SessionMode::Ask.as_str(),
192 SessionMode::Architect.as_str(),
193 SessionMode::Code.as_str()
194 ],
195 "description": "The ID of the mode to switch to"
196 }
197 },
198 "additionalProperties": false,
199 "description": description,
200 });
201
202 ToolDefinition::function("switch_mode".to_string(), description, schema)
203}