proses 0.1.1

Proses – Professional Secure Execution System
mod cli;
mod config;
mod daemon;
mod process;
mod tui;

use clap::{Parser, Subcommand};

#[derive(Parser)]
#[command(
    name = "proses",
    version = "0.1.0",
    about = "Proses – Professional Secure Execution System",
    long_about = None
)]
struct Cli {
    #[command(subcommand)]
    command: Commands,
}

#[derive(Subcommand)]
enum Commands {
    /// Start a new process (or replace an existing one with the same name)
    #[command(visible_alias = "run")]
    Start {
        /// Shell command to execute (passed verbatim to sh -c)
        command: String,
        /// Human-readable name (defaults to the first token of the command)
        #[arg(short, long)]
        name: Option<String>,
        /// Working directory (defaults to the current directory)
        #[arg(long)]
        cwd: Option<String>,
        /// Extra environment variables in KEY=VALUE format
        #[arg(long = "env", value_name = "KEY=VALUE")]
        env: Vec<String>,
        /// Max automatic restarts before marking errored (0 = unlimited)
        #[arg(long, default_value_t = 10)]
        max_restarts: u32,
    },

    /// Gracefully stop a process (it will not be auto-restarted)
    Stop {
        /// Process name or numeric ID
        name_or_id: String,
    },

    /// Stop then immediately re-launch a process
    Restart {
        /// Process name or numeric ID
        name_or_id: String,
    },

    /// Stop and permanently remove a process from the list
    #[command(visible_alias = "rm", visible_alias = "del")]
    Delete {
        /// Process name or numeric ID
        name_or_id: String,
    },

    /// Open the live process dashboard (interactive Ratatui TUI)
    #[command(visible_alias = "ls", visible_alias = "ps", visible_alias = "status")]
    List,

    /// Dump the last N lines of a process's log to stdout
    Logs {
        /// Process name or numeric ID
        name_or_id: String,
        /// Number of log lines to show
        #[arg(short = 'n', long, default_value_t = 50)]
        lines: usize,
    },

    /// Flush the in-memory store to ~/.proses/store.json
    Save,

    /// Re-launch all processes that were running when last saved
    Resurrect,

    /// Print detailed information about a process
    #[command(visible_alias = "info")]
    Show {
        /// Process name or numeric ID
        name_or_id: String,
    },

    /// Manage the background daemon
    Daemon {
        #[command(subcommand)]
        command: DaemonCommands,
    },
}

#[derive(Subcommand)]
enum DaemonCommands {
    /// Start the background daemon
    Start,
    /// Stop the background daemon
    Stop,
    /// Show whether the daemon is running and its PID
    Status,
}

fn main() {
    let cli = Cli::parse();

    // Initialise ~/.proses/ paths before anything else.
    config::init();

    match cli.command {
        // Daemon sub-commands manage the background process directly — no
        // need to auto-start the daemon first.
        Commands::Daemon { command } => match command {
            DaemonCommands::Start => daemon::start_cmd(),
            DaemonCommands::Stop => daemon::stop_cmd(),
            DaemonCommands::Status => daemon::status_cmd(),
        },

        // All other commands talk to the daemon over IPC, so we must ensure
        // it is running before proceeding.
        other => {
            daemon::ensure_running();
            dispatch(other);
        }
    }
}

fn dispatch(command: Commands) {
    match command {
        Commands::Start {
            command,
            name,
            cwd,
            env,
            max_restarts,
        } => {
            cli::start(command, name, cwd, env, max_restarts);
        }
        Commands::Stop { name_or_id } => cli::stop(name_or_id),
        Commands::Restart { name_or_id } => cli::restart(name_or_id),
        Commands::Delete { name_or_id } => cli::delete(name_or_id),
        Commands::Save => cli::save(),
        Commands::Resurrect => cli::resurrect(),
        Commands::Show { name_or_id } => cli::show(name_or_id),
        Commands::Logs { name_or_id, lines } => cli::logs(name_or_id, lines),
        Commands::List => {
            tui::run_dashboard().unwrap_or_else(|e| {
                eprintln!("  ✗  TUI error: {e}");
                std::process::exit(1);
            });
        }
        Commands::Daemon { .. } => unreachable!(),
    }
}