Skip to content

Conversation

@sogoiii
Copy link
Contributor

@sogoiii sogoiii commented Jan 4, 2026

🎯 Purpose

Provide a structured, guided onboarding experience for first-time llxprt users. The 5-step wizard helps users set up their AI provider, choose their preferred model, configure authentication, and save their setup as a reusable profile that auto-loads on every startup.

📋 Summary

  • ✨ Interactive 5-step onboarding wizard for configuration
  • 🔐 Support for OAuth and API key authentication
  • 📊 Model selection after provider choice
  • 💾 Profile auto-save with named profiles
  • 🛡️ Defensive null guards and comprehensive error handling
  • 📱 Full keyboard navigation with escape recovery

🔄 Onboarding Flow

Step 1 of 5: Choose Your AI Provider

Users select from available AI providers (Anthropic, OpenAI, Google Gemini, etc.) with human-friendly display names. Option to skip and configure manually later.

UI: Radio button selection with provider names
Actions: Next → Step 2, Skip → Skip Exit


Step 2 of 5: Choose Your Model

After selecting a provider, available models are automatically loaded and displayed. Users pick their preferred model for that provider.

UI: Loading spinner during fetch, error state with recovery option, radio button selection for models

Features:

  • Automatic model loading when provider changes
  • Error handling if models fail to load
  • Escape key to go back to provider selection
  • "Loading models..." feedback during fetch

Actions: Next → Step 3, Back → Step 1, Escape → Step 1


Step 3 of 5: Choose Authentication Method

Users select between OAuth (recommended) or API key authentication. Provider-specific API key URLs are shown for guidance.

UI: Radio buttons for OAuth vs API Key with descriptions

Features:

  • OAuth marked as "Recommended - secure & easy"
  • API key URLs provided for each provider
  • Back button to modify provider/model

Actions: Select → Step 4 (Authenticating), Back → Step 2


Step 4 of 5: Authenticate

Two paths based on auth method choice:

OAuth Path:

  • Browser automatically opens for OAuth flow
  • Loading spinner with "Opening browser for OAuth authentication..."
  • Waits for auth completion

API Key Path:

  • Text input field for API key with bullet masking
  • Paste support for clipboard-pasted keys
  • Validates key on submission
  • Shows spinner while validating

UI: Spinner + status message during auth

Features:

  • Secure key masking (bullet characters)
  • Paste support for clipboard keys
  • Error messages if auth fails
  • Escape to cancel and go back

Actions: Complete → Step 5, Error → Step 3, Escape → Step 3


Step 5 of 5: Save Your Profile

User names their configuration profile. The profile is saved, set as default, and immediately loaded in the current session.

UI: Text input for profile name

Features:

  • Profile name is mandatory (user must provide it)
  • Checks for duplicate profile names
  • Shows success message after save
  • Displays configured provider, model, and auth method
  • Escape key not available (must complete onboarding)

Actions: Save → Completion (shows next steps), Done → Exit onboarding


🛡️ Error Handling & Recovery

Model Loading Failures

  • Displays error message: "Failed to load models"
  • User can press Escape to go back to provider selection
  • Can retry with different provider

Authentication Failures

  • Shows error message for failed auth
  • Returns to auth method selection (Step 3)
  • User can retry or change method

Profile Save Failures

  • Duplicate name detection
  • Error message shown
  • User can retry with different name

State Guard Failures

  • Defensive null checks prevent crashes
  • Returns null if required state missing
  • Gracefully handles invalid transitions

✅ Validation

  • ✅ TypeScript: No type errors
  • ✅ ESLint: No warnings
  • ✅ Prettier: Formatted
  • ✅ Build: All packages successful
  • ✅ Bundle: Generated successfully

🔐 Security

  • API keys masked with bullet characters in UI
  • No sensitive data logged
  • OAuth PKCE-ready
  • Secure token handling via runtime

🧪 Testing

Reset onboarding to trigger again:

rm ~/.llxprt/welcomeConfig.json

Then restart llxprt to see the welcome wizard.

Images

SCR-20260103-ugsf SCR-20260103-uguh SCR-20260103-uhey SCR-20260103-ujge SCR-20260103-uhjk SCR-20260103-uhah

Summary by CodeRabbit

Release Notes

  • New Features
    • Interactive welcome onboarding flow for first-time users, guiding through provider selection, authentication method choice (OAuth or API key), and optional profile setup.
    • Option to skip onboarding and configure manually later using CLI commands.
    • Completion state persists to prevent the onboarding flow from reappearing on subsequent runs.

✏️ Tip: You can customize this high-level summary in your review settings.

Captures design decisions from brainstorming session:
- Flow triggers after folder trust dialog
- Dynamic provider list from ProviderManager
- Delegates to existing OAuth/auth infrastructure
- Optional profile save at completion
- First-run flag stored in config file
Detailed 13-task plan covering:
- Welcome config service for first-run detection
- useWelcomeOnboarding hook for state management
- 6 step components (Welcome, Provider, Auth, etc.)
- Integration into AppContainer and DefaultAppLayout
Add first-run welcome wizard that guides new users through:
- Provider selection from available providers
- Authentication method choice (OAuth or API key)
- OAuth authentication flow
- Optional profile save for quick restoration

Components created:
- WelcomeDialog: main orchestrator with step routing
- WelcomeStep: initial welcome with setup/skip choice
- ProviderSelectStep: provider picker with display names
- AuthMethodStep: OAuth vs API key selection
- AuthenticationStep: OAuth flow and API key input
- CompletionStep: success message with optional profile save
- SkipExitStep: skip confirmation message

