use std::io::{self, Write};
use std::path::PathBuf;
enum InstallMethod {
Cargo,
Homebrew,
Npm,
Manual(PathBuf),
}
impl InstallMethod {
fn label(&self) -> &str {
match self {
InstallMethod::Cargo => "cargo",
InstallMethod::Homebrew => "homebrew",
InstallMethod::Npm => "npm",
InstallMethod::Manual(_) => "binary",
}
}
}
fn detect_install_method() -> InstallMethod {
let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("agent"));
let path_str = exe.to_string_lossy();
if path_str.contains(".cargo") {
InstallMethod::Cargo
} else if path_str.contains("homebrew")
|| path_str.contains("Cellar")
|| path_str.contains("linuxbrew")
{
InstallMethod::Homebrew
} else if path_str.contains("node_modules")
|| path_str.contains("npm")
|| path_str.contains("npx")
{
InstallMethod::Npm
} else {
InstallMethod::Manual(exe)
}
}
fn data_directories() -> Vec<(&'static str, PathBuf)> {
let mut dirs_found = Vec::new();
if let Some(d) = dirs::config_dir().map(|d| d.join("agent-code"))
&& d.exists()
{
dirs_found.push(("Config", d));
}
if let Some(d) = dirs::cache_dir().map(|d| d.join("agent-code"))
&& d.exists()
{
dirs_found.push(("Cache", d));
}
if let Some(d) = dirs::data_local_dir().map(|d| d.join("agent-code"))
&& d.exists()
{
dirs_found.push(("Data", d));
}
dirs_found
}
fn confirm(prompt: &str) -> bool {
print!("{prompt} [y/N] ");
io::stdout().flush().ok();
let mut input = String::new();
if io::stdin().read_line(&mut input).is_err() {
return false;
}
matches!(input.trim().to_lowercase().as_str(), "y" | "yes")
}
fn remove_dir(label: &str, path: &PathBuf) {
match std::fs::remove_dir_all(path) {
Ok(()) => println!(" Removed {label}: {}", path.display()),
Err(e) => eprintln!(" Failed to remove {}: {e}", path.display()),
}
}
pub fn run(args: Option<&str>) {
let force = args.map(|a| a.trim()) == Some("--force");
let method = detect_install_method();
let data_dirs = data_directories();
println!();
println!(" Uninstall agent-code");
println!();
match &method {
InstallMethod::Cargo => {
println!(" Install method: cargo");
println!(" Action: cargo uninstall agent-code");
}
InstallMethod::Homebrew => {
println!(" Install method: homebrew");
println!(" Action: brew uninstall agent-code");
}
InstallMethod::Npm => {
println!(" Install method: npm");
println!(" Action: npm uninstall -g @avala-ai/agent-code");
}
InstallMethod::Manual(path) => {
println!(" Install method: manual binary");
println!(" Binary: {}", path.display());
}
}
if data_dirs.is_empty() {
println!(" Data dirs: (none found)");
} else {
println!();
println!(" Data directories to remove:");
for (label, path) in &data_dirs {
println!(" {label}: {}", path.display());
}
}
println!();
if !force && !confirm("Proceed with uninstall?") {
println!(" Cancelled.");
return;
}
for (label, path) in &data_dirs {
remove_dir(label, path);
}
let binary_ok = match &method {
InstallMethod::Cargo => run_package_manager("cargo", &["uninstall", "agent-code"]),
InstallMethod::Homebrew => run_package_manager("brew", &["uninstall", "agent-code"]),
InstallMethod::Npm => {
run_package_manager("npm", &["uninstall", "-g", "@avala-ai/agent-code"])
}
InstallMethod::Manual(path) => match std::fs::remove_file(path) {
Ok(()) => {
println!(" Removed binary: {}", path.display());
true
}
Err(e) => {
eprintln!(" Failed to remove binary: {e}");
eprintln!(" Try manually: sudo rm {}", path.display());
false
}
},
};
println!();
if binary_ok {
println!(" agent-code has been uninstalled ({}).", method.label());
} else {
eprintln!(" Uninstall completed with errors. See messages above.");
}
}
fn run_package_manager(program: &str, args: &[&str]) -> bool {
println!(" Running: {} {}", program, args.join(" "));
match std::process::Command::new(program)
.args(args)
.stdout(std::process::Stdio::inherit())
.stderr(std::process::Stdio::inherit())
.status()
{
Ok(status) if status.success() => true,
Ok(status) => {
eprintln!(" {} exited with status {status}", program);
false
}
Err(e) => {
eprintln!(" Failed to run {program}: {e}");
if e.kind() == io::ErrorKind::NotFound {
eprintln!(" {program} not found. Remove the binary manually.");
}
false
}
}
}