#!/usr/bin/env bash # gstack setup — build browser binary + register all skills with Claude Code set -e if ! command -v bun >/dev/null 2>&1; then echo "Error: bun is required but not installed." >&2 echo "Install it: curl -fsSL https://bun.sh/install | bash" >&2 exit 1 fi GSTACK_DIR="$(cd "$(dirname "$0")" && pwd)" SKILLS_DIR="$(dirname "$GSTACK_DIR")" BROWSE_BIN="$GSTACK_DIR/browse/dist/browse" ensure_playwright_browser() { ( cd "$GSTACK_DIR" bun --eval 'import { chromium } from "playwright"; const browser = await chromium.launch(); await browser.close();' ) >/dev/null 2>&1 } # 1. Build browse binary if needed (smart rebuild: stale sources, package.json, lock) NEEDS_BUILD=0 if [ ! -x "$BROWSE_BIN" ]; then NEEDS_BUILD=1 elif [ -n "$(find "$GSTACK_DIR/browse/src" -type f -newer "$BROWSE_BIN" -print -quit 2>/dev/null)" ]; then NEEDS_BUILD=1 elif [ "$GSTACK_DIR/package.json" -nt "$BROWSE_BIN" ]; then NEEDS_BUILD=1 elif [ -f "$GSTACK_DIR/bun.lock" ] && [ "$GSTACK_DIR/bun.lock" -nt "$BROWSE_BIN" ]; then NEEDS_BUILD=1 fi if [ "$NEEDS_BUILD" -eq 1 ]; then echo "Building browse binary..." ( cd "$GSTACK_DIR" bun install bun run build ) # Safety net: write .version if build script didn't (e.g., git not available during build) if [ ! -f "$GSTACK_DIR/browse/dist/.version" ]; then git -C "$GSTACK_DIR" rev-parse HEAD > "$GSTACK_DIR/browse/dist/.version" 2>/dev/null || true fi fi if [ ! -x "$BROWSE_BIN" ]; then echo "gstack setup failed: browse binary missing at $BROWSE_BIN" >&2 exit 1 fi # 2. Ensure Playwright's Chromium is available if ! ensure_playwright_browser; then echo "Installing Playwright Chromium..." ( cd "$GSTACK_DIR" bunx playwright install chromium ) fi if ! ensure_playwright_browser; then echo "gstack setup failed: Playwright Chromium could not be launched" >&2 exit 1 fi # 3. Ensure ~/.gstack global state directory exists mkdir -p "$HOME/.gstack/projects" # 4. Only create skill symlinks if we're inside a .claude/skills directory SKILLS_BASENAME="$(basename "$SKILLS_DIR")" if [ "$SKILLS_BASENAME" = "skills" ]; then linked=() for skill_dir in "$GSTACK_DIR"/*/; do if [ -f "$skill_dir/SKILL.md" ]; then skill_name="$(basename "$skill_dir")" # Skip node_modules [ "$skill_name" = "node_modules" ] && continue target="$SKILLS_DIR/$skill_name" # Create or update symlink; skip if a real file/directory exists if [ -L "$target" ] || [ ! -e "$target" ]; then ln -snf "gstack/$skill_name" "$target" linked+=("$skill_name") fi fi done echo "gstack ready." echo " browse: $BROWSE_BIN" if [ ${#linked[@]} -gt 0 ]; then echo " linked skills: ${linked[*]}" fi else echo "gstack ready." echo " browse: $BROWSE_BIN" echo " (skipped skill symlinks — not inside .claude/skills/)" fi # 4. First-time welcome + legacy cleanup if [ ! -d "$HOME/.gstack" ]; then mkdir -p "$HOME/.gstack" echo " Welcome! Run /gstack-upgrade anytime to stay current." fi rm -f /tmp/gstack-latest-version