Skip to main content

rustant_tools/
lib.rs

1//! # Rustant Tools
2//!
3//! Built-in tool implementations for the Rustant agent.
4//! Provides file operations, search, git integration, and shell execution.
5
6#[cfg(target_os = "macos")]
7pub mod accessibility;
8pub mod arxiv;
9pub mod arxiv_api;
10pub mod browser;
11pub mod canvas;
12pub mod career_intel;
13pub mod checkpoint;
14pub mod code_intelligence;
15pub mod codebase_search;
16pub mod compress;
17pub mod content_engine;
18pub mod experiment_tracker;
19
20#[cfg(target_os = "macos")]
21pub mod contacts;
22#[cfg(target_os = "macos")]
23pub mod daily_briefing;
24pub mod file;
25pub mod file_organizer;
26pub mod finance;
27pub mod flashcards;
28pub mod git;
29#[cfg(target_os = "macos")]
30pub mod gui_scripting;
31#[cfg(target_os = "macos")]
32pub mod homekit;
33pub mod http_api;
34pub mod imessage;
35pub mod inbox;
36pub mod knowledge_graph;
37pub mod life_planner;
38pub mod lsp;
39#[cfg(target_os = "macos")]
40pub mod macos;
41#[cfg(target_os = "macos")]
42pub mod meeting;
43pub mod pdf_generate;
44#[cfg(target_os = "macos")]
45pub mod photos;
46pub mod pomodoro;
47pub mod privacy_manager;
48pub mod registry;
49pub mod relationships;
50#[cfg(target_os = "macos")]
51pub mod safari;
52pub mod sandbox;
53#[cfg(target_os = "macos")]
54pub mod screen_analyze;
55pub mod self_improvement;
56pub mod shell;
57pub mod skill_tracker;
58pub mod slack;
59pub mod smart_edit;
60pub mod system_monitor;
61pub mod template;
62pub mod travel;
63pub mod utils;
64#[cfg(target_os = "macos")]
65pub mod voice_tool;
66pub mod web;
67
68use registry::{Tool, ToolRegistry};
69use rustant_core::types::ProgressUpdate;
70use std::path::PathBuf;
71use std::sync::Arc;
72use tokio::sync::mpsc;
73
74/// Register all built-in tools with the given workspace path.
75pub fn register_builtin_tools(registry: &mut ToolRegistry, workspace: PathBuf) {
76    register_builtin_tools_with_progress(registry, workspace, None);
77}
78
79/// Register all built-in tools, optionally with a progress channel for streaming output.
80pub fn register_builtin_tools_with_progress(
81    registry: &mut ToolRegistry,
82    workspace: PathBuf,
83    progress_tx: Option<mpsc::UnboundedSender<ProgressUpdate>>,
84) {
85    let shell_tool: Arc<dyn Tool> = if let Some(tx) = progress_tx {
86        Arc::new(shell::ShellExecTool::with_progress(workspace.clone(), tx))
87    } else {
88        Arc::new(shell::ShellExecTool::new(workspace.clone()))
89    };
90
91    #[allow(unused_mut)]
92    let mut tools: Vec<Arc<dyn Tool>> = vec![
93        Arc::new(file::FileReadTool::new(workspace.clone())),
94        Arc::new(file::FileListTool::new(workspace.clone())),
95        Arc::new(file::FileSearchTool::new(workspace.clone())),
96        Arc::new(file::FileWriteTool::new(workspace.clone())),
97        Arc::new(file::FilePatchTool::new(workspace.clone())),
98        Arc::new(git::GitStatusTool::new(workspace.clone())),
99        Arc::new(git::GitDiffTool::new(workspace.clone())),
100        Arc::new(git::GitCommitTool::new(workspace.clone())),
101        shell_tool,
102        Arc::new(utils::EchoTool),
103        Arc::new(utils::DateTimeTool),
104        Arc::new(utils::CalculatorTool),
105        // Web tools — search, fetch, and document reading
106        Arc::new(web::WebSearchTool::new()),
107        Arc::new(web::WebFetchTool::new()),
108        Arc::new(web::DocumentReadTool::new(workspace.clone())),
109        // Smart editing with fuzzy matching and auto-checkpoint
110        Arc::new(smart_edit::SmartEditTool::new(workspace.clone())),
111        // Codebase search with auto-indexing
112        Arc::new(codebase_search::CodebaseSearchTool::new(workspace.clone())),
113        // Cross-platform utility tools
114        Arc::new(file_organizer::FileOrganizerTool::new(workspace.clone())),
115        Arc::new(compress::CompressTool::new(workspace.clone())),
116        Arc::new(http_api::HttpApiTool::new()),
117        Arc::new(template::TemplateTool::new(workspace.clone())),
118        // PDF generation
119        Arc::new(pdf_generate::PdfGenerateTool::new(workspace.clone())),
120        // Personal productivity tools
121        Arc::new(pomodoro::PomodoroTool::new(workspace.clone())),
122        Arc::new(inbox::InboxTool::new(workspace.clone())),
123        Arc::new(relationships::RelationshipsTool::new(workspace.clone())),
124        // Life planner — energy-aware scheduling, deadlines, habits
125        Arc::new(life_planner::LifePlannerTool::new(workspace.clone())),
126        // Advanced personal tools
127        Arc::new(finance::FinanceTool::new(workspace.clone())),
128        Arc::new(flashcards::FlashcardsTool::new(workspace.clone())),
129        Arc::new(travel::TravelTool::new(workspace.clone())),
130        // Career intelligence
131        Arc::new(career_intel::CareerIntelTool::new(workspace.clone())),
132        // Research tools
133        Arc::new(arxiv::ArxivResearchTool::new(workspace.clone())),
134        // Cognitive extension tools
135        Arc::new(knowledge_graph::KnowledgeGraphTool::new(workspace.clone())),
136        Arc::new(experiment_tracker::ExperimentTrackerTool::new(
137            workspace.clone(),
138        )),
139        Arc::new(code_intelligence::CodeIntelligenceTool::new(
140            workspace.clone(),
141        )),
142        Arc::new(content_engine::ContentEngineTool::new(workspace.clone())),
143        Arc::new(skill_tracker::SkillTrackerTool::new(workspace.clone())),
144        Arc::new(system_monitor::SystemMonitorTool::new(workspace.clone())),
145        Arc::new(privacy_manager::PrivacyManagerTool::new(workspace.clone())),
146        Arc::new(self_improvement::SelfImprovementTool::new(
147            workspace.clone(),
148        )),
149        // Slack tool — cross-platform, uses Slack Bot Token API
150        Arc::new(slack::SlackTool::new(workspace.clone())),
151    ];
152
153    // iMessage tools — macOS only
154    #[cfg(target_os = "macos")]
155    {
156        tools.push(Arc::new(imessage::IMessageContactsTool));
157        tools.push(Arc::new(imessage::IMessageSendTool));
158        tools.push(Arc::new(imessage::IMessageReadTool));
159    }
160
161    // macOS native tools — Calendar, Reminders, Notes, App Control, etc.
162    #[cfg(target_os = "macos")]
163    {
164        tools.push(Arc::new(macos::MacosCalendarTool));
165        tools.push(Arc::new(macos::MacosRemindersTool));
166        tools.push(Arc::new(macos::MacosNotesTool));
167        tools.push(Arc::new(macos::MacosAppControlTool));
168        tools.push(Arc::new(macos::MacosNotificationTool));
169        tools.push(Arc::new(macos::MacosClipboardTool));
170        tools.push(Arc::new(macos::MacosScreenshotTool));
171        tools.push(Arc::new(macos::MacosSystemInfoTool));
172        tools.push(Arc::new(macos::MacosSpotlightTool));
173        tools.push(Arc::new(macos::MacosFinderTool));
174        tools.push(Arc::new(macos::MacosFocusModeTool));
175        tools.push(Arc::new(macos::MacosMailTool));
176        tools.push(Arc::new(macos::MacosMusicTool));
177        tools.push(Arc::new(macos::MacosShortcutsTool));
178        tools.push(Arc::new(meeting::MacosMeetingRecorderTool));
179        tools.push(Arc::new(daily_briefing::MacosDailyBriefingTool));
180        tools.push(Arc::new(gui_scripting::MacosGuiScriptingTool));
181        tools.push(Arc::new(accessibility::MacosAccessibilityTool));
182        tools.push(Arc::new(screen_analyze::MacosScreenAnalyzeTool));
183        tools.push(Arc::new(contacts::MacosContactsTool));
184        tools.push(Arc::new(safari::MacosSafariTool));
185        tools.push(Arc::new(voice_tool::MacosSayTool::new()));
186        tools.push(Arc::new(photos::MacosPhotosTool::new()));
187        tools.push(Arc::new(homekit::HomeKitTool::new()));
188    }
189
190    for tool in tools {
191        if let Err(e) = registry.register(tool) {
192            tracing::warn!("Failed to register tool: {}", e);
193        }
194    }
195}
196
197/// Register all LSP tools backed by a shared [`lsp::LspManager`].
198///
199/// The LSP tools provide code intelligence capabilities (hover, definition,
200/// references, diagnostics, completions, rename, format) by connecting to
201/// language servers installed on the system.
202pub fn register_lsp_tools(registry: &mut ToolRegistry, workspace: PathBuf) {
203    let manager = Arc::new(lsp::LspManager::new(workspace));
204    let lsp_tools = lsp::create_lsp_tools(manager);
205
206    for tool in lsp_tools {
207        if let Err(e) = registry.register(tool) {
208            tracing::warn!("Failed to register LSP tool: {}", e);
209        }
210    }
211}
212
213#[cfg(test)]
214mod tests {
215    use super::*;
216    use tempfile::TempDir;
217
218    #[test]
219    fn test_register_all_builtin_tools() {
220        let dir = TempDir::new().unwrap();
221        let mut registry = ToolRegistry::new();
222        register_builtin_tools(&mut registry, dir.path().to_path_buf());
223
224        // 40 base + 3 iMessage + 24 macOS native = 67 on macOS
225        #[cfg(target_os = "macos")]
226        assert_eq!(registry.len(), 67);
227        #[cfg(not(target_os = "macos"))]
228        assert_eq!(registry.len(), 40);
229
230        // Verify all expected tools are registered
231        let names = registry.list_names();
232        assert!(names.contains(&"file_read".to_string()));
233        assert!(names.contains(&"file_list".to_string()));
234        assert!(names.contains(&"file_search".to_string()));
235        assert!(names.contains(&"file_write".to_string()));
236        assert!(names.contains(&"file_patch".to_string()));
237        assert!(names.contains(&"git_status".to_string()));
238        assert!(names.contains(&"git_diff".to_string()));
239        assert!(names.contains(&"git_commit".to_string()));
240        assert!(names.contains(&"shell_exec".to_string()));
241        assert!(names.contains(&"echo".to_string()));
242        assert!(names.contains(&"datetime".to_string()));
243        assert!(names.contains(&"calculator".to_string()));
244
245        // iMessage tools on macOS
246        #[cfg(target_os = "macos")]
247        {
248            assert!(names.contains(&"imessage_contacts".to_string()));
249            assert!(names.contains(&"imessage_send".to_string()));
250            assert!(names.contains(&"imessage_read".to_string()));
251        }
252
253        // macOS native tools
254        #[cfg(target_os = "macos")]
255        {
256            assert!(names.contains(&"macos_calendar".to_string()));
257            assert!(names.contains(&"macos_reminders".to_string()));
258            assert!(names.contains(&"macos_notes".to_string()));
259            assert!(names.contains(&"macos_app_control".to_string()));
260            assert!(names.contains(&"macos_notification".to_string()));
261            assert!(names.contains(&"macos_clipboard".to_string()));
262            assert!(names.contains(&"macos_screenshot".to_string()));
263            assert!(names.contains(&"macos_system_info".to_string()));
264            assert!(names.contains(&"macos_spotlight".to_string()));
265            assert!(names.contains(&"macos_finder".to_string()));
266            assert!(names.contains(&"macos_focus_mode".to_string()));
267            assert!(names.contains(&"macos_mail".to_string()));
268            assert!(names.contains(&"macos_music".to_string()));
269            assert!(names.contains(&"macos_shortcuts".to_string()));
270            assert!(names.contains(&"macos_meeting_recorder".to_string()));
271            assert!(names.contains(&"macos_daily_briefing".to_string()));
272            assert!(names.contains(&"macos_gui_scripting".to_string()));
273            assert!(names.contains(&"macos_accessibility".to_string()));
274            assert!(names.contains(&"macos_screen_analyze".to_string()));
275            assert!(names.contains(&"macos_contacts".to_string()));
276            assert!(names.contains(&"macos_safari".to_string()));
277            assert!(names.contains(&"macos_say".to_string()));
278            assert!(names.contains(&"macos_photos".to_string()));
279            assert!(names.contains(&"homekit".to_string()));
280        }
281
282        // Research tools
283        assert!(names.contains(&"arxiv_research".to_string()));
284
285        // Cognitive extension tools
286        assert!(names.contains(&"knowledge_graph".to_string()));
287        assert!(names.contains(&"experiment_tracker".to_string()));
288        assert!(names.contains(&"code_intelligence".to_string()));
289        assert!(names.contains(&"content_engine".to_string()));
290        assert!(names.contains(&"skill_tracker".to_string()));
291        assert!(names.contains(&"career_intel".to_string()));
292        assert!(names.contains(&"system_monitor".to_string()));
293        assert!(names.contains(&"life_planner".to_string()));
294        assert!(names.contains(&"privacy_manager".to_string()));
295        assert!(names.contains(&"self_improvement".to_string()));
296
297        // Slack tool
298        assert!(names.contains(&"slack".to_string()));
299
300        // Cross-platform productivity tools
301        assert!(names.contains(&"pomodoro".to_string()));
302        assert!(names.contains(&"inbox".to_string()));
303        assert!(names.contains(&"relationships".to_string()));
304        assert!(names.contains(&"finance".to_string()));
305        assert!(names.contains(&"flashcards".to_string()));
306        assert!(names.contains(&"travel".to_string()));
307    }
308
309    #[test]
310    fn test_tool_definitions_are_valid_json() {
311        let dir = TempDir::new().unwrap();
312        let mut registry = ToolRegistry::new();
313        register_builtin_tools(&mut registry, dir.path().to_path_buf());
314
315        let definitions = registry.list_definitions();
316        for def in &definitions {
317            assert!(!def.name.is_empty(), "Tool name should not be empty");
318            assert!(
319                !def.description.is_empty(),
320                "Tool description should not be empty"
321            );
322            // Parameters should be a valid JSON object
323            assert!(
324                def.parameters.is_object(),
325                "Parameters should be a JSON object for tool '{}'",
326                def.name
327            );
328        }
329    }
330}