cli: show recent sessions (not commits) in `entire activity` · Entire
cli: show recent sessions (not commits) in entire activity
887f764→main·
Soph·1w ago·8 files·+604 added/-74 removed
The entire.io Overview page renders a recent-sessions feed from
GET /api/v1/me/sessions, but entire activity rendered a commits feed
from /me/commits, so the two surfaces listed different things.
Make sessions the default recent-list in entire activity, matching the
web: fetch /me/sessions (limit 50, mirroring the web's
USER_OVERVIEW_RECENT_SESSIONS_LIMIT), group by local day of lastActivityAt
(newest first), and render one row per session with the same fields the
web row shows — display name, repo, public tag, agent badge, friendly
model label, and checkpoint count. The stat cards, contribution heatmap,
and repo chart (all from /me/activity) are unchanged.
Commits stay available behind entire activity --commits.
- Port entire.io's formatModel (frontend/src/lib/model.ts) to Go so model labels read identically (claude-opus-4-6 -> "Opus 4.6", gpt-4o -> "GPT-4o", gemini-2.0-flash -> "Gemini 2.0 Flash").
- Extract the shared always-fetch-activity-plus-a-list plumbing (fetchActivityWith) and day-grouping (groupItemsByDay) so the commit and session paths don't duplicate it.
- Routes through the existing runAuthenticatedActivityAPI, so /me/sessions go to the caller's home entire-api cell with the data-API fallback.
Stacked on #1641.
Co-Authored-By: Claude Opus 4.8 (1M context) noreply@anthropic.com
Sessions
5df0b30804c2View transcript
[?
Fix Sessions Endpoint in Activity FeedClaude Code·Opus 4.8[1m]·3 steps](/content/gh/entireio/cli/session/1b0292dc-2cb1-49e9-91f5-41188abf9147#timeline-5df0b30804c2/index.html)
Changes
8
cmd/entire/cli
Mactivity_cmd.go+109/-39
Mactivity_cmd_test.go+2/-2
Mactivity_render.go+111/-2
Aactivity_sessions_test.go+178
Mactivity_tui.go+50/-31
Mactivity_types.go+34
Amodel_label.go+78
Amodel_label_test.go+42
21 unmodified lines
39 unmodified lines
90 unmodified lines
80 unmodified lines
package cli
import ( "regexp" "strings" )
// claudeModelRe matches Claude identifiers: claude-(?i)^claude-(opus|sonnet|haiku)-(1)(.*)$)
// dateSuffixRe matches a legacy date chunk (6+ digits, e.g. 20250514).
var dateSuffixRe = regexp.MustCompile(^\d{6,}$)
// formatModel turns a raw model identifier into a short display label, mirroring // entire.io's frontend formatModel (frontend/src/lib/model.ts) so the CLI's // session list reads identically to the web Overview page. // Examples: // // "claude-opus-4-6" -> "Opus 4.6" // "claude-sonnet-4-20250514" -> "Sonnet 4" // "gpt-4o" -> "GPT-4o" // "gemini-2.0-flash" -> "Gemini 2.0 Flash" // // Unknown formats pass through unchanged. Empty/whitespace input returns "" // (the web returns null; the CLI renders nothing for it). func formatModel(model string) string { trimmed := strings.TrimSpace(model) if trimmed == "" { return "" }
// Claude: title-case family, then major[.minor], dropping any date suffix. if m := claudeModelRe.FindStringSubmatch(trimmed); m != nil { family, major, rest := m[1], m[2], m[3] familyLabel := strings.ToUpper(family[:1]) + strings.ToLower(family[1:]) var minorParts []string for _, part := range strings.Split(rest, "-") { if part == "" { continue } // A 6+ digit chunk is a date suffix — ignore it and everything after. if dateSuffixRe.MatchString(part) { break } minorParts = append(minorParts, part) } if minor := strings.Join(minorParts, "."); minor != "" { return familyLabel + " " + major + "." + minor } return familyLabel + " " + major }
// GPT: gpt-4o -> "GPT-4o" (case-insensitive prefix; "gpt-" is 4 ASCII bytes). if len(trimmed) >= 4 && strings.EqualFold(trimmed[:4], "gpt-") { return "GPT-" + trimmed[4:] }
// Gemini: gemini-2.0-flash -> "Gemini 2.0 Flash" (upper-first each part). if strings.HasPrefix(strings.ToLower(trimmed), "gemini-") { parts := strings.Split(trimmed, "-") for i, part := range parts { parts[i] = upperFirst(part) } return strings.Join(parts, " ") }
return trimmed }
// upperFirst upper-cases the first rune of s, leaving the remainder unchanged
// (matching the web's part.charAt(0).toUpperCase() + part.slice(1)).
func upperFirst(s string) string {
if s == "" {
return s
}
return strings.ToUpper(string([]rune(s)[0])) + string([]rune(s)[1:])
}