@@ -458,6 +458,17 @@ func (m *TuiModel) startStartupCheck() tea.Cmd {
458458 return nil
459459 }
460460
461+ // Non-installable core packages — never prompt for these
462+ nonInstallable := map [string ]bool {
463+ "@owdproject/core" : true ,
464+ "@owdproject/cli" : true ,
465+ "@owdproject/nx" : true ,
466+ }
467+
468+ // Track what is already queued to avoid duplicates
469+ queued := map [string ]bool {}
470+
471+ // Round 1: apps, modules, and theme from catalog
461472 for _ , entry := range m .catalog .Entries {
462473 if entry .Installed && ! entry .LocalSource {
463474 m .promptQueue = append (m .promptQueue , pendingDecision {
@@ -467,6 +478,75 @@ func (m *TuiModel) startStartupCheck() tea.Cmd {
467478 Kind : entry .Kind ,
468479 Entry : entry ,
469480 })
481+ queued [entry .Name ] = true
482+ }
483+ }
484+
485+ // Round 2: find the active/pending theme and read its kit/module deps
486+ var activeThemeShort string
487+ for _ , dec := range m .promptQueue {
488+ if dec .Kind == "theme" {
489+ activeThemeShort = dec .ShortName
490+ break
491+ }
492+ }
493+ // Also check the current config theme if not in queue
494+ if activeThemeShort == "" && m .ctx != nil && m .ctx .Config .Theme != nil && * m .ctx .Config .Theme != "" {
495+ themeName := * m .ctx .Config .Theme
496+ if idx := strings .LastIndex (themeName , "/" ); idx >= 0 {
497+ activeThemeShort = themeName [idx + 1 :]
498+ } else {
499+ activeThemeShort = themeName
500+ }
501+ }
502+
503+ if activeThemeShort != "" {
504+ themeDeps , err := getThemeDependencies (m .workspaceRoot , activeThemeShort )
505+ if err == nil {
506+ for _ , dep := range themeDeps {
507+ if nonInstallable [dep ] || queued [dep ] {
508+ continue
509+ }
510+ // Only kit-* and module-* prefixed @owdproject packages
511+ short := dep
512+ if idx := strings .LastIndex (dep , "/" ); idx >= 0 {
513+ short = dep [idx + 1 :]
514+ }
515+ if ! strings .HasPrefix (short , "kit-" ) && ! strings .HasPrefix (short , "module-" ) {
516+ continue
517+ }
518+ // Skip if already installed locally
519+ if isLocallyAvailable (m .workspaceRoot , short ) {
520+ continue
521+ }
522+ // Find in catalog (may not be listed)
523+ var entry * bridge.CatalogEntry
524+ if m .catalog != nil {
525+ for _ , e := range m .catalog .Entries {
526+ if e .Name == dep {
527+ entry = & e
528+ break
529+ }
530+ }
531+ }
532+ if entry == nil {
533+ // Synthesize a minimal entry for the wizard
534+ synth := bridge.CatalogEntry {
535+ Name : dep ,
536+ ShortName : short ,
537+ Kind : "module" ,
538+ }
539+ entry = & synth
540+ }
541+ m .promptQueue = append (m .promptQueue , pendingDecision {
542+ PkgName : dep ,
543+ ShortName : short ,
544+ Action : "install" ,
545+ Kind : entry .Kind ,
546+ Entry : * entry ,
547+ })
548+ queued [dep ] = true
549+ }
470550 }
471551 }
472552
@@ -732,12 +812,13 @@ func (m *TuiModel) applyQueueChangesCmd() tea.Cmd {
732812 return taskFinishedMsg {Success : false , Err : err }
733813 }
734814
735- // Trigger setup task
815+ // Trigger setup task in background — return a log line so bubbletea
816+ // keeps taskActive=true until RunSetupTask sends taskFinishedMsg.
736817 if Program != nil {
737818 m .RunSetupTask (finalAdds , Program )
738819 }
739820
740- return nil
821+ return logLineMsg ( ">>> Workspace changes written. Running setup task…" )
741822 }
742823}
743824
@@ -3812,6 +3893,22 @@ func (m TuiModel) checkForUpdatesCmd() tea.Cmd {
38123893// Theme Dependencies & Package Metadata Helpers
38133894// ─────────────────────────────────────────────
38143895
3896+ // isLocallyAvailable checks if a package (by short name) exists as a local
3897+ // workspace directory under apps/, themes/, or packages/.
3898+ func isLocallyAvailable (workspaceRoot , shortName string ) bool {
3899+ candidates := []string {
3900+ filepath .Join (workspaceRoot , "apps" , shortName ),
3901+ filepath .Join (workspaceRoot , "themes" , shortName ),
3902+ filepath .Join (workspaceRoot , "packages" , shortName ),
3903+ }
3904+ for _ , p := range candidates {
3905+ if info , err := os .Stat (p ); err == nil && info .IsDir () {
3906+ return true
3907+ }
3908+ }
3909+ return false
3910+ }
3911+
38153912func getThemeDependencies (workspaceRoot , themeShortName string ) ([]string , error ) {
38163913 var pathsToCheck []string
38173914 pathsToCheck = append (pathsToCheck , filepath .Join (workspaceRoot , "themes" , themeShortName , "package.json" ))
0 commit comments