use super::flags::InitFlags;
use super::helpers::{
atomic_write_settings, check_mark, confirm_proceed, load_or_create_settings,
resolve_config_dir_for_agent, resolve_real_settings_path, HOOK_SCRIPT_NAME, SETTINGS_FILE,
};
use super::state::{has_skim_hook_entry, read_settings_json};
fn remove_skim_from_settings(settings: &mut serde_json::Value) {
let Some(obj) = settings.as_object_mut() else {
return;
};
if let Some(hooks_obj) = obj.get_mut("hooks").and_then(|h| h.as_object_mut()) {
if let Some(arr) = hooks_obj
.get_mut("PreToolUse")
.and_then(|p| p.as_array_mut())
{
arr.retain(|entry| !has_skim_hook_entry(entry));
if arr.is_empty() {
hooks_obj.remove("PreToolUse");
}
}
if hooks_obj.is_empty() {
obj.remove("hooks");
}
}
if let Some(mkts_obj) = obj
.get_mut("extraKnownMarketplaces")
.and_then(|m| m.as_object_mut())
{
mkts_obj.remove("skim");
if mkts_obj.is_empty() {
obj.remove("extraKnownMarketplaces");
}
}
}
pub(super) fn run_uninstall(flags: &InitFlags) -> anyhow::Result<std::process::ExitCode> {
let config_dir = resolve_config_dir_for_agent(flags.project, flags.agent)?;
let settings_path = config_dir.join(SETTINGS_FILE);
let hook_script_path = config_dir.join("hooks").join(HOOK_SCRIPT_NAME);
let settings_has_hook = read_settings_json(&settings_path)
.and_then(|json| {
json.get("hooks")?
.get("PreToolUse")?
.as_array()
.map(|arr| arr.iter().any(has_skim_hook_entry))
})
.unwrap_or(false);
let script_exists = hook_script_path.exists();
if !settings_has_hook && !script_exists {
println!(" skim hook not found. Nothing to uninstall.");
return Ok(std::process::ExitCode::SUCCESS);
}
if script_exists {
if let Ok(false) = crate::cmd::integrity::verify_script_integrity(
&config_dir,
flags.agent.cli_name(),
&hook_script_path,
) {
if !flags.force {
eprintln!("warning: hook script has been modified since installation");
eprintln!("hint: use --force to uninstall anyway");
return Ok(std::process::ExitCode::FAILURE);
}
eprintln!("warning: hook script has been modified (proceeding with --force)");
}
}
if !flags.yes {
println!();
println!(" skim init --uninstall");
println!();
if settings_has_hook {
println!(" * Remove hook entry from {}", settings_path.display());
println!(" * Remove skim from extraKnownMarketplaces");
}
if script_exists {
println!(" * Delete {}", hook_script_path.display());
}
println!();
if !confirm_proceed()? {
println!(" Cancelled.");
return Ok(std::process::ExitCode::SUCCESS);
}
}
if flags.dry_run {
if settings_has_hook {
println!(
" [dry-run] Would remove hook entry from {}",
settings_path.display()
);
println!(" [dry-run] Would remove skim from extraKnownMarketplaces");
}
if script_exists {
println!(" [dry-run] Would delete {}", hook_script_path.display());
}
return Ok(std::process::ExitCode::SUCCESS);
}
if settings_has_hook {
let real_path = resolve_real_settings_path(&settings_path)?;
let mut settings = load_or_create_settings(&real_path)?;
remove_skim_from_settings(&mut settings);
atomic_write_settings(&settings, &real_path)?;
println!(
" {} Removed: hook entry from {}",
check_mark(true),
settings_path.display()
);
}
if script_exists {
std::fs::remove_file(&hook_script_path)?;
println!(
" {} Deleted: {}",
check_mark(true),
hook_script_path.display()
);
let _ = crate::cmd::integrity::remove_hash_manifest(&config_dir, flags.agent.cli_name());
}
let global = !flags.project;
super::install::remove_guidance(flags.agent, global)?;
println!();
println!(" skim hook has been uninstalled.");
println!();
Ok(std::process::ExitCode::SUCCESS)
}