Skip to content

Instantly share code, notes, and snippets.

@usrbinkat
Last active June 25, 2025 18:30
Show Gist options
  • Save usrbinkat/e36f12cd0d8c0f98decc80b092c447f4 to your computer and use it in GitHub Desktop.
Save usrbinkat/e36f12cd0d8c0f98decc80b092c447f4 to your computer and use it in GitHub Desktop.
curl | bash :: Portable installer for Anthropic Claude Code, OpenAI Codex, and Google Gemini CLI

AI CLI Installer v3.2.0

Bash script for installing AI-powered command-line development tools.

Quick Start

curl -fsSL https://gist.githubusercontent.com/usrbinkat/e36f12cd0d8c0f98decc80b092c447f4/raw/ai-tools.sh | bash

Source: gist.github.com/usrbinkat/e36f12cd0d8c0f98decc80b092c447f4

Available Tools

Tool Package Command Requirements Key Features
Claude Code @anthropic-ai/claude-code claude Node.js 18+
macOS 10.15+, Ubuntu 20.04+/Debian 10+, Windows via WSL
Terminal-based AI agent with codebase understanding, file operations, git integration
OpenAI Codex @openai/codex codex Node.js 22+
Ubuntu 20.04+/Debian 10+, Windows via WSL2
GLIBC 2.39+
Sandboxed code execution, interactive REPL, multi-provider support
Gemini CLI @google/gemini-cli gemini Node.js 18+
macOS/Linux/WSL
1M+ token context, multimodal input, Google Search grounding, MCP server support

Installation Features

  • User-local installation to ~/.npm-global directory
  • Compatible with bash 3.2+ (including macOS default)
  • Automatic PATH configuration for bash, zsh, and fish shells
  • Idempotent execution
  • Automatic updates for existing installations

System Requirements

  • Node.js 18+ (22+ for Codex)
  • npm (included with Node.js)
  • bash 3.2+
  • Operating system compatibility varies by tool (see table above)

Usage

ai_cli_installer [OPTIONS]

OPTIONS:
    -p, --packages LIST   Comma-separated list (eg: "claude,codex,gemini" )
                          Default: all three tools
    -g, --global          Install globally (requires sudo)
                          Default: user-local installation
    -d, --dry-run         Preview what will be installed
    -l, --list            List available packages
    -v, --version         Show installer version
    -h, --help            Show this help message

Examples

Set the installer URL as an environment variable:

export AI_INSTALLER="https://gist.githubusercontent.com/usrbinkat/e36f12cd0d8c0f98decc80b092c447f4/raw/ai-tools.sh"

Installation commands:

# Install all three tools (default)
curl -fsSL $AI_INSTALLER | bash

# Install specific tools
curl -fsSL $AI_INSTALLER | bash -s -- -p claude
curl -fsSL $AI_INSTALLER | bash -s -- -p claude,gemini

# Global installation
curl -fsSL $AI_INSTALLER | bash -s -- --global

# Preview installation
curl -fsSL $AI_INSTALLER | bash -s -- --dry-run

# List available packages
curl -fsSL $AI_INSTALLER | bash -s -- --list

# Show version
curl -fsSL $AI_INSTALLER | bash -s -- --version

# Display help
curl -fsSL $AI_INSTALLER | bash -s -- --help

Docker Installation

Standard installation:

FROM ubuntu:24.04
RUN apt-get update && apt-get install -y curl
RUN curl -fsSL https://gist.githubusercontent.com/usrbinkat/e36f12cd0d8c0f98decc80b092c447f4/raw/ai-tools.sh | bash

For older base images (Claude and Gemini only):

FROM debian:12
RUN apt-get update && apt-get install -y curl
RUN curl -fsSL https://gist.githubusercontent.com/usrbinkat/e36f12cd0d8c0f98decc80b092c447f4/raw/ai-tools.sh | bash -s -- -p claude,gemini

Using Docker build arguments:

ARG AI_INSTALLER=https://gist.githubusercontent.com/usrbinkat/e36f12cd0d8c0f98decc80b092c447f4/raw/ai-tools.sh
FROM ubuntu:24.04
RUN apt-get update && apt-get install -y curl && \
    curl -fsSL ${AI_INSTALLER} | bash

Post-Installation

Reload your shell configuration:

source ~/.bashrc  # or ~/.zshrc for zsh users

Verify installations:

claude --version
codex --version
gemini --version

Tool Capabilities

Claude Code

Claude Code embeds Claude Opus 4 directly in your terminal, achieving 72.5% on SWE-bench benchmarks. It maps and explains entire codebases in seconds using agentic search to understand project structure and dependencies without manual context selection. The tool supports extended thinking with tool use for up to 7 hours of autonomous code execution. Project-specific guidance through CLAUDE.md files enables customized workflows, while the /init command generates comprehensive project documentation. Git integration handles the entire workflow from reading issues to submitting pull requests, all from your terminal.

OpenAI Codex

Platform-specific sandboxing uses Apple Seatbelt (sandbox-exec) on macOS for read-only jailing with writable roots limited to $PWD, $TMPDIR, and ~/.codex. Linux sandboxing leverages Docker containers with custom iptables/ipset firewalls that deny all egress except the OpenAI API. Multi-provider support includes OpenAI, Gemini, OpenRouter, Ollama, Azure, and any OpenAI-compatible API endpoint. AGENTS.md files provide layered project documentation that merges from ~/.codex/AGENTS.md, repository root, and current directory. The TypeScript implementation uses ink and React for interactive terminal UI, with Rust bindings for native performance.

Gemini CLI

Gemini 1.5 models achieve near-perfect recall on long-context retrieval tasks across modalities, processing millions of tokens including multiple documents and hours of video/audio. Extended context windows handle 1M tokens for comprehensive codebase analysis, large datasets, or extensive documentation that exceeds other models' limits. Multimodal capabilities enable app generation from PDFs or sketches, combining visual understanding with code generation. MCP server implementations expose Gemini's capabilities as standard protocol tools, allowing integration with other AI systems and custom workflows. Built-in Google Search grounding provides real-time information retrieval for current APIs, documentation, and technical solutions.

Troubleshooting

GLIBC Version Error

For systems showing libc.so.6: version 'GLIBC_2.39' not found:

  • Check your version: ldd --version
  • Install Claude and Gemini only: curl -fsSL $AI_INSTALLER | bash -s -- -p claude,gemini
  • Upgrade to Ubuntu 24.04+ or equivalent

Command Not Found

After installation:

  • Reload shell configuration: source ~/.bashrc
  • Verify PATH: echo $PATH | grep npm-global
  • Check installation: ls ~/.npm-global/bin/

Manual Installation

# Set installer URL
export AI_INSTALLER="https://gist.githubusercontent.com/usrbinkat/e36f12cd0d8c0f98decc80b092c447f4/raw/ai-tools.sh"

# Download script
curl -o ai_cli_installer.sh $AI_INSTALLER

# Review contents
less ai_cli_installer.sh

# Execute
chmod +x ai_cli_installer.sh
./ai_cli_installer.sh --help

Uninstallation

Remove packages:

npm uninstall -g @anthropic-ai/claude-code @openai/codex @google/gemini-cli

Remove PATH configuration from your shell RC file (~/.bashrc, ~/.zshrc, or ~/.config/fish/config.fish).

Documentation

Security

  • No external dependencies beyond Node.js and npm
  • User-local installation by default
  • All operations logged to stdout
  • Dry-run mode for installation preview

License

MIT


Created by @usrbinkat

