package commands import ( "fmt" "os" "github.com/muyue/muyue/internal/config" "github.com/muyue/muyue/internal/desktop" "github.com/muyue/muyue/internal/profiler" "github.com/spf13/cobra" ) var rootCmd = &cobra.Command{ Use: "muyue", Short: "Muyue is your AI-powered development companion", Long: `Muyue - A modern development environment with AI assistance, tool management, and seamless desktop integration.`, RunE: func(cmd *cobra.Command, args []string) error { cfg := loadOrSetupConfig() return desktop.Run(cfg, os.Args[1:]) }, } func Execute() error { return rootCmd.Execute() } // isInteractiveStdin reports whether os.Stdin is connected to a real terminal. // Used to decide between the TUI first-time setup (huh forms) and a no-op // fallback that defers onboarding to the web wizard. Returns false when the // binary is launched by a double-click on Windows (Explorer attaches a pseudo // console without a usable TTY) — which is the exact case where huh prints // "This is a command line tool. You need to open cmd.exe and run it from there." // and exits. func isInteractiveStdin() bool { stat, err := os.Stdin.Stat() if err != nil { return false } return (stat.Mode() & os.ModeCharDevice) != 0 } func loadOrSetupConfig() *config.MuyueConfig { if !config.Exists() { // No config yet. If we have a real terminal, run the rich TUI setup // (huh forms). Otherwise — typically when the user double-clicked the // shortcut on Windows — write defaults silently and let the React // onboarding wizard handle the real first-run flow once the browser // opens. This avoids huh aborting with "This is a command line tool". if isInteractiveStdin() { fmt.Println("First time setup detected!") cfg, err := profiler.RunFirstTimeSetup() if err != nil { fmt.Fprintf(os.Stderr, "Setup error: %v\n", err) os.Exit(1) } for i := range cfg.AI.Providers { if cfg.AI.Providers[i].Active && cfg.AI.Providers[i].APIKey == "" { key, err := profiler.AskAPIKey(cfg.AI.Providers[i].Name) if err == nil && key != "" { cfg.AI.Providers[i].APIKey = key } } } if err := config.Save(cfg); err != nil { fmt.Fprintf(os.Stderr, "Save error: %v\n", err) os.Exit(1) } fmt.Println("\nSetup complete! Starting muyue...") return cfg } // Non-interactive — skip the TUI, persist defaults, web onboarding // will fill in the profile / API keys. cfg := config.Default() if err := config.Save(cfg); err != nil { fmt.Fprintf(os.Stderr, "Save error: %v\n", err) os.Exit(1) } return cfg } cfg, err := config.Load() if err != nil { fmt.Fprintf(os.Stderr, "Config load error: %v\n", err) os.Exit(1) } return cfg } func init() { rootCmd.PersistentFlags().Int("port", 8080, "HTTP port for the desktop server") rootCmd.PersistentFlags().Bool("no-open", false, "Don't open browser on startup") }