Skip to content

Add first-run detection and install-time setup wizard#49

Merged
greynewell merged 5 commits intomainfrom
feature/first-run-setup
Apr 7, 2026
Merged

Add first-run detection and install-time setup wizard#49
greynewell merged 5 commits intomainfrom
feature/first-run-setup

Conversation

@greynewell
Copy link
Copy Markdown
Contributor

@greynewell greynewell commented Apr 7, 2026

Summary

  • install.sh: after a successful install, auto-runs supermodel setup when the terminal is interactive (TTY-gated — skips silent/piped/CI installs)
  • cmd/root.go: PersistentPreRunE checks for a config file on every command; if none exists, prints Run 'supermodel setup' to get started. and exits — bypass list covers setup, login, logout, version, help, completion

Test plan

  • Fresh install via curl … | sh in a terminal → setup wizard launches automatically
  • curl … | sh piped to a file / in CI → setup wizard does NOT launch
  • Running supermodel analyze with no config → prints nudge and exits cleanly
  • Running supermodel setup with no config → wizard runs without nudge loop
  • Running supermodel version with no config → prints version, no nudge

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • CLI now enforces API key configuration for most commands and prompts users to run setup if missing.
    • Installer optionally launches interactive setup when a terminal is available.
  • Changes

    • Setup/login/logout/version/help/completion still bypass the API-key check for first-time use.
    • Setup wizard flow and messaging improved: clearer step ordering, explicit hook and file-mode guidance, automatic post-setup file watching, and reloading config after authentication.

- install.sh: run `supermodel setup` automatically after install when
  attached to a TTY (skips piped/CI installs)
- cmd/root.go: PersistentPreRunE nudges users without a config to run
  `supermodel setup`; no-op for setup/login/logout/version/help/completion

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 8ba668ef-f97a-414d-9ad0-523b3a112351

📥 Commits

Reviewing files that changed from the base of the PR and between 59e1031 and bd3526e.

📒 Files selected for processing (2)
  • cmd/root.go
  • install.sh

Walkthrough

Added a root-level Cobra PersistentPreRunE that skips config loading for an explicit exemption list; for other commands it loads config and enforces presence of an API key. Installer now invokes setup only when a TTY is present. Setup wizard now performs auth login, reloads config, and starts file-watching with reordered UI steps.

Changes

Cohort / File(s) Summary
CLI root gating
cmd/root.go
Added PersistentPreRunE that checks a noConfigCommands set (setup, login, logout, version, help, completion, __complete, __completeNoDesc). Non-exempt commands call config.Load(); on load error prints message and os.Exit(1); if cfg.APIKey empty prints setup hint and os.Exit(1).
Installer behavior
install.sh
After printing version, installer now checks for a controlling TTY and, if present, runs the installed binary's setup with stdin from /dev/tty so interactive prompts work when installer is piped.
Setup wizard flow
internal/setup/wizard.go
Wizard now calls auth.Login(ctx) when cfg.APIKey is empty, reloads config, and always starts files.Watch(...) post-setup. Reordered steps (Claude Code hook moved earlier), removed selectMenu helper, preserved boolPtr, and adjusted UI messages and hook-note display logic.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant User as User
participant CLI as CLI Root
participant Config as Config Loader
participant Cmd as Subcommand
User->>CLI: run "supermodel "
CLI->>CLI: check noConfigCommands
alt command in exemption
CLI->>Cmd: run subcommand
else requires config
CLI->>Config: config.Load()
alt load fails
CLI->>User: print "Error loading config: ..." (stderr)
CLI--)CLI: os.Exit(1)
else cfg.APIKey empty
CLI->>User: print "Run 'supermodel setup' to get started." (stderr)
CLI--)CLI: os.Exit(1)
else config OK
CLI->>Cmd: run subcommand
end
end

mermaid
sequenceDiagram
participant User as User
participant Installer as install.sh
participant CLI as Installed CLI (setup)
participant Auth as Auth
participant Config as Config Loader
participant Files as File Watcher
User->>Installer: run installer
Installer->>Installer: print binary version
alt stdout is TTY
Installer->>CLI: run "supermodel setup" (stdin=/dev/tty)
CLI->>Auth: auth.Login(ctx) [if no API key]
Auth->>Config: write API key
CLI->>Config: config.Load() (reload)
CLI->>Files: files.Watch(...)
else non-interactive
Installer->>User: skip setup
end

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • jonathanpopham

Poem

