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
- toQuery: an explicit owner/name repo filter now scopes the search even when --all-repos is also set (the more specific filter wins) instead of being silently ignored.
- Config.HasFilters now counts AllRepos, so
entire search --all-repos --jsonwith an empty query produces JSON instead of opening the TUI. - Result.UnmarshalJSON clears the typed payload pointers before decoding so a reused Result keeps the "exactly one non-nil" invariant.
- Correct the stale "fetch the maximum" comment (it fetches DefaultLimit).
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
cmd/entire/cli
search
Msearch.go+21/-4
Msearch_test.go+35
Msearch_cmd.go+3/-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")
}
}