Skip to main content

gstack-upgrade

Upgrade gstack to the latest version.

Source facts

Repository
garrytan/gstack
Last source activity
September 26, 2026 at 22:57
Detected SKILL.md language
English
Stars
134,579
Forks
20,038

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
16 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
gstack-upgrade
version
1.1.0
description
Upgrade gstack to the latest version.
triggers
["upgrade gstack","update gstack version","get latest gstack"]
allowed-tools
["Bash","Read","Write","AskUserQuestion"]
<!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly --> <!-- Regenerate: bun run gen:skill-docs --> ## When to invoke this skill Detects global vs vendored install, runs the upgrade, and shows what's new. Use when asked to "upgrade gstack", "update gstack", or "get latest version". Voice triggers (speech-to-text aliases): "upgrade the tools", "update the tools", "gee stack upgrade", "g stack upgrade". # /gstack-upgrade Upgrade gstack to the latest version and show what's new. ## Inline upgrade flow This section is referenced by all skill preambles when they detect `UPGRADE_AVAILABLE`. ### Step 1: Ask the user (or auto-upgrade) First, check if auto-upgrade is enabled: ```bash _AUTO="" [ "${GSTACK_AUTO_UPGRADE:-}" = "1" ] && _AUTO="true" [ -z "$_AUTO" ] && _AUTO=$(~/.claude/skills/gstack/bin/gstack-config get auto_upgrade 2>/dev/null || true) echo "AUTO_UPGRADE=$_AUTO" ``` **If `AUTO_UPGRADE=true` or `AUTO_UPGRADE=1`:** Skip AskUserQuestion. Log "Auto-upgrading gstack v{old} → v{new}..." and proceed directly to Step 2. On setup failure, follow Step 4's install-specific recovery: vendored installs restore their backup; git installs stop with the pre-upgrade commit recorded, without a destructive reset. Never claim restoration unless it actually succeeded. **Otherwise**, use AskUserQuestion: - Question: "gstack **v{new}** is available (you're on v{old}). Upgrade now?" - Options: ["Yes, upgrade now", "Always keep me up to date", "Not now", "Never ask again"] **If "Yes, upgrade now":** Proceed to Step 2. **If "Always keep me up to date":** ```bash ~/.claude/skills/gstack/bin/gstack-config set auto_upgrade true ``` Tell user: "Auto-upgrade enabled. Future updates will install automatically." Then proceed to Step 2. **If "Not now":** Write snooze state with escalating backoff (first snooze = 24h, second = 48h, third+ = 1 week), then continue with the current skill. Do not mention the upgrade again. ```bash _SNOOZE_FILE="$HOME/.gstack/update-snoozed" _REMOTE_VER="{new}" _CUR_LEVEL=0 if [ -f "$_SNOOZE_FILE" ]; then _SNOOZED_VER=$(awk '{print $(1)}' "$_SNOOZE_FILE") if [ "$_SNOOZED_VER" = "$_REMOTE_VER" ]; then _CUR_LEVEL=$(awk '{print $(2)}' "$_SNOOZE_FILE") case "$_CUR_LEVEL" in *[!0-9]*) _CUR_LEVEL=0 ;; esac fi fi _NEW_LEVEL=$((_CUR_LEVEL + 1)) [ "$_NEW_LEVEL" -gt 3 ] && _NEW_LEVEL=3 echo "$_REMOTE_VER $_NEW_LEVEL $(date +%s)" > "$_SNOOZE_FILE" ``` Note: `{new}` is the remote version from the `UPGRADE_AVAILABLE` output — substitute it from the update check result. Tell user the snooze duration: "Next reminder in 24h" (or 48h or 1 week, depending on level). Tip: "Set `auto_upgrade: true` in `~/.gstack/config.yaml` for automatic upgrades." **If "Never ask again":** ```bash ~/.claude/skills/gstack/bin/gstack-config set update_check false ``` Tell user: "Update checks disabled. Run `~/.claude/skills/gstack/bin/gstack-config set update_check true` to re-enable." Continue with the current skill. ### Step 2: Detect install type ```bash if [ -d "$HOME/.claude/skills/gstack/.git" ]; then INSTALL_TYPE="global-git" INSTALL_DIR="$HOME/.claude/skills/gstack" elif [ -d "$HOME/.gstack/repos/gstack/.git" ]; then INSTALL_TYPE="global-git" INSTALL_DIR="$HOME/.gstack/repos/gstack" elif [ -d ".claude/skills/gstack/.git" ]; then INSTALL_TYPE="local-git" INSTALL_DIR=".claude/skills/gstack" elif [ -d ".agents/skills/gstack/.git" ]; then INSTALL_TYPE="local-git" INSTALL_DIR=".agents/skills/gstack" elif [ -d ".claude/skills/gstack" ]; then INSTALL_TYPE="vendored" INSTALL_DIR=".claude/skills/gstack" elif [ -d "$HOME/.claude/skills/gstack" ]; then INSTALL_TYPE="vendored-global" INSTALL_DIR="$HOME/.claude/skills/gstack" else echo "ERROR: gstack not found" exit 1 fi echo "Install type: $INSTALL_TYPE at $INSTALL_DIR" ``` The install type and directory path printed above will be used in all subsequent steps. Resolve `INSTALL_DIR` to an absolute path. Carry `INSTALL_TYPE`, `INSTALL_DIR`, `OLD_VERSION`, and later `NEW_VERSION` forward explicitly: if tool calls use fresh shells, reassign them from captured output before running a block. Do not rely on a prior call's working directory or shell variables. ### Step 3: Save old version Use the install directory from Step 2's output below: ```bash OLD_VERSION=$(cat "$INSTALL_DIR/VERSION" 2>/dev/null || echo "unknown") echo "OLD_VERSION=$OLD_VERSION" ``` ### Step 4: Upgrade Use the install type and directory detected in Step 2: **For git installs** (global-git, local-git): Fast-forward first (#2517) — the same policy the session-update auto-upgrade uses. `--autostash` carries local edits over the pull; render-footprint dirt is discarded first because it is regenerable and poisons stashes (#2569): ```bash cd "$INSTALL_DIR" # Discard render-footprint dirt (#2569): pre-v1.67 gbrain-enabled installs # ran gen:skill-docs:user IN PLACE, leaving generated SKILL.md / sections # files permanently modified. They are regenerable (setup re-renders to # ~/.gstack/render), so discarding is lossless. git checkout -- 'SKILL.md' '*/SKILL.md' '*/sections/*.md' 2>/dev/null || true git fetch origin PRE_UPGRADE_COMMIT=$(git rev-parse HEAD) echo "PRE_UPGRADE_COMMIT=$PRE_UPGRADE_COMMIT" if git pull --ff-only --autostash origin main; then if ./setup; then echo "FF_OK"; else echo "SETUP_FAILED: git update succeeded; stop and inspect setup output (previous commit: $PRE_UPGRADE_COMMIT)" >&2; exit 1; fi else echo "FF_REFUSED" fi ``` If the output ends with `FF_OK`, the upgrade is done — skip the fallback below entirely. On `SETUP_FAILED`, STOP; keep user changes and report the recovery commit. There is no `.bak` on the git path. Do not enter the divergence fallback merely because setup failed. Enter it only on `FF_REFUSED`, after inspecting the pull error; network/auth failures stop for repair, not reset. **Fallback (ff-only refused — local commits or divergence).** `git reset --hard` DESTROYS things: a clean tree with unpushed local commits still loses those commits. Gate it (#2517): 1. Run `git status --porcelain` and `git rev-list origin/main..HEAD --oneline` in `$INSTALL_DIR`. 2. If BOTH are empty, the reset is provably safe — run the fallback block below without asking. 3. Otherwise ask via AskUserQuestion (one-way door — destructive), listing exactly what will be discarded: each dirty file and each unpushed commit by hash + subject. Options: **A)** Discard them and upgrade (reset) — requires the explicit letter; **B)** Abort the upgrade so the user can rescue their work first (recommended when local commits exist). Never proceed on a vague reply. ```bash cd "$INSTALL_DIR" STASH_OUTPUT=$(git stash 2>&1) git reset --hard origin/main ./setup ``` If `$STASH_OUTPUT` contains "Saved working directory", warn the user: "Note: local changes were stashed (any modified generated SKILL.md/sections files were discarded first — they regenerate on setup). Run `git stash pop` in the skill directory to restore your own changes." **For vendored installs** (vendored, vendored-global): ```bash PARENT=$(dirname "$INSTALL_DIR") # A stale .bak from a previously crashed upgrade would make the mv below NEST # the live install inside it and the failure-restore arm would "restore" the # stale backup. It may also be the only good copy from that crashed run — # abort and let the human inspect, never delete it silently. [ -e "$INSTALL_DIR.bak" ] && { echo "ERROR: stale backup exists at $INSTALL_DIR.bak (from a previous failed upgrade?) — inspect it, salvage/remove it, then re-run." >&2; exit 1; } TMP_DIR=$(mktemp -d) || { echo "ERROR: mktemp failed — aborting upgrade (install untouched)." >&2; exit 1; } git clone --depth 1 https://github.com/garrytan/gstack.git "$TMP_DIR/gstack" || { echo "ERROR: clone failed — aborting upgrade (install untouched)." >&2; rm -rf "$TMP_DIR"; exit 1; } mv "$INSTALL_DIR" "$INSTALL_DIR.bak" || { rm -rf "$TMP_DIR"; exit 1; } if mv "$TMP_DIR/gstack" "$INSTALL_DIR"; then if (cd "$INSTALL_DIR" && ./setup); then rm -rf "$INSTALL_DIR.bak" "$TMP_DIR" else rm -rf "$INSTALL_DIR" mv "$INSTALL_DIR.bak" "$INSTALL_DIR" || { echo "ERROR: restore failed; backup retained." >&2; exit 1; } rm -rf "$TMP_DIR" echo "ERROR: setup failed; previous install restored." >&2 exit 1 fi else mv "$INSTALL_DIR.bak" "$INSTALL_DIR" echo "ERROR: swap failed — previous install restored; upgrade aborted." >&2 rm -rf "$TMP_DIR" exit 1 fi ``` ### Step 4.5: Handle local vendored copy Use the install directory from Step 2. Check if there's also a local vendored copy, and whether team mode is active: ```bash _ROOT=$(git rev-parse --show-toplevel 2>/dev/null) LOCAL_GSTACK="" if [ -n "$_ROOT" ] && [ -d "$_ROOT/.claude/skills/gstack" ]; then _RESOLVED_LOCAL=$(cd "$_ROOT/.claude/skills/gstack" && pwd -P) _RESOLVED_PRIMARY=$(cd "$INSTALL_DIR" && pwd -P) if [ "$_RESOLVED_LOCAL" != "$_RESOLVED_PRIMARY" ]; then LOCAL_GSTACK="$_ROOT/.claude/skills/gstack" fi fi _TEAM_MODE=$(~/.claude/skills/gstack/bin/gstack-config get team_mode 2>/dev/null || echo "false") echo "LOCAL_GSTACK=$LOCAL_GSTACK" echo "TEAM_MODE=$_TEAM_MODE" ``` **If `LOCAL_GSTACK` is non-empty AND `TEAM_MODE` is `true`:** Remove the vendored copy. Team mode uses the global install as the single source of truth. ```bash cd "$_ROOT" git rm -r --cached .claude/skills/gstack/ 2>/dev/null || true if ! grep -qF '.claude/skills/gstack/' .gitignore 2>/dev/null; then echo '.claude/skills/gstack/' >> .gitignore fi rm -rf "$LOCAL_GSTACK" ``` Tell user: "Removed vendored copy at `$LOCAL_GSTACK` (team mode active — global install is the source of truth). Commit the `.gitignore` change when ready." **If `LOCAL_GSTACK` is non-empty AND `TEAM_MODE` is NOT `true`:** Update it by copying from the freshly-upgraded primary install (same approach as README vendored install): ```bash [ -e "$LOCAL_GSTACK.bak" ] && { echo "ERROR: stale vendored backup; inspect it before retrying." >&2; exit 1; } mv "$LOCAL_GSTACK" "$LOCAL_GSTACK.bak" || exit 1 if cp -Rf "$INSTALL_DIR" "$LOCAL_GSTACK" && rm -rf "$LOCAL_GSTACK/.git" && (cd "$LOCAL_GSTACK" && ./setup); then rm -rf "$LOCAL_GSTACK.bak" echo "LOCAL_SYNC_OK" else rm -rf "$LOCAL_GSTACK" mv "$LOCAL_GSTACK.bak" "$LOCAL_GSTACK" || { echo "ERROR: restore failed; backup retained." >&2; exit 1; } echo "ERROR: sync failed; previous vendored copy restored." >&2 exit 1 fi ``` Only on `LOCAL_SYNC_OK`, tell user: "Also updated vendored copy at `$LOCAL_GSTACK` — commit `.claude/skills/gstack/` when you're ready." Otherwise stop and report the recovery outcome; do not continue migrations or announce success. ### Step 4.75: Run version migrations After `./setup` completes, run any migration scripts for versions between the old and new version. Migrations handle state fixes that `./setup` alone can't cover (stale config, orphaned files, directory structure changes). ```bash MIGRATIONS_DIR="$INSTALL_DIR/gstack-upgrade/migrations" if [ -d "$MIGRATIONS_DIR" ]; then for migration in $(find "$MIGRATIONS_DIR" -maxdepth 1 -name 'v*.sh' -type f 2>/dev/null | sort -V); do # Extract version from filename: v0.15.2.0.sh → 0.15.2.0 m_ver="$(basename "$migration" .sh | sed 's/^v//')" # Run if this migration version is newer than old version # (simple string compare works for dotted versions with same segment count) if [ "$OLD_VERSION" != "unknown" ] && [ "$(printf '%s\n%s' "$OLD_VERSION" "$m_ver" | sort -V | head -1)" = "$OLD_VERSION" ] && [ "$OLD_VERSION" != "$m_ver" ]; then echo "Running migration $m_ver..." # GSTACK_INSTALL_DIR: migrations that clean the INSTALL (not just
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub