From 2a631a2488f6f76432c6e03f9fbcb05d56a4ca87 Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Sun, 1 Feb 2026 01:10:36 +0100 Subject: [PATCH] feat(coding-agent): threaded sort mode and compact format for /resume Adds 'Threaded' as a new sort mode (now default) that displays sessions in a tree structure based on parent-child relationships. Compact one-line format with message count and age right-aligned for clean title alignment. Closes #1108 --- packages/coding-agent/CHANGELOG.md | 1 + .../interactive/components/session-selector.ts | 14 ++++++-------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index ab26f5a0..69fe5a49 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -5,6 +5,7 @@ ### Added - Added `newSession`, `tree`, and `fork` keybinding actions for `/new`, `/tree`, and `/fork` commands. All unbound by default. ([#1114](https://github.com/badlogic/pi-mono/pull/1114) by [@juanibiapina](https://github.com/juanibiapina)) +- `/resume` session picker: new "Threaded" sort mode (now default) displays sessions in a tree structure based on fork relationships. Compact one-line format with message count and age on the right. ([#1124](https://github.com/badlogic/pi-mono/pull/1124) by [@pasky](https://github.com/pasky)) ## [0.50.7] - 2026-01-31 diff --git a/packages/coding-agent/src/modes/interactive/components/session-selector.ts b/packages/coding-agent/src/modes/interactive/components/session-selector.ts index becfdfe7..77ffc842 100644 --- a/packages/coding-agent/src/modes/interactive/components/session-selector.ts +++ b/packages/coding-agent/src/modes/interactive/components/session-selector.ts @@ -400,9 +400,10 @@ class SessionList implements Component, Focusable { const displayText = session.name ?? session.firstMessage; const normalizedMessage = displayText.replace(/\n/g, " ").trim(); - // Right side: age + // Right side: message count and age const age = formatSessionDate(session.modified); - let rightPart = age; + const msgCount = String(session.messageCount); + let rightPart = `${msgCount} ${age}`; if (this.showCwd && session.cwd) { rightPart = `${shortenPath(session.cwd)} ${rightPart}`; } @@ -410,15 +411,13 @@ class SessionList implements Component, Focusable { rightPart = `${shortenPath(session.path)} ${rightPart}`; } - // Cursor and message count prefix + // Cursor const cursor = isSelected ? theme.fg("accent", "› ") : " "; - const msgCountPrefix = `(${session.messageCount}) `; // Calculate available width for message const prefixWidth = visibleWidth(prefix); - const msgCountWidth = visibleWidth(msgCountPrefix); const rightWidth = visibleWidth(rightPart) + 2; // +2 for spacing - const availableForMsg = width - 2 - prefixWidth - msgCountWidth - rightWidth; // -2 for cursor + const availableForMsg = width - 2 - prefixWidth - rightWidth; // -2 for cursor const truncatedMsg = truncateToWidth(normalizedMessage, Math.max(10, availableForMsg), "…"); @@ -437,8 +436,7 @@ class SessionList implements Component, Focusable { } // Build line - const styledMsgCount = theme.fg("dim", msgCountPrefix); - const leftPart = cursor + theme.fg("dim", prefix) + styledMsgCount + styledMsg; + const leftPart = cursor + theme.fg("dim", prefix) + styledMsg; const leftWidth = visibleWidth(leftPart); const spacing = Math.max(1, width - leftWidth - visibleWidth(rightPart)); const styledRight = theme.fg(isConfirmingDelete ? "error" : "dim", rightPart);