(
&self,
args: serde_json::Value,
ctx: ToolContext,
)
| 51 | } |
| 52 | |
| 53 | async fn execute( |
| 54 | &self, |
| 55 | args: serde_json::Value, |
| 56 | ctx: ToolContext, |
| 57 | ) -> Result<ToolResult, ToolError> { |
| 58 | let pattern: String = args["pattern"] |
| 59 | .as_str() |
| 60 | .ok_or_else(|| ToolError::InvalidArguments("pattern is required".into()))? |
| 61 | .to_string(); |
| 62 | |
| 63 | let search_path: String = args["path"] |
| 64 | .as_str() |
| 65 | .map(|s| s.to_string()) |
| 66 | .unwrap_or_else(|| ctx.directory.clone()); |
| 67 | |
| 68 | let base_dir = if search_path.is_empty() { |
| 69 | &self.directory |
| 70 | } else { |
| 71 | Path::new(&search_path) |
| 72 | }; |
| 73 | |
| 74 | let base_dir_str = base_dir.to_string_lossy().to_string(); |
| 75 | |
| 76 | if ctx.is_external_path(&base_dir_str) { |
| 77 | ctx.ask_permission( |
| 78 | crate::PermissionRequest::new("external_directory") |
| 79 | .with_pattern(format!("{}/*", base_dir_str)) |
| 80 | .with_metadata("path", serde_json::json!(&base_dir_str)), |
| 81 | ) |
| 82 | .await?; |
| 83 | } |
| 84 | |
| 85 | ctx.ask_permission( |
| 86 | crate::PermissionRequest::new("glob") |
| 87 | .with_pattern(&pattern) |
| 88 | .with_metadata("path", serde_json::json!(&base_dir_str)) |
| 89 | .always_allow(), |
| 90 | ) |
| 91 | .await?; |
| 92 | |
| 93 | let glob_pattern = glob::Pattern::new(&pattern) |
| 94 | .map_err(|e| ToolError::InvalidArguments(format!("Invalid glob pattern: {}", e)))?; |
| 95 | |
| 96 | let mut files_with_mtime: Vec<(String, SystemTime)> = Vec::new(); |
| 97 | |
| 98 | for entry in WalkDir::new(base_dir) |
| 99 | .follow_links(true) |
| 100 | .into_iter() |
| 101 | .filter_map(|e| e.ok()) |
| 102 | { |
| 103 | let path = entry.path(); |
| 104 | if !path.is_file() { |
| 105 | continue; |
| 106 | } |
| 107 | |
| 108 | let rel_path = path.strip_prefix(base_dir).unwrap_or(path); |
| 109 | let rel_str = rel_path.to_string_lossy(); |
| 110 |
nothing calls this directly
no test coverage detected