From 06997306270f32e7f943b050ad8e10575b322f17 Mon Sep 17 00:00:00 2001 From: William Valentin Date: Thu, 5 Feb 2026 22:16:29 -0800 Subject: [PATCH] feat(cli): implement sessions list command --- src/cli/sessions.test.ts | 40 ++++++++++++++++++++++++++++++++++ src/cli/sessions.ts | 46 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 src/cli/sessions.test.ts diff --git a/src/cli/sessions.test.ts b/src/cli/sessions.test.ts new file mode 100644 index 0000000..2517ba7 --- /dev/null +++ b/src/cli/sessions.test.ts @@ -0,0 +1,40 @@ +import { describe, it, expect, afterEach } from 'vitest'; +import { listSessions } from './sessions.js'; +import { SessionStore } from '../session/index.js'; +import { join } from 'path'; +import { tmpdir } from 'os'; +import { existsSync, unlinkSync } from 'fs'; + +describe('sessions command', () => { + const dbPath = join(tmpdir(), 'flynn-test-sessions-cli.db'); + let store: SessionStore; + + afterEach(() => { + store?.close(); + if (existsSync(dbPath)) unlinkSync(dbPath); + }); + + it('returns empty list when no sessions', () => { + store = new SessionStore(dbPath); + const sessions = listSessions(store); + expect(sessions).toEqual([]); + }); + + it('returns session IDs with message counts', () => { + store = new SessionStore(dbPath); + store.addMessage('telegram:123', { role: 'user', content: 'Hello' }); + store.addMessage('telegram:123', { role: 'assistant', content: 'Hi' }); + store.addMessage('tui:local', { role: 'user', content: 'Test' }); + + const sessions = listSessions(store); + expect(sessions).toHaveLength(2); + + const telegramSession = sessions.find(s => s.id === 'telegram:123'); + expect(telegramSession).toBeDefined(); + expect(telegramSession!.messageCount).toBe(2); + + const tuiSession = sessions.find(s => s.id === 'tui:local'); + expect(tuiSession).toBeDefined(); + expect(tuiSession!.messageCount).toBe(1); + }); +}); diff --git a/src/cli/sessions.ts b/src/cli/sessions.ts index 733b6e9..1fdba25 100644 --- a/src/cli/sessions.ts +++ b/src/cli/sessions.ts @@ -1,12 +1,50 @@ import type { Command } from 'commander'; +import type { SessionStore } from '../session/index.js'; +import { getDataDir } from './shared.js'; +import { resolve } from 'path'; + +export interface SessionInfo { + id: string; + messageCount: number; +} + +/** List all sessions with their message counts. */ +export function listSessions(store: SessionStore): SessionInfo[] { + const sessionIds = store.listSessions(); + return sessionIds.map((id) => ({ + id, + messageCount: store.getMessages(id).length, + })); +} export function registerSessionsCommand(program: Command): void { program .command('sessions') .description('List active sessions') - .option('-c, --config ', 'Config file path') - .action(async (_opts: { config?: string }) => { - console.error('Not yet implemented'); - process.exit(1); + .action(async () => { + const dataDir = getDataDir(); + const dbPath = resolve(dataDir, 'sessions.db'); + + const { SessionStore: Store } = await import('../session/index.js'); + const store = new Store(dbPath); + + try { + const sessions = listSessions(store); + + if (sessions.length === 0) { + console.log('No sessions found.'); + return; + } + + console.log('Sessions:'); + console.log(''); + for (const session of sessions) { + console.log(` ${session.id} (${session.messageCount} messages)`); + } + console.log(''); + console.log(`Total: ${sessions.length} session(s)`); + } finally { + store.close(); + } }); }