#!/usr/bin/env bash # update.sh — Full uplift cycle for claude-skills on Linux/macOS (and git-bash on Windows) # # Steps: # 1. git pull --ff-only in claude-skills repo # 2. Conditionally rebuild projects-meta-mcp (if source changed) # 3. Conditionally rebuild interns-mcp (if source changed) # 4. Install all skills via install.sh # 5. Show version diff (before → after) # 6. Print reload hints # # Usage: update.sh [--yes] # --yes — skip confirmation prompts (assume yes) set -euo pipefail YES_MODE=false [[ "${1:-}" == "--yes" ]] && YES_MODE=true # ── Paths ────────────────────────────────────────────────────────────────── ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" SKILLS_SRC="$ROOT/skills" TARGET="${CLAUDE_SKILLS_DIR:-$HOME/.claude/skills}" COMMON_ROOT="${COMMON_ROOT:-$HOME/projects/.common}" META_MCP="$COMMON_ROOT/lib/projects-meta-mcp" INTERNS_MCP="$COMMON_ROOT/lib/interns-mcp" # ── Helpers ──────────────────────────────────────────────────────────────── info() { echo -e "\033[1;34m[update]\033[0m $*"; } ok() { echo -e "\033[1;32m[ok]\033[0m $*"; } warn() { echo -e "\033[1;33m[warn]\033[0m $*"; } fail() { echo -e "\033[1;31m[fail]\033[0m $*" >&2; exit 1; } confirm() { local msg="$1" if $YES_MODE; then return 0; fi read -rp "$msg [y/N] " ans [[ "$ans" =~ ^[Yy] ]] } # ── Collect before-versions ──────────────────────────────────────────────── declare -A BEFORE_VERSIONS=() if [[ -d "$TARGET" ]]; then for skill_dir in "$TARGET"/*/; do name="$(basename "$skill_dir")" vf="$skill_dir/SKILL.md" if [[ -f "$vf" ]]; then ver="$(grep -oP '^version:\s*\K[0-9]+\.[0-9]+\.[0-9]+' "$vf" 2>/dev/null || echo '?')" BEFORE_VERSIONS["$name"]="$ver" fi done fi # ── Step 1: git pull ──────────────────────────────────────────────────────── info "Pulling claude-skills repo..." cd "$ROOT" if [[ -n "$(git status --porcelain)" ]]; then warn "Working tree has uncommitted changes. Stashing..." git stash push -m "update-sh-auto-stash" || fail "Cannot stash changes" STASHED=true else STASHED=false fi git pull --ff-only || fail "git pull --ff-only failed (diverged or no upstream)" ok "Repo updated." if $STASHED; then info "Popping stashed changes..." git stash pop || warn "Could not pop stash — resolve manually" fi # ── Step 2: projects-meta-mcp ───────────────────────────────────────────── if [[ -d "$META_MCP/.git" ]]; then info "Checking projects-meta-mcp..." cd "$META_MCP" PRE_SHA="$(git rev-parse HEAD)" git pull --ff-only 2>/dev/null || warn "projects-meta-mcp: pull failed or no upstream" POST_SHA="$(git rev-parse HEAD)" if [[ "$PRE_SHA" != "$POST_SHA" ]]; then info "Source changed, rebuilding projects-meta-mcp..." npm run build 2>/dev/null || npm install && npm run build ok "projects-meta-mcp rebuilt." MCP_REBUILT=true else ok "projects-meta-mcp: up to date." MCP_REBUILT=false fi else warn "projects-meta-mcp not found at $META_MCP — skipping." MCP_REBUILT=false fi # ── Step 3: interns-mcp ─────────────────────────────────────────────────── if [[ -d "$INTERNS_MCP/.git" ]]; then info "Checking interns-mcp..." cd "$INTERNS_MCP" PRE_SHA="$(git rev-parse HEAD)" git pull --ff-only 2>/dev/null || warn "interns-mcp: pull failed or no upstream" POST_SHA="$(git rev-parse HEAD)" if [[ "$PRE_SHA" != "$POST_SHA" ]]; then info "Source changed, reinstalling interns-mcp..." pip install -e . 2>/dev/null || pip3 install -e . ok "interns-mcp reinstalled." MCP_REBUILT=true else ok "interns-mcp: up to date." fi else warn "interns-mcp not found at $INTERNS_MCP — skipping." fi # ── Step 4: Install skills ───────────────────────────────────────────────── cd "$ROOT" info "Installing skills..." bash "$ROOT/scripts/install.sh" ok "Skills installed." # ── Step 5: Version diff ─────────────────────────────────────────────────── info "Version diff:" CHANGES=false for skill_dir in "$TARGET"/*/; do name="$(basename "$skill_dir")" vf="$skill_dir/SKILL.md" if [[ -f "$vf" ]]; then after="$(grep -oP '^version:\s*\K[0-9]+\.[0-9]+\.[0-9]+' "$vf" 2>/dev/null || echo '?')" before="${BEFORE_VERSIONS[$name]:-new}" if [[ "$before" != "$after" ]]; then echo " $before → $after ($name)" CHANGES=true fi fi done if ! $CHANGES; then echo " (no version changes)" fi # ── Step 6: New setup-skills hint ────────────────────────────────────────── NEW_SETUP=() for skill_dir in "$SKILLS_SRC"/*/; do name="$(basename "$skill_dir")" if [[ "$name" == setup-* ]] && [[ ! -d "$TARGET/$name" ]]; then NEW_SETUP+=("$name") fi done if [[ ${#NEW_SETUP[@]} -gt 0 ]]; then echo "" info "New setup skills available:" for s in "${NEW_SETUP[@]}"; do echo " - $s" done echo " Run the setup skill to configure it." fi # ── Step 7: Reload hints ────────────────────────────────────────────────── echo "" ok "Update complete!" if $MCP_REBUILT; then echo " → Run /reload-mcp to reload MCP servers (source changed)" fi echo " → Start a new Claude session for skill changes to take full effect"