Skip to main content

kasl/commands/
watch.rs

1//! Activity monitoring and daemon management command.
2//!
3//! Handles the core functionality of kasl - monitoring user activity to automatically detect work sessions, breaks, and workday boundaries.
4//!
5//! ## Features
6//!
7//! - **Background Monitoring**: Runs as daemon to track activity automatically
8//! - **Real-time Detection**: Immediate response to keyboard and mouse activity
9//! - **Workday Management**: Automatic start/end detection for work sessions
10//! - **Pause Tracking**: Records breaks and inactive periods
11//! - **Foreground Debugging**: Debug mode with enhanced logging
12//!
13//! ## Usage
14//!
15//! ```bash
16//! # Start background monitoring
17//! kasl watch
18//!
19//! # Run in foreground for debugging
20//! kasl watch --foreground
21//!
22//! # Stop background monitoring
23//! kasl watch --stop
24//! ```
25
26use crate::libs::{config::Config, daemon, messages::Message, monitor::Monitor};
27use crate::msg_print;
28use anyhow::Result;
29use clap::Args;
30use tracing::instrument;
31
32/// Command-line arguments for the watch command.
33///
34/// The watch command provides different operational modes to suit various use cases,
35/// from daily background monitoring to debugging and development.
36#[derive(Debug, Args)]
37pub struct WatchArgs {
38    /// Run the watcher in the foreground for debugging
39    ///
40    /// When specified, the monitor runs in the current terminal session with
41    /// enhanced logging output. This is useful for:
42    /// - Debugging activity detection issues
43    /// - Testing configuration changes
44    /// - Development and troubleshooting
45    ///
46    /// The foreground mode provides real-time feedback about detected activity,
47    /// pause events, and workday state changes.
48    #[arg(long)]
49    foreground: bool,
50
51    /// Stop any running background watcher process
52    ///
53    /// Terminates the background daemon if it's currently running. This is
54    /// useful for:
55    /// - Stopping monitoring before system shutdown
56    /// - Restarting with new configuration
57    /// - Troubleshooting daemon issues
58    ///
59    /// The stop operation is safe and will properly close database connections
60    /// and clean up system resources.
61    #[arg(long, short)]
62    stop: bool,
63}
64
65/// Main entry point for the watch command.
66///
67/// Acts as a dispatcher that routes to the appropriate operation based on the
68/// provided command-line arguments, handling the three main operational modes.
69///
70/// # Arguments
71///
72/// * `args` - Parsed command-line arguments specifying the operation mode
73///
74/// # Returns
75///
76/// Returns `Ok(())` on successful operation completion, or an error if
77/// the requested operation fails.
78#[instrument]
79pub async fn cmd(args: WatchArgs) -> Result<()> {
80    if args.stop {
81        // Stop any running background daemon
82        daemon::stop()?;
83    } else if args.foreground {
84        // Run in foreground mode with enhanced logging
85        msg_print!(Message::WatcherStartingForeground);
86        run_monitor().await?;
87    } else {
88        // Default mode: spawn background daemon
89        daemon::spawn()?;
90    }
91    Ok(())
92}
93
94/// Core monitoring logic that initializes and runs the activity monitor.
95///
96/// This function is called either directly for foreground mode or by the daemon
97/// process for background operation. It performs the following steps:
98///
99/// 1. **Configuration Loading**: Reads monitor settings from config file
100/// 2. **Monitor Initialization**: Sets up input device listeners and database connections
101/// 3. **Main Loop Execution**: Runs the continuous activity monitoring loop
102///
103/// ## Monitor Configuration
104///
105/// The monitor behavior is controlled by configuration settings:
106/// - `pause_threshold`: Seconds of inactivity before recording a pause
107/// - `poll_interval`: Milliseconds between activity checks
108/// - `activity_threshold`: Seconds of activity needed to start a workday
109/// - `min_pause_duration`: Minimum pause length to record (filters noise)
110///
111/// ## Activity Detection
112///
113/// The monitor tracks these input events:
114/// - Keyboard presses and releases
115/// - Mouse button clicks
116/// - Mouse movement
117/// - Mouse wheel scrolling
118///
119/// ## Database Operations
120///
121/// During monitoring, the system automatically:
122/// - Creates workday records when sustained activity is detected
123/// - Records pause start times when inactivity threshold is exceeded
124/// - Records pause end times when activity resumes
125/// - Updates workday end times when monitoring stops
126///
127/// # Returns
128///
129/// Returns `Ok(())` when monitoring completes normally, or an error if
130/// initialization fails or a critical error occurs during monitoring.
131///
132/// # Error Scenarios
133///
134/// - Database connection failures
135/// - Input device access denied
136/// - Invalid configuration values
137/// - System resource exhaustion
138#[instrument]
139async fn run_monitor() -> Result<()> {
140    // Load configuration with defaults for missing values
141    let config = Config::read()?;
142    let monitor_config = config.monitor.unwrap_or_default();
143
144    // Initialize the activity monitor with configuration
145    let mut monitor = Monitor::new(monitor_config)?;
146
147    // Sibling poller so foreground mode also keeps the Jira inbox warm
148    let inbox_handle = tokio::spawn(async move {
149        crate::libs::jira_inbox::run_poller().await;
150    });
151
152    let result = monitor.run().await;
153    inbox_handle.abort();
154    result
155}
156
157/// Entry point for daemon mode execution.
158///
159/// This function is called when the application is started with the `--daemon-run`
160/// flag, which happens when the main process spawns a background daemon. It sets
161/// up proper signal handling for graceful shutdown and runs the monitoring loop.
162///
163/// ## Signal Handling
164///
165/// The daemon process responds to these signals:
166/// - **SIGTERM**: Graceful shutdown (Unix)
167/// - **SIGINT**: Interrupt signal (Unix)
168/// - **Ctrl+C**: Console interrupt (Windows)
169///
170/// ## Process Management
171///
172/// The daemon:
173/// - Detaches from the parent process
174/// - Creates a PID file for process tracking
175/// - Handles cleanup on shutdown
176/// - Logs operations for debugging
177///
178/// # Returns
179///
180/// Returns `Ok(())` when the daemon shuts down normally, or an error if
181/// startup fails or a critical error occurs.
182///
183/// # Usage
184///
185/// This function is called internally by the application and should not be
186/// called directly. It's triggered by the `--daemon-run` argument which is
187/// used when spawning the background process.
188#[instrument]
189pub async fn run_as_daemon() -> Result<()> {
190    daemon::run_with_signal_handling().await
191}