install-plugins.sh 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. #!/usr/bin/env bash
  2. # ============================================================
  3. # Claude Code — Plugin installer
  4. # Run this after a fresh clone to reinstall all plugins
  5. # and their prerequisites on a new machine.
  6. #
  7. # Supports: Linux (apt/dnf/pacman), macOS (brew)
  8. # ============================================================
  9. set -euo pipefail
  10. RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; NC='\033[0m'
  11. ok() { echo -e "${GREEN}✓${NC} $1"; }
  12. warn() { echo -e "${YELLOW}⚠${NC} $1"; }
  13. info() { echo -e "${BLUE}→${NC} $1"; }
  14. err() { echo -e "${RED}✗${NC} $1"; }
  15. REPO="$(cd "$(dirname "$0")" && pwd)"
  16. # Log to file for post-mortem debugging (terminal output unchanged)
  17. LOG_FILE="$REPO/install-$(date +%Y%m%d-%H%M%S).log"
  18. exec > >(tee -a "$LOG_FILE") 2>&1
  19. # Load shared detection library
  20. # shellcheck source=lib/detect-plugins.sh
  21. source "$REPO/lib/detect-plugins.sh"
  22. # Read pinned version from plugins.lock.json
  23. # Usage: pinned_version "rtk" → prints version string or "latest"
  24. pinned_version() {
  25. local key="$1"
  26. if [ -f "$REPO/plugins.lock.json" ] && command -v python3 &>/dev/null; then
  27. python3 -c "
  28. import json, sys
  29. with open('$REPO/plugins.lock.json') as f:
  30. d = json.load(f)
  31. v = d.get('$key', {}).get('version', 'latest')
  32. print(v)
  33. " 2>/dev/null || echo "latest"
  34. else
  35. echo "latest"
  36. fi
  37. }
  38. # ============================================================
  39. # DETECT OS
  40. # ============================================================
  41. OS="unknown"
  42. PKG=""
  43. if [[ "$OSTYPE" == "darwin"* ]]; then
  44. OS="macos"
  45. elif command -v apt-get &>/dev/null; then
  46. OS="linux-apt"; PKG="apt-get"
  47. elif command -v dnf &>/dev/null; then
  48. OS="linux-dnf"; PKG="dnf"
  49. elif command -v pacman &>/dev/null; then
  50. OS="linux-pacman"; PKG="pacman"
  51. fi
  52. echo ""
  53. echo "╔══════════════════════════════════════════════════════════╗"
  54. echo "║ Claude Code — Plugin & Tool Installer ║"
  55. echo "╚══════════════════════════════════════════════════════════╝"
  56. echo ""
  57. info "OS: $OS | Repo: $REPO"
  58. echo ""
  59. # ============================================================
  60. # STEP 1 — PREREQUISITES
  61. # ============================================================
  62. echo "── Step 1: Prerequisites ───────────────────────────────────"
  63. echo ""
  64. # --- git ---
  65. if command -v git &>/dev/null; then
  66. ok "git $(git --version | awk '{print $3}')"
  67. else
  68. info "Installing git..."
  69. case $OS in
  70. macos) brew install git ;;
  71. linux-apt) sudo apt-get install -y git ;;
  72. linux-dnf) sudo dnf install -y git ;;
  73. linux-pacman) sudo pacman -S --noconfirm git ;;
  74. *) err "Cannot auto-install git on $OS — install manually"; exit 1 ;;
  75. esac
  76. ok "git installed"
  77. fi
  78. # --- Node.js (>=18) ---
  79. NODE_OK=false
  80. if command -v node &>/dev/null; then
  81. NODE_VER=$(node --version | sed 's/v//' | cut -d. -f1)
  82. if [ "$NODE_VER" -ge 18 ]; then
  83. ok "Node.js $(node --version)"; NODE_OK=true
  84. else
  85. warn "Node.js $(node --version) is too old (need >=18)"
  86. fi
  87. fi
  88. if [ "$NODE_OK" = false ]; then
  89. info "Installing Node.js 22 LTS..."
  90. case $OS in
  91. macos)
  92. brew install node@22
  93. export PATH="/opt/homebrew/opt/node@22/bin:$PATH"
  94. ;;
  95. linux-apt)
  96. curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash -
  97. sudo apt-get install -y nodejs
  98. ;;
  99. linux-dnf)
  100. curl -fsSL https://rpm.nodesource.com/setup_22.x | sudo bash -
  101. sudo dnf install -y nodejs
  102. ;;
  103. linux-pacman)
  104. sudo pacman -S --noconfirm nodejs npm
  105. ;;
  106. *) warn "Cannot auto-install Node.js on $OS — install from https://nodejs.org" ;;
  107. esac
  108. command -v node &>/dev/null && ok "Node.js $(node --version)" || err "Node.js install failed"
  109. fi
  110. # --- Rust + Cargo (for RTK) ---
  111. if command -v cargo &>/dev/null; then
  112. ok "Rust/Cargo $(cargo --version | awk '{print $2}')"
  113. else
  114. info "Installing Rust (rustup)..."
  115. curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --no-modify-path
  116. source "$HOME/.cargo/env"
  117. ok "Rust installed: $(cargo --version)"
  118. fi
  119. # --- Python 3 ---
  120. if command -v python3 &>/dev/null; then
  121. ok "Python $(python3 --version)"
  122. else
  123. info "Installing Python 3..."
  124. case $OS in
  125. macos) brew install python3 ;;
  126. linux-apt) sudo apt-get install -y python3 ;;
  127. linux-dnf) sudo dnf install -y python3 ;;
  128. linux-pacman) sudo pacman -S --noconfirm python ;;
  129. *) warn "Cannot auto-install Python on $OS" ;;
  130. esac
  131. fi
  132. # --- Claude Code CLI ---
  133. if command -v claude &>/dev/null; then
  134. ok "Claude Code $(claude --version 2>/dev/null | head -1)"
  135. else
  136. err "Claude Code not installed. Install from https://code.claude.com then re-run."
  137. exit 1
  138. fi
  139. echo ""
  140. # ============================================================
  141. # STEP 2 — GSTACK SUBMODULE
  142. # ============================================================
  143. echo "── Step 2: GStack submodule ─────────────────────────────────"
  144. echo ""
  145. # Note: GStack is managed as a git submodule in this repo.
  146. # It lives at skills-external/gstack/ and is symlinked to ~/.claude/skills/gstack/
  147. # by link.sh. Never clone it separately — use the submodule.
  148. #
  149. # First-time setup:
  150. # git submodule update --init --recursive
  151. # Update to latest:
  152. # git submodule update --remote skills-external/gstack
  153. # cd skills-external/gstack && ./setup
  154. # git add skills-external/gstack && git commit -m "chore: update gstack"
  155. GSTACK_DIR="$REPO/skills-external/gstack"
  156. if [ ! -d "$GSTACK_DIR/.git" ] && [ ! -f "$GSTACK_DIR/.git" ]; then
  157. info "Initializing GStack submodule..."
  158. cd "$REPO"
  159. git submodule update --init --recursive
  160. cd - > /dev/null
  161. fi
  162. if [ -d "$GSTACK_DIR" ]; then
  163. info "Running GStack setup..."
  164. cd "$GSTACK_DIR" && ./setup && cd - > /dev/null
  165. # Symlinks are handled by link.sh — verify it was run
  166. if [ -L "$HOME/.claude/skills/gstack" ]; then
  167. ok "GStack ready (submodule initialized, symlink OK)"
  168. else
  169. warn "GStack submodule ready but not symlinked — run: bash link.sh"
  170. fi
  171. else
  172. warn "GStack submodule directory not found after init — check .gitmodules"
  173. fi
  174. echo ""
  175. # ============================================================
  176. # STEP 3 — RTK
  177. # ============================================================
  178. echo "── Step 3: RTK — Rust Token Killer ─────────────────────────"
  179. echo ""
  180. if command -v rtk &>/dev/null; then
  181. ok "rtk already installed ($(rtk --version 2>/dev/null | head -1))"
  182. else
  183. RTK_VER=$(pinned_version "rtk")
  184. if [ "$RTK_VER" != "latest" ]; then
  185. info "Installing RTK $RTK_VER (pinned in plugins.lock.json)..."
  186. cargo install --git https://github.com/rtk-ai/rtk --tag "$RTK_VER"
  187. else
  188. info "Installing RTK (latest — consider pinning in plugins.lock.json)..."
  189. cargo install --git https://github.com/rtk-ai/rtk
  190. fi
  191. fi
  192. info "Configuring RTK PreToolUse hook (global)..."
  193. rtk init -g --auto-patch
  194. ok "RTK configured"
  195. echo ""
  196. # ============================================================
  197. # STEP 4 — GSD
  198. # ============================================================
  199. echo "── Step 4: GSD — get-shit-done ─────────────────────────────"
  200. echo ""
  201. info "Installing GSD globally..."
  202. GSD_VER=$(pinned_version "gsd")
  203. if [ "$GSD_VER" != "latest" ]; then
  204. info "Version $GSD_VER (pinned in plugins.lock.json)"
  205. npx "get-shit-done-cc@$GSD_VER" --claude --global --auto
  206. else
  207. info "Version: latest (consider pinning in plugins.lock.json)"
  208. npx get-shit-done-cc --claude --global --auto
  209. fi
  210. ok "GSD installed"
  211. echo ""
  212. # ============================================================
  213. # STEP 5 — MARKETPLACE PLUGINS (user scope, explicit)
  214. # ============================================================
  215. # All claude plugin install commands use --scope user to ensure
  216. # they install to ~/.claude/plugins/ regardless of working directory.
  217. echo "── Step 5: Marketplace plugins (scope: user) ────────────────"
  218. echo ""
  219. install_plugin() {
  220. local name="$1"
  221. local source="$2"
  222. info "Installing $name..."
  223. claude plugin install --scope user "$name@$source" 2>/dev/null \
  224. && ok "$name" \
  225. || warn "$name — skipped (already installed or failed)"
  226. }
  227. # Official Anthropic (always on)
  228. install_plugin "security-guidance" "claude-plugins-official"
  229. install_plugin "frontend-design" "claude-plugins-official"
  230. install_plugin "skill-creator" "claude-plugins-official"
  231. install_plugin "pr-review-toolkit" "claude-plugins-official"
  232. echo ""
  233. # Superpowers (always on)
  234. info "Adding Superpowers marketplace..."
  235. claude plugin marketplace add obra/superpowers-marketplace 2>/dev/null || true
  236. install_plugin "superpowers" "superpowers-marketplace"
  237. echo ""
  238. # UI/UX Pro Max (toggle)
  239. info "Adding UI/UX Pro Max marketplace..."
  240. claude plugin marketplace add nextlevelbuilder/ui-ux-pro-max-skill 2>/dev/null || true
  241. install_plugin "ui-ux-pro-max" "ui-ux-pro-max-skill"
  242. echo ""
  243. # ============================================================
  244. # STEP 6 — CONTEXT7 MCP (manual — requires API key)
  245. # ============================================================
  246. echo "── Step 6: Context7 MCP ─────────────────────────────────────"
  247. echo ""
  248. if claude mcp list 2>/dev/null | grep -q "context7"; then
  249. ok "Context7 MCP already configured"
  250. else
  251. warn "Context7 requires a free API key — cannot auto-install"
  252. echo ""
  253. echo " Steps:"
  254. echo " 1. Get a free key at https://context7.com"
  255. echo " 2. Run:"
  256. echo " claude mcp add --scope user context7 -- \\"
  257. echo " npx -y @upstash/context7-mcp --api-key YOUR_KEY"
  258. echo ""
  259. fi
  260. # ============================================================
  261. # SUMMARY
  262. # ============================================================
  263. echo ""
  264. echo "╔══════════════════════════════════════════════════════════╗"
  265. echo "║ Install Summary ║"
  266. echo "╚══════════════════════════════════════════════════════════╝"
  267. echo ""
  268. echo " ALWAYS ON (installed at user scope, ~10 tokens/session each):"
  269. echo " ✅ security-guidance — PreToolUse security hook (0 tokens)"
  270. echo " ✅ rtk — token compression hook (0 tokens)"
  271. echo " ✅ superpowers — brainstorm/plan/implement/debug workflow"
  272. echo " ✅ skill-creator — create skills from conversation"
  273. echo " ✅ pr-review-toolkit — /pr-review-toolkit:review-pr"
  274. echo ""
  275. echo " TOGGLE (installed but start OFF — /plugin-check recommends when needed):"
  276. echo " 🔄 gstack — ~/.claude/skills/gstack/ (→ submodule)"
  277. echo " 🔄 gsd — ~/.claude/skills/ (npx)"
  278. echo " 🔄 frontend-design — user scope"
  279. echo " 🔄 ui-ux-pro-max — user scope"
  280. echo " 🔄 context7 MCP — see Step 6 above"
  281. echo ""
  282. echo " All plugins installed at: user scope (~/.claude/plugins/)"
  283. echo " GStack at: ~/.claude/skills/gstack/ (symlink → submodule)"
  284. echo ""
  285. echo " → Restart Claude Code"
  286. echo " → Run /reload-plugins"
  287. echo ""