diff --git a/bin/gstack-gbrain-install b/bin/gstack-gbrain-install
new file mode 100755
index 00000000..c5bfa991
--- /dev/null
+++ b/bin/gstack-gbrain-install
@@ -0,0 +1,183 @@
+#!/usr/bin/env bash
+# gstack-gbrain-install — install the gbrain CLI on a local Mac.
+#
+# Usage:
+# gstack-gbrain-install [--install-dir
] [--pinned-commit ] [--dry-run]
+#
+# D5 detect-first: before cloning anywhere, probe likely pre-existing
+# locations (~/git/gbrain and ~/gbrain) and reuse a working clone if one
+# exists. Falls back to a fresh clone of the pinned commit at ~/gbrain
+# (override with GBRAIN_INSTALL_DIR or --install-dir).
+#
+# D19 PATH-shadowing: after `bun link`, compare `gbrain --version` output
+# to the install-dir's package.json version. On mismatch, abort with an
+# actionable error listing every gbrain on PATH. Never "silently fixes"
+# PATH; setup skills should refuse broken environments.
+#
+# Prerequisites (checked before doing anything):
+# - bun (install: curl -fsSL https://bun.sh/install | bash)
+# - git
+# - network reachability to https://github.com
+#
+# The pinned commit is declared here rather than resolved dynamically so
+# upgrades are explicit and reviewable. Update PINNED_COMMIT when gstack
+# verifies compatibility with a new gbrain release.
+#
+# Env:
+# GBRAIN_INSTALL_DIR — override default install path (~/gbrain)
+#
+# Exit codes:
+# 0 — success (or --dry-run printed the plan)
+# 2 — prerequisite missing or invalid argument
+# 3 — post-install validation failed (PATH shadow, broken binary, etc.)
+set -euo pipefail
+
+# --- defaults ---
+PINNED_COMMIT="08b3698e90532b7b66c445e6b1d8cdfe71822802" # gbrain v0.18.2
+PINNED_TAG="v0.18.2"
+GBRAIN_REPO_URL="https://github.com/garrytan/gbrain.git"
+DEFAULT_INSTALL_DIR="${GBRAIN_INSTALL_DIR:-$HOME/gbrain}"
+INSTALL_DIR="$DEFAULT_INSTALL_DIR"
+DRY_RUN=false
+VALIDATE_ONLY=false
+
+die() { echo "gstack-gbrain-install: $*" >&2; exit 2; }
+fail() { echo "gstack-gbrain-install: $*" >&2; exit 3; }
+log() { echo "gstack-gbrain-install: $*"; }
+
+# --- parse args ---
+while [ $# -gt 0 ]; do
+ case "$1" in
+ --install-dir) INSTALL_DIR="$2"; shift 2 ;;
+ --pinned-commit) PINNED_COMMIT="$2"; PINNED_TAG=""; shift 2 ;;
+ --dry-run) DRY_RUN=true; shift ;;
+ --validate-only) VALIDATE_ONLY=true; shift ;;
+ --help|-h) sed -n '2,30p' "$0" | sed 's/^# \{0,1\}//'; exit 0 ;;
+ *) die "unknown flag: $1" ;;
+ esac
+done
+
+# --- prerequisites ---
+check_prereq() {
+ local bin="$1"
+ local hint="$2"
+ if ! command -v "$bin" >/dev/null 2>&1; then
+ fail "required tool '$bin' not found. $hint"
+ fi
+}
+
+if ! $VALIDATE_ONLY; then
+ check_prereq bun "Install: curl -fsSL https://bun.sh/install | bash"
+ check_prereq git "Install: xcode-select --install (macOS) or your package manager"
+
+ # GitHub reachability — fail fast if offline rather than hanging `git clone`.
+ # --max-time 10, --head (no body), quiet. Status code 200-4xx means we reached
+ # the server (even 404 is reachability proof).
+ if ! curl -s --head --max-time 10 https://github.com >/dev/null 2>&1; then
+ fail "cannot reach https://github.com. Check your network and try again."
+ fi
+fi
+
+# --- D5 detect-first: probe common locations before cloning fresh ---
+# Accept any directory that looks like a gbrain clone: has package.json
+# with name "gbrain" and a `bin.gbrain` entry. Don't accept version mismatches
+# here — we'll let bun link run and then D19-validate.
+is_valid_clone() {
+ local dir="$1"
+ [ -d "$dir" ] || return 1
+ [ -f "$dir/package.json" ] || return 1
+ local name
+ name=$(jq -r '.name // empty' "$dir/package.json" 2>/dev/null || true)
+ [ "$name" = "gbrain" ] || return 1
+ local bin
+ bin=$(jq -r '.bin.gbrain // empty' "$dir/package.json" 2>/dev/null || true)
+ [ -n "$bin" ] || return 1
+ return 0
+}
+
+DETECTED_CLONE=""
+if ! $VALIDATE_ONLY; then
+ for candidate in "$HOME/git/gbrain" "$HOME/gbrain" "$INSTALL_DIR"; do
+ if is_valid_clone "$candidate"; then
+ DETECTED_CLONE="$candidate"
+ break
+ fi
+ done
+fi
+
+if $VALIDATE_ONLY; then
+ log "validate-only mode: skipping detect + clone + install + link"
+elif [ -n "$DETECTED_CLONE" ]; then
+ log "detected existing gbrain clone at $DETECTED_CLONE — reusing"
+ INSTALL_DIR="$DETECTED_CLONE"
+else
+ # Fresh clone path.
+ if $DRY_RUN; then
+ log "DRY RUN: would clone $GBRAIN_REPO_URL @ $PINNED_COMMIT → $INSTALL_DIR"
+ exit 0
+ fi
+ if [ -d "$INSTALL_DIR" ]; then
+ fail "install dir $INSTALL_DIR exists but is not a valid gbrain clone. Remove it or pass --install-dir ."
+ fi
+ log "cloning $GBRAIN_REPO_URL → $INSTALL_DIR"
+ git clone --quiet "$GBRAIN_REPO_URL" "$INSTALL_DIR"
+ ( cd "$INSTALL_DIR" && git checkout --quiet "$PINNED_COMMIT" )
+ log "pinned to $PINNED_COMMIT${PINNED_TAG:+ ($PINNED_TAG)}"
+fi
+
+if $DRY_RUN; then
+ log "DRY RUN: would run bun install + bun link in $INSTALL_DIR"
+ exit 0
+fi
+
+# --- install + link ---
+if ! $VALIDATE_ONLY; then
+ log "running bun install in $INSTALL_DIR"
+ ( cd "$INSTALL_DIR" && bun install --silent )
+ log "running bun link in $INSTALL_DIR"
+ ( cd "$INSTALL_DIR" && bun link --silent )
+fi
+
+# --- D19 PATH-shadowing validation ---
+# Read the version from the install-dir's package.json; compare to
+# `gbrain --version`. If they disagree, PATH is returning a DIFFERENT
+# gbrain than the one we just linked. Fail hard with remediation.
+expected_version=$(jq -r '.version // empty' "$INSTALL_DIR/package.json" 2>/dev/null || true)
+if [ -z "$expected_version" ]; then
+ fail "cannot read version from $INSTALL_DIR/package.json (install may be broken)"
+fi
+
+if ! command -v gbrain >/dev/null 2>&1; then
+ fail "bun link completed but 'gbrain' is not on PATH. Ensure ~/.bun/bin is in your PATH."
+fi
+
+actual_version=$(gbrain --version 2>/dev/null | head -1 | tr -d '[:space:]' || true)
+if [ -z "$actual_version" ]; then
+ fail "gbrain is on PATH but 'gbrain --version' produced no output — the binary may be broken."
+fi
+
+# Tolerate a leading "v" (gbrain may print either "0.18.2" or "v0.18.2").
+expected_norm="${expected_version#v}"
+actual_norm="${actual_version#v}"
+
+if [ "$actual_norm" != "$expected_norm" ]; then
+ echo "" >&2
+ echo "gstack-gbrain-install: PATH SHADOWING DETECTED" >&2
+ echo "" >&2
+ echo " We just linked gbrain $expected_version from $INSTALL_DIR," >&2
+ echo " but PATH is returning gbrain $actual_version." >&2
+ echo "" >&2
+ echo " All gbrain binaries on PATH:" >&2
+ type -a gbrain 2>&1 | sed 's/^/ /' >&2 || true
+ echo "" >&2
+ echo " Fix one of the following, then re-run /setup-gbrain:" >&2
+ echo " a) rm the shadowing binary: rm \$(which gbrain)" >&2
+ echo " b) prepend ~/.bun/bin to PATH in your shell rc" >&2
+ echo " c) point GBRAIN_INSTALL_DIR at the shadowing binary's install dir" >&2
+ echo "" >&2
+ exit 3
+fi
+
+log "installed gbrain $actual_version from $INSTALL_DIR"
+echo ""
+echo "Next: gbrain init --pglite (or run /setup-gbrain for the full setup flow)"