gestura_core_tools/
registry.rs

1//! Tool Registry
2//!
3//! Provides an authoritative, deterministic inventory of Gestura's built-in
4//! tools. This is used by interactive agent commands (e.g. `/tools`) and for
5//! answering common "what tools do you have" questions without relying on an
6//! LLM response.
7//!
8//!
9//! Note: This registry is intentionally **static** and does not depend on the
10//! `gestura-core` facade's `AppConfig`. Dynamic, configuration-dependent
11//! capability rendering lives in the `gestura-core` facade crate.
12
13/// A single tool definition.
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct ToolDefinition {
16    /// Tool name as referenced by users.
17    pub name: &'static str,
18    /// Short summary of what the tool does (one-liner, shown in tool lists).
19    pub summary: &'static str,
20    /// Rich LLM-facing description used in routing prompts and provider tool schemas.
21    ///
22    /// Should be 2-4 sentences explaining *when* to use this tool, *what* it does,
23    /// and any important caveats. More expressive than `summary`.
24    pub description: &'static str,
25    /// Keywords for semantic tool matching during pre-flight routing.
26    ///
27    /// These are lower-case tokens that strongly correlate with a user request
28    /// that should trigger this tool. Used by keyword and hybrid routers.
29    pub keywords: &'static [&'static str],
30    /// Human-readable list of inputs / parameters.
31    pub inputs: &'static [&'static str],
32    /// Human-readable list of side effects / security implications.
33    pub side_effects: &'static [&'static str],
34    /// Example invocations.
35    pub examples: &'static [&'static str],
36}
37
38/// Canonical built-in code tool names, including the legacy aggregate `code`
39/// entry and the newer split code tools.
40pub fn code_tool_names() -> &'static [&'static str] {
41    const CODE_TOOL_NAMES: &[&str] = &[
42        "code",
43        "code_read_files",
44        "code_edit_files",
45        "code_outline",
46        "code_symbols",
47        "code_references",
48        "code_definition",
49        "code_glob",
50        "code_grep",
51        "code_map",
52        "code_stats",
53        "code_deps",
54        "code_lint",
55        "code_test",
56    ];
57
58    CODE_TOOL_NAMES
59}
60
61/// Return whether a tool name belongs to the code tool family.
62pub fn is_code_tool_name(name: &str) -> bool {
63    code_tool_names()
64        .iter()
65        .any(|tool_name| tool_name.eq_ignore_ascii_case(name.trim()))
66}
67
68/// Return the set of built-in tools.
69pub fn all_tools() -> &'static [ToolDefinition] {
70    static TOOLS: &[ToolDefinition] = &[
71        ToolDefinition {
72            name: "file",
73            summary: "Read/write/list files and directories (workspace & sandbox-aware)",
74            description: "Read, write, edit, list, search, and navigate files and directories in \
75                the workspace. Use this tool whenever the user wants to open, create, modify, \
76                delete, or inspect file or directory contents. Workspace-sandboxed to prevent \
77                accidental access outside the project root.",
78            keywords: &[
79                "file",
80                "read",
81                "write",
82                "edit",
83                "create",
84                "delete",
85                "save",
86                "directory",
87                "folder",
88                "path",
89                "list",
90                "search",
91                "content",
92                "text",
93                "document",
94                "open",
95                "load",
96                "cat",
97                "find",
98                "ls",
99                "tree",
100            ],
101            inputs: &[
102                "path",
103                "operation (read/write/list)",
104                "content (for write)",
105                "options",
106            ],
107            side_effects: &["Reads local files", "May write/modify local files"],
108            examples: &[
109                "gestura tools file read ./README.md",
110                "gestura tools file write ./notes.txt --content \"hello\"",
111            ],
112        },
113        ToolDefinition {
114            name: "shell",
115            summary: "Run shell commands in a controlled environment",
116            description: "Execute shell commands, scripts, and programs in a controlled \
117                environment. Use this tool to run build systems (cargo, npm, make), run tests, \
118                lint code, install packages, or perform any other command-line operation. \
119                Runs in the workspace directory by default.",
120            keywords: &[
121                "shell", "run", "execute", "command", "terminal", "bash", "script", "process",
122                "npm", "cargo", "make", "build", "test", "lint", "install", "start", "stop",
123                "restart", "deploy", "compile", "launch",
124            ],
125            inputs: &["command", "cwd", "env"],
126            side_effects: &[
127                "Executes local processes",
128                "May modify local state depending on command",
129            ],
130            examples: &[
131                "gestura tools shell run -- command=\"ls -la\"",
132                "gestura tools shell run -- cwd=. -- command=\"cargo test\"",
133            ],
134        },
135        ToolDefinition {
136            name: "git",
137            summary: "Git status/diff/log operations for code workflows",
138            description: "Interact with Git repositories: show status, view diffs, read commit \
139                history, manage branches, stage and commit changes, stash, and resolve conflicts. \
140                Use this tool whenever the user asks about code changes, version history, \
141                branches, or anything related to source control.",
142            keywords: &[
143                "git",
144                "commit",
145                "branch",
146                "diff",
147                "status",
148                "log",
149                "merge",
150                "push",
151                "pull",
152                "repository",
153                "version",
154                "history",
155                "staged",
156                "stash",
157                "rebase",
158                "checkout",
159                "blame",
160                "conflict",
161                "tag",
162                "remote",
163            ],
164            inputs: &[
165                "repository path",
166                "operation (status/diff/log/etc)",
167                "options",
168            ],
169            side_effects: &[
170                "Reads git metadata",
171                "May change repo state for write operations",
172            ],
173            examples: &[
174                "gestura tools git status",
175                "gestura tools git diff --staged",
176            ],
177        },
178        ToolDefinition {
179            name: "code",
180            summary: "Analyze, search, edit, lint, and test code with a compatibility-friendly aggregate interface",
181            description: "Inspect and operate on source code using the legacy aggregate code tool interface. Use this when the model or session configuration expects a single `code` tool that can route to operations like stats, map, symbols, grep, batch_read, batch_edit, lint, and test. The runtime also exposes stricter split code tools, but this compatibility entry remains available for existing prompts, policies, and sessions.",
182            keywords: &[
183                "code",
184                "source",
185                "read",
186                "edit",
187                "refactor",
188                "search",
189                "grep",
190                "symbols",
191                "definition",
192                "references",
193                "outline",
194                "map",
195                "stats",
196                "deps",
197                "lint",
198                "test",
199                "batch",
200                "files",
201            ],
202            inputs: &[
203                "operation",
204                "path",
205                "symbol / pattern (operation-specific)",
206                "paths / edits / options (operation-specific)",
207            ],
208            side_effects: &[
209                "Reads local code files",
210                "May write local code files for batch_edit",
211                "May execute local subprocesses for lint/test",
212            ],
213            examples: &[
214                "Code stats: {operation:stats, path:.}",
215                "Read files: {operation:batch_read, paths:[src/main.rs, src/lib.rs]}",
216                "Edit files: {operation:batch_edit, edits:[{path:src/lib.rs, old_str:old, new_str:new}]}",
217            ],
218        },
219        ToolDefinition {
220            name: "code_read_files",
221            summary: "Read one or more exact source files with a strict file-only contract",
222            description: "Read exact file contents for known source files. Use this after you know the specific file paths you need. Pass `paths`; do not pass directory roots or edit-style arguments.",
223            keywords: &[
224                "code", "read", "file", "source", "contents", "open", "inspect",
225            ],
226            inputs: &["paths"],
227            side_effects: &["Reads local code files"],
228            examples: &["Read files: {paths:[src/main.rs, src/lib.rs]}"],
229        },
230        ToolDefinition {
231            name: "code_edit_files",
232            summary: "Apply exact str-replace edits across one or more files with a strict schema",
233            description: "Edit existing files using an `edits` array. Each edit must include only `path`, `old_str`, and `new_str`. This tool is strict: do not pass directories, `pattern`, `symbol`, or read-style fields.",
234            keywords: &["code", "edit", "replace", "refactor", "rewrite", "modify"],
235            inputs: &["edits"],
236            side_effects: &["Writes local code files"],
237            examples: &[
238                "Edit files: {edits:[{path:src/lib.rs, old_str:old_name, new_str:new_name}]}",
239            ],
240        },
241        ToolDefinition {
242            name: "code_outline",
243            summary: "Return a structured outline for one exact source file",
244            description: "Extract the outline of a single file, including functions, structs, enums, and impls. Requires an exact file path.",
245            keywords: &["code", "outline", "api", "file", "structure", "symbols"],
246            inputs: &["path"],
247            side_effects: &["Reads local code files"],
248            examples: &["Outline a file: {path:crates/my-crate/src/lib.rs}"],
249        },
250        ToolDefinition {
251            name: "code_symbols",
252            summary: "Extract top-level symbols from one exact source file",
253            description: "List the top-level symbols from a file. Requires an exact file path.",
254            keywords: &["code", "symbols", "file", "function", "struct", "enum"],
255            inputs: &["path"],
256            side_effects: &["Reads local code files"],
257            examples: &["Symbols: {path:crates/my-crate/src/lib.rs}"],
258        },
259        ToolDefinition {
260            name: "code_references",
261            summary: "Find references to a symbol across an existing code path",
262            description: "Find references for a symbol inside a file or directory path.",
263            keywords: &["code", "references", "symbol", "usage", "find"],
264            inputs: &["symbol", "path"],
265            side_effects: &["Reads local code files"],
266            examples: &["References: {symbol:MyStruct, path:.}"],
267        },
268        ToolDefinition {
269            name: "code_definition",
270            summary: "Jump to the first definition of a symbol across an existing code path",
271            description: "Find the first definition for a symbol inside a file or directory path.",
272            keywords: &["code", "definition", "symbol", "declare", "where"],
273            inputs: &["symbol", "path"],
274            side_effects: &["Reads local code files"],
275            examples: &["Definition: {symbol:execute_tool, path:.}"],
276        },
277        ToolDefinition {
278            name: "code_glob",
279            summary: "Find files by glob pattern inside a directory",
280            description: "Search for files using a glob pattern relative to a directory root.",
281            keywords: &["code", "glob", "files", "match", "pattern", "discover"],
282            inputs: &["path", "pattern", "max_results"],
283            side_effects: &["Reads local code metadata"],
284            examples: &["Glob files: {path:., pattern:**/*.rs}"],
285        },
286        ToolDefinition {
287            name: "code_grep",
288            summary: "Regex search code contents inside a directory",
289            description: "Search file contents with a regex pattern, optional file glob filtering, and context lines.",
290            keywords: &["code", "grep", "search", "regex", "text", "pattern"],
291            inputs: &[
292                "path",
293                "pattern",
294                "file_glob",
295                "context_lines",
296                "max_results",
297            ],
298            side_effects: &["Reads local code files"],
299            examples: &[
300                "Grep code: {path:., pattern:fn handle_request, file_glob:*.rs, context_lines:3}",
301            ],
302        },
303        ToolDefinition {
304            name: "code_map",
305            summary: "Summarize repository structure for a directory",
306            description: "Build a repository map for a directory root.",
307            keywords: &["code", "map", "repo", "structure", "directory", "overview"],
308            inputs: &["path", "max_depth"],
309            side_effects: &["Reads local code metadata"],
310            examples: &["Map repo: {path:., max_depth:3}"],
311        },
312        ToolDefinition {
313            name: "code_stats",
314            summary: "Compute line and language statistics for an existing path",
315            description: "Compute language and line-count statistics for a file or directory path.",
316            keywords: &["code", "stats", "lines", "languages", "count"],
317            inputs: &["path"],
318            side_effects: &["Reads local code metadata"],
319            examples: &["Code stats: {path:.}"],
320        },
321        ToolDefinition {
322            name: "code_deps",
323            summary: "Inspect Cargo dependencies for an existing path",
324            description: "Inspect Cargo dependencies for a manifest or project path.",
325            keywords: &["code", "deps", "dependencies", "cargo", "manifest"],
326            inputs: &["path"],
327            side_effects: &["Reads local manifests"],
328            examples: &["Dependencies: {path:.}"],
329        },
330        ToolDefinition {
331            name: "code_lint",
332            summary: "Run linting for a project directory",
333            description: "Run cargo clippy for a project directory. This executes a subprocess.",
334            keywords: &["code", "lint", "clippy", "cargo", "verify"],
335            inputs: &["path", "fix"],
336            side_effects: &[
337                "Executes local lint subprocesses",
338                "May modify files when fix=true",
339            ],
340            examples: &["Lint: {path:.}"],
341        },
342        ToolDefinition {
343            name: "code_test",
344            summary: "Run tests for a project directory",
345            description: "Run cargo test for a project directory, optionally filtered.",
346            keywords: &["code", "test", "cargo", "verify", "unit", "integration"],
347            inputs: &["path", "filter"],
348            side_effects: &["Executes local test subprocesses"],
349            examples: &["Test: {path:., filter:my_test_name}"],
350        },
351        ToolDefinition {
352            name: "web",
353            summary: "Fetch web pages and summarize content",
354            description: "Fetch and read content from web pages, converting HTML to readable \
355                text. Use this tool when the user provides a URL or asks to retrieve information \
356                from a specific website, read documentation, check a webpage, or download content \
357                from the internet. Handles redirects and extracts main body text.",
358            keywords: &[
359                "web",
360                "fetch",
361                "url",
362                "page",
363                "website",
364                "http",
365                "https",
366                "browse",
367                "download",
368                "documentation",
369                "internet",
370                "online",
371                "link",
372                "site",
373                "visit",
374                "open",
375                "retrieve",
376                "get",
377                "html",
378                "content",
379            ],
380            inputs: &["url", "options"],
381            side_effects: &["Performs network requests"],
382            examples: &["gestura tools web fetch https://example.com"],
383        },
384        ToolDefinition {
385            name: "web_search",
386            summary: "Search the web using configurable providers (Local/SerpAPI/DuckDuckGo/Brave)",
387            description: "Search the internet using configurable search providers (DuckDuckGo, \
388                Brave, SerpAPI). Use this tool when the user wants to find information online, \
389                look up a topic, research a question, find recent news or events, or discover \
390                relevant web resources. Returns ranked result snippets with URLs.",
391            keywords: &[
392                "search",
393                "google",
394                "find",
395                "lookup",
396                "query",
397                "results",
398                "internet",
399                "online",
400                "discover",
401                "research",
402                "news",
403                "information",
404                "duckduckgo",
405                "brave",
406                "look up",
407                "what is",
408                "how to",
409                "latest",
410            ],
411            inputs: &["query", "max_results", "provider (optional)"],
412            side_effects: &["Performs network requests", "May use API quotas"],
413            examples: &[
414                "gestura tools web_search \"rust async patterns\"",
415                "gestura tools web_search \"latest AI news\" --max-results 5",
416            ],
417        },
418        ToolDefinition {
419            name: "a2a",
420            summary: "Agent-to-Agent protocol for delegating tasks to remote agents",
421            description: "Delegate tasks to remote AI agents using the Agent-to-Agent (A2A) \
422                protocol. Use this tool when the user wants to offload a subtask to a specialized \
423                remote agent, discover what remote agents are available, or orchestrate multi-agent \
424                workflows where different agents collaborate on a larger goal.",
425            keywords: &[
426                "agent",
427                "a2a",
428                "delegate",
429                "remote",
430                "orchestrate",
431                "multi-agent",
432                "protocol",
433                "worker",
434                "supervisor",
435                "task",
436                "handoff",
437                "subagent",
438                "collaborate",
439            ],
440            inputs: &["agent_url", "task_message", "auth_token (optional)"],
441            side_effects: &[
442                "Performs network requests",
443                "May execute tasks on remote agents",
444            ],
445            examples: &[
446                "gestura tools a2a discover https://agent.example.com",
447                "gestura tools a2a send https://agent.example.com \"Summarize this document\"",
448            ],
449        },
450        ToolDefinition {
451            name: "permissions",
452            summary: "Check/request OS-level permissions (platform-specific)",
453            description: "Check and request OS-level permissions such as microphone access, \
454                accessibility, screen recording, and camera. Use this tool when a feature \
455                requires a system permission that may not yet be granted, or when the user \
456                asks what permissions the app has or needs.",
457            keywords: &[
458                "permission",
459                "permissions",
460                "microphone",
461                "accessibility",
462                "access",
463                "privacy",
464                "camera",
465                "system",
466                "grant",
467                "request",
468                "check",
469                "screen recording",
470                "allow",
471            ],
472            inputs: &["permission kind (microphone, accessibility, etc.)"],
473            side_effects: &["May prompt the OS", "May open system settings"],
474            examples: &[
475                "gestura tools permissions check",
476                "gestura tools permissions request microphone",
477            ],
478        },
479        ToolDefinition {
480            name: "mcp",
481            summary: "Search, evaluate, install, enable, disable, and manage MCP servers from the official registry",
482            description: "Discover and manage Model Context Protocol (MCP) servers from the official \
483                registry at registry.modelcontextprotocol.io. Use this tool when the user wants to \
484                find new MCP servers by keyword, evaluate a specific server's capabilities and \
485                requirements, install a server into .mcp.json, enable or disable configured servers, \
486                list installed servers, or remove a server. Always start with operation=search to \
487                find candidates, then operation=evaluate to review details, then operation=install \
488                to add the server — the tool provides LLM workflow guidance at each step.",
489            keywords: &[
490                "mcp",
491                "install",
492                "search",
493                "discover",
494                "registry",
495                "manage",
496                "configure",
497                "browse",
498                "npm",
499                "npx",
500                "enable",
501                "disable",
502                "remove",
503                "server",
504                "protocol",
505                "extension",
506                "plugin",
507                "external",
508                "connect",
509                "capability",
510                "model context",
511                "integration",
512                "list",
513                "add",
514                "setup",
515                "pypi",
516                "docker",
517                "oci",
518                "stdio",
519                "http",
520                "tool",
521            ],
522            inputs: &[
523                "operation (search/evaluate/install/enable/disable/list/remove/info)",
524                "query (for search)",
525                "limit (for search, default 20)",
526                "server_id (for evaluate/install/info)",
527                "name (local alias for install/enable/disable/remove)",
528                "scope (project|user, default project)",
529                "transport (stdio|http, for install override)",
530                "command (for install stdio override)",
531                "args (array, for install stdio override)",
532                "url (for install http override)",
533                "env (object, env vars for install)",
534            ],
535            side_effects: &[
536                "search/evaluate/info: network request to registry.modelcontextprotocol.io",
537                "install/enable/disable/remove: modifies .mcp.json on disk",
538            ],
539            examples: &[
540                "{\"operation\":\"search\",\"query\":\"filesystem\"}",
541                "{\"operation\":\"search\",\"query\":\"github\",\"limit\":10}",
542                "{\"operation\":\"evaluate\",\"server_id\":\"io.github.modelcontextprotocol/server-filesystem\"}",
543                "{\"operation\":\"install\",\"server_id\":\"io.github.modelcontextprotocol/server-filesystem\",\"name\":\"filesystem\",\"scope\":\"project\"}",
544                "{\"operation\":\"install\",\"server_id\":\"io.github.exa/exa\",\"env\":{\"EXA_API_KEY\":\"<key>\"}}",
545                "{\"operation\":\"list\",\"scope\":\"project\"}",
546                "{\"operation\":\"enable\",\"name\":\"filesystem\"}",
547                "{\"operation\":\"disable\",\"name\":\"filesystem\"}",
548                "{\"operation\":\"remove\",\"name\":\"filesystem\",\"scope\":\"project\"}",
549                "{\"operation\":\"info\",\"server_id\":\"io.github.modelcontextprotocol/server-github\"}",
550            ],
551        },
552        ToolDefinition {
553            name: "task",
554            summary: "Manage tasks for the current session: create, update details, update status, delete, list, inspect hierarchies",
555            description: "Create, update, list, and organize tasks and work items for the \
556                current session. Use this tool when the user wants to track progress on work, \
557                create a to-do list, mark items as done, build subtask hierarchies, or manage \
558                any kind of structured checklist or work breakdown. For `update`, provide \
559                `task_id` plus at least one of `name` or `description`. For `update_status`, ALWAYS \
560                provide both `task_id` and `status` in the same call; do not call `update_status` \
561                with only `task_id`, and do not use it just to confirm/preserve the current state. \
562                If no status changed, skip the task update and continue the real work.",
563            keywords: &[
564                "task",
565                "todo",
566                "work",
567                "track",
568                "checklist",
569                "reminder",
570                "subtask",
571                "status",
572                "create",
573                "list",
574                "progress",
575                "organize",
576                "mark",
577                "done",
578                "complete",
579                "in progress",
580                "workflow",
581                "breakdown",
582            ],
583            inputs: &[
584                "operation (create/update_status/update/delete/list/get_hierarchy)",
585                "task_id (for update_status/update/delete)",
586                "name (for create, optional for update)",
587                "description (optional for create/update)",
588                "status (for update_status)",
589                "parent_id (optional, for subtasks)",
590            ],
591            side_effects: &[
592                "Creates/modifies/deletes task files in .gestura/tasks/",
593                "Persists task state across sessions",
594            ],
595            examples: &[
596                "task create --name 'Implement feature' --description 'Add new API endpoint'",
597                "task update --task_id abc123 --name 'Refine onboarding task'",
598                "task update_status --task_id abc123 --status inprogress",
599                "{\"operation\":\"update_status\",\"task_id\":\"abc123\",\"status\":\"completed\"}",
600                "task delete --task_id abc123",
601                "task list",
602                "task create --name 'Write tests' --parent_id abc123",
603            ],
604        },
605        ToolDefinition {
606            name: "screenshot",
607            summary: "Capture screenshots of the screen or specific regions",
608            description: "Capture screenshots of the full screen, a specific display, or a \
609                defined region. Use this tool when the user wants to take a picture of their \
610                screen, capture a UI state, snap a region, or save a visual record. Returns \
611                a file path or inline base64 image. Requires screen recording permission.",
612            keywords: &[
613                "screenshot",
614                "capture",
615                "screen",
616                "image",
617                "photo",
618                "snap",
619                "grab",
620                "display",
621                "picture",
622                "png",
623                "jpg",
624                "snapshot",
625                "screengrab",
626                "take a picture",
627                "show me",
628            ],
629            inputs: &[
630                "output_format (optional: png/jpg)",
631                "output_path (optional; default artifact path)",
632                "return (optional: mode=path|inline_base64 + inline bounds)",
633                "region (optional: x,y,width,height)",
634                "display (optional: display number)",
635            ],
636            side_effects: &[
637                "Captures screen content (privacy-sensitive)",
638                "Creates image file on disk",
639                "May prompt for OS screen recording permission",
640            ],
641            examples: &[
642                "{\"output_path\":\"./screen.png\"}",
643                "{}",
644                "{\"output_format\":\"jpg\"}",
645                "{\"output_path\":\"./region.png\",\"region\":{\"x\":0,\"y\":0,\"width\":800,\"height\":600}}",
646            ],
647        },
648        ToolDefinition {
649            name: "screen_record",
650            summary: "Record screen video with start/stop controls",
651            description: "Record the screen as a video file (MP4 or MOV). Start a recording \
652                session with optional region and display selection, then stop it to produce a \
653                file. Use this tool when the user wants to create a screencast, demonstrate \
654                a workflow, record a tutorial, or capture video of any on-screen activity.",
655            keywords: &[
656                "record",
657                "recording",
658                "video",
659                "screen",
660                "capture",
661                "screencast",
662                "demonstration",
663                "demo",
664                "tutorial",
665                "mp4",
666                "mov",
667                "film",
668                "show",
669                "create a video",
670                "make a video",
671            ],
672            inputs: &[
673                "operation (start/stop)",
674                "output_format (optional for start: mp4/mov)",
675                "output_path (optional for start; default artifact path)",
676                "recording_id (for stop)",
677                "region (optional: x,y,width,height)",
678                "display (optional: display number)",
679            ],
680            side_effects: &[
681                "Captures screen content (privacy-sensitive)",
682                "Creates video file on disk",
683                "May prompt for OS screen recording permission",
684                "Spawns background recording process",
685            ],
686            examples: &[
687                "{\"operation\":\"start\",\"output_path\":\"./recording.mp4\"}",
688                "{\"operation\":\"start\"}",
689                "{\"operation\":\"start\",\"output_format\":\"mov\"}",
690                "{\"operation\":\"stop\",\"recording_id\":\"<recording_id>\"}",
691            ],
692        },
693        ToolDefinition {
694            name: "gui_control",
695            summary: "Drive the application GUI for self-demonstrations",
696            description: "Control the Gestura application GUI programmatically: toggle view \
697                modes, open or close the file explorer, navigate to the chat panel, or open \
698                configuration screens. Use this tool when the agent needs to demonstrate its \
699                own interface or navigate UI panels as part of a self-demonstration workflow.",
700            keywords: &[
701                "gui",
702                "interface",
703                "view",
704                "mode",
705                "explorer",
706                "editor",
707                "chat",
708                "navigate",
709                "toggle",
710                "window",
711                "ui",
712                "panel",
713                "open",
714                "close",
715                "config",
716                "settings",
717            ],
718            inputs: &[
719                "action (toggle_view_mode/open_explorer/close_explorer/open_chat/close_chat/navigate_config)",
720                "target (optional argument depending on action)",
721            ],
722            side_effects: &["Changes the physical view in the user's GUI"],
723            examples: &[
724                "{\"action\":\"toggle_view_mode\"}",
725                "{\"action\":\"open_explorer\"}",
726            ],
727        },
728    ];
729    TOOLS
730}
731
732/// Find a tool definition by name (case-insensitive).
733pub fn find_tool(name: &str) -> Option<&'static ToolDefinition> {
734    let name = name.trim().to_ascii_lowercase();
735    all_tools()
736        .iter()
737        .find(|t| t.name.eq_ignore_ascii_case(&name))
738}
739
740/// Render a compact table of built-in tools (static, no config needed).
741pub fn render_tools_overview() -> String {
742    let mut out = String::new();
743    out.push_str("**Built-in Tools:**\n\n");
744    for t in all_tools() {
745        out.push_str(&format!("• **{}** - {}\n", t.name, t.summary));
746    }
747    out.push_str("\nUse `/tools <name>` for details on a specific tool.");
748    out.push_str("\nUse `/capabilities` for full system status including MCP servers and devices.");
749    out
750}
751
752/// Render a detailed tool description.
753pub fn render_tool_detail(name: &str) -> Option<String> {
754    let t = find_tool(name)?;
755    let mut out = String::new();
756    out.push_str(&format!("**{}**\n\n", t.name));
757    out.push_str(&format!("{}\n\n", t.summary));
758
759    if !t.inputs.is_empty() {
760        out.push_str("**Inputs:**\n");
761        for i in t.inputs {
762            out.push_str(&format!("• {}\n", i));
763        }
764        out.push('\n');
765    }
766
767    if !t.side_effects.is_empty() {
768        out.push_str("**Side Effects:**\n");
769        for s in t.side_effects {
770            out.push_str(&format!("⚠ {}\n", s));
771        }
772        out.push('\n');
773    }
774
775    if !t.examples.is_empty() {
776        out.push_str("**Examples:**\n");
777        for e in t.examples {
778            out.push_str(&format!("```\n{}\n```\n", e));
779        }
780    }
781    Some(out)
782}
783
784/// Heuristic: decide whether a user message is asking for a tool inventory.
785pub fn looks_like_tools_question(input: &str) -> bool {
786    let s = input.trim().to_ascii_lowercase();
787    s == "tools"
788        || s == "tool"
789        || s == "/tools"
790        || s.contains("what tools")
791        || s.contains("available tools")
792        || s.contains("which tools")
793        || s.contains("list tools")
794        || s.contains("tool list")
795        || s.contains("show tools")
796        || s.contains("show me tools")
797}
798
799/// Heuristic: decide whether a user message is asking for full capabilities/config.
800pub fn looks_like_capabilities_question(input: &str) -> bool {
801    let s = input.trim().to_ascii_lowercase();
802    s == "/capabilities"
803        || s == "capabilities"
804        || s.contains("what can you do")
805        || s.contains("what do you have access to")
806        || s.contains("have access to")
807        || s.contains("what are your capabilities")
808        || s.contains("show capabilities")
809        || s.contains("mcp servers")
810        || s.contains("mcp tools")
811        || s.contains("configured tools")
812        || s.contains("system status")
813        || s.contains("current config")
814        || s.contains("device settings")
815}
816
817#[cfg(test)]
818mod tests {
819    use super::*;
820
821    #[test]
822    fn registry_contains_expected_tools() {
823        assert!(find_tool("file").is_some());
824        assert!(find_tool("shell").is_some());
825        assert!(find_tool("git").is_some());
826        assert!(find_tool("code").is_some());
827    }
828
829    #[test]
830    fn task_tool_description_mentions_update_requirements() {
831        let task = find_tool("task").expect("task tool should exist");
832        assert!(task.summary.contains("update details"));
833        assert!(task.description.contains(
834            "For `update`, provide `task_id` plus at least one of `name` or `description`"
835        ));
836        assert!(task.examples.iter().any(|example| {
837            example.contains("task update --task_id abc123 --name 'Refine onboarding task'")
838        }));
839        assert!(
840            task.examples
841                .iter()
842                .any(|example| example.contains("task delete --task_id abc123"))
843        );
844    }
845
846    #[test]
847    fn code_tool_helpers_cover_legacy_and_split_names() {
848        assert!(is_code_tool_name("code"));
849        assert!(is_code_tool_name("code_edit_files"));
850        assert!(is_code_tool_name("code_read_files"));
851        assert!(!is_code_tool_name("shell"));
852    }
853
854    #[test]
855    fn looks_like_tools_question_matches_common_phrases() {
856        assert!(looks_like_tools_question("what tools do you have?"));
857        assert!(looks_like_tools_question("list tools"));
858        assert!(looks_like_tools_question("/tools"));
859        assert!(!looks_like_tools_question("tell me a joke"));
860    }
861}