1#[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
74pub fn register_builtin_tools(registry: &mut ToolRegistry, workspace: PathBuf) {
76 register_builtin_tools_with_progress(registry, workspace, None);
77}
78
79pub 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 Arc::new(web::WebSearchTool::new()),
107 Arc::new(web::WebFetchTool::new()),
108 Arc::new(web::DocumentReadTool::new(workspace.clone())),
109 Arc::new(smart_edit::SmartEditTool::new(workspace.clone())),
111 Arc::new(codebase_search::CodebaseSearchTool::new(workspace.clone())),
113 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 Arc::new(pdf_generate::PdfGenerateTool::new(workspace.clone())),
120 Arc::new(pomodoro::PomodoroTool::new(workspace.clone())),
122 Arc::new(inbox::InboxTool::new(workspace.clone())),
123 Arc::new(relationships::RelationshipsTool::new(workspace.clone())),
124 Arc::new(life_planner::LifePlannerTool::new(workspace.clone())),
126 Arc::new(finance::FinanceTool::new(workspace.clone())),
128 Arc::new(flashcards::FlashcardsTool::new(workspace.clone())),
129 Arc::new(travel::TravelTool::new(workspace.clone())),
130 Arc::new(career_intel::CareerIntelTool::new(workspace.clone())),
132 Arc::new(arxiv::ArxivResearchTool::new(workspace.clone())),
134 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 Arc::new(slack::SlackTool::new(workspace.clone())),
151 ];
152
153 #[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 #[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
197pub 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 #[cfg(target_os = "macos")]
226 assert_eq!(registry.len(), 67);
227 #[cfg(not(target_os = "macos"))]
228 assert_eq!(registry.len(), 40);
229
230 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 #[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 #[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 assert!(names.contains(&"arxiv_research".to_string()));
284
285 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 assert!(names.contains(&"slack".to_string()));
299
300 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 assert!(
324 def.parameters.is_object(),
325 "Parameters should be a JSON object for tool '{}'",
326 def.name
327 );
328 }
329 }
330}