Remove tracedecay-owned hook groups from a Codex `hooks.json`.
(hooks_path: &Path)
| 1129 | |
| 1130 | /// Remove tracedecay-owned hook groups from a Codex `hooks.json`. |
| 1131 | fn uninstall_hooks(hooks_path: &Path) { |
| 1132 | let subcommands: Vec<&str> = CODEX_MANAGED_HOOKS |
| 1133 | .iter() |
| 1134 | .map(|hook| hook.subcommand) |
| 1135 | .chain(CODEX_LEGACY_HOOK_SUBCOMMANDS.iter().copied()) |
| 1136 | .collect(); |
| 1137 | |
| 1138 | if !hooks_path.exists() { |
| 1139 | return; |
| 1140 | } |
| 1141 | let Ok(mut hooks) = load_json_file_strict(hooks_path) else { |
| 1142 | return; |
| 1143 | }; |
| 1144 | |
| 1145 | let Some(events) = hooks.get_mut("hooks").and_then(|h| h.as_object_mut()) else { |
| 1146 | return; |
| 1147 | }; |
| 1148 | let mut removed_any = false; |
| 1149 | for groups in events.values_mut() { |
| 1150 | if let Some(arr) = groups.as_array_mut() { |
| 1151 | let before = arr.len(); |
| 1152 | arr.retain(|group| !subcommands.iter().any(|sc| group_has_subcommand(group, sc))); |
| 1153 | removed_any |= arr.len() != before; |
| 1154 | } |
| 1155 | } |
| 1156 | if !removed_any { |
| 1157 | return; |
| 1158 | } |
| 1159 | events.retain(|_, groups| groups.as_array().is_some_and(|a| !a.is_empty())); |
| 1160 | |
| 1161 | let is_empty = hooks |
| 1162 | .get("hooks") |
| 1163 | .and_then(|h| h.as_object()) |
| 1164 | .is_some_and(serde_json::Map::is_empty); |
| 1165 | if is_empty { |
| 1166 | std::fs::remove_file(hooks_path).ok(); |
| 1167 | eprintln!( |
| 1168 | "\x1b[32m✔\x1b[0m Removed {} (was empty)", |
| 1169 | hooks_path.display() |
| 1170 | ); |
| 1171 | } else if safe_write_json_file(hooks_path, &hooks, None).is_ok() { |
| 1172 | eprintln!( |
| 1173 | "\x1b[32m✔\x1b[0m Removed tracedecay hooks from {}", |
| 1174 | hooks_path.display() |
| 1175 | ); |
| 1176 | } |
| 1177 | } |
| 1178 | |
| 1179 | /// Remove MCP server from ~/.codex/config.toml. |
| 1180 | fn uninstall_mcp_server(config_path: &Path) -> Result<()> { |
no test coverage detected