Skip to content

Instantly share code, notes, and snippets.

@nguyenvanduocit
Last active May 5, 2026 09:53
Show Gist options
  • Select an option

  • Save nguyenvanduocit/a258aedf48c1cab6de8e67b76e5ef3b5 to your computer and use it in GitHub Desktop.

Select an option

Save nguyenvanduocit/a258aedf48c1cab6de8e67b76e5ef3b5 to your computer and use it in GitHub Desktop.
Hoàn toàn gỡ Claude Code CLI + mọi cấu hình ~/.claude (dry-run by default)
#!/usr/bin/env bash
# uninstall-claude-code.sh
#
# Hoàn toàn gỡ Claude Code CLI và mọi cấu hình trên macOS / Linux.
# Mặc định chạy ở chế độ dry-run (chỉ in, không xoá).
# Dùng --execute để thực sự xoá.
#
# Quét phổ rộng:
# - Multi-package-manager: npm, bun, pnpm, yarn
# - Multi-Node version manager: nvm, fnm, volta, n
# - Cross-platform: macOS Library/* + Linux XDG paths
# - Homebrew bins: /usr/local/bin, /opt/homebrew/bin
# - PATH cross-check: `which -a claude` cuối cùng
#
# An toàn theo thiết kế:
# - Dry-run mặc định
# - Mọi path phải nằm trong $HOME (trừ các bin dir hệ thống được whitelist)
# - In rõ từng path/lệnh; path không tồn tại → skip im lặng
set -uo pipefail
# ---------- Defaults ----------
DRY_RUN=true
INCLUDE_DESKTOP=false
KEEP_PM=false
REMOVE_COMPLETIONS=false
PURGE_KEYCHAIN=false
ASSUME_YES=false
# ---------- Colors ----------
if [[ -t 1 ]]; then
C_RED=$'\033[31m'; C_GRN=$'\033[32m'; C_YEL=$'\033[33m'
C_BLU=$'\033[34m'; C_DIM=$'\033[2m'; C_RST=$'\033[0m'
else
C_RED=""; C_GRN=""; C_YEL=""; C_BLU=""; C_DIM=""; C_RST=""
fi
# ---------- OS detect ----------
OS="$(uname -s)"
IS_MAC=false; IS_LINUX=false
case "$OS" in
Darwin) IS_MAC=true ;;
Linux) IS_LINUX=true ;;
esac
# ---------- Helpers ----------
usage() {
cat <<EOF
Usage: $(basename "$0") [OPTIONS]
Gỡ hoàn toàn Claude Code CLI và mọi cấu hình (macOS / Linux).
OPTIONS:
-e, --execute Thực sự xoá (mặc định: dry-run)
--include-desktop Xoá luôn dữ liệu Claude Desktop app
--keep-pm Bỏ qua bước npm/bun/pnpm/yarn uninstall
--remove-completions Xoá shell completion files (fish/zsh/bash)
--purge-keychain Xoá entries Keychain (macOS only, OAuth tokens)
-y, --yes Không hỏi xác nhận khi --execute
-h, --help In trợ giúp này
VÍ DỤ:
$(basename "$0") # dry-run, xem những gì sẽ bị xoá
$(basename "$0") --execute # xoá thật, có hỏi xác nhận
$(basename "$0") --execute -y # xoá thật, không hỏi
$(basename "$0") --execute --include-desktop --remove-completions --purge-keychain
# gỡ tận gốc
QUÉT NHỮNG GÌ:
Config: ~/.claude, ~/.claude.json{,.backup,.tmp.*}
Linux XDG: ~/.config/claude*, ~/.local/share/claude*, ~/.local/state/claude*
Cache: macOS: ~/Library/Caches/claude-cli-nodejs, ~/Library/Caches/com.anthropic.claude-code
Linux: ~/.cache/claude*
Binary: npm/bun/pnpm/yarn global, nvm/fnm/volta/n bin dirs,
~/.local/bin, ~/.npm-global/bin, /usr/local/bin, /opt/homebrew/bin
PATH: \`which -a claude\` cross-check cuối
+Desktop:~/Library/Application Support/Claude (macOS), ~/.config/Claude (Linux)
+Comp: fish/zsh/bash completion files
+Keychn: security service "Claude Code", "claude.ai" (macOS)
EOF
}
log() { echo "${C_BLU}[INFO]${C_RST} $*"; }
warn() { echo "${C_YEL}[WARN]${C_RST} $*"; }
err() { echo "${C_RED}[ERR ]${C_RST} $*" >&2; }
ok() { echo "${C_GRN}[ OK ]${C_RST} $*"; }
dim() { echo "${C_DIM}$*${C_RST}"; }
# Whitelist path roots cho phép xoá. $HOME mặc định an toàn.
# Thêm các bin dir hệ thống mà Claude có thể cài vào (Homebrew prefix).
WHITELIST_ROOTS=(
"$HOME"
"/usr/local/bin"
"/usr/local/share/zsh/site-functions"
"/opt/homebrew/bin"
"/opt/homebrew/share/zsh/site-functions"
)
is_safe_path() {
local p="$1"
for root in "${WHITELIST_ROOTS[@]}"; do
case "$p" in
"$root"|"$root"/*) return 0 ;;
esac
done
return 1
}
remove_path() {
local target="$1"
local expanded=()
if [[ "$target" == *"*"* ]]; then
# shellcheck disable=SC2206
expanded=( $target )
if [[ ${#expanded[@]} -eq 1 && ! -e "${expanded[0]}" && ! -L "${expanded[0]}" ]]; then
return 0
fi
else
expanded=( "$target" )
fi
for p in "${expanded[@]}"; do
if [[ ! -e "$p" && ! -L "$p" ]]; then
continue
fi
if ! is_safe_path "$p"; then
err " TỪ CHỐI (ngoài whitelist): $p"
continue
fi
local size=""
if [[ -d "$p" && ! -L "$p" ]]; then
size=$(du -sh "$p" 2>/dev/null | awk '{print $1}')
[[ -n "$size" ]] && size=" (${size})"
elif [[ -f "$p" || -L "$p" ]]; then
size=$(du -h "$p" 2>/dev/null | awk '{print $1}')
[[ -n "$size" ]] && size=" (${size})"
fi
if $DRY_RUN; then
echo " ${C_YEL}[DRY]${C_RST} would rm -rf ${p}${size}"
else
rm -rf -- "$p" && ok " removed: ${p}${size}" || err " failed: $p"
fi
done
}
run_cmd() {
local desc="$1"; shift
if $DRY_RUN; then
echo " ${C_YEL}[DRY]${C_RST} would run: ${desc}"
dim " $*"
else
log " running: $desc"
if "$@" >/dev/null 2>&1; then
ok " done: $desc"
else
warn " failed (non-fatal): $desc"
fi
fi
}
confirm() {
$ASSUME_YES && return 0
$DRY_RUN && return 0
echo
read -r -p "${C_RED}Thật sự xoá những thứ trên? Gõ 'yes' để xác nhận: ${C_RST}" ans
[[ "$ans" == "yes" ]]
}
# Trả về 0 nếu binary `claude` đăng ký với package manager, 1 nếu không
pm_has_claude() {
local pm="$1"
case "$pm" in
npm)
command -v npm >/dev/null 2>&1 && \
npm list -g --depth=0 2>/dev/null | grep -q "@anthropic-ai/claude-code"
;;
bun)
command -v bun >/dev/null 2>&1 && \
bun pm ls -g 2>/dev/null | grep -q "@anthropic-ai/claude-code"
;;
pnpm)
command -v pnpm >/dev/null 2>&1 && \
pnpm list -g --depth=0 2>/dev/null | grep -q "@anthropic-ai/claude-code"
;;
yarn)
command -v yarn >/dev/null 2>&1 && \
yarn global list 2>/dev/null | grep -q "@anthropic-ai/claude-code"
;;
*) return 1 ;;
esac
}
# ---------- Parse args ----------
while [[ $# -gt 0 ]]; do
case "$1" in
-e|--execute) DRY_RUN=false; shift ;;
--include-desktop) INCLUDE_DESKTOP=true; shift ;;
--keep-pm|--keep-npm) KEEP_PM=true; shift ;;
--remove-completions) REMOVE_COMPLETIONS=true; shift ;;
--purge-keychain) PURGE_KEYCHAIN=true; shift ;;
-y|--yes) ASSUME_YES=true; shift ;;
-h|--help) usage; exit 0 ;;
*) err "Tham số không hợp lệ: $1"; usage; exit 1 ;;
esac
done
# ---------- Banner ----------
echo
if $DRY_RUN; then
echo "${C_YEL}=== DRY-RUN MODE — KHÔNG XOÁ GÌ HẾT ===${C_RST}"
dim "OS: $OS | --execute để xoá thật"
else
echo "${C_RED}=== EXECUTE MODE — SẼ XOÁ THẬT ===${C_RST}"
dim "OS: $OS"
fi
echo
# ============================================================
# Section 1 — Config & data
# ============================================================
log "[1] Cấu hình & data Claude Code"
remove_path "$HOME/.claude"
remove_path "$HOME/.claude.json"
remove_path "$HOME/.claude.json.backup"
remove_path "$HOME/.claude.json.tmp.*"
# XDG (cross-platform, chuẩn hơn trên Linux)
XDG_CONFIG="${XDG_CONFIG_HOME:-$HOME/.config}"
XDG_DATA="${XDG_DATA_HOME:-$HOME/.local/share}"
XDG_STATE="${XDG_STATE_HOME:-$HOME/.local/state}"
XDG_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}"
remove_path "$XDG_CONFIG/claude"
remove_path "$XDG_CONFIG/claude-code"
remove_path "$XDG_CONFIG/anthropic"
remove_path "$XDG_DATA/claude"
remove_path "$XDG_DATA/claude-code"
remove_path "$XDG_STATE/claude"
remove_path "$XDG_STATE/claude-code"
# ============================================================
# Section 2 — Cache
# ============================================================
echo
log "[2] Cache"
if $IS_MAC; then
remove_path "$HOME/Library/Caches/claude-cli-nodejs"
remove_path "$HOME/Library/Caches/com.anthropic.claude-code"
remove_path "$HOME/Library/Caches/com.anthropic.claude"
fi
remove_path "$XDG_CACHE/claude"
remove_path "$XDG_CACHE/claude-code"
remove_path "$XDG_CACHE/claude-cli-nodejs"
# ============================================================
# Section 3 — Claude Desktop (opt-in)
# ============================================================
echo
if $INCLUDE_DESKTOP; then
log "[3] Claude Desktop app data (--include-desktop)"
if $IS_MAC; then
remove_path "$HOME/Library/Application Support/Claude"
remove_path "$HOME/Library/Logs/Claude"
remove_path "$HOME/Library/Saved Application State/com.anthropic.claudefordesktop.savedState"
remove_path "$HOME/Library/Preferences/com.anthropic.claudefordesktop.plist"
remove_path "$HOME/Library/Preferences/com.anthropic.claude.plist"
fi
if $IS_LINUX; then
remove_path "$XDG_CONFIG/Claude"
remove_path "$XDG_CONFIG/claude-desktop"
fi
else
log "[3] Claude Desktop ${C_DIM}(skip — dùng --include-desktop)${C_RST}"
fi
# ============================================================
# Section 4 — Package managers (uninstall) + binary sweep
# ============================================================
echo
if $KEEP_PM; then
log "[4a] PM uninstall ${C_DIM}(skip — --keep-pm)${C_RST}"
else
log "[4a] Package manager uninstall"
if pm_has_claude npm; then
run_cmd "npm uninstall -g @anthropic-ai/claude-code" \
npm uninstall -g @anthropic-ai/claude-code
else
dim " (skip) npm: không có @anthropic-ai/claude-code"
fi
if pm_has_claude bun; then
run_cmd "bun remove -g @anthropic-ai/claude-code" \
bun remove -g @anthropic-ai/claude-code
else
dim " (skip) bun: không có @anthropic-ai/claude-code"
fi
if pm_has_claude pnpm; then
run_cmd "pnpm remove -g @anthropic-ai/claude-code" \
pnpm remove -g @anthropic-ai/claude-code
else
dim " (skip) pnpm: không có @anthropic-ai/claude-code"
fi
if pm_has_claude yarn; then
run_cmd "yarn global remove @anthropic-ai/claude-code" \
yarn global remove @anthropic-ai/claude-code
else
dim " (skip) yarn: không có @anthropic-ai/claude-code"
fi
fi
echo
log "[4b] Quét binary còn sót (mọi PM bin dir + version manager)"
# Build danh sách bin dir động + tĩnh
declare -a BIN_DIRS=(
"$HOME/.local/bin"
"$HOME/.npm-global/bin"
"$HOME/.vite-plus/bin"
"$HOME/.bun/bin"
"$HOME/.deno/bin"
"$HOME/.volta/bin"
"/usr/local/bin"
"/opt/homebrew/bin"
)
# npm prefix động (nvm có thể đổi prefix theo node version)
if command -v npm >/dev/null 2>&1; then
np="$(npm prefix -g 2>/dev/null)"
[[ -n "$np" ]] && BIN_DIRS+=( "$np/bin" )
fi
# bun
if command -v bun >/dev/null 2>&1; then
bp="$(bun pm bin -g 2>/dev/null)"
[[ -n "$bp" ]] && BIN_DIRS+=( "$bp" )
fi
# pnpm
if command -v pnpm >/dev/null 2>&1; then
pp="$(pnpm bin -g 2>/dev/null)"
[[ -n "$pp" ]] && BIN_DIRS+=( "$pp" )
fi
# yarn
if command -v yarn >/dev/null 2>&1; then
yp="$(yarn global bin 2>/dev/null)"
[[ -n "$yp" ]] && BIN_DIRS+=( "$yp" )
fi
# nvm: mọi node version
if [[ -d "$HOME/.nvm/versions/node" ]]; then
while IFS= read -r -d '' nv; do
BIN_DIRS+=( "$nv/bin" )
done < <(find "$HOME/.nvm/versions/node" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)
fi
# fnm
if [[ -d "$HOME/.fnm/node-versions" ]]; then
while IFS= read -r -d '' nv; do
BIN_DIRS+=( "$nv/installation/bin" )
done < <(find "$HOME/.fnm/node-versions" -mindepth 1 -maxdepth 1 -type d -print0 2>/dev/null)
fi
# n (version manager: ~/n hoặc /usr/local/n)
[[ -d "$HOME/n/bin" ]] && BIN_DIRS+=( "$HOME/n/bin" )
# Dedupe
mapfile -t BIN_DIRS < <(printf '%s\n' "${BIN_DIRS[@]}" | awk '!seen[$0]++')
for d in "${BIN_DIRS[@]}"; do
for f in "$d/claude" "$d/claude-code"; do
[[ -e "$f" || -L "$f" ]] && remove_path "$f"
done
done
# ============================================================
# Section 5 — Shell completions (opt-in)
# ============================================================
echo
if $REMOVE_COMPLETIONS; then
log "[5] Shell completions (--remove-completions)"
remove_path "$HOME/.config/fish/completions/claude.fish"
remove_path "$HOME/.config/fish/completions/claude-code.fish"
remove_path "$HOME/.zsh/completions/_claude"
remove_path "$HOME/.oh-my-zsh/completions/_claude"
remove_path "$HOME/.bash_completion.d/claude"
if $IS_MAC; then
remove_path "/usr/local/share/zsh/site-functions/_claude"
remove_path "/opt/homebrew/share/zsh/site-functions/_claude"
fi
else
log "[5] Shell completions ${C_DIM}(skip — dùng --remove-completions)${C_RST}"
fi
# ============================================================
# Section 6 — macOS Keychain (opt-in)
# ============================================================
echo
if $PURGE_KEYCHAIN && $IS_MAC; then
log "[6] macOS Keychain entries (--purge-keychain)"
for svc in "Claude Code" "claude.ai" "Anthropic" "anthropic"; do
if security find-generic-password -s "$svc" >/dev/null 2>&1; then
run_cmd "security delete-generic-password -s '$svc'" \
security delete-generic-password -s "$svc"
else
dim " (skip) keychain service không tồn tại: $svc"
fi
done
elif $PURGE_KEYCHAIN; then
log "[6] Keychain ${C_DIM}(skip — không phải macOS)${C_RST}"
else
log "[6] Keychain ${C_DIM}(skip — dùng --purge-keychain trên macOS)${C_RST}"
fi
# ============================================================
# Section 7 — PATH cross-check
# ============================================================
echo
log "[7] PATH cross-check (\`which -a claude\`)"
if command -v which >/dev/null 2>&1; then
remaining=$(which -a claude 2>/dev/null || true)
if [[ -z "$remaining" ]]; then
ok " PATH sạch — không còn binary 'claude'"
else
if $DRY_RUN; then
warn " Sau dry-run, các binary này vẫn sẽ còn trong PATH (kiểm tra lại sau --execute):"
else
warn " Vẫn còn binary 'claude' trong PATH — không tự động xoá (nằm ngoài whitelist):"
fi
while IFS= read -r line; do
echo " - $line"
done <<< "$remaining"
dim " → Nếu cần, xoá thủ công: rm <path>"
fi
fi
# ---------- Confirm & summary ----------
echo
if $DRY_RUN; then
echo "${C_YEL}=== DRY-RUN HOÀN TẤT — chạy lại với --execute để xoá thật ===${C_RST}"
exit 0
fi
if ! confirm; then
warn "Đã huỷ. Không có gì bị xoá thêm sau confirm."
exit 1
fi
echo
ok "Hoàn tất. Claude Code đã được gỡ."
echo
log "Kiểm tra sau gỡ:"
dim " command -v claude # nên rỗng"
dim " ls ~/.claude # nên 'No such file or directory'"
dim " ls ~/.claude.json # nên 'No such file or directory'"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment