Detailed changes
  
  
    
    @@ -208,7 +208,8 @@ type Config struct {
 	// TODO: most likely remove this concept when I come back to it
 	Agents map[string]Agent `json:"-"`
 	// TODO: find a better way to do this this should probably not be part of the config
-	resolver VariableResolver
+	resolver      VariableResolver
+	dataConfigDir string `json:"-"`
 }
 
 func (c *Config) WorkingDir() string {
@@ -291,17 +292,17 @@ func (c *Config) Resolve(key string) (string, error) {
 	return c.resolver.ResolveValue(key)
 }
 
-// TODO: maybe handle this better
-func UpdatePreferredModel(modelType SelectedModelType, model SelectedModel) error {
-	cfg := Get()
-	cfg.Models[modelType] = model
+func (c *Config) UpdatePreferredModel(modelType SelectedModelType, model SelectedModel) error {
+	c.Models[modelType] = model
+	if err := c.SetConfigField(fmt.Sprintf("models.%s", modelType), model); err != nil {
+		return fmt.Errorf("failed to update preferred model: %w", err)
+	}
 	return nil
 }
 
 func (c *Config) SetConfigField(key string, value any) error {
-	configPath := GlobalConfigData()
 	// read the data
-	data, err := os.ReadFile(configPath)
+	data, err := os.ReadFile(c.dataConfigDir)
 	if err != nil {
 		if os.IsNotExist(err) {
 			data = []byte("{}")
@@ -314,7 +315,7 @@ func (c *Config) SetConfigField(key string, value any) error {
 	if err != nil {
 		return fmt.Errorf("failed to set config field %s: %w", key, err)
 	}
-	if err := os.WriteFile(configPath, []byte(newValue), 0o644); err != nil {
+	if err := os.WriteFile(c.dataConfigDir, []byte(newValue), 0o644); err != nil {
 		return fmt.Errorf("failed to write config file: %w", err)
 	}
 	return nil
  
  
  
    
    @@ -46,6 +46,8 @@ func Load(workingDir string, debug bool) (*Config, error) {
 		return nil, fmt.Errorf("failed to load config from paths %v: %w", configPaths, err)
 	}
 
+	cfg.dataConfigDir = GlobalConfigData()
+
 	cfg.setDefaults(workingDir)
 
 	if debug {
@@ -367,10 +369,11 @@ func (cfg *Config) defaultModelSelection(knownProviders []provider.Provider) (la
 }
 
 func (cfg *Config) configureSelectedModels(knownProviders []provider.Provider) error {
-	large, small, err := cfg.defaultModelSelection(knownProviders)
+	defaultLarge, defaultSmall, err := cfg.defaultModelSelection(knownProviders)
 	if err != nil {
 		return fmt.Errorf("failed to select default models: %w", err)
 	}
+	large, small := defaultLarge, defaultSmall
 
 	largeModelSelected, largeModelConfigured := cfg.Models[SelectedModelTypeLarge]
 	if largeModelConfigured {
@@ -381,18 +384,26 @@ func (cfg *Config) configureSelectedModels(knownProviders []provider.Provider) e
 			large.Provider = largeModelSelected.Provider
 		}
 		model := cfg.GetModel(large.Provider, large.Model)
+		slog.Info("Configuring selected large model", "provider", large.Provider, "model", large.Model)
+		slog.Info("MOdel configured", "model", model)
 		if model == nil {
-			return fmt.Errorf("large model %s not found for provider %s", large.Model, large.Provider)
-		}
-		if largeModelSelected.MaxTokens > 0 {
-			large.MaxTokens = largeModelSelected.MaxTokens
+			large = defaultLarge
+			// override the model type to large
+			err := cfg.UpdatePreferredModel(SelectedModelTypeLarge, large)
+			if err != nil {
+				return fmt.Errorf("failed to update preferred large model: %w", err)
+			}
 		} else {
-			large.MaxTokens = model.DefaultMaxTokens
-		}
-		if largeModelSelected.ReasoningEffort != "" {
-			large.ReasoningEffort = largeModelSelected.ReasoningEffort
+			if largeModelSelected.MaxTokens > 0 {
+				large.MaxTokens = largeModelSelected.MaxTokens
+			} else {
+				large.MaxTokens = model.DefaultMaxTokens
+			}
+			if largeModelSelected.ReasoningEffort != "" {
+				large.ReasoningEffort = largeModelSelected.ReasoningEffort
+			}
+			large.Think = largeModelSelected.Think
 		}
-		large.Think = largeModelSelected.Think
 	}
 	smallModelSelected, smallModelConfigured := cfg.Models[SelectedModelTypeSmall]
 	if smallModelConfigured {
@@ -405,25 +416,21 @@ func (cfg *Config) configureSelectedModels(knownProviders []provider.Provider) e
 
 		model := cfg.GetModel(small.Provider, small.Model)
 		if model == nil {
-			return fmt.Errorf("large model %s not found for provider %s", large.Model, large.Provider)
-		}
-		if smallModelSelected.MaxTokens > 0 {
-			small.MaxTokens = smallModelSelected.MaxTokens
+			small = defaultSmall
+			// override the model type to small
+			err := cfg.UpdatePreferredModel(SelectedModelTypeSmall, small)
+			if err != nil {
+				return fmt.Errorf("failed to update preferred small model: %w", err)
+			}
 		} else {
-			small.MaxTokens = model.DefaultMaxTokens
+			if smallModelSelected.MaxTokens > 0 {
+				small.MaxTokens = smallModelSelected.MaxTokens
+			} else {
+				small.MaxTokens = model.DefaultMaxTokens
+			}
+			small.ReasoningEffort = smallModelSelected.ReasoningEffort
+			small.Think = smallModelSelected.Think
 		}
-		small.ReasoningEffort = smallModelSelected.ReasoningEffort
-		small.Think = smallModelSelected.Think
-	}
-
-	// validate the selected models
-	largeModel := cfg.GetModel(large.Provider, large.Model)
-	if largeModel == nil {
-		return fmt.Errorf("large model %s not found for provider %s", large.Model, large.Provider)
-	}
-	smallModel := cfg.GetModel(small.Provider, small.Model)
-	if smallModel == nil {
-		return fmt.Errorf("small model %s not found for provider %s", small.Model, small.Provider)
 	}
 	cfg.Models[SelectedModelTypeLarge] = large
 	cfg.Models[SelectedModelTypeSmall] = small
  
  
  
    
    @@ -10,6 +10,7 @@ import (
 	"github.com/charmbracelet/lipgloss/v2"
 
 	"github.com/charmbracelet/crush/internal/config"
+	"github.com/charmbracelet/crush/internal/fur/provider"
 	"github.com/charmbracelet/crush/internal/message"
 	"github.com/charmbracelet/crush/internal/tui/components/anim"
 	"github.com/charmbracelet/crush/internal/tui/components/core"
@@ -296,6 +297,12 @@ func (m *assistantSectionModel) View() tea.View {
 	infoMsg := t.S().Subtle.Render(duration.String())
 	icon := t.S().Subtle.Render(styles.ModelIcon)
 	model := config.Get().GetModel(m.message.Provider, m.message.Model)
+	if model == nil {
+		// This means the model is not configured anymore
+		model = &provider.Model{
+			Name: "Unknown Model",
+		}
+	}
 	modelFormatted := t.S().Muted.Render(model.Name)
 	assistant := fmt.Sprintf("%s %s %s", icon, modelFormatted, infoMsg)
 	return tea.NewView(
  
  
  
    
    @@ -7,7 +7,11 @@ import (
 type KeyMap struct {
 	Select,
 	Next,
-	Previous key.Binding
+	Previous,
+	Yes,
+	No,
+	Tab,
+	LeftRight key.Binding
 }
 
 func DefaultKeyMap() KeyMap {
@@ -24,5 +28,21 @@ func DefaultKeyMap() KeyMap {
 			key.WithKeys("up", "ctrl+p"),
 			key.WithHelp("↑", "previous item"),
 		),
+		Yes: key.NewBinding(
+			key.WithKeys("y"),
+			key.WithHelp("y", "yes"),
+		),
+		No: key.NewBinding(
+			key.WithKeys("n"),
+			key.WithHelp("n", "no"),
+		),
+		Tab: key.NewBinding(
+			key.WithKeys("tab"),
+			key.WithHelp("tab", "switch"),
+		),
+		LeftRight: key.NewBinding(
+			key.WithKeys("left", "right"),
+			key.WithHelp("←/→", "switch"),
+		),
 	}
 }
  
  
  
    
    @@ -1,9 +1,16 @@
 package splash
 
 import (
+	"fmt"
+	"slices"
+
 	"github.com/charmbracelet/bubbles/v2/key"
 	tea "github.com/charmbracelet/bubbletea/v2"
 	"github.com/charmbracelet/crush/internal/config"
+	"github.com/charmbracelet/crush/internal/fur/provider"
+	"github.com/charmbracelet/crush/internal/tui/components/chat"
+	"github.com/charmbracelet/crush/internal/tui/components/completions"
+	"github.com/charmbracelet/crush/internal/tui/components/core"
 	"github.com/charmbracelet/crush/internal/tui/components/core/layout"
 	"github.com/charmbracelet/crush/internal/tui/components/core/list"
 	"github.com/charmbracelet/crush/internal/tui/components/dialogs/models"
@@ -43,6 +50,7 @@ type splashCmp struct {
 	state                SplashScreenState
 	modelList            *models.ModelListComponent
 	cursorRow, cursorCol int
+	selectedNo           bool // true if "No" button is selected in initialize state
 }
 
 func New() Splash {
@@ -67,6 +75,7 @@ func New() Splash {
 		state:        SplashScreenStateOnboarding,
 		logoRendered: "",
 		modelList:    modelList,
+		selectedNo:   false,
 	}
 }
 
@@ -83,8 +92,28 @@ func (s *splashCmp) Init() tea.Cmd {
 		} else {
 			s.state = SplashScreenStateReady
 		}
+	} else {
+		providers, err := config.Providers()
+		if err != nil {
+			return util.ReportError(err)
+		}
+		filteredProviders := []provider.Provider{}
+		simpleProviders := []string{
+			"anthropic",
+			"openai",
+			"gemini",
+			"xai",
+			"openrouter",
+		}
+		for _, p := range providers {
+			if slices.Contains(simpleProviders, string(p.ID)) {
+				filteredProviders = append(filteredProviders, p)
+			}
+		}
+		s.modelList.SetProviders(filteredProviders)
+		return s.modelList.Init()
 	}
-	return s.modelList.Init()
+	return nil
 }
 
 // SetSize implements SplashPage.
@@ -107,15 +136,157 @@ func (s *splashCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		return s, s.SetSize(msg.Width, msg.Height)
 	case tea.KeyPressMsg:
 		switch {
+		case key.Matches(msg, s.keyMap.Select):
+			if s.state == SplashScreenStateOnboarding {
+				modelInx := s.modelList.SelectedIndex()
+				items := s.modelList.Items()
+				selectedItem := items[modelInx].(completions.CompletionItem).Value().(models.ModelOption)
+				if s.isProviderConfigured(string(selectedItem.Provider.ID)) {
+					cmd := s.setPreferredModel(selectedItem)
+					s.state = SplashScreenStateReady
+					if b, err := config.ProjectNeedsInitialization(); err != nil {
+						return s, tea.Batch(cmd, util.ReportError(err))
+					} else if b {
+						s.state = SplashScreenStateInitialize
+						return s, cmd
+					} else {
+						s.state = SplashScreenStateReady
+						return s, tea.Batch(cmd, util.CmdHandler(OnboardingCompleteMsg{}))
+					}
+				}
+			} else if s.state == SplashScreenStateInitialize {
+				return s, s.initializeProject()
+			}
+		case key.Matches(msg, s.keyMap.Tab, s.keyMap.LeftRight):
+			if s.state == SplashScreenStateInitialize {
+				s.selectedNo = !s.selectedNo
+				return s, nil
+			}
+		case key.Matches(msg, s.keyMap.Yes):
+			if s.state == SplashScreenStateInitialize {
+				return s, s.initializeProject()
+			}
+		case key.Matches(msg, s.keyMap.No):
+			if s.state == SplashScreenStateInitialize {
+				s.state = SplashScreenStateReady
+				return s, util.CmdHandler(OnboardingCompleteMsg{})
+			}
 		default:
-			u, cmd := s.modelList.Update(msg)
-			s.modelList = u
-			return s, cmd
+			if s.state == SplashScreenStateOnboarding {
+				u, cmd := s.modelList.Update(msg)
+				s.modelList = u
+				return s, cmd
+			}
 		}
 	}
 	return s, nil
 }
 
+func (s *splashCmp) initializeProject() tea.Cmd {
+	s.state = SplashScreenStateReady
+	prompt := `Please analyze this codebase and create a CRUSH.md file containing:
+1. Build/lint/test commands - especially for running a single test
+2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.
+
+The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 20 lines long.
+If there's already a CRUSH.md, improve it.
+If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include them.
+Add the .crush directory to the .gitignore file if it's not already there.`
+
+	// Mark the project as initialized
+	if err := config.MarkProjectInitialized(); err != nil {
+		return util.ReportError(err)
+	}
+	var cmds []tea.Cmd
+
+	cmds = append(cmds, util.CmdHandler(OnboardingCompleteMsg{}))
+	if !s.selectedNo {
+		cmds = append(cmds,
+			util.CmdHandler(chat.SessionClearedMsg{}),
+			util.CmdHandler(chat.SendMsg{
+				Text: prompt,
+			}),
+		)
+	}
+	return tea.Sequence(cmds...)
+}
+
+func (s *splashCmp) setPreferredModel(selectedItem models.ModelOption) tea.Cmd {
+	cfg := config.Get()
+	model := cfg.GetModel(string(selectedItem.Provider.ID), selectedItem.Model.ID)
+	if model == nil {
+		return util.ReportError(fmt.Errorf("model %s not found for provider %s", selectedItem.Model.ID, selectedItem.Provider.ID))
+	}
+
+	selectedModel := config.SelectedModel{
+		Model:           selectedItem.Model.ID,
+		Provider:        string(selectedItem.Provider.ID),
+		ReasoningEffort: model.DefaultReasoningEffort,
+		MaxTokens:       model.DefaultMaxTokens,
+	}
+
+	err := cfg.UpdatePreferredModel(config.SelectedModelTypeLarge, selectedModel)
+	if err != nil {
+		return util.ReportError(err)
+	}
+
+	// Now lets automatically setup the small model
+	knownProvider, err := s.getProvider(selectedItem.Provider.ID)
+	if err != nil {
+		return util.ReportError(err)
+	}
+	if knownProvider == nil {
+		// for local provider we just use the same model
+		err = cfg.UpdatePreferredModel(config.SelectedModelTypeSmall, selectedModel)
+		if err != nil {
+			return util.ReportError(err)
+		}
+	} else {
+		smallModel := knownProvider.DefaultSmallModelID
+		model := cfg.GetModel(string(selectedItem.Provider.ID), smallModel)
+		// should never happen
+		if model == nil {
+			err = cfg.UpdatePreferredModel(config.SelectedModelTypeSmall, selectedModel)
+			if err != nil {
+				return util.ReportError(err)
+			}
+			return nil
+		}
+		smallSelectedModel := config.SelectedModel{
+			Model:           smallModel,
+			Provider:        string(selectedItem.Provider.ID),
+			ReasoningEffort: model.DefaultReasoningEffort,
+			MaxTokens:       model.DefaultMaxTokens,
+		}
+		err = cfg.UpdatePreferredModel(config.SelectedModelTypeSmall, smallSelectedModel)
+		if err != nil {
+			return util.ReportError(err)
+		}
+	}
+	return nil
+}
+
+func (s *splashCmp) getProvider(providerID provider.InferenceProvider) (*provider.Provider, error) {
+	providers, err := config.Providers()
+	if err != nil {
+		return nil, err
+	}
+	for _, p := range providers {
+		if p.ID == providerID {
+			return &p, nil
+		}
+	}
+	return nil, nil
+}
+
+func (s *splashCmp) isProviderConfigured(providerID string) bool {
+	cfg := config.Get()
+	if _, ok := cfg.Providers[providerID]; ok {
+		return true
+	}
+	return false
+}
+
 // View implements SplashPage.
 func (s *splashCmp) View() tea.View {
 	t := styles.CurrentTheme()
@@ -141,6 +312,56 @@ func (s *splashCmp) View() tea.View {
 			s.logoRendered,
 			modelSelector,
 		)
+	case SplashScreenStateInitialize:
+		t := styles.CurrentTheme()
+
+		titleStyle := t.S().Base.Foreground(t.FgBase)
+		bodyStyle := t.S().Base.Foreground(t.FgMuted)
+		shortcutStyle := t.S().Base.Foreground(t.Success)
+
+		initText := lipgloss.JoinVertical(
+			lipgloss.Left,
+			titleStyle.Render("Would you like to initialize this project?"),
+			"",
+			bodyStyle.Render("When I initialize your codebase I examine the project and put the"),
+			bodyStyle.Render("result into a CRUSH.md file which serves as general context."),
+			"",
+			bodyStyle.Render("You can also initialize anytime via ")+shortcutStyle.Render("ctrl+p")+bodyStyle.Render("."),
+			"",
+			bodyStyle.Render("Would you like to initialize now?"),
+		)
+
+		yesButton := core.SelectableButton(core.ButtonOpts{
+			Text:           "Yep!",
+			UnderlineIndex: 0,
+			Selected:       !s.selectedNo,
+		})
+
+		noButton := core.SelectableButton(core.ButtonOpts{
+			Text:           "Nope",
+			UnderlineIndex: 0,
+			Selected:       s.selectedNo,
+		})
+
+		buttons := lipgloss.JoinHorizontal(lipgloss.Left, yesButton, "  ", noButton)
+
+		remainingHeight := s.height - lipgloss.Height(s.logoRendered) - (SplashScreenPaddingY * 2)
+
+		initContent := t.S().Base.AlignVertical(lipgloss.Bottom).Height(remainingHeight).Render(
+			lipgloss.JoinVertical(
+				lipgloss.Left,
+				initText,
+				"",
+				buttons,
+			),
+		)
+
+		content = lipgloss.JoinVertical(
+			lipgloss.Left,
+			s.logoRendered,
+			initContent,
+		)
+
 	default:
 		// Show just the logo for other states
 		content = s.logoRendered
@@ -192,6 +413,14 @@ func (s *splashCmp) Bindings() []key.Binding {
 			s.keyMap.Next,
 			s.keyMap.Previous,
 		}
+	} else if s.state == SplashScreenStateInitialize {
+		return []key.Binding{
+			s.keyMap.Select,
+			s.keyMap.Yes,
+			s.keyMap.No,
+			s.keyMap.Tab,
+			s.keyMap.LeftRight,
+		}
 	}
 	return []key.Binding{}
 }
  
  
  
    
    @@ -1,214 +0,0 @@
-package init
-
-import (
-	"github.com/charmbracelet/bubbles/v2/key"
-	tea "github.com/charmbracelet/bubbletea/v2"
-	"github.com/charmbracelet/lipgloss/v2"
-
-	"github.com/charmbracelet/crush/internal/config"
-	cmpChat "github.com/charmbracelet/crush/internal/tui/components/chat"
-	"github.com/charmbracelet/crush/internal/tui/components/core"
-	"github.com/charmbracelet/crush/internal/tui/components/dialogs"
-	"github.com/charmbracelet/crush/internal/tui/styles"
-	"github.com/charmbracelet/crush/internal/tui/util"
-)
-
-const InitDialogID dialogs.DialogID = "init"
-
-// InitDialogCmp is a component that asks the user if they want to initialize the project.
-type InitDialogCmp interface {
-	dialogs.DialogModel
-}
-
-type initDialogCmp struct {
-	wWidth, wHeight int
-	width, height   int
-	selected        int
-	keyMap          KeyMap
-}
-
-// NewInitDialogCmp creates a new InitDialogCmp.
-func NewInitDialogCmp() InitDialogCmp {
-	return &initDialogCmp{
-		selected: 0,
-		keyMap:   DefaultKeyMap(),
-	}
-}
-
-// Init implements tea.Model.
-func (m *initDialogCmp) Init() tea.Cmd {
-	return nil
-}
-
-// Update implements tea.Model.
-func (m *initDialogCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
-	switch msg := msg.(type) {
-	case tea.WindowSizeMsg:
-		m.wWidth = msg.Width
-		m.wHeight = msg.Height
-		cmd := m.SetSize()
-		return m, cmd
-	case tea.KeyPressMsg:
-		switch {
-		case key.Matches(msg, m.keyMap.Close):
-			return m, tea.Batch(
-				util.CmdHandler(dialogs.CloseDialogMsg{}),
-				m.handleInitialization(false),
-			)
-		case key.Matches(msg, m.keyMap.ChangeSelection):
-			m.selected = (m.selected + 1) % 2
-			return m, nil
-		case key.Matches(msg, m.keyMap.Select):
-			return m, tea.Batch(
-				util.CmdHandler(dialogs.CloseDialogMsg{}),
-				m.handleInitialization(m.selected == 0),
-			)
-		case key.Matches(msg, m.keyMap.Y):
-			return m, tea.Batch(
-				util.CmdHandler(dialogs.CloseDialogMsg{}),
-				m.handleInitialization(true),
-			)
-		case key.Matches(msg, m.keyMap.N):
-			return m, tea.Batch(
-				util.CmdHandler(dialogs.CloseDialogMsg{}),
-				m.handleInitialization(false),
-			)
-		}
-	}
-	return m, nil
-}
-
-func (m *initDialogCmp) renderButtons() string {
-	t := styles.CurrentTheme()
-	baseStyle := t.S().Base
-
-	buttons := []core.ButtonOpts{
-		{
-			Text:           "Yes",
-			UnderlineIndex: 0, // "Y"
-			Selected:       m.selected == 0,
-		},
-		{
-			Text:           "No",
-			UnderlineIndex: 0, // "N"
-			Selected:       m.selected == 1,
-		},
-	}
-
-	content := core.SelectableButtons(buttons, "  ")
-
-	return baseStyle.AlignHorizontal(lipgloss.Right).Width(m.width - 4).Render(content)
-}
-
-func (m *initDialogCmp) renderContent() string {
-	t := styles.CurrentTheme()
-	baseStyle := t.S().Base
-
-	explanation := t.S().Text.
-		Width(m.width - 4).
-		Render("Initialization generates a new CRUSH.md file that contains information about your codebase, this file serves as memory for each project, you can freely add to it to help the agents be better at their job.")
-
-	question := t.S().Text.
-		Width(m.width - 4).
-		Render("Would you like to initialize this project?")
-
-	return baseStyle.Render(lipgloss.JoinVertical(
-		lipgloss.Left,
-		explanation,
-		"",
-		question,
-	))
-}
-
-func (m *initDialogCmp) render() string {
-	t := styles.CurrentTheme()
-	baseStyle := t.S().Base
-	title := core.Title("Initialize Project", m.width-4)
-
-	content := m.renderContent()
-	buttons := m.renderButtons()
-
-	dialogContent := lipgloss.JoinVertical(
-		lipgloss.Top,
-		title,
-		"",
-		content,
-		"",
-		buttons,
-		"",
-	)
-
-	return baseStyle.
-		Padding(0, 1).
-		Border(lipgloss.RoundedBorder()).
-		BorderForeground(t.BorderFocus).
-		Width(m.width).
-		Render(dialogContent)
-}
-
-// View implements tea.Model.
-func (m *initDialogCmp) View() tea.View {
-	return tea.NewView(m.render())
-}
-
-// SetSize sets the size of the component.
-func (m *initDialogCmp) SetSize() tea.Cmd {
-	m.width = min(90, m.wWidth)
-	m.height = min(15, m.wHeight)
-	return nil
-}
-
-// ID implements DialogModel.
-func (m *initDialogCmp) ID() dialogs.DialogID {
-	return InitDialogID
-}
-
-// Position implements DialogModel.
-func (m *initDialogCmp) Position() (int, int) {
-	row := (m.wHeight / 2) - (m.height / 2)
-	col := (m.wWidth / 2) - (m.width / 2)
-	return row, col
-}
-
-// handleInitialization handles the initialization logic when the dialog is closed.
-func (m *initDialogCmp) handleInitialization(initialize bool) tea.Cmd {
-	if initialize {
-		// Run the initialization command
-		prompt := `Please analyze this codebase and create a CRUSH.md file containing:
-1. Build/lint/test commands - especially for running a single test
-2. Code style guidelines including imports, formatting, types, naming conventions, error handling, etc.
-
-The file you create will be given to agentic coding agents (such as yourself) that operate in this repository. Make it about 20 lines long.
-If there's already a CRUSH.md, improve it.
-If there are Cursor rules (in .cursor/rules/ or .cursorrules) or Copilot rules (in .github/copilot-instructions.md), make sure to include them.
-Add the .crush directory to the .gitignore file if it's not already there.`
-
-		// Mark the project as initialized
-		if err := config.MarkProjectInitialized(); err != nil {
-			return util.ReportError(err)
-		}
-
-		return tea.Sequence(
-			util.CmdHandler(cmpChat.SessionClearedMsg{}),
-			util.CmdHandler(cmpChat.SendMsg{
-				Text: prompt,
-			}),
-		)
-	} else {
-		// Mark the project as initialized without running the command
-		if err := config.MarkProjectInitialized(); err != nil {
-			return util.ReportError(err)
-		}
-	}
-	return nil
-}
-
-// CloseInitDialogMsg is a message that is sent when the init dialog is closed.
-type CloseInitDialogMsg struct {
-	Initialize bool
-}
-
-// ShowInitDialogMsg is a message that is sent to show the init dialog.
-type ShowInitDialogMsg struct {
-	Show bool
-}
  
  
  
    
    @@ -1,69 +0,0 @@
-package init
-
-import (
-	"github.com/charmbracelet/bubbles/v2/key"
-)
-
-type KeyMap struct {
-	ChangeSelection,
-	Select,
-	Y,
-	N,
-	Close key.Binding
-}
-
-func DefaultKeyMap() KeyMap {
-	return KeyMap{
-		ChangeSelection: key.NewBinding(
-			key.WithKeys("tab", "left", "right", "h", "l"),
-			key.WithHelp("tab/←/→", "toggle selection"),
-		),
-		Select: key.NewBinding(
-			key.WithKeys("enter"),
-			key.WithHelp("enter", "confirm"),
-		),
-		Y: key.NewBinding(
-			key.WithKeys("y"),
-			key.WithHelp("y", "yes"),
-		),
-		N: key.NewBinding(
-			key.WithKeys("n"),
-			key.WithHelp("n", "no"),
-		),
-		Close: key.NewBinding(
-			key.WithKeys("esc"),
-			key.WithHelp("esc", "cancel"),
-		),
-	}
-}
-
-// KeyBindings implements layout.KeyMapProvider
-func (k KeyMap) KeyBindings() []key.Binding {
-	return []key.Binding{
-		k.ChangeSelection,
-		k.Select,
-		k.Y,
-		k.N,
-		k.Close,
-	}
-}
-
-// FullHelp implements help.KeyMap.
-func (k KeyMap) FullHelp() [][]key.Binding {
-	m := [][]key.Binding{}
-	slice := k.KeyBindings()
-	for i := 0; i < len(slice); i += 4 {
-		end := min(i+4, len(slice))
-		m = append(m, slice[i:end])
-	}
-	return m
-}
-
-// ShortHelp implements help.KeyMap.
-func (k KeyMap) ShortHelp() []key.Binding {
-	return []key.Binding{
-		k.ChangeSelection,
-		k.Select,
-		k.Close,
-	}
-}
  
  
  
    
    @@ -18,6 +18,7 @@ import (
 type ModelListComponent struct {
 	list      list.ListModel
 	modelType int
+	providers []provider.Provider
 }
 
 func NewModelListComponent(keyMap list.KeyMap, inputStyle lipgloss.Style, inputPlaceholder string) *ModelListComponent {
@@ -36,7 +37,16 @@ func NewModelListComponent(keyMap list.KeyMap, inputStyle lipgloss.Style, inputP
 }
 
 func (m *ModelListComponent) Init() tea.Cmd {
-	return tea.Batch(m.list.Init(), m.SetModelType(m.modelType))
+	var cmds []tea.Cmd
+	if len(m.providers) == 0 {
+		providers, err := config.Providers()
+		m.providers = providers
+		if err != nil {
+			cmds = append(cmds, util.ReportError(err))
+		}
+	}
+	cmds = append(cmds, m.list.Init(), m.SetModelType(m.modelType))
+	return tea.Batch(cmds...)
 }
 
 func (m *ModelListComponent) Update(msg tea.Msg) (*ModelListComponent, tea.Cmd) {
@@ -65,11 +75,6 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
 	t := styles.CurrentTheme()
 	m.modelType = modelType
 
-	providers, err := config.Providers()
-	if err != nil {
-		return util.ReportError(err)
-	}
-
 	modelItems := []util.Model{}
 	selectIndex := 0
 
@@ -144,7 +149,7 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
 	}
 
 	// Then add the known providers from the predefined list
-	for _, provider := range providers {
+	for _, provider := range m.providers {
 		// Skip if we already added this provider as an unknown provider
 		if addedProviders[string(provider.ID)] {
 			continue
@@ -187,3 +192,7 @@ func (m *ModelListComponent) GetModelType() int {
 func (m *ModelListComponent) SetInputPlaceholder(placeholder string) {
 	m.list.SetFilterPlaceholder(placeholder)
 }
+
+func (m *ModelListComponent) SetProviders(providers []provider.Provider) {
+	m.providers = providers
+}
  
  
  
    
    @@ -220,6 +220,14 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 		if cmd != nil {
 			return p, cmd
 		}
+	case splash.OnboardingCompleteMsg:
+		p.state = ChatStateNewMessage
+		err := p.app.InitCoderAgent()
+		if err != nil {
+			return p, util.ReportError(err)
+		}
+		p.focusedPane = PanelTypeEditor
+		return p, p.SetSize(p.width, p.height)
 	case tea.KeyPressMsg:
 		switch {
 		case key.Matches(msg, p.keyMap.NewSession):
@@ -233,6 +241,11 @@ func (p *chatPage) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 				return p, util.ReportWarn("File attachments are not supported by the current model: " + model.Name)
 			}
 		case key.Matches(msg, p.keyMap.Tab):
+			if p.state == ChatStateOnboarding || p.state == ChatStateInitProject {
+				u, cmd := p.splash.Update(msg)
+				p.splash = u.(splash.Splash)
+				return p, cmd
+			}
 			p.changeFocus()
 			return p, nil
 		case key.Matches(msg, p.keyMap.Cancel):
@@ -416,6 +429,7 @@ func (p *chatPage) newSession() tea.Cmd {
 		// Cannot start a new session if we are not in the session state
 		return nil
 	}
+
 	// blank session
 	p.session = session.Session{}
 	p.state = ChatStateNewMessage
@@ -456,8 +470,12 @@ func (p *chatPage) changeFocus() {
 	switch p.focusedPane {
 	case PanelTypeChat:
 		p.focusedPane = PanelTypeEditor
+		p.editor.Focus()
+		p.chat.Blur()
 	case PanelTypeEditor:
 		p.focusedPane = PanelTypeChat
+		p.chat.Focus()
+		p.editor.Blur()
 	}
 }
 
  
  
  
    
    @@ -19,7 +19,6 @@ import (
 	"github.com/charmbracelet/crush/internal/tui/components/dialogs/commands"
 	"github.com/charmbracelet/crush/internal/tui/components/dialogs/compact"
 	"github.com/charmbracelet/crush/internal/tui/components/dialogs/filepicker"
-	initDialog "github.com/charmbracelet/crush/internal/tui/components/dialogs/init"
 	"github.com/charmbracelet/crush/internal/tui/components/dialogs/models"
 	"github.com/charmbracelet/crush/internal/tui/components/dialogs/permissions"
 	"github.com/charmbracelet/crush/internal/tui/components/dialogs/quit"
@@ -65,23 +64,6 @@ func (a appModel) Init() tea.Cmd {
 	cmd = a.status.Init()
 	cmds = append(cmds, cmd)
 
-	// Check if we should show the init dialog
-	cmds = append(cmds, func() tea.Msg {
-		shouldShow, err := config.ProjectNeedsInitialization()
-		if err != nil {
-			return util.InfoMsg{
-				Type: util.InfoTypeError,
-				Msg:  "Failed to check init status: " + err.Error(),
-			}
-		}
-		if shouldShow {
-			return dialogs.OpenDialogMsg{
-				Model: initDialog.NewInitDialogCmp(),
-			}
-		}
-		return nil
-	})
-
 	return tea.Batch(cmds...)
 }
 
@@ -156,7 +138,7 @@ func (a *appModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 
 	// Model Switch
 	case models.ModelSelectedMsg:
-		config.UpdatePreferredModel(msg.ModelType, msg.Model)
+		config.Get().UpdatePreferredModel(msg.ModelType, msg.Model)
 
 		// Update the agent with the new model/provider configuration
 		if err := a.app.UpdateAgentModel(); err != nil {