code-yeongyu / code-yeongyu/senpi
feat(setup): first-run onboarding/setup wizard (provider sign-in → default model → theme), modeled on oh-my-pi's versioned scenes
- Dominant language
- TypeScript
- Stars
- 429
- Forks
- 98
- Avg merge
- 5h 3m
- Merged PRs (30d)
- 526
Description
## Problem
senpi/omo has **no first-run onboarding flow**. A brand-new user launches into a welcome header + an empty prompt, and everything after that is manual and error-driven:
- Auth is `/login`, discovered only via the "No models available. Use /login ..." error text (`packages/coding-agent/src/core/auth-guidance.ts:6-20`).
- Model selection is `/model`, also discovered via error guidance (`auth-guidance.ts:19-20`).
- senpi *has* a first-time setup dialog (theme + analytics only), but `shouldRunFirstTimeSetup()` hard-gates it to the official Pi distribution (`@earendil-works/pi-coding-agent`, app name `pi`, config dir `.pi`) **plus** `PI_EXPERIMENTAL=1` — so senpi/omo distributions never see it (`packages/coding-agent/src/cli/startup-ui.ts:113-128`, dialog at `src/modes/interactive/components/first-time-setup.ts:31-117`).
- The omo layer's existing "onboarding" is a **conversational tour skill**, not setup: `omo/packages/omo-senpi/src/components/onboarding/component.ts:23-65` injects a hidden bootstrap message that makes the agent read `skills/onboarding/SKILL.md` (6-lane tour). It configures no provider, no model, no auth — and it has a **chicken-and-egg problem: the tour itself needs a working provider+model to run**, which is exactly what a first-run user doesn't have.
- `omo setup` (omo-native launcher) can detect/import sibling harness credentials (`omo/packages/omo-native/bin/lib/setup-detect.js:99-137`, `setup-import.js:202-218`) but only when the user explicitly types it; first launch just prints a one-line stderr hint (`launcher.js:131-132,155-156`).
## Reference: how oh-my-pi does it (can1357/oh-my-pi @ v18.0.0, 96f4280976)
oh-my-pi ships a versioned, fullscreen TUI setup wizard worth copying structurally:
- **Trigger**: cold launch compares persisted `setupVersion` against `CURRENT_SETUP_VERSION` (=2) and lazy-loads the wizard when stale (`src/main.ts:525-531`, `src/modes/setup-version.ts:22`). Explicit re-run via `omp setup` forces it (`src/commands/setup.ts:30,57`; non-TTY exits 1).
- **Scenes, in order** (`src/modes/setup-wizard/index.ts:17-23`): providers (OAuth sign-in + web-search tabs) → default model (ModelBrowser) → glyph mode (nerd/unicode/ascii) → composer shape (`minVersion: 2`) → theme (live preview). Each scene has a `minVersion`, so upgrades replay **only new scenes** — the wizard doubles as upgrade onboarding.
- **UX**: fullscreen overlay with animated splash (2.6s) → 420ms cross-dissolve → scenes → "Setup saved" outro (`wizard-overlay.ts`); Esc skips a scene; arrow/enter/mouse supported.
- **Gates** (`index.ts:39-63`): never runs on non-TTY, on session resume, with `OMP_SKIP_SETUP=1`, or with `startup.setupWizard: false`.
- **Persistence**: global `config.yml` + project `.omp/config.yml` for model roles; OAuth tokens in the agent DB; `markSetupWizardComplete()` writes `setupVersion` (`index.ts:65-70`).
- Notable: no telemetry-consent scene; no non-interactive wizard.
## Why this issue lives in senpi (not omo)
The wizard needs engine-level surfaces: pre-interactive TUI dialogs, the `/login` OAuth/API-key flows, the model selector, theme + `shareAnalytics` settings, and the existing (Pi-gated) `first-time-setup.ts` scaffolding. All of that is senpi runtime code. The omo plugin layer only gets extension events (`session_start` etc.) and can't render a wizard before the first prompt. omo-side work (credential import scene, tour chaining) hangs off an extension hook proposed below and can be a follow-up in oh-my-openagent.
## Proposal
### A. Gating & trigger
- Add `setupVersion` to `settings.json` schema; introduce `CURRENT_SETUP_VERSION = 1`.
- On interactive cold launch, run the wizard when: TTY && not resuming && not `--print`/`-p` && `setupVersion < CURRENT_SETUP_VERSION` && `SENPI_SKIP_SETUP` unset && `startup.setupWizard !== false`.
- Generalize `shouldRunFirstTimeSetup()` (`startup-ui.ts:113-128`): drop the official-Pi-distribution hard gate and the `PI_EXPERIMENTAL` requirement; make it branding-aware so pi/senpi/omo distributions all qualify (keep custom-agent-dir exclusion).
- Explicit re-run: `senpi --setup` (and/or a `/setup` slash command). Non-TTY invocation exits 1 with a clear message, mirroring `omp setup`.
### B. Scenes (v1)
1. **Provider sign-in** — reuse the `/login` flows (OAuth + API-key paste). Show already-detected credentials; Esc to skip. `auth-guidance.ts` messages should then point at the wizard too.
2. **Default model** — reuse the `/model` selector; persist the default role.
3. **Theme** — lift the existing dark/light step from `first-time-setup.ts`.
4. **Analytics opt-in** — keep the existing `shareAnalytics` question.
Each scene carries a `minVersion` (oh-my-pi pattern) so future scenes replay for upgraders without re-running everything.
### C. Extension hook (the omo integration point)
- Let extensions contribute wizard scenes and/or a **post-setup hook**.
- omo-senpi then: (a) contributes a "found credentials from Claude Code/opencode/... — import?" scene reusing omo-native `setup-detect.js`/`setup-import.js` logic, and (b) chains the existing conversational onboarding tour **after** wizard completion, so `omo-onboarding:bootstrap` fires only once a working provider+model exists. Ordering contract: wizard → auth+model guaranteed → tour.
### D. Persistence
- `settings.json`: `setupVersion`, `theme`, `shareAnalytics`, default model role. Credentials stay in the existing auth store. No new config files.
## Acceptance criteria
- [ ] Fresh install (no `settings.json`, no auth), interactive launch: wizard runs **before** the first prompt; completing it leaves a working provider + default model.
- [ ] Wizard never runs: headless (`-p`/`--print`), non-TTY, session resume, `SENPI_SKIP_SETUP=1`, or `startup.setupWizard: false`.
- [ ] Esc skips an individual scene; skipping everything still lands in the normal TUI with `/login` guidance intact.
- [ ] Bumping `CURRENT_SETUP_VERSION` replays only scenes with a higher `minVersion` for existing users.
- [ ] `senpi --setup` forces a full re-run; non-TTY exits 1.
- [ ] omo/senpi distributions get the wizard (Pi-distribution gate removed); custom agent dirs still excluded.
- [ ] Extensions can register a post-setup hook; on a true first run under omo, the onboarding tour fires **after** the wizard.
- [ ] Tests mirror oh-my-pi's `setup-wizard.test.ts` coverage: scene selection/version gating, persistence, skip paths; `first-time-setup` component tests updated.
## Out of scope / follow-ups
- oh-my-openagent side: the credential-import scene + tour-chaining component work (file there once the senpi hook shape lands).
- Glyph/composer-style appearance scenes beyond theme — add later via `minVersion` bumps.
- Non-interactive/headless setup wizard (oh-my-pi doesn't have one either).
Contributor guide
Assessment
This issue has not been assessed yet.