Integration:
- Added welcomeConfig.ts for persistence in ~/.llxprt/
- Added useWelcomeOnboarding hook for state management
- Integrated into AppContainer after folder trust
- Added to DialogManager for rendering
Add ModelSelectStep component allowing users to select their preferred model
after choosing a provider. Updated onboarding flow to 5 steps:
1. Choose Provider
2. Choose Model (NEW)
3. Choose Auth Method
4. Authenticate
5. Save Profile

Models are fetched when provider is selected and persisted via setActiveModel.
Add defensive null guards in WelcomeDialog to prevent runtime crashes from
invalid state transitions. Introduce ModelsLoadStatus tracking for detailed
model loading feedback: loading, success, error, and empty states. Show
contextual error messages and allow users to escape from error conditions.
@sogoiii sogoiii requested a review from acoliver as a code owner January 4, 2026 07:40
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 4, 2026

Walkthrough

Introduces a complete welcome onboarding flow for first-run CLI users with persistent config storage, multi-step UI components (welcome, provider selection, authentication, completion), a state management hook orchestrating the flow, and integration into the app container and dialog manager.

Changes

Cohort / File(s) Summary
Welcome Config Persistence
packages/cli/src/config/welcomeConfig.ts
New module managing welcome state persistence via welcomeConfig.json. Exports constants, config interface, and functions for loading/saving config, marking completion, and resetting for tests. Supports environment override and in-memory caching.
Onboarding State & Actions
packages/cli/src/ui/hooks/useWelcomeOnboarding.ts
New hook orchestrating multi-step onboarding flow (welcome, provider, model, auth method, authentication, completion, skipped). Manages state transitions, provider/model discovery, profile saving with validation, and completion persistence.
UI State & Context Extensions
packages/cli/src/ui/contexts/UIStateContext.tsx, packages/cli/src/ui/contexts/UIActionsContext.tsx
Expanded UIState interface with isWelcomeDialogOpen, welcomeState, welcomeAvailableProviders/Models fields. Added welcomeActions (startSetup, selectProvider, selectModel, etc.) and triggerWelcomeAuth to UIActions.
Welcome UI Components
packages/cli/src/ui/components/WelcomeOnboarding/WelcomeStep.tsx, ProviderSelectStep.tsx, AuthMethodStep.tsx, AuthenticationStep.tsx, CompletionStep.tsx, SkipExitStep.tsx, WelcomeDialog.tsx, index.ts
Seven new Ink-based UI step components plus main WelcomeDialog orchestrator. Handles provider selection, auth method choice (OAuth/API key), masked input for keys, profile saving, and skip/exit flows. Barrel export consolidates the suite.
App Integration
packages/cli/src/ui/AppContainer.tsx, packages/cli/src/ui/components/DialogManager.tsx, packages/cli/src/ui/layouts/DefaultAppLayout.tsx
AppContainer imports and invokes useWelcomeOnboarding hook, wires state/actions into UIState/UIActions. DialogManager renders WelcomeDialog when isWelcomeDialogOpen is true. DefaultAppLayout gates dialog visibility on welcome dialog state.
Documentation & Planning
docs/plans/2026-01-03-welcome-onboarding.md, project-plans/welcome-onboarding/PRD.md, project-plans/welcome-onboarding/DESIGN.md
High-level implementation plan, comprehensive PRD with requirements/wireframes, and detailed design document covering flow stages, component structure, state management, integration, and edge cases.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant WelcomeDialog
    participant useWelcomeOnboarding as Hook: useWelcomeOnboarding
    participant AuthService as Auth Service
    participant Config as Welcome Config

    User->>WelcomeDialog: App starts (first-run)
    WelcomeDialog->>useWelcomeOnboarding: Initialize hook
    useWelcomeOnboarding->>Config: Load welcome state
    Config-->>useWelcomeOnboarding: welcomeCompleted = false
    useWelcomeOnboarding-->>WelcomeDialog: Show Welcome step
    
    User->>WelcomeDialog: Select "Setup"
    WelcomeDialog->>useWelcomeOnboarding: startSetup()
    useWelcomeOnboarding->>useWelcomeOnboarding: Load available providers
    useWelcomeOnboarding-->>WelcomeDialog: Show Provider Select step
    
    User->>WelcomeDialog: Select provider
    WelcomeDialog->>useWelcomeOnboarding: selectProvider(providerId)
    useWelcomeOnboarding->>useWelcomeOnboarding: Load models for provider
    useWelcomeOnboarding-->>WelcomeDialog: Show Model Select step
    
    User->>WelcomeDialog: Select model
    WelcomeDialog->>useWelcomeOnboarding: selectModel(modelId)
    useWelcomeOnboarding-->>WelcomeDialog: Show Auth Method step
    
    User->>WelcomeDialog: Select auth method (OAuth or API Key)
    WelcomeDialog->>useWelcomeOnboarding: selectAuthMethod(method)
    useWelcomeOnboarding-->>WelcomeDialog: Show Authentication step
    
    alt OAuth Flow
        User->>WelcomeDialog: Initiate OAuth
        WelcomeDialog->>AuthService: triggerAuth(provider, 'oauth')
        AuthService-->>AuthService: Open browser for OAuth
        AuthService-->>WelcomeDialog: Auth complete
        WelcomeDialog->>useWelcomeOnboarding: onAuthComplete()
    else API Key Flow
        User->>WelcomeDialog: Enter API Key
        WelcomeDialog->>AuthService: triggerAuth(provider, 'api_key', apiKey)
        AuthService-->>WelcomeDialog: Validate and set key
        WelcomeDialog->>useWelcomeOnboarding: onAuthComplete()
    end
    
    useWelcomeOnboarding-->>WelcomeDialog: Show Completion step
    User->>WelcomeDialog: Save profile (optional)
    WelcomeDialog->>useWelcomeOnboarding: saveProfile(profileName)
    useWelcomeOnboarding->>Config: Save profile config
    Config-->>useWelcomeOnboarding: Success
    
    User->>WelcomeDialog: Dismiss
    WelcomeDialog->>useWelcomeOnboarding: dismiss()
    useWelcomeOnboarding->>Config: Mark welcome completed
    Config-->>useWelcomeOnboarding: Persisted
    useWelcomeOnboarding-->>WelcomeDialog: Close dialog
    WelcomeDialog-->>User: App proceeds to normal flow
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

The review spans multiple integration layers: a new persistence module with file I/O, a complex state-management hook with provider/model discovery and auth orchestration, six specialized UI components and a main dialog orchestrator, and integration across three key app files. While individual components follow patterns, the heterogeneous mix of auth logic, keyboard handling, form validation, state transitions, and file operations requires careful scrutiny across boundaries.

Suggested reviewers

  • acoliver

Poem

🐰 A welcome so warm, a hop through the flow,
With providers and keys in a neat little show!
From setup to auth, then a profile so grand,
We've built the onboarding that helps users land!
Hopping through steps with delightful finesse, 🎉

Pre-merge checks and finishing touches

✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title accurately describes the main feature being introduced: a welcome onboarding flow for first-time users. It is specific, concise, and directly relates to the substantial changes across multiple components and configuration files.
Description check ✅ Passed The PR description exceeds template requirements with comprehensive sections covering purpose, onboarding flow (5 detailed steps), error handling, validation results, security notes, and testing instructions. All required template sections are substantially addressed with examples and implementation details.
✨ Finishing touches
  • 📝 Generate docstrings

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@github-actions
Copy link
Contributor

github-actions bot commented Jan 4, 2026

⚠️ LLxprt PR Review blocked

  • No linked issues were detected in this PR's description.
  • Please reference an existing issue with text such as Fixes #123 so the automated review knows what problem to evaluate.
  • The PR has been returned to draft to prevent accidental merges without an issue.

Copy link
Contributor

@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 (5)
packages/cli/src/ui/components/WelcomeOnboarding/AuthenticationStep.tsx (1)

92-106: OAuth effect may re-trigger on dependency changes causing duplicate auth attempts.

The authStarted flag guards against re-triggering, but it's not included as a dependency in the effect (it is, actually). However, if onComplete or onError callbacks change identity between renders (e.g., if not memoized in parent), this could cause unnecessary re-renders of the effect, though authStarted should still prevent duplicate calls.

The current implementation is correct with the authStarted guard. However, consider moving the Promise handling to avoid the .then()/.catch() pattern which doesn't handle the case where the component unmounts mid-auth:

🔎 Suggested improvement with cleanup
   // Start OAuth flow automatically
   useEffect(() => {
+    let cancelled = false;
     if (method === 'oauth' && !authStarted) {
       setAuthStarted(true);
       setIsAuthenticating(true);
       triggerAuth(provider, 'oauth')
         .then(() => {
+          if (cancelled) return;
           onComplete();
         })
         .catch((error: unknown) => {
+          if (cancelled) return;
           setIsAuthenticating(false);
           onError(error instanceof Error ? error.message : String(error));
         });
     }
+    return () => {
+      cancelled = true;
+    };
   }, [method, provider, triggerAuth, onComplete, onError, authStarted]);
packages/cli/src/ui/hooks/useWelcomeOnboarding.ts (3)

78-78: Unused settings parameter.

The settings parameter is destructured as _settings but never used in the hook. Either remove it from UseWelcomeOnboardingOptions interface and the hook signature, or document why it's needed for future use.

