1use std::collections::HashMap;
2
3use crate::config::constants::tools;
4use crate::config::types::CapabilityLevel;
5use crate::gemini::FunctionDeclaration;
6use serde_json::json;
7
8use super::builtins::builtin_tool_registrations;
9
10fn base_function_declarations() -> Vec<FunctionDeclaration> {
11 vec![
12 FunctionDeclaration {
14 name: tools::GREP_SEARCH.to_string(),
15 description: "Fast code search using ripgrep. Find code patterns, function definitions, TODOs, or text across files. Supports exact/fuzzy/multi-pattern/similarity modes. Use 'concise' format and reasonable max_results to manage token budget. Search first, read second—avoid loading full files until you've identified relevant matches.".to_string(),
16 parameters: json!({
17 "type": "object",
18 "properties": {
19 "pattern": {"type": "string", "description": "Search pattern. Example: 'fn \\w+' or 'TODO|FIXME'"},
20 "path": {"type": "string", "description": "Directory path to search in (relative). Default: '.'", "default": "."},
21 "mode": {"type": "string", "description": "Search mode: 'exact' | 'fuzzy' | 'multi' | 'similarity'", "default": "exact"},
22 "max_results": {"type": "integer", "description": "Max results (token efficiency). Default: 100", "default": 100},
23 "case_sensitive": {"type": "boolean", "description": "Case sensitive search. Default: true", "default": true},
24 "patterns": {"type": "array", "items": {"type": "string"}, "description": "For mode='multi'. Example: ['fn \\w+','use \\w+']"},
26 "logic": {"type": "string", "description": "For mode='multi': 'AND' or 'OR'", "default": "AND"},
27 "fuzzy_threshold": {"type": "number", "description": "Fuzzy matching threshold (0.0-1.0)", "default": 0.7},
29 "reference_file": {"type": "string", "description": "For mode='similarity': reference file path"},
31 "content_type": {"type": "string", "description": "For mode='similarity': 'structure'|'imports'|'functions'|'all'", "default": "all"},
32 "response_format": {"type": "string", "description": "'concise' (default) or 'detailed' (raw rg JSON)", "default": "concise"}
33 },
34 "required": ["pattern"]
35 }),
36 },
37
38 FunctionDeclaration {
40 name: "list_files".to_string(),
41 description: "Explore workspace structure. Modes: 'list' (dir contents), 'recursive' (deep scan), 'find_name' (filename search), 'find_content' (content-based discovery). Use metadata as references—avoid reading full contents until necessary. Always paginate large dirs (per_page=50 default). Monitor 'has_more' flag. Use 'concise' format to minimize token usage.".to_string(),
42 parameters: json!({
43 "type": "object",
44 "properties": {
45 "path": {"type": "string", "description": "Path to search from (relative). Example: 'src'"},
46 "mode": {"type": "string", "description": "'list' | 'recursive' | 'find_name' | 'find_content'", "default": "list"},
47 "max_items": {"type": "integer", "description": "Cap total items scanned (token safety). Default: 1000", "default": 1000},
48 "page": {"type": "integer", "description": "Page number (1-based). Default: 1", "default": 1},
49 "per_page": {"type": "integer", "description": "Items per page. Default: 50", "default": 50},
50 "response_format": {"type": "string", "description": "'concise' (default) omits low-signal fields; 'detailed' includes them", "default": "concise"},
51 "include_hidden": {"type": "boolean", "description": "Include hidden files", "default": false},
52 "name_pattern": {"type": "string", "description": "Optional pattern for 'recursive'/'find_name' modes. Use '*' or omit for all files. Example: '*.rs'", "default": "*"},
53 "content_pattern": {"type": "string", "description": "For 'find_content' mode. Example: 'fn main'"},
54 "file_extensions": {"type": "array", "items": {"type": "string"}, "description": "Filter by file extensions"},
55 "case_sensitive": {"type": "boolean", "description": "Case sensitive pattern matching", "default": true},
56 "ast_grep_pattern": {"type": "string", "description": "Optional AST pattern to filter files"}
57 },
58 "required": ["path"]
59 }),
60 },
61
62 FunctionDeclaration {
64 name: tools::READ_FILE.to_string(),
65 description: "Read file contents. Auto-chunks large files (>2000 lines): first 800 + last 800 lines. Adjust with chunk_lines/max_lines. Only read after search identifies relevance.".to_string(),
66 parameters: json!({
67 "type": "object",
68 "properties": {
69 "path": {"type": "string", "description": "File path to read"},
70 "max_bytes": {"type": "integer", "description": "Maximum bytes to read (optional)", "default": null},
71 "chunk_lines": {"type": "integer", "description": "Line threshold for chunking (optional, default: 2000)", "default": 2000},
72 "max_lines": {"type": "integer", "description": "Alternative parameter for chunk_lines (optional)", "default": null}
73 },
74 "required": ["path"]
75 }),
76 },
77
78 FunctionDeclaration {
80 name: tools::WRITE_FILE.to_string(),
81 description: "Create or modify files. Modes: 'overwrite' (replace), 'append' (add to end), 'skip_if_exists' (safe create). Ensure directory exists first. Validate after write.".to_string(),
82 parameters: json!({
83 "type": "object",
84 "properties": {
85 "path": {"type": "string", "description": "File path to write to"},
86 "content": {"type": "string", "description": "Content to write to the file"},
87 "mode": {"type": "string", "description": "Write mode: 'overwrite' (default) or 'append'", "default": "overwrite"}
88 },
89 "required": ["path", "content"]
90 }),
91 },
92
93 FunctionDeclaration {
95 name: tools::EDIT_FILE.to_string(),
96 description: "Precise text replacement via exact string match. Read file first to capture old_str with exact whitespace/indentation. Preferred over write_file for targeted changes. Cannot handle pattern-based refactoring—use ast_grep for that.".to_string(),
97 parameters: json!({
98 "type": "object",
99 "properties": {
100 "path": {"type": "string", "description": "File path to edit"},
101 "old_str": {"type": "string", "description": "Exact text to replace (must match exactly)"},
102 "new_str": {"type": "string", "description": "New text to replace with"}
103 },
104 "required": ["path", "old_str", "new_str"]
105 }),
106 },
107
108 FunctionDeclaration {
110 name: tools::RUN_TERMINAL_CMD.to_string(),
111 description: "Execute shell commands. Auto-truncates large output (>10k lines): first 5k + last 5k. Modes: 'terminal' (default), 'pty' (interactive), 'streaming' (long-running). Set timeouts. Prefer specialized tools for file ops.".to_string(),
112 parameters: json!({
113 "type": "object",
114 "properties": {
115 "command": {"type": "array", "items": {"type": "string"}, "description": "Program + args as array"},
116 "working_dir": {"type": "string", "description": "Working directory relative to workspace"},
117 "timeout_secs": {"type": "integer", "description": "Command timeout in seconds (default: 30)", "default": 30},
118 "mode": {"type": "string", "description": "Execution mode: 'terminal' | 'pty' | 'streaming'", "default": "terminal"},
119 "response_format": {"type": "string", "description": "'concise' (default) or 'detailed'", "default": "concise"}
120 },
121 "required": ["command"]
122 }),
123 },
124 FunctionDeclaration {
125 name: tools::RUN_PTY_CMD.to_string(),
126 description: "Execute a command inside a pseudo-terminal. Use for interactive programs (e.g., REPLs, full-screen TUIs) that require TTY semantics. Provide the command as a string plus optional args array or as an array of program + args.".to_string(),
127 parameters: json!({
128 "type": "object",
129 "properties": {
130 "command": {
131 "description": "Command to run (string or [program, ...args])",
132 "oneOf": [
133 {"type": "string"},
134 {"type": "array", "items": {"type": "string"}}
135 ]
136 },
137 "args": {
138 "type": "array",
139 "items": {"type": "string"},
140 "description": "Arguments to append when command is provided as string"
141 },
142 "working_dir": {
143 "type": "string",
144 "description": "Working directory relative to the workspace"
145 },
146 "timeout_secs": {
147 "type": "integer",
148 "description": "Timeout before forcefully terminating the command",
149 "default": 300
150 },
151 "rows": {
152 "type": "integer",
153 "description": "Pseudo-terminal rows",
154 "default": 24
155 },
156 "cols": {
157 "type": "integer",
158 "description": "Pseudo-terminal columns",
159 "default": 80
160 }
161 },
162 "required": ["command"]
163 }),
164 },
165 FunctionDeclaration {
166 name: tools::CREATE_PTY_SESSION.to_string(),
167 description: "Spawn and register a persistent PTY session (e.g., bash shell) that can be reused across tool calls. Requires a unique session_id.".to_string(),
168 parameters: json!({
169 "type": "object",
170 "properties": {
171 "session_id": {"type": "string", "description": "Unique identifier for the PTY session"},
172 "command": {
173 "description": "Command to start in the session (string or [program, ...args])",
174 "oneOf": [
175 {"type": "string"},
176 {"type": "array", "items": {"type": "string"}}
177 ]
178 },
179 "args": {
180 "type": "array",
181 "items": {"type": "string"},
182 "description": "Arguments to append when command is provided as string"
183 },
184 "working_dir": {
185 "type": "string",
186 "description": "Working directory relative to the workspace"
187 },
188 "rows": {
189 "type": "integer",
190 "description": "Pseudo-terminal rows",
191 "default": 24
192 },
193 "cols": {
194 "type": "integer",
195 "description": "Pseudo-terminal columns",
196 "default": 80
197 }
198 },
199 "required": ["session_id", "command"]
200 }),
201 },
202 FunctionDeclaration {
203 name: tools::LIST_PTY_SESSIONS.to_string(),
204 description: "List active PTY sessions created via create_pty_session.".to_string(),
205 parameters: json!({
206 "type": "object",
207 "properties": {},
208 "additionalProperties": false
209 }),
210 },
211 FunctionDeclaration {
212 name: tools::CLOSE_PTY_SESSION.to_string(),
213 description: "Terminate and remove a persistent PTY session.".to_string(),
214 parameters: json!({
215 "type": "object",
216 "properties": {
217 "session_id": {"type": "string", "description": "Identifier returned by create_pty_session"}
218 },
219 "required": ["session_id"]
220 }),
221 },
222 FunctionDeclaration {
223 name: tools::CURL.to_string(),
224 description: "Fetch HTTPS content (sandboxed). Public hosts only—blocks localhost/private IPs. Size-limited. Returns security_notice. Use for documentation or small JSON payloads.".to_string(),
225 parameters: json!({
226 "type": "object",
227 "properties": {
228 "url": {"type": "string", "description": "HTTPS URL to fetch (public hosts only)."},
229 "method": {"type": "string", "description": "HTTP method: 'GET' (default) or 'HEAD'.", "default": "GET"},
230 "max_bytes": {"type": "integer", "description": "Maximum response bytes to read (must respect policy cap).", "default": 65536},
231 "timeout_secs": {"type": "integer", "description": "Request timeout in seconds (<=30)", "default": 10},
232 "save_response": {"type": "boolean", "description": "When true, saves the body to /tmp/vtcode-curl and returns the path so you can inspect then delete it.", "default": false}
233 },
234 "required": ["url"]
235 }),
236 },
237
238 FunctionDeclaration {
240 name: tools::AST_GREP_SEARCH.to_string(),
241 description: "Syntax-aware code search and refactoring using AST patterns. Operations: 'search', 'transform', 'lint', 'refactor'. Use when text search is insufficient (structural patterns, multi-lang support). Preview transforms before applying. Set reasonable limits for token efficiency.".to_string(),
242 parameters: json!({
243 "type": "object",
244 "properties": {
245 "operation": {"type": "string", "description": "Operation type: 'search', 'transform', 'lint', 'refactor', 'custom'", "default": "search"},
246 "pattern": {"type": "string", "description": "AST-grep pattern to search for"},
247 "path": {"type": "string", "description": "File or directory path to search in", "default": "."},
248 "language": {"type": "string", "description": "Programming language (auto-detected if not specified)"},
249 "replacement": {"type": "string", "description": "Replacement pattern for transform operations"},
250 "refactor_type": {"type": "string", "description": "Type of refactoring: 'extract_function', 'remove_console_logs', 'simplify_conditions', 'extract_constants', 'modernize_syntax'"},
251 "context_lines": {"type": "integer", "description": "Number of context lines to show", "default": 0},
252 "max_results": {"type": "integer", "description": "Maximum number of results", "default": 100},
253 "preview_only": {"type": "boolean", "description": "Preview changes without applying (transform only)", "default": true},
254 "update_all": {"type": "boolean", "description": "Update all matches (transform only)", "default": false},
255 "interactive": {"type": "boolean", "description": "Interactive mode (custom only)", "default": false},
256 "severity_filter": {"type": "string", "description": "Filter lint results by severity"}
257 },
258 "required": ["pattern", "path"]
259 }),
260 },
261
262 FunctionDeclaration {
264 name: tools::SIMPLE_SEARCH.to_string(),
265 description: "Provides simple bash-like file operations and searches for quick, straightforward tasks. This tool offers direct access to common Unix commands like grep, find, ls, cat, head, tail, and file indexing. Use this tool when you need basic file operations without the complexity of advanced search features. It is ideal for quick file content previews, directory listings, or simple pattern matching. The tool supports various commands: 'grep' for text searching, 'find' for file discovery, 'ls' for directory listing, 'cat' for full file reading, 'head'/'tail' for partial file reading, and 'index' for file indexing. This tool is less powerful than specialized search tools but provides fast, intuitive access to common operations. Use appropriate max_results limits to prevent excessive output, especially with recursive operations.".to_string(),
266 parameters: json!({
267 "type": "object",
268 "properties": {
269 "command": {"type": "string", "description": "Command to execute: 'grep', 'find', 'ls', 'cat', 'head', 'tail', 'index'", "default": "grep"},
270 "pattern": {"type": "string", "description": "Search pattern for grep/find commands"},
271 "file_pattern": {"type": "string", "description": "File pattern filter for grep"},
272 "file_path": {"type": "string", "description": "File path for cat/head/tail commands"},
273 "path": {"type": "string", "description": "Directory path for ls/find/index commands", "default": "."},
274 "start_line": {"type": "integer", "description": "Start line number for cat command"},
275 "end_line": {"type": "integer", "description": "End line number for cat command"},
276 "lines": {"type": "integer", "description": "Number of lines for head/tail commands", "default": 10},
277 "max_results": {"type": "integer", "description": "Maximum results to return", "default": 50},
278 "show_hidden": {"type": "boolean", "description": "Show hidden files for ls command", "default": false}
279 },
280 "required": []
281 }),
282 },
283
284 FunctionDeclaration {
286 name: tools::BASH.to_string(),
287 description: "Executes bash-like commands through a pseudo-terminal interface for interactive operations. This tool provides access to common shell commands with enhanced terminal emulation. Use this tool when you need interactive command execution, complex shell pipelines, or commands that require a proper terminal environment. It supports essential commands like 'ls' for directory listing, 'pwd' for current directory, 'grep' for text search, 'find' for file discovery, 'cat'/'head'/'tail' for file content viewing, and file manipulation commands like 'mkdir', 'rm', 'cp', 'mv'. The tool includes safety restrictions and should be used as a complement to specialized tools rather than a replacement. Prefer 'run_terminal_cmd' for non-interactive commands and file/search tools for file operations. Always use appropriate flags and be aware of the recursive and force options which can affect multiple files.".to_string(),
288 parameters: json!({
289 "type": "object",
290 "properties": {
291 "bash_command": {"type": "string", "description": "Bash command to execute: 'ls', 'pwd', 'grep', 'find', 'cat', 'head', 'tail', 'mkdir', 'rm', 'cp', 'mv', 'stat', 'run'", "default": "ls"},
292 "path": {"type": "string", "description": "Path for file/directory operations"},
293 "source": {"type": "string", "description": "Source path for cp/mv operations"},
294 "dest": {"type": "string", "description": "Destination path for cp/mv operations"},
295 "pattern": {"type": "string", "description": "Search pattern for grep/find"},
296 "recursive": {"type": "boolean", "description": "Recursive operation", "default": false},
297 "show_hidden": {"type": "boolean", "description": "Show hidden files", "default": false},
298 "parents": {"type": "boolean", "description": "Create parent directories", "default": false},
299 "force": {"type": "boolean", "description": "Force operation", "default": false},
300 "lines": {"type": "integer", "description": "Number of lines for head/tail", "default": 10},
301 "start_line": {"type": "integer", "description": "Start line for cat"},
302 "end_line": {"type": "integer", "description": "End line for cat"},
303 "name_pattern": {"type": "string", "description": "Name pattern for find"},
304 "type_filter": {"type": "string", "description": "Type filter for find (f=file, d=directory)"},
305 "command": {"type": "string", "description": "Command to run for arbitrary execution"},
306 "args": {"type": "array", "items": {"type": "string"}, "description": "Arguments for command execution"}
307 },
308 "required": []
309 }),
310 },
311
312 FunctionDeclaration {
314 name: tools::APPLY_PATCH.to_string(),
315 description: "Applies Codex-style patch blocks to modify multiple files in the workspace. This tool is specialized for applying structured patches that contain changes to multiple files or complex modifications. Use this tool when you receive patch content in the Codex format (marked with '*** Begin Patch' and '*** End Patch') instead of making individual file edits. The tool parses the patch format, validates the changes, and applies them atomically to prevent partial updates. It is particularly useful for applying code review suggestions, automated refactoring changes, or complex multi-file modifications. The tool provides detailed feedback on which files were modified and any issues encountered during application. Always ensure the patch content is complete and properly formatted before using this tool.".to_string(),
316 parameters: json!({
317 "type": "object",
318 "properties": {
319 "input": {"type": "string", "description": "Patch content in Codex patch format"}
320 },
321 "required": ["input"]
322 }),
323 },
324 FunctionDeclaration {
325 name: tools::UPDATE_PLAN.to_string(),
326 description: "Records or updates the agent's current multi-step plan. Provide a concise explanation (optional) and a list of plan steps with their status. Exactly one step may be marked 'in_progress'; all other steps must be 'pending' or 'completed'. Use this tool to keep the user informed about your approach for complex tasks, render the plan as a Markdown TODO list with checkboxes, and update it whenever progress changes.".to_string(),
327 parameters: json!({
328 "type": "object",
329 "properties": {
330 "explanation": {
331 "type": "string",
332 "description": "Optional summary explaining the plan or changes made."
333 },
334 "plan": {
335 "type": "array",
336 "description": "Ordered list of plan steps with status metadata.",
337 "items": {
338 "type": "object",
339 "properties": {
340 "step": {
341 "type": "string",
342 "description": "Description of the work to perform."
343 },
344 "status": {
345 "type": "string",
346 "enum": ["pending", "in_progress", "completed"],
347 "description": "Current state of the step."
348 }
349 },
350 "required": ["step", "status"],
351 "additionalProperties": false
352 }
353 }
354 },
355 "required": ["plan"],
356 "additionalProperties": false
357 }),
358 },
359 ]
360}
361
362pub fn build_function_declarations() -> Vec<FunctionDeclaration> {
363 build_function_declarations_with_mode(true)
364}
365
366pub fn build_function_declarations_with_mode(
367 todo_planning_enabled: bool,
368) -> Vec<FunctionDeclaration> {
369 let mut declarations = base_function_declarations();
370 if !todo_planning_enabled {
371 declarations.retain(|decl| decl.name != tools::UPDATE_PLAN);
372 }
373 declarations
374}
375
376pub fn build_function_declarations_for_level(level: CapabilityLevel) -> Vec<FunctionDeclaration> {
378 let tool_capabilities: HashMap<&'static str, CapabilityLevel> = builtin_tool_registrations()
379 .into_iter()
380 .filter(|registration| registration.expose_in_llm())
381 .map(|registration| (registration.name(), registration.capability()))
382 .collect();
383
384 build_function_declarations()
385 .into_iter()
386 .filter(|fd| {
387 tool_capabilities
388 .get(fd.name.as_str())
389 .map(|required| level >= *required)
390 .unwrap_or(false)
391 })
392 .collect()
393}