Add persistent theme config and fix crew session theming
- Fix crew sessions missing theme application (ConfigureGasTownSession) - Add theme persistence to .gastown/config.json - gt theme <name> now saves to config - gt theme apply reads from config, falls back to hash-based default - Improve rig detection using GT_RIG env var and path parsing - gt theme shows whether theme is configured or default 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -506,6 +506,10 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
||||
_ = t.SetEnvironment(sessionID, "GT_RIG", r.Name)
|
||||
_ = t.SetEnvironment(sessionID, "GT_CREW", name)
|
||||
|
||||
// Apply rig-based theming (uses config if set, falls back to hash)
|
||||
theme := getThemeForRig(r.Name)
|
||||
_ = t.ConfigureGasTownSession(sessionID, theme, r.Name, name, "crew")
|
||||
|
||||
// Wait for shell to be ready after session creation
|
||||
if err := t.WaitForShellReady(sessionID, 5*time.Second); err != nil {
|
||||
return fmt.Errorf("waiting for shell: %w", err)
|
||||
@@ -847,6 +851,10 @@ func runCrewRestart(cmd *cobra.Command, args []string) error {
|
||||
t.SetEnvironment(sessionID, "GT_RIG", r.Name)
|
||||
t.SetEnvironment(sessionID, "GT_CREW", name)
|
||||
|
||||
// Apply rig-based theming (uses config if set, falls back to hash)
|
||||
theme := getThemeForRig(r.Name)
|
||||
_ = t.ConfigureGasTownSession(sessionID, theme, r.Name, name, "crew")
|
||||
|
||||
// Wait for shell to be ready
|
||||
if err := t.WaitForShellReady(sessionID, 5*time.Second); err != nil {
|
||||
return fmt.Errorf("waiting for shell: %w", err)
|
||||
|
||||
@@ -2,10 +2,14 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/steveyegge/gastown/internal/config"
|
||||
"github.com/steveyegge/gastown/internal/tmux"
|
||||
"github.com/steveyegge/gastown/internal/workspace"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -63,9 +67,15 @@ func runTheme(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// Show current theme assignment
|
||||
if len(args) == 0 {
|
||||
theme := tmux.AssignTheme(rigName)
|
||||
theme := getThemeForRig(rigName)
|
||||
fmt.Printf("Rig: %s\n", rigName)
|
||||
fmt.Printf("Theme: %s (%s)\n", theme.Name, theme.Style())
|
||||
// Show if it's configured vs default
|
||||
if configured := loadRigTheme(rigName); configured != "" {
|
||||
fmt.Printf("(configured in .gastown/config.json)\n")
|
||||
} else {
|
||||
fmt.Printf("(default, based on rig name hash)\n")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -76,10 +86,13 @@ func runTheme(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("unknown theme: %s (use --list to see available themes)", themeName)
|
||||
}
|
||||
|
||||
// TODO: Save to rig config.json
|
||||
fmt.Printf("Theme '%s' selected for rig '%s'\n", themeName, rigName)
|
||||
fmt.Println("Note: Run 'gt theme apply' to apply to running sessions")
|
||||
fmt.Println("(Persistent config not yet implemented)")
|
||||
// Save to rig config
|
||||
if err := saveRigTheme(rigName, themeName); err != nil {
|
||||
return fmt.Errorf("saving theme config: %w", err)
|
||||
}
|
||||
|
||||
fmt.Printf("Theme '%s' saved for rig '%s'\n", themeName, rigName)
|
||||
fmt.Println("Run 'gt theme apply' to apply to running sessions")
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -133,7 +146,8 @@ func runThemeApply(cmd *cobra.Command, args []string) error {
|
||||
role = "polecat"
|
||||
}
|
||||
|
||||
theme = tmux.AssignTheme(rig)
|
||||
// Use configured theme, fall back to hash-based assignment
|
||||
theme = getThemeForRig(rig)
|
||||
}
|
||||
|
||||
// Apply theme and status format
|
||||
@@ -165,29 +179,115 @@ func runThemeApply(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// detectCurrentRig determines the rig from environment or cwd.
|
||||
func detectCurrentRig() string {
|
||||
// Try environment first
|
||||
if rig := detectCurrentSession(); rig != "" {
|
||||
// Extract rig from session name
|
||||
parts := strings.SplitN(rig, "-", 3)
|
||||
if len(parts) >= 2 && parts[0] == "gt" {
|
||||
// Try environment first (GT_RIG is set in tmux sessions)
|
||||
if rig := os.Getenv("GT_RIG"); rig != "" {
|
||||
return rig
|
||||
}
|
||||
|
||||
// Try to extract from tmux session name
|
||||
if session := detectCurrentSession(); session != "" {
|
||||
// Extract rig from session name: gt-<rig>-...
|
||||
parts := strings.SplitN(session, "-", 3)
|
||||
if len(parts) >= 2 && parts[0] == "gt" && parts[1] != "mayor" && parts[1] != "deacon" {
|
||||
return parts[1]
|
||||
}
|
||||
}
|
||||
|
||||
// Try to detect from cwd
|
||||
cwd, err := findBeadsWorkDir()
|
||||
// Try to detect from actual cwd path
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Extract rig name from path
|
||||
// Typical paths: /Users/stevey/gt/<rig>/...
|
||||
parts := strings.Split(cwd, "/")
|
||||
for i, p := range parts {
|
||||
if p == "gt" && i+1 < len(parts) {
|
||||
return parts[i+1]
|
||||
}
|
||||
// Find town root to extract rig name
|
||||
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)
|
||||
// Patterns: <rig>/..., mayor/..., deacon/...
|
||||
parts := strings.Split(rel, string(filepath.Separator))
|
||||
if len(parts) > 0 && parts[0] != "." && parts[0] != "mayor" && parts[0] != "deacon" {
|
||||
return parts[0]
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// getThemeForRig returns the theme for a rig, checking config first.
|
||||
func getThemeForRig(rigName string) tmux.Theme {
|
||||
// Try to load configured theme
|
||||
if themeName := loadRigTheme(rigName); themeName != "" {
|
||||
if theme := tmux.GetThemeByName(themeName); theme != nil {
|
||||
return *theme
|
||||
}
|
||||
}
|
||||
// Fall back to hash-based assignment
|
||||
return tmux.AssignTheme(rigName)
|
||||
}
|
||||
|
||||
// loadRigTheme loads the theme name from rig config.
|
||||
func loadRigTheme(rigName string) string {
|
||||
townRoot, err := workspace.FindFromCwd()
|
||||
if err != nil || townRoot == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
configPath := filepath.Join(townRoot, rigName, ".gastown", "config.json")
|
||||
cfg, err := config.LoadRigConfig(configPath)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if cfg.Theme != nil && cfg.Theme.Name != "" {
|
||||
return cfg.Theme.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// saveRigTheme saves the theme name to rig config.
|
||||
func saveRigTheme(rigName, themeName string) error {
|
||||
townRoot, err := workspace.FindFromCwd()
|
||||
if err != nil {
|
||||
return fmt.Errorf("finding workspace: %w", err)
|
||||
}
|
||||
if townRoot == "" {
|
||||
return fmt.Errorf("not in a Gas Town workspace")
|
||||
}
|
||||
|
||||
configPath := filepath.Join(townRoot, rigName, ".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 theme
|
||||
cfg.Theme = &config.ThemeConfig{
|
||||
Name: themeName,
|
||||
}
|
||||
|
||||
// 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