feat: add --mail flag to gt rig reset to clear stale messages

Closes gt-48bs: gt rig reset now clears stale mail messages.
- Non-pinned messages are closed with reason 'Cleared during reset'
- Pinned messages have their content cleared but remain open
- Works with both --mail flag and default reset (all state)
This commit is contained in:
Steve Yegge
2025-12-21 11:28:05 -08:00
parent a80ae0a212
commit d5f4188ed6
2 changed files with 74 additions and 3 deletions

View File

@@ -547,6 +547,60 @@ func (b *Beads) ClearHandoffContent(role string) error {
return b.Update(issue.ID, UpdateOptions{Description: &empty})
}
// ClearMailResult contains statistics from a ClearMail operation.
type ClearMailResult struct {
Closed int // Number of messages closed
Cleared int // Number of pinned messages cleared (content removed)
}
// ClearMail closes or clears all open messages.
// Non-pinned messages are closed with the given reason.
// Pinned messages have their description cleared but remain open.
func (b *Beads) ClearMail(reason string) (*ClearMailResult, error) {
// List all open messages
issues, err := b.List(ListOptions{
Status: "open",
Type: "message",
Priority: -1,
})
if err != nil {
return nil, fmt.Errorf("listing messages: %w", err)
}
result := &ClearMailResult{}
// Separate pinned from non-pinned
var toClose []string
var toClear []*Issue
for _, issue := range issues {
if issue.Status == StatusPinned {
toClear = append(toClear, issue)
} else {
toClose = append(toClose, issue.ID)
}
}
// Close non-pinned messages in batch
if len(toClose) > 0 {
if err := b.CloseWithReason(reason, toClose...); err != nil {
return nil, fmt.Errorf("closing messages: %w", err)
}
result.Closed = len(toClose)
}
// Clear pinned messages
empty := ""
for _, issue := range toClear {
if err := b.Update(issue.ID, UpdateOptions{Description: &empty}); err != nil {
return nil, fmt.Errorf("clearing pinned message %s: %w", issue.ID, err)
}
result.Cleared++
}
return result, nil
}
// MRFields holds the structured fields for a merge-request issue.
// These fields are stored as key: value lines in the issue description.
type MRFields struct {

View File

@@ -71,14 +71,15 @@ var rigRemoveCmd = &cobra.Command{
var rigResetCmd = &cobra.Command{
Use: "reset",
Short: "Reset rig state (handoff content, etc.)",
Short: "Reset rig state (handoff content, mail, etc.)",
Long: `Reset various rig state.
By default, resets all resettable state. Use flags to reset specific items.
Examples:
gt rig reset # Reset all state
gt rig reset --handoff # Clear handoff content only`,
gt rig reset --handoff # Clear handoff content only
gt rig reset --mail # Clear stale mail messages only`,
RunE: runRigReset,
}
@@ -113,6 +114,7 @@ var (
rigAddPrefix string
rigAddCrew string
rigResetHandoff bool
rigResetMail bool
rigResetRole string
rigShutdownForce bool
rigShutdownNuclear bool
@@ -130,6 +132,7 @@ func init() {
rigAddCmd.Flags().StringVar(&rigAddCrew, "crew", "main", "Default crew workspace name")
rigResetCmd.Flags().BoolVar(&rigResetHandoff, "handoff", false, "Clear handoff content")
rigResetCmd.Flags().BoolVar(&rigResetMail, "mail", false, "Clear stale mail messages")
rigResetCmd.Flags().StringVar(&rigResetRole, "role", "", "Role to reset (default: auto-detect from cwd)")
rigShutdownCmd.Flags().BoolVarP(&rigShutdownForce, "force", "f", false, "Force immediate shutdown")
@@ -319,7 +322,7 @@ func runRigReset(cmd *cobra.Command, args []string) error {
}
// If no specific flags, reset all; otherwise only reset what's specified
resetAll := !rigResetHandoff
resetAll := !rigResetHandoff && !rigResetMail
bd := beads.New(townRoot)
@@ -331,6 +334,20 @@ func runRigReset(cmd *cobra.Command, args []string) error {
fmt.Printf("%s Cleared handoff content for %s\n", style.Success.Render("✓"), roleKey)
}
// Clear stale mail messages
if resetAll || rigResetMail {
result, err := bd.ClearMail("Cleared during reset")
if err != nil {
return fmt.Errorf("clearing mail: %w", err)
}
if result.Closed > 0 || result.Cleared > 0 {
fmt.Printf("%s Cleared mail: %d closed, %d pinned cleared\n",
style.Success.Render("✓"), result.Closed, result.Cleared)
} else {
fmt.Printf("%s No mail to clear\n", style.Success.Render("✓"))
}
}
return nil
}