proc-janitor 0.4.0

Automatic orphan process cleaner daemon for macOS/Linux
use clap::{Parser, Subcommand};

#[derive(Parser, Debug)]
#[command(name = "proc-janitor")]
#[command(about = "Automated orphan process cleanup daemon for macOS/Linux")]
#[command(
    long_about = "proc-janitor detects and cleans up orphaned processes (PPID=1) matching\nconfigurable regex patterns. Use 'scan' to detect, 'clean' to kill,\nor 'start' to run as a background daemon.\n\nQuick start:\n  proc-janitor scan              Detect orphaned processes (safe, no killing)\n  proc-janitor clean             Kill all detected orphans\n  proc-janitor clean --pid 123   Kill specific orphan by PID\n  proc-janitor clean -m 'node'   Kill orphans matching pattern\n  proc-janitor config init       Set up with auto-detection\n  proc-janitor start             Start background daemon"
)]
#[command(version)]
pub struct Cli {
    /// Output in JSON format
    #[arg(long, short = 'j', global = true)]
    pub json: bool,

    #[command(subcommand)]
    pub command: Commands,
}

#[derive(Subcommand, Debug)]
pub enum Commands {
    /// Start the daemon
    Start {
        /// Run in foreground (don't daemonize)
        #[arg(long, short = 'f')]
        foreground: bool,
    },

    /// Stop the daemon
    Stop,

    /// Show daemon status
    Status,

    /// Scan for orphaned processes (detection only, no killing)
    Scan,

    /// Clean up orphaned processes (kills all by default, use filters to be selective)
    Clean {
        /// Kill only specific PIDs (space-separated)
        #[arg(long, short = 'p', num_args = 1..)]
        pid: Vec<u32>,

        /// Kill only orphans whose command line matches this regex pattern
        #[arg(long, short = 'm')]
        pattern: Option<String>,
    },

    /// Show process tree visualization
    Tree {
        /// Only show target processes
        #[arg(short, long)]
        targets_only: bool,
    },

    /// Configuration management
    #[command(subcommand)]
    Config(ConfigCommands),

    /// View logs
    Logs {
        /// Follow log output
        #[arg(short, long)]
        follow: bool,

        /// Number of lines to show
        #[arg(short = 'n', long, default_value = "50", value_parser = clap::value_parser!(u64).range(1..))]
        lines: u64,
    },

    /// Session-based process tracking
    #[command(subcommand)]
    Session(SessionCommands),

    /// Generate shell completions
    Completions {
        /// Shell to generate completions for (bash, zsh, fish, powershell)
        #[arg(value_enum)]
        shell: clap_complete::Shell,
    },

    /// Diagnose common issues and check system health
    Doctor,
}

#[derive(Subcommand, Debug)]
pub enum ConfigCommands {
    /// Create a configuration file (auto-detects orphaned processes)
    Init {
        /// Overwrite existing config file
        #[arg(long)]
        force: bool,

        /// Use a preset configuration:
        ///   claude   - Target Claude Code & MCP server processes
        ///   dev      - Target common dev tools (node, cargo, python, bundlers)
        ///   minimal  - Empty targets for fully manual configuration
        #[arg(long)]
        preset: Option<String>,

        /// List available presets and exit
        #[arg(long)]
        list_presets: bool,

        /// Skip confirmation prompts (auto-accept detected targets)
        #[arg(long, short = 'y')]
        yes: bool,
    },

    /// Edit configuration file
    Edit,

    /// Show current configuration
    Show,

    /// Show all environment variable overrides
    Env,
}

#[derive(Subcommand, Debug)]
pub enum SessionCommands {
    /// Register a new session for tracking
    Register {
        /// Custom session ID (auto-generated if not provided)
        #[arg(short, long)]
        id: Option<String>,

        /// Human-readable session name
        #[arg(short, long)]
        name: Option<String>,

        /// Session source (claude-code, terminal, vscode, tmux, or custom)
        #[arg(short, long, default_value = "terminal")]
        source: String,

        /// Parent PID to track (defaults to current process)
        #[arg(short, long)]
        parent_pid: Option<u32>,
    },

    /// Track a PID under an existing session
    Track {
        /// Session ID
        session_id: String,

        /// Process ID to track
        pid: u32,
    },

    /// Clean up all processes in a session
    Clean {
        /// Session ID to clean
        session_id: String,

        /// Show what would be cleaned without doing it
        #[arg(long, short = 'd')]
        dry_run: bool,
    },

    /// List all active sessions
    List,

    /// Unregister a session without killing processes
    Unregister {
        /// Session ID to unregister
        session_id: String,
    },

    /// Auto-detect and clean orphaned sessions
    AutoClean {
        /// Show what would be cleaned without doing it
        #[arg(long, short = 'd')]
        dry_run: bool,
    },
}