Skip to main content

kasl/commands/
mod.rs

1//! Command-line interface commands for kasl application.
2//!
3//! Contains all CLI command implementations for task management, activity monitoring,
4//! reporting, and system configuration.
5//!
6//! ## Usage
7//!
8//! ```bash
9//! kasl watch                    # Start activity monitoring
10//! kasl task add --name "Review code" # Create a new task
11//! kasl report                   # Generate today's report
12//! kasl export tasks --format csv # Export tasks to CSV
13//! ```
14
15pub mod autostart;
16pub mod export;
17pub mod inbox;
18pub mod init;
19pub mod migrations;
20pub mod pauses;
21pub mod report;
22pub mod sum;
23pub mod tag;
24pub mod task;
25pub mod template;
26pub mod update;
27pub mod watch;
28
29use crate::{db::workdays::Workdays, libs::messages::types::Message, msg_info, msg_warning};
30use anyhow::Result;
31use chrono::Local;
32use clap::{Parser, Subcommand};
33
34/// Defines the main subcommands that the application can execute.
35#[derive(Debug, Subcommand)]
36enum Commands {
37    /// Manage autostart configuration for system boot
38    ///
39    /// Controls whether kasl automatically starts monitoring when the system boots.
40    /// Supports both system-level and user-level autostart on Windows.
41    #[command(about = "Manage autostart on system boot")]
42    Autostart(autostart::AutostartArgs),
43
44    /// Set up application configuration interactively
45    ///
46    /// Guides the user through setting up API credentials, monitor settings,
47    /// and other configuration options required for kasl to function properly.
48    ///
49    /// `init` stays as a deprecated alias until 2.0. An alias rather than a
50    /// hidden variant: clap_complete emits hidden subcommands into the
51    /// completion scripts, so Tab would keep teaching the old spelling.
52    #[command(about = "Set up configuration", alias = "init")]
53    Setup(init::SetupArgs),
54
55    /// Comprehensive task management command
56    ///
57    /// Handles all task-related operations including creation, editing, deletion,
58    /// viewing, and integration with external services like GitLab and Jira.
59    #[command(about = "Create task")]
60    Task(task::TaskArgs),
61
62    /// Manually end the current workday
63    ///
64    /// Records the end timestamp for today's work session. Typically used
65    /// when the automatic monitoring needs to be manually finalized.
66    #[command(about = "Write end timestamp to database")]
67    End,
68
69    /// Display monthly working hours summary
70    ///
71    /// Shows a comprehensive overview of work hours, productivity metrics,
72    /// and daily breakdowns for the current month.
73    #[command(about = "Get summary")]
74    Sum(sum::SumArgs),
75
76    /// Update kasl itself to the latest release
77    ///
78    /// Checks GitHub releases for newer versions and automatically downloads
79    /// and installs updates if available.
80    ///
81    /// `update` stays as a deprecated alias until 2.0; it read as "update my
82    /// data", which is what every other command does.
83    #[command(name = "self-update", about = "Update kasl itself to the latest release", alias = "update")]
84    SelfUpdate,
85
86    /// Generate and optionally submit work reports
87    ///
88    /// Creates detailed daily reports with work intervals, tasks, and productivity
89    /// metrics. Can automatically submit reports to configured APIs.
90    #[command(about = "Prepare a report")]
91    Report(report::ReportArgs),
92
93    /// Export application data to external formats
94    ///
95    /// Supports exporting tasks, reports, and summaries to CSV, JSON, and Excel
96    /// formats for external analysis or backup purposes.
97    #[command(about = "Export data to various formats")]
98    Export(export::ExportArgs),
99
100    /// Manage reusable task templates
101    ///
102    /// Create, edit, and use templates for frequently created tasks to
103    /// streamline task creation workflow.
104    #[command(about = "Manage task templates")]
105    Template(template::TemplateArgs),
106
107    /// Organize tasks with custom tags
108    ///
109    /// Create and manage tags to categorize and filter tasks by project,
110    /// priority, or any custom criteria.
111    #[command(about = "Manage task tags")]
112    Tag(tag::TagArgs),
113
114    /// Background activity monitoring daemon
115    ///
116    /// Monitors user input activity to automatically detect work sessions,
117    /// breaks, and workday boundaries. Can run as a background service.
118    #[command(about = "Watch user activity in the background to record pauses")]
119    Watch(watch::WatchArgs),
120
121    /// View recorded pauses and record ones the monitor missed
122    ///
123    /// Lists detected pauses for a date, and lets the user add an absence the
124    /// activity monitor did not catch or remove one recorded by mistake.
125    #[command(about = "View pauses and record ones the monitor missed")]
126    Pauses(pauses::PausesArgs),
127
128    /// Jira inbox of assigned open issues
129    ///
130    /// Syncs assigned unresolved Jira issues into a local table, lists them
131    /// by priority, and supports pin / dismiss / open / import into tasks.
132    #[command(about = "Manage Jira inbox issues")]
133    Inbox(inbox::InboxArgs),
134
135    /// Print a shell completion script
136    ///
137    /// Emits the completion script for the chosen shell on stdout; source it
138    /// from the shell profile to get completion for kasl's commands and flags.
139    #[command(about = "Print a shell completion script (source it from your shell profile)")]
140    Completions {
141        /// Target shell
142        shell: clap_complete::Shell,
143    },
144
145    /// Database migration management utilities (debug builds only)
146    ///
147    /// Provides tools for database schema management, migration history,
148    /// and rollback operations. Available only in debug builds for safety.
149    #[cfg(debug_assertions)]
150    #[command(about = "Database migration management")]
151    Migrations(migrations::MigrationsArgs),
152}
153
154/// The main CLI structure that parses command-line arguments.
155///
156/// Uses `clap` to define the application's interface and delegates
157/// command execution to the appropriate subcommand module. The CLI
158/// requires at least one subcommand to be specified.
159///
160/// # Examples
161///
162/// ```bash
163/// # Display help
164/// kasl --help
165///
166/// # Run a specific command
167/// kasl task add --name "New task"
168/// ```
169#[derive(Debug, Parser)]
170#[command(name = "kasl", author, version, about, long_about = None)]
171#[command(arg_required_else_help(true))]
172pub struct Cli {
173    #[command(subcommand)]
174    command: Commands,
175}
176
177impl Cli {
178    /// Parses command-line arguments and executes the corresponding command.
179    ///
180    /// This is the main entry point for the CLI logic. It handles command
181    /// routing and provides centralized error handling for all commands.
182    ///
183    /// # Examples
184    ///
185    /// ```rust,no_run
186    /// use kasl::commands::Cli;
187    ///
188    /// #[tokio::main]
189    /// async fn main() -> anyhow::Result<()> {
190    ///     Cli::menu().await
191    /// }
192    /// ```
193    pub async fn menu() -> Result<()> {
194        let cli = Self::parse();
195
196        match cli.command {
197            Commands::Autostart(args) => autostart::cmd(args),
198            Commands::Setup(args) => {
199                warn_if_deprecated_alias();
200                init::cmd(args)
201            }
202            Commands::Task(args) => task::cmd(args).await,
203            Commands::End => {
204                // Manually end the current workday
205                Workdays::new()?.insert_end(Local::now().date_naive())?;
206                msg_info!(Message::WorkdayEnded);
207                Ok(())
208            }
209            Commands::Sum(args) => sum::cmd(args).await,
210            Commands::Report(args) => report::cmd(args).await,
211            Commands::Export(args) => export::cmd(args).await,
212            Commands::Template(args) => template::cmd(args),
213            Commands::Tag(args) => tag::cmd(args).await,
214            Commands::SelfUpdate => {
215                warn_if_deprecated_alias();
216                update::cmd().await
217            }
218            Commands::Watch(args) => watch::cmd(args).await,
219            Commands::Pauses(args) => pauses::cmd(args).await,
220            Commands::Completions { shell } => {
221                use clap::CommandFactory;
222                clap_complete::generate(shell, &mut Self::command(), "kasl", &mut std::io::stdout());
223                Ok(())
224            }
225            Commands::Inbox(args) => inbox::cmd(args).await,
226
227            // Database migrations only available in debug builds
228            #[cfg(debug_assertions)]
229            Commands::Migrations(args) => migrations::cmd(args),
230        }
231    }
232}
233
234/// Old command names still accepted as aliases, with their replacements.
235///
236/// Removed in 2.0; until then the alias works and says so.
237const DEPRECATED_ALIASES: [(&str, &str); 2] = [("init", "setup"), ("update", "self-update")];
238
239/// Prints a rename notice when the command was invoked by its old name.
240///
241/// clap resolves an alias to the canonical variant without recording which
242/// spelling was typed, so the first non-flag argument is what tells them
243/// apart.
244fn warn_if_deprecated_alias() {
245    let Some(typed) = std::env::args().nth(1) else {
246        return;
247    };
248    if let Some((old, new)) = DEPRECATED_ALIASES.iter().find(|(old, _)| *old == typed) {
249        msg_warning!(Message::DeprecatedCommand(old, new));
250    }
251}