Streamline first-time setup with recommended defaults - #660
Conversation
There was a problem hiding this comment.
Pull request overview
Streamlines onboarding with OAuth-based recommended defaults while preserving customizable setup.
Changes:
- Adds fast setup with automatic account and agent configuration.
- Improves installer TTY handling and mise upgrades.
- Adds concise branding, output, documentation, and tests.
Tip
If you aren't ready for review, convert to a draft PR.
Click "Convert to draft" or run gh pr ready --undo.
Click "Ready for review" or run gh pr ready to reengage.
Reviewed changes
Copilot reviewed 21 out of 21 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
.surface |
Adds the customize flag. |
README.md |
Documents setup and mise. |
e2e/installer.bats |
Tests installer TTY routing. |
e2e/setup.bats |
Updates setup safety documentation. |
install.md |
Updates installation guidance. |
internal/appctx/context.go |
Adds notice suppression state. |
internal/cli/root.go |
Suppresses post-setup notices. |
internal/cli/root_test.go |
Tests notice suppression. |
internal/commands/commands.go |
Updates setup catalog text. |
internal/commands/quickstart.go |
Routes first runs to fast setup. |
internal/commands/upgrade_selfupdate.go |
Detects mise installations. |
internal/commands/upgrade_selfupdate_test.go |
Tests mise detection. |
internal/commands/wizard.go |
Implements recommended setup. |
internal/commands/wizard_agents.go |
Configures detected agents. |
internal/commands/wizard_test.go |
Tests streamlined setup. |
internal/tui/brand.go |
Adds logo-only rendering. |
internal/tui/brand_anim.go |
Adds logo-only animation. |
internal/tui/brand_test.go |
Tests logo rendering. |
scripts/install.ps1 |
Updates Windows setup behavior. |
scripts/install.sh |
Routes setup through /dev/tty. |
skills/basecamp-doctor/SKILL.md |
Updates setup remediation guidance. |
Suppressed comments (2)
internal/commands/wizard.go:94
- The recommended flow never clears an existing global
project_id:PersistValuemerges onlyaccount_idinto the existing file, whileapp.Config.ProjectIDalso remains populated. Re-running default setup after a customized or pre-seeded configuration therefore keeps a default project, contrary to this flow’s advertised defaults. Remove the globalproject_idand clear the in-memory value (with coverage for an existing project setting); also define how any higher-precedence local/repo value should be handled.
if err := resolve.PersistValue("account_id", accountID, "global"); err != nil {
return fmt.Errorf("saving the default account: %w", err)
}
README.md:108
- This list promises the first account unconditionally, but account-bound OAuth uses its bound account instead. Reflect that precedence in the documented recommended defaults.
- First available account, saved globally
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.
Suppressed comments (5)
scripts/install.sh:528
BASECAMP_NONINTERACTIVEis parsed by the CLI as a case-insensitive boolean (1ortrue), but this installer gate only recognizes1. On a TTY,BASECAMP_NONINTERACTIVE=truetherefore launchessetup; setup rejects the session andset -eaborts the installer instead of taking the noninteractive fallback. Match the CLI's truthy values here.
elif [[ "${BASECAMP_NONINTERACTIVE:-}" != "1" ]] && [[ -t 1 ]] && [[ -t 2 ]] && [[ -c /dev/tty ]]; then
internal/commands/wizard.go:46
- This help text does not match
automaticAccount: account-bound OAuth credentials win before account discovery, and only unbound credentials fall back to the first authorized account. Describe that precedence so users are not told the CLI chose an arbitrary first account.
Long: "Authenticate with Basecamp, select the first available account, save it globally, " +
"and connect detected coding agents. Use --customize to choose each setting.",
README.md:26
- The setup implementation prefers the account bound to the OAuth token and only lists accounts as a fallback. Saying it always selects the first available account misdocuments the normal account-bound OAuth path.
On an interactive terminal, the installer opens Basecamp setup: approve OAuth in your browser and the CLI selects the first available account, saves it globally, skips a default project, and connects every detected coding agent. Use `basecamp setup --customize` to choose those settings instead.
README.md:108
- This recommended-default list conflicts with the implemented precedence: the OAuth-bound account is selected first, with the first available account used only as a fallback. Update the bullet so the documented setup result is accurate.
- First available account, saved globally
install.md:5
- The installer does not always choose the first available account:
automaticAccountprefers the account bound to the OAuth token. Document the bound-account-first behavior here, consistent with the PR description and implementation.
On an interactive terminal, the installer runs the recommended setup after download: browser authentication, the first available account saved globally, no default project, and every detected coding agent connected. Use `basecamp setup --customize` when you want to choose those settings. In non-interactive environments, the installer installs the baseline agent skill and, on a best-effort basis, attempts to connect a single detected coding agent.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
internal/commands/wizard.go:94
PersistValuepreserves every existing JSON key, so this only changesaccount_id; an existing globalproject_idsurvives recommended setup. The advertised account-wide starter commands will then remain project-scoped (and the retained project may belong to the previous account), despite this path rejecting--projectand promising no default project. Clearproject_idfrom the global config as part of applying these defaults, and clear the in-memory value too.
if err := resolve.PersistValue("account_id", accountID, "global"); err != nil {
return fmt.Errorf("saving the default account: %w", err)
}
scripts/install.sh:528
- The CLI treats
BASECAMP_NONINTERACTIVE=truecase-insensitively as enabled (internal/config/config.go:468-470), but this installer only recognizes1. On a TTY,BASECAMP_NONINTERACTIVE=true curl … | bashtherefore enters this branch, andbasecamp setupimmediately refuses underset -e, making an otherwise successful install exit as a failure instead of usingpost_install_setup. Match both truthy spellings here.
elif [[ "${BASECAMP_NONINTERACTIVE:-}" != "1" ]] && [[ -t 1 ]] && [[ -t 2 ]] && [[ -c /dev/tty ]]; then
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 20 out of 20 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
scripts/install.ps1:428
- The PowerShell installer does not honor the same
BASECAMP_NONINTERACTIVEescape hatch as the Bash installer and CLI. When an agent runs under an interactive-looking PowerShell host with this variable set, it still invokes human setup; setup then refuses, and the noninteractiveInvoke-PostInstallSetupfallback is skipped. Include the environment gate in this branch.
} elseif ($isInteractive) {
& $installedBinary setup
scripts/install.sh:528
BASECAMP_NONINTERACTIVEis parsed as a truthy boolean by the CLI (true/TRUEas well as1), but this installer gate recognizes only1. On an interactive terminal withBASECAMP_NONINTERACTIVE=true, the installer enters this branch, and the installed CLI immediately refuses setup instead of running the noninteractive fallback. Match the CLI's accepted truthy forms here.
elif [[ "${BASECAMP_NONINTERACTIVE:-}" != "1" ]] && [[ -t 1 ]] && [[ -t 2 ]] && [[ -c /dev/tty ]]; then
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (1)
scripts/install.sh:528
BASECAMP_NONINTERACTIVEis case-insensitively truthy for both1andtruein the CLI, but this gate skips setup only for1. WithBASECAMP_NONINTERACTIVE=truein a TTY, the installer invokessetup; setup rejects the interactive flow andset -eaborts the installer instead of taking the noninteractive fallback. Match the CLI's boolean semantics here.
elif [[ "${BASECAMP_NONINTERACTIVE:-}" != "1" ]] && [[ -t 1 ]] && [[ -t 2 ]] && [[ -c /dev/tty ]]; then
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
internal/commands/wizard.go:361
- When browser OAuth returns a BC5 resource-bound account, an explicit
--accountstill wins here. If the user passes a different ID, setup persists an account that the newly issued token is guaranteed not to access, yet reports success becauseRequireAccountonly checks that the ID is numeric. Reject a mismatch betweenexplicitAccountIDandboundAccountID(and update the precedence test) rather than saving an unusable default.
if explicitAccountID != "" {
return explicitAccountID, "", nil
}
if boundAccountID != "" {
return boundAccountID, "", nil
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (6)
Previously missed (2) — in code that hasn't changed since the last review.
internal/commands/wizard.go:95
- Recommended setup preserves any existing global
project_id:PersistValueloads the current config and only changesaccount_id. Therefore rerunningbasecamp setupcan finish with a project default still active, contradicting the advertised “No default project” behavior. Removeproject_idfrom the global config as part of this transaction (and update the in-memory project state) before reporting completion.
if err := resolve.PersistValue("account_id", accountID, "global"); err != nil {
internal/commands/wizard.go:47
- The help text says setup always selects the first account, but
automaticAccountprefers an OAuth-bound account. Describe that precedence so users with account-bound credentials are not told the wrong default.
Long: "Authenticate with Basecamp, select the first available account, save it globally, " +
"and connect detected coding agents. Use --customize to choose each setting or --minimal for a concise completion message.",
README.md:26
- This onboarding description omits the higher-priority OAuth-bound account selection and can promise the wrong account. Match the actual OAuth-bound-then-first-available behavior.
On an interactive terminal, the installer opens Basecamp setup: approve OAuth in your browser and the CLI selects the first available account, saves it globally, skips a default project, and connects every detected coding agent. Use `basecamp setup --customize` to choose those settings instead.
README.md:108
- This bullet says the first account is always selected, but account-bound OAuth credentials take precedence. Document the actual selection order.
- First available account, saved globally
install.md:5
- The setup description says the first account is always selected, while the implementation first uses an OAuth-bound account. Update the guide to reflect the actual precedence.
On an interactive terminal, the installer runs the recommended setup after download: browser authentication, the first available account saved globally, no default project, and every detected coding agent connected. Use `basecamp setup --customize` when you want to choose those settings. In non-interactive environments, the installer installs the baseline agent skill and, on a best-effort basis, attempts to connect a single detected coding agent.
install.md:33
- Piped input is no longer a reason for the Bash installer to skip setup: the new
/dev/ttyredirection specifically makes the documentedcurl | bashpath interactive when stdout and stderr are terminals. Listing “piped input” here incorrectly tells users that the standard install command skips authentication.
> **Note:** On an interactive terminal, the install scripts run `basecamp setup` with the recommended defaults. In non-interactive environments (CI, piped input, coding agents), they skip authentication and run `basecamp setup agents`, which installs the baseline agent skill and **attempts to connect** a single detected coding agent (best effort). If several agents are detected, or none is, only the baseline skill is installed and the per-agent commands are surfaced. Explicitly skipping first-time setup with `BASECAMP_SKIP_SETUP=1` still runs `setup agents`.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 22 out of 22 changed files in this pull request and generated no new comments.
Suppressed comments (2)
Previously missed (1) — in code that hasn't changed since the last review.
scripts/install.ps1:430
- The PowerShell installer does not account for
BASECAMP_NONINTERACTIVEbefore invoking interactive setup. With that variable enabled in an otherwise interactive console,setupCanRunrejects the invocation, and the installer neither authenticates nor runs the intended noninteractive agent setup fallback. Include the environment escape hatch in this branch, as the Bash installer does.
Info 'Skipping first-time setup because PowerShell is running non-interactively.'
scripts/install.sh:528
BASECAMP_NONINTERACTIVE=true(including case variants) is treated as enabled by the CLI, but this check excludes only the literal1. In an interactive terminal the installer therefore launchessetup, which immediately refuses to run instead of taking the noninteractivesetup agentspath. Match the same true values accepted byconfig.NonInteractiveEnv.
elif [[ "${BASECAMP_NONINTERACTIVE:-}" != "1" ]] && [[ -t 1 ]] && [[ -t 2 ]] && [[ -c /dev/tty ]]; then
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated no new comments.
Suppressed comments (6)
Previously missed (2) — in code that hasn't changed since the last review.
internal/commands/wizard.go:47
- The help text does not match the implemented precedence:
automaticAccountchooses an OAuth-bound account before the first account returned by authorization. This matters for users authorized for multiple accounts; describe the bound-account preference rather than promising the first account.
Long: "Authenticate with Basecamp, select the first available account, save it globally, " +
"and connect detected coding agents. Use --customize to choose each setting or --minimal for a concise completion message.",
skills/basecamp-doctor/SKILL.md:31
- The setup gate also requires terminal stdout through
app.IsInteractive; terminal stdin and stderr alone are insufficient. This remediation guidance should name all three streams so agents do not suggest a setup invocation that will still be refused when stdout is redirected.
refuses with a usage error in machine-output modes or when stdin and stderr are
not both terminals. `basecamp setup --customize` additionally asks the user to
scripts/install.sh:528
BASECAMP_NONINTERACTIVE=trueis a supported truthy value (config.NonInteractiveEnvacceptstruecase-insensitively), but this installer only recognizes1. In an interactive shell it will invokesetup; the CLI then rejects the run as noninteractive, andset -eaborts the installation instead of taking the post-install fallback. Match the CLI's truthy-value handling here.
elif [[ "${BASECAMP_NONINTERACTIVE:-}" != "1" ]] && [[ -t 1 ]] && [[ -t 2 ]] && [[ -c /dev/tty ]]; then
README.md:26
- Recommended setup prefers the OAuth-bound account and only falls back to the first available account. This installation summary currently promises the fallback behavior unconditionally, which can misdescribe the selected account for multi-account users.
On an interactive terminal, the installer opens Basecamp setup: approve OAuth in your browser and the CLI selects the first available account, saves it globally, skips a default project, and connects every detected coding agent. Use `basecamp setup --customize` to choose those settings instead.
README.md:108
- This default is inaccurate for OAuth credentials carrying an account resource: the implementation selects that bound account before considering the first available account. Document both branches so this section agrees with setup behavior.
- First available account, saved globally
install.md:5
- The recommended flow uses the OAuth-bound account when one exists and only otherwise chooses the first available account. Calling it always the first account can set the wrong expectation for multi-account installations.
On an interactive terminal, the installer runs the recommended setup after download: browser authentication, the first available account saved globally, no default project, and every detected coding agent connected. Use `basecamp setup --customize` when you want to choose those settings. In non-interactive environments, the installer installs the baseline agent skill and, on a best-effort basis, attempts to connect a single detected coding agent.
97ba621 to
7094c11
Compare
jeremy
left a comment
There was a problem hiding this comment.
Read this against the gate work in #652/#653/#654, since that is what this rewrites around. Five threads below; none of them blocks, and the ordering is by how much I'd want it fixed before merge.
What checks out:
- The gate survives intact.
setupCanRunis byte-identical towizardCanRunand is called from every entry point —runFastSetup,runWizard, andisFirstRun— so both the installer-invokedsetupand barebasecampare still covered. - The fast path adds no new stdin reads and no
tui.*prompts.spinner.gois lipgloss-only andisWriterTTY-guarded, so it can't reach for/dev/ttythe way huh does;launchers_test.gocorrectly needed no edit. - The Omarchy subprocess is safe to run unattended:
exec.CommandContextwithStdinunset (so/dev/null), a one-minutecontext.WithTimeout, and--yeson bothplugin addandplugin update. basecamp-doctor/SKILL.mdstill forbids baresetupfor agents and now covers--customizetoo.setup.batslost nothing; every existing row still runs.
One judgment call I'm not turning into a thread: runFastSetup overwrites app.Config.AccountID before RequireAccount(), which is fine as a flow, but it means the fast path is the only place that writes an account without the user seeing which one until the Authenticated as line — worth a Using account <name> line if you're in there for thread 4 anyway.
7094c11 to
f8bfd46
Compare
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 24 out of 24 changed files in this pull request and generated 2 comments.
Suppressed comments (1)
install.md:33
- The new Bash path explicitly runs setup for the canonical
curl | bashcase by reconnecting stdin to/dev/tty, so “piped input” no longer means setup is skipped. This note gives users the opposite expectation for the documented install command; distinguish a piped script with a controlling terminal from an environment that actually lacks a usable terminal.
> **Note:** On an interactive terminal, the install scripts run `basecamp setup` with the recommended defaults. In non-interactive environments (CI, piped input, coding agents), they skip authentication and run `basecamp setup agents`, which installs the baseline agent skill and **attempts to connect** a single detected coding agent (best effort). If several agents are detected, or none is, only the baseline skill is installed and the per-agent commands are surfaced. Explicitly skipping first-time setup with `BASECAMP_SKIP_SETUP=1` still runs `setup agents`.
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
internal/commands/wizard.go:679
omarchy plugin updatealso exits successfully when the plugin is already current, but that path is always rendered as “updated,” so rerunning setup reports a change that did not occur. Use a neutral success label such as “ready” unless the command output is inspected to distinguish an actual update.
fmt.Fprintln(w, styles.RenderStatus(true, "Basecamp plugin updated for Omarchy"))
|
Verified the review fixes at b2566e9, reviewer-side — code, not just replies:
Also: this branch is already rebased over the merged #670, and the |
Summary
This aligns Basecamp CLI onboarding with the streamlined setup experience in the HEY CLI: approve browser OAuth once, apply sensible defaults, configure detected integrations, and get started. The existing question-by-question flow remains available for people who want to choose each setting.
basecampandbasecamp setupopen browser OAuth, select the OAuth-bound or first available account, save it globally, and leave the default project unset37signals.basecampplugin when absent, or update the existing installationbasecamp setup --customizebasecamp setup --minimal, intended for Omarchy plugin-driven CLI installs, to end withSETUP COMPLETEinstead of showing starter commandsCLI setup
Setup run if Omarchy plugin installs the CLI (opens in a modal)