@@ -45,6 +45,8 @@ CONFIGURATION:
4545 AGENT_TUI_API_LISTEN / AGENT_TUI_API_ALLOW_REMOTE / AGENT_TUI_API_STATE
4646 Deprecated aliases for WS settings
4747 AGENT_TUI_SESSION_STORE Session metadata log path (default: ~/.agent-tui/sessions.jsonl)
48+ AGENT_TUI_RECORD_STATE Recording state file path (default: ~/.agent-tui/recordings.json)
49+ AGENT_TUI_RECORDINGS_DIR Default recordings output directory (default: current directory)
4850 AGENT_TUI_LOG Log file path (optional)
4951 AGENT_TUI_LOG_FORMAT Log format (text or json; default: text)
5052 AGENT_TUI_LOG_STREAM Log output stream (stderr or stdout; default: stderr)
@@ -302,6 +304,7 @@ MODES:
302304 list List active sessions (default)
303305 show <id> Show details for a session
304306 attach Attach with TTY (defaults to --session or active)
307+ record Record session activity to VHS artifacts
305308 switch <id> Set the active session
306309 cleanup [--all] Remove dead/orphaned sessions" ) ]
307310 #[ command( after_long_help = "\
@@ -313,6 +316,10 @@ EXAMPLES:
313316 agent-tui -s abc123 sessions attach # Attach to session by id (TTY)
314317 agent-tui sessions switch abc123 # Set active session
315318 agent-tui -s abc123 sessions attach -T # Attach without TTY (stream output only)
319+ agent-tui sessions record # Record active session in background
320+ agent-tui sessions record --foreground
321+ agent-tui sessions record -o docs/recordings
322+ agent-tui -s abc123 sessions record stop
316323 agent-tui sessions attach --detach-keys 'ctrl-]' # Custom detach sequence
317324 agent-tui sessions cleanup # Remove dead sessions
318325 agent-tui sessions cleanup --all # Remove all sessions" ) ]
@@ -441,6 +448,45 @@ pub enum SessionsCommand {
441448 detach_keys : Option < DetachKeys > ,
442449 } ,
443450
451+ /// Record a running session to VHS artifacts (.gif + .tape)
452+ #[ command( long_about = "\
453+ Record a running session to VHS artifacts.
454+
455+ By default recording starts in background and returns immediately.
456+ Use --foreground to wait until recording exits.
457+
458+ OUTPUT PATH RULES:
459+ -o/--output-file omitted Uses AGENT_TUI_RECORDINGS_DIR or current directory
460+ Existing directory Creates timestamped <session>-<time>.gif/.tape
461+ Existing file Uses file stem for .gif/.tape pair
462+ Non-existing path w/ ext Treated as file path
463+ Non-existing path no ext Treated as directory" ) ]
464+ #[ command( after_long_help = "\
465+ EXAMPLES:
466+ agent-tui sessions record
467+ agent-tui sessions record --foreground
468+ agent-tui sessions record -o docs/recordings
469+ agent-tui sessions record -o docs/recordings/demo.gif
470+ agent-tui sessions record stop" ) ]
471+ #[ command( args_conflicts_with_subcommands = true ) ]
472+ Record {
473+ /// Output file or directory for recording artifacts
474+ #[ arg(
475+ short = 'o' ,
476+ long = "output-file" ,
477+ value_name = "PATH" ,
478+ value_hint = ValueHint :: AnyPath
479+ ) ]
480+ output_file : Option < PathBuf > ,
481+
482+ /// Run recorder in foreground (wait until recording exits)
483+ #[ arg( long) ]
484+ foreground : bool ,
485+
486+ #[ command( subcommand) ]
487+ command : Option < RecordCommand > ,
488+ } ,
489+
444490 /// Set the active session without attaching
445491 #[ command( alias = "select" ) ]
446492 Switch {
@@ -456,6 +502,12 @@ pub enum SessionsCommand {
456502 } ,
457503}
458504
505+ #[ derive( Debug , Subcommand ) ]
506+ pub enum RecordCommand {
507+ /// Stop recording for the selected or active session
508+ Stop ,
509+ }
510+
459511#[ derive( Debug , Subcommand ) ]
460512pub enum LiveCommand {
461513 /// Show the live preview API details
@@ -874,6 +926,69 @@ mod tests {
874926 ) ) ;
875927 }
876928
929+ #[ test]
930+ fn test_sessions_record_defaults ( ) {
931+ let cli = Cli :: parse_from ( [ "agent-tui" , "sessions" , "record" ] ) ;
932+ let Commands :: Sessions { command } = cli. command else {
933+ panic ! ( "Expected Sessions command, got {:?}" , cli. command) ;
934+ } ;
935+ assert ! ( matches!(
936+ command,
937+ Some ( SessionsCommand :: Record {
938+ output_file: None ,
939+ foreground: false ,
940+ command: None
941+ } )
942+ ) ) ;
943+ }
944+
945+ #[ test]
946+ fn test_sessions_record_foreground ( ) {
947+ let cli = Cli :: parse_from ( [ "agent-tui" , "sessions" , "record" , "--foreground" ] ) ;
948+ let Commands :: Sessions { command } = cli. command else {
949+ panic ! ( "Expected Sessions command, got {:?}" , cli. command) ;
950+ } ;
951+ assert ! ( matches!(
952+ command,
953+ Some ( SessionsCommand :: Record {
954+ foreground: true ,
955+ command: None ,
956+ ..
957+ } )
958+ ) ) ;
959+ }
960+
961+ #[ test]
962+ fn test_sessions_record_output_file ( ) {
963+ let cli = Cli :: parse_from ( [ "agent-tui" , "sessions" , "record" , "-o" , "out.gif" ] ) ;
964+ let Commands :: Sessions { command } = cli. command else {
965+ panic ! ( "Expected Sessions command, got {:?}" , cli. command) ;
966+ } ;
967+ assert ! ( matches!(
968+ command,
969+ Some ( SessionsCommand :: Record {
970+ output_file: Some ( _) ,
971+ foreground: false ,
972+ command: None
973+ } )
974+ ) ) ;
975+ }
976+
977+ #[ test]
978+ fn test_sessions_record_stop ( ) {
979+ let cli = Cli :: parse_from ( [ "agent-tui" , "sessions" , "record" , "stop" ] ) ;
980+ let Commands :: Sessions { command } = cli. command else {
981+ panic ! ( "Expected Sessions command, got {:?}" , cli. command) ;
982+ } ;
983+ assert ! ( matches!(
984+ command,
985+ Some ( SessionsCommand :: Record {
986+ command: Some ( RecordCommand :: Stop ) ,
987+ ..
988+ } )
989+ ) ) ;
990+ }
991+
877992 #[ test]
878993 fn test_sessions_cleanup ( ) {
879994 let cli = Cli :: parse_from ( [ "agent-tui" , "sessions" , "cleanup" ] ) ;
0 commit comments