use anyhow::{Context, Result};
use colored::Colorize;
use std::fs;
use std::path::PathBuf;
use super::{uninstall_claude_code, uninstall_codex, uninstall_opencode};
fn get_colgrep_data_dir() -> Result<PathBuf> {
let data_dir = dirs::data_dir().context("Could not determine data directory")?;
Ok(data_dir.join("colgrep"))
}
fn get_colgrep_cache_dir() -> Result<PathBuf> {
let home = dirs::home_dir().context("Could not determine home directory")?;
Ok(home.join(".cache").join("colgrep"))
}
fn get_hf_cache_dir() -> Result<PathBuf> {
let home = dirs::home_dir().context("Could not determine home directory")?;
Ok(home.join(".cache").join("huggingface").join("hub"))
}
pub fn uninstall_all() -> Result<()> {
println!();
println!("{}", "Completely uninstalling colgrep...".yellow().bold());
println!();
uninstall_ai_tools();
remove_data_directory()?;
remove_cache_directory()?;
check_hf_models();
print_binary_removal_instructions();
println!();
println!(
"{}",
"Colgrep has been completely uninstalled.".green().bold()
);
println!();
Ok(())
}
fn uninstall_ai_tools() {
println!("{}", "Removing from AI coding tools...".cyan());
match uninstall_claude_code() {
Ok(()) => {}
Err(_) => {
println!(
" {} Claude Code: not installed or already removed",
"-".dimmed()
);
}
}
match uninstall_codex() {
Ok(()) => {}
Err(_) => {
println!(" {} Codex: not installed or already removed", "-".dimmed());
}
}
match uninstall_opencode() {
Ok(()) => {}
Err(_) => {
println!(
" {} OpenCode: not installed or already removed",
"-".dimmed()
);
}
}
println!();
}
fn remove_data_directory() -> Result<()> {
let data_dir = get_colgrep_data_dir()?;
if data_dir.exists() {
let indices_dir = data_dir.join("indices");
let index_count = if indices_dir.exists() {
fs::read_dir(&indices_dir)
.map(|rd| rd.filter_map(|e| e.ok()).count())
.unwrap_or(0)
} else {
0
};
fs::remove_dir_all(&data_dir)
.with_context(|| format!("Failed to remove data directory: {}", data_dir.display()))?;
if index_count > 0 {
println!(
"{} Removed {} index(es) from {}",
"✓".green(),
index_count,
data_dir.display()
);
} else {
println!(
"{} Removed data directory: {}",
"✓".green(),
data_dir.display()
);
}
} else {
println!(
" {} No data directory found at {}",
"-".dimmed(),
data_dir.display()
);
}
Ok(())
}
fn remove_cache_directory() -> Result<()> {
let cache_dir = get_colgrep_cache_dir()?;
if cache_dir.exists() {
fs::remove_dir_all(&cache_dir).with_context(|| {
format!("Failed to remove cache directory: {}", cache_dir.display())
})?;
println!(
"{} Removed cache directory: {}",
"✓".green(),
cache_dir.display()
);
} else {
println!(
" {} No cache directory found at {}",
"-".dimmed(),
cache_dir.display()
);
}
Ok(())
}
fn check_hf_models() {
if let Ok(hf_dir) = get_hf_cache_dir() {
if hf_dir.exists() {
let mut model_dirs: Vec<PathBuf> = Vec::new();
if let Ok(entries) = fs::read_dir(&hf_dir) {
for entry in entries.filter_map(|e| e.ok()) {
let name = entry.file_name().to_string_lossy().to_string();
if name.starts_with("models--lightonai--") {
model_dirs.push(entry.path());
}
}
}
if !model_dirs.is_empty() {
println!();
println!(
"{} Found {} colgrep model(s) in HuggingFace cache:",
"!".yellow(),
model_dirs.len()
);
for dir in &model_dirs {
println!(" {}", dir.display());
}
println!();
println!(" To remove these models, run:");
for dir in &model_dirs {
println!(" {}", format!("rm -rf \"{}\"", dir.display()).cyan());
}
}
}
}
}
fn print_binary_removal_instructions() {
println!();
println!(
"{}",
"To complete the uninstall, remove the colgrep binary:".cyan()
);
if let Ok(current_exe) = std::env::current_exe() {
println!(" {}", format!("rm \"{}\"", current_exe.display()).cyan());
} else {
println!(" {}", "rm $(which colgrep)".cyan());
}
println!();
println!(" Or if installed via cargo:");
println!(" {}", "cargo uninstall colgrep".cyan());
}