@@ -63,6 +63,9 @@ export class CommandHandler {
6363 '`/model claude`, `/model kimi`, or `/model codex` - Switch engine (resets session)' ,
6464 '`/model <name>` - Set model for current engine' ,
6565 '`/memory` - Memory document commands' ,
66+ '`/sessions` - List all sessions in this chat' ,
67+ '`/switch [N]` - Switch to session N (or show list to pick)' ,
68+ '`/session <prefix>` - Switch by session ID prefix' ,
6669 '`/help` - Show this help message' ,
6770 '' ,
6871 '**Agent Commands** (pass through to the agent — Claude only):' ,
@@ -95,7 +98,8 @@ export class CommandHandler {
9598 } catch ( err ) {
9699 this . logger . warn ( { err, chatId } , 'Failed to release persistent executor on /reset' ) ;
97100 }
98- await this . sender . sendTextNotice ( chatId , '✅ Session Reset' , 'Conversation cleared. Working directory preserved.' , 'green' ) ;
101+ await this . sender . sendTextNotice ( chatId , '✅ Session Reset' ,
102+ 'New session created. Previous sessions preserved — use `/sessions` to switch back.' , 'green' ) ;
99103 return true ;
100104
101105 case '/stop' : {
@@ -162,6 +166,23 @@ export class CommandHandler {
162166 return true ;
163167 }
164168
169+ case '/sessions' : {
170+ await this . handleSessionsCommand ( chatId ) ;
171+ return true ;
172+ }
173+
174+ case '/switch' : {
175+ const args = text . slice ( '/switch' . length ) . trim ( ) ;
176+ await this . handleSwitchCommand ( chatId , args ) ;
177+ return true ;
178+ }
179+
180+ case '/session' : {
181+ const args = text . slice ( '/session' . length ) . trim ( ) ;
182+ await this . handleSessionPrefixCommand ( chatId , args ) ;
183+ return true ;
184+ }
185+
165186 default :
166187 // Unrecognized /xxx commands — not handled here, pass through to Claude
167188 return false ;
@@ -409,6 +430,63 @@ export class CommandHandler {
409430 ) ;
410431 }
411432
433+ private async handleSessionsCommand ( chatId : string ) : Promise < void > {
434+ const sessions = this . sessionManager . listSessions ( chatId ) ;
435+ if ( sessions . length === 0 ) {
436+ await this . sender . sendTextNotice ( chatId , '📋 Sessions' , 'No sessions yet. Send a message to start one.' ) ;
437+ return ;
438+ }
439+ const lines = sessions . map ( s => {
440+ const marker = s . isActive ? ' ▶' : '' ;
441+ const sid = s . sessionId ? ` \`${ s . sessionId . slice ( 0 , 8 ) } \`` : '' ;
442+ const title = s . title ? ` — ${ s . title } ` : '' ;
443+ const time = new Date ( s . lastUsed ) . toLocaleString ( ) ;
444+ return `**${ s . index + 1 } .**${ sid } ${ title } (${ time } )${ marker } ` ;
445+ } ) ;
446+ lines . push ( '' , 'Use `/switch N` to switch, or `/session <prefix>` to switch by session ID.' ) ;
447+ await this . sender . sendTextNotice ( chatId , '📋 Sessions' , lines . join ( '\n' ) ) ;
448+ }
449+
450+ private async handleSwitchCommand ( chatId : string , args : string ) : Promise < void > {
451+ if ( ! args ) {
452+ await this . handleSessionsCommand ( chatId ) ;
453+ return ;
454+ }
455+ const num = parseInt ( args , 10 ) ;
456+ if ( isNaN ( num ) || num < 1 ) {
457+ await this . sender . sendTextNotice ( chatId , '❌ Invalid' , 'Usage: `/switch N` where N is the session number from `/sessions`.' , 'red' ) ;
458+ return ;
459+ }
460+ const success = this . sessionManager . switchSession ( chatId , num - 1 ) ;
461+ if ( success ) {
462+ const session = this . sessionManager . getSession ( chatId ) ;
463+ const sid = session . sessionId ? `\`${ session . sessionId . slice ( 0 , 8 ) } ...\`` : '_new_' ;
464+ await this . sender . sendTextNotice ( chatId , '✅ Switched' , `Switched to session ${ num } (${ sid } ).` , 'green' ) ;
465+ } else {
466+ const sessions = this . sessionManager . listSessions ( chatId ) ;
467+ await this . sender . sendTextNotice ( chatId , '❌ Invalid' ,
468+ `Session ${ num } does not exist. You have ${ sessions . length } session(s). Use \`/sessions\` to list.` , 'red' ) ;
469+ }
470+ }
471+
472+ private async handleSessionPrefixCommand ( chatId : string , prefix : string ) : Promise < void > {
473+ if ( ! prefix || prefix . length < 8 ) {
474+ await this . sender . sendTextNotice ( chatId , '❌ Invalid' ,
475+ 'Usage: `/session <prefix>` — provide at least 8 characters of the session ID.' , 'red' ) ;
476+ return ;
477+ }
478+ const idx = this . sessionManager . switchToSessionByPrefix ( chatId , prefix ) ;
479+ if ( idx >= 0 ) {
480+ const session = this . sessionManager . getSession ( chatId ) ;
481+ const sid = session . sessionId ? `\`${ session . sessionId . slice ( 0 , 8 ) } ...\`` : '_new_' ;
482+ await this . sender . sendTextNotice ( chatId , '✅ Switched' ,
483+ `Switched to session ${ idx + 1 } (${ sid } ).` , 'green' ) ;
484+ } else {
485+ await this . sender . sendTextNotice ( chatId , '❌ Not Found' ,
486+ `No session found matching prefix \`${ prefix } \`. Use \`/sessions\` to list available sessions.` , 'red' ) ;
487+ }
488+ }
489+
412490 private defaultModelForEngine ( engine : EngineName ) : string | undefined {
413491 switch ( engine ) {
414492 case 'claude' :
0 commit comments