Add mol-polecat-work, mol-polecat-lease, mol-gastown-boot formulas; fix spawn.go to use catalog ID

This commit is contained in:
Steve Yegge
2025-12-25 11:46:58 -08:00
parent 7a9d8104b0
commit 9446cd4806
6 changed files with 1803 additions and 1419 deletions

View File

@@ -197,6 +197,36 @@ Examples:
RunE: runCrewPristine,
}
var crewNextCmd = &cobra.Command{
Use: "next",
Short: "Switch to next crew session in same rig",
Long: `Switch to the next crew session in the same rig.
Cycles through crew sessions alphabetically. When you reach the last
crew, wraps around to the first.
This command is bound to C-b n in crew sessions for quick switching.
Examples:
gt crew next # Switch to next crew in same rig`,
RunE: runCrewNext,
}
var crewPrevCmd = &cobra.Command{
Use: "prev",
Short: "Switch to previous crew session in same rig",
Long: `Switch to the previous crew session in the same rig.
Cycles through crew sessions in reverse alphabetical order. When you
reach the first crew, wraps around to the last.
This command is bound to C-b p in crew sessions for quick switching.
Examples:
gt crew prev # Switch to previous crew in same rig`,
RunE: runCrewPrev,
}
func init() {
// Add flags
crewAddCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to create crew workspace in")
@@ -236,6 +266,8 @@ func init() {
crewCmd.AddCommand(crewRenameCmd)
crewCmd.AddCommand(crewPristineCmd)
crewCmd.AddCommand(crewRestartCmd)
crewCmd.AddCommand(crewNextCmd)
crewCmd.AddCommand(crewPrevCmd)
rootCmd.AddCommand(crewCmd)
}

View File

@@ -0,0 +1,79 @@
package cmd
import (
"fmt"
"os/exec"
"sort"
"github.com/spf13/cobra"
)
// cycleCrewSession switches to the next or previous crew session in the same rig.
// direction: 1 for next, -1 for previous
func cycleCrewSession(direction int) error {
// Get current session
currentSession := getCurrentTmuxSession()
if currentSession == "" {
return fmt.Errorf("not in a tmux session")
}
// Parse rig name from current session
rigName, _, ok := parseCrewSessionName(currentSession)
if !ok {
return fmt.Errorf("not in a crew session (expected gt-<rig>-crew-<name>)")
}
// Find all crew sessions for this rig
sessions, err := findRigCrewSessions(rigName)
if err != nil {
return fmt.Errorf("listing sessions: %w", err)
}
if len(sessions) == 0 {
return fmt.Errorf("no crew sessions found for rig %s", rigName)
}
// 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 {
// Current session not in list (shouldn't happen)
return fmt.Errorf("current session not found in crew list")
}
// Calculate target index (with wrapping)
targetIdx := (currentIdx + direction + len(sessions)) % len(sessions)
if targetIdx == currentIdx {
// Only one session, nothing to switch to
fmt.Printf("Only one crew session in rig %s\n", rigName)
return nil
}
targetSession := sessions[targetIdx]
// Switch to target session
cmd := exec.Command("tmux", "switch-client", "-t", targetSession)
if err := cmd.Run(); err != nil {
return fmt.Errorf("switching to %s: %w", targetSession, err)
}
return nil
}
func runCrewNext(cmd *cobra.Command, args []string) error {
return cycleCrewSession(1)
}
func runCrewPrev(cmd *cobra.Command, args []string) error {
return cycleCrewSession(-1)
}

View File

@@ -239,3 +239,74 @@ func ensureMainBranch(dir, roleName string) bool {
return true
}
// parseCrewSessionName extracts rig and crew name from a tmux session name.
// Format: gt-<rig>-crew-<name>
// Returns empty strings and false if the format doesn't match.
func parseCrewSessionName(sessionName string) (rigName, crewName string, ok bool) {
// Must start with "gt-" and contain "-crew-"
if !strings.HasPrefix(sessionName, "gt-") {
return "", "", false
}
// Remove "gt-" prefix
rest := sessionName[3:]
// Find "-crew-" separator
idx := strings.Index(rest, "-crew-")
if idx == -1 {
return "", "", false
}
rigName = rest[:idx]
crewName = rest[idx+6:] // len("-crew-") = 6
if rigName == "" || crewName == "" {
return "", "", false
}
return rigName, crewName, true
}
// getCurrentTmuxSession returns the current tmux session name.
// Returns empty string if not in tmux.
func getCurrentTmuxSession() string {
if os.Getenv("TMUX") == "" {
return ""
}
cmd := exec.Command("tmux", "display-message", "-p", "#{session_name}")
out, err := cmd.Output()
if err != nil {
return ""
}
return strings.TrimSpace(string(out))
}
// findRigCrewSessions returns all crew sessions for a given rig, sorted alphabetically.
// Uses tmux list-sessions to find sessions matching gt-<rig>-crew-* pattern.
func findRigCrewSessions(rigName string) ([]string, error) {
cmd := exec.Command("tmux", "list-sessions", "-F", "#{session_name}")
out, err := cmd.Output()
if err != nil {
// No tmux server or no sessions
return nil, nil
}
prefix := fmt.Sprintf("gt-%s-crew-", rigName)
var sessions []string
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
if line == "" {
continue
}
if strings.HasPrefix(line, prefix) {
sessions = append(sessions, line)
}
}
// Sessions are already sorted by tmux, but sort explicitly for consistency
// (alphabetical by session name means alphabetical by crew name)
return sessions, nil
}

View File

@@ -104,9 +104,8 @@ func runSpawn(cmd *cobra.Command, args []string) error {
// Auto-use mol-polecat-work for issue-based spawns (Phase 3: Polecat Work Cycle)
// This gives polecats a structured workflow with checkpoints for crash recovery.
// Can be overridden with explicit --molecule flag.
// Note: gt-lwuu is the proto ID for mol-polecat-work
if spawnIssue != "" && spawnMolecule == "" {
spawnMolecule = "gt-lwuu"
spawnMolecule = "mol-polecat-work"
}
// Find workspace first (needed for rig inference)