Skip to content

Instantly share code, notes, and snippets.

@kokoye2007
Last active June 11, 2026 12:59
Show Gist options
  • Select an option

  • Save kokoye2007/e8d30985faca6292a50bdd3b0409105f to your computer and use it in GitHub Desktop.

Select an option

Save kokoye2007/e8d30985faca6292a50bdd3b0409105f to your computer and use it in GitHub Desktop.
Vibe Code Tours — student setup (Linux/macOS/WSL): nvm+Node22, uv+Python3.12, git, Claude Code + verify
#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────
# ⚠️ MOVED: https://github.com/vibe-code-tours/vibecode-setup
# This gist copy is deprecated and may be stale. Use the repo.
# ──────────────────────────────────────────────────────────────
# api-setup.sh — point Claude Code + opencode at the bootcamp LLM proxy.
# ./api-setup.sh <KEY> <PROXY_URL>
# VIBE_KEY=sk-... VIBE_PROXY=https://<proxy> ./api-setup.sh
# ./api-setup.sh --restore # bring personal Claude login back
#
# TIP: `source api-setup.sh` (instead of running it) applies the config to your
# CURRENT shell immediately — no second `source ~/.zshrc` needed.
set -euo pipefail
# Detect if this script was sourced (then env exports survive into your shell).
SOURCED=0
if [ -n "${ZSH_EVAL_CONTEXT:-}" ]; then
case "$ZSH_EVAL_CONTEXT" in *:file) SOURCED=1;; esac
elif [ -n "${BASH_SOURCE:-}" ]; then
[ "${BASH_SOURCE[0]}" != "$0" ] && SOURCED=1
fi
# Resolve this script's path (works run or sourced, bash or zsh) for accurate hints.
if [ -n "${BASH_SOURCE:-}" ]; then
SELF="${BASH_SOURCE[0]}"
elif [ -n "${ZSH_VERSION:-}" ]; then
SELF="$(eval 'printf %s "${(%):-%x}"')"
else
SELF="$0"
fi
# --restore is a file op (no sourcing needed) -> always show the run-form command.
case "$SELF" in
bash|-bash|zsh|-zsh|sh|-sh|"") SELF_CMD="bash api-setup.sh" ;;
*) SELF_CMD="bash $SELF" ;;
esac
CRED="$HOME/.claude/.credentials.json"
CRED_BAK="$HOME/.claude/.credentials.json.vibe-bak"
if [ "${1:-}" = "--restore" ]; then
[ -f "$CRED_BAK" ] && mv "$CRED_BAK" "$CRED" && echo "Restored personal Claude login." \
|| echo "No backup at $CRED_BAK."
echo "Also remove the vibe-code-tours block from your shell profile to fully revert."
exit 0
fi
# Auto-load a key file (beginner path: no args, just edit vibe-key.env + run).
# Looks next to the script, then in the current dir.
SELF_DIR="$(cd "$(dirname "$SELF")" 2>/dev/null && pwd || echo .)"
for KF in "$SELF_DIR/vibe-key.env" "./vibe-key.env"; do
if [ -f "$KF" ]; then
# shellcheck disable=SC1090
set -a; . "$KF"; set +a
echo "Loaded key file: $KF"
break
fi
done
KEY="${1:-${VIBE_KEY:-}}"
PROXY="${2:-${VIBE_PROXY:-}}"
[ -n "$PROXY" ] || { echo "ERROR: proxy URL not set. ./api-setup.sh <KEY> <PROXY_URL>" >&2; exit 1; }
case "$PROXY" in https://*) : ;; *) echo "ERROR: PROXY must start https://" >&2; exit 1;; esac
PROXY="${PROXY%/}"; PROXY="${PROXY%/v1}" # strip trailing slash + accidental /v1
if [ -z "$KEY" ]; then printf "Paste your key (sk-...): "; read -r KEY; fi
case "$KEY" in sk-*) : ;; *) echo "ERROR: key must start sk-" >&2; exit 1;; esac
# 1. back up + remove stored Claude login (it overrides env vars)
if [ -f "$CRED" ]; then
cp "$CRED" "$CRED_BAK"; rm -f "$CRED"
echo "Backed up Claude login -> $CRED_BAK (restore: $SELF_CMD --restore)"
fi
# 2. shell profile
case "${SHELL##*/}" in
zsh) PROFILE="$HOME/.zshrc" ;;
bash) PROFILE="$HOME/.bashrc" ;;
*) PROFILE="$HOME/.profile" ;;
esac
touch "$PROFILE"
MS="# >>> vibe-code-tours >>>"; ME="# <<< vibe-code-tours <<<"
if grep -q "$MS" "$PROFILE" 2>/dev/null; then
tmp=$(mktemp); sed "/$MS/,/$ME/d" "$PROFILE" > "$tmp" && mv "$tmp" "$PROFILE"
fi
cat >> "$PROFILE" <<EOF
$MS
# Vibe Code Tours LLM proxy
export VIBE_PROXY="$PROXY"
# Claude Code (Anthropic-compatible) — base has NO /v1
export ANTHROPIC_BASE_URL="\$VIBE_PROXY"
export ANTHROPIC_AUTH_TOKEN="$KEY"
export ANTHROPIC_API_KEY="$KEY"
# force proxy models so Claude Code never requests claude-opus-* (403)
export ANTHROPIC_MODEL="mimo-v2.5-pro"
export ANTHROPIC_SMALL_FAST_MODEL="mimo-v2.5"
# let /model picker list proxy models
export CLAUDE_CODE_ENABLE_GATEWAY_MODEL_DISCOVERY="1"
# opencode / OpenAI-compatible — base HAS /v1
export OPENAI_BASE_URL="\$VIBE_PROXY/v1"
export OPENAI_API_KEY="$KEY"
vibe-model() { export OPENAI_MODEL="\$1"; echo "model: \$1"; }
$ME
EOF
# 3. opencode config file (env alone is unreliable for opencode)
OC="$HOME/.config/opencode"; mkdir -p "$OC"
cat > "$OC/opencode.json" <<OCJSON
{
"\$schema": "https://opencode.ai/config.json",
"provider": {
"vibe": {
"npm": "@ai-sdk/openai-compatible",
"name": "Vibe Code Tours",
"options": { "baseURL": "$PROXY/v1", "apiKey": "$KEY" },
"models": {
"mimo-v2.5": { "name": "MiMo v2.5 (fast)" },
"mimo-v2.5-pro": { "name": "MiMo v2.5 Pro (reasoning)" },
"deepseek-flash": { "name": "DeepSeek Flash (backup)" }
}
}
},
"model": "vibe/mimo-v2.5"
}
OCJSON
echo "opencode config -> $OC/opencode.json"
# 4. live test (Anthropic /v1/messages — what Claude Code calls)
echo ""; echo "Testing key ..."
code=$(curl -s -o /tmp/vibe_t.json -w "%{http_code}" "$PROXY/v1/messages" \
-H "x-api-key: $KEY" -H "anthropic-version: 2023-06-01" -H "Content-Type: application/json" \
-d '{"model":"mimo-v2.5","max_tokens":10,"messages":[{"role":"user","content":"ok"}]}' || true)
case "$code" in
200) echo "OK Key works." ;;
401) echo "FAIL Key rejected (401)." >&2; exit 1 ;;
429) echo "WARN budget/rate cap (429) — key valid." ;;
*) echo "WARN HTTP $code — see /tmp/vibe_t.json" ;;
esac
rm -f /tmp/vibe_t.json 2>/dev/null || true
echo ""
echo "Done. Config -> $PROFILE"
echo "Models: mimo-v2.5 (fast) · mimo-v2.5-pro (reasoning) · deepseek-flash"
echo "Switch: Claude Code /model mimo-v2.5-pro · opencode --model vibe/mimo-v2.5-pro"
echo "Restore personal Claude login: $SELF_CMD --restore"
echo ""
if [ "$SOURCED" = "1" ]; then
# sourced: load the profile NOW so it is live in this shell
# shellcheck disable=SC1090
. "$PROFILE"
echo "✅ Active in THIS shell. Run: claude (or) opencode"
else
echo "Activate now — copy-paste this line:"
echo ""
echo " source $PROFILE"
echo ""
echo "(or next time: 'source api-setup.sh' to skip this step)"
echo "Then run: claude (or) opencode"
fi

