diff --git a/pkg/cmd/inbox/cache.go b/pkg/cmd/inbox/cache.go index 63dba53..265d548 100644 --- a/pkg/cmd/inbox/cache.go +++ b/pkg/cmd/inbox/cache.go @@ -25,9 +25,12 @@ const ( // inboxCache stores the date_updated of tasks observed in the workspace scan // so subsequent runs can skip the comment fetch for tasks that have not changed. +// IncludeSelf records whether the cached mentions were captured with +// self-authored mentions included, so a flag mismatch triggers a cold rescan. type inboxCache struct { - ScannedAt int64 `json:"scanned_at"` - Tasks map[string]inboxCacheEntry `json:"tasks"` + ScannedAt int64 `json:"scanned_at"` + IncludeSelf bool `json:"include_self"` + Tasks map[string]inboxCacheEntry `json:"tasks"` } type inboxCacheEntry struct { diff --git a/pkg/cmd/inbox/inbox.go b/pkg/cmd/inbox/inbox.go index 92fffe6..0bf8ecb 100644 --- a/pkg/cmd/inbox/inbox.go +++ b/pkg/cmd/inbox/inbox.go @@ -17,11 +17,12 @@ import ( ) type inboxOptions struct { - factory *cmdutil.Factory - days int - limit int - noCache bool - jsonFlags cmdutil.JSONFlags + factory *cmdutil.Factory + days int + limit int + noCache bool + includeSelf bool + jsonFlags cmdutil.JSONFlags } type mention struct { @@ -115,6 +116,7 @@ approximates your inbox by combining these two endpoints.`, cmd.Flags().IntVar(&opts.days, "days", 7, "How many days back to search") cmd.Flags().IntVar(&opts.limit, "limit", 200, "Maximum number of tasks to scan for mentions") cmd.Flags().BoolVar(&opts.noCache, "no-cache", false, "Bypass the local cache and re-fetch comments for every task") + cmd.Flags().BoolVar(&opts.includeSelf, "include-self", false, "Include @mentions where you authored the comment (useful for self-reminders)") cmdutil.AddJSONFlags(cmd, &opts.jsonFlags) return cmd @@ -185,7 +187,7 @@ func inboxRun(opts *inboxOptions) error { fmt.Fprintf(ios.ErrOut, "Warning: failed to load inbox cache: %v\n", err) cache = &inboxCache{Tasks: map[string]inboxCacheEntry{}} } - if !cache.IsFresh(time.Now()) { + if !cache.IsFresh(time.Now()) || cache.IncludeSelf != opts.includeSelf { cache = &inboxCache{Tasks: map[string]inboxCacheEntry{}} } } @@ -258,7 +260,8 @@ func inboxRun(opts *inboxOptions) error { } for _, c := range r.comments { - if containsMention(c.CommentText, username) && strings.ToLower(c.User.Username) != username { + isSelf := strings.ToLower(c.User.Username) == username + if containsMention(c.CommentText, username) && (opts.includeSelf || !isSelf) { ms, _ := strconv.ParseInt(c.Date, 10, 64) attachments := extractAttachmentURLs(c.Comment) addMention(mention{ @@ -311,6 +314,7 @@ func inboxRun(opts *inboxOptions) error { if cache == nil { cache = &inboxCache{Tasks: map[string]inboxCacheEntry{}} } + cache.IncludeSelf = opts.includeSelf updateCacheFromTasks(cache, workspaceTasks, mentionsByTask, time.Now()) if err := saveInboxCache(cachePath, cache); err != nil { fmt.Fprintf(ios.ErrOut, "Warning: failed to save inbox cache: %v\n", err) diff --git a/pkg/cmd/inbox/inbox_test.go b/pkg/cmd/inbox/inbox_test.go index 6056ac9..6b26b84 100644 --- a/pkg/cmd/inbox/inbox_test.go +++ b/pkg/cmd/inbox/inbox_test.go @@ -30,6 +30,51 @@ func TestNewCmdInbox_Defaults(t *testing.T) { if limitFlag.DefValue != "200" { t.Errorf("--limit default = %q, want %q", limitFlag.DefValue, "200") } + + includeSelfFlag := cmd.Flags().Lookup("include-self") + if includeSelfFlag == nil { + t.Fatal("expected --include-self flag") + } + if includeSelfFlag.DefValue != "false" { + t.Errorf("--include-self default = %q, want %q", includeSelfFlag.DefValue, "false") + } +} + +func TestInboxCache_FlagMismatchInvalidates(t *testing.T) { + now := time.Now() + fresh := &inboxCache{ + ScannedAt: now.UnixMilli(), + IncludeSelf: false, + Tasks: map[string]inboxCacheEntry{"a": {DateUpdated: "1"}}, + } + if !fresh.IsFresh(now) { + t.Fatal("cache should be fresh by timestamp") + } + // The invalidation lives in inboxRun, but the cache field must round-trip + // through JSON so the next process sees the prior run's setting. + dir := t.TempDir() + path := filepath.Join(dir, "cache.json") + if err := saveInboxCache(path, fresh); err != nil { + t.Fatalf("save: %v", err) + } + loaded, err := loadInboxCache(path) + if err != nil { + t.Fatalf("load: %v", err) + } + if loaded.IncludeSelf { + t.Error("IncludeSelf round-trip lost — expected false") + } + fresh.IncludeSelf = true + if err := saveInboxCache(path, fresh); err != nil { + t.Fatalf("save: %v", err) + } + loaded, err = loadInboxCache(path) + if err != nil { + t.Fatalf("load: %v", err) + } + if !loaded.IncludeSelf { + t.Error("IncludeSelf=true did not round-trip") + } } func TestContainsMention_TaskDescriptions(t *testing.T) {