mirror of
https://github.com/cexll/myclaude.git
synced 2026-02-04 02:20:42 +08:00
feat: add uninstall scripts with selective module removal
- uninstall.py: Python uninstaller with --list, --dry-run, --module options - uninstall.sh: Bash uninstaller with same functionality - Reads installed_modules.json for precise removal - Supports partial uninstall (--module dev) - --purge option for complete removal - Cleans PATH from shell configs (.bashrc/.zshrc) Generated with SWE-Agent.ai Co-Authored-By: SWE-Agent.ai <noreply@swe-agent.ai>
This commit is contained in:
302
uninstall.py
Executable file
302
uninstall.py
Executable file
@@ -0,0 +1,302 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Uninstaller for myclaude - reads installed_modules.json for precise removal."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import json
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Dict, List, Optional, Set
|
||||||
|
|
||||||
|
DEFAULT_INSTALL_DIR = "~/.claude"
|
||||||
|
|
||||||
|
# Files created by installer itself (not by modules)
|
||||||
|
INSTALLER_FILES = ["install.log", "installed_modules.json", "installed_modules.json.bak"]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(argv: Optional[List[str]] = None) -> argparse.Namespace:
|
||||||
|
parser = argparse.ArgumentParser(description="Uninstall myclaude")
|
||||||
|
parser.add_argument(
|
||||||
|
"--install-dir",
|
||||||
|
default=DEFAULT_INSTALL_DIR,
|
||||||
|
help="Installation directory (defaults to ~/.claude)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--module",
|
||||||
|
help="Comma-separated modules to uninstall (default: all installed)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--list",
|
||||||
|
action="store_true",
|
||||||
|
help="List installed modules and exit",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--dry-run",
|
||||||
|
action="store_true",
|
||||||
|
help="Show what would be removed without actually removing",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--purge",
|
||||||
|
action="store_true",
|
||||||
|
help="Remove entire install directory (DANGEROUS: removes user files too)",
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-y", "--yes",
|
||||||
|
action="store_true",
|
||||||
|
help="Skip confirmation prompt",
|
||||||
|
)
|
||||||
|
return parser.parse_args(argv)
|
||||||
|
|
||||||
|
|
||||||
|
def load_installed_modules(install_dir: Path) -> Dict[str, Any]:
|
||||||
|
"""Load installed_modules.json to know what was installed."""
|
||||||
|
status_file = install_dir / "installed_modules.json"
|
||||||
|
if not status_file.exists():
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
with status_file.open("r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
except (json.JSONDecodeError, OSError):
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def load_config(install_dir: Path) -> Dict[str, Any]:
|
||||||
|
"""Try to load config.json from source repo to understand module structure."""
|
||||||
|
# Look for config.json in common locations
|
||||||
|
candidates = [
|
||||||
|
Path(__file__).parent / "config.json",
|
||||||
|
install_dir / "config.json",
|
||||||
|
]
|
||||||
|
for path in candidates:
|
||||||
|
if path.exists():
|
||||||
|
try:
|
||||||
|
with path.open("r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
except (json.JSONDecodeError, OSError):
|
||||||
|
continue
|
||||||
|
return {}
|
||||||
|
|
||||||
|
|
||||||
|
def get_module_files(module_name: str, config: Dict[str, Any]) -> Set[str]:
|
||||||
|
"""Extract files/dirs that a module installs based on config.json operations."""
|
||||||
|
files: Set[str] = set()
|
||||||
|
modules = config.get("modules", {})
|
||||||
|
module_cfg = modules.get(module_name, {})
|
||||||
|
|
||||||
|
for op in module_cfg.get("operations", []):
|
||||||
|
op_type = op.get("type", "")
|
||||||
|
target = op.get("target", "")
|
||||||
|
|
||||||
|
if op_type == "copy_file" and target:
|
||||||
|
files.add(target)
|
||||||
|
elif op_type == "copy_dir" and target:
|
||||||
|
files.add(target)
|
||||||
|
elif op_type == "merge_dir":
|
||||||
|
# merge_dir merges subdirs like commands/, agents/ into install_dir
|
||||||
|
source = op.get("source", "")
|
||||||
|
source_path = Path(__file__).parent / source
|
||||||
|
if source_path.exists():
|
||||||
|
for subdir in source_path.iterdir():
|
||||||
|
if subdir.is_dir():
|
||||||
|
files.add(subdir.name)
|
||||||
|
elif op_type == "run_command":
|
||||||
|
# install.sh installs bin/codeagent-wrapper
|
||||||
|
cmd = op.get("command", "")
|
||||||
|
if "install.sh" in cmd or "install.bat" in cmd:
|
||||||
|
files.add("bin/codeagent-wrapper")
|
||||||
|
files.add("bin")
|
||||||
|
|
||||||
|
return files
|
||||||
|
|
||||||
|
|
||||||
|
def cleanup_shell_config(rc_file: Path, bin_dir: Path) -> bool:
|
||||||
|
"""Remove PATH export added by installer from shell config."""
|
||||||
|
if not rc_file.exists():
|
||||||
|
return False
|
||||||
|
|
||||||
|
content = rc_file.read_text(encoding="utf-8")
|
||||||
|
original = content
|
||||||
|
|
||||||
|
patterns = [
|
||||||
|
r"\n?# Added by myclaude installer\n",
|
||||||
|
rf'\nexport PATH="{re.escape(str(bin_dir))}:\$PATH"\n?',
|
||||||
|
]
|
||||||
|
|
||||||
|
for pattern in patterns:
|
||||||
|
content = re.sub(pattern, "\n", content)
|
||||||
|
|
||||||
|
content = re.sub(r"\n{3,}$", "\n\n", content)
|
||||||
|
|
||||||
|
if content != original:
|
||||||
|
rc_file.write_text(content, encoding="utf-8")
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def list_installed(install_dir: Path) -> None:
|
||||||
|
"""List installed modules."""
|
||||||
|
status = load_installed_modules(install_dir)
|
||||||
|
modules = status.get("modules", {})
|
||||||
|
|
||||||
|
if not modules:
|
||||||
|
print("No modules installed (installed_modules.json not found or empty)")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Installed modules in {install_dir}:")
|
||||||
|
print(f"{'Module':<15} {'Status':<10} {'Installed At'}")
|
||||||
|
print("-" * 50)
|
||||||
|
for name, info in modules.items():
|
||||||
|
st = info.get("status", "unknown")
|
||||||
|
ts = info.get("installed_at", "unknown")[:19]
|
||||||
|
print(f"{name:<15} {st:<10} {ts}")
|
||||||
|
|
||||||
|
|
||||||
|
def main(argv: Optional[List[str]] = None) -> int:
|
||||||
|
args = parse_args(argv)
|
||||||
|
install_dir = Path(args.install_dir).expanduser().resolve()
|
||||||
|
bin_dir = install_dir / "bin"
|
||||||
|
|
||||||
|
if not install_dir.exists():
|
||||||
|
print(f"Install directory not found: {install_dir}")
|
||||||
|
print("Nothing to uninstall.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if args.list:
|
||||||
|
list_installed(install_dir)
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Load installation status
|
||||||
|
status = load_installed_modules(install_dir)
|
||||||
|
installed_modules = status.get("modules", {})
|
||||||
|
config = load_config(install_dir)
|
||||||
|
|
||||||
|
# Determine which modules to uninstall
|
||||||
|
if args.module:
|
||||||
|
selected = [m.strip() for m in args.module.split(",") if m.strip()]
|
||||||
|
# Validate
|
||||||
|
for m in selected:
|
||||||
|
if m not in installed_modules:
|
||||||
|
print(f"Error: Module '{m}' is not installed")
|
||||||
|
print("Use --list to see installed modules")
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
selected = list(installed_modules.keys())
|
||||||
|
|
||||||
|
if not selected and not args.purge:
|
||||||
|
print("No modules to uninstall.")
|
||||||
|
print("Use --list to see installed modules, or --purge to remove everything.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
# Collect files to remove
|
||||||
|
files_to_remove: Set[str] = set()
|
||||||
|
for module_name in selected:
|
||||||
|
files_to_remove.update(get_module_files(module_name, config))
|
||||||
|
|
||||||
|
# Add installer files if removing all modules
|
||||||
|
if set(selected) == set(installed_modules.keys()):
|
||||||
|
files_to_remove.update(INSTALLER_FILES)
|
||||||
|
|
||||||
|
# Show what will be removed
|
||||||
|
print(f"Install directory: {install_dir}")
|
||||||
|
if args.purge:
|
||||||
|
print(f"\n⚠️ PURGE MODE: Will remove ENTIRE directory including user files!")
|
||||||
|
else:
|
||||||
|
print(f"\nModules to uninstall: {', '.join(selected)}")
|
||||||
|
print(f"\nFiles/directories to remove:")
|
||||||
|
for f in sorted(files_to_remove):
|
||||||
|
path = install_dir / f
|
||||||
|
exists = "✓" if path.exists() else "✗ (not found)"
|
||||||
|
print(f" {f} {exists}")
|
||||||
|
|
||||||
|
# Confirmation
|
||||||
|
if not args.yes and not args.dry_run:
|
||||||
|
prompt = "\nProceed with uninstallation? [y/N] "
|
||||||
|
response = input(prompt).strip().lower()
|
||||||
|
if response not in ("y", "yes"):
|
||||||
|
print("Aborted.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if args.dry_run:
|
||||||
|
print("\n[Dry run] No files were removed.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
print(f"\nUninstalling...")
|
||||||
|
removed: List[str] = []
|
||||||
|
|
||||||
|
if args.purge:
|
||||||
|
shutil.rmtree(install_dir)
|
||||||
|
print(f" ✓ Removed {install_dir}")
|
||||||
|
removed.append(str(install_dir))
|
||||||
|
else:
|
||||||
|
# Remove files/dirs in reverse order (files before parent dirs)
|
||||||
|
for item in sorted(files_to_remove, key=lambda x: x.count("/"), reverse=True):
|
||||||
|
path = install_dir / item
|
||||||
|
if not path.exists():
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
if path.is_dir():
|
||||||
|
# Only remove if empty or if it's a known module dir
|
||||||
|
if item in ("bin",):
|
||||||
|
# For bin, only remove codeagent-wrapper
|
||||||
|
wrapper = path / "codeagent-wrapper"
|
||||||
|
if wrapper.exists():
|
||||||
|
wrapper.unlink()
|
||||||
|
print(f" ✓ Removed bin/codeagent-wrapper")
|
||||||
|
removed.append("bin/codeagent-wrapper")
|
||||||
|
# Remove bin if empty
|
||||||
|
if path.exists() and not any(path.iterdir()):
|
||||||
|
path.rmdir()
|
||||||
|
print(f" ✓ Removed empty bin/")
|
||||||
|
else:
|
||||||
|
shutil.rmtree(path)
|
||||||
|
print(f" ✓ Removed {item}/")
|
||||||
|
removed.append(item)
|
||||||
|
else:
|
||||||
|
path.unlink()
|
||||||
|
print(f" ✓ Removed {item}")
|
||||||
|
removed.append(item)
|
||||||
|
except OSError as e:
|
||||||
|
print(f" ✗ Failed to remove {item}: {e}", file=sys.stderr)
|
||||||
|
|
||||||
|
# Update installed_modules.json
|
||||||
|
status_file = install_dir / "installed_modules.json"
|
||||||
|
if status_file.exists() and selected != list(installed_modules.keys()):
|
||||||
|
# Partial uninstall: update status file
|
||||||
|
for m in selected:
|
||||||
|
installed_modules.pop(m, None)
|
||||||
|
if installed_modules:
|
||||||
|
with status_file.open("w", encoding="utf-8") as f:
|
||||||
|
json.dump({"modules": installed_modules}, f, indent=2)
|
||||||
|
print(f" ✓ Updated installed_modules.json")
|
||||||
|
|
||||||
|
# Remove install dir if empty
|
||||||
|
if install_dir.exists() and not any(install_dir.iterdir()):
|
||||||
|
install_dir.rmdir()
|
||||||
|
print(f" ✓ Removed empty install directory")
|
||||||
|
|
||||||
|
# Clean shell configs
|
||||||
|
for rc_name in (".bashrc", ".zshrc"):
|
||||||
|
rc_file = Path.home() / rc_name
|
||||||
|
if cleanup_shell_config(rc_file, bin_dir):
|
||||||
|
print(f" ✓ Cleaned PATH from {rc_name}")
|
||||||
|
|
||||||
|
print("")
|
||||||
|
if removed:
|
||||||
|
print(f"✓ Uninstallation complete ({len(removed)} items removed)")
|
||||||
|
else:
|
||||||
|
print("✓ Nothing to remove")
|
||||||
|
|
||||||
|
if install_dir.exists() and any(install_dir.iterdir()):
|
||||||
|
remaining = list(install_dir.iterdir())
|
||||||
|
print(f"\nNote: {len(remaining)} items remain in {install_dir}")
|
||||||
|
print("These are either user files or from other modules.")
|
||||||
|
print("Use --purge to remove everything (DANGEROUS).")
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
||||||
225
uninstall.sh
Executable file
225
uninstall.sh
Executable file
@@ -0,0 +1,225 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
set -e
|
||||||
|
|
||||||
|
INSTALL_DIR="${INSTALL_DIR:-$HOME/.claude}"
|
||||||
|
BIN_DIR="${INSTALL_DIR}/bin"
|
||||||
|
STATUS_FILE="${INSTALL_DIR}/installed_modules.json"
|
||||||
|
DRY_RUN=false
|
||||||
|
PURGE=false
|
||||||
|
YES=false
|
||||||
|
LIST_ONLY=false
|
||||||
|
MODULES=""
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<EOF
|
||||||
|
Usage: $0 [OPTIONS]
|
||||||
|
|
||||||
|
Uninstall myclaude modules.
|
||||||
|
|
||||||
|
Options:
|
||||||
|
--install-dir DIR Installation directory (default: ~/.claude)
|
||||||
|
--module MODULES Comma-separated modules to uninstall (default: all)
|
||||||
|
--list List installed modules and exit
|
||||||
|
--dry-run Show what would be removed without removing
|
||||||
|
--purge Remove entire install directory (DANGEROUS)
|
||||||
|
-y, --yes Skip confirmation prompt
|
||||||
|
-h, --help Show this help
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
$0 --list # List installed modules
|
||||||
|
$0 --dry-run # Preview what would be removed
|
||||||
|
$0 --module dev # Uninstall only 'dev' module
|
||||||
|
$0 -y # Uninstall all without confirmation
|
||||||
|
$0 --purge -y # Remove everything (DANGEROUS)
|
||||||
|
EOF
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
# Parse arguments
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case $1 in
|
||||||
|
--install-dir) INSTALL_DIR="$2"; BIN_DIR="${INSTALL_DIR}/bin"; STATUS_FILE="${INSTALL_DIR}/installed_modules.json"; shift 2 ;;
|
||||||
|
--module) MODULES="$2"; shift 2 ;;
|
||||||
|
--list) LIST_ONLY=true; shift ;;
|
||||||
|
--dry-run) DRY_RUN=true; shift ;;
|
||||||
|
--purge) PURGE=true; shift ;;
|
||||||
|
-y|--yes) YES=true; shift ;;
|
||||||
|
-h|--help) usage ;;
|
||||||
|
*) echo "Unknown option: $1" >&2; exit 1 ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
# Check if install dir exists
|
||||||
|
if [ ! -d "$INSTALL_DIR" ]; then
|
||||||
|
echo "Install directory not found: $INSTALL_DIR"
|
||||||
|
echo "Nothing to uninstall."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# List installed modules
|
||||||
|
list_modules() {
|
||||||
|
if [ ! -f "$STATUS_FILE" ]; then
|
||||||
|
echo "No modules installed (installed_modules.json not found)"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "Installed modules in $INSTALL_DIR:"
|
||||||
|
echo "Module Status Installed At"
|
||||||
|
echo "--------------------------------------------------"
|
||||||
|
# Parse JSON with basic tools (no jq dependency)
|
||||||
|
python3 -c "
|
||||||
|
import json, sys
|
||||||
|
try:
|
||||||
|
with open('$STATUS_FILE') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
for name, info in data.get('modules', {}).items():
|
||||||
|
status = info.get('status', 'unknown')
|
||||||
|
ts = info.get('installed_at', 'unknown')[:19]
|
||||||
|
print(f'{name:<15} {status:<10} {ts}')
|
||||||
|
except Exception as e:
|
||||||
|
print(f'Error reading status file: {e}', file=sys.stderr)
|
||||||
|
sys.exit(1)
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$LIST_ONLY" = true ]; then
|
||||||
|
list_modules
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Get installed modules from status file
|
||||||
|
get_installed_modules() {
|
||||||
|
if [ ! -f "$STATUS_FILE" ]; then
|
||||||
|
echo ""
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
python3 -c "
|
||||||
|
import json
|
||||||
|
try:
|
||||||
|
with open('$STATUS_FILE') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
print(' '.join(data.get('modules', {}).keys()))
|
||||||
|
except:
|
||||||
|
print('')
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
INSTALLED=$(get_installed_modules)
|
||||||
|
|
||||||
|
# Determine modules to uninstall
|
||||||
|
if [ -n "$MODULES" ]; then
|
||||||
|
SELECTED="$MODULES"
|
||||||
|
else
|
||||||
|
SELECTED="$INSTALLED"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$SELECTED" ] && [ "$PURGE" != true ]; then
|
||||||
|
echo "No modules to uninstall."
|
||||||
|
echo "Use --list to see installed modules, or --purge to remove everything."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Install directory: $INSTALL_DIR"
|
||||||
|
|
||||||
|
if [ "$PURGE" = true ]; then
|
||||||
|
echo ""
|
||||||
|
echo "⚠️ PURGE MODE: Will remove ENTIRE directory including user files!"
|
||||||
|
else
|
||||||
|
echo ""
|
||||||
|
echo "Modules to uninstall: $SELECTED"
|
||||||
|
echo ""
|
||||||
|
echo "Files/directories that may be removed:"
|
||||||
|
for item in commands agents skills docs bin CLAUDE.md install.log installed_modules.json; do
|
||||||
|
if [ -e "${INSTALL_DIR}/${item}" ]; then
|
||||||
|
echo " $item ✓"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Confirmation
|
||||||
|
if [ "$YES" != true ] && [ "$DRY_RUN" != true ]; then
|
||||||
|
echo ""
|
||||||
|
read -p "Proceed with uninstallation? [y/N] " response
|
||||||
|
case "$response" in
|
||||||
|
[yY]|[yY][eE][sS]) ;;
|
||||||
|
*) echo "Aborted."; exit 0 ;;
|
||||||
|
esac
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" = true ]; then
|
||||||
|
echo ""
|
||||||
|
echo "[Dry run] No files were removed."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Uninstalling..."
|
||||||
|
|
||||||
|
if [ "$PURGE" = true ]; then
|
||||||
|
rm -rf "$INSTALL_DIR"
|
||||||
|
echo " ✓ Removed $INSTALL_DIR"
|
||||||
|
else
|
||||||
|
# Remove codeagent-wrapper binary
|
||||||
|
if [ -f "${BIN_DIR}/codeagent-wrapper" ]; then
|
||||||
|
rm -f "${BIN_DIR}/codeagent-wrapper"
|
||||||
|
echo " ✓ Removed bin/codeagent-wrapper"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove bin directory if empty
|
||||||
|
if [ -d "$BIN_DIR" ] && [ -z "$(ls -A "$BIN_DIR" 2>/dev/null)" ]; then
|
||||||
|
rmdir "$BIN_DIR"
|
||||||
|
echo " ✓ Removed empty bin/"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Remove installed directories
|
||||||
|
for dir in commands agents skills docs; do
|
||||||
|
if [ -d "${INSTALL_DIR}/${dir}" ]; then
|
||||||
|
rm -rf "${INSTALL_DIR}/${dir}"
|
||||||
|
echo " ✓ Removed ${dir}/"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove installed files
|
||||||
|
for file in CLAUDE.md install.log installed_modules.json installed_modules.json.bak; do
|
||||||
|
if [ -f "${INSTALL_DIR}/${file}" ]; then
|
||||||
|
rm -f "${INSTALL_DIR}/${file}"
|
||||||
|
echo " ✓ Removed ${file}"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove install directory if empty
|
||||||
|
if [ -d "$INSTALL_DIR" ] && [ -z "$(ls -A "$INSTALL_DIR" 2>/dev/null)" ]; then
|
||||||
|
rmdir "$INSTALL_DIR"
|
||||||
|
echo " ✓ Removed empty install directory"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Clean up PATH from shell config files
|
||||||
|
cleanup_shell_config() {
|
||||||
|
local rc_file="$1"
|
||||||
|
if [ -f "$rc_file" ]; then
|
||||||
|
if grep -q "# Added by myclaude installer" "$rc_file" 2>/dev/null; then
|
||||||
|
# Create backup
|
||||||
|
cp "$rc_file" "${rc_file}.bak"
|
||||||
|
# Remove myclaude lines
|
||||||
|
grep -v "# Added by myclaude installer" "$rc_file" | \
|
||||||
|
grep -v "export PATH=\"${BIN_DIR}:\$PATH\"" > "${rc_file}.tmp"
|
||||||
|
mv "${rc_file}.tmp" "$rc_file"
|
||||||
|
echo " ✓ Cleaned PATH from $(basename "$rc_file")"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup_shell_config "$HOME/.bashrc"
|
||||||
|
cleanup_shell_config "$HOME/.zshrc"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "✓ Uninstallation complete"
|
||||||
|
|
||||||
|
# Check for remaining files
|
||||||
|
if [ -d "$INSTALL_DIR" ] && [ -n "$(ls -A "$INSTALL_DIR" 2>/dev/null)" ]; then
|
||||||
|
remaining=$(ls -1 "$INSTALL_DIR" 2>/dev/null | wc -l | tr -d ' ')
|
||||||
|
echo ""
|
||||||
|
echo "Note: $remaining items remain in $INSTALL_DIR"
|
||||||
|
echo "These are either user files or from other modules."
|
||||||
|
echo "Use --purge to remove everything (DANGEROUS)."
|
||||||
|
fi
|
||||||
Reference in New Issue
Block a user