Merge pull request #276 from joshuavial/feat/crew-list-all

feat: add --all to gt crew list
This commit is contained in:
Steve Yegge
2026-01-08 17:23:23 -08:00
committed by GitHub
3 changed files with 179 additions and 27 deletions

View File

@@ -19,6 +19,7 @@ var (
crewAccount string
crewAgentOverride string
crewAll bool
crewListAll bool
crewDryRun bool
)
@@ -77,7 +78,8 @@ Shows git branch, session state, and git status for each workspace.
Examples:
gt crew list # List in current rig
gt crew list --rig greenplace # List in specific rig
gt crew list --rig greenplace # List in specific rig
gt crew list --all # List in all rigs
gt crew list --json # JSON output`,
RunE: runCrewList,
}
@@ -323,6 +325,7 @@ func init() {
crewAddCmd.Flags().BoolVar(&crewBranch, "branch", false, "Create a feature branch (crew/<name>)")
crewListCmd.Flags().StringVar(&crewRig, "rig", "", "Filter by rig name")
crewListCmd.Flags().BoolVar(&crewListAll, "all", false, "List crew workspaces in all rigs")
crewListCmd.Flags().BoolVar(&crewJSON, "json", false, "Output as JSON")
crewAtCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to use")

View File

@@ -6,7 +6,9 @@ import (
"os"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/crew"
"github.com/steveyegge/gastown/internal/git"
"github.com/steveyegge/gastown/internal/rig"
"github.com/steveyegge/gastown/internal/style"
"github.com/steveyegge/gastown/internal/tmux"
)
@@ -22,43 +24,63 @@ type CrewListItem struct {
}
func runCrewList(cmd *cobra.Command, args []string) error {
crewMgr, r, err := getCrewManager(crewRig)
if err != nil {
return err
if crewListAll && crewRig != "" {
return fmt.Errorf("cannot use --all with --rig")
}
workers, err := crewMgr.List()
if err != nil {
return fmt.Errorf("listing crew workers: %w", err)
}
if len(workers) == 0 {
fmt.Println("No crew workspaces found.")
return nil
var rigs []*rig.Rig
if crewListAll {
allRigs, _, err := getAllRigs()
if err != nil {
return err
}
rigs = allRigs
} else {
_, r, err := getCrewManager(crewRig)
if err != nil {
return err
}
rigs = []*rig.Rig{r}
}
// Check session and git status for each worker
t := tmux.NewTmux()
var items []CrewListItem
for _, w := range workers {
sessionID := crewSessionName(r.Name, w.Name)
hasSession, _ := t.HasSession(sessionID)
for _, r := range rigs {
crewGit := git.NewGit(r.Path)
crewMgr := crew.NewManager(r, crewGit)
crewGit := git.NewGit(w.ClonePath)
gitClean := true
if status, err := crewGit.Status(); err == nil {
gitClean = status.Clean
workers, err := crewMgr.List()
if err != nil {
fmt.Fprintf(os.Stderr, "warning: failed to list crew workers in %s: %v\n", r.Name, err)
continue
}
items = append(items, CrewListItem{
Name: w.Name,
Rig: r.Name,
Branch: w.Branch,
Path: w.ClonePath,
HasSession: hasSession,
GitClean: gitClean,
})
for _, w := range workers {
sessionID := crewSessionName(r.Name, w.Name)
hasSession, _ := t.HasSession(sessionID)
workerGit := git.NewGit(w.ClonePath)
gitClean := true
if status, err := workerGit.Status(); err == nil {
gitClean = status.Clean
}
items = append(items, CrewListItem{
Name: w.Name,
Rig: r.Name,
Branch: w.Branch,
Path: w.ClonePath,
HasSession: hasSession,
GitClean: gitClean,
})
}
}
if len(items) == 0 {
fmt.Println("No crew workspaces found.")
return nil
}
if crewJSON {

View File

@@ -0,0 +1,127 @@
package cmd
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
"github.com/spf13/cobra"
"github.com/steveyegge/gastown/internal/config"
)
func setupTestTownForCrewList(t *testing.T, rigs map[string][]string) string {
t.Helper()
townRoot := t.TempDir()
mayorDir := filepath.Join(townRoot, "mayor")
if err := os.MkdirAll(mayorDir, 0755); err != nil {
t.Fatalf("mkdir mayor: %v", err)
}
townConfig := &config.TownConfig{
Type: "town",
Version: config.CurrentTownVersion,
Name: "test-town",
PublicName: "Test Town",
CreatedAt: time.Date(2024, 1, 1, 0, 0, 0, 0, time.UTC),
}
if err := config.SaveTownConfig(filepath.Join(mayorDir, "town.json"), townConfig); err != nil {
t.Fatalf("save town.json: %v", err)
}
rigsConfig := &config.RigsConfig{
Version: config.CurrentRigsVersion,
Rigs: make(map[string]config.RigEntry),
}
for rigName, crewNames := range rigs {
rigsConfig.Rigs[rigName] = config.RigEntry{
GitURL: "https://example.com/" + rigName + ".git",
AddedAt: time.Now(),
}
rigPath := filepath.Join(townRoot, rigName)
crewDir := filepath.Join(rigPath, "crew")
if err := os.MkdirAll(crewDir, 0755); err != nil {
t.Fatalf("mkdir crew dir: %v", err)
}
for _, crewName := range crewNames {
if err := os.MkdirAll(filepath.Join(crewDir, crewName), 0755); err != nil {
t.Fatalf("mkdir crew worker: %v", err)
}
}
}
if err := config.SaveRigsConfig(filepath.Join(mayorDir, "rigs.json"), rigsConfig); err != nil {
t.Fatalf("save rigs.json: %v", err)
}
return townRoot
}
func TestRunCrewList_AllWithRigErrors(t *testing.T) {
townRoot := setupTestTownForCrewList(t, map[string][]string{"rig-a": {"alice"}})
originalWd, _ := os.Getwd()
defer os.Chdir(originalWd)
if err := os.Chdir(townRoot); err != nil {
t.Fatalf("chdir: %v", err)
}
crewListAll = true
crewRig = "rig-a"
defer func() {
crewListAll = false
crewRig = ""
}()
err := runCrewList(&cobra.Command{}, nil)
if err == nil {
t.Fatal("expected error for --all with --rig, got nil")
}
}
func TestRunCrewList_AllAggregatesJSON(t *testing.T) {
townRoot := setupTestTownForCrewList(t, map[string][]string{
"rig-a": {"alice"},
"rig-b": {"bob"},
})
originalWd, _ := os.Getwd()
defer os.Chdir(originalWd)
if err := os.Chdir(townRoot); err != nil {
t.Fatalf("chdir: %v", err)
}
crewListAll = true
crewJSON = true
crewRig = ""
defer func() {
crewListAll = false
crewJSON = false
}()
output := captureStdout(t, func() {
if err := runCrewList(&cobra.Command{}, nil); err != nil {
t.Fatalf("runCrewList failed: %v", err)
}
})
var items []CrewListItem
if err := json.Unmarshal([]byte(output), &items); err != nil {
t.Fatalf("unmarshal output: %v", err)
}
if len(items) != 2 {
t.Fatalf("expected 2 crew workers, got %d", len(items))
}
rigs := map[string]bool{}
for _, item := range items {
rigs[item.Rig] = true
}
if !rigs["rig-a"] || !rigs["rig-b"] {
t.Fatalf("expected crew from rig-a and rig-b, got: %#v", rigs)
}
}