Add crew rename, pristine commands and status integration

- gt crew rename: Rename crew workspace (kills session, renames dir)
- gt crew pristine: Sync crew workspaces with git pull + bd sync
- gt status: Show crew workers per rig with 👤 indicator

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Steve Yegge
2025-12-18 19:20:41 -08:00
parent e91a15396b
commit 915594c44c
4 changed files with 266 additions and 0 deletions

View File

@@ -150,6 +150,36 @@ Examples:
RunE: runCrewStatus,
}
var crewRenameCmd = &cobra.Command{
Use: "rename <old-name> <new-name>",
Short: "Rename a crew workspace",
Long: `Rename a crew workspace.
Kills any running session, renames the directory, and updates state.
The new session will use the new name (gt-<rig>-crew-<new-name>).
Examples:
gt crew rename dave david # Rename dave to david
gt crew rename madmax max # Rename madmax to max`,
Args: cobra.ExactArgs(2),
RunE: runCrewRename,
}
var crewPristineCmd = &cobra.Command{
Use: "pristine [<name>]",
Short: "Sync crew workspaces with remote",
Long: `Ensure crew workspace(s) are up-to-date.
Runs git pull and bd sync for the specified crew, or all crew workers.
Reports any uncommitted changes that may need attention.
Examples:
gt crew pristine # Pristine all crew workers
gt crew pristine dave # Pristine specific worker
gt crew pristine --json # JSON output`,
RunE: runCrewPristine,
}
func init() {
// Add flags
crewAddCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to create crew workspace in")
@@ -170,6 +200,11 @@ func init() {
crewStatusCmd.Flags().StringVar(&crewRig, "rig", "", "Filter by rig name")
crewStatusCmd.Flags().BoolVar(&crewJSON, "json", false, "Output as JSON")
crewRenameCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to use")
crewPristineCmd.Flags().StringVar(&crewRig, "rig", "", "Filter by rig name")
crewPristineCmd.Flags().BoolVar(&crewJSON, "json", false, "Output as JSON")
// Add subcommands
crewCmd.AddCommand(crewAddCmd)
crewCmd.AddCommand(crewListCmd)
@@ -177,6 +212,8 @@ func init() {
crewCmd.AddCommand(crewRemoveCmd)
crewCmd.AddCommand(crewRefreshCmd)
crewCmd.AddCommand(crewStatusCmd)
crewCmd.AddCommand(crewRenameCmd)
crewCmd.AddCommand(crewPristineCmd)
rootCmd.AddCommand(crewCmd)
}
@@ -819,3 +856,112 @@ func runCrewStatus(cmd *cobra.Command, args []string) error {
return nil
}
func runCrewRename(cmd *cobra.Command, args []string) error {
oldName := args[0]
newName := args[1]
crewMgr, r, err := getCrewManager(crewRig)
if err != nil {
return err
}
// Kill any running session for the old name
t := tmux.NewTmux()
oldSessionID := crewSessionName(r.Name, oldName)
if hasSession, _ := t.HasSession(oldSessionID); hasSession {
if err := t.KillSession(oldSessionID); err != nil {
return fmt.Errorf("killing old session: %w", err)
}
fmt.Printf("Killed session %s\n", oldSessionID)
}
// Perform the rename
if err := crewMgr.Rename(oldName, newName); err != nil {
if err == crew.ErrCrewNotFound {
return fmt.Errorf("crew workspace '%s' not found", oldName)
}
if err == crew.ErrCrewExists {
return fmt.Errorf("crew workspace '%s' already exists", newName)
}
return fmt.Errorf("renaming crew workspace: %w", err)
}
fmt.Printf("%s Renamed crew workspace: %s/%s → %s/%s\n",
style.Bold.Render("✓"), r.Name, oldName, r.Name, newName)
fmt.Printf("New session will be: %s\n", style.Dim.Render(crewSessionName(r.Name, newName)))
return nil
}
func runCrewPristine(cmd *cobra.Command, args []string) error {
crewMgr, r, err := getCrewManager(crewRig)
if err != nil {
return err
}
var workers []*crew.CrewWorker
if len(args) > 0 {
// Specific worker
name := args[0]
worker, err := crewMgr.Get(name)
if err != nil {
if err == crew.ErrCrewNotFound {
return fmt.Errorf("crew workspace '%s' not found", name)
}
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
}
var results []*crew.PristineResult
for _, w := range workers {
result, err := crewMgr.Pristine(w.Name)
if err != nil {
return fmt.Errorf("pristine %s: %w", w.Name, err)
}
results = append(results, result)
}
if crewJSON {
enc := json.NewEncoder(os.Stdout)
enc.SetIndent("", " ")
return enc.Encode(results)
}
// Text output
for _, result := range results {
fmt.Printf("%s %s/%s\n", style.Bold.Render("→"), r.Name, result.Name)
if result.HadChanges {
fmt.Printf(" %s\n", style.Bold.Render("⚠ Has uncommitted changes"))
}
if result.Pulled {
fmt.Printf(" %s git pull\n", style.Dim.Render("✓"))
} else if result.PullError != "" {
fmt.Printf(" %s git pull: %s\n", style.Bold.Render("✗"), result.PullError)
}
if result.Synced {
fmt.Printf(" %s bd sync\n", style.Dim.Render("✓"))
} else if result.SyncError != "" {
fmt.Printf(" %s bd sync: %s\n", style.Bold.Render("✗"), result.SyncError)
}
}
return nil
}