@@ -3148,6 +3148,25 @@ func attentionPriority(status data.AttentionStatus) int {
31483148 }
31493149}
31503150
3151+ // sortByFrecency re-sorts the session slice by frecency when the current
3152+ // sort field is SortByFrecency. Sessions the user launches often and
3153+ // recently sort first; sessions with no launch history keep their incoming
3154+ // (updated-time) order via a stable sort.
3155+ func (m * Model ) sortByFrecency (sessions []data.Session ) {
3156+ if m .sort .Field != data .SortByFrecency {
3157+ return
3158+ }
3159+ now := time .Now ()
3160+ slices .SortStableFunc (sessions , func (a , b data.Session ) int {
3161+ sa := config .FrecencyScore (m .cfg .SessionLaunches [a .ID ], now )
3162+ sb := config .FrecencyScore (m .cfg .SessionLaunches [b .ID ], now )
3163+ if m .sort .Order == data .Ascending {
3164+ return cmp .Compare (sa , sb )
3165+ }
3166+ return cmp .Compare (sb , sa )
3167+ })
3168+ }
3169+
31513170// ---------------------------------------------------------------------------
31523171// Sort / pivot cycling
31533172// ---------------------------------------------------------------------------
@@ -3157,6 +3176,7 @@ var sortFields = []data.SortField{
31573176 data .SortByFolder ,
31583177 data .SortByName ,
31593178 data .SortByAttention ,
3179+ data .SortByFrecency ,
31603180}
31613181
31623182func (m * Model ) cycleSort () {
@@ -3254,6 +3274,8 @@ func sortDisplayLabel(f data.SortField) string {
32543274 return "name"
32553275 case data .SortByAttention :
32563276 return "attention"
3277+ case data .SortByFrecency :
3278+ return "frecency"
32573279 default :
32583280 return "updated"
32593281 }
@@ -3439,6 +3461,7 @@ func (m *Model) resolveShellAndLaunchDirect(sessionID, cwd, mode string) tea.Cmd
34393461// runs the Copilot CLI session resume in the current terminal, and quits
34403462// the TUI when the session ends.
34413463func (m * Model ) launchInPlace (sessionID , cwd string ) tea.Cmd {
3464+ m .recordLaunch (sessionID )
34423465 cfg := m .resumeConfigForSession (cwd )
34433466 cmd , err := platform .NewResumeCmd (sessionID , cfg )
34443467 if err != nil {
@@ -3464,6 +3487,7 @@ func launchStyleForMode(mode string) string {
34643487
34653488// launchExternal opens the session in a new tab, window, or pane depending on launchStyle.
34663489func (m * Model ) launchExternal (shell platform.ShellInfo , sessionID , cwd , launchStyle string ) tea.Cmd {
3490+ m .recordLaunch (sessionID )
34673491 cfg := m .resumeConfigForSession (cwd )
34683492 cfg .LaunchStyle = launchStyle
34693493 return func () tea.Msg {
@@ -3488,6 +3512,17 @@ func (m Model) resumeConfigForSession(cwd string) platform.ResumeConfig {
34883512 }
34893513}
34903514
3515+ // recordLaunch stamps a session launch in the config so the frecency sort
3516+ // can rank frequently and recently launched sessions first. Empty IDs (new
3517+ // sessions started from a folder) are ignored.
3518+ func (m * Model ) recordLaunch (sessionID string ) {
3519+ if sessionID == "" {
3520+ return
3521+ }
3522+ m .cfg .RecordLaunch (sessionID , time .Now ())
3523+ m .saveConfig ()
3524+ }
3525+
34913526func (m Model ) selectedSessionID () string {
34923527 if sess , ok := m .sessionList .Selected (); ok {
34933528 return sess .ID
@@ -4300,6 +4335,8 @@ func sortFieldFromConfig(s string) data.SortField {
43004335 return data .SortByName
43014336 case pivotFolder , "cwd" :
43024337 return data .SortByFolder
4338+ case "frecency" :
4339+ return data .SortByFrecency
43034340 default :
43044341 return data .SortByUpdated
43054342 }
@@ -4315,6 +4352,8 @@ func sortFieldToConfig(f data.SortField) string {
43154352 return "name"
43164353 case data .SortByFolder :
43174354 return "folder"
4355+ case data .SortByFrecency :
4356+ return "frecency"
43184357 default :
43194358 return "updated"
43204359 }
0 commit comments