Created
February 24, 2026 10:33
-
-
Save felixhaeberle/90e2ce0ef7ea307e77e355b81b360408 to your computer and use it in GitHub Desktop.
openclaw / moltbot / clawdbot – complete uninstall script
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bash | |
| set -euo pipefail | |
| # ============================================================ | |
| # OpenClaw "One file to rule it all" — FULL PURGE | |
| # Verified names/aliases: openclaw, clawdbot, moltbot | |
| # Related ecosystem name: moltbook | |
| # Verified folders: ~/.openclaw (incl. ~/.openclaw/openclaw.json, ~/.openclaw/workspace) | |
| # | |
| # macOS + Linux. Interactive. Idempotent. Conservative scanning. | |
| # | |
| # RUN WITH: | |
| # chmod +x purge-openclaw.sh | |
| # ./purge-openclaw.sh | |
| # | |
| # ============================================================ | |
| TITLE="OpenClaw Full Purge" | |
| TARGET_NAMES=(openclaw clawdbot moltbot moltbook) | |
| PRIMARY_DIRS=( | |
| "$HOME/.openclaw" | |
| "$HOME/.config/openclaw" | |
| "$HOME/.cache/openclaw" | |
| "$HOME/.local/share/openclaw" | |
| ) | |
| # Optional related dirs (only removed if found): | |
| OPTIONAL_DIRS=( | |
| "$HOME/.clawdbot" | |
| "$HOME/.moltbot" | |
| "$HOME/.moltbook" | |
| "$HOME/.config/clawdbot" | |
| "$HOME/.config/moltbot" | |
| "$HOME/.config/moltbook" | |
| "$HOME/.cache/clawdbot" | |
| "$HOME/.cache/moltbot" | |
| "$HOME/.cache/moltbook" | |
| "$HOME/.local/share/clawdbot" | |
| "$HOME/.local/share/moltbot" | |
| "$HOME/.local/share/moltbook" | |
| ) | |
| # Common binary locations to check (plus whatever is on PATH via `command -v`) | |
| BIN_CANDIDATES=( | |
| "/usr/local/bin" | |
| "/opt/homebrew/bin" | |
| "$HOME/.local/bin" | |
| "$HOME/.bun/bin" | |
| "$HOME/.npm-global/bin" | |
| "$HOME/.volta/bin" | |
| "$HOME/.asdf/shims" | |
| ) | |
| # macOS persistence locations | |
| MAC_LAUNCH_AGENTS=("$HOME/Library/LaunchAgents" "/Library/LaunchAgents") | |
| MAC_LAUNCH_DAEMONS=("/Library/LaunchDaemons") | |
| # Linux persistence locations | |
| LINUX_SYSTEMD_USER_DIRS=("$HOME/.config/systemd/user") | |
| LINUX_CRON_DIRS=("/etc/cron.d" "/etc/cron.daily" "/etc/cron.hourly" "/etc/cron.weekly" "/etc/cron.monthly") | |
| bold() { printf "\033[1m%s\033[0m\n" "$*"; } | |
| info() { printf " • %s\n" "$*"; } | |
| ok() { printf " \033[32m✓ %s\033[0m\n" "$*"; } | |
| warn() { printf " \033[33m⚠ %s\033[0m\n" "$*"; } | |
| have() { command -v "$1" >/dev/null 2>&1; } | |
| confirm() { | |
| local prompt="$1" | |
| read -r -p "$prompt [y/N] " ans | |
| [[ "${ans:-}" =~ ^[Yy]$ ]] | |
| } | |
| rm_any() { | |
| local p="$1" | |
| if [[ -e "$p" || -L "$p" ]]; then | |
| rm -rf "$p" | |
| ok "Removed: $p" | |
| else | |
| info "Not found: $p" | |
| fi | |
| } | |
| list_matches_in_dir() { | |
| local dir="$1" | |
| [[ -d "$dir" ]] || return 0 | |
| local found=0 | |
| for name in "${TARGET_NAMES[@]}"; do | |
| if ls "$dir" 2>/dev/null | grep -i -q "$name"; then | |
| found=1 | |
| break | |
| fi | |
| done | |
| [[ "$found" -eq 1 ]] || return 0 | |
| warn "Matches under: $dir" | |
| # show matching entries (case-insensitive) | |
| ls -1 "$dir" 2>/dev/null | while read -r entry; do | |
| for name in "${TARGET_NAMES[@]}"; do | |
| if printf "%s" "$entry" | grep -i -q "$name"; then | |
| info "$dir/$entry" | |
| break | |
| fi | |
| done | |
| done | |
| } | |
| delete_matches_in_dir() { | |
| local dir="$1" | |
| [[ -d "$dir" ]] || return 0 | |
| for entry in "$dir"/*; do | |
| [[ -e "$entry" ]] || continue | |
| local base | |
| base="$(basename "$entry")" | |
| for name in "${TARGET_NAMES[@]}"; do | |
| if printf "%s" "$base" | grep -i -q "$name"; then | |
| rm_any "$entry" | |
| break | |
| fi | |
| done | |
| done | |
| } | |
| uninstall_global_packages() { | |
| bold "1) Uninstall global packages (if installed)" | |
| local any=0 | |
| if have npm; then | |
| any=1 | |
| npm uninstall -g openclaw clawdbot moltbot moltbook >/dev/null 2>&1 || info "npm: none of the packages were installed globally" | |
| else | |
| info "npm not found" | |
| fi | |
| if have pnpm; then | |
| any=1 | |
| pnpm remove -g openclaw clawdbot moltbot moltbook >/dev/null 2>&1 || info "pnpm: none of the packages were installed globally" | |
| else | |
| info "pnpm not found" | |
| fi | |
| if have yarn; then | |
| any=1 | |
| yarn global remove openclaw clawdbot moltbot moltbook >/dev/null 2>&1 || info "yarn: none of the packages were installed globally" | |
| else | |
| info "yarn not found" | |
| fi | |
| if have bun; then | |
| any=1 | |
| bun remove openclaw clawdbot moltbot moltbook >/dev/null 2>&1 || info "bun: none of the packages were installed globally" | |
| else | |
| info "bun not found" | |
| fi | |
| [[ "$any" -eq 1 ]] && ok "Global uninstall attempts complete." | |
| } | |
| remove_binaries() { | |
| bold "2) Remove binaries / shims" | |
| # Remove resolved PATH hits first | |
| for name in "${TARGET_NAMES[@]}"; do | |
| if have "$name"; then | |
| local p | |
| p="$(command -v "$name")" | |
| warn "Found on PATH: $name -> $p" | |
| if confirm "Remove this file?"; then | |
| rm_any "$p" | |
| else | |
| info "Skipped: $p" | |
| fi | |
| fi | |
| done | |
| # Remove common candidate locations | |
| for d in "${BIN_CANDIDATES[@]}"; do | |
| [[ -d "$d" ]] || continue | |
| for name in "${TARGET_NAMES[@]}"; do | |
| rm_any "$d/$name" | |
| done | |
| done | |
| } | |
| purge_dirs() { | |
| bold "3) Purge verified config/cache/data directories" | |
| warn "These are safe targets commonly used by OpenClaw installs, incl. ~/.openclaw (config + workspace)." | |
| for d in "${PRIMARY_DIRS[@]}"; do | |
| rm_any "$d" | |
| done | |
| bold "4) Purge optional legacy/related directories (only if present)" | |
| for d in "${OPTIONAL_DIRS[@]}"; do | |
| rm_any "$d" | |
| done | |
| } | |
| purge_persistence() { | |
| bold "5) Check persistence (LaunchAgents/systemd/cron) and remove matching entries" | |
| # macOS LaunchAgents/Daemons | |
| if [[ "$(uname -s)" == "Darwin" ]]; then | |
| for dir in "${MAC_LAUNCH_AGENTS[@]}" "${MAC_LAUNCH_DAEMONS[@]}"; do | |
| list_matches_in_dir "$dir" | |
| if [[ -d "$dir" ]]; then | |
| if confirm "Delete matching LaunchAgent/Daemon entries under $dir ?"; then | |
| delete_matches_in_dir "$dir" | |
| else | |
| info "Skipped: $dir" | |
| fi | |
| fi | |
| done | |
| fi | |
| # Linux systemd user units | |
| if [[ -d "${LINUX_SYSTEMD_USER_DIRS[0]}" ]]; then | |
| for dir in "${LINUX_SYSTEMD_USER_DIRS[@]}"; do | |
| list_matches_in_dir "$dir" | |
| if confirm "Delete matching systemd user unit files under $dir ?"; then | |
| delete_matches_in_dir "$dir" | |
| if have systemctl; then | |
| warn "If any units existed, you may want to run:" | |
| info "systemctl --user daemon-reload" | |
| info "systemctl --user reset-failed" | |
| fi | |
| else | |
| info "Skipped: $dir" | |
| fi | |
| done | |
| fi | |
| # Cron snippets (best-effort; system locations may require sudo) | |
| for dir in "${LINUX_CRON_DIRS[@]}"; do | |
| [[ -d "$dir" ]] || continue | |
| list_matches_in_dir "$dir" | |
| warn "Cron directory may require sudo: $dir" | |
| if confirm "Attempt to delete matching cron entries under $dir (may fail without sudo)?"; then | |
| delete_matches_in_dir "$dir" || warn "Some deletions may have failed (permissions)." | |
| else | |
| info "Skipped: $dir" | |
| fi | |
| done | |
| } | |
| purge_project_node_modules() { | |
| bold "6) Optional: remove local node_modules occurrences (CURRENT directory tree only)" | |
| warn "This scans ONLY under: $(pwd)" | |
| if ! confirm "Scan and delete folders with names matching openclaw/clawdbot/moltbot/moltbook inside node_modules?"; then | |
| info "Skipped node_modules cleanup." | |
| return 0 | |
| fi | |
| # Only delete directories inside node_modules that match target names | |
| while IFS= read -r p; do | |
| rm_any "$p" | |
| done < <(find . -type d -path "*/node_modules/*" \( \ | |
| -iname "*openclaw*" -o -iname "*clawdbot*" -o -iname "*moltbot*" -o -iname "*moltbook*" \ | |
| \) 2>/dev/null || true) | |
| ok "node_modules cleanup complete." | |
| } | |
| verify() { | |
| bold "7) Verification" | |
| for name in "${TARGET_NAMES[@]}"; do | |
| if have "$name"; then | |
| warn "Still found on PATH: $name -> $(command -v "$name")" | |
| warn "Run this for deeper visibility:" | |
| info "type -a $name" | |
| else | |
| ok "Not on PATH: $name" | |
| fi | |
| done | |
| bold "8) Quick check: ~/.openclaw" | |
| if [[ -e "$HOME/.openclaw" ]]; then | |
| warn "Still exists: $HOME/.openclaw" | |
| else | |
| ok "~/.openclaw removed" | |
| fi | |
| } | |
| main() { | |
| bold "$TITLE" | |
| warn "Targets (verified): ${TARGET_NAMES[*]}" | |
| warn "This will remove files/directories and may require sudo for system locations." | |
| if ! confirm "Proceed?"; then | |
| echo "Aborted." | |
| exit 0 | |
| fi | |
| uninstall_global_packages | |
| remove_binaries | |
| purge_dirs | |
| purge_persistence | |
| purge_project_node_modules | |
| verify | |
| bold "Done." | |
| info "If you removed shell/PATH references manually, restart your terminal or run: exec \$SHELL -l" | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment