package cmd import ( "fmt" "os" "path/filepath" "strings" "github.com/spf13/cobra" "github.com/steveyegge/gastown/internal/config" "github.com/steveyegge/gastown/internal/polecat" "github.com/steveyegge/gastown/internal/workspace" ) var ( namepoolListFlag bool namepoolThemeFlag string ) var namepoolCmd = &cobra.Command{ Use: "namepool", GroupID: GroupWorkspace, Short: "Manage polecat name pools", Long: `Manage themed name pools for polecats in Gas Town. By default, polecats get themed names from the Mad Max universe (furiosa, nux, slit, etc.). You can change the theme or add custom names. Examples: gt namepool # Show current pool status gt namepool --list # List available themes gt namepool themes # Show theme names gt namepool set minerals # Set theme to 'minerals' gt namepool add ember # Add custom name to pool gt namepool reset # Reset pool state`, RunE: runNamepool, } var namepoolThemesCmd = &cobra.Command{ Use: "themes [theme]", Short: "List available themes and their names", RunE: runNamepoolThemes, } var namepoolSetCmd = &cobra.Command{ Use: "set ", Short: "Set the namepool theme for this rig", Args: cobra.ExactArgs(1), RunE: runNamepoolSet, } var namepoolAddCmd = &cobra.Command{ Use: "add ", Short: "Add a custom name to the pool", Args: cobra.ExactArgs(1), RunE: runNamepoolAdd, } var namepoolResetCmd = &cobra.Command{ Use: "reset", Short: "Reset the pool state (release all names)", RunE: runNamepoolReset, } func init() { rootCmd.AddCommand(namepoolCmd) namepoolCmd.AddCommand(namepoolThemesCmd) namepoolCmd.AddCommand(namepoolSetCmd) namepoolCmd.AddCommand(namepoolAddCmd) namepoolCmd.AddCommand(namepoolResetCmd) namepoolCmd.Flags().BoolVarP(&namepoolListFlag, "list", "l", false, "List available themes") } func runNamepool(cmd *cobra.Command, args []string) error { // List themes mode if namepoolListFlag { return runNamepoolThemes(cmd, nil) } // Show current pool status rigName, rigPath := detectCurrentRigWithPath() if rigName == "" { return fmt.Errorf("not in a rig directory") } // Load pool pool := polecat.NewNamePool(rigPath, rigName) if err := pool.Load(); err != nil { // Pool doesn't exist yet, show defaults fmt.Printf("Rig: %s\n", rigName) fmt.Printf("Theme: %s (default)\n", polecat.DefaultTheme) fmt.Printf("Active polecats: 0\n") fmt.Printf("Max pool size: %d\n", polecat.DefaultPoolSize) return nil } // Show pool status fmt.Printf("Rig: %s\n", rigName) fmt.Printf("Theme: %s\n", pool.GetTheme()) fmt.Printf("Active polecats: %d\n", pool.ActiveCount()) activeNames := pool.ActiveNames() if len(activeNames) > 0 { fmt.Printf("In use: %s\n", strings.Join(activeNames, ", ")) } // Check if configured settingsPath := filepath.Join(rigPath, "settings", "config.json") if settings, err := config.LoadRigSettings(settingsPath); err == nil && settings.Namepool != nil { fmt.Printf("(configured in settings/config.json)\n") } return nil } func runNamepoolThemes(cmd *cobra.Command, args []string) error { themes := polecat.ListThemes() if len(args) == 0 { // List all themes fmt.Println("Available themes:") for _, theme := range themes { names, _ := polecat.GetThemeNames(theme) fmt.Printf("\n %s (%d names):\n", theme, len(names)) // Show first 10 names preview := names if len(preview) > 10 { preview = preview[:10] } fmt.Printf(" %s...\n", strings.Join(preview, ", ")) } return nil } // Show specific theme names theme := args[0] names, err := polecat.GetThemeNames(theme) if err != nil { return fmt.Errorf("unknown theme: %s (available: %s)", theme, strings.Join(themes, ", ")) } fmt.Printf("Theme: %s (%d names)\n\n", theme, len(names)) for i, name := range names { if i > 0 && i%5 == 0 { fmt.Println() } fmt.Printf(" %-12s", name) } fmt.Println() return nil } func runNamepoolSet(cmd *cobra.Command, args []string) error { theme := args[0] // Validate theme themes := polecat.ListThemes() valid := false for _, t := range themes { if t == theme { valid = true break } } if !valid { return fmt.Errorf("unknown theme: %s (available: %s)", theme, strings.Join(themes, ", ")) } // Get rig rigName, rigPath := detectCurrentRigWithPath() if rigName == "" { return fmt.Errorf("not in a rig directory") } // Update pool pool := polecat.NewNamePool(rigPath, rigName) if err := pool.Load(); err != nil && !os.IsNotExist(err) { return fmt.Errorf("loading pool: %w", err) } if err := pool.SetTheme(theme); err != nil { return err } if err := pool.Save(); err != nil { return fmt.Errorf("saving pool: %w", err) } // Also save to rig config if err := saveRigNamepoolConfig(rigPath, theme, nil); err != nil { return fmt.Errorf("saving config: %w", err) } fmt.Printf("Theme '%s' set for rig '%s'\n", theme, rigName) fmt.Printf("New polecats will use names from this theme.\n") return nil } func runNamepoolAdd(cmd *cobra.Command, args []string) error { name := args[0] rigName, rigPath := detectCurrentRigWithPath() if rigName == "" { return fmt.Errorf("not in a rig directory") } // Load pool pool := polecat.NewNamePool(rigPath, rigName) if err := pool.Load(); err != nil && !os.IsNotExist(err) { return fmt.Errorf("loading pool: %w", err) } pool.AddCustomName(name) if err := pool.Save(); err != nil { return fmt.Errorf("saving pool: %w", err) } fmt.Printf("Added '%s' to the name pool\n", name) return nil } func runNamepoolReset(cmd *cobra.Command, args []string) error { rigName, rigPath := detectCurrentRigWithPath() if rigName == "" { return fmt.Errorf("not in a rig directory") } // Load pool pool := polecat.NewNamePool(rigPath, rigName) if err := pool.Load(); err != nil && !os.IsNotExist(err) { return fmt.Errorf("loading pool: %w", err) } pool.Reset() if err := pool.Save(); err != nil { return fmt.Errorf("saving pool: %w", err) } fmt.Printf("Pool reset for rig '%s'\n", rigName) fmt.Printf("All names released and available for reuse.\n") return nil } // detectCurrentRigWithPath determines the rig name and path from cwd. func detectCurrentRigWithPath() (string, string) { cwd, err := os.Getwd() if err != nil { return "", "" } townRoot, err := workspace.FindFromCwd() if err != nil || townRoot == "" { return "", "" } // Get path relative to town root rel, err := filepath.Rel(townRoot, cwd) if err != nil { return "", "" } // Extract first path component (rig name) parts := strings.Split(rel, string(filepath.Separator)) if len(parts) > 0 && parts[0] != "." && parts[0] != "mayor" && parts[0] != "deacon" { return parts[0], filepath.Join(townRoot, parts[0]) } return "", "" } // saveRigNamepoolConfig saves the namepool config to rig settings. func saveRigNamepoolConfig(rigPath, theme string, customNames []string) error { settingsPath := filepath.Join(rigPath, "settings", "config.json") // Load existing settings or create new var settings *config.RigSettings settings, err := config.LoadRigSettings(settingsPath) if err != nil { // Create new settings if not found if os.IsNotExist(err) || strings.Contains(err.Error(), "not found") { settings = config.NewRigSettings() } else { return fmt.Errorf("loading settings: %w", err) } } // Set namepool settings.Namepool = &config.NamepoolConfig{ Style: theme, Names: customNames, } // Save (creates directory if needed) if err := config.SaveRigSettings(settingsPath, settings); err != nil { return fmt.Errorf("saving settings: %w", err) } return nil }