#!/bin/bash # uninstall-fabric.sh # # Description: Safely uninstalls the Fabric AI CLI (Daniel Miessler) and related configuration. # Avoids removing the 'fabric' Python deployment library. # Detects OS and uses appropriate package managers if applicable. # Logs all actions to a file. # # Usage: ./uninstall-fabric.sh # # Author: GitHub Copilot set -u # Configuration LOG_FILE="uninstall-fabric.log" CURRENT_DATE=$(date +'%Y-%m-%d %H:%M:%S') # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Initialize log file echo "Fabric AI CLI Uninstallation Log - Started at $CURRENT_DATE" > "$LOG_FILE" # Logging functions log() { local message="$1" echo -e "[$(date +'%H:%M:%S')] $message" | tee -a "$LOG_FILE" } info() { local message="$1" echo -e "${BLUE}[INFO]${NC} $message" | tee -a "$LOG_FILE" } success() { local message="$1" echo -e "${GREEN}[SUCCESS]${NC} $message" | tee -a "$LOG_FILE" } warning() { local message="$1" echo -e "${YELLOW}[WARNING]${NC} $message" | tee -a "$LOG_FILE" } error() { local message="$1" echo -e "${RED}[ERROR]${NC} $message" | tee -a "$LOG_FILE" exit 1 } # Function to detect Operating System detect_os() { if [[ -f /etc/os-release ]]; then # shellcheck source=/dev/null . /etc/os-release OS_NAME=$ID VERSION_ID=$VERSION_ID info "Detected OS: $NAME ($ID) $VERSION_ID" else error "Could not detect operating system. /etc/os-release file not found." fi } # Function to check for root privileges check_privileges() { if [[ $EUID -ne 0 ]]; then warning "This script is not running as root." warning "System package removal might fail or require sudo password." else info "Running with root privileges." fi } # Function to confirm action confirm_execution() { echo -e "\n${YELLOW}WARNING: This script will attempt to uninstall the Fabric AI CLI (Daniel Miessler).${NC}" echo -e "It will NOT remove the 'fabric' Python deployment library." echo -e "It will remove the 'fabric' binary if identified as the AI tool, and configuration files." echo -e "Please ensure you have backups if necessary.\n" read -p "Do you want to proceed? (y/N): " -n 1 -r echo if [[ ! $REPLY =~ ^[Yy]$ ]]; then info "Operation cancelled by user." exit 0 fi } # Function to check if a binary is the Fabric AI tool is_fabric_ai_tool() { local bin_path="$1" # Check help output for keywords # The AI tool usually mentions 'patterns', 'context', 'session', 'model' if "$bin_path" --help 2>&1 | grep -qE "Daniel Miessler|patterns|context|session|model"; then return 0 fi return 1 } # Function to uninstall binary uninstall_binary() { local bin_path bin_path=$(command -v fabric) if [[ -n "$bin_path" ]]; then info "Found 'fabric' binary at: $bin_path" if is_fabric_ai_tool "$bin_path"; then info "Identified as Fabric AI CLI." # Check if owned by system package local pkg_owner="" if [[ "$OS_NAME" =~ (debian|ubuntu|linuxmint|pop|kali) ]]; then if dpkg -S "$bin_path" &> /dev/null; then pkg_owner=$(dpkg -S "$bin_path" | cut -d: -f1) fi elif [[ "$OS_NAME" =~ (fedora|centos|rhel|almalinux|rocky) ]]; then if rpm -qf "$bin_path" &> /dev/null; then pkg_owner=$(rpm -qf "$bin_path") fi fi if [[ -n "$pkg_owner" ]]; then info "Binary is owned by system package: $pkg_owner" info "Removing package $pkg_owner..." local sudo_prefix="" [[ $EUID -ne 0 ]] && sudo_prefix="sudo" if [[ "$OS_NAME" =~ (debian|ubuntu|linuxmint|pop|kali) ]]; then $sudo_prefix apt-get remove -y "$pkg_owner" >> "$LOG_FILE" 2>&1 || error "Failed to remove package $pkg_owner" else $sudo_prefix dnf remove -y "$pkg_owner" >> "$LOG_FILE" 2>&1 || error "Failed to remove package $pkg_owner" fi success "Removed system package $pkg_owner." else info "Binary is not owned by a system package. Removing manually..." rm -f "$bin_path" || error "Failed to remove $bin_path" success "Removed binary $bin_path." fi else warning "The binary at $bin_path does not appear to be the Fabric AI CLI. Skipping removal to be safe." warning "Run '$bin_path --help' to verify what it is." fi else info "'fabric' binary not found in PATH." fi } # Function to uninstall from pipx uninstall_pipx() { if command -v pipx &> /dev/null; then info "Checking pipx for 'fabric'..." if pipx list | grep -q "package fabric"; then info "Found 'fabric' installed via pipx. Uninstalling..." pipx uninstall fabric >> "$LOG_FILE" 2>&1 || error "Failed to uninstall fabric via pipx" success "Uninstalled fabric via pipx." else info "'fabric' not found in pipx." fi fi } # Function to remove configuration files remove_config() { local config_dirs=( "$HOME/.config/fabric" "$HOME/.fabric" "$HOME/.local/share/fabric" ) for dir in "${config_dirs[@]}"; do if [[ -d "$dir" ]]; then info "Found configuration directory: $dir" rm -rf "$dir" || error "Failed to remove $dir" success "Removed $dir." fi done } # Main execution flow main() { detect_os check_privileges confirm_execution info "Starting uninstallation process..." # Check pipx first as it manages its own binaries uninstall_pipx # Check binary uninstall_binary # Remove config remove_config echo -e "\n----------------------------------------------------------------" success "Uninstallation steps completed." info "A log of this operation has been saved to: $LOG_FILE" echo -e "${YELLOW}Note: If you removed system-level components, a reboot might be recommended.${NC}" echo -e "----------------------------------------------------------------" } # Trap interrupts trap 'echo -e "\n${RED}Script interrupted by user.${NC}"; exit 1' INT TERM # Run main main