🔎 If settings is not needed, remove it
 export interface UseWelcomeOnboardingOptions {
-  settings: LoadedSettings;
   isFolderTrustComplete: boolean;
 }

 export const useWelcomeOnboarding = (
   options: UseWelcomeOnboardingOptions,
 ): UseWelcomeOnboardingReturn => {
-  const { settings: _settings, isFolderTrustComplete } = options;
+  const { isFolderTrustComplete } = options;

108-140: Model loading effect lacks cancellation for unmount/stale requests.

If the provider changes rapidly or the component unmounts during the async listAvailableModels call, stale results could be applied to state. Consider adding a cleanup function:

🔎 Add cancellation to prevent stale state updates
   // Load available models when provider is selected
   useEffect(() => {
+    let cancelled = false;
     const loadModels = async () => {
       if (!state.selectedProvider) {
         setAvailableModels([]);
         setState((prev) => ({ ...prev, modelsLoadStatus: 'idle' }));
         return;
       }

       setState((prev) => ({ ...prev, modelsLoadStatus: 'loading' }));

       try {
         const models = await runtime.listAvailableModels(
           state.selectedProvider,
         );
+        if (cancelled) return;
         const modelInfos: ModelInfo[] = models.map((m) => ({
           id: m.name,
           name: m.name,
         }));
         setAvailableModels(modelInfos);
         setState((prev) => ({ ...prev, modelsLoadStatus: 'success' }));
         debug.log(
           `Loaded ${modelInfos.length} models for ${state.selectedProvider}`,
         );
       } catch (error) {
+        if (cancelled) return;
         debug.log(`Failed to load models: ${error}`);
         setAvailableModels([]);
         setState((prev) => ({ ...prev, modelsLoadStatus: 'error' }));
       }
     };

     loadModels();
+    return () => {
+      cancelled = true;
+    };
   }, [runtime, state.selectedProvider]);

171-183: onAuthComplete logs stale state.selectedProvider due to closure.

The debug.log on line 173-175 captures state.selectedProvider from the closure, but this value might be stale if the callback is invoked after state has changed. Since this is only for logging, it's a minor issue, but be aware the logged value may not reflect the actual current state.

docs/plans/2026-01-03-welcome-onboarding.md (1)

141-148: Plan document shows outdated step count (3 steps vs 5 in implementation).

The plan document shows WelcomeStep type without 'model' step, and UI text shows "Step X of 3". The actual implementation (useWelcomeOnboarding.ts) has 5 steps including 'model' and UI shows "Step 4 of 5". This is expected since the model selection was added after the initial plan, but consider updating the plan document to reflect the final implementation for future reference.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c260738 and da955ae.

📒 Files selected for processing (19)
  • docs/plans/2026-01-03-welcome-onboarding.md
  • packages/cli/src/config/welcomeConfig.ts
  • packages/cli/src/ui/AppContainer.tsx
  • packages/cli/src/ui/components/DialogManager.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/AuthMethodStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/AuthenticationStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/CompletionStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/ModelSelectStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/ProviderSelectStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/SkipExitStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/WelcomeDialog.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/WelcomeStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/index.ts
  • packages/cli/src/ui/contexts/UIActionsContext.tsx
  • packages/cli/src/ui/contexts/UIStateContext.tsx
  • packages/cli/src/ui/hooks/useWelcomeOnboarding.ts
  • packages/cli/src/ui/layouts/DefaultAppLayout.tsx
  • project-plans/welcome-onboarding/DESIGN.md
  • project-plans/welcome-onboarding/PRD.md
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Don't use any - Always specify proper types. Use unknown if the type is truly unknown and add proper type guards.
Do not use console.log or console.debug - Use the sophisticated logging system instead. Log files are written to ~/.llxprt/debug/
Fix all linting errors, including warnings about any types

Files:

  • packages/cli/src/ui/layouts/DefaultAppLayout.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/AuthMethodStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/ModelSelectStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/ProviderSelectStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/WelcomeDialog.tsx
  • packages/cli/src/ui/contexts/UIActionsContext.tsx
  • packages/cli/src/ui/contexts/UIStateContext.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/CompletionStep.tsx
  • packages/cli/src/ui/hooks/useWelcomeOnboarding.ts
  • packages/cli/src/ui/components/WelcomeOnboarding/WelcomeStep.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/AuthenticationStep.tsx
  • packages/cli/src/ui/AppContainer.tsx
  • packages/cli/src/ui/components/DialogManager.tsx
  • packages/cli/src/ui/components/WelcomeOnboarding/SkipExitStep.tsx
  • packages/cli/src/config/welcomeConfig.ts
  • packages/cli/src/ui/components/WelcomeOnboarding/index.ts
**/*.md

📄 CodeRabbit inference engine (CLAUDE.md)

Documentation-only changes (*.md files, docs/) do NOT require build/test/lint cycle

Files:

  • project-plans/welcome-onboarding/PRD.md
  • project-plans/welcome-onboarding/DESIGN.md
  • docs/plans/2026-01-03-welcome-onboarding.md
🧠 Learnings (1)
📚 Learning: 2025-12-12T23:31:19.937Z
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 779
File: project-plans/20251212issue488/PLAN.md:1-7
Timestamp: 2025-12-12T23:31:19.937Z
Learning: Do not validate or review plan documents in the vybestack/llxprt-code repository after implementation. Specifically, skip validation for files under project-plans (e.g., project-plans/**/PLAN.md) once the feature is implemented; these documents are not checked in reviews post-implementation.

Applied to files:

  • project-plans/welcome-onboarding/PRD.md
  • project-plans/welcome-onboarding/DESIGN.md
🧬 Code graph analysis (7)
packages/cli/src/ui/components/WelcomeOnboarding/ModelSelectStep.tsx (4)
packages/cli/src/ui/hooks/useWelcomeOnboarding.ts (2)
  • ModelInfo (57-60)
  • ModelsLoadStatus (27-27)
packages/cli/src/ui/components/shared/RadioButtonSelect.tsx (2)
  • RadioSelectItem (21-25)
  • RadioButtonSelect (56-100)
packages/cli/src/ui/hooks/useKeypress.ts (1)
  • useKeypress (23-41)
packages/cli/src/ui/colors.ts (1)
  • Colors (12-78)
packages/cli/src/ui/components/WelcomeOnboarding/WelcomeDialog.tsx (13)
packages/cli/src/ui/hooks/useWelcomeOnboarding.ts (4)
  • WelcomeState (29-37)
  • WelcomeActions (39-50)
  • ModelInfo (57-60)
  • WelcomeStep (18-25)
