$npx -y skills add gaia-react/gaia --skill update-gaiaPull the latest GAIA release into this project without clobbering customizations. Three-way merge per file using .gaia/manifest.json classes. Trigger when the user clicks the statusline Run /update-gaia indicator or asks "update GAIA", "pull the latest GAIA", "apply the new GAI
| 1 | Pull the latest GAIA release into this project without clobbering customizations. Does a three-way comparison per file (adopter / baseline / latest) and respects explicit classes in `.gaia/manifest.json`: |
| 2 | |
| 3 | - **`owned`**: GAIA controls fully. Overwrites silently if unchanged from baseline; prompts if drifted. |
| 4 | - **`shared`**: GAIA seeds, you customize. Emits a `.gaia-merge/` patch for manual resolution on drift. |
| 5 | - **`wiki-owned`**: GAIA-seeded concept/decision/module wiki pages. Same drift handling as `shared`. |
| 6 | - **adopter-owned (implicit)**: anything not in the manifest, plus sentinels like `wiki/hot.md`, `wiki/log.md`, `CHANGELOG.md`, `.gaia/VERSION`, `.gaia/manifest.json`. Never touched. |
| 7 | |
| 8 | Backups land in `.gaia-backup/<timestamp>/`. Conflict patches land in `.gaia-merge/`. |
| 9 | |
| 10 | ## Pre-flight: Worktree check |
| 11 | |
| 12 | This wrapper changes `.gaia/VERSION` and opens a PR, both belong on the main checkout, not a per-SPEC worktree branch. If invoked from a linked worktree, reject hard with a message that surfaces the cached version state from main so the user knows whether a GAIA update is even pending. |
| 13 | |
| 14 | Detection (run this first, before anything else): |
| 15 | |
| 16 | ```bash |
| 17 | common_dir="$(git rev-parse --git-common-dir 2>/dev/null)" |
| 18 | if [ -n "$common_dir" ]; then |
| 19 | case "$common_dir" in |
| 20 | /*) absolute_common_dir="$common_dir" ;; |
| 21 | *) absolute_common_dir="$(pwd)/$common_dir" ;; |
| 22 | esac |
| 23 | main_root="$(cd "$(dirname "$absolute_common_dir")" 2>/dev/null && pwd)" |
| 24 | current_root="$(git rev-parse --show-toplevel 2>/dev/null)" |
| 25 | if [ -n "$main_root" ] && [ -n "$current_root" ] && [ "$main_root" != "$current_root" ]; then |
| 26 | cached_line="Cached state unavailable on main; symlinks may be broken, run \`.gaia/cli/gaia setup link-worktree\` to repair." |
| 27 | cache_file="$main_root/.gaia/local/cache/shared/update-check.json" |
| 28 | if [ -f "$cache_file" ] && command -v jq >/dev/null 2>&1; then |
| 29 | gaia_current="$(jq -r '.gaiaCurrent // ""' "$cache_file" 2>/dev/null)" |
| 30 | gaia_latest="$(jq -r '.gaiaLatest // ""' "$cache_file" 2>/dev/null)" |
| 31 | gaia_has_update="$(jq -r '.gaiaHasUpdate // false' "$cache_file" 2>/dev/null)" |
| 32 | if [ -n "$gaia_current" ] && [ -n "$gaia_latest" ]; then |
| 33 | update_phrase="not-available" |
| 34 | if [ "$gaia_has_update" = "true" ]; then update_phrase="available"; fi |
| 35 | cached_line="Cached on main: GAIA $gaia_current installed; latest $gaia_latest (update $update_phrase)." |
| 36 | fi |
| 37 | fi |
| 38 | cat <<EOF |
| 39 | /update-gaia must run from the main checkout, not a worktree. |
| 40 | |
| 41 | Worktree: $current_root |
| 42 | Main checkout: $main_root |
| 43 | |
| 44 | $cached_line |
| 45 | |
| 46 | Run \`cd $main_root\` then re-invoke /update-gaia. |
| 47 | EOF |
| 48 | exit 1 |
| 49 | fi |
| 50 | fi |
| 51 | ``` |
| 52 | |
| 53 | If the detection does not fire, fall through to the existing `## Pre-flight: Branch check` section. |
| 54 | |
| 55 | ## Pre-flight: Branch check |
| 56 | |
| 57 | ```bash |
| 58 | git branch --show-current |
| 59 | ``` |
| 60 | |
| 61 | If the current branch is `main` or `master`, set a flag (`SHOULD_CREATE_BRANCH=true`) but **do not create the branch yet**, creation is deferred until after the Step 4 "Proceed" confirmation. Steps 1-4 can exit early (already up to date, or the user aborts); branching before then leaves an orphan `chore/update-gaia-*` branch when there was nothing to update. |
| 62 | |
| 63 | Otherwise set `SHOULD_CREATE_BRANCH=false` and proceed on the current branch. |
| 64 | |
| 65 | ## Step 1: Read baseline version |
| 66 | |
| 67 | ```bash |
| 68 | cat .gaia/VERSION 2>/dev/null || echo MISSING |
| 69 | ``` |
| 70 | |
| 71 | If the file is missing, stop and tell the user: |
| 72 | |
| 73 | > "No `.gaia/VERSION` found, this project was not scaffolded from GAIA, or the marker was deleted. Run `/gaia-init` on a fresh `create-gaia` scaffold first." |
| 74 | |
| 75 | Persist the trimmed version as `BASELINE` (e.g., `1.0.0`). |
| 76 | |
| 77 | ## Step 2: Resolve latest release |
| 78 | |
| 79 | ```bash |
| 80 | gh release list --repo gaia-react/gaia --limit 1 --json tagName --jq '.[0].tagName' |
| 81 | ``` |
| 82 | |
| 83 | Persist as `LATEST_TAG` (e.g., `v1.0.1`) and `LATEST` (strip leading `v`). |
| 84 | |
| 85 | If `gh` is unavailable, fall back to: |
| 86 | |
| 87 | ```bash |
| 88 | curl -fsSL https://api.github.com/repos/gaia-react/gaia/releases/latest | jq -r .tag_name |
| 89 | ``` |
| 90 | |
| 91 | If both fail, stop and ask the user to supply the target version explicitly. |
| 92 | |
| 93 | ## Step 3: Compare versions |
| 94 | |
| 95 | - If `LATEST == BASELINE`: |
| 96 | - **First, detect an interrupted prior run.** If `.gaia/VERSION` differs from the last commit (`git diff --quiet HEAD -- .gaia/VERSION` exits non-zero, this catches a staged or unstaged bump), a previous `/update-gaia` already bumped the version but the update was never committed. Do **not** print "up to date", the bumped VERSION makes every re-run look current, so saying it dead-ends the user. Instead read the committed baseline (`git show HEAD:.gaia/VERSION`) for context and tell the user: the update to `v$LATEST` is already applied to the working |