|
| 1 | +#!/bin/sh |
| 2 | +# skill-sommelier bootstrap installer. |
| 3 | +# |
| 4 | +# Prepares the environment for the skill-sommelier Claude Code plugin: |
| 5 | +# 1. Claude Code (required) — installs if missing |
| 6 | +# 2. gh (optional companion) — used by ss-skill-discover |
| 7 | +# 3. uv (optional companion) — used by Python-flavored skills |
| 8 | +# 4. Prints the two slash commands you paste into Claude Code to add |
| 9 | +# the marketplace and install the plugin. |
| 10 | +# |
| 11 | +# Plugin install itself happens *inside* Claude Code — there is no shell |
| 12 | +# entry point for `/plugin marketplace add`. This script just guarantees |
| 13 | +# the prerequisites are in place. |
| 14 | +# |
| 15 | +# Usage: |
| 16 | +# curl -LsSf https://raw.githubusercontent.com/JasonLo/skill-sommelier/v0.6.0/scripts/install.sh | sh |
| 17 | +# |
| 18 | +# Flags / env: |
| 19 | +# --yes, INSTALLER_ASSUME_YES=1 non-interactive; required deps Y, optional N |
| 20 | +# INSTALL_CLAUDE_CODE=0 never auto-install Claude Code |
| 21 | +# INSTALL_GH=1 opt in to gh during unattended runs |
| 22 | +# INSTALL_UV=1 opt in to uv during unattended runs |
| 23 | + |
| 24 | +set -eu |
| 25 | + |
| 26 | +ASSUME_YES="${INSTALLER_ASSUME_YES:-0}" |
| 27 | +INSTALL_CLAUDE_CODE_OVERRIDE="${INSTALL_CLAUDE_CODE:-}" |
| 28 | +INSTALL_GH_OVERRIDE="${INSTALL_GH:-}" |
| 29 | +INSTALL_UV_OVERRIDE="${INSTALL_UV:-}" |
| 30 | + |
| 31 | +while [ $# -gt 0 ]; do |
| 32 | + case "$1" in |
| 33 | + -y|--yes) ASSUME_YES=1; shift ;; |
| 34 | + -h|--help) |
| 35 | + sed -n '2,22p' "$0" | sed 's/^# \{0,1\}//' |
| 36 | + exit 0 ;; |
| 37 | + *) printf 'Unknown flag: %s\n' "$1" >&2; exit 1 ;; |
| 38 | + esac |
| 39 | +done |
| 40 | + |
| 41 | +# Pipe-from-curl into a non-TTY shell (CI, Docker RUN, devcontainers) → unattended. |
| 42 | +if [ ! -t 0 ] && [ ! -r /dev/tty ]; then |
| 43 | + ASSUME_YES=1 |
| 44 | +fi |
| 45 | + |
| 46 | +# --------------------------------------------------------------------------- |
| 47 | +# helpers |
| 48 | +# --------------------------------------------------------------------------- |
| 49 | + |
| 50 | +confirm() { |
| 51 | + # confirm "<prompt>" <default Y|N> |
| 52 | + prompt="$1" |
| 53 | + default="${2:-N}" |
| 54 | + if [ "$ASSUME_YES" = "1" ]; then |
| 55 | + [ "$default" = "Y" ] |
| 56 | + return |
| 57 | + fi |
| 58 | + printf '%s ' "$prompt" |
| 59 | + read -r reply < /dev/tty || reply="" |
| 60 | + case "$reply" in |
| 61 | + [Yy]) return 0 ;; |
| 62 | + [Nn]) return 1 ;; |
| 63 | + "") [ "$default" = "Y" ] ;; |
| 64 | + *) return 1 ;; |
| 65 | + esac |
| 66 | +} |
| 67 | + |
| 68 | +have() { command -v "$1" >/dev/null 2>&1; } |
| 69 | + |
| 70 | +# Detect a usable system package manager for installing gh. Empty if none. |
| 71 | +detect_pkg_mgr() { |
| 72 | + if have brew; then echo brew |
| 73 | + elif have apt-get;then echo apt |
| 74 | + elif have dnf; then echo dnf |
| 75 | + elif have yum; then echo yum |
| 76 | + elif have pacman; then echo pacman |
| 77 | + elif have zypper; then echo zypper |
| 78 | + fi |
| 79 | +} |
| 80 | + |
| 81 | +# Download a remote install script to a tmp file, then execute. Never pipe |
| 82 | +# third-party URLs straight to a shell — the file goes to disk first so it |
| 83 | +# could be reviewed if anything looks off. |
| 84 | +run_remote_installer() { |
| 85 | + url="$1" |
| 86 | + label="$2" |
| 87 | + runner="${3:-sh}" |
| 88 | + tmp="$(mktemp "${TMPDIR:-/tmp}/skill-sommelier-${label}.XXXXXX.sh")" |
| 89 | + trap 'rm -f "$tmp"' EXIT HUP INT TERM |
| 90 | + if ! curl -fsSL "$url" -o "$tmp"; then |
| 91 | + printf ' ✗ failed to download %s installer from %s\n' "$label" "$url" >&2 |
| 92 | + rm -f "$tmp" |
| 93 | + trap - EXIT HUP INT TERM |
| 94 | + return 1 |
| 95 | + fi |
| 96 | + "$runner" "$tmp" |
| 97 | + rc=$? |
| 98 | + rm -f "$tmp" |
| 99 | + trap - EXIT HUP INT TERM |
| 100 | + return $rc |
| 101 | +} |
| 102 | + |
| 103 | +echo "=== skill-sommelier bootstrap ===" |
| 104 | +echo |
| 105 | + |
| 106 | +# --------------------------------------------------------------------------- |
| 107 | +# [1/4] Claude Code (required) |
| 108 | +# --------------------------------------------------------------------------- |
| 109 | + |
| 110 | +printf '[1/4] Checking Claude Code... ' |
| 111 | +if have claude; then |
| 112 | + cc_version="$(claude --version 2>/dev/null | head -n1 || echo 'installed')" |
| 113 | + printf '✓ %s\n' "$cc_version" |
| 114 | +else |
| 115 | + echo "not found" |
| 116 | + if [ "$INSTALL_CLAUDE_CODE_OVERRIDE" = "0" ]; then |
| 117 | + echo " INSTALL_CLAUDE_CODE=0 set — skipping. Install Claude Code yourself, then re-run." |
| 118 | + exit 1 |
| 119 | + fi |
| 120 | + if confirm " Install Claude Code? (Y/n)" "Y"; then |
| 121 | + installed=0 |
| 122 | + # Anthropic's native installer (preferred — no Node required). |
| 123 | + if run_remote_installer "https://claude.ai/install.sh" "claude-code" "bash"; then |
| 124 | + installed=1 |
| 125 | + elif have npm; then |
| 126 | + echo " native installer failed — falling back to npm" |
| 127 | + if npm install -g @anthropic-ai/claude-code; then |
| 128 | + installed=1 |
| 129 | + fi |
| 130 | + fi |
| 131 | + if [ "$installed" = "0" ]; then |
| 132 | + echo " ✗ Could not install Claude Code automatically." >&2 |
| 133 | + echo " See https://docs.claude.com/claude-code for manual instructions." >&2 |
| 134 | + exit 1 |
| 135 | + fi |
| 136 | + if ! have claude; then |
| 137 | + echo " ⚠ 'claude' is installed but not on PATH yet." |
| 138 | + echo " Open a new shell, or follow the installer's PATH hint above." |
| 139 | + fi |
| 140 | + else |
| 141 | + echo " Skipped. skill-sommelier requires Claude Code — install it and re-run." |
| 142 | + exit 1 |
| 143 | + fi |
| 144 | +fi |
| 145 | +echo |
| 146 | + |
| 147 | +# --------------------------------------------------------------------------- |
| 148 | +# [2/4] gh — optional, used by ss-skill-discover for GitHub search |
| 149 | +# --------------------------------------------------------------------------- |
| 150 | + |
| 151 | +printf '[2/4] Optional: gh CLI (for ss-skill-discover)... ' |
| 152 | +if have gh; then |
| 153 | + printf '✓ %s\n' "$(gh --version 2>/dev/null | head -n1)" |
| 154 | +else |
| 155 | + echo "not found" |
| 156 | + default_gh="N" |
| 157 | + [ "$INSTALL_GH_OVERRIDE" = "1" ] && default_gh="Y" |
| 158 | + if confirm " Install gh? (y/N)" "$default_gh"; then |
| 159 | + pm="$(detect_pkg_mgr)" |
| 160 | + case "$pm" in |
| 161 | + brew) brew install gh ;; |
| 162 | + apt) sudo -v && sudo apt-get update && sudo apt-get install -y gh ;; |
| 163 | + dnf) sudo -v && sudo dnf install -y gh ;; |
| 164 | + yum) sudo -v && sudo yum install -y gh ;; |
| 165 | + pacman) sudo -v && sudo pacman -S --noconfirm github-cli ;; |
| 166 | + zypper) sudo -v && sudo zypper install -y gh ;; |
| 167 | + *) |
| 168 | + echo " ⚠ No supported package manager detected." |
| 169 | + echo " See https://github.com/cli/cli#installation" |
| 170 | + ;; |
| 171 | + esac |
| 172 | + have gh && printf ' ✓ gh installed\n' || true |
| 173 | + else |
| 174 | + echo " Skipped. ss-skill-discover will prompt you for gh later if it needs it." |
| 175 | + fi |
| 176 | +fi |
| 177 | +echo |
| 178 | + |
| 179 | +# --------------------------------------------------------------------------- |
| 180 | +# [3/4] uv — optional, used by Python skills (ss-modern-python, ss-python-to-chtc, ...) |
| 181 | +# --------------------------------------------------------------------------- |
| 182 | + |
| 183 | +printf '[3/4] Optional: uv (for Python skills)... ' |
| 184 | +if have uv; then |
| 185 | + printf '✓ %s\n' "$(uv --version 2>/dev/null | head -n1)" |
| 186 | +else |
| 187 | + echo "not found" |
| 188 | + default_uv="N" |
| 189 | + [ "$INSTALL_UV_OVERRIDE" = "1" ] && default_uv="Y" |
| 190 | + if confirm " Install uv? (y/N)" "$default_uv"; then |
| 191 | + if run_remote_installer "https://astral.sh/uv/install.sh" "uv" "sh"; then |
| 192 | + have uv && printf ' ✓ uv installed\n' || \ |
| 193 | + echo " ⚠ uv installed but not on PATH yet — open a new shell." |
| 194 | + else |
| 195 | + echo " ✗ uv install failed. See https://docs.astral.sh/uv/" >&2 |
| 196 | + fi |
| 197 | + else |
| 198 | + echo " Skipped. The Python-flavored skills will work without it, but slower." |
| 199 | + fi |
| 200 | +fi |
| 201 | +echo |
| 202 | + |
| 203 | +# --------------------------------------------------------------------------- |
| 204 | +# [4/4] Plugin install — runs inside Claude Code |
| 205 | +# --------------------------------------------------------------------------- |
| 206 | + |
| 207 | +cat <<'EOF' |
| 208 | +[4/4] Bootstrap complete. |
| 209 | +
|
| 210 | +Open Claude Code and paste these two commands: |
| 211 | +
|
| 212 | + /plugin marketplace add JasonLo/skill-sommelier |
| 213 | + /plugin install skill-sommelier@skill-sommelier |
| 214 | +
|
| 215 | +Quickstart once installed: |
| 216 | +
|
| 217 | + /skill-sommelier:ss-skill-discover |
| 218 | +
|
| 219 | +Updates: |
| 220 | + /skill-sommelier:ss-update — check + apply skill-sommelier updates |
| 221 | + /plugin marketplace update — refresh the marketplace cache |
| 222 | +
|
| 223 | +Uninstall later: |
| 224 | + curl -LsSf https://raw.githubusercontent.com/JasonLo/skill-sommelier/v0.6.0/scripts/uninstall.sh | sh |
| 225 | +EOF |
0 commit comments