|
| 1 | +#!/usr/bin/env bash |
| 2 | +set -euo pipefail |
| 3 | + |
| 4 | +# free-code installer |
| 5 | +# Usage: curl -fsSL https://raw.githubusercontent.com/paoloanzn/free-code/main/install.sh | bash |
| 6 | + |
| 7 | +RED='\033[0;31m' |
| 8 | +GREEN='\033[0;32m' |
| 9 | +YELLOW='\033[1;33m' |
| 10 | +CYAN='\033[0;36m' |
| 11 | +BOLD='\033[1m' |
| 12 | +DIM='\033[2m' |
| 13 | +RESET='\033[0m' |
| 14 | + |
| 15 | +REPO="https://github.com/paoloanzn/free-code.git" |
| 16 | +INSTALL_DIR="$HOME/free-code" |
| 17 | +BUN_MIN_VERSION="1.3.11" |
| 18 | + |
| 19 | +info() { printf "${CYAN}[*]${RESET} %s\n" "$*"; } |
| 20 | +ok() { printf "${GREEN}[+]${RESET} %s\n" "$*"; } |
| 21 | +warn() { printf "${YELLOW}[!]${RESET} %s\n" "$*"; } |
| 22 | +fail() { printf "${RED}[x]${RESET} %s\n" "$*"; exit 1; } |
| 23 | + |
| 24 | +header() { |
| 25 | + echo "" |
| 26 | + printf "${BOLD}${CYAN}" |
| 27 | + cat << 'ART' |
| 28 | + ___ _ |
| 29 | + / _|_ __ ___ ___ ___ __| | ___ |
| 30 | + | |_| '__/ _ \/ _ \_____ / __/ _` |/ _ \ |
| 31 | + | _| | | __/ __/_____| (_| (_| | __/ |
| 32 | + |_| |_| \___|\___| \___\__,_|\___| |
| 33 | +
|
| 34 | +ART |
| 35 | + printf "${RESET}" |
| 36 | + printf "${DIM} The free build of Claude Code${RESET}\n" |
| 37 | + echo "" |
| 38 | +} |
| 39 | + |
| 40 | +# ------------------------------------------------------------------- |
| 41 | +# System checks |
| 42 | +# ------------------------------------------------------------------- |
| 43 | + |
| 44 | +check_os() { |
| 45 | + case "$(uname -s)" in |
| 46 | + Darwin) OS="macos" ;; |
| 47 | + Linux) OS="linux" ;; |
| 48 | + *) fail "Unsupported OS: $(uname -s). macOS or Linux required." ;; |
| 49 | + esac |
| 50 | + ok "OS: $(uname -s) $(uname -m)" |
| 51 | +} |
| 52 | + |
| 53 | +check_git() { |
| 54 | + if ! command -v git &>/dev/null; then |
| 55 | + fail "git is not installed. Install it first: |
| 56 | + macOS: xcode-select --install |
| 57 | + Linux: sudo apt install git (or your distro's equivalent)" |
| 58 | + fi |
| 59 | + ok "git: $(git --version | head -1)" |
| 60 | +} |
| 61 | + |
| 62 | +# Compare semver: returns 0 if $1 >= $2 |
| 63 | +version_gte() { |
| 64 | + [ "$(printf '%s\n' "$1" "$2" | sort -V | head -1)" = "$2" ] |
| 65 | +} |
| 66 | + |
| 67 | +check_bun() { |
| 68 | + if command -v bun &>/dev/null; then |
| 69 | + local ver |
| 70 | + ver="$(bun --version 2>/dev/null || echo "0.0.0")" |
| 71 | + if version_gte "$ver" "$BUN_MIN_VERSION"; then |
| 72 | + ok "bun: v${ver}" |
| 73 | + return |
| 74 | + fi |
| 75 | + warn "bun v${ver} found but v${BUN_MIN_VERSION}+ required. Upgrading..." |
| 76 | + else |
| 77 | + info "bun not found. Installing..." |
| 78 | + fi |
| 79 | + install_bun |
| 80 | +} |
| 81 | + |
| 82 | +install_bun() { |
| 83 | + curl -fsSL https://bun.sh/install | bash |
| 84 | + # Source the updated profile so bun is on PATH for this session |
| 85 | + export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}" |
| 86 | + export PATH="$BUN_INSTALL/bin:$PATH" |
| 87 | + if ! command -v bun &>/dev/null; then |
| 88 | + fail "bun installation succeeded but binary not found on PATH. |
| 89 | + Add this to your shell profile and restart: |
| 90 | + export PATH=\"\$HOME/.bun/bin:\$PATH\"" |
| 91 | + fi |
| 92 | + ok "bun: v$(bun --version) (just installed)" |
| 93 | +} |
| 94 | + |
| 95 | +# ------------------------------------------------------------------- |
| 96 | +# Clone & build |
| 97 | +# ------------------------------------------------------------------- |
| 98 | + |
| 99 | +clone_repo() { |
| 100 | + if [ -d "$INSTALL_DIR" ]; then |
| 101 | + warn "$INSTALL_DIR already exists" |
| 102 | + if [ -d "$INSTALL_DIR/.git" ]; then |
| 103 | + info "Pulling latest changes..." |
| 104 | + git -C "$INSTALL_DIR" pull --ff-only origin main 2>/dev/null || { |
| 105 | + warn "Pull failed, continuing with existing copy" |
| 106 | + } |
| 107 | + fi |
| 108 | + else |
| 109 | + info "Cloning repository..." |
| 110 | + git clone --depth 1 "$REPO" "$INSTALL_DIR" |
| 111 | + fi |
| 112 | + ok "Source: $INSTALL_DIR" |
| 113 | +} |
| 114 | + |
| 115 | +install_deps() { |
| 116 | + info "Installing dependencies..." |
| 117 | + cd "$INSTALL_DIR" |
| 118 | + bun install --frozen-lockfile 2>/dev/null || bun install |
| 119 | + ok "Dependencies installed" |
| 120 | +} |
| 121 | + |
| 122 | +build_binary() { |
| 123 | + info "Building free-code (all experimental features enabled)..." |
| 124 | + cd "$INSTALL_DIR" |
| 125 | + bun run build:dev:full |
| 126 | + ok "Binary built: $INSTALL_DIR/cli-dev" |
| 127 | +} |
| 128 | + |
| 129 | +link_binary() { |
| 130 | + local link_dir="$HOME/.local/bin" |
| 131 | + mkdir -p "$link_dir" |
| 132 | + |
| 133 | + ln -sf "$INSTALL_DIR/cli-dev" "$link_dir/free-code" |
| 134 | + ok "Symlinked: $link_dir/free-code" |
| 135 | + |
| 136 | + if ! echo "$PATH" | tr ':' '\n' | grep -qx "$link_dir"; then |
| 137 | + warn "$link_dir is not on your PATH" |
| 138 | + echo "" |
| 139 | + printf "${YELLOW} Add this to your shell profile (~/.bashrc, ~/.zshrc, etc.):${RESET}\n" |
| 140 | + printf "${BOLD} export PATH=\"\$HOME/.local/bin:\$PATH\"${RESET}\n" |
| 141 | + echo "" |
| 142 | + fi |
| 143 | +} |
| 144 | + |
| 145 | +# ------------------------------------------------------------------- |
| 146 | +# Main |
| 147 | +# ------------------------------------------------------------------- |
| 148 | + |
| 149 | +header |
| 150 | +info "Starting installation..." |
| 151 | +echo "" |
| 152 | + |
| 153 | +check_os |
| 154 | +check_git |
| 155 | +check_bun |
| 156 | +echo "" |
| 157 | + |
| 158 | +clone_repo |
| 159 | +install_deps |
| 160 | +build_binary |
| 161 | +link_binary |
| 162 | + |
| 163 | +echo "" |
| 164 | +printf "${GREEN}${BOLD} Installation complete!${RESET}\n" |
| 165 | +echo "" |
| 166 | +printf " ${BOLD}Run it:${RESET}\n" |
| 167 | +printf " ${CYAN}free-code${RESET} # interactive REPL\n" |
| 168 | +printf " ${CYAN}free-code -p \"your prompt\"${RESET} # one-shot mode\n" |
| 169 | +echo "" |
| 170 | +printf " ${BOLD}Set your API key:${RESET}\n" |
| 171 | +printf " ${CYAN}export ANTHROPIC_API_KEY=\"sk-ant-...\"${RESET}\n" |
| 172 | +echo "" |
| 173 | +printf " ${BOLD}Or log in with Claude.ai:${RESET}\n" |
| 174 | +printf " ${CYAN}free-code /login${RESET}\n" |
| 175 | +echo "" |
| 176 | +printf " ${DIM}Source: $INSTALL_DIR${RESET}\n" |
| 177 | +printf " ${DIM}Binary: $INSTALL_DIR/cli-dev${RESET}\n" |
| 178 | +printf " ${DIM}Link: ~/.local/bin/free-code${RESET}\n" |
| 179 | +echo "" |
0 commit comments