From 844504686e54df496502875a77cc48e1e27e9160 Mon Sep 17 00:00:00 2001 From: gastown/polecats/capable Date: Thu, 1 Jan 2026 19:03:29 -0800 Subject: [PATCH] feat(mail): Add gt mail clear command for bulk inbox clearing (gt-o0xbp) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds gt mail clear [target] command that: - Clears all messages from an inbox - Supports clearing your own inbox (no args) or another agent's - Reports count of deleted messages - Handles partial failures gracefully Use case: Town quiescence - reset all inboxes across workers efficiently. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- internal/cmd/mail.go | 84 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) diff --git a/internal/cmd/mail.go b/internal/cmd/mail.go index 5f4f010e..ae16aa4f 100644 --- a/internal/cmd/mail.go +++ b/internal/cmd/mail.go @@ -294,6 +294,30 @@ Examples: RunE: runMailRelease, } +var mailClearCmd = &cobra.Command{ + Use: "clear [target]", + Short: "Clear all messages from an inbox", + Long: `Clear (delete) all messages from an inbox. + +SYNTAX: + gt mail clear # Clear your own inbox + gt mail clear # Clear another agent's inbox + +BEHAVIOR: +1. List all messages in the target inbox +2. Delete each message +3. Print count of deleted messages + +Use case: Town quiescence - reset all inboxes across workers efficiently. + +Examples: + gt mail clear # Clear your inbox + gt mail clear gastown/polecats/joe # Clear joe's inbox + gt mail clear mayor/ # Clear mayor's inbox`, + Args: cobra.MaximumNArgs(1), + RunE: runMailClear, +} + func init() { // Send flags mailSendCmd.Flags().StringVarP(&mailSubject, "subject", "s", "", "Message subject (required)") @@ -345,6 +369,7 @@ func init() { mailCmd.AddCommand(mailReplyCmd) mailCmd.AddCommand(mailClaimCmd) mailCmd.AddCommand(mailReleaseCmd) + mailCmd.AddCommand(mailClearCmd) rootCmd.AddCommand(mailCmd) } @@ -744,6 +769,65 @@ func runMailArchive(cmd *cobra.Command, args []string) error { return nil } +func runMailClear(cmd *cobra.Command, args []string) error { + // Determine which inbox to clear (target arg or auto-detect) + address := "" + if len(args) > 0 { + address = args[0] + } else { + address = detectSender() + } + + // All mail uses town beads (two-level architecture) + workDir, err := findMailWorkDir() + if err != nil { + return fmt.Errorf("not in a Gas Town workspace: %w", err) + } + + // Get mailbox + router := mail.NewRouter(workDir) + mailbox, err := router.GetMailbox(address) + if err != nil { + return fmt.Errorf("getting mailbox: %w", err) + } + + // List all messages + messages, err := mailbox.List() + if err != nil { + return fmt.Errorf("listing messages: %w", err) + } + + if len(messages) == 0 { + fmt.Printf("%s Inbox %s is already empty\n", style.Dim.Render("○"), address) + return nil + } + + // Delete each message + deleted := 0 + var errors []string + for _, msg := range messages { + if err := mailbox.Delete(msg.ID); err != nil { + errors = append(errors, fmt.Sprintf("%s: %v", msg.ID, err)) + } else { + deleted++ + } + } + + // Report results + if len(errors) > 0 { + fmt.Printf("%s Cleared %d/%d messages from %s\n", + style.Bold.Render("⚠"), deleted, len(messages), address) + for _, e := range errors { + fmt.Printf(" Error: %s\n", e) + } + return fmt.Errorf("failed to clear %d messages", len(errors)) + } + + fmt.Printf("%s Cleared %d messages from %s\n", + style.Bold.Render("✓"), deleted, address) + return nil +} + // findMailWorkDir returns the town root for all mail operations. // // Two-level beads architecture: