#!/bin/bash #=============================================================================== # # Claude Code Installer for Linux # A colorized, interactive installer with full system detection # # Features: # - Detects Linux distribution and version # - Checks/removes existing Node.js and npm installations # - Installs Node.js 20 LTS # - Installs Claude Code # - Configures permissions interactively # #=============================================================================== # Exit on error set -e #------------------------------------------------------------------------------- # Color Definitions #------------------------------------------------------------------------------- RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' MAGENTA='\033[0;35m' CYAN='\033[0;36m' WHITE='\033[1;37m' BOLD='\033[1m' DIM='\033[2m' RESET='\033[0m' # Status icons CHECK="βœ“" CROSS="βœ—" ARROW="➜" GEAR="βš™" PACKAGE="πŸ“¦" ROCKET="πŸš€" SHIELD="πŸ›‘" WARNING="⚠" INFO="β„Ή" #------------------------------------------------------------------------------- # Helper Functions #------------------------------------------------------------------------------- print_banner() { clear echo -e "${CYAN}" echo "╔═══════════════════════════════════════════════════════════════════════════╗" echo "β•‘ β•‘" echo "β•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ•— β–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β•‘" echo "β•‘ β–ˆβ–ˆβ•”β•β•β•β•β•β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•”β•β•β•β•β• β•‘" echo "β•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β•‘" echo "β•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β• β•‘" echo "β•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•”β•β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•— β•‘" echo "β•‘ β•šβ•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β• β•šβ•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β• β•šβ•β•β•β•β•β•β• β•‘" echo "β•‘ β•‘" echo "β•‘ ${WHITE}C O D E I N S T A L L E R${CYAN} β•‘" echo "β•‘ β•‘" echo "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•" echo -e "${RESET}" echo "" } print_section() { echo "" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo -e "${BOLD}${WHITE} $1${RESET}" echo -e "${BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo "" } print_step() { echo -e "${CYAN}${ARROW}${RESET} ${WHITE}$1${RESET}" } print_success() { echo -e "${GREEN}${CHECK}${RESET} ${GREEN}$1${RESET}" } print_error() { echo -e "${RED}${CROSS}${RESET} ${RED}$1${RESET}" } print_warning() { echo -e "${YELLOW}${WARNING}${RESET} ${YELLOW}$1${RESET}" } print_info() { echo -e "${BLUE}${INFO}${RESET} ${DIM}$1${RESET}" } print_package() { echo -e "${MAGENTA}${PACKAGE}${RESET} $1" } spinner() { local pid=$1 local delay=0.1 local spinstr='⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏' while [ "$(ps a | awk '{print $1}' | grep $pid)" ]; do local temp=${spinstr#?} printf " ${CYAN}[%c]${RESET} " "$spinstr" local spinstr=$temp${spinstr%"$temp"} sleep $delay printf "\b\b\b\b\b\b" done printf " \b\b\b\b" } confirm() { local prompt="$1" local default="${2:-y}" if [[ "$default" == "y" ]]; then prompt_text="${prompt} [${GREEN}Y${RESET}/${RED}n${RESET}]" else prompt_text="${prompt} [${GREEN}y${RESET}/${RED}N${RESET}]" fi echo -ne "${YELLOW}?${RESET} ${prompt_text}: " read -r response if [[ -z "$response" ]]; then response="$default" fi [[ "$response" =~ ^[Yy]$ ]] } wait_for_key() { echo "" echo -ne "${DIM}Press any key to continue...${RESET}" read -n 1 -s echo "" } #------------------------------------------------------------------------------- # System Detection Functions #------------------------------------------------------------------------------- detect_distro() { print_section "${GEAR} System Detection" print_step "Identifying your Linux distribution..." sleep 0.5 # Initialize variables DISTRO="Unknown" DISTRO_VERSION="Unknown" DISTRO_CODENAME="Unknown" DISTRO_FAMILY="Unknown" PACKAGE_MANAGER="Unknown" # Try to detect from /etc/os-release (most modern distros) if [[ -f /etc/os-release ]]; then source /etc/os-release DISTRO="${NAME:-Unknown}" DISTRO_VERSION="${VERSION_ID:-Unknown}" DISTRO_CODENAME="${VERSION_CODENAME:-Unknown}" # Determine distro family and package manager case "${ID:-}" in ubuntu|debian|linuxmint|pop|elementary|zorin|kali) DISTRO_FAMILY="Debian" PACKAGE_MANAGER="apt" ;; fedora|rhel|centos|rocky|almalinux|oracle) DISTRO_FAMILY="RedHat" if command -v dnf &> /dev/null; then PACKAGE_MANAGER="dnf" else PACKAGE_MANAGER="yum" fi ;; arch|manjaro|endeavouros|garuda) DISTRO_FAMILY="Arch" PACKAGE_MANAGER="pacman" ;; opensuse*|sles) DISTRO_FAMILY="SUSE" PACKAGE_MANAGER="zypper" ;; alpine) DISTRO_FAMILY="Alpine" PACKAGE_MANAGER="apk" ;; *) DISTRO_FAMILY="Unknown" ;; esac # Fallback detection methods elif [[ -f /etc/lsb-release ]]; then source /etc/lsb-release DISTRO="${DISTRIB_ID:-Unknown}" DISTRO_VERSION="${DISTRIB_RELEASE:-Unknown}" DISTRO_CODENAME="${DISTRIB_CODENAME:-Unknown}" DISTRO_FAMILY="Debian" PACKAGE_MANAGER="apt" elif [[ -f /etc/redhat-release ]]; then DISTRO=$(cat /etc/redhat-release | cut -d' ' -f1) DISTRO_VERSION=$(cat /etc/redhat-release | grep -oE '[0-9]+\.[0-9]+' | head -1) DISTRO_FAMILY="RedHat" PACKAGE_MANAGER="yum" fi # Get kernel and architecture info KERNEL_VERSION=$(uname -r) ARCH=$(uname -m) # Display detected information echo "" echo -e " ${WHITE}β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”${RESET}" echo -e " ${WHITE}β”‚${RESET} ${BOLD}System Information${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}Distribution:${RESET} ${GREEN}$DISTRO${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}Version:${RESET} ${GREEN}$DISTRO_VERSION${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}Codename:${RESET} ${GREEN}$DISTRO_CODENAME${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}Family:${RESET} ${GREEN}$DISTRO_FAMILY${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}Package Manager:${RESET} ${GREEN}$PACKAGE_MANAGER${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}Kernel:${RESET} ${GREEN}$KERNEL_VERSION${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}Architecture:${RESET} ${GREEN}$ARCH${RESET}" echo -e " ${WHITE}β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜${RESET}" echo "" # Check if distribution is supported if [[ "$PACKAGE_MANAGER" == "Unknown" ]]; then print_error "Unsupported Linux distribution detected!" print_info "This installer supports: Debian/Ubuntu, Fedora/RHEL/CentOS, Arch, openSUSE, Alpine" exit 1 fi print_success "Distribution detected successfully!" sleep 1 } #------------------------------------------------------------------------------- # Node.js Detection and Installation #------------------------------------------------------------------------------- check_existing_node() { print_section "${PACKAGE} Node.js & npm Detection" print_step "Checking for existing Node.js installation..." sleep 0.5 NODE_INSTALLED=false NPM_INSTALLED=false NODE_VERSION="" NPM_VERSION="" NEEDS_UPGRADE=false # Check Node.js if command -v node &> /dev/null; then NODE_INSTALLED=true NODE_VERSION=$(node --version 2>/dev/null || echo "error") if [[ "$NODE_VERSION" == "error" ]]; then NODE_VERSION="Broken installation" NEEDS_UPGRADE=true fi fi # Check npm if command -v npm &> /dev/null; then NPM_INSTALLED=true NPM_VERSION=$(npm --version 2>/dev/null || echo "error") if [[ "$NPM_VERSION" == "error" ]]; then NPM_VERSION="Broken installation" NEEDS_UPGRADE=true fi fi # Display current status echo "" echo -e " ${WHITE}β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”${RESET}" echo -e " ${WHITE}β”‚${RESET} ${BOLD}Current Installation Status${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€${RESET}" if [[ "$NODE_INSTALLED" == true ]]; then # Check if version is sufficient (need v18+) NODE_MAJOR=$(echo "$NODE_VERSION" | sed 's/v//' | cut -d'.' -f1) if [[ "$NODE_MAJOR" =~ ^[0-9]+$ ]] && [[ "$NODE_MAJOR" -ge 18 ]]; then echo -e " ${WHITE}β”‚${RESET} ${GREEN}${CHECK}${RESET} Node.js: ${GREEN}$NODE_VERSION${RESET} (Compatible)" else echo -e " ${WHITE}β”‚${RESET} ${YELLOW}${WARNING}${RESET} Node.js: ${YELLOW}$NODE_VERSION${RESET} (Needs upgrade to v18+)" NEEDS_UPGRADE=true fi else echo -e " ${WHITE}β”‚${RESET} ${RED}${CROSS}${RESET} Node.js: ${RED}Not installed${RESET}" NEEDS_UPGRADE=true fi if [[ "$NPM_INSTALLED" == true ]]; then echo -e " ${WHITE}β”‚${RESET} ${GREEN}${CHECK}${RESET} npm: ${GREEN}v$NPM_VERSION${RESET}" else echo -e " ${WHITE}β”‚${RESET} ${RED}${CROSS}${RESET} npm: ${RED}Not installed${RESET}" fi echo -e " ${WHITE}β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜${RESET}" echo "" # Determine action needed if [[ "$NODE_INSTALLED" == true ]] && [[ "$NEEDS_UPGRADE" == true ]]; then print_warning "Your Node.js version is incompatible with Claude Code." print_info "Claude Code requires Node.js version 18.0.0 or higher." echo "" if confirm "Would you like to remove the existing installation and install Node.js 20 LTS?"; then remove_existing_node install_node else print_error "Cannot proceed without Node.js 18+. Exiting." exit 1 fi elif [[ "$NODE_INSTALLED" == false ]]; then print_info "Node.js is not installed on your system." echo "" if confirm "Would you like to install Node.js 20 LTS?"; then install_node else print_error "Cannot proceed without Node.js. Exiting." exit 1 fi else print_success "Node.js installation is compatible!" echo "" if confirm "Would you like to reinstall Node.js anyway (fresh installation)?" "n"; then remove_existing_node install_node fi fi } remove_existing_node() { print_section "${GEAR} Removing Existing Node.js Installation" print_step "Removing Node.js, npm, and related packages..." echo "" case "$PACKAGE_MANAGER" in apt) print_info "Removing Debian/Ubuntu packages..." # List of packages to remove PACKAGES_TO_REMOVE="nodejs npm libnode-dev libnode72 nodejs-doc" for pkg in $PACKAGES_TO_REMOVE; do if dpkg -l | grep -q "^ii $pkg"; then echo -ne " ${YELLOW}β†’${RESET} Removing ${CYAN}$pkg${RESET}..." sudo apt remove -y $pkg > /dev/null 2>&1 || true echo -e " ${GREEN}done${RESET}" fi done # Remove NodeSource list if exists if [[ -f /etc/apt/sources.list.d/nodesource.list ]]; then echo -ne " ${YELLOW}β†’${RESET} Removing old NodeSource repository..." sudo rm -f /etc/apt/sources.list.d/nodesource.list echo -e " ${GREEN}done${RESET}" fi # Clean up echo -ne " ${YELLOW}β†’${RESET} Running autoremove..." sudo apt autoremove -y > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" echo -ne " ${YELLOW}β†’${RESET} Cleaning apt cache..." sudo apt clean > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" ;; dnf|yum) print_info "Removing Fedora/RHEL packages..." echo -ne " ${YELLOW}β†’${RESET} Removing nodejs and npm..." sudo $PACKAGE_MANAGER remove -y nodejs npm > /dev/null 2>&1 || true echo -e " ${GREEN}done${RESET}" ;; pacman) print_info "Removing Arch packages..." echo -ne " ${YELLOW}β†’${RESET} Removing nodejs and npm..." sudo pacman -Rns --noconfirm nodejs npm > /dev/null 2>&1 || true echo -e " ${GREEN}done${RESET}" ;; zypper) print_info "Removing openSUSE packages..." echo -ne " ${YELLOW}β†’${RESET} Removing nodejs and npm..." sudo zypper remove -y nodejs npm > /dev/null 2>&1 || true echo -e " ${GREEN}done${RESET}" ;; apk) print_info "Removing Alpine packages..." echo -ne " ${YELLOW}β†’${RESET} Removing nodejs and npm..." sudo apk del nodejs npm > /dev/null 2>&1 || true echo -e " ${GREEN}done${RESET}" ;; esac # Remove global npm packages location if [[ -d /usr/local/lib/node_modules ]]; then echo -ne " ${YELLOW}β†’${RESET} Cleaning global node_modules..." sudo rm -rf /usr/local/lib/node_modules echo -e " ${GREEN}done${RESET}" fi # Remove npm cache if [[ -d ~/.npm ]]; then echo -ne " ${YELLOW}β†’${RESET} Cleaning npm cache..." rm -rf ~/.npm echo -e " ${GREEN}done${RESET}" fi # Remove nvm if exists if [[ -d ~/.nvm ]]; then if confirm " Found nvm installation. Remove it too?" "n"; then rm -rf ~/.nvm # Remove nvm lines from shell configs sed -i '/NVM_DIR/d' ~/.bashrc 2>/dev/null || true sed -i '/nvm.sh/d' ~/.bashrc 2>/dev/null || true sed -i '/NVM_DIR/d' ~/.zshrc 2>/dev/null || true sed -i '/nvm.sh/d' ~/.zshrc 2>/dev/null || true print_success "nvm removed" fi fi echo "" print_success "Existing Node.js installation removed!" sleep 1 } install_node() { print_section "${PACKAGE} Installing Node.js 20 LTS" print_step "Preparing to install Node.js 20 LTS..." echo "" case "$PACKAGE_MANAGER" in apt) print_info "Using NodeSource repository for Debian/Ubuntu..." echo "" # Install prerequisites echo -ne " ${YELLOW}β†’${RESET} Installing prerequisites..." sudo apt update > /dev/null 2>&1 sudo apt install -y ca-certificates curl gnupg > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" # Add NodeSource repository echo -ne " ${YELLOW}β†’${RESET} Adding NodeSource repository..." curl -fsSL https://deb.nodesource.com/setup_20.x 2>/dev/null | sudo -E bash - > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" # Install Node.js echo -e " ${YELLOW}β†’${RESET} Installing Node.js 20 LTS..." sudo apt install -y nodejs 2>&1 | while read line; do echo -e " ${DIM}$line${RESET}" done ;; dnf) print_info "Using NodeSource repository for Fedora..." echo "" echo -ne " ${YELLOW}β†’${RESET} Adding NodeSource repository..." curl -fsSL https://rpm.nodesource.com/setup_20.x 2>/dev/null | sudo bash - > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" echo -e " ${YELLOW}β†’${RESET} Installing Node.js 20 LTS..." sudo dnf install -y nodejs 2>&1 | while read line; do echo -e " ${DIM}$line${RESET}" done ;; yum) print_info "Using NodeSource repository for RHEL/CentOS..." echo "" echo -ne " ${YELLOW}β†’${RESET} Adding NodeSource repository..." curl -fsSL https://rpm.nodesource.com/setup_20.x 2>/dev/null | sudo bash - > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" echo -e " ${YELLOW}β†’${RESET} Installing Node.js 20 LTS..." sudo yum install -y nodejs 2>&1 | while read line; do echo -e " ${DIM}$line${RESET}" done ;; pacman) print_info "Installing from Arch repositories..." echo "" echo -ne " ${YELLOW}β†’${RESET} Updating package database..." sudo pacman -Sy > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" echo -e " ${YELLOW}β†’${RESET} Installing Node.js..." sudo pacman -S --noconfirm nodejs npm 2>&1 | while read line; do echo -e " ${DIM}$line${RESET}" done ;; zypper) print_info "Installing from openSUSE repositories..." echo "" echo -ne " ${YELLOW}β†’${RESET} Refreshing repositories..." sudo zypper refresh > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" echo -e " ${YELLOW}β†’${RESET} Installing Node.js..." sudo zypper install -y nodejs20 npm20 2>&1 | while read line; do echo -e " ${DIM}$line${RESET}" done ;; apk) print_info "Installing from Alpine repositories..." echo "" echo -ne " ${YELLOW}β†’${RESET} Updating package index..." sudo apk update > /dev/null 2>&1 echo -e " ${GREEN}done${RESET}" echo -e " ${YELLOW}β†’${RESET} Installing Node.js..." sudo apk add nodejs npm 2>&1 | while read line; do echo -e " ${DIM}$line${RESET}" done ;; esac echo "" # Rehash command paths hash -r 2>/dev/null || true print_success "Node.js installation completed!" sleep 1 } verify_node_installation() { print_section "${CHECK} Verifying Installation" print_step "Checking Node.js and npm versions..." echo "" sleep 0.5 # Rehash to ensure we get fresh paths hash -r 2>/dev/null || true # Verify Node.js if command -v node &> /dev/null; then NEW_NODE_VERSION=$(node --version) NODE_MAJOR=$(echo "$NEW_NODE_VERSION" | sed 's/v//' | cut -d'.' -f1) if [[ "$NODE_MAJOR" -ge 18 ]]; then echo -e " ${GREEN}${CHECK}${RESET} Node.js: ${GREEN}${BOLD}$NEW_NODE_VERSION${RESET} ${GREEN}(Compatible!)${RESET}" NODE_OK=true else echo -e " ${RED}${CROSS}${RESET} Node.js: ${RED}$NEW_NODE_VERSION (Too old - need v18+)${RESET}" NODE_OK=false fi else echo -e " ${RED}${CROSS}${RESET} Node.js: ${RED}Not found!${RESET}" NODE_OK=false fi # Verify npm if command -v npm &> /dev/null; then NEW_NPM_VERSION=$(npm --version) echo -e " ${GREEN}${CHECK}${RESET} npm: ${GREEN}${BOLD}v$NEW_NPM_VERSION${RESET}" NPM_OK=true else echo -e " ${RED}${CROSS}${RESET} npm: ${RED}Not found!${RESET}" NPM_OK=false fi # Show paths echo "" echo -e " ${DIM}Node path: $(which node 2>/dev/null || echo 'Not found')${RESET}" echo -e " ${DIM}npm path: $(which npm 2>/dev/null || echo 'Not found')${RESET}" echo "" if [[ "$NODE_OK" == true ]] && [[ "$NPM_OK" == true ]]; then print_success "All requirements verified successfully!" return 0 else print_error "Verification failed! Please check the installation." return 1 fi } #------------------------------------------------------------------------------- # Claude Code Installation #------------------------------------------------------------------------------- install_claude_code() { print_section "${ROCKET} Installing Claude Code" print_step "Installing Claude Code via npm..." echo "" # Create npm global directory in user space to avoid permission issues if [[ ! -d ~/.npm-global ]]; then mkdir -p ~/.npm-global npm config set prefix '~/.npm-global' # Add to PATH if not already there if ! grep -q 'npm-global/bin' ~/.bashrc 2>/dev/null; then echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc fi if [[ -f ~/.zshrc ]] && ! grep -q 'npm-global/bin' ~/.zshrc 2>/dev/null; then echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.zshrc fi # Update current session export PATH=~/.npm-global/bin:$PATH fi echo -e " ${YELLOW}β†’${RESET} Downloading and installing Claude Code..." echo "" # Install with visible progress npm install -g @anthropic-ai/claude-code 2>&1 | while IFS= read -r line; do if [[ "$line" == *"added"* ]] || [[ "$line" == *"packages"* ]]; then echo -e " ${GREEN}$line${RESET}" elif [[ "$line" == *"WARN"* ]]; then echo -e " ${YELLOW}$line${RESET}" elif [[ "$line" == *"ERR"* ]] || [[ "$line" == *"error"* ]]; then echo -e " ${RED}$line${RESET}" else echo -e " ${DIM}$line${RESET}" fi done # Rehash hash -r 2>/dev/null || true echo "" # Verify installation if command -v claude &> /dev/null; then CLAUDE_PATH=$(which claude) print_success "Claude Code installed successfully!" echo -e " ${DIM}Location: $CLAUDE_PATH${RESET}" else # Try with full path if [[ -f ~/.npm-global/bin/claude ]]; then print_success "Claude Code installed successfully!" echo -e " ${DIM}Location: ~/.npm-global/bin/claude${RESET}" print_warning "You may need to restart your terminal or run: source ~/.bashrc" else print_error "Claude Code installation may have failed!" print_info "Try running: npm install -g @anthropic-ai/claude-code" return 1 fi fi sleep 1 } #------------------------------------------------------------------------------- # Permission Configuration #------------------------------------------------------------------------------- configure_permissions() { print_section "${SHIELD} Permission Configuration" echo -e " Claude Code can run with different permission levels:" echo "" echo -e " ${WHITE}β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”${RESET}" echo -e " ${WHITE}β”‚${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}1.${RESET} ${BOLD}Normal Mode${RESET} (Recommended for most users) ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${DIM}Claude will ask permission before file edits and commands${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${GREEN}βœ“ Safe βœ“ Interactive βœ“ Full control${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}2.${RESET} ${BOLD}Auto-Accept Edits${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${DIM}Automatically accept file edits, ask for commands${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${YELLOW}⚑ Faster editing βœ“ Command safety${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}3.${RESET} ${BOLD}Full Auto Mode${RESET} (--dangerously-skip-permissions) ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${DIM}Skip all permission prompts (use with caution!)${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${RED}⚠ No confirmations ⚠ Full system access${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${CYAN}4.${RESET} ${BOLD}Don't start Claude${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${DIM}Exit installer without starting Claude${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β”‚${RESET} ${WHITE}β”‚${RESET}" echo -e " ${WHITE}β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜${RESET}" echo "" while true; do echo -ne "${YELLOW}?${RESET} Select permission mode [${GREEN}1${RESET}/${CYAN}2${RESET}/${RED}3${RESET}/${WHITE}4${RESET}]: " read -r choice case "$choice" in 1) PERMISSION_MODE="normal" CLAUDE_CMD="claude" print_success "Selected: Normal Mode" break ;; 2) PERMISSION_MODE="auto-edit" CLAUDE_CMD="claude --auto-accept-edits" print_success "Selected: Auto-Accept Edits" break ;; 3) PERMISSION_MODE="full-auto" echo "" print_warning "⚠️ WARNING: Full Auto Mode gives Claude unrestricted access!" echo -e " ${RED}This mode will:${RESET}" echo -e " ${RED} β€’ Execute commands without confirmation${RESET}" echo -e " ${RED} β€’ Modify files without asking${RESET}" echo -e " ${RED} β€’ Have full access to your system${RESET}" echo "" if confirm "Are you sure you want to use Full Auto Mode?" "n"; then CLAUDE_CMD="claude --dangerously-skip-permissions" print_success "Selected: Full Auto Mode" break else echo "" print_info "Please select a different mode." echo "" fi ;; 4) PERMISSION_MODE="exit" CLAUDE_CMD="" print_info "Installation complete. You can start Claude anytime with: claude" break ;; *) print_error "Invalid choice. Please enter 1, 2, 3, or 4." ;; esac done } #------------------------------------------------------------------------------- # Launch Claude Code #------------------------------------------------------------------------------- launch_claude() { if [[ "$PERMISSION_MODE" == "exit" ]]; then print_section "${CHECK} Installation Complete!" echo -e " ${GREEN}Claude Code has been successfully installed!${RESET}" echo "" echo -e " ${WHITE}To start Claude Code, run:${RESET}" echo "" echo -e " ${CYAN}claude${RESET} ${DIM}# Normal mode${RESET}" echo -e " ${CYAN}claude --auto-accept-edits${RESET} ${DIM}# Auto-accept file edits${RESET}" echo -e " ${CYAN}claude --dangerously-skip-permissions${RESET} ${DIM}# Full auto mode${RESET}" echo "" echo -e " ${DIM}If 'claude' is not found, restart your terminal or run:${RESET}" echo -e " ${CYAN}source ~/.bashrc${RESET}" echo "" print_success "Thank you for installing Claude Code!" return fi print_section "${ROCKET} Launching Claude Code" echo -e " ${WHITE}Starting Claude Code with: ${CYAN}$CLAUDE_CMD${RESET}" echo "" echo -e " ${DIM}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${RESET}" echo "" sleep 1 # Ensure PATH includes npm-global export PATH=~/.npm-global/bin:$PATH # Launch Claude exec $CLAUDE_CMD } #------------------------------------------------------------------------------- # Main Script Execution #------------------------------------------------------------------------------- main() { # Check if running as root (not recommended but handle gracefully) if [[ $EUID -eq 0 ]]; then print_warning "Running as root. This is not recommended for Claude Code." print_info "Consider running as a regular user with sudo access." echo "" if ! confirm "Continue anyway?"; then exit 1 fi fi # Display banner print_banner echo -e " ${WHITE}Welcome to the Claude Code Installer!${RESET}" echo "" echo -e " ${DIM}This script will:${RESET}" echo -e " ${DIM} 1. Detect your Linux distribution${RESET}" echo -e " ${DIM} 2. Check/install Node.js 20 LTS${RESET}" echo -e " ${DIM} 3. Install Claude Code${RESET}" echo -e " ${DIM} 4. Configure permissions${RESET}" echo "" if ! confirm "Ready to begin installation?"; then echo "" print_info "Installation cancelled. Goodbye!" exit 0 fi # Run installation steps detect_distro check_existing_node verify_node_installation || exit 1 install_claude_code || exit 1 configure_permissions launch_claude } # Run main function main "$@"