Skip to content

Streamline first-time setup with recommended defaults - #660

Merged
robzolkos merged 8 commits into
mainfrom
fast-default-setup
Aug 31, 2026
Merged

Streamline first-time setup with recommended defaults#660
robzolkos merged 8 commits into
mainfrom
fast-default-setup

Conversation

@robzolkos

@robzolkos robzolkos commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

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.

  • On an unauthenticated first interactive run, make bare basecamp and basecamp setup open browser OAuth, select the OAuth-bound or first available account, save it globally, and leave the default project unset
  • Automatically configure every detected coding agent, showing progress while installation runs and a durable status when it finishes
  • Detect Omarchy and install and enable the 37signals.basecamp plugin when absent, or update the existing installation
  • Preserve the guided setup flow as basecamp setup --customize
  • Add basecamp setup --minimal, intended for Omarchy plugin-driven CLI installs, to end with SETUP COMPLETE instead of showing starter commands
  • Refresh the default setup presentation with the current Basecamp snowglobe, concise browser-authentication output, integration status, and account-wide starter commands
  • Run interactive setup from the installers, including routing piped Bash installs through the controlling terminal so OAuth can complete
  • Document mise installation and direct mise-managed upgrades back through mise

CLI setup

image

Setup run if Omarchy plugin installs the CLI (opens in a modal)

image

Copilot AI balanced review requested due to automatic review settings August 26, 2026 20:38
@github-actions github-actions Bot added commands CLI command implementations tui Terminal UI tests Tests (unit and e2e) skills Agent skills docs labels Aug 26, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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: PersistValue merges only account_id into the existing file, while app.Config.ProjectID also 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 global project_id and 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.

Comment thread scripts/install.sh Outdated
Comment thread internal/commands/wizard.go Outdated
Comment thread README.md Outdated
Comment thread install.md Outdated
Copilot AI review requested due to automatic review settings August 26, 2026 20:44

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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_NONINTERACTIVE is parsed by the CLI as a case-insensitive boolean (1 or true), but this installer gate only recognizes 1. On a TTY, BASECAMP_NONINTERACTIVE=true therefore launches setup; setup rejects the session and set -e aborts 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: automaticAccount prefers 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.

Copilot AI review requested due to automatic review settings August 26, 2026 20:52

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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

  • PersistValue preserves every existing JSON key, so this only changes account_id; an existing global project_id survives 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 --project and promising no default project. Clear project_id from 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=true case-insensitively as enabled (internal/config/config.go:468-470), but this installer only recognizes 1. On a TTY, BASECAMP_NONINTERACTIVE=true curl … | bash therefore enters this branch, and basecamp setup immediately refuses under set -e, making an otherwise successful install exit as a failure instead of using post_install_setup. Match both truthy spellings here.
  elif [[ "${BASECAMP_NONINTERACTIVE:-}" != "1" ]] && [[ -t 1 ]] && [[ -t 2 ]] && [[ -c /dev/tty ]]; then

