|
|
@@ -0,0 +1,179 @@
|
|
|
+#!/usr/bin/env bash
|
|
|
+set -euo pipefail
|
|
|
+
|
|
|
+# free-code installer
|
|
|
+# Usage: curl -fsSL https://raw.githubusercontent.com/paoloanzn/free-code/main/install.sh | bash
|
|
|
+
|
|
|
+RED='\033[0;31m'
|
|
|
+GREEN='\033[0;32m'
|
|
|
+YELLOW='\033[1;33m'
|
|
|
+CYAN='\033[0;36m'
|
|
|
+BOLD='\033[1m'
|
|
|
+DIM='\033[2m'
|
|
|
+RESET='\033[0m'
|
|
|
+
|
|
|
+REPO="https://github.com/paoloanzn/free-code.git"
|
|
|
+INSTALL_DIR="$HOME/free-code"
|
|
|
+BUN_MIN_VERSION="1.3.11"
|
|
|
+
|
|
|
+info() { printf "${CYAN}[*]${RESET} %s\n" "$*"; }
|
|
|
+ok() { printf "${GREEN}[+]${RESET} %s\n" "$*"; }
|
|
|
+warn() { printf "${YELLOW}[!]${RESET} %s\n" "$*"; }
|
|
|
+fail() { printf "${RED}[x]${RESET} %s\n" "$*"; exit 1; }
|
|
|
+
|
|
|
+header() {
|
|
|
+ echo ""
|
|
|
+ printf "${BOLD}${CYAN}"
|
|
|
+ cat << 'ART'
|
|
|
+ ___ _
|
|
|
+ / _|_ __ ___ ___ ___ __| | ___
|
|
|
+ | |_| '__/ _ \/ _ \_____ / __/ _` |/ _ \
|
|
|
+ | _| | | __/ __/_____| (_| (_| | __/
|
|
|
+ |_| |_| \___|\___| \___\__,_|\___|
|
|
|
+
|
|
|
+ART
|
|
|
+ printf "${RESET}"
|
|
|
+ printf "${DIM} The free build of Claude Code${RESET}\n"
|
|
|
+ echo ""
|
|
|
+}
|
|
|
+
|
|
|
+# -------------------------------------------------------------------
|
|
|
+# System checks
|
|
|
+# -------------------------------------------------------------------
|
|
|
+
|
|
|
+check_os() {
|
|
|
+ case "$(uname -s)" in
|
|
|
+ Darwin) OS="macos" ;;
|
|
|
+ Linux) OS="linux" ;;
|
|
|
+ *) fail "Unsupported OS: $(uname -s). macOS or Linux required." ;;
|
|
|
+ esac
|
|
|
+ ok "OS: $(uname -s) $(uname -m)"
|
|
|
+}
|
|
|
+
|
|
|
+check_git() {
|
|
|
+ if ! command -v git &>/dev/null; then
|
|
|
+ fail "git is not installed. Install it first:
|
|
|
+ macOS: xcode-select --install
|
|
|
+ Linux: sudo apt install git (or your distro's equivalent)"
|
|
|
+ fi
|
|
|
+ ok "git: $(git --version | head -1)"
|
|
|
+}
|
|
|
+
|
|
|
+# Compare semver: returns 0 if $1 >= $2
|
|
|
+version_gte() {
|
|
|
+ [ "$(printf '%s\n' "$1" "$2" | sort -V | head -1)" = "$2" ]
|
|
|
+}
|
|
|
+
|
|
|
+check_bun() {
|
|
|
+ if command -v bun &>/dev/null; then
|
|
|
+ local ver
|
|
|
+ ver="$(bun --version 2>/dev/null || echo "0.0.0")"
|
|
|
+ if version_gte "$ver" "$BUN_MIN_VERSION"; then
|
|
|
+ ok "bun: v${ver}"
|
|
|
+ return
|
|
|
+ fi
|
|
|
+ warn "bun v${ver} found but v${BUN_MIN_VERSION}+ required. Upgrading..."
|
|
|
+ else
|
|
|
+ info "bun not found. Installing..."
|
|
|
+ fi
|
|
|
+ install_bun
|
|
|
+}
|
|
|
+
|
|
|
+install_bun() {
|
|
|
+ curl -fsSL https://bun.sh/install | bash
|
|
|
+ # Source the updated profile so bun is on PATH for this session
|
|
|
+ export BUN_INSTALL="${BUN_INSTALL:-$HOME/.bun}"
|
|
|
+ export PATH="$BUN_INSTALL/bin:$PATH"
|
|
|
+ if ! command -v bun &>/dev/null; then
|
|
|
+ fail "bun installation succeeded but binary not found on PATH.
|
|
|
+ Add this to your shell profile and restart:
|
|
|
+ export PATH=\"\$HOME/.bun/bin:\$PATH\""
|
|
|
+ fi
|
|
|
+ ok "bun: v$(bun --version) (just installed)"
|
|
|
+}
|
|
|
+
|
|
|
+# -------------------------------------------------------------------
|
|
|
+# Clone & build
|
|
|
+# -------------------------------------------------------------------
|
|
|
+
|
|
|
+clone_repo() {
|
|
|
+ if [ -d "$INSTALL_DIR" ]; then
|
|
|
+ warn "$INSTALL_DIR already exists"
|
|
|
+ if [ -d "$INSTALL_DIR/.git" ]; then
|
|
|
+ info "Pulling latest changes..."
|
|
|
+ git -C "$INSTALL_DIR" pull --ff-only origin main 2>/dev/null || {
|
|
|
+ warn "Pull failed, continuing with existing copy"
|
|
|
+ }
|
|
|
+ fi
|
|
|
+ else
|
|
|
+ info "Cloning repository..."
|
|
|
+ git clone --depth 1 "$REPO" "$INSTALL_DIR"
|
|
|
+ fi
|
|
|
+ ok "Source: $INSTALL_DIR"
|
|
|
+}
|
|
|
+
|
|
|
+install_deps() {
|
|
|
+ info "Installing dependencies..."
|
|
|
+ cd "$INSTALL_DIR"
|
|
|
+ bun install --frozen-lockfile 2>/dev/null || bun install
|
|
|
+ ok "Dependencies installed"
|
|
|
+}
|
|
|
+
|
|
|
+build_binary() {
|
|
|
+ info "Building free-code (all experimental features enabled)..."
|
|
|
+ cd "$INSTALL_DIR"
|
|
|
+ bun run build:dev:full
|
|
|
+ ok "Binary built: $INSTALL_DIR/cli-dev"
|
|
|
+}
|
|
|
+
|
|
|
+link_binary() {
|
|
|
+ local link_dir="$HOME/.local/bin"
|
|
|
+ mkdir -p "$link_dir"
|
|
|
+
|
|
|
+ ln -sf "$INSTALL_DIR/cli-dev" "$link_dir/free-code"
|
|
|
+ ok "Symlinked: $link_dir/free-code"
|
|
|
+
|
|
|
+ if ! echo "$PATH" | tr ':' '\n' | grep -qx "$link_dir"; then
|
|
|
+ warn "$link_dir is not on your PATH"
|
|
|
+ echo ""
|
|
|
+ printf "${YELLOW} Add this to your shell profile (~/.bashrc, ~/.zshrc, etc.):${RESET}\n"
|
|
|
+ printf "${BOLD} export PATH=\"\$HOME/.local/bin:\$PATH\"${RESET}\n"
|
|
|
+ echo ""
|
|
|
+ fi
|
|
|
+}
|
|
|
+
|
|
|
+# -------------------------------------------------------------------
|
|
|
+# Main
|
|
|
+# -------------------------------------------------------------------
|
|
|
+
|
|
|
+header
|
|
|
+info "Starting installation..."
|
|
|
+echo ""
|
|
|
+
|
|
|
+check_os
|
|
|
+check_git
|
|
|
+check_bun
|
|
|
+echo ""
|
|
|
+
|
|
|
+clone_repo
|
|
|
+install_deps
|
|
|
+build_binary
|
|
|
+link_binary
|
|
|
+
|
|
|
+echo ""
|
|
|
+printf "${GREEN}${BOLD} Installation complete!${RESET}\n"
|
|
|
+echo ""
|
|
|
+printf " ${BOLD}Run it:${RESET}\n"
|
|
|
+printf " ${CYAN}free-code${RESET} # interactive REPL\n"
|
|
|
+printf " ${CYAN}free-code -p \"your prompt\"${RESET} # one-shot mode\n"
|
|
|
+echo ""
|
|
|
+printf " ${BOLD}Set your API key:${RESET}\n"
|
|
|
+printf " ${CYAN}export ANTHROPIC_API_KEY=\"sk-ant-...\"${RESET}\n"
|
|
|
+echo ""
|
|
|
+printf " ${BOLD}Or log in with Claude.ai:${RESET}\n"
|
|
|
+printf " ${CYAN}free-code /login${RESET}\n"
|
|
|
+echo ""
|
|
|
+printf " ${DIM}Source: $INSTALL_DIR${RESET}\n"
|
|
|
+printf " ${DIM}Binary: $INSTALL_DIR/cli-dev${RESET}\n"
|
|
|
+printf " ${DIM}Link: ~/.local/bin/free-code${RESET}\n"
|
|
|
+echo ""
|