hematite/agent/
find_files.rs1use crate::agent::fuzzy::fuzzy_match;
2use serde_json::Value;
3use std::fmt::Write as _;
4use walkdir::WalkDir;
5
6pub async fn find_files_fuzzy(args: &Value) -> Result<String, String> {
9 let query = args
10 .get("query")
11 .and_then(|v| v.as_str())
12 .ok_or_else(|| "Missing required argument: 'query'".to_string())?;
13
14 let max_results = args.get("limit").and_then(|v| v.as_u64()).unwrap_or(20) as usize;
15 let workspace_root = crate::tools::file_ops::workspace_root();
16
17 let mut matches = Vec::new();
18
19 for entry in WalkDir::new(&workspace_root)
20 .into_iter()
21 .filter_entry(|e| {
22 let name = e.file_name().to_string_lossy();
23 !name.starts_with('.') && name != "target" && name != "node_modules"
24 })
25 .filter_map(|e| e.ok())
26 {
27 if entry.file_type().is_file() {
28 let rel_path = entry
29 .path()
30 .strip_prefix(&workspace_root)
31 .unwrap_or(entry.path());
32 let path_str = rel_path.to_string_lossy();
33
34 if let Some((_, score)) = fuzzy_match(&path_str, query) {
35 matches.push((path_str.into_owned(), score));
36 }
37 }
38 }
39
40 matches.sort_by_key(|m| m.1);
42 matches.truncate(max_results);
43
44 if matches.is_empty() {
45 return Ok(format!("No files found matching '{}'", query));
46 }
47
48 let mut output = format!("Found {} matches for '{}':\n", matches.len(), query);
49 for (path, score) in matches {
50 let confidence = if score <= -100 {
51 "High"
52 } else if score <= 5 {
53 "Moderate"
54 } else {
55 "Low"
56 };
57 let _ = writeln!(output, "- {} (Confidence: {})", path, confidence);
58 }
59
60 Ok(output)
61}