Comment thread install.md Outdated
Copilot AI review requested due to automatic review settings August 26, 2026 21:01

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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_NONINTERACTIVE escape 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 noninteractive Invoke-PostInstallSetup fallback is skipped. Include the environment gate in this branch.
    } elseif ($isInteractive) {
      & $installedBinary setup

scripts/install.sh:528

  • BASECAMP_NONINTERACTIVE is parsed as a truthy boolean by the CLI (true/TRUE as well as 1), but this installer gate recognizes only 1. On an interactive terminal with BASECAMP_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

Copilot AI review requested due to automatic review settings August 26, 2026 21:12

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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_NONINTERACTIVE is case-insensitively truthy for both 1 and true in the CLI, but this gate skips setup only for 1. With BASECAMP_NONINTERACTIVE=true in a TTY, the installer invokes setup; setup rejects the interactive flow and set -e aborts 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

Copilot AI review requested due to automatic review settings August 26, 2026 21:36

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 --account still 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 because RequireAccount only checks that the ID is numeric. Reject a mismatch between explicitAccountID and boundAccountID (and update the precedence test) rather than saving an unusable default.
	if explicitAccountID != "" {
		return explicitAccountID, "", nil
	}
	if boundAccountID != "" {
		return boundAccountID, "", nil

Copilot AI review requested due to automatic review settings August 26, 2026 21:48

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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: PersistValue loads the current config and only changes account_id. Therefore rerunning basecamp setup can finish with a project default still active, contradicting the advertised “No default project” behavior. Remove project_id from 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 automaticAccount prefers 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/tty redirection specifically makes the documented curl | bash path 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`.

Copilot AI review requested due to automatic review settings August 26, 2026 21:53

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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_NONINTERACTIVE before invoking interactive setup. With that variable enabled in an otherwise interactive console, setupCanRun rejects 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 literal 1. In an interactive terminal the installer therefore launches setup, which immediately refuses to run instead of taking the noninteractive setup agents path. Match the same true values accepted by config.NonInteractiveEnv.
  elif [[ "${BASECAMP_NONINTERACTIVE:-}" != "1" ]] && [[ -t 1 ]] && [[ -t 2 ]] && [[ -c /dev/tty ]]; then

Copilot AI review requested due to automatic review settings August 26, 2026 22:12
Copilot AI review requested due to automatic review settings August 27, 2026 01:28

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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: automaticAccount chooses 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=true is a supported truthy value (config.NonInteractiveEnv accepts true case-insensitively), but this installer only recognizes 1. In an interactive shell it will invoke setup; the CLI then rejects the run as noninteractive, and set -e aborts 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.

@jeremy jeremy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

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. setupCanRun is byte-identical to wizardCanRun and is called from every entry point — runFastSetup, runWizard, and isFirstRun — so both the installer-invoked setup and bare basecamp are still covered.
  • The fast path adds no new stdin reads and no tui.* prompts. spinner.go is lipgloss-only and isWriterTTY-guarded, so it can't reach for /dev/tty the way huh does; launchers_test.go correctly needed no edit.
  • The Omarchy subprocess is safe to run unattended: exec.CommandContext with Stdin unset (so /dev/null), a one-minute context.WithTimeout, and --yes on both plugin add and plugin update.
  • basecamp-doctor/SKILL.md still forbids bare setup for agents and now covers --customize too.
  • setup.bats lost 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.

Comment thread e2e/setup.bats
Comment thread scripts/install.sh Outdated
Comment thread scripts/install.sh Outdated
Comment thread internal/commands/wizard.go
Comment thread internal/commands/wizard.go Outdated
Copilot AI review requested due to automatic review settings August 31, 2026 13:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 | bash case 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`.

Comment thread skills/basecamp-doctor/SKILL.md Outdated
Comment thread scripts/install.ps1 Outdated
Copilot AI review requested due to automatic review settings August 31, 2026 14:57

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated 2 comments.

Comment thread scripts/install.ps1 Outdated
Comment thread internal/commands/wizard.go Outdated
Copilot AI review requested due to automatic review settings August 31, 2026 15:29

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

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 update also 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"))

Copilot AI review requested due to automatic review settings August 31, 2026 16:05

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 26 out of 26 changed files in this pull request and generated no new comments.

@jeremy

jeremy commented Aug 31, 2026

Copy link
Copy Markdown
Member

Verified the review fixes at b2566e9, reviewer-side — code, not just replies:

  • All five of my threads hold in the diff: the --customize/--minimal bats rows with assert_refused, the { : </dev/tty; } open-probe, run_first_time_setup warning instead of aborting under set -e, the gate now running before --project validation (with the bats row asserting the config set project_id hint and the absence of --customize), and automaticAccount preserving/validating a configured account with explicit-vs-bound mismatch rejection.
  • The two Copilot threads that resolved without visible replies also hold: Test-InteractiveSession covers all three streams, and Test-TruthyEnvironmentValue $env:BASECAMP_NONINTERACTIVE gates the PowerShell setup branch.
  • Gate coverage is still complete after the new commits (runFastSetup, runWizard, isFirstRun — no prompt sinks outside it), remote checks are fully green, and local bin/ci exits 0 at head.

Also: this branch is already rebased over the merged #670, and the setupCanRun doc comment correctly carries the new --styled/--md override semantics. Nothing left from my review — LGTM.

@robzolkos
robzolkos merged commit 76d3f2e into main Aug 31, 2026
35 checks passed
@robzolkos
robzolkos deleted the fast-default-setup branch August 31, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

commands CLI command implementations docs skills Agent skills tests Tests (unit and e2e) tui Terminal UI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants