Add gt start --all to start witnesses and refineries for all rigs
Simplifies startup from: gt start && gt witness start gastown && gt refinery start gastown && ... To: gt start --all Discovers all registered rigs and starts their witness and refinery sessions.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"github.com/steveyegge/gastown/internal/claude"
|
||||||
"github.com/steveyegge/gastown/internal/config"
|
"github.com/steveyegge/gastown/internal/config"
|
||||||
"github.com/steveyegge/gastown/internal/git"
|
"github.com/steveyegge/gastown/internal/git"
|
||||||
"github.com/steveyegge/gastown/internal/polecat"
|
"github.com/steveyegge/gastown/internal/polecat"
|
||||||
@@ -19,6 +20,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
startAll bool
|
||||||
shutdownGraceful bool
|
shutdownGraceful bool
|
||||||
shutdownWait int
|
shutdownWait int
|
||||||
shutdownAll bool
|
shutdownAll bool
|
||||||
@@ -35,7 +37,8 @@ var startCmd = &cobra.Command{
|
|||||||
The Deacon is the health-check orchestrator that monitors Mayor and Witnesses.
|
The Deacon is the health-check orchestrator that monitors Mayor and Witnesses.
|
||||||
The Mayor is the global coordinator that dispatches work.
|
The Mayor is the global coordinator that dispatches work.
|
||||||
|
|
||||||
Other agents (Witnesses, Refineries, Polecats) are started lazily as needed.
|
By default, other agents (Witnesses, Refineries) are started lazily as needed.
|
||||||
|
Use --all to start Witnesses and Refineries for all registered rigs immediately.
|
||||||
|
|
||||||
To stop Gas Town, use 'gt shutdown'.`,
|
To stop Gas Town, use 'gt shutdown'.`,
|
||||||
RunE: runStart,
|
RunE: runStart,
|
||||||
@@ -66,6 +69,9 @@ Use --nuclear to force cleanup even if polecats have uncommitted work (DANGER).`
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
startCmd.Flags().BoolVarP(&startAll, "all", "a", false,
|
||||||
|
"Also start Witnesses and Refineries for all rigs")
|
||||||
|
|
||||||
shutdownCmd.Flags().BoolVarP(&shutdownGraceful, "graceful", "g", false,
|
shutdownCmd.Flags().BoolVarP(&shutdownGraceful, "graceful", "g", false,
|
||||||
"Send ESC to agents and wait for them to handoff before killing")
|
"Send ESC to agents and wait for them to handoff before killing")
|
||||||
shutdownCmd.Flags().IntVarP(&shutdownWait, "wait", "w", 30,
|
shutdownCmd.Flags().IntVarP(&shutdownWait, "wait", "w", 30,
|
||||||
@@ -118,6 +124,47 @@ func runStart(cmd *cobra.Command, args []string) error {
|
|||||||
fmt.Printf(" %s Deacon started\n", style.Bold.Render("✓"))
|
fmt.Printf(" %s Deacon started\n", style.Bold.Render("✓"))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If --all, start witnesses and refineries for all rigs
|
||||||
|
if startAll {
|
||||||
|
fmt.Println()
|
||||||
|
fmt.Println("Starting rig agents...")
|
||||||
|
|
||||||
|
rigs, err := discoverAllRigs(townRoot)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" %s Could not discover rigs: %v\n", style.Dim.Render("○"), err)
|
||||||
|
} else {
|
||||||
|
for _, r := range rigs {
|
||||||
|
// Start Witness
|
||||||
|
witnessSession := fmt.Sprintf("gt-%s-witness", r.Name)
|
||||||
|
witnessRunning, _ := t.HasSession(witnessSession)
|
||||||
|
if witnessRunning {
|
||||||
|
fmt.Printf(" %s %s witness already running\n", style.Dim.Render("○"), r.Name)
|
||||||
|
} else {
|
||||||
|
created, err := ensureWitnessSession(r.Name, r)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" %s %s witness failed: %v\n", style.Dim.Render("○"), r.Name, err)
|
||||||
|
} else if created {
|
||||||
|
fmt.Printf(" %s %s witness started\n", style.Bold.Render("✓"), r.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start Refinery
|
||||||
|
refinerySession := fmt.Sprintf("gt-%s-refinery", r.Name)
|
||||||
|
refineryRunning, _ := t.HasSession(refinerySession)
|
||||||
|
if refineryRunning {
|
||||||
|
fmt.Printf(" %s %s refinery already running\n", style.Dim.Render("○"), r.Name)
|
||||||
|
} else {
|
||||||
|
created, err := ensureRefinerySession(r.Name, r)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf(" %s %s refinery failed: %v\n", style.Dim.Render("○"), r.Name, err)
|
||||||
|
} else if created {
|
||||||
|
fmt.Printf(" %s %s refinery started\n", style.Bold.Render("✓"), r.Name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
fmt.Printf("%s Gas Town is running\n", style.Bold.Render("✓"))
|
fmt.Printf("%s Gas Town is running\n", style.Bold.Render("✓"))
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
@@ -128,6 +175,76 @@ func runStart(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// discoverAllRigs finds all rigs in the workspace.
|
||||||
|
func discoverAllRigs(townRoot string) ([]*rig.Rig, error) {
|
||||||
|
rigsConfigPath := filepath.Join(townRoot, "mayor", "rigs.json")
|
||||||
|
rigsConfig, err := config.LoadRigsConfig(rigsConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("loading rigs config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
g := git.NewGit(townRoot)
|
||||||
|
rigMgr := rig.NewManager(townRoot, rigsConfig, g)
|
||||||
|
|
||||||
|
return rigMgr.DiscoverRigs()
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensureRefinerySession creates a refinery tmux session if it doesn't exist.
|
||||||
|
// Returns true if a new session was created, false if it already existed.
|
||||||
|
func ensureRefinerySession(rigName string, r *rig.Rig) (bool, error) {
|
||||||
|
t := tmux.NewTmux()
|
||||||
|
sessionName := fmt.Sprintf("gt-%s-refinery", rigName)
|
||||||
|
|
||||||
|
// Check if session already exists
|
||||||
|
running, err := t.HasSession(sessionName)
|
||||||
|
if err != nil {
|
||||||
|
return false, fmt.Errorf("checking session: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if running {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Working directory is the refinery's rig clone
|
||||||
|
refineryRigDir := filepath.Join(r.Path, "refinery", "rig")
|
||||||
|
if _, err := os.Stat(refineryRigDir); os.IsNotExist(err) {
|
||||||
|
// Fall back to rig path if refinery/rig doesn't exist
|
||||||
|
refineryRigDir = r.Path
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure Claude settings exist (autonomous role needs mail in SessionStart)
|
||||||
|
if err := claude.EnsureSettingsForRole(refineryRigDir, "refinery"); err != nil {
|
||||||
|
return false, fmt.Errorf("ensuring Claude settings: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create new tmux session
|
||||||
|
if err := t.NewSession(sessionName, refineryRigDir); err != nil {
|
||||||
|
return false, fmt.Errorf("creating session: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set environment
|
||||||
|
t.SetEnvironment(sessionName, "GT_ROLE", "refinery")
|
||||||
|
t.SetEnvironment(sessionName, "GT_RIG", rigName)
|
||||||
|
|
||||||
|
// Set beads environment
|
||||||
|
beadsDir := filepath.Join(r.Path, "mayor", "rig", ".beads")
|
||||||
|
t.SetEnvironment(sessionName, "BEADS_DIR", beadsDir)
|
||||||
|
t.SetEnvironment(sessionName, "BEADS_NO_DAEMON", "1")
|
||||||
|
t.SetEnvironment(sessionName, "BEADS_AGENT_NAME", fmt.Sprintf("%s/refinery", rigName))
|
||||||
|
|
||||||
|
// Apply Gas Town theming
|
||||||
|
theme := tmux.AssignTheme(rigName)
|
||||||
|
_ = t.ConfigureGasTownSession(sessionName, theme, rigName, "refinery", "refinery")
|
||||||
|
|
||||||
|
// Launch Claude in a respawn loop
|
||||||
|
loopCmd := `while true; do echo "🛢️ Starting Refinery for ` + rigName + `..."; claude --dangerously-skip-permissions; echo ""; echo "Refinery exited. Restarting in 2s... (Ctrl-C to stop)"; sleep 2; done`
|
||||||
|
if err := t.SendKeysDelayed(sessionName, loopCmd, 200); err != nil {
|
||||||
|
return false, fmt.Errorf("sending command: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
func runShutdown(cmd *cobra.Command, args []string) error {
|
func runShutdown(cmd *cobra.Command, args []string) error {
|
||||||
t := tmux.NewTmux()
|
t := tmux.NewTmux()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user