From 1dc31024ca2e1fff45ff6157ea6683f59ed484b7 Mon Sep 17 00:00:00 2001 From: gastown/crew/max Date: Wed, 21 Jan 2026 21:19:04 -0800 Subject: [PATCH] feat(mail): accept multiple message IDs in delete command Allow `gt mail delete` to accept multiple message IDs at once, matching the existing behavior of archive, mark-read, and mark-unread. Also adds --body as an alias for --message in mail reply. Co-Authored-By: Claude Opus 4.5 --- internal/cmd/mail.go | 15 ++++++++++----- internal/cmd/mail_inbox.go | 32 ++++++++++++++++++++++++-------- internal/cmd/mail_thread.go | 3 +++ 3 files changed, 37 insertions(+), 13 deletions(-) diff --git a/internal/cmd/mail.go b/internal/cmd/mail.go index 13c5f22a..334d18fe 100644 --- a/internal/cmd/mail.go +++ b/internal/cmd/mail.go @@ -176,12 +176,16 @@ Exits silently with code 1 if no unread messages.`, } var mailDeleteCmd = &cobra.Command{ - Use: "delete ", - Short: "Delete a message", - Long: `Delete (acknowledge) a message. + Use: "delete [message-id...]", + Short: "Delete messages", + Long: `Delete (acknowledge) one or more messages. -This closes the message in beads.`, - Args: cobra.ExactArgs(1), +This closes the messages in beads. + +Examples: + gt mail delete hq-abc123 + gt mail delete hq-abc123 hq-def456 hq-ghi789`, + Args: cobra.MinimumNArgs(1), RunE: runMailDelete, } @@ -461,6 +465,7 @@ func init() { // Reply flags mailReplyCmd.Flags().StringVarP(&mailReplySubject, "subject", "s", "", "Override reply subject (default: Re: )") mailReplyCmd.Flags().StringVarP(&mailReplyMessage, "message", "m", "", "Reply message body") + mailReplyCmd.Flags().StringVar(&mailReplyMessage, "body", "", "Reply message body (alias for --message)") // Search flags mailSearchCmd.Flags().StringVar(&mailSearchFrom, "from", "", "Filter by sender address") diff --git a/internal/cmd/mail_inbox.go b/internal/cmd/mail_inbox.go index 2f865203..99c07f6e 100644 --- a/internal/cmd/mail_inbox.go +++ b/internal/cmd/mail_inbox.go @@ -224,11 +224,6 @@ func runMailPeek(cmd *cobra.Command, args []string) error { } func runMailDelete(cmd *cobra.Command, args []string) error { - if len(args) == 0 { - return errors.New("msgID argument required") - } - msgID := args[0] - // Determine which inbox address := detectSender() @@ -237,11 +232,32 @@ func runMailDelete(cmd *cobra.Command, args []string) error { return err } - if err := mailbox.Delete(msgID); err != nil { - return fmt.Errorf("deleting message: %w", err) + // Delete all specified messages + deleted := 0 + var errors []string + for _, msgID := range args { + if err := mailbox.Delete(msgID); err != nil { + errors = append(errors, fmt.Sprintf("%s: %v", msgID, err)) + } else { + deleted++ + } } - fmt.Printf("%s Message deleted\n", style.Bold.Render("✓")) + // Report results + if len(errors) > 0 { + fmt.Printf("%s Deleted %d/%d messages\n", + style.Bold.Render("⚠"), deleted, len(args)) + for _, e := range errors { + fmt.Printf(" Error: %s\n", e) + } + return fmt.Errorf("failed to delete %d messages", len(errors)) + } + + if len(args) == 1 { + fmt.Printf("%s Message deleted\n", style.Bold.Render("✓")) + } else { + fmt.Printf("%s Deleted %d messages\n", style.Bold.Render("✓"), deleted) + } return nil } diff --git a/internal/cmd/mail_thread.go b/internal/cmd/mail_thread.go index 181a8b4f..33b45f7d 100644 --- a/internal/cmd/mail_thread.go +++ b/internal/cmd/mail_thread.go @@ -80,6 +80,9 @@ func runMailThread(cmd *cobra.Command, args []string) error { } func runMailReply(cmd *cobra.Command, args []string) error { + if mailReplyMessage == "" { + return fmt.Errorf("required flag \"message\" or \"body\" not set") + } msgID := args[0] // Get message body from positional arg or flag (positional takes precedence)