From f808b3bd511718da9836a99325b061ac288eb262 Mon Sep 17 00:00:00 2001 From: Steve Yegge Date: Thu, 25 Dec 2025 11:33:54 -0800 Subject: [PATCH] fix: Parse rig/name format in crew commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All crew commands now accept "rig/name" syntax (e.g., "beads/emma") in addition to requiring --rig flag. The rig is extracted from the first path component. Affected commands: - gt crew at - gt crew restart - gt crew refresh - gt crew remove - gt crew rename - gt crew status - gt crew pristine 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/cmd/crew_at.go | 7 +++++++ internal/cmd/crew_helpers.go | 15 +++++++++++++++ internal/cmd/crew_lifecycle.go | 21 +++++++++++++++++++++ internal/cmd/crew_maintenance.go | 12 ++++++++++++ internal/cmd/crew_status.go | 19 +++++++++++++++---- 5 files changed, 70 insertions(+), 4 deletions(-) diff --git a/internal/cmd/crew_at.go b/internal/cmd/crew_at.go index 87af044e..de43aab8 100644 --- a/internal/cmd/crew_at.go +++ b/internal/cmd/crew_at.go @@ -19,6 +19,13 @@ func runCrewAt(cmd *cobra.Command, args []string) error { // Determine crew name: from arg, or auto-detect from cwd if len(args) > 0 { name = args[0] + // Parse rig/name format (e.g., "beads/emma" -> rig=beads, name=emma) + if rig, crewName, ok := parseRigSlashName(name); ok { + if crewRig == "" { + crewRig = rig + } + name = crewName + } } else { // Try to detect from current directory detected, err := detectCrewFromCwd() diff --git a/internal/cmd/crew_helpers.go b/internal/cmd/crew_helpers.go index d17aa7f7..03aef354 100644 --- a/internal/cmd/crew_helpers.go +++ b/internal/cmd/crew_helpers.go @@ -69,6 +69,21 @@ func crewSessionName(rigName, crewName string) string { return fmt.Sprintf("gt-%s-crew-%s", rigName, crewName) } +// parseRigSlashName parses "rig/name" format into separate rig and name parts. +// Returns (rig, name, true) if the format matches, or ("", original, false) if not. +// Examples: +// - "beads/emma" -> ("beads", "emma", true) +// - "emma" -> ("", "emma", false) +// - "beads/crew/emma" -> ("beads", "crew/emma", true) - only first slash splits +func parseRigSlashName(input string) (rig, name string, ok bool) { + // Only split on first slash to handle edge cases + idx := strings.Index(input, "/") + if idx == -1 { + return "", input, false + } + return input[:idx], input[idx+1:], true +} + // crewDetection holds the result of detecting crew workspace from cwd. type crewDetection struct { rigName string diff --git a/internal/cmd/crew_lifecycle.go b/internal/cmd/crew_lifecycle.go index c51368b5..9584d787 100644 --- a/internal/cmd/crew_lifecycle.go +++ b/internal/cmd/crew_lifecycle.go @@ -15,6 +15,13 @@ import ( func runCrewRemove(cmd *cobra.Command, args []string) error { name := args[0] + // Parse rig/name format (e.g., "beads/emma" -> rig=beads, name=emma) + if rig, crewName, ok := parseRigSlashName(name); ok { + if crewRig == "" { + crewRig = rig + } + name = crewName + } crewMgr, r, err := getCrewManager(crewRig) if err != nil { @@ -59,6 +66,13 @@ func runCrewRemove(cmd *cobra.Command, args []string) error { func runCrewRefresh(cmd *cobra.Command, args []string) error { name := args[0] + // Parse rig/name format (e.g., "beads/emma" -> rig=beads, name=emma) + if rig, crewName, ok := parseRigSlashName(name); ok { + if crewRig == "" { + crewRig = rig + } + name = crewName + } crewMgr, r, err := getCrewManager(crewRig) if err != nil { @@ -143,6 +157,13 @@ func runCrewRefresh(cmd *cobra.Command, args []string) error { func runCrewRestart(cmd *cobra.Command, args []string) error { name := args[0] + // Parse rig/name format (e.g., "beads/emma" -> rig=beads, name=emma) + if rig, crewName, ok := parseRigSlashName(name); ok { + if crewRig == "" { + crewRig = rig + } + name = crewName + } crewMgr, r, err := getCrewManager(crewRig) if err != nil { diff --git a/internal/cmd/crew_maintenance.go b/internal/cmd/crew_maintenance.go index 709ea129..7665515f 100644 --- a/internal/cmd/crew_maintenance.go +++ b/internal/cmd/crew_maintenance.go @@ -14,6 +14,14 @@ import ( func runCrewRename(cmd *cobra.Command, args []string) error { oldName := args[0] newName := args[1] + // Parse rig/name format for oldName (e.g., "beads/emma" -> rig=beads, name=emma) + if rig, crewName, ok := parseRigSlashName(oldName); ok { + if crewRig == "" { + crewRig = rig + } + oldName = crewName + } + // Note: newName is just the new name, no rig prefix expected crewMgr, r, err := getCrewManager(crewRig) if err != nil { @@ -59,6 +67,10 @@ func runCrewPristine(cmd *cobra.Command, args []string) error { if len(args) > 0 { // Specific worker name := args[0] + // Parse rig/name format (e.g., "beads/emma" -> rig=beads, name=emma) + if _, crewName, ok := parseRigSlashName(name); ok { + name = crewName + } worker, err := crewMgr.Get(name) if err != nil { if err == crew.ErrCrewNotFound { diff --git a/internal/cmd/crew_status.go b/internal/cmd/crew_status.go index 28379046..0c2f4b49 100644 --- a/internal/cmd/crew_status.go +++ b/internal/cmd/crew_status.go @@ -31,6 +31,18 @@ type CrewStatusItem struct { } 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 + } + } + crewMgr, r, err := getCrewManager(crewRig) if err != nil { return err @@ -38,13 +50,12 @@ func runCrewStatus(cmd *cobra.Command, args []string) error { var workers []*crew.CrewWorker - if len(args) > 0 { + if targetName != "" { // Specific worker - name := args[0] - worker, err := crewMgr.Get(name) + worker, err := crewMgr.Get(targetName) if err != nil { if err == crew.ErrCrewNotFound { - return fmt.Errorf("crew workspace '%s' not found", name) + return fmt.Errorf("crew workspace '%s' not found", targetName) } return fmt.Errorf("getting crew worker: %w", err) }