(&self, args: &serde_json::Value)
| 942 | } |
| 943 | |
| 944 | fn exec_code_search(&self, args: &serde_json::Value) -> Result<String, String> { |
| 945 | let pattern = args["pattern"] |
| 946 | .as_str() |
| 947 | .ok_or("missing required parameter: pattern")?; |
| 948 | |
| 949 | let opts = crate::content_search::ContentSearchOptions { |
| 950 | path_filter: args["path_filter"].as_str().map(String::from), |
| 951 | file_type: args["file_type"].as_str().map(String::from), |
| 952 | exclude_type: None, |
| 953 | max_results: Some(args["max_results"].as_u64().unwrap_or(30) as usize), |
| 954 | case_insensitive: false, |
| 955 | }; |
| 956 | |
| 957 | match crate::content_search::search_content(self.root, pattern, opts) { |
| 958 | Ok(result) => { |
| 959 | if result.matches.is_empty() { |
| 960 | Ok("No matches found.".into()) |
| 961 | } else { |
| 962 | // Build a compact response with matches + metadata |
| 963 | let response = json!({ |
| 964 | "matches": result.matches, |
| 965 | "total_matches": result.total_matches, |
| 966 | "showing": result.matches.len(), |
| 967 | "dir_facets": result.dir_facets, |
| 968 | }); |
| 969 | serde_json::to_string_pretty(&response) |
| 970 | .map_err(|e| format!("serialize error: {e}")) |
| 971 | } |
| 972 | } |
| 973 | Err(crate::content_search::ContentSearchError::IndexNotFound) => { |
| 974 | Err("Content index not built. Run 'atomic vault query enrich' first.".into()) |
| 975 | } |
| 976 | Err(e) => Err(format!("code_search failed: {e}")), |
| 977 | } |
| 978 | } |
| 979 | |
| 980 | fn exec_vault_read(&self, args: &serde_json::Value) -> Result<String, String> { |
| 981 | let path = args["path"] |
no test coverage detected