From e029e5b9e154058f099351f1093f312c482533e3 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sun, 15 Mar 2026 01:20:33 -0500 Subject: [PATCH] feat: upgrade skill with auto-upgrade, 4-option prompt, vendored sync - Auto-upgrade mode via config or GSTACK_AUTO_UPGRADE=1 env var - 4-option AskUserQuestion: upgrade once, always, not now, never - Step 4.5: sync local vendored copy after upgrading primary install - Snooze write with escalating backoff on "Not now" - Update preamble text in gen-skill-docs for new upgrade flow - Regenerate all SKILL.md files Co-Authored-By: Claude Opus 4.6 --- SKILL.md | 2 +- browse/SKILL.md | 2 +- gstack-upgrade/SKILL.md | 84 +++++++++++++++++++++++++++++++--- gstack-upgrade/SKILL.md.tmpl | 84 +++++++++++++++++++++++++++++++--- plan-ceo-review/SKILL.md | 2 +- plan-eng-review/SKILL.md | 2 +- qa/SKILL.md | 2 +- retro/SKILL.md | 2 +- review/SKILL.md | 2 +- scripts/gen-skill-docs.ts | 2 +- setup-browser-cookies/SKILL.md | 2 +- ship/SKILL.md | 2 +- 12 files changed, 166 insertions(+), 22 deletions(-) diff --git a/SKILL.md b/SKILL.md index c90218c6..9cc9acdc 100644 --- a/SKILL.md +++ b/SKILL.md @@ -23,7 +23,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # gstack browse: QA Testing & Dogfooding diff --git a/browse/SKILL.md b/browse/SKILL.md index e383e906..22c5d888 100644 --- a/browse/SKILL.md +++ b/browse/SKILL.md @@ -23,7 +23,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # browse: QA Testing & Dogfooding diff --git a/gstack-upgrade/SKILL.md b/gstack-upgrade/SKILL.md index 6c5edaed..1cf7d548 100644 --- a/gstack-upgrade/SKILL.md +++ b/gstack-upgrade/SKILL.md @@ -1,12 +1,13 @@ --- name: gstack-upgrade -version: 1.0.0 +version: 1.1.0 description: | Upgrade gstack to the latest version. Detects global vs vendored install, runs the upgrade, and shows what's new. allowed-tools: - Bash - Read + - Write - AskUserQuestion --- @@ -20,13 +21,56 @@ Upgrade gstack to the latest version and show what's new. This section is referenced by all skill preambles when they detect `UPGRADE_AVAILABLE`. -### Step 1: Ask the user +### Step 1: Ask the user (or auto-upgrade) -Use AskUserQuestion: -- Question: "gstack **v{new}** is available (you're on v{old}). Upgrade now? Takes ~10 seconds." -- Options: ["Yes, upgrade now", "Later (ask again tomorrow)"] +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 "Later":** Run `touch ~/.gstack/last-update-check` to reset the 24h timer and continue with the current skill. Do not mention the upgrade again. +**If `AUTO_UPGRADE=true` or `AUTO_UPGRADE=1`:** Skip AskUserQuestion. Log "Auto-upgrading gstack v{old} → v{new}..." and proceed directly to Step 2. If `./setup` fails during auto-upgrade, restore from backup (`.bak` directory) and warn the user: "Auto-upgrade failed — restored previous version. Run `/gstack-upgrade` manually to retry." + +**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=~/.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 @@ -79,12 +123,40 @@ cd "$INSTALL_DIR" && ./setup rm -rf "$INSTALL_DIR.bak" "$TMP_DIR" ``` +### Step 4.5: Sync local vendored copy + +After upgrading the primary install, check if there's also a local copy in the current project that needs updating: + +```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 +echo "LOCAL_GSTACK=$LOCAL_GSTACK" +``` + +If `LOCAL_GSTACK` is non-empty, update it by copying from the freshly-upgraded primary install (same approach as README vendored install): +```bash +mv "$LOCAL_GSTACK" "$LOCAL_GSTACK.bak" +cp -Rf "$INSTALL_DIR" "$LOCAL_GSTACK" +rm -rf "$LOCAL_GSTACK/.git" +cd "$LOCAL_GSTACK" && ./setup +rm -rf "$LOCAL_GSTACK.bak" +``` +Tell user: "Also updated vendored copy at `$LOCAL_GSTACK` — commit `.claude/skills/gstack/` when you're ready." + ### Step 5: Write marker + clear cache ```bash mkdir -p ~/.gstack echo "$OLD_VERSION" > ~/.gstack/just-upgraded-from rm -f ~/.gstack/last-update-check +rm -f ~/.gstack/update-snoozed ``` ### Step 6: Show What's New diff --git a/gstack-upgrade/SKILL.md.tmpl b/gstack-upgrade/SKILL.md.tmpl index a945de17..4a124be1 100644 --- a/gstack-upgrade/SKILL.md.tmpl +++ b/gstack-upgrade/SKILL.md.tmpl @@ -1,12 +1,13 @@ --- name: gstack-upgrade -version: 1.0.0 +version: 1.1.0 description: | Upgrade gstack to the latest version. Detects global vs vendored install, runs the upgrade, and shows what's new. allowed-tools: - Bash - Read + - Write - AskUserQuestion --- @@ -18,13 +19,56 @@ Upgrade gstack to the latest version and show what's new. This section is referenced by all skill preambles when they detect `UPGRADE_AVAILABLE`. -### Step 1: Ask the user +### Step 1: Ask the user (or auto-upgrade) -Use AskUserQuestion: -- Question: "gstack **v{new}** is available (you're on v{old}). Upgrade now? Takes ~10 seconds." -- Options: ["Yes, upgrade now", "Later (ask again tomorrow)"] +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 "Later":** Run `touch ~/.gstack/last-update-check` to reset the 24h timer and continue with the current skill. Do not mention the upgrade again. +**If `AUTO_UPGRADE=true` or `AUTO_UPGRADE=1`:** Skip AskUserQuestion. Log "Auto-upgrading gstack v{old} → v{new}..." and proceed directly to Step 2. If `./setup` fails during auto-upgrade, restore from backup (`.bak` directory) and warn the user: "Auto-upgrade failed — restored previous version. Run `/gstack-upgrade` manually to retry." + +**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=~/.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 @@ -77,12 +121,40 @@ cd "$INSTALL_DIR" && ./setup rm -rf "$INSTALL_DIR.bak" "$TMP_DIR" ``` +### Step 4.5: Sync local vendored copy + +After upgrading the primary install, check if there's also a local copy in the current project that needs updating: + +```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 +echo "LOCAL_GSTACK=$LOCAL_GSTACK" +``` + +If `LOCAL_GSTACK` is non-empty, update it by copying from the freshly-upgraded primary install (same approach as README vendored install): +```bash +mv "$LOCAL_GSTACK" "$LOCAL_GSTACK.bak" +cp -Rf "$INSTALL_DIR" "$LOCAL_GSTACK" +rm -rf "$LOCAL_GSTACK/.git" +cd "$LOCAL_GSTACK" && ./setup +rm -rf "$LOCAL_GSTACK.bak" +``` +Tell user: "Also updated vendored copy at `$LOCAL_GSTACK` — commit `.claude/skills/gstack/` when you're ready." + ### Step 5: Write marker + clear cache ```bash mkdir -p ~/.gstack echo "$OLD_VERSION" > ~/.gstack/just-upgraded-from rm -f ~/.gstack/last-update-check +rm -f ~/.gstack/update-snoozed ``` ### Step 6: Show What's New diff --git a/plan-ceo-review/SKILL.md b/plan-ceo-review/SKILL.md index dfb7bae5..7bb5dad0 100644 --- a/plan-ceo-review/SKILL.md +++ b/plan-ceo-review/SKILL.md @@ -23,7 +23,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # Mega Plan Review Mode diff --git a/plan-eng-review/SKILL.md b/plan-eng-review/SKILL.md index 88307f60..d8a052ab 100644 --- a/plan-eng-review/SKILL.md +++ b/plan-eng-review/SKILL.md @@ -22,7 +22,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # Plan Review Mode diff --git a/qa/SKILL.md b/qa/SKILL.md index 1a9c1de3..dd4b888d 100644 --- a/qa/SKILL.md +++ b/qa/SKILL.md @@ -23,7 +23,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # /qa: Systematic QA Testing diff --git a/retro/SKILL.md b/retro/SKILL.md index 38212816..f1e92c20 100644 --- a/retro/SKILL.md +++ b/retro/SKILL.md @@ -22,7 +22,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # /retro — Weekly Engineering Retrospective diff --git a/review/SKILL.md b/review/SKILL.md index 306794f3..b572283a 100644 --- a/review/SKILL.md +++ b/review/SKILL.md @@ -23,7 +23,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # Pre-Landing PR Review diff --git a/scripts/gen-skill-docs.ts b/scripts/gen-skill-docs.ts index bf142141..7f6bd249 100644 --- a/scripts/gen-skill-docs.ts +++ b/scripts/gen-skill-docs.ts @@ -102,7 +102,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true \`\`\` -If output shows \`UPGRADE_AVAILABLE \`: read \`~/.claude/skills/gstack/gstack-upgrade/SKILL.md\` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, \`touch ~/.gstack/last-update-check\` if no). If \`JUST_UPGRADED \`: tell user "Running gstack v{to} (just updated!)" and continue.`; +If output shows \`UPGRADE_AVAILABLE \`: read \`~/.claude/skills/gstack/gstack-upgrade/SKILL.md\` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If \`JUST_UPGRADED \`: tell user "Running gstack v{to} (just updated!)" and continue.`; } function generateBrowseSetup(): string { diff --git a/setup-browser-cookies/SKILL.md b/setup-browser-cookies/SKILL.md index e5d3357e..b2f8fc61 100644 --- a/setup-browser-cookies/SKILL.md +++ b/setup-browser-cookies/SKILL.md @@ -20,7 +20,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # Setup Browser Cookies diff --git a/ship/SKILL.md b/ship/SKILL.md index 1701f44c..386299b9 100644 --- a/ship/SKILL.md +++ b/ship/SKILL.md @@ -22,7 +22,7 @@ _UPD=$(~/.claude/skills/gstack/bin/gstack-update-check 2>/dev/null || .claude/sk [ -n "$_UPD" ] && echo "$_UPD" || true ``` -If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (AskUserQuestion → upgrade if yes, `touch ~/.gstack/last-update-check` if no). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. +If output shows `UPGRADE_AVAILABLE `: read `~/.claude/skills/gstack/gstack-upgrade/SKILL.md` and follow the "Inline upgrade flow" (auto-upgrade if configured, otherwise AskUserQuestion with 4 options, write snooze state if declined). If `JUST_UPGRADED `: tell user "Running gstack v{to} (just updated!)" and continue. # Ship: Fully Automated Ship Workflow