Merge origin/main
This commit is contained in:
@@ -101,12 +101,10 @@ func runCrewAt(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Apply rig-based theming (non-fatal: theming failure doesn't affect operation)
|
||||
// Note: ConfigureGasTownSession includes cycle bindings
|
||||
theme := getThemeForRig(r.Name)
|
||||
_ = t.ConfigureGasTownSession(sessionID, theme, r.Name, name, "crew")
|
||||
|
||||
// Set up C-b n/p keybindings for crew session cycling (non-fatal)
|
||||
_ = t.SetCrewCycleBindings(sessionID)
|
||||
|
||||
// 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)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
@@ -28,6 +31,7 @@ var cycleCmd = &cobra.Command{
|
||||
Session groups:
|
||||
- Town sessions: Mayor ↔ Deacon
|
||||
- Crew sessions: All crew members in the same rig (e.g., gastown/crew/max ↔ gastown/crew/joe)
|
||||
- Rig infra sessions: Witness ↔ Refinery (per rig)
|
||||
|
||||
The appropriate cycling is detected automatically from the session name.`,
|
||||
}
|
||||
@@ -83,6 +87,89 @@ func cycleToSession(direction int, sessionOverride string) error {
|
||||
return cycleCrewSession(direction, session)
|
||||
}
|
||||
|
||||
// Unknown session type (polecat, witness, refinery) - do nothing
|
||||
// Check if it's a rig infra session (witness or refinery)
|
||||
if rig := parseRigInfraSession(session); rig != "" {
|
||||
return cycleRigInfraSession(direction, session, rig)
|
||||
}
|
||||
|
||||
// Unknown session type (polecat) - do nothing
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseRigInfraSession extracts rig name if this is a witness or refinery session.
|
||||
// Returns empty string if not a rig infra session.
|
||||
// Format: gt-<rig>-witness or gt-<rig>-refinery
|
||||
func parseRigInfraSession(session string) string {
|
||||
if !strings.HasPrefix(session, "gt-") {
|
||||
return ""
|
||||
}
|
||||
rest := session[3:] // Remove "gt-" prefix
|
||||
|
||||
// Check for -witness or -refinery suffix
|
||||
if strings.HasSuffix(rest, "-witness") {
|
||||
return strings.TrimSuffix(rest, "-witness")
|
||||
}
|
||||
if strings.HasSuffix(rest, "-refinery") {
|
||||
return strings.TrimSuffix(rest, "-refinery")
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// cycleRigInfraSession cycles between witness and refinery sessions for a rig.
|
||||
func cycleRigInfraSession(direction int, currentSession, rig string) error {
|
||||
// Find running infra sessions for this rig
|
||||
witnessSession := fmt.Sprintf("gt-%s-witness", rig)
|
||||
refinerySession := fmt.Sprintf("gt-%s-refinery", rig)
|
||||
|
||||
var sessions []string
|
||||
allSessions, err := listTmuxSessions()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, s := range allSessions {
|
||||
if s == witnessSession || s == refinerySession {
|
||||
sessions = append(sessions, s)
|
||||
}
|
||||
}
|
||||
|
||||
if len(sessions) == 0 {
|
||||
return nil // No infra sessions running
|
||||
}
|
||||
|
||||
// Sort for consistent ordering
|
||||
sort.Strings(sessions)
|
||||
|
||||
// Find current position
|
||||
currentIdx := -1
|
||||
for i, s := range sessions {
|
||||
if s == currentSession {
|
||||
currentIdx = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if currentIdx == -1 {
|
||||
return nil // Current session not in list
|
||||
}
|
||||
|
||||
// Calculate target index (with wrapping)
|
||||
targetIdx := (currentIdx + direction + len(sessions)) % len(sessions)
|
||||
|
||||
if targetIdx == currentIdx {
|
||||
return nil // Only one session
|
||||
}
|
||||
|
||||
// Switch to target session
|
||||
cmd := exec.Command("tmux", "switch-client", "-t", sessions[targetIdx])
|
||||
return cmd.Run()
|
||||
}
|
||||
|
||||
// listTmuxSessions returns all tmux session names.
|
||||
func listTmuxSessions() ([]string, error) {
|
||||
out, err := exec.Command("tmux", "list-sessions", "-F", "#{session_name}").Output()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return splitLines(string(out)), nil
|
||||
}
|
||||
|
||||
@@ -185,12 +185,10 @@ func startDeaconSession(t *tmux.Tmux) error {
|
||||
_ = t.SetEnvironment(DeaconSessionName, "BD_ACTOR", "deacon")
|
||||
|
||||
// Apply Deacon theme (non-fatal: theming failure doesn't affect operation)
|
||||
// Note: ConfigureGasTownSession includes cycle bindings
|
||||
theme := tmux.DeaconTheme()
|
||||
_ = t.ConfigureGasTownSession(DeaconSessionName, theme, "", "Deacon", "health-check")
|
||||
|
||||
// Set up C-b n/p keybindings for town session cycling (non-fatal)
|
||||
_ = t.SetTownCycleBindings(DeaconSessionName)
|
||||
|
||||
// Launch Claude directly (no shell respawn loop)
|
||||
// Restarts are handled by daemon via ensureDeaconRunning on each heartbeat
|
||||
// The startup hook handles context loading automatically
|
||||
|
||||
@@ -75,6 +75,12 @@ Examples:
|
||||
}
|
||||
|
||||
func runFeed(cmd *cobra.Command, args []string) error {
|
||||
// Must be in a Gas Town workspace
|
||||
townRoot, err := workspace.FindFromCwdOrError()
|
||||
if err != nil {
|
||||
return fmt.Errorf("not in a Gas Town workspace (run from ~/gt or a rig directory)")
|
||||
}
|
||||
|
||||
// Determine working directory
|
||||
workDir, err := os.Getwd()
|
||||
if err != nil {
|
||||
@@ -83,11 +89,6 @@ func runFeed(cmd *cobra.Command, args []string) error {
|
||||
|
||||
// If --rig specified, find that rig's beads directory
|
||||
if feedRig != "" {
|
||||
townRoot, err := workspace.FindFromCwdOrError()
|
||||
if err != nil {
|
||||
return fmt.Errorf("not in a Gas Town workspace: %w", err)
|
||||
}
|
||||
|
||||
// Try common beads locations for the rig
|
||||
candidates := []string{
|
||||
fmt.Sprintf("%s/%s/mayor/rig", townRoot, feedRig),
|
||||
|
||||
@@ -123,12 +123,10 @@ func startMayorSession(t *tmux.Tmux) error {
|
||||
_ = t.SetEnvironment(MayorSessionName, "BD_ACTOR", "mayor")
|
||||
|
||||
// Apply Mayor theme (non-fatal: theming failure doesn't affect operation)
|
||||
// Note: ConfigureGasTownSession includes cycle bindings
|
||||
theme := tmux.MayorTheme()
|
||||
_ = t.ConfigureGasTownSession(MayorSessionName, theme, "", "Mayor", "coordinator")
|
||||
|
||||
// Set up C-b n/p keybindings for town session cycling (non-fatal)
|
||||
_ = t.SetTownCycleBindings(MayorSessionName)
|
||||
|
||||
// Launch Claude - the startup hook handles 'gt prime' automatically
|
||||
// Use SendKeysDelayed to allow shell initialization after NewSession
|
||||
// Export GT_ROLE and BD_ACTOR in the command since tmux SetEnvironment only affects new panes
|
||||
|
||||
@@ -747,7 +747,7 @@ func runStartCrew(cmd *cobra.Command, args []string) error {
|
||||
return fmt.Errorf("restarting claude: %w", err)
|
||||
}
|
||||
// Wait for Claude to start, then prime
|
||||
shells := []string{"bash", "zsh", "sh", "fish", "tcsh", "ksh"}
|
||||
shells := constants.SupportedShells
|
||||
if err := t.WaitForCommand(sessionID, shells, 15*time.Second); err != nil {
|
||||
style.PrintWarning("Timeout waiting for Claude to start: %v", err)
|
||||
}
|
||||
@@ -774,12 +774,10 @@ func runStartCrew(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Apply rig-based theming (non-fatal: theming failure doesn't affect operation)
|
||||
// Note: ConfigureGasTownSession includes cycle bindings
|
||||
theme := getThemeForRig(rigName)
|
||||
_ = t.ConfigureGasTownSession(sessionID, theme, rigName, name, "crew")
|
||||
|
||||
// Set up C-b n/p keybindings for crew session cycling (non-fatal)
|
||||
_ = t.SetCrewCycleBindings(sessionID)
|
||||
|
||||
// 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)
|
||||
@@ -791,7 +789,7 @@ func runStartCrew(cmd *cobra.Command, args []string) error {
|
||||
}
|
||||
|
||||
// Wait for Claude to start
|
||||
shells := []string{"bash", "zsh", "sh", "fish", "tcsh", "ksh"}
|
||||
shells := constants.SupportedShells
|
||||
if err := t.WaitForCommand(sessionID, shells, 15*time.Second); err != nil {
|
||||
style.PrintWarning("Timeout waiting for Claude to start: %v", err)
|
||||
}
|
||||
@@ -925,7 +923,7 @@ func startCrewMember(rigName, crewName, townRoot string) error {
|
||||
}
|
||||
|
||||
// Wait for Claude to start
|
||||
shells := []string{"bash", "zsh", "sh", "fish", "tcsh", "ksh"}
|
||||
shells := constants.SupportedShells
|
||||
if err := t.WaitForCommand(sessionID, shells, 15*time.Second); err != nil {
|
||||
// Non-fatal: Claude might still be starting
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user