@@ -304,6 +304,18 @@ struct SessionsCommand {
304304 /// Show all sessions including from other directories
305305 #[ arg( long) ]
306306 all : bool ,
307+
308+ /// Filter sessions from the last N days
309+ #[ arg( long, short = 'd' ) ]
310+ days : Option < u32 > ,
311+
312+ /// Filter sessions since a specific date (YYYY-MM-DD format)
313+ #[ arg( long) ]
314+ since : Option < String > ,
315+
316+ /// Filter sessions until a specific date (YYYY-MM-DD format)
317+ #[ arg( long) ]
318+ until : Option < String > ,
307319}
308320
309321/// Config command.
@@ -478,7 +490,15 @@ async fn main() -> Result<()> {
478490 }
479491 } ,
480492 Some ( Commands :: Resume ( resume_cli) ) => run_resume ( resume_cli) . await ,
481- Some ( Commands :: Sessions ( sessions_cli) ) => list_sessions ( sessions_cli. all ) . await ,
493+ Some ( Commands :: Sessions ( sessions_cli) ) => {
494+ list_sessions (
495+ sessions_cli. all ,
496+ sessions_cli. days ,
497+ sessions_cli. since ,
498+ sessions_cli. until ,
499+ )
500+ . await
501+ }
482502 Some ( Commands :: Export ( export_cli) ) => export_cli. run ( ) . await ,
483503 Some ( Commands :: Import ( import_cli) ) => import_cli. run ( ) . await ,
484504 Some ( Commands :: Config ( config_cli) ) => show_config ( config_cli) . await ,
@@ -680,7 +700,14 @@ async fn run_resume(resume_cli: ResumeCommand) -> Result<()> {
680700 Ok ( ( ) )
681701}
682702
683- async fn list_sessions ( show_all : bool ) -> Result < ( ) > {
703+ async fn list_sessions (
704+ show_all : bool ,
705+ days : Option < u32 > ,
706+ since : Option < String > ,
707+ until : Option < String > ,
708+ ) -> Result < ( ) > {
709+ use chrono:: { DateTime , NaiveDate , Utc } ;
710+
684711 let config = cortex_engine:: Config :: default ( ) ;
685712 let sessions = cortex_engine:: list_sessions ( & config. cortex_home ) ?;
686713
@@ -693,58 +720,146 @@ async fn list_sessions(show_all: bool) -> Result<()> {
693720 return Ok ( ( ) ) ;
694721 }
695722
696- let current_dir = std:: env:: current_dir ( ) . ok ( ) ;
697- let filtered: Vec < _ > = if show_all {
698- sessions. iter ( ) . collect ( )
723+ // Parse date filters
724+ let now = Utc :: now ( ) ;
725+ let since_date: Option < DateTime < Utc > > = if let Some ( days_val) = days {
726+ Some ( now - chrono:: Duration :: days ( days_val as i64 ) )
727+ } else if let Some ( ref since_str) = since {
728+ match NaiveDate :: parse_from_str ( since_str, "%Y-%m-%d" ) {
729+ Ok ( date) => Some (
730+ date. and_hms_opt ( 0 , 0 , 0 )
731+ . unwrap ( )
732+ . and_local_timezone ( Utc )
733+ . unwrap ( ) ,
734+ ) ,
735+ Err ( _) => {
736+ eprintln ! (
737+ "Warning: Invalid --since date format '{}'. Expected YYYY-MM-DD." ,
738+ since_str
739+ ) ;
740+ None
741+ }
742+ }
699743 } else {
700- sessions
701- . iter ( )
702- . filter ( |s| current_dir. as_ref ( ) . is_none_or ( |cwd| s. cwd == * cwd) )
703- . collect ( )
744+ None
745+ } ;
746+
747+ let until_date: Option < DateTime < Utc > > = if let Some ( ref until_str) = until {
748+ match NaiveDate :: parse_from_str ( until_str, "%Y-%m-%d" ) {
749+ Ok ( date) => Some (
750+ date. and_hms_opt ( 23 , 59 , 59 )
751+ . unwrap ( )
752+ . and_local_timezone ( Utc )
753+ . unwrap ( ) ,
754+ ) ,
755+ Err ( _) => {
756+ eprintln ! (
757+ "Warning: Invalid --until date format '{}'. Expected YYYY-MM-DD." ,
758+ until_str
759+ ) ;
760+ None
761+ }
762+ }
763+ } else {
764+ None
704765 } ;
705766
706- let display_sessions = if filtered. is_empty ( ) {
767+ let current_dir = std:: env:: current_dir ( ) . ok ( ) ;
768+
769+ // Apply all filters: directory, since, and until
770+ let filtered: Vec < _ > = sessions
771+ . iter ( )
772+ . filter ( |s| {
773+ // Directory filter
774+ if !show_all && current_dir. as_ref ( ) . is_some_and ( |cwd| s. cwd != * cwd) {
775+ return false ;
776+ }
777+
778+ // Parse session timestamp for date filtering
779+ if since_date. is_some ( ) || until_date. is_some ( ) {
780+ if let Ok ( session_time) = DateTime :: parse_from_rfc3339 ( & s. timestamp ) {
781+ let session_utc = session_time. with_timezone ( & Utc ) ;
782+ if let Some ( ref since) = since_date {
783+ if session_utc < * since {
784+ return false ;
785+ }
786+ }
787+ if let Some ( ref until) = until_date {
788+ if session_utc > * until {
789+ return false ;
790+ }
791+ }
792+ }
793+ }
794+
795+ true
796+ } )
797+ . collect ( ) ;
798+
799+ let display_sessions = if filtered. is_empty ( ) && since_date. is_none ( ) && until_date. is_none ( ) {
707800 & sessions
708801 } else {
709802 & filtered. iter ( ) . map ( |s| ( * s) . clone ( ) ) . collect ( )
710803 } ;
711804
712- println ! ( "Recent Sessions{}:" , if show_all { " (all)" } else { "" } ) ;
713- println ! ( "{:-<80}" , "" ) ;
805+ // Build header with active filters
806+ let mut filter_info = String :: new ( ) ;
807+ if show_all {
808+ filter_info. push_str ( " (all)" ) ;
809+ }
810+ if let Some ( days_val) = days {
811+ filter_info. push_str ( & format ! ( " (last {} days)" , days_val) ) ;
812+ } else {
813+ if since_date. is_some ( ) {
814+ filter_info. push_str ( & format ! ( " (since {})" , since. as_deref( ) . unwrap_or( "" ) ) ) ;
815+ }
816+ if until_date. is_some ( ) {
817+ filter_info. push_str ( & format ! ( " (until {})" , until. as_deref( ) . unwrap_or( "" ) ) ) ;
818+ }
819+ }
714820
715- for session in display_sessions. iter ( ) . take ( 15 ) {
716- let model = session. model . as_deref ( ) . unwrap_or ( "unknown" ) ;
717- let date = if session. timestamp . len ( ) >= 19 {
718- session. timestamp [ ..19 ] . replace ( 'T' , " " )
719- } else {
720- session. timestamp . clone ( )
721- } ;
821+ println ! ( "Recent Sessions{}:" , filter_info) ;
822+ println ! ( "{:-<80}" , "" ) ;
722823
723- let branch_info = session
724- . git_branch
725- . as_ref ( )
726- . map ( |b| format ! ( " [{b}]" ) )
727- . unwrap_or_default ( ) ;
824+ if display_sessions. is_empty ( ) {
825+ println ! ( "No sessions match the specified filters." ) ;
826+ } else {
827+ for session in display_sessions. iter ( ) . take ( 15 ) {
828+ let model = session. model . as_deref ( ) . unwrap_or ( "unknown" ) ;
829+ let date = if session. timestamp . len ( ) >= 19 {
830+ session. timestamp [ ..19 ] . replace ( 'T' , " " )
831+ } else {
832+ session. timestamp . clone ( )
833+ } ;
728834
729- println ! (
730- "{} | {} | {} msgs | {}{branch_info}" ,
731- & session. id[ ..8 . min( session. id. len( ) ) ] ,
732- date,
733- session. message_count,
734- model,
735- ) ;
736- println ! ( " {}" , session. cwd. display( ) ) ;
737- }
835+ let branch_info = session
836+ . git_branch
837+ . as_ref ( )
838+ . map ( |b| format ! ( " [{b}]" ) )
839+ . unwrap_or_default ( ) ;
840+
841+ println ! (
842+ "{} | {} | {} msgs | {}{branch_info}" ,
843+ & session. id[ ..8 . min( session. id. len( ) ) ] ,
844+ date,
845+ session. message_count,
846+ model,
847+ ) ;
848+ println ! ( " {}" , session. cwd. display( ) ) ;
849+ }
738850
739- if display_sessions. len ( ) > 15 {
740- println ! ( "\n ... and {} more sessions" , display_sessions. len( ) - 15 ) ;
851+ if display_sessions. len ( ) > 15 {
852+ println ! ( "\n ... and {} more sessions" , display_sessions. len( ) - 15 ) ;
853+ }
741854 }
742855
743856 println ! ( "\n To resume: Cortex resume <session-id>" ) ;
744857 println ! ( " Cortex resume --last" ) ;
745858 if !show_all {
746859 println ! ( " Cortex sessions --all (show all directories)" ) ;
747860 }
861+ println ! ( " Cortex sessions --days 7 (last 7 days)" ) ;
862+ println ! ( " Cortex sessions --since 2024-01-01 --until 2024-12-31 (date range)" ) ;
748863 Ok ( ( ) )
749864}
750865
0 commit comments