feat: wire setup-gbrain + brain-restore + brain-uninstall to use the helper

setup-gbrain Step 7 now invokes gstack-gbrain-source-wireup --strict after
gstack-brain-init + gbrain_sync_mode is set. Strict mode means the user sees
the failure rather than silently ending up with an unwired brain.

bin/gstack-brain-init drops 60 lines of dead code: the HTTP POST to
${GBRAIN_URL}/ingest-repo, the GBRAIN_URL_VAL/GBRAIN_TOKEN_VAL probes, the
consumers.json writer, and the chore commit step. CONSUMERS_FILE variable
declaration removed. The closing message no longer points at the dead
gstack-brain-consumer add path.

bin/gstack-brain-restore drops the 18-line consumers.json token-rehydration
block (was a no-op for the only consumer that ever existed). Adds a
best-effort wireup invocation after the brain-repo clone so 2nd-Mac restore
gets gbrain federation automatically. Failure prints a stderr WARNING but
does not abort the restore — restore's primary job is the git clone.

bin/gstack-brain-uninstall calls the helper's --uninstall mode (which
removes the gbrain source registration, the git worktree, and the
future-launchd-plist stub) before the existing legacy consumers.json
removal. Ordering is fragile-by-design: helper derives source-id via
multi-fallback so it works even after .git is destroyed.

bin/gstack-brain-consumer gets a DEPRECATED header note. Stays in the tree
for one cycle of grace; removal in v1.13.0.0.

setup-gbrain/SKILL.md is regenerated from the .tmpl via gen:skill-docs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-04-25 22:07:32 -07:00
parent b21bf34893
commit fbb1a82d97
6 changed files with 65 additions and 91 deletions
+6 -69
View File
@@ -22,11 +22,9 @@
# 8. Prompt for remote (default: gh repo create --private gstack-brain-$USER)
# 9. Initial commit + push
# 10. Write ~/.gstack-brain-remote.txt (URL-only, safe to share)
# 11. Register GBrain consumer (HTTP POST if GBRAIN_URL set; else defer)
#
# Env:
# GSTACK_HOME — override ~/.gstack
# GBRAIN_URL — GBrain ingest endpoint base URL (for consumer registration)
set -euo pipefail
@@ -34,7 +32,6 @@ GSTACK_HOME="${GSTACK_HOME:-$HOME/.gstack}"
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
CONFIG_BIN="$SCRIPT_DIR/gstack-config"
REMOTE_FILE="$HOME/.gstack-brain-remote.txt"
CONSUMERS_FILE="$GSTACK_HOME/consumers.json"
REMOTE_URL=""
while [ $# -gt 0 ]; do
@@ -280,68 +277,6 @@ fi
echo "$REMOTE_URL" > "$REMOTE_FILE"
chmod 600 "$REMOTE_FILE"
# ---- register GBrain consumer ----
mkdir -p "$GSTACK_HOME"
CONSUMER_STATUS="pending"
GBRAIN_URL_VAL="${GBRAIN_URL:-$("$CONFIG_BIN" get gbrain_url 2>/dev/null || echo "")}"
GBRAIN_TOKEN_VAL="${GBRAIN_TOKEN:-$("$CONFIG_BIN" get gbrain_token 2>/dev/null || echo "")}"
if [ -n "$GBRAIN_URL_VAL" ] && [ -n "$GBRAIN_TOKEN_VAL" ]; then
# Try the HTTP handoff.
HTTP_RESP=$(curl -sS -X POST "${GBRAIN_URL_VAL%/}/ingest-repo" \
-H "Authorization: Bearer $GBRAIN_TOKEN_VAL" \
-H "Content-Type: application/json" \
--data "{\"repo_url\":\"$REMOTE_URL\"}" \
-w "\n%{http_code}" 2>&1 || echo -e "\ncurl-error")
HTTP_CODE=$(echo "$HTTP_RESP" | tail -1)
if [ "$HTTP_CODE" = "200" ] || [ "$HTTP_CODE" = "201" ] || [ "$HTTP_CODE" = "204" ]; then
CONSUMER_STATUS="ok"
echo "GBrain consumer registered: $GBRAIN_URL_VAL"
else
echo "GBrain ingest endpoint returned HTTP $HTTP_CODE; will retry on next skill run."
fi
elif [ -z "$GBRAIN_URL_VAL" ]; then
echo "(GBRAIN_URL not configured; skipping consumer registration. Set it with:"
echo " gstack-config set gbrain_url <url>"
echo " gstack-config set gbrain_token <token>"
echo " then run: gstack-brain-consumer add gbrain --ingest-url <url> --token <token>)"
fi
# Write consumers.json — the canonical registry. Tokens are NOT stored here;
# they stay in gstack-config (machine-local). This file IS synced so a new
# machine knows which consumers exist and can prompt for tokens.
python3 - "$CONSUMERS_FILE" "$GBRAIN_URL_VAL" "$CONSUMER_STATUS" <<'PYEOF'
import sys, json, os
path, url, status = sys.argv[1:4]
try:
with open(path) as f:
data = json.load(f)
except (FileNotFoundError, json.JSONDecodeError):
data = {"consumers": []}
# Upsert GBrain entry.
entry = {"name": "gbrain", "ingest_url": url, "status": status, "token_ref": "gbrain_token"}
updated = False
for i, c in enumerate(data.get("consumers", [])):
if c.get("name") == "gbrain":
data["consumers"][i] = entry
updated = True
break
if not updated:
data.setdefault("consumers", []).append(entry)
with open(path, "w") as f:
json.dump(data, f, indent=2)
f.write("\n")
PYEOF
# Stage and commit consumers.json in the same session.
cd "$GSTACK_HOME"
git add -f consumers.json 2>/dev/null || true
if ! git diff --cached --quiet 2>/dev/null; then
git -c user.email="gstack@localhost" -c user.name="gstack-brain-init" \
commit -q -m "chore: register GBrain consumer"
git push -q origin HEAD 2>/dev/null || true
fi
# ---- done ----
cat <<EOF
@@ -350,12 +285,14 @@ Repo: $GSTACK_HOME (git)
Remote: $REMOTE_URL
Remote URL also saved at: $REMOTE_FILE
Sync happens automatically at the start and end of each skill (no daemon).
Check status anytime with:
Sync to GitHub happens automatically at the start and end of each skill
(no daemon). Check status anytime with:
gstack-brain-sync --status
To activate sync, the next skill you run will ask you one question about
privacy mode (sync everything / artifacts only / off).
The next skill run will ask you one question about privacy mode (full /
artifacts-only / off). After that, /setup-gbrain Step 7 (or the
gstack-gbrain-source-wireup helper) registers this repo as a federated
source on gbrain so its content is searchable via 'gbrain search'.
New machine? On the other laptop, put a copy of:
$REMOTE_FILE