33
44use anyhow:: Result ;
55use std:: path:: Path ;
6+ use std:: sync:: atomic:: { AtomicBool , Ordering } ;
7+ use std:: sync:: Arc ;
68use std:: time:: { Duration , Instant } ;
79
810pub struct WatchConfig {
@@ -32,10 +34,19 @@ pub fn run_watch(
3234 ) ;
3335 eprintln ! ( " Press Ctrl-C to stop.\n " ) ;
3436
37+ // Graceful shutdown via signal handler
38+ let running = Arc :: new ( AtomicBool :: new ( true ) ) ;
39+ let r = running. clone ( ) ;
40+ ctrlc:: set_handler ( move || {
41+ r. store ( false , Ordering :: SeqCst ) ;
42+ } ) . ok ( ) ;
43+
44+ let watch_start = Instant :: now ( ) ;
45+ let mut handoff_count: u32 = 0 ;
3546 let mut last_handoff: Option < Instant > = None ;
3647 let mut last_size: u64 = 0 ;
3748
38- loop {
49+ while running . load ( Ordering :: SeqCst ) {
3950 std:: thread:: sleep ( watch_config. poll_interval ) ;
4051
4152 // Find latest JSONL
@@ -106,6 +117,7 @@ pub fn run_watch(
106117 let result = handoff_with_chain ( config, & handoff_text, & project_dir. to_string_lossy ( ) ) ;
107118
108119 if result. success {
120+ handoff_count += 1 ;
109121 eprintln ! ( " \u{2705} Auto-handed off to {}" , result. agent) ;
110122 if !handoff_path. as_os_str ( ) . is_empty ( ) {
111123 eprintln ! ( " \u{1f4c4} Saved: {}" , handoff_path. display( ) ) ;
@@ -128,6 +140,14 @@ pub fn run_watch(
128140 last_handoff = Some ( Instant :: now ( ) ) ;
129141 last_size = current_size;
130142 }
143+
144+ // Graceful shutdown summary
145+ let elapsed = watch_start. elapsed ( ) ;
146+ eprintln ! ( "\n \u{1f6d1} Watch stopped." ) ;
147+ eprintln ! ( " Uptime: {}m {}s | Handoffs: {}" ,
148+ elapsed. as_secs( ) / 60 , elapsed. as_secs( ) % 60 , handoff_count) ;
149+
150+ Ok ( ( ) )
131151}
132152
133153/// Handoff with chain — try each agent in priority order.
0 commit comments