#!/usr/bin/env bash # ============================================================================= # pimp-my-termux — AI Coding Agents Installer # Installs: Claude Code, Gemini CLI, GitHub Copilot CLI, Aider # # Usage (in Termux): # bash install-ai-agents.sh # bash install-ai-agents.sh --skip-update # skip pkg update (faster re-runs) # ============================================================================= # --- Colors ------------------------------------------------------------------ R='\033[0;31m' G='\033[0;32m' Y='\033[1;33m' B='\033[0;34m' C='\033[0;36m' M='\033[0;35m' BOLD='\033[1m' DIM='\033[2m' NC='\033[0m' # --- Helpers ----------------------------------------------------------------- info() { echo -e "${C}[·]${NC} $*"; } success() { echo -e "${G}[✓]${NC} $*"; } warn() { echo -e "${Y}[!]${NC} $*"; } error() { echo -e "${R}[✗]${NC} $*"; } header() { echo -e "\n${BOLD}${B}━━━ $* ${NC}"; } step() { echo -e " ${DIM}→${NC} $*"; } FAILED_TOOLS=() SKIP_UPDATE=false [[ "${1:-}" == "--skip-update" ]] && SKIP_UPDATE=true # --- Banner ------------------------------------------------------------------ print_banner() { echo -e "${M}${BOLD}" echo " ╔═══════════════════════════════════════════╗" echo " ║ pimp-my-termux · AI Agents ║" echo " ║ Claude · Gemini · Copilot · Aider ║" echo " ╚═══════════════════════════════════════════╝" echo -e "${NC}" } # --- Termux guard ------------------------------------------------------------ check_termux() { if [[ -z "${PREFIX:-}" || ! -d "/data/data/com.termux" ]]; then error "This script must run inside Termux on Android." exit 1 fi success "Termux detected (PREFIX=$PREFIX)" } # --- Storage permission ------------------------------------------------------ setup_storage() { if [[ ! -d "$HOME/storage" ]]; then info "Requesting storage permission (termux-setup-storage)…" termux-setup-storage sleep 2 else success "Storage already set up" fi } # --- Package update ---------------------------------------------------------- update_packages() { if $SKIP_UPDATE; then warn "Skipping package update (--skip-update flag)" return fi info "Updating package lists…" pkg update -y && pkg upgrade -y success "Packages up to date" } # --- Core dependencies ------------------------------------------------------- install_dependencies() { header "Core Dependencies" local deps=(nodejs-lts git python make clang binutils) for dep in "${deps[@]}"; do if pkg list-installed 2>/dev/null | grep -q "^${dep}/"; then success "$dep already installed" else info "Installing $dep…" if pkg install -y "$dep"; then success "$dep installed" else error "Failed to install $dep — subsequent tools may fail" fi fi done # pip upgrade info "Upgrading pip…" python -m pip install --upgrade pip --quiet success "pip upgraded" } # --- npm global prefix ------------------------------------------------------- configure_npm() { header "npm Configuration" # In Termux the default npm global prefix is already $PREFIX, which is # writable. Only reconfigure if it looks misconfigured. local npm_prefix npm_prefix=$(npm config get prefix 2>/dev/null || echo "") if [[ "$npm_prefix" == "$PREFIX" ]]; then success "npm prefix already correct ($PREFIX)" else info "Setting npm prefix to \$PREFIX ($PREFIX)…" npm config set prefix "$PREFIX" success "npm prefix set" fi # Confirm node/npm versions step "node $(node --version)" step "npm $(npm --version)" } # --- Install: Claude Code ---------------------------------------------------- install_claude_code() { header "Claude Code CLI" info "Installing @anthropic-ai/claude-code…" if npm install -g @anthropic-ai/claude-code; then success "Claude Code installed → $(claude --version 2>/dev/null || echo 'run: claude --version')" echo -e " ${DIM}API key setup: export ANTHROPIC_API_KEY='sk-ant-…'${NC}" echo -e " ${DIM}Get a key: https://console.anthropic.com/settings/api-keys${NC}" else error "Claude Code installation failed" FAILED_TOOLS+=("claude-code") fi } # --- Install: Gemini CLI ------------------------------------------------------ install_gemini_cli() { header "Gemini CLI" info "Installing @google/gemini-cli…" if npm install -g @google/gemini-cli; then success "Gemini CLI installed → $(gemini --version 2>/dev/null || echo 'run: gemini --version')" echo -e " ${DIM}API key setup: export GEMINI_API_KEY='AIza…'${NC}" echo -e " ${DIM}Get a key: https://aistudio.google.com/app/apikey${NC}" else error "Gemini CLI installation failed" FAILED_TOOLS+=("gemini-cli") fi } # --- Install: GitHub Copilot CLI --------------------------------------------- install_copilot_cli() { header "GitHub Copilot CLI" info "Installing @githubnext/github-copilot-cli…" if npm install -g @githubnext/github-copilot-cli; then success "GitHub Copilot CLI installed" echo -e " ${DIM}Authenticate: github-copilot-cli auth${NC}" echo -e " ${DIM}Requires: GitHub account with Copilot subscription${NC}" echo -e " ${DIM}Aliases added after auth: ?? (shell) git? (git) gh? (gh)${NC}" else error "GitHub Copilot CLI installation failed" FAILED_TOOLS+=("copilot-cli") fi } # --- Install: Aider (replaces Cursor CLI) ------------------------------------ install_aider() { header "Aider (AI Pair Programmer)" echo -e " ${Y}Note about Cursor:${NC} Cursor is a desktop IDE (Electron app) with no" echo -e " terminal CLI and cannot run on Android/Termux." echo -e " ${G}Aider${NC} fills that role: it's an open-source AI coding agent that" echo -e " works in your terminal, supports Claude/Gemini/GPT-4 and edits real" echo -e " files with git integration — more powerful for headless use.\n" info "Installing aider-chat via pip…" # Use pipx if available for cleaner isolation, else pip directly if command -v pipx &>/dev/null; then if pipx install aider-chat; then success "Aider installed via pipx" else warn "pipx install failed, retrying with pip…" pip install --upgrade aider-chat fi else if pip install --upgrade aider-chat; then success "Aider installed via pip" else error "Aider installation failed" FAILED_TOOLS+=("aider") return fi fi local aider_ver aider_ver=$(aider --version 2>/dev/null || echo "unknown") success "Aider $aider_ver → run: aider --help" echo -e " ${DIM}With Claude: aider --model claude-sonnet-4-6${NC}" echo -e " ${DIM}With Gemini: aider --model gemini/gemini-2.5-pro${NC}" echo -e " ${DIM}With GPT-4: aider --model gpt-4o${NC}" echo -e " ${DIM}Docs: https://aider.chat${NC}" } # --- Shell environment hints ------------------------------------------------- print_env_hints() { header "Environment Setup" echo -e " Add these exports to ${BOLD}~/.bashrc${NC} (or ~/.zshrc):\n" cat << 'ENVBLOCK' # ── AI Coding Agents ────────────────────────────────────────────── export ANTHROPIC_API_KEY="sk-ant-YOUR_KEY_HERE" export GEMINI_API_KEY="AIzaYOUR_KEY_HERE" export OPENAI_API_KEY="sk-YOUR_KEY_HERE" # optional, for aider/GPT # Copilot CLI shell aliases (run once: github-copilot-cli auth) eval "$(github-copilot-cli alias -- "$0")" # adds ?? git? gh? shortcuts ENVBLOCK echo "" info "Apply immediately with: source ~/.bashrc" } # --- Summary ----------------------------------------------------------------- print_summary() { header "Installation Summary" local all_tools=("claude (claude-code)" "gemini (gemini-cli)" "github-copilot-cli" "aider") for tool in "${all_tools[@]}"; do local bin bin=$(echo "$tool" | awk '{print $1}') if command -v "$bin" &>/dev/null; then success "$tool" else # check if it was in our failed list local label label=$(echo "$tool" | sed 's/[()]//g') warn "$tool — not found in PATH (may need to reopen Termux)" fi done if [[ ${#FAILED_TOOLS[@]} -gt 0 ]]; then echo "" error "The following installations encountered errors:" for t in "${FAILED_TOOLS[@]}"; do echo -e " ${R}•${NC} $t" done echo "" warn "Try re-running: bash install-ai-agents.sh --skip-update" else echo "" echo -e " ${G}${BOLD}All tools installed successfully!${NC}" fi echo "" echo -e " ${DIM}Reopen Termux or run 'source ~/.bashrc' to refresh PATH.${NC}" echo "" } # --- Main -------------------------------------------------------------------- main() { print_banner check_termux setup_storage update_packages install_dependencies configure_npm install_claude_code install_gemini_cli install_copilot_cli install_aider print_env_hints print_summary } main "$@"