feat(polecat): Add bulk removal with --all and multi-polecat support (gt-hcc0)
- Add --all flag to remove all polecats from a rig: `gt polecat remove gastown --all` - Support multiple rig/polecat args: `gt polecat remove gastown/A gastown/B` - Report summary of removed polecats and any failures - Validate that --all is not used with rig/polecat format 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -22,9 +22,10 @@ import (
|
|||||||
|
|
||||||
// Polecat command flags
|
// Polecat command flags
|
||||||
var (
|
var (
|
||||||
polecatListJSON bool
|
polecatListJSON bool
|
||||||
polecatListAll bool
|
polecatListAll bool
|
||||||
polecatForce bool
|
polecatForce bool
|
||||||
|
polecatRemoveAll bool
|
||||||
)
|
)
|
||||||
|
|
||||||
var polecatCmd = &cobra.Command{
|
var polecatCmd = &cobra.Command{
|
||||||
@@ -70,18 +71,20 @@ Example:
|
|||||||
}
|
}
|
||||||
|
|
||||||
var polecatRemoveCmd = &cobra.Command{
|
var polecatRemoveCmd = &cobra.Command{
|
||||||
Use: "remove <rig>/<polecat>",
|
Use: "remove <rig>/<polecat>... | <rig> --all",
|
||||||
Short: "Remove a polecat from a rig",
|
Short: "Remove polecats from a rig",
|
||||||
Long: `Remove a polecat from a rig.
|
Long: `Remove one or more polecats from a rig.
|
||||||
|
|
||||||
Fails if session is running (stop first).
|
Fails if session is running (stop first).
|
||||||
Warns if uncommitted changes exist.
|
Warns if uncommitted changes exist.
|
||||||
Use --force to bypass checks.
|
Use --force to bypass checks.
|
||||||
|
|
||||||
Example:
|
Examples:
|
||||||
gt polecat remove gastown/Toast
|
gt polecat remove gastown/Toast
|
||||||
gt polecat remove gastown/Toast --force`,
|
gt polecat remove gastown/Toast gastown/Furiosa
|
||||||
Args: cobra.ExactArgs(1),
|
gt polecat remove gastown --all
|
||||||
|
gt polecat remove gastown --all --force`,
|
||||||
|
Args: cobra.MinimumNArgs(1),
|
||||||
RunE: runPolecatRemove,
|
RunE: runPolecatRemove,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,6 +205,7 @@ func init() {
|
|||||||
|
|
||||||
// Remove flags
|
// Remove flags
|
||||||
polecatRemoveCmd.Flags().BoolVarP(&polecatForce, "force", "f", false, "Force removal, bypassing checks")
|
polecatRemoveCmd.Flags().BoolVarP(&polecatForce, "force", "f", false, "Force removal, bypassing checks")
|
||||||
|
polecatRemoveCmd.Flags().BoolVar(&polecatRemoveAll, "all", false, "Remove all polecats in the rig")
|
||||||
|
|
||||||
// Sync flags
|
// Sync flags
|
||||||
polecatSyncCmd.Flags().BoolVar(&polecatSyncAll, "all", false, "Sync all polecats in the rig")
|
polecatSyncCmd.Flags().BoolVar(&polecatSyncAll, "all", false, "Sync all polecats in the rig")
|
||||||
@@ -384,36 +388,115 @@ func runPolecatAdd(cmd *cobra.Command, args []string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runPolecatRemove(cmd *cobra.Command, args []string) error {
|
func runPolecatRemove(cmd *cobra.Command, args []string) error {
|
||||||
rigName, polecatName, err := parseAddress(args[0])
|
// Build list of polecats to remove
|
||||||
if err != nil {
|
type polecatToRemove struct {
|
||||||
return err
|
rigName string
|
||||||
|
polecatName string
|
||||||
|
mgr *polecat.Manager
|
||||||
|
r *rig.Rig
|
||||||
}
|
}
|
||||||
|
var toRemove []polecatToRemove
|
||||||
|
|
||||||
mgr, r, err := getPolecatManager(rigName)
|
if polecatRemoveAll {
|
||||||
if err != nil {
|
// --all flag: first arg is just the rig name
|
||||||
return err
|
rigName := args[0]
|
||||||
}
|
// Check if it looks like rig/polecat format
|
||||||
|
if _, _, err := parseAddress(rigName); err == nil {
|
||||||
|
return fmt.Errorf("with --all, provide just the rig name (e.g., 'gt polecat remove gastown --all')")
|
||||||
|
}
|
||||||
|
|
||||||
// Check if session is running
|
mgr, r, err := getPolecatManager(rigName)
|
||||||
if !polecatForce {
|
if err != nil {
|
||||||
t := tmux.NewTmux()
|
return err
|
||||||
sessMgr := session.NewManager(t, r)
|
}
|
||||||
running, _ := sessMgr.IsRunning(polecatName)
|
|
||||||
if running {
|
polecats, err := mgr.List()
|
||||||
return fmt.Errorf("session is running. Stop it first with: gt session stop %s/%s", rigName, polecatName)
|
if err != nil {
|
||||||
|
return fmt.Errorf("listing polecats: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(polecats) == 0 {
|
||||||
|
fmt.Println("No polecats to remove.")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, p := range polecats {
|
||||||
|
toRemove = append(toRemove, polecatToRemove{
|
||||||
|
rigName: rigName,
|
||||||
|
polecatName: p.Name,
|
||||||
|
mgr: mgr,
|
||||||
|
r: r,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Multiple rig/polecat arguments
|
||||||
|
for _, arg := range args {
|
||||||
|
rigName, polecatName, err := parseAddress(arg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("invalid address '%s': %w", arg, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
mgr, r, err := getPolecatManager(rigName)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
toRemove = append(toRemove, polecatToRemove{
|
||||||
|
rigName: rigName,
|
||||||
|
polecatName: polecatName,
|
||||||
|
mgr: mgr,
|
||||||
|
r: r,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("Removing polecat %s/%s...\n", rigName, polecatName)
|
// Remove each polecat
|
||||||
|
t := tmux.NewTmux()
|
||||||
|
var removeErrors []string
|
||||||
|
removed := 0
|
||||||
|
|
||||||
if err := mgr.Remove(polecatName, polecatForce); err != nil {
|
for _, p := range toRemove {
|
||||||
if errors.Is(err, polecat.ErrHasChanges) {
|
// Check if session is running
|
||||||
return fmt.Errorf("polecat has uncommitted changes. Use --force to remove anyway")
|
if !polecatForce {
|
||||||
|
sessMgr := session.NewManager(t, p.r)
|
||||||
|
running, _ := sessMgr.IsRunning(p.polecatName)
|
||||||
|
if running {
|
||||||
|
removeErrors = append(removeErrors, fmt.Sprintf("%s/%s: session is running (stop first or use --force)", p.rigName, p.polecatName))
|
||||||
|
continue
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return fmt.Errorf("removing polecat: %w", err)
|
|
||||||
|
fmt.Printf("Removing polecat %s/%s...\n", p.rigName, p.polecatName)
|
||||||
|
|
||||||
|
if err := p.mgr.Remove(p.polecatName, polecatForce); err != nil {
|
||||||
|
if errors.Is(err, polecat.ErrHasChanges) {
|
||||||
|
removeErrors = append(removeErrors, fmt.Sprintf("%s/%s: has uncommitted changes (use --force)", p.rigName, p.polecatName))
|
||||||
|
} else {
|
||||||
|
removeErrors = append(removeErrors, fmt.Sprintf("%s/%s: %v", p.rigName, p.polecatName, err))
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf(" %s removed\n", style.Success.Render("✓"))
|
||||||
|
removed++
|
||||||
|
}
|
||||||
|
|
||||||
|
// Report results
|
||||||
|
if len(removeErrors) > 0 {
|
||||||
|
fmt.Printf("\n%s Some removals failed:\n", style.Warning.Render("Warning:"))
|
||||||
|
for _, e := range removeErrors {
|
||||||
|
fmt.Printf(" - %s\n", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if removed > 0 {
|
||||||
|
fmt.Printf("\n%s Removed %d polecat(s).\n", style.SuccessPrefix, removed)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(removeErrors) > 0 {
|
||||||
|
return fmt.Errorf("%d removal(s) failed", len(removeErrors))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("%s Polecat %s removed.\n", style.SuccessPrefix, polecatName)
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user