Moved → vibe-code-tours/vibecode-setup

Pricing cheat sheet now lives in the org repo (versioned, maintained):

➡️ https://github.com/vibe-code-tours/vibecode-setup

# install dev tools
curl -fsSL https://raw.githubusercontent.com/vibe-code-tours/vibecode-setup/main/student-setup.sh | bash
# connect to the proxy (see repo README)
curl -fsSLO https://raw.githubusercontent.com/vibe-code-tours/vibecode-setup/main/api-setup.sh

This gist is no longer updated.

Moved → vibe-code-tours/vibecode-setup

Student setup guide now lives in the org repo (versioned, maintained):

➡️ https://github.com/vibe-code-tours/vibecode-setup

# install dev tools
curl -fsSL https://raw.githubusercontent.com/vibe-code-tours/vibecode-setup/main/student-setup.sh | bash
# connect to the proxy (see repo README)
curl -fsSLO https://raw.githubusercontent.com/vibe-code-tours/vibecode-setup/main/api-setup.sh

This gist is no longer updated.

Moved → vibe-code-tours/vibecode-setup

Dev-tools install guide now lives in the org repo (versioned, maintained):

➡️ https://github.com/vibe-code-tours/vibecode-setup

# install dev tools
curl -fsSL https://raw.githubusercontent.com/vibe-code-tours/vibecode-setup/main/student-setup.sh | bash
# connect to the proxy (see repo README)
curl -fsSLO https://raw.githubusercontent.com/vibe-code-tours/vibecode-setup/main/api-setup.sh