packages/cli/src/ui/components/WelcomeOnboarding/index.ts (7)
  • WelcomeDialog (7-7)
  • WelcomeStep (8-8)
  • ProviderSelectStep (9-9)
  • AuthMethodStep (10-10)
  • AuthenticationStep (11-11)
  • CompletionStep (12-12)
  • SkipExitStep (13-13)
packages/cli/src/ui/hooks/useKeypress.ts (1)
  • useKeypress (23-41)
scripts/oldui-tmux-harness.js (1)
  • key (274-274)
packages/cli/src/ui/components/WelcomeOnboarding/WelcomeStep.tsx (2)
  • WelcomeChoice (15-15)
  • WelcomeStep (22-70)
packages/cli/src/ui/components/WelcomeOnboarding/ProviderSelectStep.tsx (1)
  • ProviderSelectStep (32-90)
packages/cli/src/ui/components/WelcomeOnboarding/ModelSelectStep.tsx (1)
  • ModelSelectStep (29-129)
packages/cli/src/ui/components/WelcomeOnboarding/AuthMethodStep.tsx (1)
  • AuthMethodStep (36-120)
packages/cli/src/ui/components/WelcomeOnboarding/AuthenticationStep.tsx (1)
  • AuthenticationStep (27-174)
packages/cli/src/ui/components/WelcomeOnboarding/CompletionStep.tsx (1)
  • CompletionStep (21-162)
packages/cli/src/ui/components/WelcomeOnboarding/SkipExitStep.tsx (1)
  • SkipExitStep (17-57)
packages/cli/test-utils/ink-stub.ts (1)
  • Box (22-22)
packages/cli/src/ui/colors.ts (1)
  • Colors (12-78)
packages/cli/src/ui/contexts/UIStateContext.tsx (1)
packages/cli/src/ui/hooks/useWelcomeOnboarding.ts (2)
  • WelcomeState (29-37)
  • ModelInfo (57-60)
packages/cli/src/ui/components/WelcomeOnboarding/CompletionStep.tsx (2)
packages/cli/src/ui/hooks/useKeypress.ts (1)
  • useKeypress (23-41)
packages/cli/src/ui/colors.ts (1)
  • Colors (12-78)
packages/cli/src/ui/hooks/useWelcomeOnboarding.ts (3)
packages/cli/src/config/settings.ts (1)
  • LoadedSettings (339-467)
packages/cli/src/ui/contexts/RuntimeContext.tsx (1)
  • useRuntimeApi (187-189)
packages/cli/src/config/welcomeConfig.ts (2)
  • isWelcomeCompleted (80-82)
  • markWelcomeCompleted (72-78)
packages/cli/src/ui/AppContainer.tsx (1)
packages/cli/src/ui/hooks/useWelcomeOnboarding.ts (1)
  • useWelcomeOnboarding (75-348)
packages/cli/src/ui/components/WelcomeOnboarding/SkipExitStep.tsx (3)
packages/cli/src/ui/components/WelcomeOnboarding/index.ts (1)
  • SkipExitStep (13-13)
packages/cli/src/ui/hooks/useKeypress.ts (1)
  • useKeypress (23-41)
packages/cli/src/ui/colors.ts (1)
  • Colors (12-78)
🪛 LanguageTool
project-plans/welcome-onboarding/PRD.md

[grammar] ~541-~541: Ensure spelling is correct
Context: ...-017]** Welcome flow shall start within 500ms of app launch. - [REQ-017.1] Firs...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🪛 markdownlint-cli2 (0.18.1)
project-plans/welcome-onboarding/PRD.md

18-18: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


27-27: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


93-93: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


94-94: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


95-95: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


98-98: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


99-99: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


100-100: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


103-103: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


104-104: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


105-105: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


106-106: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


109-109: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


110-110: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


111-111: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


112-112: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


115-115: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


116-116: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


117-117: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


118-118: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


121-121: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


122-122: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


123-123: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


124-124: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


127-127: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


128-128: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


129-129: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


132-132: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


133-133: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


134-134: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


137-137: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


138-138: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


139-139: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


140-140: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


141-141: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


142-142: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


147-147: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


148-148: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


149-149: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


150-150: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


151-151: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


154-154: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


155-155: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


156-156: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


157-157: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


160-160: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


161-161: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


162-162: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


165-165: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


166-166: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


167-167: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


172-172: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


173-173: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


174-174: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


177-177: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


178-178: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


179-179: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


182-182: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


183-183: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


184-184: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


189-189: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


210-210: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


229-229: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


250-250: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


265-265: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


280-280: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


297-297: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


447-447: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


542-542: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


543-543: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


546-546: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


547-547: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


552-552: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


553-553: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


554-554: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


557-557: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


558-558: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


559-559: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


564-564: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


565-565: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


566-566: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


569-569: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


570-570: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


571-571: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


578-578: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


579-579: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


580-580: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


583-583: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


584-584: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


585-585: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


588-588: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


589-589: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


590-590: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


593-593: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


594-594: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


595-595: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


598-598: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


599-599: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


600-600: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


605-605: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


606-606: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


607-607: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


610-610: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


611-611: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


612-612: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


615-615: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


616-616: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


617-617: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


652-652: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


653-653: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


654-654: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


655-655: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


658-658: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


659-659: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


660-660: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


661-661: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


664-664: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


665-665: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


666-666: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


667-667: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


670-670: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


671-671: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


672-672: Unordered list indentation
Expected: 0; Actual: 2

