search: address PR review — repo scoping, HasFilters, JSON invariant · Entire

search: address PR review — repo scoping, HasFilters, JSON invariant

7ef3a57→main·

alishakawaguchi·1mo ago·3 files·+59 added/-7 removed

Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com

Sessions

0debb873cd3aView transcript

[?
Redesign Search TUI Table with Master-Detail LayoutClaude Code·Opus 4.8[1m]·1 step](/content/gh/entireio/cli/session/a891fc9c-9a5b-4cbb-8341-7bb4230da520#timeline-0debb873cd3a/index.html)

Changes

3

153 unmodified lines

154
155
156
157
158
159
160
161
162
219 unmodified lines

382
383
384
382
385
386
387
388
157 unmodified lines

546
547
548
546
549
550
551
552
553
554
555
556
557
558
559
560
548
561
562
563
564
550
565
566
567
568
569
570

153 unmodified lines

r.Type = raw.Type
     r.Meta = raw.Meta
     r.rawData = raw.Data
     // Clear any previously-decoded payloads so a reused Result keeps the
     // "exactly one typed pointer is non-nil" invariant.
     r.Checkpoint, r.Commit, r.Session = nil, nil, nil

switch raw.Type {
     case TypeCheckpoint:
219 unmodified lines

// HasFilters reports whether any filter fields are set on the config.
func (c Config) HasFilters() bool {
     return c.Author != "" || c.Date != "" || c.Branch != "" || len(c.Repos) > 0
     return c.Author != "" || c.Date != "" || c.Branch != "" || len(c.Repos) > 0 || c.AllRepos
}

// ParsedInput holds the parsed query and optional filters extracted from search input.
157 unmodified lines

return nil, err
    }
     allRepos := cfg.AllRepos || (len(cfg.Repos) == 1 && cfg.Repos[0] == AllReposFilter)
     if len(cfg.Repos) > 0 && !allRepos {
         hasExplicitRepo := false
         for _, repo := range cfg.Repos {
             if repo != AllReposFilter {
                 hasExplicitRepo = true
                 break
            }
        }
        switch {
        case hasExplicitRepo:
             // An explicit owner/name filter always scopes the search, even when
             // --all-repos is also set (the more specific filter wins).
             for _, repo := range cfg.Repos {
                 q.Add("repo", repo)
                 if repo != AllReposFilter {
                     q.Add("repo", repo)
                }
            }
        } else if !allRepos && cfg.Owner != "" && cfg.Repo != "" {
        case allRepos:
             // No repo scoping — search every accessible repo.
        case cfg.Owner != "" && cfg.Repo != "":
             q.Set("repo", cfg.Owner+"/"+cfg.Repo)
        }
         // Don't set types — let the API return all types (checkpoints, commits, sessions, etc.)

Mcmd/entire/cli/search/search.go+21/-4

638 unmodified lines

639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
118 unmodified lines

795
796
797
798
799
800
801
802
803

638 unmodified lines

}
}

func TestSearch_ExplicitRepoWinsOverAllRepos(t *testing.T) {
     t.Parallel()

var capturedReq *http.Request
     srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
         capturedReq = r
         resp := Response{Results: []Result{}, Total: 0, Page: 1}
         w.Header().Set("Content-Type", "application/json")
         json.NewEncoder(w).Encode(resp) //nolint:errcheck // test helper response
     }))
     defer srv.Close()

// --all-repos alongside an explicit owner/name filter must scope to the
     // explicit repo (the more specific filter wins), not search all repos.
     _, err := Search(context.Background(), Config{
         ServiceURL:  srv.URL,
         GitHubToken:  "tok",
         Owner:       "default-owner",
         Repo:        "default-repo",
         Query:       "q",
         AllRepos:    true,
         Repos:       []string{"owner/explicit"},
     })
     if err != nil {
         t.Fatal(err)
     }

if got := capturedReq.URL.Query()["repo"]; len(got) != 1 || got[0] != "owner/explicit" {
         t.Errorf("repo params = %v, want [owner/explicit]", got)
     }
}

func TestSearch_MultipleExplicitReposRejected(t *testing.T) {
     t.Parallel()

118 unmodified lines

if !(Config{Repos: []string{"entirehq/entire.io"}}).HasFilters() {
         t.Error("config with Repos should have filters")
     }
     if !(Config{AllRepos: true}).HasFilters() {
         t.Error("config with AllRepos should have filters")
     }
     if !(Config{Author: "alice", Date: testDateWeek}).HasFilters() {
         t.Error("config with both should have filters")
     }
}