diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 91f73beaf6..375c589dce 100755 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -165,7 +165,11 @@ func New(config *Config) (*Daemon, error) { logger.Printf("Warning: failed to initialize town registry: %v", err) } - // Set GT_TOWN_ROOT in tmux global environment so run-shell subprocesses + // Set GT_TOWN_ROOT in the daemon process env so Go code (e.g., + // sessionPrefixPattern) can read it without relying on GT_ROOT. + os.Setenv("GT_TOWN_ROOT", config.TownRoot) + + // Also set GT_TOWN_ROOT in tmux global environment so run-shell subprocesses // (e.g., gt cycle next/prev) can find the workspace even when CWD is $HOME. // Non-fatal: tmux server may not be running yet — daemon creates sessions shortly. t := tmux.NewTmux() diff --git a/internal/tmux/tmux.go b/internal/tmux/tmux.go index 49ff7662ea..0eb7a0cb6c 100644 --- a/internal/tmux/tmux.go +++ b/internal/tmux/tmux.go @@ -3426,6 +3426,9 @@ var safePrefixRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9-]{0,19}$`) func sessionPrefixPattern() string { seen := map[string]bool{"hq": true, "gt": true} // always include HQ + gastown fallback townRoot := os.Getenv("GT_ROOT") + if townRoot == "" { + townRoot = os.Getenv("GT_TOWN_ROOT") + } if townRoot != "" { for _, p := range config.AllRigPrefixes(townRoot) { if safePrefixRe.MatchString(p) { diff --git a/internal/tmux/tmux_test.go b/internal/tmux/tmux_test.go index 3e1d86ddde..cb14926c88 100644 --- a/internal/tmux/tmux_test.go +++ b/internal/tmux/tmux_test.go @@ -2146,6 +2146,38 @@ func TestSessionPrefixPattern_WithTownRoot(t *testing.T) { } } +func TestSessionPrefixPattern_FallsBackToGTTownRoot(t *testing.T) { + // When GT_ROOT is empty but GT_TOWN_ROOT is set, sessionPrefixPattern + // should use GT_TOWN_ROOT to discover rig prefixes. + townRoot := os.Getenv("GT_ROOT") + if townRoot == "" { + townRoot = os.Getenv("GT_TOWN_ROOT") + } + if townRoot == "" { + t.Skip("neither GT_ROOT nor GT_TOWN_ROOT set; skipping") + } + + // Clear GT_ROOT, set GT_TOWN_ROOT — simulates daemon startup env. + t.Setenv("GT_ROOT", "") + t.Setenv("GT_TOWN_ROOT", townRoot) + + pattern := sessionPrefixPattern() + if !strings.Contains(pattern, "gt") { + t.Errorf("pattern %q missing 'gt'", pattern) + } + if !strings.Contains(pattern, "hq") { + t.Errorf("pattern %q missing 'hq'", pattern) + } + // With a real rigs.json via GT_TOWN_ROOT, we expect more than just gt+hq. + // At minimum there should be 3+ prefixes in a multi-rig town. + inner := strings.TrimPrefix(pattern, "^(") + inner = strings.TrimSuffix(inner, ")-") + prefixes := strings.Split(inner, "|") + if len(prefixes) < 3 { + t.Errorf("expected at least 3 prefixes via GT_TOWN_ROOT fallback, got %d: %v", len(prefixes), prefixes) + } +} + func TestZombieStatusString(t *testing.T) { t.Parallel() tests := []struct {