Skip to main content

hematite/agent/
find_files.rs

1use crate::agent::fuzzy::fuzzy_match;
2use serde_json::Value;
3use std::fmt::Write as _;
4use walkdir::WalkDir;
5
6/// Precision File Discovery using Fuzzy Matching.
7/// Replaces broad grep loops with surgical name-based sightings.
8pub 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    // Sort by score (lower is better)
41    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}