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>
This commit is contained in:
310
internal/cmd/namepool.go
Normal file
310
internal/cmd/namepool.go
Normal file
@@ -0,0 +1,310 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user