1use serde_json::json;
2
3use crate::api::types::Tool;
4
5pub fn get_tools_schemas() -> Vec<Tool> {
7 get_filtered_tools_schemas(true, true)
8}
9
10pub fn get_filtered_tools_schemas(is_git_repo: bool, has_github_token: bool) -> Vec<Tool> {
16 let mut tools = Vec::with_capacity(40);
17
18 tools.push(create_tool(
20 "execute_shell_command",
21 "Execute a shell command.",
22 json!({
23 "command": { "type": "string" },
24 "is_background": { "type": "boolean" }
25 }),
26 vec!["command"],
27 ));
28 tools.push(create_tool(
29 "get_system_info",
30 "Get system information.",
31 json!({}),
32 vec![],
33 ));
34 tools.push(create_tool(
35 "start_background_process",
36 "Start a command in the background, allowing the agent to continuously monitor its logs \
37 and terminate it when needed. Ideal for dev servers.",
38 json!({
39 "command": { "type": "string", "description": "The command to run" },
40 "cwd": { "type": "string", "description": "Optional: working directory" },
41 "env": {
42 "type": "object",
43 "description": "Optional: environment variables as key-value pairs"
44 }
45 }),
46 vec!["command"],
47 ));
48 tools.push(create_tool(
49 "read_background_process_logs",
50 "Read accumulated stdout/stderr logs from a running background process.",
51 json!({
52 "pid": { "type": "integer", "description": "The process ID (PID) of the background process" }
53 }),
54 vec!["pid"],
55 ));
56 tools.push(create_tool(
57 "kill_background_process",
58 "Kill a background process started by the agent.",
59 json!({
60 "pid": { "type": "integer", "description": "The process ID (PID) to terminate" }
61 }),
62 vec!["pid"],
63 ));
64 tools.push(create_tool(
65 "list_background_processes",
66 "List all active background processes started by the agent.",
67 json!({}),
68 vec![],
69 ));
70 tools.push(create_tool(
71 "check_port_status",
72 "Check if a local port is occupied, free, or blocked.",
73 json!({
74 "port": { "type": "integer", "description": "Port number to check" },
75 "host": { "type": "string", "description": "Optional: host (defaults to '127.0.0.1')" }
76 }),
77 vec!["port"],
78 ));
79
80 tools.push(create_tool(
82 "read_local_file",
83 "Read a local file.",
84 json!({
85 "file_path": { "type": "string" },
86 "start_line": { "type": "integer" },
87 "end_line": { "type": "integer" }
88 }),
89 vec!["file_path"],
90 ));
91 tools.push(create_tool(
92 "write_local_file",
93 "Write to a local file.",
94 json!({
95 "file_path": { "type": "string" },
96 "content": { "type": "string" }
97 }),
98 vec!["file_path", "content"],
99 ));
100 tools.push(create_tool(
101 "replace_text_in_file",
102 "Replace text in a file.",
103 json!({
104 "file_path": { "type": "string" },
105 "old_text": { "type": "string" },
106 "new_text": { "type": "string" }
107 }),
108 vec!["file_path", "old_text", "new_text"],
109 ));
110 tools.push(create_tool(
111 "list_directory",
112 "List directory contents.",
113 json!({
114 "path": { "type": "string" }
115 }),
116 vec![],
117 ));
118 tools.push(create_tool(
119 "tree_view",
120 "Show directory tree.",
121 json!({
122 "path": { "type": "string" },
123 "max_depth": { "type": "integer" }
124 }),
125 vec![],
126 ));
127 tools.push(create_tool(
128 "delete_file",
129 "Delete a file or directory.",
130 json!({
131 "file_path": { "type": "string" }
132 }),
133 vec!["file_path"],
134 ));
135 tools.push(create_tool(
136 "rename_file",
137 "Rename or move a file.",
138 json!({
139 "source_path": { "type": "string" },
140 "destination_path": { "type": "string" }
141 }),
142 vec!["source_path", "destination_path"],
143 ));
144 tools.push(create_tool(
145 "diff_files",
146 "Compare two files.",
147 json!({
148 "file1": { "type": "string" },
149 "file2": { "type": "string" }
150 }),
151 vec!["file1", "file2"],
152 ));
153 tools.push(create_tool(
154 "hash_file",
155 "Calculate file hash.",
156 json!({
157 "path": { "type": "string" },
158 "algorithm": { "type": "string", "enum": ["sha256", "md5"] }
159 }),
160 vec!["path"],
161 ));
162 tools.push(create_tool(
163 "count_lines",
164 "Count lines, words and characters in a file.",
165 json!({
166 "path": { "type": "string" }
167 }),
168 vec!["path"],
169 ));
170 tools.push(create_tool(
171 "search_files",
172 "Search files for a text pattern using native Rust (no shell process needed). Fast \
173 parallel search with regex support.",
174 json!({
175 "query": { "type": "string" },
176 "path": { "type": "string" },
177 "glob": { "type": "string" },
178 "max_results": { "type": "integer" }
179 }),
180 vec!["query"],
181 ));
182 tools.push(create_tool(
183 "bulk_rename",
184 "Rename multiple files in a directory using a regex pattern.",
185 json!({
186 "path": { "type": "string" },
187 "pattern": { "type": "string" },
188 "replacement": { "type": "string" }
189 }),
190 vec!["path", "pattern", "replacement"],
191 ));
192
193 tools.push(create_tool(
194 "copy_file",
195 "Copy a file from source_path to destination_path natively.",
196 json!({
197 "source_path": { "type": "string" },
198 "destination_path": { "type": "string" }
199 }),
200 vec!["source_path", "destination_path"],
201 ));
202 tools.push(create_tool(
203 "copy_directory",
204 "Recursively copy a directory from source_path to destination_path natively.",
205 json!({
206 "source_path": { "type": "string" },
207 "destination_path": { "type": "string" }
208 }),
209 vec!["source_path", "destination_path"],
210 ));
211 tools.push(create_tool(
212 "create_directory",
213 "Create a directory (and any necessary parent directories) natively.",
214 json!({
215 "directory_path": { "type": "string" }
216 }),
217 vec!["directory_path"],
218 ));
219 tools.push(create_tool(
220 "file_exists",
221 "Check if a file or directory exists at the given path.",
222 json!({
223 "file_path": { "type": "string" }
224 }),
225 vec!["file_path"],
226 ));
227 tools.push(create_tool(
228 "get_file_info",
229 "Get metadata for a file (type, size, timestamps, permissions) natively.",
230 json!({
231 "file_path": { "type": "string" }
232 }),
233 vec!["file_path"],
234 ));
235
236 tools.push(create_tool(
238 "run_python_code",
239 "Execute Python code snippet.",
240 json!({
241 "code": { "type": "string" }
242 }),
243 vec!["code"],
244 ));
245 tools.push(create_tool(
246 "fetch_url",
247 "Fetch and clean content from a URL.",
248 json!({
249 "url": { "type": "string" }
250 }),
251 vec!["url"],
252 ));
253 tools.push(create_tool(
254 "get_env_var",
255 "Read an environment variable.",
256 json!({
257 "name": { "type": "string" }
258 }),
259 vec!["name"],
260 ));
261
262 tools.push(create_tool(
264 "regex_replace_in_file",
265 "Replace text in a file using a regular expression.",
266 json!({
267 "file_path": { "type": "string" },
268 "regex": { "type": "string" },
269 "replacement": { "type": "string" }
270 }),
271 vec!["file_path", "regex", "replacement"],
272 ));
273 tools.push(create_tool(
274 "json_update_value",
275 "Read a JSON file, update a value at a specified key path (e.g. \
276 'dependencies.tokio.version'), and save it.",
277 json!({
278 "file_path": { "type": "string" },
279 "key_path": { "type": "string" },
280 "new_value": { "type": "string" }
281 }),
282 vec!["file_path", "key_path", "new_value"],
283 ));
284 tools.push(create_tool(
285 "edit_file_by_lines",
286 "Edit a file by specifying one or more non-overlapping line ranges. This is highly \
287 efficient for modifying code without rewriting the whole file.",
288 json!({
289 "file_path": {
290 "type": "string",
291 "description": "Path to the file to edit"
292 },
293 "edits": {
294 "type": "array",
295 "description": "List of line-based replacement edits",
296 "items": {
297 "type": "object",
298 "properties": {
299 "start_line": {
300 "type": "integer",
301 "description": "The 1-indexed start line number of the range to replace (inclusive)"
302 },
303 "end_line": {
304 "type": "integer",
305 "description": "The 1-indexed end line number of the range to replace (inclusive)"
306 },
307 "replacement_content": {
308 "type": "string",
309 "description": "The new content to insert in place of the target line range"
310 },
311 "target_content": {
312 "type": "string",
313 "description": "Optional: The exact content of the lines being replaced. If provided, it will be verified against the file contents to ensure correctness before applying the edit."
314 }
315 },
316 "required": ["start_line", "end_line", "replacement_content"]
317 }
318 }
319 }),
320 vec!["file_path", "edits"],
321 ));
322 tools.push(create_tool(
323 "apply_diff_patch",
324 "Apply a unified diff patch to a local file. Ideal for complex or multi-hunk code modifications.",
325 json!({
326 "file_path": { "type": "string", "description": "Path to the file to patch" },
327 "patch_content": { "type": "string", "description": "The unified diff content (including hunk headers @@)" }
328 }),
329 vec!["file_path", "patch_content"],
330 ));
331 tools.push(create_tool(
332 "list_symbols",
333 "Parse code symbols (functions, structs, classes, etc.) from a file using lightweight \
334 regex.",
335 json!({
336 "file_path": { "type": "string" }
337 }),
338 vec!["file_path"],
339 ));
340 tools.push(create_tool(
341 "view_symbol_contents",
342 "View the full implementation code of a specific symbol (function, class, struct, enum, or impl) from a file.",
343 json!({
344 "file_path": { "type": "string", "description": "Path to the file to inspect" },
345 "symbol_name": { "type": "string", "description": "The name of the symbol/definition to view" }
346 }),
347 vec!["file_path", "symbol_name"],
348 ));
349 tools.push(create_tool(
350 "screenshot_webapp",
351 "Take a screenshot of a local web app or website using Microsoft Edge or Google Chrome in \
352 headless mode.",
353 json!({
354 "url": { "type": "string" },
355 "output_path": { "type": "string" }
356 }),
357 vec!["url", "output_path"],
358 ));
359 tools.push(create_tool(
360 "web_search_duckduckgo",
361 "Perform an internet search query via DuckDuckGo and return top results.",
362 json!({
363 "query": { "type": "string" }
364 }),
365 vec!["query"],
366 ));
367
368 tools.push(create_tool(
370 "move_code_block",
371 "Move a code block (function, struct, etc.) from one file to another using regex.",
372 json!({
373 "source_path": { "type": "string" },
374 "destination_path": { "type": "string" },
375 "block_pattern": { "type": "string" }
376 }),
377 vec!["source_path", "destination_path", "block_pattern"],
378 ));
379 tools.push(create_tool(
380 "split_file",
381 "Split a file into multiple parts based on a regex pattern.",
382 json!({
383 "file_path": { "type": "string" },
384 "split_pattern": { "type": "string" },
385 "output_prefix": { "type": "string" }
386 }),
387 vec!["file_path", "split_pattern", "output_prefix"],
388 ));
389 tools.push(create_tool(
390 "cleanup_file",
391 "Clean up a file by removing trailing spaces and normalizing line endings.",
392 json!({
393 "file_path": { "type": "string" }
394 }),
395 vec!["file_path"],
396 ));
397 tools.push(create_tool(
398 "summarize_project",
399 "Analyze the current project and provide a high-level summary of files, languages, and \
400 structure.",
401 json!({}),
402 vec![],
403 ));
404 tools.push(create_tool(
405 "list_todo_tasks",
406 "Search the project for TODO, FIXME, HACK, and BUG comments and list them with file and \
407 line info.",
408 json!({}),
409 vec![],
410 ));
411 tools.push(create_tool(
412 "project_checkpoint",
413 "Create a project-wide backup archive of the source code and configuration.",
414 json!({
415 "name": { "type": "string", "description": "Short mnemonic name for the checkpoint" }
416 }),
417 vec!["name"],
418 ));
419 tools.push(create_tool(
420 "restore_checkpoint",
421 "Restore the project from a previously created checkpoint archive.",
422 json!({
423 "checkpoint_file": { "type": "string", "description": "Filename of the .tar.gz checkpoint" }
424 }),
425 vec!["checkpoint_file"],
426 ));
427 tools.push(create_tool(
428 "project_wide_replace",
429 "Perform a global search and replace across the entire project (filtering target files by \
430 glob).",
431 json!({
432 "old_text": { "type": "string" },
433 "new_text": { "type": "string" },
434 "glob": { "type": "string", "description": "Glob pattern for files, e.g. '**/*.rs'" }
435 }),
436 vec!["old_text", "new_text"],
437 ));
438
439 if is_git_repo {
441 tools.push(create_tool(
442 "git_status",
443 "Show git status.",
444 json!({
445 "path": { "type": "string" }
446 }),
447 vec![],
448 ));
449 tools.push(create_tool(
450 "git_diff",
451 "Show git diff.",
452 json!({
453 "path": { "type": "string" },
454 "staged": { "type": "boolean" }
455 }),
456 vec![],
457 ));
458 tools.push(create_tool(
459 "git_log",
460 "Show git commit history.",
461 json!({
462 "path": { "type": "string" },
463 "count": { "type": "integer" }
464 }),
465 vec![],
466 ));
467 tools.push(create_tool(
468 "git_branch",
469 "List, create, delete, or switch git branches.",
470 json!({
471 "path": { "type": "string" },
472 "action": { "type": "string", "enum": ["list", "create", "delete", "switch"] },
473 "name": { "type": "string" }
474 }),
475 vec![],
476 ));
477 tools.push(create_tool(
478 "git_add",
479 "Stage files for commit.",
480 json!({
481 "path": { "type": "string" },
482 "files": { "type": "string" }
483 }),
484 vec![],
485 ));
486 tools.push(create_tool(
487 "git_commit",
488 "Commit staged changes.",
489 json!({
490 "path": { "type": "string" },
491 "message": { "type": "string" }
492 }),
493 vec!["message"],
494 ));
495 tools.push(create_tool(
496 "git_push",
497 "Push commits to remote.",
498 json!({
499 "path": { "type": "string" },
500 "remote": { "type": "string" },
501 "branch": { "type": "string" }
502 }),
503 vec![],
504 ));
505 tools.push(create_tool(
506 "git_pull",
507 "Pull changes from remote.",
508 json!({
509 "path": { "type": "string" },
510 "remote": { "type": "string" },
511 "branch": { "type": "string" }
512 }),
513 vec![],
514 ));
515 tools.push(create_tool(
516 "git_checkout",
517 "Checkout branch or file.",
518 json!({
519 "path": { "type": "string" },
520 "target": { "type": "string" }
521 }),
522 vec!["target"],
523 ));
524 tools.push(create_tool(
525 "git_clone",
526 "Clone a git repository.",
527 json!({
528 "url": { "type": "string" },
529 "dest": { "type": "string" }
530 }),
531 vec!["url"],
532 ));
533 tools.push(create_tool(
534 "git_remote_list",
535 "List git remotes.",
536 json!({
537 "path": { "type": "string" }
538 }),
539 vec![],
540 ));
541 tools.push(create_tool(
542 "git_stash",
543 "Stash, pop, or list git stashes.",
544 json!({
545 "path": { "type": "string" },
546 "action": { "type": "string", "enum": ["save", "pop", "list"] }
547 }),
548 vec![],
549 ));
550 }
551
552 if has_github_token {
554 tools.push(create_tool(
555 "github_repo_info",
556 "Get GitHub repository information. Requires GITHUB_TOKEN.",
557 json!({
558 "repo": { "type": "string" }
559 }),
560 vec!["repo"],
561 ));
562 tools.push(create_tool(
563 "github_repo_list_issues",
564 "List GitHub issues for a repository.",
565 json!({
566 "repo": { "type": "string" },
567 "state": { "type": "string", "enum": ["open", "closed", "all"] },
568 "limit": { "type": "integer" }
569 }),
570 vec!["repo"],
571 ));
572 tools.push(create_tool(
573 "github_issue_create",
574 "Create a GitHub issue. Requires GITHUB_TOKEN.",
575 json!({
576 "repo": { "type": "string" },
577 "title": { "type": "string" },
578 "body": { "type": "string" },
579 "labels": { "type": "string" }
580 }),
581 vec!["repo", "title"],
582 ));
583 tools.push(create_tool(
584 "github_issue_update",
585 "Update a GitHub issue. Requires GITHUB_TOKEN.",
586 json!({
587 "repo": { "type": "string" },
588 "issue_number": { "type": "integer" },
589 "title": { "type": "string" },
590 "body": { "type": "string" },
591 "state": { "type": "string", "enum": ["open", "closed"] }
592 }),
593 vec!["repo", "issue_number"],
594 ));
595 tools.push(create_tool(
596 "github_pr_list",
597 "List GitHub pull requests.",
598 json!({
599 "repo": { "type": "string" },
600 "state": { "type": "string", "enum": ["open", "closed", "all"] },
601 "limit": { "type": "integer" }
602 }),
603 vec!["repo"],
604 ));
605 tools.push(create_tool(
606 "github_pr_create",
607 "Create a GitHub pull request. Requires GITHUB_TOKEN.",
608 json!({
609 "repo": { "type": "string" },
610 "title": { "type": "string" },
611 "head": { "type": "string" },
612 "base": { "type": "string" },
613 "body": { "type": "string" },
614 "draft": { "type": "boolean" }
615 }),
616 vec!["repo", "title", "head", "base"],
617 ));
618 tools.push(create_tool(
619 "github_pr_info",
620 "Get detailed information about a pull request.",
621 json!({
622 "repo": { "type": "string" },
623 "pr_number": { "type": "integer" }
624 }),
625 vec!["repo", "pr_number"],
626 ));
627 tools.push(create_tool(
628 "github_pr_merge",
629 "Merge a GitHub pull request. Requires GITHUB_TOKEN.",
630 json!({
631 "repo": { "type": "string" },
632 "pr_number": { "type": "integer" },
633 "method": { "type": "string", "enum": ["merge", "squash", "rebase"] }
634 }),
635 vec!["repo", "pr_number"],
636 ));
637 tools.push(create_tool(
638 "github_search_code",
639 "Search code on GitHub. Requires GITHUB_TOKEN.",
640 json!({
641 "query": { "type": "string" },
642 "repo": { "type": "string" },
643 "limit": { "type": "integer" }
644 }),
645 vec!["query"],
646 ));
647 tools.push(create_tool(
648 "github_search_repos",
649 "Search GitHub repositories. Requires GITHUB_TOKEN.",
650 json!({
651 "query": { "type": "string" },
652 "limit": { "type": "integer" }
653 }),
654 vec!["query"],
655 ));
656 tools.push(create_tool(
657 "github_get_file",
658 "Get file content from a GitHub repository.",
659 json!({
660 "repo": { "type": "string" },
661 "path": { "type": "string" },
662 "ref": { "type": "string" }
663 }),
664 vec!["repo", "path"],
665 ));
666 tools.push(create_tool(
667 "github_workflow_list",
668 "List GitHub Actions workflows.",
669 json!({
670 "repo": { "type": "string" }
671 }),
672 vec!["repo"],
673 ));
674 tools.push(create_tool(
675 "github_workflow_runs",
676 "List GitHub Actions workflow runs.",
677 json!({
678 "repo": { "type": "string" },
679 "workflow_id": { "type": "string" },
680 "limit": { "type": "integer" }
681 }),
682 vec!["repo"],
683 ));
684 }
685
686 tools
687}
688
689fn create_tool(name: &str, desc: &str, props: serde_json::Value, required: Vec<&str>) -> Tool {
690 Tool {
691 r#type: "function".to_string(),
692 function: crate::api::types::FunctionDefinition {
693 name: name.to_string(),
694 description: desc.to_string(),
695 parameters: json!({
696 "type": "object",
697 "properties": props,
698 "required": required
699 }),
700 },
701 }
702}