diff --git a/internal/cmd/crew.go b/internal/cmd/crew.go index 0d9f4761..523f0cc3 100644 --- a/internal/cmd/crew.go +++ b/internal/cmd/crew.go @@ -239,27 +239,29 @@ var crewPrevCmd = &cobra.Command{ } var crewStartCmd = &cobra.Command{ - Use: "start [name...]", - Short: "Start crew workspace(s) (creates if needed)", - Long: `Start one or more crew workspaces, creating them if they don't exist. + Use: "start [name]", + Short: "Start crew worker(s) in a rig", + Long: `Start crew workers in a rig, creating workspaces if they don't exist. + +Takes the rig name as the first argument. Optionally specify a crew member name +to start just that worker, or use --all to start all crew members in the rig. -This is an alias for 'gt start crew'. It combines 'gt crew add' and 'gt crew at --detached'. The crew session starts in the background with Claude running and ready. -The name can include the rig in slash format (e.g., greenplace/joe). -If not specified, the rig is inferred from the current directory. - -Role Discovery: - If no name is provided, attempts to detect the crew workspace from the - current directory. If you're in /crew//, it will start that - workspace automatically. - Examples: - gt crew start joe # Start joe in current rig - gt crew start greenplace/joe # Start joe in gastown rig - gt crew start beads/grip beads/fang # Start multiple crew members - gt crew start joe --rig beads # Start joe in beads rig - gt crew start # Auto-detect from cwd`, + gt crew start gastown joe # Start joe in gastown rig + gt crew start gastown --all # Start all crew in gastown rig + gt crew start beads # Error: specify name or --all + gt crew start beads grip fang # Start grip and fang in beads rig`, + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return fmt.Errorf("requires at least 1 argument: the rig name") + } + if len(args) == 1 && !crewAll { + return fmt.Errorf("specify a crew member name or use --all to start all crew in the rig") + } + return nil + }, RunE: runCrewStart, } @@ -294,7 +296,7 @@ func init() { crewRestartCmd.Flags().BoolVar(&crewAll, "all", false, "Restart all running crew sessions") crewRestartCmd.Flags().BoolVar(&crewDryRun, "dry-run", false, "Show what would be restarted without restarting") - crewStartCmd.Flags().StringVar(&crewRig, "rig", "", "Rig to use") + crewStartCmd.Flags().BoolVar(&crewAll, "all", false, "Start all crew members in the rig") crewStartCmd.Flags().StringVar(&crewAccount, "account", "", "Claude Code account handle to use") // Add subcommands diff --git a/internal/cmd/crew_lifecycle.go b/internal/cmd/crew_lifecycle.go index c2398970..d652ecc6 100644 --- a/internal/cmd/crew_lifecycle.go +++ b/internal/cmd/crew_lifecycle.go @@ -216,48 +216,57 @@ func runCrewRefresh(cmd *cobra.Command, args []string) error { return nil } -// runCrewStart is an alias for runStartCrew, handling multiple input formats. -// It supports: "name", "rig/name", "rig/crew/name" formats, or auto-detection from cwd. -// Multiple names can be provided to start multiple crew members at once. +// runCrewStart starts crew workers in a rig. +// args[0] is the rig name (required) +// args[1:] are crew member names (optional, or use --all flag) func runCrewStart(cmd *cobra.Command, args []string) error { - // If no args, try to detect from current directory - if len(args) == 0 { - detected, err := detectCrewFromCwd() - if err != nil { - return fmt.Errorf("could not detect crew workspace from current directory: %w\n\nUsage: gt crew start ", err) - } - name := detected.crewName - if crewRig == "" { - crewRig = detected.rigName - } - fmt.Printf("Detected crew workspace: %s/%s\n", detected.rigName, name) + rigName := args[0] + crewNames := args[1:] - startCrewRig = crewRig - startCrewAccount = crewAccount - return runStartCrew(cmd, []string{name}) + // Get the rig manager and rig + crewMgr, r, err := getCrewManager(rigName) + if err != nil { + return err } - // Process each name - var lastErr error - for _, name := range args { - // Handle rig/crew/name format (e.g., "gastown/crew/joe" -> "gastown/joe") - if strings.Contains(name, "/crew/") { - parts := strings.SplitN(name, "/crew/", 2) - if len(parts) == 2 && parts[0] != "" && parts[1] != "" { - name = parts[0] + "/" + parts[1] - } + // If --all flag, get all crew members + if crewAll { + workers, err := crewMgr.List() + if err != nil { + return fmt.Errorf("listing crew: %w", err) } + if len(workers) == 0 { + fmt.Printf("No crew members in rig %s\n", rigName) + return nil + } + for _, w := range workers { + crewNames = append(crewNames, w.Name) + } + } - // Set the start.go flags from crew.go flags before calling - startCrewRig = crewRig + // Start each crew member + var lastErr error + startedCount := 0 + for _, name := range crewNames { + // Set the start.go flags before calling runStartCrew + startCrewRig = rigName startCrewAccount = crewAccount - if err := runStartCrew(cmd, []string{name}); err != nil { - fmt.Printf("Error starting %s: %v\n", name, err) + // Use rig/name format for runStartCrew + fullName := rigName + "/" + name + if err := runStartCrew(cmd, []string{fullName}); err != nil { + fmt.Printf("Error starting %s/%s: %v\n", rigName, name, err) lastErr = err + } else { + startedCount++ } } + if startedCount > 0 { + fmt.Printf("\n%s Started %d crew member(s) in %s\n", + style.Bold.Render("✓"), startedCount, r.Name) + } + return lastErr }