use anyhow::{Context, Result};
use std::path::PathBuf;
use crate::cache::CacheManager;
use crate::output;
pub(super) fn handle_stats(as_json: bool, pretty_json: bool) -> Result<()> {
log::info!("Showing index statistics");
let cache = CacheManager::new(".");
if !cache.exists() {
anyhow::bail!(
"No index found in current directory.\n\
\n\
Run 'rfx index' to build the code search index first.\n\
This will scan all files in the current directory and create a .reflex/ cache.\n\
\n\
Example:\n\
$ rfx index # Index current directory\n\
$ rfx stats # Show index statistics"
);
}
let stats = cache.stats()?;
if as_json {
let json_output = if pretty_json {
serde_json::to_string_pretty(&stats)?
} else {
serde_json::to_string(&stats)?
};
println!("{}", json_output);
} else {
println!("Reflex Index Statistics");
println!("=======================");
let root = std::env::current_dir()?;
if crate::git::is_git_repo(&root) {
match crate::git::get_git_state(&root) {
Ok(git_state) => {
let dirty_indicator = if git_state.dirty { " (uncommitted changes)" } else { " (clean)" };
println!("Branch: {}@{}{}",
git_state.branch,
&git_state.commit[..7],
dirty_indicator);
match cache.get_branch_info(&git_state.branch) {
Ok(branch_info) => {
if branch_info.commit_sha != git_state.commit {
println!(" ⚠️ Index commit mismatch (indexed: {})",
&branch_info.commit_sha[..7]);
}
if git_state.dirty && !branch_info.is_dirty {
println!(" ⚠️ Uncommitted changes not indexed");
}
}
Err(_) => {
println!(" ⚠️ Branch not indexed");
}
}
}
Err(e) => {
log::warn!("Failed to get git state: {}", e);
}
}
} else {
println!("Branch: (None)");
}
println!("Files indexed: {}", stats.total_files);
println!("Index size: {} bytes", stats.index_size_bytes);
println!("Last updated: {}", stats.last_updated);
if !stats.files_by_language.is_empty() {
println!("\nFiles by language:");
let mut lang_vec: Vec<_> = stats.files_by_language.iter().collect();
lang_vec.sort_by(|a, b| b.1.cmp(a.1).then(a.0.cmp(b.0)));
let max_lang_len = lang_vec.iter().map(|(lang, _)| lang.len()).max().unwrap_or(8);
let lang_width = max_lang_len.max(8);
println!(" {:<width$} Files Lines", "Language", width = lang_width);
println!(" {} ----- -------", "-".repeat(lang_width));
for (language, file_count) in lang_vec {
let line_count = stats.lines_by_language.get(language).copied().unwrap_or(0);
println!(" {:<width$} {:5} {:7}",
language, file_count, line_count,
width = lang_width);
}
}
}
Ok(())
}
pub(super) fn handle_clear(skip_confirm: bool) -> Result<()> {
let cache = CacheManager::new(".");
if !cache.exists() {
println!("No cache to clear.");
return Ok(());
}
if !skip_confirm {
println!("This will delete the local Reflex cache at: {:?}", cache.path());
print!("Are you sure? [y/N] ");
use std::io::{self, Write};
io::stdout().flush()?;
let mut input = String::new();
io::stdin().read_line(&mut input)?;
if !input.trim().eq_ignore_ascii_case("y") {
println!("Cancelled.");
return Ok(());
}
}
cache.clear()?;
println!("Cache cleared successfully.");
Ok(())
}
pub(super) fn handle_list_files(as_json: bool, pretty_json: bool) -> Result<()> {
let cache = CacheManager::new(".");
if !cache.exists() {
anyhow::bail!(
"No index found in current directory.\n\
\n\
Run 'rfx index' to build the code search index first.\n\
This will scan all files in the current directory and create a .reflex/ cache.\n\
\n\
Example:\n\
$ rfx index # Index current directory\n\
$ rfx list-files # List indexed files"
);
}
let files = cache.list_files()?;
if as_json {
let json_output = if pretty_json {
serde_json::to_string_pretty(&files)?
} else {
serde_json::to_string(&files)?
};
println!("{}", json_output);
} else if files.is_empty() {
println!("No files indexed yet.");
} else {
println!("Indexed Files ({} total):", files.len());
println!();
for file in files {
println!(" {} ({})",
file.path,
file.language);
}
}
Ok(())
}
pub(super) fn handle_mcp() -> Result<()> {
log::info!("Starting MCP server");
crate::mcp::run_mcp_server()
}
pub(super) fn handle_context(
structure: bool,
path: Option<String>,
file_types: bool,
project_type: bool,
framework: bool,
entry_points: bool,
test_layout: bool,
config_files: bool,
depth: usize,
json: bool,
) -> Result<()> {
let cache = CacheManager::new(".");
if !cache.exists() {
anyhow::bail!(
"No index found in current directory.\n\
\n\
Run 'rfx index' to build the code search index first.\n\
\n\
Example:\n\
$ rfx index # Index current directory\n\
$ rfx context # Generate context"
);
}
let opts = crate::context::ContextOptions {
structure,
path,
file_types,
project_type,
framework,
entry_points,
test_layout,
config_files,
depth,
json,
};
let context_output = crate::context::generate_context(&cache, &opts)
.context("Failed to generate codebase context")?;
println!("{}", context_output);
Ok(())
}