(MD007, ul-indent)


696-696: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


701-701: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


706-706: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


711-711: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


716-716: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


807-807: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


813-813: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


819-819: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


825-825: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


852-852: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


863-863: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

project-plans/welcome-onboarding/DESIGN.md

19-19: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


38-38: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

docs/plans/2026-01-03-welcome-onboarding.md

19-19: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


106-106: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


121-121: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


318-318: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


333-333: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


408-408: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


423-423: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


515-515: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


530-530: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


653-653: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


668-668: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


824-824: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


839-839: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


990-990: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1005-1005: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1067-1067: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1083-1083: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1211-1211: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1229-1229: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1244-1244: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1253-1253: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1262-1262: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1284-1284: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1320-1320: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1330-1330: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1346-1346: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1364-1364: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1375-1375: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1383-1383: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1387-1387: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1393-1393: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1397-1397: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1403-1403: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1409-1409: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1420-1420: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1426-1426: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1432-1432: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)


1442-1442: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🔇 Additional comments (27)
packages/cli/src/ui/layouts/DefaultAppLayout.tsx (1)

146-165: LGTM!

The addition of uiState.isWelcomeDialogOpen to the dialogsVisible check is consistent with the existing pattern for other dialogs and correctly integrates the welcome onboarding flow into the layout's dialog management.

packages/cli/src/ui/components/DialogManager.tsx (2)

12-12: LGTM!

The import statement correctly adds the WelcomeDialog component for onboarding integration.


102-112: LGTM!

The WelcomeDialog integration follows the established pattern of other dialogs in this file. The conditional rendering based on uiState.isWelcomeDialogOpen and the props passed align with the onboarding feature design.

packages/cli/src/ui/contexts/UIStateContext.tsx (2)

30-30: LGTM!

The import statement correctly brings in the necessary types for the welcome onboarding feature.


160-164: LGTM!

The welcome onboarding fields added to the UIState interface are well-typed and align with the onboarding feature requirements. The types WelcomeState and ModelInfo are properly imported and defined in the related hook module.

packages/cli/src/ui/components/WelcomeOnboarding/SkipExitStep.tsx (1)

1-57: LGTM! Clean and focused component.

This component is well-implemented with proper TypeScript typing, correct use of the useKeypress hook, and clear user guidance. The keyboard interaction (Enter to continue) is intuitive and properly gated by the isFocused prop.

packages/cli/src/ui/components/WelcomeOnboarding/WelcomeStep.tsx (1)

1-70: LGTM! Well-structured onboarding entry point.

The component provides a clear initial choice for users with helpful descriptive text and proper keyboard navigation hints. TypeScript types are well-defined, and the integration with RadioButtonSelect is correct.

packages/cli/src/ui/components/WelcomeOnboarding/CompletionStep.tsx (2)

37-54: LGTM! Excellent error handling pattern.

The handleProfileSubmit function demonstrates proper error handling with type-safe narrowing from unknown to Error. The validation logic (trimmed name check) and state management are well-implemented.


57-89: LGTM! Robust keyboard input handling.

The keypress handler correctly manages multiple input scenarios: submission, dismissal, backspace/delete, and printable character input. The filtering to printable ASCII characters (line 82) is a good defensive practice that handles paste operations safely.

packages/cli/src/ui/contexts/UIActionsContext.tsx (1)

84-102: LGTM! Well-designed onboarding actions interface.

The new welcome onboarding actions are properly typed and integrate cleanly into the existing UIActions interface. The method signatures are intuitive, with appropriate async handling for saveProfile and triggerWelcomeAuth. The optional apiKey parameter in triggerWelcomeAuth correctly accommodates both OAuth and API key authentication flows.

packages/cli/src/ui/components/WelcomeOnboarding/AuthMethodStep.tsx (2)

46-81: LGTM! Excellent use of React optimization hooks.

The component properly uses useMemo to memoize the options array (only recomputing when supportsOAuth changes) and useCallback to memoize the selection handler. The dependency arrays are correct, ensuring optimal performance without stale closures.


1-120: LGTM! Well-architected authentication method selection.

The component elegantly handles provider-specific authentication capabilities, conditionally offering OAuth when supported while always providing API key fallback. The hardcoded provider configurations (lines 16, 18-24) are appropriate for this onboarding context, and the UI provides helpful hints like API key URLs.

packages/cli/src/ui/components/WelcomeOnboarding/WelcomeDialog.tsx (1)

1-146: LGTM! Well-structured onboarding orchestration.

The component properly orchestrates the multi-step onboarding flow with:

  • Defensive null guards for steps requiring prerequisites (lines 79, 91, 102, 115)
  • Global escape handling that respects authentication state
  • Clean switch-based step rendering
  • Proper TypeScript typing throughout
project-plans/welcome-onboarding/PRD.md (1)

1-879: Skipping review of plan document per established guidance.

Based on learnings, plan documents under project-plans/ are not reviewed after implementation.

packages/cli/src/ui/components/WelcomeOnboarding/ModelSelectStep.tsx (1)

1-129: LGTM! Comprehensive state handling for model selection.

The component properly handles all model loading states:

  • Loading, error, success (with/without models)
  • Escape key recovery in error and empty states (lines 68-75)
  • Clear user guidance for each state
  • Proper TypeScript typing with no any usage
packages/cli/src/ui/components/WelcomeOnboarding/ProviderSelectStep.tsx (1)

