#[cfg(not(target_arch = "wasm32"))]
use super::ApplyPatchTool;
use super::datetime::DateTimeTool;
use super::wait::WaitTool;
use crate::builtin::BuiltinTool;
use meerkat_core::wait_interrupt::WaitInterruptReceiver;
#[cfg(not(target_arch = "wasm32"))]
use std::path::PathBuf;
#[derive(Debug)]
pub struct UtilityToolSet {
pub wait: WaitTool,
pub datetime: DateTimeTool,
#[cfg(not(target_arch = "wasm32"))]
pub apply_patch: ApplyPatchTool,
}
impl UtilityToolSet {
#[cfg(target_arch = "wasm32")]
pub fn new() -> Self {
Self {
wait: WaitTool::new(),
datetime: DateTimeTool::new(),
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn new(project_root: PathBuf) -> Self {
Self {
wait: WaitTool::new(),
datetime: DateTimeTool::new(),
apply_patch: ApplyPatchTool::new(project_root),
}
}
#[cfg(target_arch = "wasm32")]
pub fn with_interrupt(interrupt_rx: WaitInterruptReceiver) -> Self {
Self {
wait: WaitTool::with_interrupt(interrupt_rx),
datetime: DateTimeTool::new(),
}
}
#[cfg(not(target_arch = "wasm32"))]
pub fn with_interrupt(interrupt_rx: WaitInterruptReceiver, project_root: PathBuf) -> Self {
Self {
wait: WaitTool::with_interrupt(interrupt_rx),
datetime: DateTimeTool::new(),
apply_patch: ApplyPatchTool::new(project_root),
}
}
#[cfg(target_arch = "wasm32")]
pub fn tools(&self) -> Vec<&dyn BuiltinTool> {
vec![
&self.wait as &dyn BuiltinTool,
&self.datetime as &dyn BuiltinTool,
]
}
#[cfg(not(target_arch = "wasm32"))]
pub fn tools(&self) -> Vec<&dyn BuiltinTool> {
vec![
&self.wait as &dyn BuiltinTool,
&self.datetime as &dyn BuiltinTool,
&self.apply_patch as &dyn BuiltinTool,
]
}
#[cfg(target_arch = "wasm32")]
pub fn tool_names() -> Vec<&'static str> {
vec!["wait", "datetime"]
}
#[cfg(not(target_arch = "wasm32"))]
pub fn tool_names() -> Vec<&'static str> {
vec!["wait", "datetime", "apply_patch"]
}
pub fn usage_instructions() -> &'static str {
r"# Utility Tools
You have access to utility tools for timing and coordination.
## Available Tools
- `wait` - Pause execution for a specified number of seconds (max 60)
- `datetime` - Get the current date and time
- `apply_patch` - Apply structured file edits inside the project root
## Using the Wait Tool
The `wait` tool is essential for async operations. Use it to:
- **Wait between status checks**: After starting async delegated work, call `wait(15)` before checking status
- **Rate limiting**: When making repeated API calls, wait between them
- **Coordination**: When timing matters, use wait to synchronize operations
Example pattern for delegated work:
1. Start the async work or branch
2. Do other useful work
3. `wait(20)` - wait 20 seconds
4. Check whether it is done
5. If not done: `wait(15)` then check again
**DO NOT** poll status in a tight loop without waiting. Always use `wait` between status checks.
## Using the DateTime Tool
Use `datetime` when you need to:
- Know the current time for scheduling or logging
- Calculate durations or deadlines
- Include timestamps in outputs or task metadata"
}
}
impl Default for UtilityToolSet {
#[cfg(target_arch = "wasm32")]
fn default() -> Self {
Self::new()
}
#[cfg(not(target_arch = "wasm32"))]
fn default() -> Self {
Self::new(std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")))
}
}
#[cfg(test)]
#[allow(clippy::unwrap_used, clippy::expect_used)]
mod tests {
use super::*;
#[test]
fn test_utility_tool_set_creation() {
#[cfg(target_arch = "wasm32")]
let tool_set = UtilityToolSet::new();
#[cfg(not(target_arch = "wasm32"))]
let tool_set = UtilityToolSet::new(PathBuf::from("/tmp/test"));
assert_eq!(tool_set.wait.name(), "wait");
assert_eq!(tool_set.datetime.name(), "datetime");
#[cfg(not(target_arch = "wasm32"))]
assert_eq!(tool_set.apply_patch.name(), "apply_patch");
}
#[test]
fn test_utility_tool_set_tools() {
#[cfg(target_arch = "wasm32")]
let tool_set = UtilityToolSet::new();
#[cfg(not(target_arch = "wasm32"))]
let tool_set = UtilityToolSet::new(PathBuf::from("/tmp/test"));
let tools = tool_set.tools();
#[cfg(target_arch = "wasm32")]
assert_eq!(tools.len(), 2);
#[cfg(not(target_arch = "wasm32"))]
assert_eq!(tools.len(), 3);
let names: Vec<_> = tools.iter().map(|t| t.name()).collect();
assert!(names.contains(&"wait"));
assert!(names.contains(&"datetime"));
#[cfg(not(target_arch = "wasm32"))]
assert!(names.contains(&"apply_patch"));
}
#[test]
fn test_utility_tool_set_tool_names() {
let names = UtilityToolSet::tool_names();
#[cfg(target_arch = "wasm32")]
assert_eq!(names.len(), 2);
#[cfg(not(target_arch = "wasm32"))]
assert_eq!(names.len(), 3);
assert!(names.contains(&"wait"));
assert!(names.contains(&"datetime"));
#[cfg(not(target_arch = "wasm32"))]
assert!(names.contains(&"apply_patch"));
}
#[test]
fn test_utility_tool_set_all_enabled_by_default() {
#[cfg(target_arch = "wasm32")]
let tool_set = UtilityToolSet::new();
#[cfg(not(target_arch = "wasm32"))]
let tool_set = UtilityToolSet::new(PathBuf::from("/tmp/test"));
let tools = tool_set.tools();
for tool in tools {
assert!(
tool.default_enabled(),
"Tool {} should be enabled by default",
tool.name()
);
}
}
#[test]
fn test_utility_tool_set_usage_instructions() {
let instructions = UtilityToolSet::usage_instructions();
assert!(instructions.contains("wait"));
assert!(instructions.contains("datetime"));
assert!(instructions.contains("DO NOT"));
}
#[test]
fn test_utility_tool_set_default() {
let tool_set = UtilityToolSet::default();
#[cfg(target_arch = "wasm32")]
assert_eq!(tool_set.tools().len(), 2);
#[cfg(not(target_arch = "wasm32"))]
assert_eq!(tool_set.tools().len(), 3);
}
}