# Setup Wizard Design

**Date:** 2026-02-10
**Status:** Draft

## Overview

Interactive setup wizard for Flynn. Two entry points:

1. **Auto-trigger** — `flynn start` detects no config, offers to run the wizard
2. **Explicit** — `flynn setup` command, re-runnable for reconfiguration

Philosophy: minimal-first to get running fast, then menu-driven for advanced config.

## First-Run Flow

When `flynn start` detects no config at `~/.config/flynn/config.yaml`:

```
No configuration found. Would you like to run the setup wizard? [Y/n]
```

Sequential steps:

1. **Welcome** — One-liner: "Let's get Flynn running. This takes about 2 minutes."
2. **Model provider** — Pick from top tier (Anthropic/OpenAI/Ollama), or "More providers" for second tier (Gemini, OpenRouter, xAI, Bedrock, GitHub Models). Prompt for API key (or host for Ollama). Configured as `default` tier. Optionally configure `fast` tier with cheaper model from same provider.
3. **Channel** — Suggest WebChat (zero external setup). Offer Telegram/Discord, or "More channels" for Slack/WhatsApp. Prompt for required fields only.
4. **Write config** — Generate `~/.config/flynn/config.yaml`, print path, show summary.
5. **Offer to start** — "Start Flynn now? [Y/n]"
6. **Offer more** — If declined: "Configure more features? [Y/n]" drops into the menu.

## Menu-Driven Reconfiguration

Entry: `flynn setup` with existing config, or "configure more" after first-run.

```
Flynn Setup — Current Configuration
  Models:    anthropic (default tier)
  Channels:  webchat, telegram
  Memory:    keyword search (no embeddings)
  Automation: none
  Security:  default (no sandbox)
  Gateway:   port 3777, no auth

What would you like to configure?
  1. Model Providers
  2. Channels
  3. Memory
  4. Automation
  5. Security
  6. Gateway
  0. Done — save and exit
>
```

Each section: show current state, offer add/change/remove, return to menu.

On "Done": write config, offer restart (if daemon running) or start.

Config is buffered in memory; only written to disk on "Done". Ctrl+C exits cleanly with no partial writes.

## Provider Setup Flows

### Top Tier

**Anthropic:**
```
API key: sk-ant-••••••
Model [claude-sonnet-4-20250514]:
```

**OpenAI:**
```
API key: sk-••••••
Model [gpt-4.1]:
```

**Ollama:**
```
Host [http://localhost:11434]:
Model [llama3.3]:
```

### Second Tier

OpenRouter, xAI, Gemini, Bedrock, GitHub Models — same pattern (API key + model with defaults). OpenRouter and xAI reuse OpenAI-compatible flow with different base URLs.

### Tier Assignment

First provider → `default` tier. Then:

```
Configure a fast tier for compaction/delegation? [y/N]
```

If yes, suggest cheaper model from same provider (e.g., claude-haiku-4-5-20251001, gpt-4.1-mini, smaller Ollama model).

`complex` and `local` tiers skipped — power-user YAML territory.

## Channel Setup Flows

**WebChat** (recommended, zero setup):
```
Gateway port [3777]:
✓ WebChat enabled — visit http://localhost:3777 after starting
```

**Telegram:**
```
Bot token (from @BotFather): 123456:ABC-••••••
Allowed chat IDs (comma-separated): 12345678, -100987654
```

**Discord:**
```
Bot token: MTIz••••••
Allowed guild IDs (comma-separated, or * for all): 123456789
```

**Slack:**
```
Bot token (xoxb-...): xoxb-••••••
App token (xapp-...): xapp-••••••
Allowed channel IDs (comma-separated, or * for all): C0123ABC
```

**WhatsApp:**
```
⚠ WhatsApp requires QR code authentication on first connect.
  It will appear in the terminal when Flynn starts.
Allowed phone numbers (comma-separated, or * for all): 61412345678
```

