Skip to content

fix(init): --help should not mutate config or scan filesystem#762

Open
brandonlipman wants to merge 1 commit intogarrytan:masterfrom
brandonlipman:fix/init-help-config-overwrite
Open

fix(init): --help should not mutate config or scan filesystem#762
brandonlipman wants to merge 1 commit intogarrytan:masterfrom
brandonlipman:fix/init-help-config-overwrite

Conversation

@brandonlipman
Copy link
Copy Markdown
Contributor

@brandonlipman brandonlipman commented May 9, 2026

Summary

gbrain init --help currently falls through to the smart-detection branch in runInit() instead of printing help. From a directory with 1000+ .md files, that path scans cwd, prints "Found ~N .md files. For a brain this size, Supabase gives faster search...", and then defaults to PGLite — calling saveConfig() and overwriting any existing Postgres config.

Repro

Confirmed on a real install:

$ cat ~/.gbrain/config.json
{ \"engine\": \"postgres\", \"database_url\": \"postgresql://postgres.<ref>:<pwd>@aws-1-us-west-2.pooler.supabase.com:6543/postgres\" }

$ cd ~ && gbrain init --help    # ~ has 1000+ .md files
Found ~1500 .md files. For a brain this size, Supabase gives faster
search and remote access ($25/mo). PGLite works too but search will be slower at scale.

  gbrain init --supabase   Set up with Supabase (recommended for large brains)
  gbrain init --pglite     Use local PGLite anyway

Setting up local brain with PGLite (no server needed)...
[migration output...]

$ cat ~/.gbrain/config.json
{ \"engine\": \"pglite\", \"database_path\": \"/Users/<user>/.gbrain/brain.pglite\" }

The Supabase data was intact, but gbrain stopped pointing at it until config was manually restored from a backup. On a 10K+ page brain that's a several-minute incident at best, a data-loss-shaped scare at worst ("did gbrain init just nuke my brain?").

Root cause

cli.ts:62-69 only routes --helpprintOpHelp() for shared-op commands:

if (subArgs.includes('--help') || subArgs.includes('-h')) {
  const op = cliOps.get(command);
  if (op) {
    printOpHelp(op);
    return;
  }
}

CLI_ONLY commands (init, embed, serve, etc.) skip this branch — they fall through to handleCliOnly with --help still in argv. None of the CLI_ONLY handlers check for it.

Fix

Add a --help / -h guard at the top of runInit() that prints help text and returns. Help should never mutate state — Postel's robustness principle for CLI tools.

Help text covers all flags (engine selection, AI provider options, thin-client mode) so users running --help get the canonical list rather than reading the source.

Why per-command, not cli.ts-level

A wider architectural fix — adding --help routing for all CLI_ONLY commands in cli.ts — is plausible follow-up, but each CLI_ONLY command would still need its own help text. This per-command pattern matches how shared ops do it via printOpHelp(). Init is the highest-stakes case because it's the only CLI_ONLY command that calls saveConfig().

If the maintainer prefers the wider fix, happy to extend; flagging here as the local-minimum patch.

Smoke test

From a directory with 1500 .md files, with GBRAIN_HOME pointed at a fresh tempdir:

  • Before fix: ~/.gbrain/config.json materialized with engine: 'pglite'
  • After fix: help text printed, no config dir created

Test plan

  • bun run typecheck clean
  • Smoke-tested gbrain init --help from a 1500-file directory — no config mutation
  • No automated test added (init.ts has no existing test coverage; happy to add test/init-help-guard.test.ts if desired)

🤖 Generated with Claude Code


View in Codesmith
Need help on this PR? Tag @codesmith with what you need.

  • Let Codesmith autofix CI failures and bot reviews

`gbrain init --help` (and `-h`) currently fall through to the smart-detection
branch in runInit(), which scans cwd for .md files and on a directory with
1000+ files prints "Found ~1500 .md files. For a brain this size, Supabase
gives faster search..." then defaults to PGLite — calling saveConfig() and
overwriting any existing Postgres config with `engine: 'pglite' +
database_path: ~/.gbrain/brain.pglite`.

Confirmed in the wild: ran `gbrain init --help` from $HOME on a machine where
~/.gbrain/config.json pointed at a Supabase Postgres brain with 10K+ pages.
The config was silently flipped to PGLite. The Supabase data was intact, but
gbrain stopped pointing at it until the config was manually restored.

Root cause: cli.ts:62-69 only routes --help → printOpHelp() for shared-op
commands; CLI_ONLY commands (init, embed, etc.) fall through to their handler
with --help still in argv. None of them check for it.

Fix: add a --help/-h guard at the top of runInit() that prints help text and
returns. Help should never mutate state — Postel's robustness principle for
CLI tools.

Help text covers all flags (engine selection, AI provider options, thin-client
mode) so users running `--help` get the canonical list rather than having to
read the source.

A wider architectural fix — adding --help routing for all CLI_ONLY commands in
cli.ts — is plausible follow-up, but each CLI_ONLY command would still need
its own help text. This per-command pattern matches how shared ops handle it
via printOpHelp(). Init is the highest-stakes case because it's the only
CLI_ONLY command that calls saveConfig().

Smoke test: from a directory with 1500 .md files, with GBRAIN_HOME pointed at
a fresh tempdir:
  - Before fix: ~/.gbrain/config.json materialized with engine: 'pglite'
  - After fix: help text printed, no config dir created

`bun run typecheck` clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant