Polecats now get themed names from the Mad Max universe by default
(furiosa, nux, slit, etc.) instead of generic polecat-01, polecat-02.
Changes:
- Add NamepoolConfig to config/types.go for per-rig theme configuration
- Update namepool.go with three built-in themes:
- mad-max (default): furiosa, nux, imperator, etc.
- minerals: obsidian, quartz, ruby, etc.
- wasteland: rust, chrome, fury, etc.
- Add gt namepool commands: themes, set, add, reset
- Update manager.go to load namepool config from rig settings
Configuration in .gastown/config.json:
```json
{
"namepool": {
"style": "minerals",
"max_before_numbering": 50
}
}
```
Issue: beads-rs0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
311 lines
7.6 KiB
Go
311 lines
7.6 KiB
Go
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",
|
|
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 <theme>",
|
|
Short: "Set the namepool theme for this rig",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: runNamepoolSet,
|
|
}
|
|
|
|
var namepoolAddCmd = &cobra.Command{
|
|
Use: "add <name>",
|
|
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
|
|
configPath := filepath.Join(rigPath, ".gastown", "config.json")
|
|
if cfg, err := config.LoadRigConfig(configPath); err == nil && cfg.Namepool != nil {
|
|
fmt.Printf("(configured in .gastown/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 config.
|
|
func saveRigNamepoolConfig(rigPath, theme string, customNames []string) error {
|
|
configPath := filepath.Join(rigPath, ".gastown", "config.json")
|
|
|
|
// Load existing config or create new
|
|
var cfg *config.RigConfig
|
|
cfg, err := config.LoadRigConfig(configPath)
|
|
if err != nil {
|
|
// Create new config if not found
|
|
if os.IsNotExist(err) || strings.Contains(err.Error(), "not found") {
|
|
cfg = &config.RigConfig{
|
|
Type: "rig",
|
|
Version: config.CurrentRigConfigVersion,
|
|
}
|
|
} else {
|
|
return fmt.Errorf("loading config: %w", err)
|
|
}
|
|
}
|
|
|
|
// Set namepool
|
|
cfg.Namepool = &config.NamepoolConfig{
|
|
Style: theme,
|
|
Names: customNames,
|
|
}
|
|
|
|
// Ensure directory exists
|
|
if err := os.MkdirAll(filepath.Dir(configPath), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Save
|
|
if err := config.SaveRigConfig(configPath, cfg); err != nil {
|
|
return fmt.Errorf("saving config: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|