Input validation: Telegram tokens match `\d+:.+`, Slack tokens start with `xoxb-`/`xapp-`. Invalid input re-prompts.

After each channel: "Add another channel? [y/N]"

## Menu Section Flows

### Memory
```
Enable vector search for semantic memory? [y/N]
Embedding provider:
  1. OpenAI (recommended)
  2. Gemini
  3. Ollama (local)
  4. Voyage AI
API key [reuse from model provider? Y/n]:
```

Smart default: reuse API key if same provider already configured for models.

### Automation
```
Enable cron scheduler? [y/N]
Enable webhook receiver? [y/N]
Enable Gmail watcher? [y/N]
```

Webhooks prompt for shared secret. Gmail prompts for OAuth credentials path. Cron jobs configured in YAML later.

### Security
```
Enable Docker sandboxing? [y/N]
Enable DM pairing for unknown senders? [y/N]
Tool policy profile [full]: (minimal/messaging/coding/full)
```

### Gateway
```
Port [3777]:
Set auth token? [y/N]
Enable Tailscale Serve? [y/N]
Enable gateway lock (single client)? [y/N]
```

## File Structure

```
src/cli/setup.ts              — Command registration + main orchestrator
src/cli/setup/
  prompts.ts                  — Readline helpers (ask, choose, confirm, password)
  providers.ts                — Model provider setup flows
  channels.ts                 — Channel setup flows
  memory.ts                   — Memory/embeddings setup
  automation.ts               — Cron, webhooks, Gmail setup
  security.ts                 — Tool policy, sandbox, pairing, hooks
  gateway.ts                  — Port, auth, Tailscale, lock
  summary.ts                  — Config summary renderer
```

## Dependencies

- **readline** — Node built-in, same as minimal TUI
- **yaml** — Round-trip YAML parsing to preserve comments when editing existing configs

No `inquirer` or `prompts` packages.

## Prompt Helpers (prompts.ts)

- `ask(question, default?)` — Free text input
- `choose(question, options[])` — Numbered list, return selection
- `confirm(question, default?)` — Y/n boolean
- `password(question)` — Hidden input for API keys

## Config I/O Strategy

- Load existing YAML with `yaml` package (round-trip, preserves comments)
- Buffer all changes in memory
- Write only on "Done" or end of first-run flow
- Preserve `${ENV_VAR}` references in existing configs — don't resolve them
- `mkdir -p ~/.config/flynn` if directory doesn't exist

## Secrets Handling

Secrets (API keys, bot tokens) entered inline and stored as plaintext in `config.yaml`. Users can manually replace with `${ENV_VAR}` references later. The wizard's job is to minimize time-to-working-config.

## Integration with Existing Systems

- **`flynn start`** — Check for config, offer wizard if missing. After wizard, proceed to start.
- **`flynn doctor`** — Validates the config the wizard produces. Wizard does not test API connectivity.
- **`flynn setup`** — New command, re-runnable. With existing config: straight to menu.

Clean separation: wizard produces config, doctor validates it.

## Testing Strategy

- Unit tests for prompt helpers (`ask`, `choose`, `confirm`)
- Unit tests for config generation — given answers, assert correct YAML structure
- Unit tests for input validation (token formats, port ranges)
- Integration test: mock readline, run full first-run flow, assert valid config

## Edge Cases

- Config directory missing → `mkdir -p` before writing
- Ctrl+C during wizard → clean exit, no partial config
- Existing config with `${VAR}` references → preserve them
- Re-running setup → load current values as defaults, don't overwrite untouched fields
- Invalid input → re-prompt with explanation, never crash

## Explicitly Out of Scope

- Config migration between versions
- Undo/rollback
- Config backup (user has git)
- Network validation of API keys (that's `flynn doctor`)
- `complex` and `local` model tier configuration
- Zhipu, Llama.cpp provider setup (niche, manual YAML)
