Skip to content

Commit 740e6b1

Browse files
Max_ProMax_Pro
authored andcommitted
feat(session-manager): improve session list with preview, time, and changes
- Add first user message preview (50 chars) when title is empty - Show precise timestamp (YYYY-MM-DD HH:MM) instead of date only - Display file changes summary (files/additions/deletions) - Rename columns for clarity: Title → Title/Preview, First/Last → Updated
1 parent 2443161 commit 740e6b1

File tree

3 files changed

+70
-8
lines changed

3 files changed

+70
-8
lines changed

src/tools/session-manager/storage.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ export async function getMainSessions(options: GetMainSessionsOptions): Promise<
3232

3333
if (options.directory && meta.directory !== options.directory) continue
3434

35+
if (!meta.preview) {
36+
meta.preview = await getFirstUserMessagePreview(meta.id)
37+
}
38+
3539
sessions.push(meta)
3640
} catch {
3741
continue
@@ -45,6 +49,21 @@ export async function getMainSessions(options: GetMainSessionsOptions): Promise<
4549
return sessions.sort((a, b) => b.time.updated - a.time.updated)
4650
}
4751

52+
async function getFirstUserMessagePreview(sessionID: string, maxLength = 50): Promise<string | undefined> {
53+
const messages = await readSessionMessages(sessionID)
54+
const firstUserMessage = messages.find((m) => m.role === "user")
55+
if (!firstUserMessage) return undefined
56+
57+
for (const part of firstUserMessage.parts) {
58+
if (part.type === "text" && part.text) {
59+
const text = part.text.trim().replace(/\s+/g, " ")
60+
if (text.length <= maxLength) return text
61+
return text.substring(0, maxLength) + "..."
62+
}
63+
}
64+
return undefined
65+
}
66+
4867
export async function getAllSessions(): Promise<string[]> {
4968
if (!existsSync(MESSAGE_STORAGE)) return []
5069

src/tools/session-manager/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ export interface SessionMetadata {
6666
deletions: number
6767
files: number
6868
}
69+
/** First user message preview (auto-generated for display) */
70+
preview?: string
6971
}
7072

7173
export interface SessionListArgs {

src/tools/session-manager/utils.ts

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,68 @@ export async function formatSessionList(sessions: SessionMetadata[]): Promise<st
66
return "No sessions found."
77
}
88

9-
const infos: SessionInfo[] = []
9+
interface EnrichedInfo extends SessionInfo {
10+
preview?: string
11+
files?: number
12+
additions?: number
13+
deletions?: number
14+
}
15+
16+
const infos: EnrichedInfo[] = []
1017
for (const meta of sessions) {
1118
const info = await getSessionInfo(meta.id)
1219
if (info) {
13-
info.title = meta.title
14-
infos.push(info)
20+
const enriched: EnrichedInfo = {
21+
...info,
22+
title: meta.title,
23+
preview: meta.preview,
24+
files: meta.summary?.files,
25+
additions: meta.summary?.additions,
26+
deletions: meta.summary?.deletions,
27+
}
28+
infos.push(enriched)
1529
}
1630
}
1731

1832
if (infos.length === 0) {
1933
return "No valid sessions found."
2034
}
2135

22-
const headers = ["Session ID", "Title", "Messages", "First", "Last", "Agents"]
36+
const formatDateTime = (date: Date | undefined): string => {
37+
if (!date) return "N/A"
38+
const d = date.toISOString().split("T")
39+
const time = d[1].substring(0, 5)
40+
return `${d[0]} ${time}`
41+
}
42+
43+
const formatChanges = (info: EnrichedInfo): string => {
44+
if (info.files === undefined) return "-"
45+
const parts: string[] = []
46+
if (info.files > 0) parts.push(`${info.files}F`)
47+
if (info.additions && info.additions > 0) parts.push(`+${info.additions}`)
48+
if (info.deletions && info.deletions > 0) parts.push(`-${info.deletions}`)
49+
return parts.length > 0 ? parts.join("/") : "-"
50+
}
51+
52+
const getDisplayTitle = (info: EnrichedInfo): string => {
53+
if (info.title && info.title.trim()) return truncate(info.title, 30)
54+
if (info.preview) return truncate(info.preview, 30)
55+
return "(untitled)"
56+
}
57+
58+
const truncate = (str: string, maxLen: number): string => {
59+
if (str.length <= maxLen) return str
60+
return str.substring(0, maxLen - 3) + "..."
61+
}
62+
63+
const headers = ["Session ID", "Title/Preview", "Msgs", "Updated", "Changes", "Agents"]
2364
const rows = infos.map((info) => [
2465
info.id,
25-
info.title ?? "(untitled)",
66+
getDisplayTitle(info),
2667
info.message_count.toString(),
27-
info.first_message?.toISOString().split("T")[0] ?? "N/A",
28-
info.last_message?.toISOString().split("T")[0] ?? "N/A",
29-
info.agents_used.join(", ") || "none",
68+
formatDateTime(info.last_message),
69+
formatChanges(info),
70+
truncate(info.agents_used.join(", ") || "none", 20),
3071
])
3172

3273
const colWidths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => r[i].length)))

0 commit comments

Comments
 (0)