🔐 A gate at CLI's porch, politely set,
The installer waits if no human is met.
Setup logs in and reloads the keys,
Files start watching, hooks flip with ease.
Small reorder, tidy flow — the CLI breathes. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add first-run detection and install-time setup wizard' clearly and concisely summarizes the main changes across the PR: enabling automatic setup on first install (via install.sh TTY detection) and enforcing config checks (via root.go's PersistentPreRunE).
Description check ✅ Passed The PR description follows the required template structure with a clear Summary section explaining the changes, a detailed Test plan with concrete scenarios, and appropriate context linking to Claude Code.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/first-run-setup

Comment @coderabbitai help to get the list of available commands and usage tips.

greynewell and others added 2 commits April 7, 2026 15:04
- Authentication is now inline: if no API key, opens browser OAuth flow
  immediately instead of redirecting user to run `supermodel login` first
- Removed the file mode toggle step — file mode is the default, no question
  needed; the product is explained in the header instead
- 3 clean steps: Authenticate → Repository → Claude Code hook → Analyze
- Punchier copy, fewer words per step
- Removed unused boolPtr and selectMenu helpers

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add back the file mode Y/N step with a concise explanation of
  what .graph files are and how agents use them
- Replace the "run analyze now?" prompt with an unconditional
  supermodel watch launch — explains what watch does and how to
  stop/restart before handing over the terminal

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (1)
cmd/root.go (1)

37-42: Optional: avoid the second config.Load() on every gated command.

This pre-run now reads config once, and commands like cmd/analyze.go read it again in RunE. Passing the loaded cfg through cmd.Context() would keep the gate and drop the duplicate file/env parse on every call.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@cmd/root.go` around lines 37 - 42, Pre-load the config once in the root
command pre-run and attach it to the cobra command context so subcommands stop
calling config.Load() again: after cfg, err := config.Load() in the root
pre-run, create a new context via context.WithValue(ctx, configKey, cfg) and
call cmd.SetContext(newCtx); then update gated commands (e.g., Analyze RunE) to
read cfg from cmd.Context().Value(configKey) instead of calling config.Load(),
and remove the duplicated file/env parse.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@cmd/root.go`:
- Around line 37-40: The current check conflates config load errors with missing
API key; change the logic around config.Load() in root.go so you first handle a
non-nil err from config.Load() by printing the actual error (include err) to
stderr and exiting, and only if err is nil then inspect cfg.APIKey and print the
"Run 'supermodel setup'..." nudge when cfg.APIKey == ""; reference the cfg
variable and config.Load() call and ensure the err branch reports the real error
instead of the setup message.
- Around line 13-20: The noConfigCommands allowlist misses Cobra's internal
completion commands so PersistentPreRunE tries to load config for "__complete"
and crashes; update the map noConfigCommands to include the keys "__complete"
and "__completeNoDesc", or modify the root PersistentPreRunE check to walk the
command's parent chain (using cmd.Name() on each ancestor) and skip config
loading if any ancestor name is in noConfigCommands; reference the
noConfigCommands variable and the root PersistentPreRunE handler when making the
change.

In `@install.sh`:
- Around line 76-79: The setup wizard is using stdout terminal check ([ -t 1 ])
but promptui reads stdin, which is the piped shell; modify the logic around the
installation invocation (the block that calls "$INSTALL_DIR/$BINARY" setup) to
instead verify /dev/tty exists and is readable, and when launching the setup
command redirect /dev/tty into its stdin (e.g., open /dev/tty as stdin for the
"$INSTALL_DIR/$BINARY" setup process) so interactive prompts (called via setup)
work even in piped installs; keep the existing variables INSTALL_DIR and BINARY
and only change the condition and invocation to use /dev/tty as stdin.

---

Nitpick comments:
In `@cmd/root.go`:
- Around line 37-42: Pre-load the config once in the root command pre-run and
attach it to the cobra command context so subcommands stop calling config.Load()
again: after cfg, err := config.Load() in the root pre-run, create a new context
via context.WithValue(ctx, configKey, cfg) and call cmd.SetContext(newCtx); then
update gated commands (e.g., Analyze RunE) to read cfg from
cmd.Context().Value(configKey) instead of calling config.Load(), and remove the
duplicated file/env parse.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b3d6e0d8-8bfc-4689-8967-d12754086374

📥 Commits

Reviewing files that changed from the base of the PR and between f902bef and 266e537.

📒 Files selected for processing (2)
  • cmd/root.go
  • install.sh

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
internal/setup/wizard.go (1)

137-177: ⚠️ Potential issue | 🟠 Major

Make config-save failure fatal before printing success.

With the new first-run gate, setup is only really done once the config is persisted. Right now a save error is downgraded to a warning, then the wizard still prints Setup complete and starts watch mode. A user who came in with only an env-var API key can walk away thinking setup worked, then hit the “Run 'supermodel setup' to get started.” nudge again on the next command.

