Skip to main content

hematite/agent/
find_files.rs

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