Allow `gt crew status <rig>` to work without requiring --rig flag. This matches the pattern already used by crew start and crew stop. Desire path: hq-v33hb Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
173 lines
4.4 KiB
Go
173 lines
4.4 KiB
Go
package cmd
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/spf13/cobra"
|
|
"github.com/steveyegge/gastown/internal/crew"
|
|
"github.com/steveyegge/gastown/internal/git"
|
|
"github.com/steveyegge/gastown/internal/mail"
|
|
"github.com/steveyegge/gastown/internal/style"
|
|
"github.com/steveyegge/gastown/internal/tmux"
|
|
)
|
|
|
|
// CrewStatusItem represents detailed status for a crew worker.
|
|
type CrewStatusItem struct {
|
|
Name string `json:"name"`
|
|
Rig string `json:"rig"`
|
|
Path string `json:"path"`
|
|
Branch string `json:"branch"`
|
|
HasSession bool `json:"has_session"`
|
|
SessionID string `json:"session_id,omitempty"`
|
|
GitClean bool `json:"git_clean"`
|
|
GitModified []string `json:"git_modified,omitempty"`
|
|
GitUntracked []string `json:"git_untracked,omitempty"`
|
|
MailTotal int `json:"mail_total"`
|
|
MailUnread int `json:"mail_unread"`
|
|
}
|
|
|
|
func runCrewStatus(cmd *cobra.Command, args []string) error {
|
|
// Parse rig/name format before getting manager (e.g., "beads/emma" -> rig=beads, name=emma)
|
|
var targetName string
|
|
if len(args) > 0 {
|
|
targetName = args[0]
|
|
if rig, crewName, ok := parseRigSlashName(targetName); ok {
|
|
if crewRig == "" {
|
|
crewRig = rig
|
|
}
|
|
targetName = crewName
|
|
} else if crewRig == "" {
|
|
// Check if single arg (without "/") is a valid rig name
|
|
// If so, show status for all crew in that rig
|
|
if _, _, err := getRig(targetName); err == nil {
|
|
crewRig = targetName
|
|
targetName = "" // Show all crew in the rig
|
|
}
|
|
}
|
|
}
|
|
|
|
crewMgr, r, err := getCrewManager(crewRig)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
var workers []*crew.CrewWorker
|
|
|
|
if targetName != "" {
|
|
// Specific worker
|
|
worker, err := crewMgr.Get(targetName)
|
|
if err != nil {
|
|
if err == crew.ErrCrewNotFound {
|
|
return fmt.Errorf("crew workspace '%s' not found", targetName)
|
|
}
|
|
return fmt.Errorf("getting crew worker: %w", err)
|
|
}
|
|
workers = []*crew.CrewWorker{worker}
|
|
} else {
|
|
// All workers
|
|
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
|
|
}
|
|
|
|
t := tmux.NewTmux()
|
|
var items []CrewStatusItem
|
|
|
|
for _, w := range workers {
|
|
sessionID := crewSessionName(r.Name, w.Name)
|
|
hasSession, _ := t.HasSession(sessionID)
|
|
|
|
// Git status
|
|
crewGit := git.NewGit(w.ClonePath)
|
|
gitStatus, _ := crewGit.Status()
|
|
branch, _ := crewGit.CurrentBranch()
|
|
|
|
gitClean := true
|
|
var modified, untracked []string
|
|
if gitStatus != nil {
|
|
gitClean = gitStatus.Clean
|
|
modified = append(gitStatus.Modified, gitStatus.Added...)
|
|
modified = append(modified, gitStatus.Deleted...)
|
|
untracked = gitStatus.Untracked
|
|
}
|
|
|
|
// Mail status (non-fatal: display defaults to 0 if count fails)
|
|
mailDir := filepath.Join(w.ClonePath, "mail")
|
|
mailTotal, mailUnread := 0, 0
|
|
if _, err := os.Stat(mailDir); err == nil {
|
|
mailbox := mail.NewMailbox(mailDir)
|
|
mailTotal, mailUnread, _ = mailbox.Count()
|
|
}
|
|
|
|
item := CrewStatusItem{
|
|
Name: w.Name,
|
|
Rig: r.Name,
|
|
Path: w.ClonePath,
|
|
Branch: branch,
|
|
HasSession: hasSession,
|
|
GitClean: gitClean,
|
|
GitModified: modified,
|
|
GitUntracked: untracked,
|
|
MailTotal: mailTotal,
|
|
MailUnread: mailUnread,
|
|
}
|
|
if hasSession {
|
|
item.SessionID = sessionID
|
|
}
|
|
|
|
items = append(items, item)
|
|
}
|
|
|
|
if crewJSON {
|
|
enc := json.NewEncoder(os.Stdout)
|
|
enc.SetIndent("", " ")
|
|
return enc.Encode(items)
|
|
}
|
|
|
|
// Text output
|
|
for i, item := range items {
|
|
if i > 0 {
|
|
fmt.Println()
|
|
}
|
|
|
|
sessionStatus := style.Dim.Render("○ stopped")
|
|
if item.HasSession {
|
|
sessionStatus = style.Bold.Render("● running")
|
|
}
|
|
|
|
fmt.Printf("%s %s/%s\n", sessionStatus, item.Rig, item.Name)
|
|
fmt.Printf(" Path: %s\n", item.Path)
|
|
fmt.Printf(" Branch: %s\n", item.Branch)
|
|
|
|
if item.GitClean {
|
|
fmt.Printf(" Git: %s\n", style.Dim.Render("clean"))
|
|
} else {
|
|
fmt.Printf(" Git: %s\n", style.Bold.Render("dirty"))
|
|
if len(item.GitModified) > 0 {
|
|
fmt.Printf(" Modified: %s\n", strings.Join(item.GitModified, ", "))
|
|
}
|
|
if len(item.GitUntracked) > 0 {
|
|
fmt.Printf(" Untracked: %s\n", strings.Join(item.GitUntracked, ", "))
|
|
}
|
|
}
|
|
|
|
if item.MailUnread > 0 {
|
|
fmt.Printf(" Mail: %d unread / %d total\n", item.MailUnread, item.MailTotal)
|
|
} else {
|
|
fmt.Printf(" Mail: %s\n", style.Dim.Render(fmt.Sprintf("%d messages", item.MailTotal)))
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|