1-90: LGTM! Clean provider selection with graceful fallback.

The component properly handles provider display with:

  • Fallback for unmapped provider IDs (line 40: PROVIDER_DISPLAY_NAMES[provider] || provider)
  • Clear skip option handling via sentinel value
  • Appropriate max visible items (8) for provider list
  • Proper TypeScript typing throughout
packages/cli/src/ui/AppContainer.tsx (3)

629-640: LGTM! Proper welcome onboarding integration.

The hook integration correctly:

  • Sequences after folder trust (line 639: isFolderTrustComplete: !isFolderTrustDialogOpen && !isRestarting)
  • Exposes all necessary state and actions for the onboarding flow
  • Maintains consistent naming with other dialog patterns

1500-1519: LGTM! Initial prompt properly gated.

The initial prompt submission correctly waits for welcome dialog completion (line 1500), ensuring new users complete onboarding before any automated queries run.


1651-1655: LGTM! UI state and actions properly extended.

The welcome onboarding state and actions are correctly:

  • Added to UIState (lines 1651-1655)
  • Added to UIActions (lines 1734-1736)
  • Included in memoization dependencies (lines 1821-1822)

This follows the established pattern for other dialogs in the application.

Also applies to: 1734-1736, 1821-1822

packages/cli/src/ui/components/WelcomeOnboarding/AuthenticationStep.tsx (3)

79-87: API key character filtering is correct and handles paste securely.

The regex /[^\x20-\x7E]/g correctly filters to printable ASCII characters, which is appropriate for API keys. The implementation properly handles both single keystrokes and pasted multi-character sequences. The bullet masking on line 136 ensures the key is never displayed in plain text.


42-55: LGTM - handleApiKeySubmit has proper error handling.

The callback correctly:

  • Guards against empty input
  • Sets loading state before async operation
  • Resets loading state on error
  • Properly extracts error messages from unknown error types

57-90: Escape key handling during authentication could be clearer.

The escape key check on line 60 allows going back even when method !== 'api_key' (i.e., during OAuth), but line 65-67 only processes other keys for api_key mode. This is intentional but the logic flow could be clearer. The current behavior is correct - Escape works in both modes when not authenticating.

packages/cli/src/ui/hooks/useWelcomeOnboarding.ts (3)

220-264: Profile saving implementation is thorough with good error handling and logging.

The saveProfile function:

  • Checks for duplicate profile names before saving
  • Saves the profile snapshot
  • Sets it as default for auto-load on startup
  • Loads the profile immediately in the current session
  • Has comprehensive debug logging throughout
  • Properly propagates errors to the caller

273-327: triggerAuth handles provider switching and auth comprehensively.

The implementation correctly:

  • Switches the active provider first using switchActiveProvider
  • Sets the selected model if available
  • Handles both OAuth and API key authentication paths
  • Throws appropriate errors for missing OAuth manager or API key
  • Includes detailed debug logging

One minor observation: if state.selectedModel is set but setActiveModel fails, the error isn't caught separately - it would propagate to the caller which is acceptable behavior.


198-217: goBack function is missing the 'model' step transition.

The switch statement handles transitions from model → provider, but the current 'model' case clears selectedProvider which seems incorrect. Going back from model selection should preserve the provider and just go back to provider selection for re-selection. However, looking more carefully, this appears intentional to allow the user to choose a different provider.

Actually, the logic seems correct:

  • From model → go to provider and clear selectedProvider (to re-select)
  • From auth_method → go to model and clear selectedModel
docs/plans/2026-01-03-welcome-onboarding.md (2)

1-1464: Overall plan document provides comprehensive implementation guidance.

The document successfully outlines:

  • Config persistence with welcomeConfig.ts
  • State management with useWelcomeOnboarding hook
  • Component architecture with step-based navigation
  • Integration points in AppContainer and DefaultAppLayout
  • Testing and manual verification steps

The static analysis markdownlint warnings (MD036) about "Emphasis used instead of a heading" are false positives for this plan document format - the **Step X:** pattern is an intentional stylistic choice that aids readability.


89-91: The actual implementation (packages/cli/src/config/welcomeConfig.ts:68) uses console.error, violating the coding guideline for **/*.{ts,tsx} files.

Replace console.error('Error saving welcome config:', error); with the sophisticated logging system (DebugLogger) per coding guidelines. The documentation file itself does not require changes as markdown files are excluded from code guidelines.

⛔ Skipped due to learnings
Learnt from: CR
Repo: vybestack/llxprt-code PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-05T14:59:34.368Z
Learning: Applies to **/*.{ts,tsx} : Do not use `console.log` or `console.debug` - Use the sophisticated logging system instead. Log files are written to ~/.llxprt/debug/

