feat(uninstall): interactive 3-mode prompt + safe-by-default — claude/codex/gemini/qwen
After user lost 6 months of work to `rm -rf ~/.claude`, ALL 4 uninstall
scripts (claude, codex, gemini, qwen) now require explicit choice:
Modes:
1) safe (default) — remove binary + env + settings.json
KEEP projects, history, commands(skills),
plans, file-history, plugins
2) settings-only — clear settings.json only, keep binary + data
3) full — wipe everything (tar backup first, requires
typing 'WIPE' to confirm)
4) cancel — exit, do nothing
Default flow:
- Interactive prompt with PREVIEW of user data (size, project count,
command count, history line count) before any destructive op
- Cancel option always available
- Each file backed up to *.uninstall.bak.<TS> before removal
- /etc/environment + .bashrc + /etc/profile.d backed up before sed
Non-interactive (CI / scripts):
UCLAUDE_MODE=safe sudo bash uclaude_uninstall.sh
UCLAUDE_MODE=full sudo bash uclaude_uninstall.sh # creates tar backup
UCLAUDE_YES=1 # skip prompt, default to safe mode
NEVER again should an uninstaller silently destroy user data.
Per-tool env vars: UCLAUDE_MODE / UCODEX_MODE / UGEMINI_MODE / UQWEN_MODE.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -5,6 +5,57 @@
|
||||
# Usage: sudo bash ucodex_uninstall.sh
|
||||
set -euo pipefail
|
||||
|
||||
# ---- Interactive mode select (post user-data-loss incident) ----
|
||||
# Modes:
|
||||
# safe (default) — remove binary + settings, KEEP user data
|
||||
# settings-only — clear settings only, keep binary + user data
|
||||
# full — wipe everything (with tar backup first)
|
||||
|
||||
MODE="${UCODEX_MODE:-}"
|
||||
ASSUME_YES="${UCLAUDE_YES:-0}"
|
||||
TIMESTAMP=$(date +%Y%m%d%H%M%S)
|
||||
|
||||
inventory_user_dirs_codex() {
|
||||
for user_home in /root /home/*; do
|
||||
local d="$user_home/.codex"
|
||||
[ -d "$d" ] || continue
|
||||
local size_kb
|
||||
size_kb=$(du -sk "$d" 2>/dev/null | awk '{print $1}')
|
||||
echo " $d ($((size_kb / 1024)) MB)"
|
||||
done
|
||||
}
|
||||
|
||||
if [ -z "$MODE" ] && [ "$ASSUME_YES" != "1" ]; then
|
||||
echo
|
||||
echo "What to uninstall Codex CLI?"
|
||||
echo " 1) safe — remove binary + settings, KEEP user data (recommended)"
|
||||
echo " 2) settings-only — only clear settings, keep binary + user data"
|
||||
echo " 3) full — wipe everything (tar backup created first)"
|
||||
echo " 4) cancel"
|
||||
echo
|
||||
echo "Your data right now:"
|
||||
inventory_user_dirs_codex
|
||||
echo
|
||||
while true; do
|
||||
read -r -p "Choose [1/2/3/4] (default 1=safe): " choice
|
||||
case "${choice:-1}" in
|
||||
1|safe) MODE="safe"; break ;;
|
||||
2|settings-only) MODE="settings-only"; break ;;
|
||||
3|full)
|
||||
echo "WARNING: full mode DESTROYS all Codex CLI user data."
|
||||
read -r -p "Type 'WIPE' to confirm: " confirm
|
||||
[ "$confirm" = "WIPE" ] && MODE="full" && break
|
||||
echo "Confirmation failed, pick again." ;;
|
||||
4|cancel|q|exit) echo "Cancelled."; exit 0 ;;
|
||||
*) echo "Invalid — pick 1/2/3/4." ;;
|
||||
esac
|
||||
done
|
||||
elif [ -z "$MODE" ]; then
|
||||
MODE="safe"
|
||||
fi
|
||||
|
||||
echo "Mode: $MODE"
|
||||
|
||||
OS="$(uname -s)"
|
||||
IS_MACOS=false
|
||||
[ "$OS" = "Darwin" ] && IS_MACOS=true
|
||||
@@ -62,12 +113,40 @@ fi
|
||||
|
||||
# ---- Remove config ----
|
||||
|
||||
for user_home in "${_home_dirs[@]}"; do
|
||||
CODEX_DIR="$user_home/.codex"
|
||||
for user_home in "${_home_dirs[@]}"; do CODEX_DIR="$user_home/.codex"
|
||||
if [ -d "$CODEX_DIR" ]; then
|
||||
info "Removing $CODEX_DIR..."
|
||||
rm -rf "$CODEX_DIR"
|
||||
log "Removed $CODEX_DIR"
|
||||
# PRESERVE user data by default. See uclaude_uninstall.sh history.
|
||||
case "${MODE:-safe}" in
|
||||
safe)
|
||||
# Remove only managed config files, keep everything else
|
||||
for f in settings.json config.toml config.yaml; do
|
||||
if [ -f "$user_home/.codex/$f" ]; then
|
||||
cp -p "$user_home/.codex/$f" "$user_home/.codex/$f.uninstall.bak.$TIMESTAMP"
|
||||
rm -f "$user_home/.codex/$f"
|
||||
echo " Removed $user_home/.codex/$f (backup: $f.uninstall.bak.$TIMESTAMP)"
|
||||
fi
|
||||
done
|
||||
echo " PRESERVED user data in $user_home/.codex"
|
||||
;;
|
||||
settings-only)
|
||||
for f in settings.json config.toml config.yaml; do
|
||||
if [ -f "$user_home/.codex/$f" ]; then
|
||||
cp -p "$user_home/.codex/$f" "$user_home/.codex/$f.uninstall.bak.$TIMESTAMP"
|
||||
rm -f "$user_home/.codex/$f"
|
||||
fi
|
||||
done
|
||||
;;
|
||||
full)
|
||||
BAK="$user_home/.codex.uninstall-backup.$TIMESTAMP.tar.gz"
|
||||
if tar -czf "$BAK" -C "$user_home" .codex 2>/dev/null; then
|
||||
echo " Backup: $BAK"
|
||||
rm -rf "$user_home/.codex"
|
||||
echo " Removed $user_home/.codex (restore: tar -xzf $BAK -C $user_home)"
|
||||
else
|
||||
echo " ERROR: backup failed — refusing to delete $user_home/.codex"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
done
|
||||
|
||||
|
||||
Reference in New Issue
Block a user