Files
gastown/internal/cmd/namepool.go
Steve Yegge 900a440ce8 feat(namepool): Add themed name pools for polecats
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>
2025-12-19 21:59:03 -08:00

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
}