cli: show owner/repo, not the repo_id ULID, in recap's scope line · Entire

cli: show owner/repo, not the repo_id ULID, in recap's scope line

24b5d4bmain·

Soph·1w ago·4 files·+71 added/-26 removed

/me/recap identifies the scoped repo by its repo_id ULID and echoes it back in repo, so recap rendered "repo 01KSFAN..." once routed to a cell. Thread the human owner/repo name (which the CLI already knows for the current repo) through RenderOptions/recapTUIOptions and prefer it in the scope label. Only set when actually scoped, so an unscoped recap isn't mislabelled.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

Sessions

0a8172314e8aView transcript

[?
Route CLI Commands to entire-apiClaude Code·2 steps](/content/gh/entireio/cli/session/473b5e55-d61f-4bc6-bcae-ab078e0d3cdd#timeline-0a8172314e8a/index.html)

Changes

4

137 unmodified lines

138
139
140
141
142
143
144
145
146
147
148
149
150
143
144
145
146
147
151
152
153
154
155
156
157
158
159
2 unmodified lines

162
163
164
156
157
158
159
160
165
166
167
168
169
170
171
172
173

137 unmodified lines

return err
    }
    rangeKey := f.rangeKey()
    // The ?repo= value is a repo_id ULID when routed to a cell, which the recap
    // response echoes back; show the human owner/repo name instead. Only when
    // actually scoped (a repo query was sent), so an unscoped recap isn't
    // mislabelled as scoped to the current repo.
    repoName := ""
    if repoSlug != "" {
        repoName = currentRepoSlug(ctx)
    }
    if f.useTUI(interactive.IsTerminalWriter(w), interactive.CanPromptInteractively(), IsAccessibleMode()) {
        return runRecapTUI(ctx, client, recapTUIOptions{
            Range: rangeKey,
            View:  mode,
            Agent: f.agentName(),
            Repo:  repoSlug,
            Color: color,
            Range:    rangeKey,
            View:     mode,
            Agent:    f.agentName(),
            Repo:     repoSlug,
            RepoName: repoName,
            Color:    color,
        })
    }
    start, end := rangeKey.Bounds(time.Now())
2 unmodified lines

return handleRecapFetchError(errW, err)
    }
    fmt.Fprint(w, recap.RenderStaticRecap(resp, recap.RenderOptions{
        Range: rangeKey,
        View:  mode,
        Agent: f.agentName(),
        Width: terminalWidth(w),
        Color: color,
        Range:    rangeKey,
        View:     mode,
        Agent:    f.agentName(),
        Width:    terminalWidth(w),
        Color:    color,
        RepoName: repoName,
    }))
    fmt.Fprintln(w)
    return nil

Mcmd/entire/cli/recap.go+20/-10

22 unmodified lines

23
24
25
26
27
28
29
30
26
27
28
29
30
31
32
33
34
35
36
37
38
126 unmodified lines

165
166
167
163
168
169
170
171
142 unmodified lines

314
315
316
312
317
318
319
320
321
322
323
324
325

22 unmodified lines

)

type RenderOptions struct {
    Range    RangeKey
    View     ViewMode
    Agent    string
    Width    int
    Color    bool
    Range Key
    View  ViewMode
    Agent string
    Width int
    Color bool
    // RepoName is the human owner/repo name of the repo the recap is scoped to,
    // when the caller knows it. It is preferred over the response's repo field
    // for display: /me/recap identifies the scoped repo by its repo_id ULID
    // (echoed back verbatim), which is meaningless to show a user.
    RepoName string
    Location *time.Location
}

126 unmodified lines

lines = append(lines, "", styles.muted.Render("top")+"  "+strings.Join(top, styles.muted.Render(" · ")))
    }
    context := []string{plural(len(filteredAgents(resp, opts)), "agent")}
    if repoScope := repoScopeText(resp); repoScope != "" {
    if repoScope := repoScopeText(resp, opts.RepoName); repoScope != "" {
        context = append(context, repoScope)
    }
    if !agentFiltered {
142 unmodified lines

return t.Format("Jan 2, 2006 15:04 MST")
}

func repoScopeText(resp *MeRecapResponse) string {
func repoScopeText(resp *MeRecapResponse, repoName string) string {
    // The caller-supplied human name wins when the recap is scoped to one repo:
    // the response echoes the queried repo_id ULID, not an owner/repo slug.
    if name := strings.TrimSpace(repoName); name != "" {
        return "repo " + name
    }
    repos := recapRepoNames(resp)
switch {
case len(repos) == 1:

Mcmd/entire/cli/recap/render_static.go+17/-7

6 unmodified lines

7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

6 unmodified lines

"time"
)

func TestRenderStaticRecap_RepoNameOverridesEchoedID(t *testing.T) {
    t.Parallel()
    // /me/recap echoes the queried repo_id ULID in Repo; the caller-supplied
    // human name must be shown instead.
    resp := &MeRecapResponse{
        Repo:    ptr("01KSFAN13YPQ0EWBV5KRE7F5HV"),
        Since:   "2026-05-02T04:00:00Z",
        Until:   "2026-05-09T04:00:00Z",
        Summary: Summary{Me: SummaryTotals{Checkpoints: 1}, RepoCount: 1, ActiveDays: 1},
    }
    out := RenderStaticRecap(resp, RenderOptions{Range: RangeDay, View: ViewYou, Width: 80, RepoName: "entireio/cli"})
    if !strings.Contains(out, "repo entireio/cli") {
         t.Errorf("expected human repo name in output, got:\n%s", out)
    }
    if strings.Contains(out, "01KSFAN13YPQ0EWBV5KRE7F5HV") {
         t.Errorf("expected the repo_id ULID to be hidden, got:\n%s", out)
    }
}

func TestRenderStaticRecap_ServerBackedBoth90(t *testing.T) {
    t.Parallel()
    resp := &MeRecapResponse{

Mcmd/entire/cli/recap/static_server_test.go+19

20 unmodified lines

21
22
23
24
24
25
26
27
28
29
30
7 unmodified lines

38
39
40
38
39
40
41
42
43
44
45
46
47
33 unmodified lines

81
82
83
84
85
86
87
161 unmodified lines

249
250
251
247
248
249
250
251
252
253
254
255
256
257
258
259
260

20 unmodified lines

View  recap.ViewMode
    Agent string
    Repo  string
    Color bool
    // RepoName is the human owner/repo display name for the scoped repo; Repo is
    // the ?repo= query value (a repo_id ULID when routed to a cell).
    RepoName string
    Color    bool
}

type recapDataMsg struct {
7 unmodified lines

}

type recapTUIModel struct {
    ctx    context.Context
    client *api.Client
    repo   string
    ctx      context.Context
    client   *api.Client
    repo     string
    repoName string

rangeKey recap.RangeKey
    view     recap.ViewMode
33 unmodified lines

ctx:       ctx,
    client:    client,
    repo:      opts.Repo,
    repoName:  opts.RepoName,
    rangeKey:  opts.Range,
    view:      opts.View,
    agent:     opts.Agent,
161 unmodified lines

}
    if m.resp != nil {
        m.viewport.SetContent(recap.RenderStaticRecap(m.resp, recap.RenderOptions{
            Range: m.rangeKey,
            View:  m.view,
            Agent: m.agent,
            Width: m.width,
            Color: m.color,
            Range:    m.rangeKey,
            View:     m.view,
            Agent:    m.agent,
            Width:    m.width,
            Color:    m.color,
            RepoName: m.repoName,
        }))
    }
    return m