💡 Suggested fix
 	cfg.Files = boolPtr(filesEnabled)
 	if err := cfg.Save(); err != nil {
-		fmt.Fprintf(os.Stderr, "  %sWarning: could not save config: %v%s\n", yellow, err, reset)
+		return fmt.Errorf("save config: %w", err)
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/setup/wizard.go` around lines 137 - 177, The config save failure
should be fatal: after calling cfg.Save() (the existing if err := cfg.Save();
err != nil { ... } block), do not just warn and continue; instead print the
error and terminate so the function never prints "Setup complete" or starts
watch mode. Replace the current warning branch with a fatal exit (e.g., print
the error via fmt.Fprintf and call os.Exit(1) or return a non-nil error from
this function) so that on cfg.Save() failure the code never reaches the success
messages or the return files.Watch(...) call.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/setup/wizard.go`:
- Around line 50-58: After auth.Login(ctx) succeeds, the code currently ignores
a failure from config.Load() and continues using the stale cfg which can
overwrite the new API key later; update the post-login reload in wizard.go so
that if config.Load() returns an error you return that error (or wrap it with
context) instead of keeping the old cfg, i.e. in the block after auth.Login(ctx)
check loadErr and fail fast (return fmt.Errorf(...)) when config.Load() fails so
the new key in the persisted config isn't lost.

---

Outside diff comments:
In `@internal/setup/wizard.go`:
- Around line 137-177: The config save failure should be fatal: after calling
cfg.Save() (the existing if err := cfg.Save(); err != nil { ... } block), do not
just warn and continue; instead print the error and terminate so the function
never prints "Setup complete" or starts watch mode. Replace the current warning
branch with a fatal exit (e.g., print the error via fmt.Fprintf and call
os.Exit(1) or return a non-nil error from this function) so that on cfg.Save()
failure the code never reaches the success messages or the return
files.Watch(...) call.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: cb41b5b5-19c8-465b-b5d9-5b595f7e43dc

📥 Commits

Reviewing files that changed from the base of the PR and between 266e537 and 59e1031.

📒 Files selected for processing (1)
  • internal/setup/wizard.go

Comment on lines 50 to +58
if cfg.APIKey == "" {
fmt.Printf(" %sRun 'supermodel login' first, then re-run 'supermodel setup'.%s\n\n", yellow, reset)
return nil
fmt.Printf(" %sOpening your browser to sign in and generate an API key…%s\n\n", dWhite, reset)
if err := auth.Login(ctx); err != nil {
return fmt.Errorf("authentication failed — run 'supermodel login' to try again")
}
// Reload config to pick up the saved key.
if reloaded, loadErr := config.Load(); loadErr == nil {
cfg = reloaded
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Return on a post-login reload failure.

Small but real edge case: auth.Login(ctx) has already saved the new key, so if config.Load() fails here and we keep the old cfg, the later save on Line 138 can write that stale struct back out and clear the key we just created. I’d fail fast instead of silently continuing.

💡 Suggested fix
 		// Reload config to pick up the saved key.
-		if reloaded, loadErr := config.Load(); loadErr == nil {
-			cfg = reloaded
-		}
+		reloaded, loadErr := config.Load()
+		if loadErr != nil {
+			return fmt.Errorf("reload config after login: %w", loadErr)
+		}
+		cfg = reloaded
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/setup/wizard.go` around lines 50 - 58, After auth.Login(ctx)
succeeds, the code currently ignores a failure from config.Load() and continues
using the stale cfg which can overwrite the new API key later; update the
post-login reload in wizard.go so that if config.Load() returns an error you
return that error (or wrap it with context) instead of keeping the old cfg, i.e.
in the block after auth.Login(ctx) check loadErr and fail fast (return
fmt.Errorf(...)) when config.Load() fails so the new key in the persisted config
isn't lost.

greynewell and others added 2 commits April 7, 2026 15:23
- cmd/root.go: split config load error from missing-key nudge so real
  errors (e.g. corrupt YAML) show the actual message rather than the
  setup prompt
- cmd/root.go: add __complete and __completeNoDesc to noConfigCommands
  so Cobra's shell completion internals don't crash without a config
- install.sh: replace [ -t 1 ] with [ -r /dev/tty ] and redirect
  /dev/tty as stdin for setup invocation, so interactive prompts work
  correctly in piped installs (curl … | sh)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@greynewell greynewell merged commit 46ca8f6 into main Apr 7, 2026
6 checks passed
@greynewell greynewell deleted the feature/first-run-setup branch April 7, 2026 19:26
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