#!/usr/bin/env bash
# ai_cli_installer - Installer for AI CLI tools
# Version: 3.2.0
set -euo pipefail
###############################################################################
## Constants
###############################################################################
readonly SCRIPT_VERSION="3.2.0"
readonly SCRIPT_NAME="$(basename "$0")"
# Colors
readonly GREEN='\033[0;32m'
readonly YELLOW='\033[1;33m'
readonly RED='\033[0;31m'
readonly BLUE='\033[0;34m'
readonly NC='\033[0m'
# Package definitions (Bash 3.2 compatible - no associative arrays)
# Format: package_name:npm_package
PACKAGES="claude:@anthropic-ai/claude-code codex:@openai/codex gemini:@google/gemini-cli"
# Helper function to get npm package name
get_npm_package() {
local key="$1"
for pkg in $PACKAGES; do
if [[ "${pkg%%:*}" == "$key" ]]; then
echo "${pkg#*:}"
return 0
fi
done
return 1
}
# Helper function to list available packages
list_package_names() {
local names=""
for pkg in $PACKAGES; do
names="$names ${pkg%%:*}"
done
echo "${names# }" # Remove leading space
}
###############################################################################
## Output Functions
###############################################################################
info() { printf "${BLUE}[INFO]${NC} %s\n" "$*"; }
warn() { printf "${YELLOW}[WARN]${NC} %s\n" "$*" >&2; }
error() { printf "${RED}[ERROR]${NC} %s\n" "$*" >&2; }
success() { printf "${GREEN}[✓]${NC} %s\n" "$*"; }
###############################################################################
## Help & Version
###############################################################################
show_version() {
echo "${SCRIPT_NAME} version ${SCRIPT_VERSION}"
}
show_help() {
cat <<EOF
${SCRIPT_NAME} - Install AI CLI tools
USAGE:
${SCRIPT_NAME} [OPTIONS]
OPTIONS:
-p, --packages LIST Comma-separated list of packages to install
Available: $(list_package_names)
Default: all available packages
-g, --global Install globally (requires sudo)
Default: user-local installation
-d, --dry-run Show what would be done without making changes
-l, --list List available packages
-v, --version Show version information
-h, --help Show this help message
EXAMPLES:
${SCRIPT_NAME} # Install all tools (user-local)
${SCRIPT_NAME} -p claude # Install only Claude (user-local)
${SCRIPT_NAME} -p claude,gemini # Install Claude and Gemini
${SCRIPT_NAME} --global # Install globally (requires sudo)
${SCRIPT_NAME} --dry-run # Preview changes
EOF
}
###############################################################################
## Utility Functions
###############################################################################
command_exists() {
command -v "$1" >/dev/null 2>&1
}
detect_shell() {
basename "$SHELL"
}
get_shell_rc() {
local shell_name="$1"
case "$shell_name" in
bash) echo "${HOME}/.bashrc" ;;
zsh) echo "${HOME}/.zshrc" ;;
fish) echo "${HOME}/.config/fish/config.fish" ;;
*) echo "" ;;
esac
}
add_to_path() {
local npm_bin="$1"
local shell_name=$(detect_shell)
local rc_file=$(get_shell_rc "$shell_name")
if [[ -z "$rc_file" ]]; then
warn "Unsupported shell: $shell_name. Please add $npm_bin to PATH manually."
return 1
fi
local export_line
case "$shell_name" in
fish)
# Use fish_user_paths for proper persistence
export_line="set -U fish_user_paths $npm_bin \$fish_user_paths"
;;
*)
export_line="export PATH=\"$npm_bin:\$PATH\""
;;
esac
# Check if PATH entry already exists
if [[ -f "$rc_file" ]] && grep -qF "$npm_bin" "$rc_file" 2>/dev/null; then
info "PATH already configured in $rc_file" >&2
else
echo "" >> "$rc_file"
echo "# Added by ${SCRIPT_NAME} on $(date)" >> "$rc_file"
echo "$export_line" >> "$rc_file"
info "Added $npm_bin to PATH in $rc_file" >&2
fi
# Export for current session (with duplication guard)
if [[ ":$PATH:" != *":$npm_bin:"* ]]; then
export PATH="$npm_bin:$PATH"
fi
}
check_node_version() {
if ! command_exists node; then
error "Node.js is not installed. Please install Node.js 18 or higher."
return 1
fi
# Try node -p first, fallback to version string parsing
local major_version=$(node -p 'process.versions.node.split(".")[0]' 2>/dev/null \
|| node --version | sed 's/v//' | cut -d. -f1)
if [[ "$major_version" -lt 18 ]]; then
error "Node.js 18 or higher required. Found: v$(node --version | sed 's/v//')"
return 1
fi
info "Node.js version: $(node --version)"
return 0
}
###############################################################################
## Main Installation Logic
###############################################################################
setup_npm_prefix() {
local force_global="$1"
local install_global=false
local npm_cmd="npm"
local npm_prefix="${HOME}/.npm-global"
if [[ "$force_global" == "true" ]]; then
# User explicitly wants global installation
if [[ $EUID -eq 0 ]]; then
npm_cmd="npm"
install_global=true
npm_prefix=""
elif command_exists sudo; then
# Check if npm is available to sudo
if sudo -n which npm >/dev/null 2>&1; then
npm_cmd="sudo npm"
install_global=true
npm_prefix=""
else
error "Cannot install globally: npm not available to sudo (likely installed via version manager)" >&2
error "Try without --global flag for user-local installation" >&2
exit 1
fi
else
error "Cannot install globally: no root access available" >&2
error "Try without --global flag for user-local installation" >&2
exit 1
fi
info "Installing globally (system-wide)" >&2
else
# Default: user-local installation
mkdir -p "${npm_prefix}/lib" "${npm_prefix}/bin"
npm config --global set prefix "${npm_prefix}" 2>/dev/null
add_to_path "${npm_prefix}/bin"
info "Installing to user directory: ${npm_prefix}" >&2
fi
# Return command and prefix (this MUST be the only stdout output)
echo "${npm_cmd}|${npm_prefix}"
}
install_package() {
local package_key="$1"
local npm_package="$2"
local npm_cmd="$3"
local dry_run="$4"
local npm_prefix="$5"
if [[ "$dry_run" == "true" ]]; then
info "[DRY RUN] Would install: $npm_package"
return 0
fi
if command_exists "$package_key"; then
info "Updating $package_key..."
$npm_cmd update -g "$npm_package"
else
info "Installing $package_key..."
$npm_cmd install -g "$npm_package"
fi
# For user-local installs, the command might not be in PATH yet
# Check both current PATH and the npm prefix location
if command_exists "$package_key"; then
success "$package_key installed successfully"
elif [[ -n "$npm_prefix" ]] && [[ -x "${npm_prefix}/bin/$package_key" ]]; then
success "$package_key installed successfully (will be available after shell reload)"
else
error "Failed to install $package_key"
return 1
fi
}
###############################################################################
## Main Function
###############################################################################
main() {
local packages_to_install=""
local force_global=false
local dry_run=false
local list_packages=false
# Parse arguments
while [[ $# -gt 0 ]]; do
case "$1" in
-p|--packages)
[[ -z "${2:-}" ]] && { error "--packages requires an argument"; exit 1; }
packages_to_install="$2"
shift 2
;;
-g|--global)
force_global=true
shift
;;
-d|--dry-run)
dry_run=true
shift
;;
-l|--list)
list_packages=true
shift
;;
-v|--version)
show_version
exit 0
;;
-h|--help)
show_help
exit 0
;;
*)
error "Unknown option: $1"
show_help
exit 1
;;
esac
done
# List packages if requested
if [[ "$list_packages" == "true" ]]; then
echo "Available packages:"
for pkg in $PACKAGES; do
local name="${pkg%%:*}"
local npm_pkg="${pkg#*:}"
echo " - $name ($npm_pkg)"
done
exit 0
fi
# Check prerequisites
check_node_version || exit 1
if ! command_exists npm; then
error "npm is not installed"
exit 1
fi
info "npm version: $(npm --version)"
# Setup npm prefix
IFS='|' read -r npm_cmd npm_prefix <<< "$(setup_npm_prefix "$force_global")"
# Determine packages to install
if [[ -z "$packages_to_install" ]]; then
packages_to_install=$(list_package_names | tr ' ' ',')
fi
# Install packages
IFS=',' read -ra requested_packages <<< "$packages_to_install"
local failed=0
for pkg in "${requested_packages[@]}"; do
pkg=$(echo "$pkg" | tr -d ' ') # Trim whitespace
local npm_package=$(get_npm_package "$pkg")
if [[ -z "$npm_package" ]]; then
warn "Unknown package: $pkg"
continue
fi
if ! install_package "$pkg" "$npm_package" "$npm_cmd" "$dry_run" "$npm_prefix"; then
((failed++))
fi
done
# Summary
echo
if [[ $failed -eq 0 ]]; then
success "All installations completed successfully!"
# Check if PATH needs to be reloaded
if [[ -n "$npm_prefix" ]]; then
local shell_name=$(detect_shell)
local rc_file=$(get_shell_rc "$shell_name")
if [[ -n "$rc_file" ]]; then
warn "Restart your shell or run: source $rc_file"
fi
fi
else
error "$failed package(s) failed to install."
exit 1
fi
}
# Run main function
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment