Files
gstack/setup-gbrain/sections/transcript-gate.md.tmpl
Garry Tanandcapy-ai[bot] 4c6fa89cdc v1.91.159.0 security: gstack's Bun entry points ignore the audited repo's .env and bunfig.toml (S1) (#3312)
* security: isolate every gstack Bun entry point from the audited checkout's .env and bunfig.toml

Bun auto-loads .env, .env.local and .env.<NODE_ENV> from its cwd and runs a
cwd bunfig.toml preload before the program starts. gstack runs inside the repo
it audits, so a committed .env could steer state roots, the claude binary or
spend caps, and a bunfig preload ran arbitrary code in every bin and hook.

Every bun shebang, shell and skill caller, TypeScript spawn and compiled build
now disables both (--no-env-file --config=/dev/null, bunIsolationArgs(),
--no-compile-autoload-dotenv/bunfig), with a ratchet and hostile-fixture
regressions.

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: name the directly executed scripts in the isolation ratchet

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: follow the isolation flags in touchfiles, copy lists, recipe assertions and the harness pin

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix: keep browse/src/bun-child-config.ts unchanged so the native cookie qualification still binds

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* fix(windows): the empty bunfig outlives its process so the timeline hook's detached repair child can read it

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* v1.91.159.0 security: gstack's Bun entry points ignore the audited repo's .env and bunfig.toml (S1)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

* test: plan-mode render pattern accepts the isolation flags

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>

---------

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-20 00:24:28 +00:00

104 lines
5.2 KiB
Cheetah

After memory sync is wired (Step 7) but before persisting the CLAUDE.md
config (Step 8), ask whether this machine's coding-agent session
transcripts may go into gbrain. Curated `~/.gstack/` artifacts (learnings,
timeline, plans, designs, retros) sync whatever the answer; this question
covers transcripts only. Transcripts are ingested only after the user picks
`recent`, `all` or `new` (only sessions that start from now on).
Check whether the user already chose. The gate keys on `has`, because `get`
prints the `off` default for a key that was never set:
```bash
_TIM=$(~/.claude/skills/gstack/bin/gstack-config get transcript_ingest_mode 2>/dev/null || true)
if ~/.claude/skills/gstack/bin/gstack-config has transcript_ingest_mode; then
_T='[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]t[0-9][0-9]:[0-9][0-9]:[0-9][0-9]z'
case "$(printf '%s' "$_TIM" | tr -d '[:space:]' | tr '[:upper:]' '[:lower:]')" in
recent|all|off|recent+repos|all+repos|new@$_T|new@$_T+repos)
echo "TRANSCRIPT_MODE: $_TIM (repos: $(~/.claude/skills/gstack/bin/gstack-config get transcript_repos 2>/dev/null || true))" ;;
*) echo "TRANSCRIPT_MODE: ask (stored value '$_TIM' is not recognized by this version)" ;;
esac
else
echo "TRANSCRIPT_MODE: ask (not set)"
fi
```
- `TRANSCRIPT_MODE:` with a value (`recent`, `all`, `off`, `new@<time>`,
any of them with `+repos`): the user already chose. Do not ask again;
continue to Step 8.
- `TRANSCRIPT_MODE: ask`: ask once, below. With `SESSION_KIND: spawned` or
`headless`, do not ask and do not store a value. Consent is never
auto-chosen; leave the key unset (transcripts stay skipped) and report
that the next interactive `/sync-gbrain` will ask.
Size the question with two probes. `--sources transcript` makes the probe
count transcripts even though none are consented yet:
```bash
bun --no-env-file --config=/dev/null run ~/.claude/skills/gstack/bin/gstack-memory-ingest.ts --probe --sources transcript
bun --no-env-file --config=/dev/null run ~/.claude/skills/gstack/bin/gstack-memory-ingest.ts --probe --sources transcript --all-history
```
Read `Total files in window`, `Total bytes` and the estimate from each.
**No transcripts found** (both probes report 0): ask yes/no instead:
> "Ingest coding-agent sessions as they appear?"
Yes means `recent` (then ask the scope question below); No stores `off`.
**Otherwise**, AskUserQuestion with the counts. Name both session sources
and the destination brain from Step 4: the user's Supabase brain (Paths 1,
2a, 2b) or the local PGLite brain on this machine (Path 3).
> "Found <N_recent> Claude Code and Codex sessions from the last 90 days
> (<N_all> in all history, <bytes>) across the projects on this machine
> that your repo policy allows. Ingest them into your <Supabase | local
> PGLite> brain?
>
> What you get: gstack skills load recent context from your past sessions,
> so the agent finds your prior work without you describing it, and you
> can ask 'what was I doing on day X'. Per-session pages are searchable,
> taggable and deletable. Secret scanning runs before any push.
>
> What stays the same: nothing leaves this machine unless brain sync is
> enabled (Step 7). Per-repo trust policies still apply. The first full
> sync can take a while on a large history (estimate: <est>); skills stay
> usable while it runs.
>
> Multi-machine note: with brain sync enabled, transcript pages sync across
> your machines. Deleting a page later removes it from gbrain, but git
> history keeps it in earlier commits. Use `gstack-transcript-prune` to
> delete in bulk, and `git filter-repo` on the brain remote to remove it
> from history."
Options:
- A) Yes, last 90 days (`recent`)
- B) Yes, all history (`all`)
- C) Yes, only new sessions starting now (`new`)
- E) No, never ingest transcripts (`off`)
On any yes, ask a second, separate question: "Which repos' sessions?"
A) every project repo your repo policy allows, or B) only this repo (offer
B only when `git remote get-url origin` succeeds here). Store nothing until
both answers are known; if the user cancels in between, store nothing.
Then store the scope, then the mode value (never the letter; `new` stores
`new@` plus the current UTC time):
```bash
~/.claude/skills/gstack/bin/gstack-config set transcript_repos "$(git remote get-url origin)" # only this repo
~/.claude/skills/gstack/bin/gstack-config unset transcript_repos # every repo
~/.claude/skills/gstack/bin/gstack-config set transcript_ingest_mode <recent|all|off|new@$(date -u +%Y-%m-%dT%H:%M:%SZ)>
bun --no-env-file --config=/dev/null run ~/.claude/skills/gstack/lib/transcript-consent.ts --describe
```
Tell the user the `--describe` line: what will be ingested, in words.
For `recent`, `all` or `new@…`, run the first sync now:
```bash
bun --no-env-file --config=/dev/null run ~/.claude/skills/gstack/bin/gstack-gbrain-sync.ts --full --no-brain-sync
```
(`--no-brain-sync` because Step 7 already wired that path; this runs the
code import and memory ingest stages, and `all` walks the full history.)
For `off`, run nothing here; other memory still syncs on the next
`/sync-gbrain`.
New transcripts are ingested on the next `/sync-gbrain` run. Nothing at
skill start ingests them.
Reference doc for users: `setup-gbrain/memory.md#transcripts`.