From fbb1a82d97b94979f486b033f0411622e6691909 Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sat, 25 Apr 2026 22:07:32 -0700 Subject: [PATCH] feat: wire setup-gbrain + brain-restore + brain-uninstall to use the helper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- bin/gstack-brain-consumer | 5 +++ bin/gstack-brain-init | 75 +++----------------------------------- bin/gstack-brain-restore | 28 ++++---------- bin/gstack-brain-uninstall | 10 +++++ setup-gbrain/SKILL.md | 19 +++++++++- setup-gbrain/SKILL.md.tmpl | 19 +++++++++- 6 files changed, 65 insertions(+), 91 deletions(-) diff --git a/bin/gstack-brain-consumer b/bin/gstack-brain-consumer index cf92ea3e..88ccae73 100755 --- a/bin/gstack-brain-consumer +++ b/bin/gstack-brain-consumer @@ -1,6 +1,11 @@ #!/usr/bin/env bash # gstack-brain-consumer — manage the consumer (reader) registry. # +# DEPRECATED in v1.12.3.0. This binary targets a gbrain HTTP /ingest-repo +# endpoint that never shipped on the gbrain side. Live federation now uses +# `gbrain sources` directly via bin/gstack-gbrain-source-wireup. This file +# stays for one cycle to avoid breaking external scripts; removal in v1.13.0.0. +# # Consumer = a reader that ingests the gstack-brain git repo as a source of # session memory. v1 primary consumer is GBrain; later versions can register # Codex, OpenClaw, or third-party readers. diff --git a/bin/gstack-brain-init b/bin/gstack-brain-init index 3ed48559..4bf665cc 100755 --- a/bin/gstack-brain-init +++ b/bin/gstack-brain-init @@ -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 " - echo " gstack-config set gbrain_token " - echo " then run: gstack-brain-consumer add gbrain --ingest-url --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 <") -PYEOF -fi - # ---- write remote helper file if missing ---- if [ ! -f "$REMOTE_FILE" ]; then echo "$REMOTE_URL" > "$REMOTE_FILE" @@ -222,6 +204,12 @@ if [ ! -f "$REMOTE_FILE" ]; then echo "Wrote $REMOTE_FILE for future skill-run auto-detection." fi +# ---- wire the cloned brain into gbrain (best-effort) ---- +WIREUP_BIN="$SCRIPT_DIR/gstack-gbrain-source-wireup" +if [ -x "$WIREUP_BIN" ]; then + "$WIREUP_BIN" || >&2 echo "WARNING: gbrain wireup failed; run $WIREUP_BIN manually after fixing prereqs" +fi + cat </dev/null || true rm -f "$GSTACK_HOME/.brain-skip.txt" 2>/dev/null || true rm -f "$GSTACK_HOME/.brain-sync-status.json" 2>/dev/null || true rm -rf "$GSTACK_HOME/.brain-sync.lock.d" 2>/dev/null || true + +# ---- unregister gbrain federated source + remove worktree (best-effort) ---- +# The wireup helper handles: gbrain sources remove, git worktree remove, +# launchd plist (future). All best-effort; uninstall continues on failure. +WIREUP_BIN="$SCRIPT_DIR/gstack-gbrain-source-wireup" +if [ -x "$WIREUP_BIN" ]; then + "$WIREUP_BIN" --uninstall 2>/dev/null || true +fi + +# ---- legacy consumers.json (no longer written by gstack-brain-init since v1.12.3.0) ---- rm -f "$GSTACK_HOME/consumers.json" 2>/dev/null || true # ---- clear config keys ---- diff --git a/setup-gbrain/SKILL.md b/setup-gbrain/SKILL.md index 639ab11b..0914b3b8 100644 --- a/setup-gbrain/SKILL.md +++ b/setup-gbrain/SKILL.md @@ -1345,7 +1345,7 @@ For `/setup-gbrain --repo` invocations, execute ONLY Step 6 and exit. --- -## Step 7: Offer gstack-brain-sync +## Step 7: Offer gstack-brain-sync + wire it into gbrain Separate AskUserQuestion: "Also sync your gstack session memory (learnings, plans, retros) to a private git repo that gbrain can index across machines?" @@ -1363,6 +1363,23 @@ If yes: # or "full" if user picked yes-full ``` +Then wire the brain repo into gbrain so its content is searchable from any +gbrain client (this Claude Code session, future Macs, optional cloud agents). +The helper creates a `git worktree` of `~/.gstack/`, registers it as a +federated source on the user's gbrain (Supabase or PGLite), and runs an +initial `gbrain sync`. Local-Mac only. No cloud agent required. Subsequent +skill runs trigger incremental sync via the existing skill-end push hook. + +```bash +~/.claude/skills/gstack/bin/gstack-gbrain-source-wireup --strict +``` + +`--strict` exits non-zero on missing prereqs (gbrain not installed, < 0.18.0, +or no `~/.gstack/.git` yet) so the user sees the failure rather than silently +ending up with an unwired brain. On non-zero exit, surface the helper's +output and STOP per skill rules — search-across-machines won't work until +the prereq is fixed. + --- ## Step 8: Persist `## GBrain Configuration` in CLAUDE.md diff --git a/setup-gbrain/SKILL.md.tmpl b/setup-gbrain/SKILL.md.tmpl index 685e15e0..21c4093c 100644 --- a/setup-gbrain/SKILL.md.tmpl +++ b/setup-gbrain/SKILL.md.tmpl @@ -347,7 +347,7 @@ For `/setup-gbrain --repo` invocations, execute ONLY Step 6 and exit. --- -## Step 7: Offer gstack-brain-sync +## Step 7: Offer gstack-brain-sync + wire it into gbrain Separate AskUserQuestion: "Also sync your gstack session memory (learnings, plans, retros) to a private git repo that gbrain can index across machines?" @@ -365,6 +365,23 @@ If yes: # or "full" if user picked yes-full ``` +Then wire the brain repo into gbrain so its content is searchable from any +gbrain client (this Claude Code session, future Macs, optional cloud agents). +The helper creates a `git worktree` of `~/.gstack/`, registers it as a +federated source on the user's gbrain (Supabase or PGLite), and runs an +initial `gbrain sync`. Local-Mac only. No cloud agent required. Subsequent +skill runs trigger incremental sync via the existing skill-end push hook. + +```bash +~/.claude/skills/gstack/bin/gstack-gbrain-source-wireup --strict +``` + +`--strict` exits non-zero on missing prereqs (gbrain not installed, < 0.18.0, +or no `~/.gstack/.git` yet) so the user sees the failure rather than silently +ending up with an unwired brain. On non-zero exit, surface the helper's +output and STOP per skill rules — search-across-machines won't work until +the prereq is fixed. + --- ## Step 8: Persist `## GBrain Configuration` in CLAUDE.md