This gist is no longer updated.

#!/usr/bin/env bash
# ──────────────────────────────────────────────────────────────
# ⚠️ MOVED: https://github.com/vibe-code-tours/vibecode-setup
# This gist copy is deprecated and may be stale. Use the repo.
# ──────────────────────────────────────────────────────────────
# Vibe Code Tours — student setup (standalone, self-contained).
# Works on: Linux, macOS, Windows-WSL (Ubuntu). Native Windows: install WSL first.
#
# One-liner for students:
# curl -fsSL <GIST_RAW_URL> | bash
# Or download then run:
# bash student-setup.sh
#
# Idempotent — safe to re-run. Installs: nvm+Node 22 LTS, uv+Python 3.12
# (project pin via .python-version), git, Claude Code, opencode CLI. Then verifies.
# System Python 3.12-3.14 all accepted by verify.
# git, Claude Code. Then verifies everything. No repo clone required.
set -u
NODE_TRACK="--lts" # Node 22 LTS
PY_VER="3.12"
say() { printf '\n\033[1;33m==> %s\033[0m\n' "$*"; }
ok() { printf ' \033[32mOK\033[0m %s\n' "$*"; }
skip() { printf ' \033[36m--\033[0m %s (already present)\n' "$*"; }
warn() { printf ' \033[33m!!\033[0m %s\n' "$*"; }
have() { command -v "$1" >/dev/null 2>&1; }
# ---------- OS detect ----------
OS="unknown"
case "$(uname -s)" in
Linux*) OS="linux" ;;
Darwin*) OS="macos" ;;
esac
IS_WSL=0
if [ "$OS" = "linux" ] && grep -qiE "microsoft|wsl" /proc/version 2>/dev/null; then IS_WSL=1; fi
say "Vibe Code Tours setup — OS: $OS$([ "$IS_WSL" = 1 ] && echo ' (WSL)')"
if [ "$OS" = "unknown" ]; then
warn "Unsupported shell. On native Windows: run 'wsl --install' in PowerShell,"
warn "reboot, open Ubuntu, then re-run this script inside WSL."
exit 1
fi
APT=0; [ "$OS" = "linux" ] && have apt && APT=1
# ---------- 1. Node via nvm ----------
say "1/5 Node.js (nvm + Node 22 LTS)"
export NVM_DIR="${NVM_DIR:-$HOME/.nvm}"
if [ ! -s "$NVM_DIR/nvm.sh" ]; then
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash && ok "nvm installed"
else
skip "nvm"
fi
# shellcheck disable=SC1090
# nvm's scripts are not `set -u` clean (the auto-use path dereferences unset
# vars when a Node version is already installed), which would abort here.
# Disable nounset around sourcing + nvm calls, then restore it.
set +u
[ -s "$NVM_DIR/nvm.sh" ] && . "$NVM_DIR/nvm.sh"
if have nvm; then
nvm install $NODE_TRACK >/dev/null 2>&1 && nvm use $NODE_TRACK >/dev/null 2>&1
nvm alias default node >/dev/null 2>&1
ok "Node $(node --version 2>/dev/null)"
else
warn "nvm not on PATH this shell — CLOSE this terminal, open a new one, re-run."
fi
set -u
# ---------- 2. Python via uv ----------
say "2/5 Python (uv + Python $PY_VER)"
if ! have uv; then
curl -LsSf https://astral.sh/uv/install.sh | sh
export PATH="$HOME/.local/bin:$HOME/.cargo/bin:$PATH"
ok "uv installed"
else
skip "uv"
fi
if have uv; then
uv python install "$PY_VER" >/dev/null 2>&1 || true
ok "Python $PY_VER ready (uv)"
else
warn "uv not on PATH this shell — restart terminal, re-run."
fi
# ---------- 3. Git ----------
say "3/5 Git"
if ! have git; then
if [ "$APT" = 1 ]; then sudo apt update -y && sudo apt install -y git && ok "git installed"
elif [ "$OS" = "macos" ]; then
if have brew; then brew install git && ok "git installed"
else warn "Run: xcode-select --install (provides git on macOS)"; fi
fi
else
skip "git $(git --version | awk '{print $3}')"
fi
# ---------- 4. Claude Code ----------
# Two install paths: official installer (curl) first, npm fallback for regions
# where claude.ai is blocked (Myanmar, etc — npm registry is not geo-restricted).
say "4/5 Claude Code"
if ! have claude; then
installed=0
# Path A: official installer
if curl -fsSL https://claude.ai/install.sh -o /tmp/_claude_install.sh 2>/dev/null \
&& bash /tmp/_claude_install.sh >/dev/null 2>&1; then
export PATH="$HOME/.local/bin:$PATH"
hash -r 2>/dev/null || true # flush bash command-not-found cache
if have claude || [ -x "$HOME/.local/bin/claude" ]; then
ok "Claude Code installed (official curl)"; installed=1
fi
fi
# Path B: npm fallback (works when claude.ai is blocked)
if [ "$installed" = 0 ] && have npm; then
if npm install -g @anthropic-ai/claude-code >/dev/null 2>&1; then
hash -r 2>/dev/null || true
have claude && { ok "Claude Code installed (npm fallback)"; installed=1; }
fi
fi
if [ "$installed" = 0 ]; then
warn "Claude Code install failed both paths (curl + npm)."
warn "Likely cause: claude.ai is blocked at your network/country level."
warn "For Class 1 you can still join — install an alternative CLI:"
warn " npm install -g @google/gemini-cli # generous free tier"
warn " npm install -g opencode-ai # model-agnostic"
warn "See SETUP.md 'If Claude Code is blocked in your region' for details."
fi
rm -f /tmp/_claude_install.sh
else
skip "claude $(claude --version 2>/dev/null | head -1)"
fi
# ---------- 5. opencode CLI (open-source agent CLI) ----------
# Default install alongside Claude Code. Path A = official installer, Path B = npm.
# Works in blocked regions where claude.ai is unreachable (npm registry is not geo-blocked).
say "5/5 opencode CLI"
if ! have opencode; then
installed=0
if curl -fsSL https://opencode.ai/install -o /tmp/_opencode_install.sh 2>/dev/null \
&& bash /tmp/_opencode_install.sh >/dev/null 2>&1; then
export PATH="$HOME/.opencode/bin:$HOME/.local/bin:$PATH"
hash -r 2>/dev/null || true # flush bash command-not-found cache
if have opencode || [ -x "$HOME/.opencode/bin/opencode" ]; then
ok "opencode installed (official curl)"; installed=1
fi
fi
if [ "$installed" = 0 ] && have npm; then
if npm install -g opencode-ai >/dev/null 2>&1; then
hash -r 2>/dev/null || true
have opencode && { ok "opencode installed (npm fallback)"; installed=1; }
fi
fi
if [ "$installed" = 0 ]; then
warn "opencode install failed both paths."
warn "Try manually: npm install -g opencode-ai"
fi
rm -f /tmp/_opencode_install.sh
else
skip "opencode $(opencode --version 2>/dev/null | head -1)"
fi
# ---------- verify (inline, no external file) ----------
say "Verifying"
PASS=0; FAIL=0
check() { # name cmd regex
local out
if out=$($2 2>&1); then
if echo "$out" | grep -qE "$3"; then ok "$1: $(echo "$out" | head -1)"; PASS=$((PASS+1));
else warn "$1: got '$(echo "$out" | head -1)' — expected /$3/"; FAIL=$((FAIL+1)); fi
else warn "$1: not found"; FAIL=$((FAIL+1)); fi
}
check "Node" "node --version" "^v(22|23|24)\."
check "npm" "npm --version" "^(1[0-9]|2[0-9])\."
check "Python" "python3 --version" "^Python 3\.(12|13|14)"
check "uv" "uv --version" "^uv 0\."
check "Claude Code" "claude --version" "^[2-9]\."
check "git" "git --version" "git version 2\.([3-9][0-9]|[1-9][0-9]{2})"
check "opencode" "opencode --version" "^[0-9]"
say "Result"
echo " Passed: $PASS Failed: $FAIL"
if [ "$FAIL" -eq 0 ]; then
say "All set. You're ready for Chapter 1."
else
warn "Some checks failed. Most fix with: CLOSE this terminal, open a new one, re-run this script."
warn "Still stuck? Post the exact output above in the cohort channel (#setup-help)."
exit 1
fi
# MOVED -> https://github.com/vibe-code-tours/vibecode-setup (this gist copy is deprecated)
# Vibe Code Tours — your key file.
# 1. Copy this file to: vibe-key.env
# 2. Paste your two values from the cohort channel (no spaces, no quotes).
# 3. Run: ./api-setup.sh
VIBE_PROXY=https://paste-proxy-url-from-channel
VIBE_KEY=sk-paste-your-key-from-channel
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment