Skip to content

Instantly share code, notes, and snippets.

@ASRagab
Last active March 2, 2026 22:53
Show Gist options
  • Select an option

  • Save ASRagab/2d1eb00babcc0f10adecb507eefb8f39 to your computer and use it in GitHub Desktop.

Select an option

Save ASRagab/2d1eb00babcc0f10adecb507eefb8f39 to your computer and use it in GitHub Desktop.
HAI Operators — Prerequisite Bootstrap (test)
#!/usr/bin/env bash
# HAI Operators — Prerequisite Bootstrap
# Installs system-level prerequisites, provisions Claude Code settings,
# and optionally installs plugins.
# Safe to run multiple times (idempotent).
#
# Usage:
# bash bootstrap.sh (interactive)
# curl -fsSL <url> | bash (remote)
set -euo pipefail
# --- Colors & formatting ---
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
BOLD='\033[1m'
NC='\033[0m'
info() { printf "${BLUE}==>${NC} ${BOLD}%s${NC}\n" "$*"; }
ok() { printf "${GREEN} ✓${NC} %s\n" "$*"; }
warn() { printf "${YELLOW} !${NC} %s\n" "$*"; }
fail() { printf "${RED} ✗${NC} %s\n" "$*"; }
# --- Pipe mode detection ---
# When run via curl|bash, stdin is the script — read prompts won't work.
# Detect this and auto-proceed with installs.
INTERACTIVE=true
if [ ! -t 0 ]; then
INTERACTIVE=false
info "Running in non-interactive mode (pipe detected). Will auto-install missing tools."
fi
# Helper: prompt user or auto-accept in pipe mode
confirm() {
local prompt="$1" default="${2:-Y}"
if [ "$INTERACTIVE" = false ]; then
return 0 # auto-yes
fi
read -rp "$prompt" answer
case "${answer:-$default}" in
[Yy]*) return 0 ;;
*) return 1 ;;
esac
}
# --- State tracking ---
declare -a MISSING=()
declare -a INSTALLED=()
declare -a FAILED=()
declare -a SKIPPED=()
# --- Detection functions ---
detect_brew() {
if command -v brew &>/dev/null; then
ok "Homebrew $(brew --version 2>/dev/null | head -1 | awk '{print $2}')"
return 0
fi
MISSING+=("brew")
return 1
}
detect_python() {
if command -v python3 &>/dev/null; then
local ver major minor
ver=$(python3 --version 2>/dev/null | awk '{print $2}')
major=$(echo "$ver" | cut -d. -f1)
minor=$(echo "$ver" | cut -d. -f2)
if [ "$major" -ge 3 ] && [ "$minor" -ge 12 ]; then
ok "Python $ver"
return 0
else
warn "Python $ver found (need 3.12+)"
MISSING+=("python")
return 1
fi
fi
MISSING+=("python")
return 1
}
detect_node() {
if command -v node &>/dev/null; then
local ver major
ver=$(node --version 2>/dev/null | sed 's/^v//')
major=$(echo "$ver" | cut -d. -f1)
if [ "$major" -ge 22 ]; then
ok "Node.js v$ver"
return 0
else
warn "Node.js v$ver found (need 22+)"
MISSING+=("node")
return 1
fi
fi
MISSING+=("node")
return 1
}
detect_gh() {
if command -v gh &>/dev/null; then
ok "GitHub CLI $(gh --version 2>/dev/null | head -1 | awk '{print $3}')"
return 0
fi
MISSING+=("gh")
return 1
}
detect_gh_auth() {
if gh auth status &>/dev/null 2>&1; then
ok "GitHub CLI authenticated"
return 0
fi
MISSING+=("gh_auth")
return 1
}
detect_gcloud() {
if command -v gcloud &>/dev/null; then
ok "gcloud CLI $(gcloud --version 2>/dev/null | head -1 | awk '{print $NF}')"
return 0
fi
MISSING+=("gcloud")
return 1
}
# --- Install functions ---
install_brew() {
info "Installing Homebrew..."
/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
if command -v brew &>/dev/null; then
INSTALLED+=("Homebrew")
ok "Homebrew installed"
elif [ -f /opt/homebrew/bin/brew ]; then
eval "$(/opt/homebrew/bin/brew shellenv)"
INSTALLED+=("Homebrew")
ok "Homebrew installed (run 'eval \$(/opt/homebrew/bin/brew shellenv)' in new terminals)"
else
FAILED+=("Homebrew — see https://brew.sh for manual install")
fail "Homebrew install failed"
fi
}
install_python() {
info "Installing Python 3.12 via Homebrew..."
if brew install python@3.12; then
if python3 --version 2>/dev/null | grep -q "3\.1[2-9]"; then
INSTALLED+=("Python 3.12")
ok "Python 3.12 installed"
else
warn "Python installed but not on PATH. You may need to restart your terminal."
INSTALLED+=("Python 3.12 (restart terminal)")
fi
else
FAILED+=("Python 3.12 — try: brew install python@3.12")
fail "Python 3.12 install failed"
fi
}
install_node() {
info "Installing Node.js 22 via Homebrew..."
if brew install node@22; then
INSTALLED+=("Node.js 22")
ok "Node.js 22 installed"
else
FAILED+=("Node.js 22 — try: brew install node@22")
fail "Node.js 22 install failed"
fi
}
install_gh() {
info "Installing GitHub CLI via Homebrew..."
if brew install gh; then
INSTALLED+=("GitHub CLI")
ok "GitHub CLI installed"
else
FAILED+=("GitHub CLI — try: brew install gh")
fail "GitHub CLI install failed"
fi
}
run_gh_auth() {
info "Authenticating GitHub CLI..."
echo "A browser window will open. Sign in with your GitHub account and authorize the CLI."
echo ""
if gh auth login --web; then
INSTALLED+=("GitHub auth")
ok "GitHub CLI authenticated"
else
FAILED+=("GitHub auth — try: gh auth login --web")
fail "GitHub CLI auth failed"
fi
}
install_gcloud() {
info "Installing gcloud CLI via Homebrew..."
if brew install google-cloud-sdk; then
INSTALLED+=("gcloud CLI")
ok "gcloud CLI installed"
else
FAILED+=("gcloud CLI — try: brew install google-cloud-sdk")
fail "gcloud CLI install failed"
fi
}
# --- Config provisioning ---
# Template files ship at scripts/configs/ inside the plugin repo.
# Resolve SCRIPT_DIR so we can find them relative to this script.
# When piped via curl|bash, BASH_SOURCE is unset — fall back gracefully.
if [ -n "${BASH_SOURCE[0]:-}" ]; then
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
else
SCRIPT_DIR=""
fi
provision_configs() {
local config_dir="${CLAUDE_CONFIG_DIR:-$HOME/.claude}"
mkdir -p "$config_dir"
if [ -z "$SCRIPT_DIR" ]; then
warn "Running from pipe — config templates not available. Run /hai-setup inside Claude Code to provision settings."
SKIPPED+=("settings.json (pipe mode)" "CLAUDE.md (pipe mode)")
return
fi
# --- settings.json ---
local settings_src="$SCRIPT_DIR/configs/settings.json"
local settings_dst="$config_dir/settings.json"
if [ -f "$settings_src" ]; then
if [ -f "$settings_dst" ]; then
info "settings.json already exists at $settings_dst"
if confirm "Overwrite with HAI recommended settings? [y/N] " "N"; then
cp "$settings_src" "$settings_dst"
INSTALLED+=("settings.json")
ok "Settings provisioned at $settings_dst"
else
SKIPPED+=("settings.json")
fi
else
cp "$settings_src" "$settings_dst"
INSTALLED+=("settings.json")
ok "Settings provisioned at $settings_dst"
fi
else
warn "Template settings.json not found at $settings_src — skipping"
fi
# --- CLAUDE.md ---
local claude_src="$SCRIPT_DIR/configs/CLAUDE.md"
local claude_dst="$config_dir/CLAUDE.md"
if [ -f "$claude_src" ]; then
if [ -f "$claude_dst" ]; then
info "CLAUDE.md already exists at $claude_dst"
if confirm "Overwrite with HAI team instructions? [y/N] " "N"; then
cp "$claude_src" "$claude_dst"
INSTALLED+=("CLAUDE.md")
ok "CLAUDE.md provisioned at $claude_dst"
else
SKIPPED+=("CLAUDE.md")
fi
else
cp "$claude_src" "$claude_dst"
INSTALLED+=("CLAUDE.md")
ok "CLAUDE.md provisioned at $claude_dst"
fi
else
warn "Template CLAUDE.md not found at $claude_src — skipping"
fi
}
# --- Plugin installation ---
install_plugins() {
if ! command -v claude &>/dev/null; then
warn "Claude Code CLI not found. Install plugins manually after launching Claude Code."
return
fi
echo ""
if ! confirm "Install HAI plugins via Claude CLI now? [Y/n] "; then
SKIPPED+=("Plugin installation")
return
fi
info "Adding marketplace..."
claude plugin marketplace add git@github.com:joinhandshake/claude-operators.git 2>/dev/null || true
info "Installing plugins..."
if claude plugin install hai-operators@claude-operators 2>/dev/null; then ok "hai-operators installed"; else warn "hai-operators install failed"; fi
if claude plugin install hai-database@claude-operators 2>/dev/null; then ok "hai-database installed"; else warn "hai-database install failed"; fi
if claude plugin install hai-playbook@claude-operators 2>/dev/null; then ok "hai-playbook installed"; else warn "hai-playbook install failed"; fi
if claude plugin install slack@claude-plugins-official 2>/dev/null; then ok "slack installed"; else warn "slack install failed"; fi
INSTALLED+=("HAI plugins")
}
# --- Summary ---
print_summary() {
echo ""
printf "${BOLD}%s${NC}\n" "═══════════════════════════════════════"
printf "${BOLD}%s${NC}\n" " HAI Operators — Bootstrap Summary"
printf "${BOLD}%s${NC}\n" "═══════════════════════════════════════"
echo ""
if [ ${#INSTALLED[@]} -gt 0 ]; then
printf '%b\n' "${GREEN}Installed/Configured:${NC}"
for item in "${INSTALLED[@]}"; do printf ' %b %s\n' "${GREEN}✓${NC}" "$item"; done
echo ""
fi
if [ ${#SKIPPED[@]} -gt 0 ]; then
printf '%b\n' "${BLUE}Already installed / Skipped:${NC}"
for item in "${SKIPPED[@]}"; do printf ' %b %s\n' "${BLUE}—${NC}" "$item"; done
echo ""
fi
if [ ${#FAILED[@]} -gt 0 ]; then
printf '%b\n' "${RED}Failed:${NC}"
for item in "${FAILED[@]}"; do printf ' %b %s\n' "${RED}✗${NC}" "$item"; done
echo ""
fi
echo ""
if [ ${#FAILED[@]} -gt 0 ]; then
printf '%b\n' "${RED}${BOLD}Some items failed (see above).${NC}"
elif [ ${#SKIPPED[@]} -gt 0 ]; then
printf '%b\n' "${YELLOW}${BOLD}Some items were skipped. Re-run this script when ready.${NC}"
else
printf '%b\n' "${GREEN}${BOLD}All prerequisites installed!${NC}"
fi
echo ""
printf '%b\n' "${BOLD}Next step:${NC} Open Claude Code and run ${BOLD}/hai-setup${NC}"
echo ""
}
# --- Main ---
main() {
echo ""
printf "${BOLD}%s${NC}\n" "HAI Operators — Prerequisite Bootstrap"
printf "%s\n" "Checking your system for required tools..."
echo ""
# Phase 1: Detect everything
detect_brew || true
detect_python || true
detect_node || true
detect_gh || true
detect_gcloud || true
echo ""
# If nothing missing, just check gh auth
if [ ${#MISSING[@]} -eq 0 ]; then
info "All tools are installed!"
SKIPPED+=("Homebrew" "Python 3.12+" "Node.js 22+" "GitHub CLI" "gcloud CLI")
detect_gh_auth || MISSING+=("gh_auth")
fi
# Phase 2: Confirm and install missing items
if [ ${#MISSING[@]} -gt 0 ]; then
echo ""
info "The following items need to be installed or configured:"
for item in "${MISSING[@]}"; do
case "$item" in
brew) printf " • Homebrew (macOS package manager)\n" ;;
python) printf " • Python 3.12+ (for Drive scripts)\n" ;;
node) printf " • Node.js 22+ (for gcloud MCP)\n" ;;
gh) printf " • GitHub CLI (for plugin installation)\n" ;;
gh_auth) printf " • GitHub CLI authentication\n" ;;
gcloud) printf " • gcloud CLI (for BigQuery and Drive)\n" ;;
esac
done
echo ""
if confirm "Install/configure these now? [Y/n] "; then
for item in "${MISSING[@]}"; do
case "$item" in
brew) install_brew ;;
python) install_python ;;
node) install_node ;;
gh) install_gh ;;
gh_auth) run_gh_auth ;;
gcloud) install_gcloud ;;
esac
done
else
info "Skipped. You can re-run this script anytime."
for item in "${MISSING[@]}"; do SKIPPED+=("$item"); done
fi
fi
# Phase 3: Check gh auth if gh was just installed
if command -v gh &>/dev/null; then
if ! gh auth status &>/dev/null 2>&1; then
if [[ ! " ${MISSING[*]} " =~ " gh_auth " ]] && [[ ! " ${FAILED[*]} " == *"GitHub auth"* ]]; then
echo ""
if confirm "GitHub CLI is not authenticated. Log in now? [Y/n] "; then
run_gh_auth
fi
fi
fi
fi
# Phase 4: Provision Claude Code configs (settings.json + CLAUDE.md)
echo ""
provision_configs
# Phase 5: Install plugins (if claude CLI available)
install_plugins
print_summary
}
main "$@"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment