innate_core/install/
uninstall.rs1use super::{agents::*, skills::*, ui::*, *};
2
3fn remove_binary_from_path() -> ConfigStatus {
4 let dest = home_dir().join(".local").join("bin").join(binary_name());
5 if !dest.exists() && !dest.is_symlink() {
6 return ConfigStatus::Skipped(format!("~/.local/bin/{} not found", binary_name()));
7 }
8 match std::fs::remove_file(&dest) {
9 Ok(()) => ConfigStatus::Updated(dest),
10 Err(e) => ConfigStatus::Error(e.to_string()),
11 }
12}
13
14fn remove_data_dir() -> ConfigStatus {
15 let data = home_dir().join(".innate");
16 if !data.exists() {
17 return ConfigStatus::Skipped("~/.innate/ not found".into());
18 }
19 match std::fs::remove_dir_all(&data) {
20 Ok(()) => ConfigStatus::Updated(data),
21 Err(e) => ConfigStatus::Error(e.to_string()),
22 }
23}
24
25pub fn run_uninstall(yes: bool, purge_data: bool) -> anyhow::Result<()> {
26 let version = env!("CARGO_PKG_VERSION");
27 box_open(&format!("Innate v{version} — Uninstall"));
28
29 if !yes && !prompt_confirm("Remove Innate from your system?", false) {
31 println!("{}", gray("│"));
32 box_close("Cancelled — nothing changed.");
33 return Ok(());
34 }
35
36 let remove_data = purge_data
38 || (!yes
39 && prompt_confirm(
40 "Also delete knowledge data (~/.innate/)? This cannot be undone.",
41 false,
42 ));
43
44 let global_claude = home_dir().join(".claude.json");
46 let global_claude_legacy = home_dir().join(".claude").join("settings.json");
48 let project_claude = PathBuf::from(".claude").join("settings.json");
49
50 for (label, path) in &[
51 ("claude (global)", global_claude.as_path()),
52 ("claude (global, legacy)", global_claude_legacy.as_path()),
53 ("claude (project)", project_claude.as_path()),
54 ] {
55 match remove_claude_config(path) {
56 ConfigStatus::Updated(p) => {
57 result_line(&format!(
58 "{}: Removed MCP config {}",
59 bold(label),
60 gray(&tilde_path(&p))
61 ));
62 }
63 ConfigStatus::Error(e) => {
64 warn_line(&format!("{}: \x1b[31mError — {e}\x1b[0m", bold(label)));
65 }
66 _ => {}
67 }
68 }
69
70 match remove_codex_config() {
71 ConfigStatus::Updated(p) => {
72 result_line(&format!(
73 "{}: Removed MCP config {}",
74 bold("codex"),
75 gray(&tilde_path(&p))
76 ));
77 }
78 ConfigStatus::Error(e) => {
79 warn_line(&format!("{}: \x1b[31mError — {e}\x1b[0m", bold("codex")));
80 }
81 _ => {}
82 }
83
84 match remove_opencode_config() {
85 ConfigStatus::Updated(p) => {
86 result_line(&format!(
87 "{}: Removed MCP config {}",
88 bold("opencode"),
89 gray(&tilde_path(&p))
90 ));
91 }
92 ConfigStatus::Error(e) => {
93 warn_line(&format!("{}: \x1b[31mError — {e}\x1b[0m", bold("opencode")));
94 }
95 _ => {}
96 }
97
98 match remove_skill() {
100 ConfigStatus::Updated(p) => {
101 result_line(&format!(
102 "{}: Removed skill {}",
103 bold("claude"),
104 gray(&tilde_path(&p))
105 ));
106 }
107 ConfigStatus::Error(e) => {
108 warn_line(&format!(
109 "{}: \x1b[31mSkill error — {e}\x1b[0m",
110 bold("claude")
111 ));
112 }
113 _ => {}
114 }
115
116 for (name, status) in remove_commands() {
117 match status {
118 ConfigStatus::Updated(p) => {
119 result_line(&format!(
120 "{}: Removed /{name} {}",
121 bold("claude"),
122 gray(&tilde_path(&p))
123 ));
124 }
125 ConfigStatus::Error(e) => {
126 warn_line(&format!(
127 "{}: \x1b[31mCommand /{name} error — {e}\x1b[0m",
128 bold("claude")
129 ));
130 }
131 _ => {}
132 }
133 }
134
135 match remove_binary_from_path() {
137 ConfigStatus::Updated(p) => {
138 result_line(&format!("Removed binary {}", gray(&tilde_path(&p))));
139 }
140 ConfigStatus::Error(e) => {
141 warn_line(&format!("\x1b[31mCould not remove binary: {e}\x1b[0m"));
142 }
143 _ => {}
144 }
145
146 if remove_data {
148 match remove_data_dir() {
149 ConfigStatus::Updated(p) => {
150 result_line(&format!("Removed data {}", gray(&tilde_path(&p))));
151 }
152 ConfigStatus::Error(e) => {
153 warn_line(&format!("\x1b[31mCould not remove data: {e}\x1b[0m"));
154 }
155 _ => {}
156 }
157 } else {
158 info(&gray("Knowledge data (~/.innate/) kept."));
159 }
160
161 sep();
162 box_close("Innate uninstalled. Restart your agents.");
163 Ok(())
164}