#!/usr/bin/env bash # vibecom installer — installs the CLI. Collects nothing. # # Source: https://github.com/DugboTek/vibecom-cli # Collected: https://github.com/DugboTek/vibecom-cli/blob/main/COLLECTION.md # # Installing does not enable telemetry by itself. Guided setup shows the exact # personal projects it found and changes nothing unless you confirm. # Verify the bundle you are about to run: # curl -fsSL https://www.vibecom.build/cli.js | shasum -a 256 set -euo pipefail ORIGIN="https://www.vibecom.build" BIN_DIR="${VIBECOM_BIN_DIR:-${VIBELAND_BIN_DIR:-$HOME/.local/bin}}" TARGET="$BIN_DIR/vibecom" CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/vibecom" # --- presentation ----------------------------------------------------------- # 24-bit colour when attached to a terminal; plain text when piped or NO_COLOR. if [ -t 1 ] && [ -z "${NO_COLOR:-}" ] && [ "${TERM:-}" != "dumb" ]; then COLOR=1; else COLOR=0; fi # community blue → signal lime, one stop per line. Deliberately NOT per-character: # the wordmark is box-drawing UTF-8, and every portable byte-slicing tool # (awk substr, cut, ${x:i:1}) splits multi-byte characters into mush. RAMP="69;174;242 79;194;227 100;215;194 129;226;157 159;236;123 217;255;87" grad_row() { # $1 text, $2 zero-based row if [ "$COLOR" -eq 0 ]; then printf '%s' "$1"; return; fi i=0 for c in $RAMP; do if [ "$i" -eq "$2" ]; then printf '\033[38;2;%sm%s\033[0m' "$c" "$1"; return; fi i=$((i + 1)) done printf '%s' "$1" } grad() { grad_row "$1" 2; } dim() { if [ "$COLOR" -eq 1 ]; then printf '\033[2m%s\033[0m' "$1"; else printf '%s' "$1"; fi; } green(){ if [ "$COLOR" -eq 1 ]; then printf '\033[32m%s\033[0m' "$1"; else printf '%s' "$1"; fi; } step() { printf ' %s %s' "$(dim '◇')" "$1"; } done_() { printf '\r %s %s\033[K\n' "$(green '✔')" "$1"; } fail() { printf '\r \033[31m✖\033[0m %s\033[K\n' "$1" >&2; } echo if [ "${COLUMNS:-$(tput cols 2>/dev/null || echo 80)}" -ge 66 ]; then ROW=0 while IFS= read -r line; do printf ' %s\n' "$(grad_row "$line" "$ROW")" ROW=$((ROW + 1)) done <<'ART' ██╗ ██╗██╗██████╗ ███████╗ ██████╗ ██████╗ ███╗ ███╗ ██║ ██║██║██╔══██╗██╔════╝██╔════╝██╔═══██╗████╗ ████║ ██║ ██║██║██████╔╝█████╗ ██║ ██║ ██║██╔████╔██║ ╚██╗ ██╔╝██║██╔══██╗██╔══╝ ██║ ██║ ██║██║╚██╔╝██║ ╚████╔╝ ██║██████╔╝███████╗╚██████╗╚██████╔╝██║ ╚═╝ ██║ ╚═══╝ ╚═╝╚═════╝ ╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝ ART else printf ' %s\n' "$(grad '◆ vibecom')" fi printf ' %s\n\n' "$(dim 'installer — collects nothing')" # --- checks ----------------------------------------------------------------- step "checking prerequisites" command -v curl >/dev/null 2>&1 || { fail "curl is required"; exit 1; } if ! command -v node >/dev/null 2>&1; then fail "Node.js 20+ is required (the CLI is a single bundled node script)" printf ' %s\n' "$(dim 'https://nodejs.org — or: brew install node')" >&2 exit 1 fi NODE_MAJOR=$(node -p 'process.versions.node.split(".")[0]') if [ "$NODE_MAJOR" -lt 20 ]; then fail "Node.js 20+ required, found $(node -v)" exit 1 fi done_ "node $(node -v | tr -d v)" mkdir -p "$BIN_DIR" # A temp DIRECTORY, so the file keeps a .js name: mktemp's random suffix would # read as an unknown file extension and `node --check` would refuse to parse it. TMPDIR_X=$(mktemp -d "${TMPDIR:-/tmp}/vibecom.XXXXXX") TMP="$TMPDIR_X/cli.js" trap 'rm -rf "$TMPDIR_X"' EXIT step "downloading from $ORIGIN" curl -fsSL "$ORIGIN/cli.js" -o "$TMP" || { fail "could not download the CLI from $ORIGIN"; exit 1; } SIZE=$(wc -c < "$TMP" | tr -d ' ') done_ "downloaded $(dim "$((SIZE / 1024))K")" step "verifying" head -n 1 "$TMP" | grep -q '^#!/usr/bin/env node' || { fail "that does not look like the CLI"; exit 1; } if ! node --check "$TMP" 2>"$TMPDIR_X/err"; then fail "downloaded CLI failed to parse" sed 's/^/ /' "$TMPDIR_X/err" >&2 exit 1 fi done_ "shebang and syntax" step "installing" chmod 755 "$TMP" mv "$TMP" "$TARGET" rm -rf "$TMPDIR_X" trap - EXIT # Remember where this copy came from. Overridden by the host you log into and # by VIBECOM_ORIGIN, so an install from localhost can still target production. mkdir -p "$CONFIG_DIR" chmod 700 "$CONFIG_DIR" printf '%s\n' "$ORIGIN" > "$CONFIG_DIR/origin" done_ "$(dim "$TARGET")" echo # The rename left the old binary in place with its own config directory, so # muscle memory ran a stale build against stale project links — including # origins that pointed at a localhost dev server and silently failed to report. # Replace it with a shim: the old name keeps working and always uses the # current binary and the migrated config. LEGACY="$BIN_DIR/vibeland" if [ -f "$LEGACY" ] && [ "$LEGACY" != "$TARGET" ]; then { echo '#!/usr/bin/env bash' echo '# vibeland was renamed to vibecom. Forwarding so the old name keeps working.' echo 'printf " vibeland is now vibecom - forwarding\n" >&2' echo 'exec "'"$TARGET"'" "$@"' } > "$LEGACY" chmod 755 "$LEGACY" done_ "$(dim 'vibeland now forwards to vibecom')" fi printf ' %s %s\n' "$(green '✔')" "$(green 'Nothing is being collected.')" printf ' %s\n' "$(dim 'Your shell profile, Claude Code settings, and Codex')" printf ' %s\n' "$(dim 'config were not touched.')" echo case ":$PATH:" in *":$BIN_DIR:"*) ;; *) printf ' %s\n' "$(dim "$BIN_DIR is not on your PATH — add it with:")" printf ' export PATH="%s:$PATH"\n\n' "$BIN_DIR" ;; esac printf ' %s\n' "$(grad 'starting setup')" printf ' %s\n' "$(dim 'Your browser will open so you can sign in safely.')" printf ' %s\n\n' "$(dim 'You can change this anytime.')" # curl occupies stdin with this script. Reattach the CLI to the terminal so # the copied command remains one uninterrupted setup flow. if [ -r /dev/tty ]; then # The wordmark is already on screen. Telling the CLI keeps the handoff # reading as one continuous setup instead of two programs starting. VIBECOM_BANNER_ALREADY_SHOWN=1 "$TARGET"