Files
everything-claude-code/.codebuddy/uninstall.sh
轻舟Joshua c38bc799fd feat(install): add CodeBuddy(Tencent) adaptation with installation scripts (#1038)
* feat(install): add CodeBuddy(Tencent) adaptation with installation scripts

* fix: add codebuddy to SUPPORTED_INSTALL_TARGETS

* fix(codebuddy): resolve installer path issues, unused vars, and uninstall safety
2026-03-31 14:06:26 -07:00

185 lines
5.5 KiB
Bash
Executable File

#!/bin/bash
#
# ECC CodeBuddy Uninstaller
# Uninstalls Everything Claude Code workflows from a CodeBuddy project.
#
# Usage:
# ./uninstall.sh # Uninstall from current directory
# ./uninstall.sh ~ # Uninstall globally from ~/.codebuddy/
#
set -euo pipefail
# Resolve the directory where this script lives
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
# CodeBuddy directory name
CODEBUDDY_DIR=".codebuddy"
resolve_path() {
python3 -c 'import os, sys; print(os.path.realpath(sys.argv[1]))' "$1"
}
is_valid_manifest_entry() {
local file_path="$1"
case "$file_path" in
""|/*|~*|*/../*|../*|*/..|..)
return 1
;;
esac
return 0
}
# Main uninstall function
do_uninstall() {
local target_dir="$PWD"
# Check if ~ was specified (or expanded to $HOME)
if [ "$#" -ge 1 ]; then
if [ "$1" = "~" ] || [ "$1" = "$HOME" ]; then
target_dir="$HOME"
fi
fi
# Check if we're already inside a .codebuddy directory
local current_dir_name="$(basename "$target_dir")"
local codebuddy_full_path
if [ "$current_dir_name" = ".codebuddy" ]; then
# Already inside the codebuddy directory, use it directly
codebuddy_full_path="$target_dir"
else
# Normal case: append CODEBUDDY_DIR to target_dir
codebuddy_full_path="$target_dir/$CODEBUDDY_DIR"
fi
echo "ECC CodeBuddy Uninstaller"
echo "=========================="
echo ""
echo "Target: $codebuddy_full_path/"
echo ""
if [ ! -d "$codebuddy_full_path" ]; then
echo "Error: $CODEBUDDY_DIR directory not found at $target_dir"
exit 1
fi
codebuddy_root_resolved="$(resolve_path "$codebuddy_full_path")"
# Manifest file path
MANIFEST="$codebuddy_full_path/.ecc-manifest"
if [ ! -f "$MANIFEST" ]; then
echo "Warning: No manifest file found (.ecc-manifest)"
echo ""
echo "This could mean:"
echo " 1. ECC was installed with an older version without manifest support"
echo " 2. The manifest file was manually deleted"
echo ""
read -p "Do you want to remove the entire $CODEBUDDY_DIR directory? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Uninstall cancelled."
exit 0
fi
rm -rf "$codebuddy_full_path"
echo "Uninstall complete!"
echo ""
echo "Removed: $codebuddy_full_path/"
exit 0
fi
echo "Found manifest file - will only remove files installed by ECC"
echo ""
read -p "Are you sure you want to uninstall ECC from $CODEBUDDY_DIR? (y/N) " -n 1 -r
echo
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
echo "Uninstall cancelled."
exit 0
fi
# Counters
removed=0
skipped=0
# Read manifest and remove files
while IFS= read -r file_path; do
[ -z "$file_path" ] && continue
if ! is_valid_manifest_entry "$file_path"; then
echo "Skipped: $file_path (invalid manifest entry)"
skipped=$((skipped + 1))
continue
fi
full_path="$codebuddy_full_path/$file_path"
# Security check: ensure the path resolves inside the target directory.
# Use Python to compute a reliable relative path so symlinks cannot
# escape the boundary.
relative="$(python3 -c 'import os,sys; print(os.path.relpath(os.path.abspath(sys.argv[1]), sys.argv[2]))' "$full_path" "$codebuddy_root_resolved")"
case "$relative" in
../*|..)
echo "Skipped: $file_path (outside target directory)"
skipped=$((skipped + 1))
continue
;;
esac
if [ -L "$full_path" ] || [ -f "$full_path" ]; then
rm -f "$full_path"
echo "Removed: $file_path"
removed=$((removed + 1))
elif [ -d "$full_path" ]; then
# Only remove directory if it's empty
if [ -z "$(ls -A "$full_path" 2>/dev/null)" ]; then
rmdir "$full_path" 2>/dev/null || true
if [ ! -d "$full_path" ]; then
echo "Removed: $file_path/"
removed=$((removed + 1))
fi
else
echo "Skipped: $file_path/ (not empty - contains user files)"
skipped=$((skipped + 1))
fi
else
skipped=$((skipped + 1))
fi
done < "$MANIFEST"
while IFS= read -r empty_dir; do
[ "$empty_dir" = "$codebuddy_full_path" ] && continue
relative_dir="${empty_dir#$codebuddy_full_path/}"
rmdir "$empty_dir" 2>/dev/null || true
if [ ! -d "$empty_dir" ]; then
echo "Removed: $relative_dir/"
removed=$((removed + 1))
fi
done < <(find "$codebuddy_full_path" -depth -type d -empty 2>/dev/null | sort -r)
# Try to remove the main codebuddy directory if it's empty
if [ -d "$codebuddy_full_path" ] && [ -z "$(ls -A "$codebuddy_full_path" 2>/dev/null)" ]; then
rmdir "$codebuddy_full_path" 2>/dev/null || true
if [ ! -d "$codebuddy_full_path" ]; then
echo "Removed: $CODEBUDDY_DIR/"
removed=$((removed + 1))
fi
fi
echo ""
echo "Uninstall complete!"
echo ""
echo "Summary:"
echo " Removed: $removed items"
echo " Skipped: $skipped items (not found or user-modified)"
echo ""
if [ -d "$codebuddy_full_path" ]; then
echo "Note: $CODEBUDDY_DIR directory still exists (contains user-added files)"
fi
}
# Execute uninstall
do_uninstall "$@"