Comment on lines +39 to +44
try {
if (fs.existsSync(configPath)) {
const content = fs.readFileSync(configPath, 'utf-8');
cachedConfig = JSON.parse(content) as WelcomeConfig;
return cachedConfig;
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Add runtime validation for parsed config structure.

The type assertion as WelcomeConfig at Line 42 assumes the JSON structure is valid without verification. If the file is corrupted or manually edited, this could lead to runtime errors elsewhere.

🔎 Proposed fix with validation
 export function loadWelcomeConfig(): WelcomeConfig {
   if (cachedConfig) {
     return cachedConfig;
   }
 
   const configPath = getWelcomeConfigPath();
 
   try {
     if (fs.existsSync(configPath)) {
       const content = fs.readFileSync(configPath, 'utf-8');
-      cachedConfig = JSON.parse(content) as WelcomeConfig;
+      const parsed = JSON.parse(content);
+      
+      // Validate structure
+      if (
+        typeof parsed === 'object' &&
+        parsed !== null &&
+        typeof parsed.welcomeCompleted === 'boolean'
+      ) {
+        cachedConfig = parsed as WelcomeConfig;
+      } else {
+        // Invalid structure, fall through to default
+        throw new Error('Invalid config structure');
+      }
       return cachedConfig;
     }
   } catch (_error) {
     // If parsing fails, return default
   }
 
   cachedConfig = { welcomeCompleted: false };
   return cachedConfig;
 }
🤖 Prompt for AI Agents
In packages/cli/src/config/welcomeConfig.ts around lines 39 to 44, the JSON is
parsed and cast to WelcomeConfig without runtime validation; replace the blind
type assertion with a validation step: after JSON.parse, validate that the
resulting object has the expected properties and types (e.g., required keys,
string/boolean/array shapes) using a small inline check or an existing
schema/validator function, log or handle parse/validation errors (delete or
ignore invalid file and return a safe default cachedConfig) instead of returning
a malformed object, and ensure cachedConfig is only assigned when validation
succeeds.

});
cachedConfig = config;
} catch (error) {
console.error('Error saving welcome config:', error);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Replace console.error with the logging system.

The coding guidelines explicitly prohibit console.error and require using the sophisticated logging system. Log files are written to ~/.llxprt/debug/.

🔎 Proposed fix
+import { Logger } from '@vybestack/llxprt-code-core';
+
+const logger = new Logger('welcomeConfig');
+
 export function saveWelcomeConfig(config: WelcomeConfig): void {
   const configPath = getWelcomeConfigPath();
 
   try {
     const dirPath = path.dirname(configPath);
     if (!fs.existsSync(dirPath)) {
       fs.mkdirSync(dirPath, { recursive: true });
     }
 
     fs.writeFileSync(configPath, JSON.stringify(config, null, 2), {
       encoding: 'utf-8',
       mode: 0o600,
     });
     cachedConfig = config;
   } catch (error) {
-    console.error('Error saving welcome config:', error);
+    logger.error('Error saving welcome config:', error);
   }
 }

As per coding guidelines.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
console.error('Error saving welcome config:', error);
import { Logger } from '@vybestack/llxprt-code-core';
const logger = new Logger('welcomeConfig');
export function saveWelcomeConfig(config: WelcomeConfig): void {
const configPath = getWelcomeConfigPath();
try {
const dirPath = path.dirname(configPath);
if (!fs.existsSync(dirPath)) {
fs.mkdirSync(dirPath, { recursive: true });
}
fs.writeFileSync(configPath, JSON.stringify(config, null, 2), {
encoding: 'utf-8',
mode: 0o600,
});
cachedConfig = config;
} catch (error) {
logger.error('Error saving welcome config:', error);
}
}
🤖 Prompt for AI Agents
In packages/cli/src/config/welcomeConfig.ts at line 68, replace the
console.error call with the project's logging system: import or obtain the
module logger used across the CLI (the central logger that writes to
~/.llxprt/debug/), remove the console.error invocation, and call logger.error
with a clear message and the caught error object so the error details are
recorded (e.g., logger.error("Error saving welcome config", error)); ensure the
logger import/usage follows the same pattern as other CLI modules.

Comment on lines +7 to +13
export { WelcomeDialog } from './WelcomeDialog.js';
export { WelcomeStep } from './WelcomeStep.js';
export { ProviderSelectStep } from './ProviderSelectStep.js';
export { AuthMethodStep } from './AuthMethodStep.js';
export { AuthenticationStep } from './AuthenticationStep.js';
export { CompletionStep } from './CompletionStep.js';
export { SkipExitStep } from './SkipExitStep.js';
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Check if ModelSelectStep component exists
fd -t f "ModelSelectStep" packages/cli/src/ui/components/WelcomeOnboarding/

Repository: vybestack/llxprt-code

Length of output: 133


🏁 Script executed:

# List all files in the WelcomeOnboarding directory to see what actually exists
ls -la packages/cli/src/ui/components/WelcomeOnboarding/

Repository: vybestack/llxprt-code

Length of output: 781


🏁 Script executed:

# Check the current index.ts to see all exports
cat -n packages/cli/src/ui/components/WelcomeOnboarding/index.ts

Repository: vybestack/llxprt-code

Length of output: 629


🏁 Script executed:

# Search for any references to ModelSelectStep in the codebase
rg "ModelSelectStep" packages/cli/

Repository: vybestack/llxprt-code

Length of output: 518


Add ModelSelectStep export to the barrel file.

The ModelSelectStep component exists and is actively used by WelcomeDialog, but it's not exported from the barrel file while all other step components are. This breaks the consistent API surface for the module and should be exported to match the pattern.

Add this line to packages/cli/src/ui/components/WelcomeOnboarding/index.ts:

export { ModelSelectStep } from './ModelSelectStep.js';
🤖 Prompt for AI Agents
In packages/cli/src/ui/components/WelcomeOnboarding/index.ts around lines 7 to
13, the barrel file is missing an export for ModelSelectStep which is used by
WelcomeDialog; add an export for the component to match the other step exports
by adding an export line for ModelSelectStep that re-exports it from
'./ModelSelectStep.js'.

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.

2 participants