@@ -1090,6 +1090,15 @@ pub struct App {
10901090 pub last_exit_key_warning : Option < std:: time:: Instant > ,
10911091 /// Which exit key ('c' or 'd') started the current confirmation sequence.
10921092 pub exit_key_sequence_start : Option < char > ,
1093+
1094+ // ---- Coven daemon integration ----------------------------------------
1095+ pub daemon_online : bool ,
1096+ pub daemon_last_checked : u64 ,
1097+
1098+ // ---- Familiar switcher (F2) ------------------------------------------
1099+ pub familiar_switcher_open : bool ,
1100+ pub familiar_switcher_list : Vec < String > ,
1101+ pub familiar_switcher_idx : usize ,
10931102}
10941103
10951104// Spinner verbs are now imported from claurst_core::spinner
@@ -1442,6 +1451,26 @@ impl App {
14421451 managed_agents_active : false ,
14431452 last_exit_key_warning : None ,
14441453 exit_key_sequence_start : None ,
1454+ daemon_online : dirs:: home_dir ( )
1455+ . map ( |h| h. join ( ".coven" ) . join ( "coven.sock" ) . exists ( ) )
1456+ . unwrap_or ( false ) ,
1457+ daemon_last_checked : 0 ,
1458+ familiar_switcher_open : false ,
1459+ familiar_switcher_list : {
1460+ let mut ids: Vec < String > = vec ! [
1461+ "nova" . to_string( ) , "kitty" . to_string( ) , "cody" . to_string( ) ,
1462+ "charm" . to_string( ) , "sage" . to_string( ) , "astra" . to_string( ) ,
1463+ "echo" . to_string( ) ,
1464+ ] ;
1465+ use claurst_core:: coven_shared;
1466+ if let Some ( familiars) = coven_shared:: load_familiars ( ) {
1467+ for f in familiars {
1468+ if !ids. contains ( & f. id ) { ids. push ( f. id ) ; }
1469+ }
1470+ }
1471+ ids
1472+ } ,
1473+ familiar_switcher_idx : 0 ,
14451474 }
14461475 }
14471476
@@ -2949,6 +2978,34 @@ impl App {
29492978 return false ;
29502979 }
29512980
2981+ // ---- Familiar switcher (F2) ----------------------------------------
2982+ if self . familiar_switcher_open {
2983+ match key. code {
2984+ KeyCode :: Esc | KeyCode :: F ( 2 ) => { self . familiar_switcher_open = false ; }
2985+ KeyCode :: Char ( 'j' ) | KeyCode :: Down => {
2986+ let len = self . familiar_switcher_list . len ( ) ;
2987+ if len > 0 { self . familiar_switcher_idx = ( self . familiar_switcher_idx + 1 ) % len; }
2988+ }
2989+ KeyCode :: Char ( 'k' ) | KeyCode :: Up => {
2990+ let len = self . familiar_switcher_list . len ( ) ;
2991+ if len > 0 { self . familiar_switcher_idx = ( self . familiar_switcher_idx + len - 1 ) % len; }
2992+ }
2993+ KeyCode :: Enter => {
2994+ if let Some ( id) = self . familiar_switcher_list . get ( self . familiar_switcher_idx ) . cloned ( ) {
2995+ self . config . familiar = Some ( id. clone ( ) ) ;
2996+ self . push_notification (
2997+ crate :: notifications:: NotificationKind :: Info ,
2998+ format ! ( "\u{2728} Familiar: {}" , id) ,
2999+ None ,
3000+ ) ;
3001+ }
3002+ self . familiar_switcher_open = false ;
3003+ }
3004+ _ => { }
3005+ }
3006+ return false ;
3007+ }
3008+
29523009
29533010 if self . global_search . visible {
29543011 return self . handle_global_search_key ( key) ;
@@ -4123,6 +4180,19 @@ impl App {
41234180 self . show_help = !self . show_help ;
41244181 self . help_overlay . toggle ( ) ;
41254182 }
4183+ KeyCode :: F ( 2 ) => {
4184+ if self . familiar_switcher_open {
4185+ self . familiar_switcher_open = false ;
4186+ } else {
4187+ self . familiar_switcher_open = true ;
4188+ let current = self . config . familiar . as_deref ( ) . unwrap_or ( "kitty" ) ;
4189+ if let Some ( idx) = self . familiar_switcher_list . iter ( ) . position ( |id| id == current) {
4190+ self . familiar_switcher_idx = idx;
4191+ } else {
4192+ self . familiar_switcher_idx = 0 ;
4193+ }
4194+ }
4195+ }
41264196 KeyCode :: Char ( '?' )
41274197 if !self . is_streaming
41284198 && self . prompt_input . is_empty ( )
@@ -6138,6 +6208,14 @@ impl App {
61386208 loop {
61396209 self . frame_count = self . frame_count . wrapping_add ( 1 ) ;
61406210
6211+ // Re-check daemon socket ~every 30 seconds (300 frames at 10fps).
6212+ if self . frame_count . wrapping_sub ( self . daemon_last_checked ) >= 300 {
6213+ self . daemon_last_checked = self . frame_count ;
6214+ self . daemon_online = dirs:: home_dir ( )
6215+ . map ( |h| h. join ( ".coven" ) . join ( "coven.sock" ) . exists ( ) )
6216+ . unwrap_or ( false ) ;
6217+ }
6218+
61416219 // Drain background session-list results.
61426220 if let Some ( ref mut rx) = self . session_list_rx {
61436221 match rx. try_recv ( ) {
0 commit comments