[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M6MWGJ2CJQS4TM5R4HC2MMXV":3,"skill-same:01M6MWGJ2CJQS4TM5R4HC2MMXV":82},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":17,"publishedAt":38,"linkOnly":39,"markdownContent":40,"markdownBody":41,"frontMatter":42,"refName":49,"commitSha":50,"skillDirectory":51,"forksCount":52,"repositoryHtmlUrl":53,"repositoryHomepageUrl":6,"ownerHtmlUrl":54,"ownerBlogUrl":6,"ownerDescription":6,"updatedAt":55,"install":56,"resources":69},"01M6MWGJ2CJQS4TM5R4HC2MMXV","gstack-upgrade",null,"Upgrade gstack to the latest version. Detects global vs vendored install,\nruns the upgrade, and shows what's new. Use when asked to \"upgrade gstack\",\n\"update gstack\", or \"get latest version\".\nVoice triggers (speech-to-text aliases): \"upgrade the tools\", \"update the tools\", \"gee stack upgrade\", \"g stack upgrade\".","forbotsake\u002Fforbotsake","forbotsake","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F273708741?v=4","01M6MW9A04DY7WQBA8YF96SVJE","01M6MW9A47HYRVBBQ642740E75",5,"MIT","https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgstack\u002Fgstack-upgrade\u002FSKILL.md",[],[18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37],"gstack upgrade","upgrade","gstack","install","aliases","latest","triggers","get","speech-to-text","version","use","update","write","read","text","stack","voice","Bash","Tools","speech","2026-11-10 10:12:30",false,"---\nname: gstack-upgrade\nversion: 1.1.0\ndescription: |\n  Upgrade gstack to the latest version. Detects global vs vendored install,\n  runs the upgrade, and shows what's new. Use when asked to \"upgrade gstack\",\n  \"update gstack\", or \"get latest version\".\n  Voice triggers (speech-to-text aliases): \"upgrade the tools\", \"update the tools\", \"gee stack upgrade\", \"g stack upgrade\".\nallowed-tools:\n  - Bash\n  - Read\n  - Write\n  - AskUserQuestion\n---\n\u003C!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->\n\u003C!-- Regenerate: bun run gen:skill-docs -->\n\n# \u002Fgstack-upgrade\n\nUpgrade gstack to the latest version and show what's new.\n\n## Inline upgrade flow\n\nThis section is referenced by all skill preambles when they detect `UPGRADE_AVAILABLE`.\n\n### Step 1: Ask the user (or auto-upgrade)\n\nFirst, check if auto-upgrade is enabled:\n```bash\n_AUTO=\"\"\n[ \"${GSTACK_AUTO_UPGRADE:-}\" = \"1\" ] && _AUTO=\"true\"\n[ -z \"$_AUTO\" ] && _AUTO=$(~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-config get auto_upgrade 2>\u002Fdev\u002Fnull || true)\necho \"AUTO_UPGRADE=$_AUTO\"\n```\n\n**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 `.\u002Fsetup` fails during auto-upgrade, restore from backup (`.bak` directory) and warn the user: \"Auto-upgrade failed — restored previous version. Run `\u002Fgstack-upgrade` manually to retry.\"\n\n**Otherwise**, use AskUserQuestion:\n- Question: \"gstack **v{new}** is available (you're on v{old}). Upgrade now?\"\n- Options: [\"Yes, upgrade now\", \"Always keep me up to date\", \"Not now\", \"Never ask again\"]\n\n**If \"Yes, upgrade now\":** Proceed to Step 2.\n\n**If \"Always keep me up to date\":**\n```bash\n~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-config set auto_upgrade true\n```\nTell user: \"Auto-upgrade enabled. Future updates will install automatically.\" Then proceed to Step 2.\n\n**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.\n```bash\n_SNOOZE_FILE=~\u002F.gstack\u002Fupdate-snoozed\n_REMOTE_VER=\"{new}\"\n_CUR_LEVEL=0\nif [ -f \"$_SNOOZE_FILE\" ]; then\n  _SNOOZED_VER=$(awk '{print $1}' \"$_SNOOZE_FILE\")\n  if [ \"$_SNOOZED_VER\" = \"$_REMOTE_VER\" ]; then\n    _CUR_LEVEL=$(awk '{print $2}' \"$_SNOOZE_FILE\")\n    case \"$_CUR_LEVEL\" in *[!0-9]*) _CUR_LEVEL=0 ;; esac\n  fi\nfi\n_NEW_LEVEL=$((_CUR_LEVEL + 1))\n[ \"$_NEW_LEVEL\" -gt 3 ] && _NEW_LEVEL=3\necho \"$_REMOTE_VER $_NEW_LEVEL $(date +%s)\" > \"$_SNOOZE_FILE\"\n```\nNote: `{new}` is the remote version from the `UPGRADE_AVAILABLE` output — substitute it from the update check result.\n\nTell user the snooze duration: \"Next reminder in 24h\" (or 48h or 1 week, depending on level). Tip: \"Set `auto_upgrade: true` in `~\u002F.gstack\u002Fconfig.yaml` for automatic upgrades.\"\n\n**If \"Never ask again\":**\n```bash\n~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-config set update_check false\n```\nTell user: \"Update checks disabled. Run `~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-config set update_check true` to re-enable.\"\nContinue with the current skill.\n\n### Step 2: Detect install type\n\n```bash\nif [ -d \"$HOME\u002F.claude\u002Fskills\u002Fgstack\u002F.git\" ]; then\n  INSTALL_TYPE=\"global-git\"\n  INSTALL_DIR=\"$HOME\u002F.claude\u002Fskills\u002Fgstack\"\nelif [ -d \"$HOME\u002F.gstack\u002Frepos\u002Fgstack\u002F.git\" ]; then\n  INSTALL_TYPE=\"global-git\"\n  INSTALL_DIR=\"$HOME\u002F.gstack\u002Frepos\u002Fgstack\"\nelif [ -d \".claude\u002Fskills\u002Fgstack\u002F.git\" ]; then\n  INSTALL_TYPE=\"local-git\"\n  INSTALL_DIR=\".claude\u002Fskills\u002Fgstack\"\nelif [ -d \".agents\u002Fskills\u002Fgstack\u002F.git\" ]; then\n  INSTALL_TYPE=\"local-git\"\n  INSTALL_DIR=\".agents\u002Fskills\u002Fgstack\"\nelif [ -d \".claude\u002Fskills\u002Fgstack\" ]; then\n  INSTALL_TYPE=\"vendored\"\n  INSTALL_DIR=\".claude\u002Fskills\u002Fgstack\"\nelif [ -d \"$HOME\u002F.claude\u002Fskills\u002Fgstack\" ]; then\n  INSTALL_TYPE=\"vendored-global\"\n  INSTALL_DIR=\"$HOME\u002F.claude\u002Fskills\u002Fgstack\"\nelse\n  echo \"ERROR: gstack not found\"\n  exit 1\nfi\necho \"Install type: $INSTALL_TYPE at $INSTALL_DIR\"\n```\n\nThe install type and directory path printed above will be used in all subsequent steps.\n\n### Step 3: Save old version\n\nUse the install directory from Step 2's output below:\n\n```bash\nOLD_VERSION=$(cat \"$INSTALL_DIR\u002FVERSION\" 2>\u002Fdev\u002Fnull || echo \"unknown\")\n```\n\n### Step 4: Upgrade\n\nUse the install type and directory detected in Step 2:\n\n**For git installs** (global-git, local-git):\n```bash\ncd \"$INSTALL_DIR\"\nSTASH_OUTPUT=$(git stash 2>&1)\ngit fetch origin\ngit reset --hard origin\u002Fmain\n.\u002Fsetup\n```\nIf `$STASH_OUTPUT` contains \"Saved working directory\", warn the user: \"Note: local changes were stashed. Run `git stash pop` in the skill directory to restore them.\"\n\n**For vendored installs** (vendored, vendored-global):\n```bash\nPARENT=$(dirname \"$INSTALL_DIR\")\nTMP_DIR=$(mktemp -d)\ngit clone --depth 1 https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgstack.git \"$TMP_DIR\u002Fgstack\"\nmv \"$INSTALL_DIR\" \"$INSTALL_DIR.bak\"\nmv \"$TMP_DIR\u002Fgstack\" \"$INSTALL_DIR\"\ncd \"$INSTALL_DIR\" && .\u002Fsetup\nrm -rf \"$INSTALL_DIR.bak\" \"$TMP_DIR\"\n```\n\n### Step 4.5: Sync local vendored copy\n\nUse the install directory from Step 2. Check if there's also a local vendored copy that needs updating:\n\n```bash\n_ROOT=$(git rev-parse --show-toplevel 2>\u002Fdev\u002Fnull)\nLOCAL_GSTACK=\"\"\nif [ -n \"$_ROOT\" ] && [ -d \"$_ROOT\u002F.claude\u002Fskills\u002Fgstack\" ]; then\n  _RESOLVED_LOCAL=$(cd \"$_ROOT\u002F.claude\u002Fskills\u002Fgstack\" && pwd -P)\n  _RESOLVED_PRIMARY=$(cd \"$INSTALL_DIR\" && pwd -P)\n  if [ \"$_RESOLVED_LOCAL\" != \"$_RESOLVED_PRIMARY\" ]; then\n    LOCAL_GSTACK=\"$_ROOT\u002F.claude\u002Fskills\u002Fgstack\"\n  fi\nfi\necho \"LOCAL_GSTACK=$LOCAL_GSTACK\"\n```\n\nIf `LOCAL_GSTACK` is non-empty, update it by copying from the freshly-upgraded primary install (same approach as README vendored install):\n```bash\nmv \"$LOCAL_GSTACK\" \"$LOCAL_GSTACK.bak\"\ncp -Rf \"$INSTALL_DIR\" \"$LOCAL_GSTACK\"\nrm -rf \"$LOCAL_GSTACK\u002F.git\"\ncd \"$LOCAL_GSTACK\" && .\u002Fsetup\nrm -rf \"$LOCAL_GSTACK.bak\"\n```\nTell user: \"Also updated vendored copy at `$LOCAL_GSTACK` — commit `.claude\u002Fskills\u002Fgstack\u002F` when you're ready.\"\n\nIf `.\u002Fsetup` fails, restore from backup and warn the user:\n```bash\nrm -rf \"$LOCAL_GSTACK\"\nmv \"$LOCAL_GSTACK.bak\" \"$LOCAL_GSTACK\"\n```\nTell user: \"Sync failed — restored previous version at `$LOCAL_GSTACK`. Run `\u002Fgstack-upgrade` manually to retry.\"\n\n### Step 4.75: Run version migrations\n\nAfter `.\u002Fsetup` completes, run any migration scripts for versions between the old\nand new version. Migrations handle state fixes that `.\u002Fsetup` alone can't cover\n(stale config, orphaned files, directory structure changes).\n\n```bash\nMIGRATIONS_DIR=\"$INSTALL_DIR\u002Fgstack-upgrade\u002Fmigrations\"\nif [ -d \"$MIGRATIONS_DIR\" ]; then\n  for migration in $(find \"$MIGRATIONS_DIR\" -maxdepth 1 -name 'v*.sh' -type f 2>\u002Fdev\u002Fnull | sort -V); do\n    # Extract version from filename: v0.15.2.0.sh → 0.15.2.0\n    m_ver=\"$(basename \"$migration\" .sh | sed 's\u002F^v\u002F\u002F')\"\n    # Run if this migration version is newer than old version\n    # (simple string compare works for dotted versions with same segment count)\n    if [ \"$OLD_VERSION\" != \"unknown\" ] && [ \"$(printf '%s\\n%s' \"$OLD_VERSION\" \"$m_ver\" | sort -V | head -1)\" = \"$OLD_VERSION\" ] && [ \"$OLD_VERSION\" != \"$m_ver\" ]; then\n      echo \"Running migration $m_ver...\"\n      bash \"$migration\" || echo \"  Warning: migration $m_ver had errors (non-fatal)\"\n    fi\n  done\nfi\n```\n\nMigrations are idempotent bash scripts in `gstack-upgrade\u002Fmigrations\u002F`. Each is named\n`v{VERSION}.sh` and runs only when upgrading from an older version. See CONTRIBUTING.md\nfor how to add new migrations.\n\n### Step 5: Write marker + clear cache\n\n```bash\nmkdir -p ~\u002F.gstack\necho \"$OLD_VERSION\" > ~\u002F.gstack\u002Fjust-upgraded-from\nrm -f ~\u002F.gstack\u002Flast-update-check\nrm -f ~\u002F.gstack\u002Fupdate-snoozed\n```\n\n### Step 6: Show What's New\n\nRead `$INSTALL_DIR\u002FCHANGELOG.md`. Find all version entries between the old version and the new version. Summarize as 5-7 bullets grouped by theme. Don't overwhelm — focus on user-facing changes. Skip internal refactors unless they're significant.\n\nFormat:\n```\ngstack v{new} — upgraded from v{old}!\n\nWhat's new:\n- [bullet 1]\n- [bullet 2]\n- ...\n\nHappy shipping!\n```\n\n### Step 7: Continue\n\nAfter showing What's New, continue with whatever skill the user originally invoked. The upgrade is done — no further action needed.\n\n---\n\n## Standalone usage\n\nWhen invoked directly as `\u002Fgstack-upgrade` (not from a preamble):\n\n1. Force a fresh update check (bypass cache):\n```bash\n~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-update-check --force 2>\u002Fdev\u002Fnull || \\\n.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-update-check --force 2>\u002Fdev\u002Fnull || true\n```\nUse the output to determine if an upgrade is available.\n\n2. If `UPGRADE_AVAILABLE \u003Cold> \u003Cnew>`: follow Steps 2-6 above.\n\n3. If no output (primary is up to date): check for a stale local vendored copy.\n\nRun the Step 2 bash block above to detect the primary install type and directory (`INSTALL_TYPE` and `INSTALL_DIR`). Then run the Step 4.5 detection bash block above to check for a local vendored copy (`LOCAL_GSTACK`).\n\n**If `LOCAL_GSTACK` is empty** (no local vendored copy): tell the user \"You're already on the latest version (v{version}).\"\n\n**If `LOCAL_GSTACK` is non-empty**, compare versions:\n```bash\nPRIMARY_VER=$(cat \"$INSTALL_DIR\u002FVERSION\" 2>\u002Fdev\u002Fnull || echo \"unknown\")\nLOCAL_VER=$(cat \"$LOCAL_GSTACK\u002FVERSION\" 2>\u002Fdev\u002Fnull || echo \"unknown\")\necho \"PRIMARY=$PRIMARY_VER LOCAL=$LOCAL_VER\"\n```\n\n**If versions differ:** follow the Step 4.5 sync bash block above to update the local copy from the primary. Tell user: \"Global v{PRIMARY_VER} is up to date. Updated local vendored copy from v{LOCAL_VER} → v{PRIMARY_VER}. Commit `.claude\u002Fskills\u002Fgstack\u002F` when you're ready.\"\n\n**If versions match:** tell the user \"You're on the latest version (v{PRIMARY_VER}). Global and local vendored copy are both up to date.\"\n","\u003C!-- AUTO-GENERATED from SKILL.md.tmpl — do not edit directly -->\n\u003C!-- Regenerate: bun run gen:skill-docs -->\n\n# \u002Fgstack-upgrade\n\nUpgrade gstack to the latest version and show what's new.\n\n## Inline upgrade flow\n\nThis section is referenced by all skill preambles when they detect `UPGRADE_AVAILABLE`.\n\n### Step 1: Ask the user (or auto-upgrade)\n\nFirst, check if auto-upgrade is enabled:\n```bash\n_AUTO=\"\"\n[ \"${GSTACK_AUTO_UPGRADE:-}\" = \"1\" ] && _AUTO=\"true\"\n[ -z \"$_AUTO\" ] && _AUTO=$(~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-config get auto_upgrade 2>\u002Fdev\u002Fnull || true)\necho \"AUTO_UPGRADE=$_AUTO\"\n```\n\n**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 `.\u002Fsetup` fails during auto-upgrade, restore from backup (`.bak` directory) and warn the user: \"Auto-upgrade failed — restored previous version. Run `\u002Fgstack-upgrade` manually to retry.\"\n\n**Otherwise**, use AskUserQuestion:\n- Question: \"gstack **v{new}** is available (you're on v{old}). Upgrade now?\"\n- Options: [\"Yes, upgrade now\", \"Always keep me up to date\", \"Not now\", \"Never ask again\"]\n\n**If \"Yes, upgrade now\":** Proceed to Step 2.\n\n**If \"Always keep me up to date\":**\n```bash\n~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-config set auto_upgrade true\n```\nTell user: \"Auto-upgrade enabled. Future updates will install automatically.\" Then proceed to Step 2.\n\n**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.\n```bash\n_SNOOZE_FILE=~\u002F.gstack\u002Fupdate-snoozed\n_REMOTE_VER=\"{new}\"\n_CUR_LEVEL=0\nif [ -f \"$_SNOOZE_FILE\" ]; then\n  _SNOOZED_VER=$(awk '{print $1}' \"$_SNOOZE_FILE\")\n  if [ \"$_SNOOZED_VER\" = \"$_REMOTE_VER\" ]; then\n    _CUR_LEVEL=$(awk '{print $2}' \"$_SNOOZE_FILE\")\n    case \"$_CUR_LEVEL\" in *[!0-9]*) _CUR_LEVEL=0 ;; esac\n  fi\nfi\n_NEW_LEVEL=$((_CUR_LEVEL + 1))\n[ \"$_NEW_LEVEL\" -gt 3 ] && _NEW_LEVEL=3\necho \"$_REMOTE_VER $_NEW_LEVEL $(date +%s)\" > \"$_SNOOZE_FILE\"\n```\nNote: `{new}` is the remote version from the `UPGRADE_AVAILABLE` output — substitute it from the update check result.\n\nTell user the snooze duration: \"Next reminder in 24h\" (or 48h or 1 week, depending on level). Tip: \"Set `auto_upgrade: true` in `~\u002F.gstack\u002Fconfig.yaml` for automatic upgrades.\"\n\n**If \"Never ask again\":**\n```bash\n~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-config set update_check false\n```\nTell user: \"Update checks disabled. Run `~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-config set update_check true` to re-enable.\"\nContinue with the current skill.\n\n### Step 2: Detect install type\n\n```bash\nif [ -d \"$HOME\u002F.claude\u002Fskills\u002Fgstack\u002F.git\" ]; then\n  INSTALL_TYPE=\"global-git\"\n  INSTALL_DIR=\"$HOME\u002F.claude\u002Fskills\u002Fgstack\"\nelif [ -d \"$HOME\u002F.gstack\u002Frepos\u002Fgstack\u002F.git\" ]; then\n  INSTALL_TYPE=\"global-git\"\n  INSTALL_DIR=\"$HOME\u002F.gstack\u002Frepos\u002Fgstack\"\nelif [ -d \".claude\u002Fskills\u002Fgstack\u002F.git\" ]; then\n  INSTALL_TYPE=\"local-git\"\n  INSTALL_DIR=\".claude\u002Fskills\u002Fgstack\"\nelif [ -d \".agents\u002Fskills\u002Fgstack\u002F.git\" ]; then\n  INSTALL_TYPE=\"local-git\"\n  INSTALL_DIR=\".agents\u002Fskills\u002Fgstack\"\nelif [ -d \".claude\u002Fskills\u002Fgstack\" ]; then\n  INSTALL_TYPE=\"vendored\"\n  INSTALL_DIR=\".claude\u002Fskills\u002Fgstack\"\nelif [ -d \"$HOME\u002F.claude\u002Fskills\u002Fgstack\" ]; then\n  INSTALL_TYPE=\"vendored-global\"\n  INSTALL_DIR=\"$HOME\u002F.claude\u002Fskills\u002Fgstack\"\nelse\n  echo \"ERROR: gstack not found\"\n  exit 1\nfi\necho \"Install type: $INSTALL_TYPE at $INSTALL_DIR\"\n```\n\nThe install type and directory path printed above will be used in all subsequent steps.\n\n### Step 3: Save old version\n\nUse the install directory from Step 2's output below:\n\n```bash\nOLD_VERSION=$(cat \"$INSTALL_DIR\u002FVERSION\" 2>\u002Fdev\u002Fnull || echo \"unknown\")\n```\n\n### Step 4: Upgrade\n\nUse the install type and directory detected in Step 2:\n\n**For git installs** (global-git, local-git):\n```bash\ncd \"$INSTALL_DIR\"\nSTASH_OUTPUT=$(git stash 2>&1)\ngit fetch origin\ngit reset --hard origin\u002Fmain\n.\u002Fsetup\n```\nIf `$STASH_OUTPUT` contains \"Saved working directory\", warn the user: \"Note: local changes were stashed. Run `git stash pop` in the skill directory to restore them.\"\n\n**For vendored installs** (vendored, vendored-global):\n```bash\nPARENT=$(dirname \"$INSTALL_DIR\")\nTMP_DIR=$(mktemp -d)\ngit clone --depth 1 https:\u002F\u002Fgithub.com\u002Fgarrytan\u002Fgstack.git \"$TMP_DIR\u002Fgstack\"\nmv \"$INSTALL_DIR\" \"$INSTALL_DIR.bak\"\nmv \"$TMP_DIR\u002Fgstack\" \"$INSTALL_DIR\"\ncd \"$INSTALL_DIR\" && .\u002Fsetup\nrm -rf \"$INSTALL_DIR.bak\" \"$TMP_DIR\"\n```\n\n### Step 4.5: Sync local vendored copy\n\nUse the install directory from Step 2. Check if there's also a local vendored copy that needs updating:\n\n```bash\n_ROOT=$(git rev-parse --show-toplevel 2>\u002Fdev\u002Fnull)\nLOCAL_GSTACK=\"\"\nif [ -n \"$_ROOT\" ] && [ -d \"$_ROOT\u002F.claude\u002Fskills\u002Fgstack\" ]; then\n  _RESOLVED_LOCAL=$(cd \"$_ROOT\u002F.claude\u002Fskills\u002Fgstack\" && pwd -P)\n  _RESOLVED_PRIMARY=$(cd \"$INSTALL_DIR\" && pwd -P)\n  if [ \"$_RESOLVED_LOCAL\" != \"$_RESOLVED_PRIMARY\" ]; then\n    LOCAL_GSTACK=\"$_ROOT\u002F.claude\u002Fskills\u002Fgstack\"\n  fi\nfi\necho \"LOCAL_GSTACK=$LOCAL_GSTACK\"\n```\n\nIf `LOCAL_GSTACK` is non-empty, update it by copying from the freshly-upgraded primary install (same approach as README vendored install):\n```bash\nmv \"$LOCAL_GSTACK\" \"$LOCAL_GSTACK.bak\"\ncp -Rf \"$INSTALL_DIR\" \"$LOCAL_GSTACK\"\nrm -rf \"$LOCAL_GSTACK\u002F.git\"\ncd \"$LOCAL_GSTACK\" && .\u002Fsetup\nrm -rf \"$LOCAL_GSTACK.bak\"\n```\nTell user: \"Also updated vendored copy at `$LOCAL_GSTACK` — commit `.claude\u002Fskills\u002Fgstack\u002F` when you're ready.\"\n\nIf `.\u002Fsetup` fails, restore from backup and warn the user:\n```bash\nrm -rf \"$LOCAL_GSTACK\"\nmv \"$LOCAL_GSTACK.bak\" \"$LOCAL_GSTACK\"\n```\nTell user: \"Sync failed — restored previous version at `$LOCAL_GSTACK`. Run `\u002Fgstack-upgrade` manually to retry.\"\n\n### Step 4.75: Run version migrations\n\nAfter `.\u002Fsetup` completes, run any migration scripts for versions between the old\nand new version. Migrations handle state fixes that `.\u002Fsetup` alone can't cover\n(stale config, orphaned files, directory structure changes).\n\n```bash\nMIGRATIONS_DIR=\"$INSTALL_DIR\u002Fgstack-upgrade\u002Fmigrations\"\nif [ -d \"$MIGRATIONS_DIR\" ]; then\n  for migration in $(find \"$MIGRATIONS_DIR\" -maxdepth 1 -name 'v*.sh' -type f 2>\u002Fdev\u002Fnull | sort -V); do\n    # Extract version from filename: v0.15.2.0.sh → 0.15.2.0\n    m_ver=\"$(basename \"$migration\" .sh | sed 's\u002F^v\u002F\u002F')\"\n    # Run if this migration version is newer than old version\n    # (simple string compare works for dotted versions with same segment count)\n    if [ \"$OLD_VERSION\" != \"unknown\" ] && [ \"$(printf '%s\\n%s' \"$OLD_VERSION\" \"$m_ver\" | sort -V | head -1)\" = \"$OLD_VERSION\" ] && [ \"$OLD_VERSION\" != \"$m_ver\" ]; then\n      echo \"Running migration $m_ver...\"\n      bash \"$migration\" || echo \"  Warning: migration $m_ver had errors (non-fatal)\"\n    fi\n  done\nfi\n```\n\nMigrations are idempotent bash scripts in `gstack-upgrade\u002Fmigrations\u002F`. Each is named\n`v{VERSION}.sh` and runs only when upgrading from an older version. See CONTRIBUTING.md\nfor how to add new migrations.\n\n### Step 5: Write marker + clear cache\n\n```bash\nmkdir -p ~\u002F.gstack\necho \"$OLD_VERSION\" > ~\u002F.gstack\u002Fjust-upgraded-from\nrm -f ~\u002F.gstack\u002Flast-update-check\nrm -f ~\u002F.gstack\u002Fupdate-snoozed\n```\n\n### Step 6: Show What's New\n\nRead `$INSTALL_DIR\u002FCHANGELOG.md`. Find all version entries between the old version and the new version. Summarize as 5-7 bullets grouped by theme. Don't overwhelm — focus on user-facing changes. Skip internal refactors unless they're significant.\n\nFormat:\n```\ngstack v{new} — upgraded from v{old}!\n\nWhat's new:\n- [bullet 1]\n- [bullet 2]\n- ...\n\nHappy shipping!\n```\n\n### Step 7: Continue\n\nAfter showing What's New, continue with whatever skill the user originally invoked. The upgrade is done — no further action needed.\n\n---\n\n## Standalone usage\n\nWhen invoked directly as `\u002Fgstack-upgrade` (not from a preamble):\n\n1. Force a fresh update check (bypass cache):\n```bash\n~\u002F.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-update-check --force 2>\u002Fdev\u002Fnull || \\\n.claude\u002Fskills\u002Fgstack\u002Fbin\u002Fgstack-update-check --force 2>\u002Fdev\u002Fnull || true\n```\nUse the output to determine if an upgrade is available.\n\n2. If `UPGRADE_AVAILABLE \u003Cold> \u003Cnew>`: follow Steps 2-6 above.\n\n3. If no output (primary is up to date): check for a stale local vendored copy.\n\nRun the Step 2 bash block above to detect the primary install type and directory (`INSTALL_TYPE` and `INSTALL_DIR`). Then run the Step 4.5 detection bash block above to check for a local vendored copy (`LOCAL_GSTACK`).\n\n**If `LOCAL_GSTACK` is empty** (no local vendored copy): tell the user \"You're already on the latest version (v{version}).\"\n\n**If `LOCAL_GSTACK` is non-empty**, compare versions:\n```bash\nPRIMARY_VER=$(cat \"$INSTALL_DIR\u002FVERSION\" 2>\u002Fdev\u002Fnull || echo \"unknown\")\nLOCAL_VER=$(cat \"$LOCAL_GSTACK\u002FVERSION\" 2>\u002Fdev\u002Fnull || echo \"unknown\")\necho \"PRIMARY=$PRIMARY_VER LOCAL=$LOCAL_VER\"\n```\n\n**If versions differ:** follow the Step 4.5 sync bash block above to update the local copy from the primary. Tell user: \"Global v{PRIMARY_VER} is up to date. Updated local vendored copy from v{LOCAL_VER} → v{PRIMARY_VER}. Commit `.claude\u002Fskills\u002Fgstack\u002F` when you're ready.\"\n\n**If versions match:** tell the user \"You're on the latest version (v{PRIMARY_VER}). Global and local vendored copy are both up to date.\"\n",{"name":5,"version":43,"description":44,"allowed-tools":45},"1.1.0","Upgrade gstack to the latest version. Detects global vs vendored install,\nruns the upgrade, and shows what's new. Use when asked to \"upgrade gstack\",\n\"update gstack\", or \"get latest version\".\nVoice triggers (speech-to-text aliases): \"upgrade the tools\", \"update the tools\", \"gee stack upgrade\", \"g stack upgrade\".\n",[35,46,47,48],"Read","Write","AskUserQuestion","main","d91f0c567548c80403f85619693a405ed65d2b60",".claude\u002Fskills\u002Fgstack\u002Fgstack-upgrade",1,"https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake","https:\u002F\u002Fgithub.com\u002Fforbotsake","2026-08-17 21:39:59",{"source":8,"skillName":5,"commands":57,"prompt":67,"zipAvailable":68},[58,61,64],{"tool":59,"command":60},"npx","npx skills add forbotsake\u002Fforbotsake --skill gstack-upgrade",{"tool":62,"command":63},"bunx","bunx skills add forbotsake\u002Fforbotsake --skill gstack-upgrade",{"tool":65,"command":66},"pnpm","pnpm dlx skills add forbotsake\u002Fforbotsake --skill gstack-upgrade","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgstack\u002Fgstack-upgrade\u002FSKILL.md",true,[70,76],{"path":71,"resourceType":72,"resourceTypeDesc":73,"sizeBytes":74,"sourceHtmlUrl":75},"migrations\u002Fv0.15.2.0.sh","SCRIPT","脚本",854,"https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgstack\u002Fgstack-upgrade\u002Fmigrations\u002Fv0.15.2.0.sh",{"path":77,"resourceType":78,"resourceTypeDesc":79,"sizeBytes":80,"sourceHtmlUrl":81},"SKILL.md.tmpl","OTHER","其他",9523,"https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002F.claude\u002Fskills\u002Fgstack\u002Fgstack-upgrade\u002FSKILL.md.tmpl",[83,95,122,151,174,207],{"skillNo":84,"name":85,"nameZh":6,"description":86,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":87,"categories":88,"keywords":89,"publishedAt":38},"01M6MWS487CZVEME2DWGT48C58","forbotsake-upgrade","Upgrade forbotsake to the latest version. Detects install type (git clone\nvs vendored), runs the upgrade, and shows what's new.\nUse when: \"upgrade forbotsake\", \"update forbotsake\", \"get latest version\",\n\"forbotsake update\".","https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002Fforbotsake-upgrade\u002FSKILL.md",[],[19,90,9,29,91,35,31,30,21,28,27,92,25,93,23,94],"forbotsake upgrade","git","git clone","type","clone",{"skillNo":96,"name":97,"nameZh":6,"description":98,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":99,"categories":100,"keywords":106,"publishedAt":38},"01M6MWRZ7XD9Q73JTRBQ0N8682","forbotsake-spy","Stage 3: RESEARCH (competitors). Analyzes 3-5 competitors to find messaging whitespace\nand positioning gaps. Produces competitor-analysis.md with a messaging matrix showing\nwhat each competitor says, what's missing, and where you can win.\nUse when: \"competitor analysis\", \"competitive research\", \"what are others doing\",\n\"market landscape\", \"who am I competing with\", \"spy on competitors\",\n\"messaging whitespace\", \"differentiation research\".\nProactively invoke when the user mentions competitors or asks how to differentiate.","https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002Fforbotsake-spy\u002FSKILL.md",[101],{"categoryNo":102,"categoryName":103,"taxonomyType":104,"taxonomyTypeDesc":105},"01JBSEEDCAT000000000000021","资料检索与研究","SCENARIO","使用场景",[107,9,108,109,110,35,111,31,30,112,28,113,114,115,116,117,118,119,120,121],"forbotsake spy","messaging","analysis","how","competitors","edit","positioning","landscape","missing","how to","differentiation","competitive","you","websearch","matrix",{"skillNo":123,"name":124,"nameZh":6,"description":125,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":126,"categories":127,"keywords":137,"publishedAt":38},"01M6MWRPHXWYB38V08BZM8SSRE","forbotsake-sharpen","Stage 4.5: SHARPEN. Takes a specific outreach target (person or organization) and\nproduces a deep execution plan with contextual research, relationship mapping,\nangle selection, and a multi-touch sequence. Reads your founder profile and strategy\nto leverage warm paths and unfair advantages.\nUse when: \"refine this plan\", \"go deeper on this\", \"sharpen execution\",\n\"how do I approach [person]\", \"outreach to [person]\", \"target [person\u002Forg]\",\n\"approach [person]\", \"engage [org]\".\nProactively invoke when the user mentions approaching a specific person or\norganization as part of their marketing strategy.\nRequires: strategy.md (from \u002Fforbotsake-marketing-start).","https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002Fforbotsake-sharpen\u002FSKILL.md",[128,131,132],{"categoryNo":129,"categoryName":130,"taxonomyType":104,"taxonomyTypeDesc":105},"01JBSEEDCAT000000000000019","营销推广",{"categoryNo":102,"categoryName":103,"taxonomyType":104,"taxonomyTypeDesc":105},{"categoryNo":133,"categoryName":134,"taxonomyType":135,"taxonomyTypeDesc":136},"01JBSEEDCAT000000000000035","市场营销","PROFESSION","适用职业",[138,139,9,140,110,35,141,31,30,112,142,143,144,145,146,147,148,149,120,150],"sharpen","forbotsake sharpen","start","plan","selection","relationship","organization","refine","deeper","deep","leverage","paths","sequence",{"skillNo":152,"name":153,"nameZh":6,"description":154,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":155,"categories":156,"keywords":159,"publishedAt":38},"01M6MWRHXHECX1N4WNHBJJQKER","forbotsake-retro","Stage 9: MEASURE. Reviews what you published, analyzes performance data,\nand produces a retro report with evidence-based recommendations.\nTells you what to double down on, what to drop, and what to try next.\nUse when: \"what worked\", \"marketing retro\", \"measure results\", \"review performance\",\n\"which content performed best\", \"should I keep doing this\".\nProactively invoke one week after \u002Fforbotsake-publish was last run.","https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002Fforbotsake-retro\u002FSKILL.md",[157,158],{"categoryNo":129,"categoryName":130,"taxonomyType":104,"taxonomyTypeDesc":105},{"categoryNo":133,"categoryName":134,"taxonomyType":135,"taxonomyTypeDesc":136},[160,161,9,162,163,164,165,166,35,167,31,30,112,168,169,170,119,171,172,173],"retro","forbotsake retro","review","performance","run","publish","data","marketing","next","drop","recommendations","reviews","which","what worked",{"skillNo":175,"name":176,"nameZh":6,"description":177,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":178,"categories":179,"keywords":192,"publishedAt":38},"01M6MWRB8GN0AZTEVTQD1GBA8K","forbotsake-publish","Stage 8: SHIP. Two modes: POST (auto-post via Claude for Chrome) or COPY\n(format for copy-paste). Handles X\u002FTwitter threads, LinkedIn posts, blog posts,\nand more. Logs everything for retrospective analysis.\nFor auto-posting, the user needs the Claude for Chrome extension installed.\nWithout it, the skill falls back to COPY mode automatically.\nUse when: \"publish this\", \"format for twitter\", \"post to X\", \"ship this content\",\n\"format for blog\", \"send this email\", \"ready to publish\", \"auto-post\",\n\"post this to X\", \"publish to LinkedIn\".\nProactively invoke when the user has content in content\u002F and says they want to ship it.","https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002Fforbotsake-publish\u002FSKILL.md",[180,183,186,189],{"categoryNo":181,"categoryName":182,"taxonomyType":104,"taxonomyTypeDesc":105},"01JBSEEDCAT000000000000005","邮件与沟通",{"categoryNo":184,"categoryName":185,"taxonomyType":104,"taxonomyTypeDesc":105},"01JBSEEDCAT000000000000018","写作与文案",{"categoryNo":187,"categoryName":188,"taxonomyType":104,"taxonomyTypeDesc":105},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":190,"categoryName":191,"taxonomyType":135,"taxonomyTypeDesc":136},"01JBSEEDCAT000000000000036","内容创作者",[165,193,9,194,195,196,197,198,32,31,30,112,199,200,201,202,203,204,205,206],"forbotsake publish","MCP","image","create","logs","ship","context","auto","ready","chrome extension","posts","paste","mode","send",{"skillNo":208,"name":209,"nameZh":6,"description":210,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":6,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":211,"categories":212,"keywords":215,"publishedAt":38},"01M6MWR5K98QN9BDH4179Y47F0","forbotsake-marketing-start","The starting point for marketing your product. Reads your codebase to understand\nwhat you built, asks 5 hard questions about positioning, audience, and channels,\nthen produces strategy.md with your marketing strategy and first concrete move.\nUse when: \"how should I market this\", \"go-to-market\", \"GTM\", \"positioning\",\n\"I don't know how to sell this\", \"help me market\", \"marketing strategy\".\nProactively invoke when the user asks about marketing, growth, or getting users\nand no strategy.md exists yet.","https:\u002F\u002Fgithub.com\u002Fforbotsake\u002Fforbotsake\u002Fblob\u002Fmain\u002Fforbotsake-marketing-start\u002FSKILL.md",[213,214],{"categoryNo":129,"categoryName":130,"taxonomyType":104,"taxonomyTypeDesc":105},{"categoryNo":133,"categoryName":134,"taxonomyType":135,"taxonomyTypeDesc":136},[140,167,216,9,217,218,110,35,31,30,112,219,113,220,116,221,222,119,120,223],"forbotsake marketing start","go","product","me","gtm","go-to-market","move","for"]