feat: add gt mail check command and update prime for GGT cutover
- Add 'gt mail check' with --inject flag for Claude Code hooks - Update Mayor prime output to include mail commands and startup info - Enables full cutover from PGT 'town' commands to GGT 'gt' commands 🤖 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,6 +22,8 @@ var (
|
||||
mailInboxJSON bool
|
||||
mailReadJSON bool
|
||||
mailInboxUnread bool
|
||||
mailCheckInject bool
|
||||
mailCheckJSON bool
|
||||
)
|
||||
|
||||
var mailCmd = &cobra.Command{
|
||||
@@ -87,6 +89,25 @@ This closes the message in beads.`,
|
||||
RunE: runMailDelete,
|
||||
}
|
||||
|
||||
var mailCheckCmd = &cobra.Command{
|
||||
Use: "check",
|
||||
Short: "Check for new mail (for hooks)",
|
||||
Long: `Check for new mail - useful for Claude Code hooks.
|
||||
|
||||
Exit codes (normal mode):
|
||||
0 - New mail available
|
||||
1 - No new mail
|
||||
|
||||
Exit codes (--inject mode):
|
||||
0 - Always (hooks should never block)
|
||||
Output: system-reminder if mail exists, silent if no mail
|
||||
|
||||
Examples:
|
||||
gt mail check # Simple check
|
||||
gt mail check --inject # For hooks`,
|
||||
RunE: runMailCheck,
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Send flags
|
||||
mailSendCmd.Flags().StringVarP(&mailSubject, "subject", "s", "", "Message subject (required)")
|
||||
@@ -102,11 +123,16 @@ func init() {
|
||||
// Read flags
|
||||
mailReadCmd.Flags().BoolVar(&mailReadJSON, "json", false, "Output as JSON")
|
||||
|
||||
// Check flags
|
||||
mailCheckCmd.Flags().BoolVar(&mailCheckInject, "inject", false, "Output format for Claude Code hooks")
|
||||
mailCheckCmd.Flags().BoolVar(&mailCheckJSON, "json", false, "Output as JSON")
|
||||
|
||||
// Add subcommands
|
||||
mailCmd.AddCommand(mailSendCmd)
|
||||
mailCmd.AddCommand(mailInboxCmd)
|
||||
mailCmd.AddCommand(mailReadCmd)
|
||||
mailCmd.AddCommand(mailDeleteCmd)
|
||||
mailCmd.AddCommand(mailCheckCmd)
|
||||
|
||||
rootCmd.AddCommand(mailCmd)
|
||||
}
|
||||
@@ -363,3 +389,81 @@ func detectSender() string {
|
||||
// Default to mayor
|
||||
return "mayor/"
|
||||
}
|
||||
|
||||
func runMailCheck(cmd *cobra.Command, args []string) error {
|
||||
// Find workspace
|
||||
workDir, err := findBeadsWorkDir()
|
||||
if err != nil {
|
||||
if mailCheckInject {
|
||||
// Inject mode: always exit 0, silent on error
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("not in a Gas Town workspace: %w", err)
|
||||
}
|
||||
|
||||
// Determine which inbox
|
||||
address := detectSender()
|
||||
|
||||
// Get mailbox
|
||||
router := mail.NewRouter(workDir)
|
||||
mailbox, err := router.GetMailbox(address)
|
||||
if err != nil {
|
||||
if mailCheckInject {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("getting mailbox: %w", err)
|
||||
}
|
||||
|
||||
// Count unread
|
||||
_, unread, err := mailbox.Count()
|
||||
if err != nil {
|
||||
if mailCheckInject {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("counting messages: %w", err)
|
||||
}
|
||||
|
||||
// JSON output
|
||||
if mailCheckJSON {
|
||||
result := map[string]interface{}{
|
||||
"address": address,
|
||||
"unread": unread,
|
||||
"has_new": unread > 0,
|
||||
}
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
enc.SetIndent("", " ")
|
||||
return enc.Encode(result)
|
||||
}
|
||||
|
||||
// Inject mode: output system-reminder if mail exists
|
||||
if mailCheckInject {
|
||||
if unread > 0 {
|
||||
// Get subjects for context
|
||||
messages, _ := mailbox.ListUnread()
|
||||
var subjects []string
|
||||
for _, msg := range messages {
|
||||
subjects = append(subjects, fmt.Sprintf("- From %s: %s", msg.From, msg.Subject))
|
||||
}
|
||||
|
||||
fmt.Println("<system-reminder>")
|
||||
fmt.Printf("You have %d unread message(s) in your inbox.\n\n", unread)
|
||||
for _, s := range subjects {
|
||||
fmt.Println(s)
|
||||
}
|
||||
fmt.Println()
|
||||
fmt.Println("Run 'gt mail inbox' to see your messages, or 'gt mail read <id>' for a specific message.")
|
||||
fmt.Println("</system-reminder>")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Normal mode
|
||||
if unread > 0 {
|
||||
fmt.Printf("%s %d unread message(s)\n", style.Bold.Render("📬"), unread)
|
||||
os.Exit(0)
|
||||
} else {
|
||||
fmt.Println("No new mail")
|
||||
os.Exit(1)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -156,10 +156,15 @@ func outputMayorContext(ctx RoleContext) {
|
||||
fmt.Println("- Monitor overall system health")
|
||||
fmt.Println()
|
||||
fmt.Println("## Key Commands")
|
||||
fmt.Println("- `gt mail inbox` - Check your messages")
|
||||
fmt.Println("- `gt mail read <id>` - Read a specific message")
|
||||
fmt.Println("- `gt status` - Show overall town status")
|
||||
fmt.Println("- `gt rigs` - List all rigs")
|
||||
fmt.Println("- `bd ready` - Issues ready to work")
|
||||
fmt.Println()
|
||||
fmt.Println("## Startup")
|
||||
fmt.Println("Check for handoff messages with 🤝 HANDOFF in subject - continue predecessor's work.")
|
||||
fmt.Println()
|
||||
fmt.Printf("Town root: %s\n", style.Dim.Render(ctx.TownRoot))
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user