esk 0.8.0

Encrypted Secrets Keeper with multi-target deploy
Documentation
pub mod delete;
pub mod deploy;
pub mod doctor;
pub mod generate;
pub mod get;
pub mod init;
pub mod list;
pub mod llm_context;
pub mod set;
pub mod status;
pub mod sync;

use clap::{Parser, Subcommand};

use crate::config::GenerateFormat;
use crate::reconcile::ConflictPreference;

#[derive(Parser)]
#[command(
    name = "esk",
    about = "Encrypted secrets management with multi-target deploy",
    version
)]
pub struct Cli {
    #[command(subcommand)]
    pub command: Commands,
}

#[derive(Subcommand)]
pub enum Commands {
    /// Delete a secret value
    Delete {
        /// Secret key name
        key: String,
        /// Environment to delete from
        #[arg(long)]
        env: String,
        /// Skip auto-sync after deleting
        #[arg(long)]
        no_sync: bool,
        /// Strict mode: fail if any remote push fails (skip target deploy)
        #[arg(long)]
        strict: bool,
    },
    /// Diagnose project health
    Doctor,
    /// Deploy secrets to configured targets
    Deploy {
        /// Filter by environment
        #[arg(long)]
        env: Option<String>,
        /// Force deploy even if hashes match
        #[arg(long)]
        force: bool,
        /// Show what would be deployed without deploying
        #[arg(long)]
        dry_run: bool,
        /// Show detailed output
        #[arg(long, short)]
        verbose: bool,
        /// Skip value validation
        #[arg(long)]
        skip_validation: bool,
        /// Strict mode: fail if any required secrets are missing (default: warn and deploy available)
        #[arg(long)]
        strict: bool,
        /// Allow deploying empty/whitespace-only values
        #[arg(long)]
        allow_empty: bool,
        /// Remove orphaned secrets from targets (deployed but no longer in config)
        #[arg(long)]
        prune: bool,
    },
    /// Initialize encrypted store and config
    Init {
        /// Store encryption key in OS keychain instead of file
        #[arg(long)]
        keychain: bool,
    },
    /// Set a secret value
    Set {
        /// Secret key name
        key: String,
        /// Environment to set for
        #[arg(long)]
        env: String,
        /// Secret value (WARNING: visible in process list; omit for interactive prompt)
        #[arg(long)]
        value: Option<String>,
        /// Config group to register the secret under (skips interactive prompt)
        #[arg(long)]
        group: Option<String>,
        /// Skip auto-sync after setting
        #[arg(long)]
        no_sync: bool,
        /// Strict mode: fail if any remote push fails (skip target deploy)
        #[arg(long)]
        strict: bool,
        /// Skip value validation
        #[arg(long)]
        skip_validation: bool,
        /// Bypass interactive confirmations (empty value, etc.)
        #[arg(long)]
        force: bool,
    },
    /// Retrieve a secret value
    Get {
        /// Secret key name
        key: String,
        /// Environment to retrieve from
        #[arg(long)]
        env: String,
    },
    /// List all secrets and their status
    List {
        /// Filter by environment
        #[arg(long)]
        env: Option<String>,
    },
    /// Show deploy and sync status
    Status {
        /// Filter by environment
        #[arg(long)]
        env: Option<String>,
        /// Show all targets including deployed ones
        #[arg(long)]
        all: bool,
    },
    /// Generate code or config files from secret definitions
    Generate {
        /// Output format (omit to run all configured outputs)
        #[arg(value_enum)]
        format: Option<GenerateFormat>,
        /// Output file path (requires a format argument)
        #[arg(long, short)]
        output: Option<String>,
        /// Print generated output to stdout without writing files
        #[arg(long)]
        preview: bool,
    },
    /// Print LLM context reference document
    #[command(name = "llm-context", hide = true)]
    LlmContext,
    /// Sync secrets with remotes (pull, reconcile, push)
    Sync {
        /// Environment to sync (omit to sync all)
        #[arg(long)]
        env: Option<String>,
        /// Sync a specific remote only
        #[arg(long)]
        only: Option<String>,
        /// Show what would change without modifying anything
        #[arg(long)]
        dry_run: bool,
        /// Strict mode: fail if any remote is unreachable (no partial reconciliation)
        #[arg(long)]
        strict: bool,
        /// Bypass version jump protection (use with caution)
        #[arg(long)]
        force: bool,
        /// Auto-deploy targets after syncing
        #[arg(long = "with-deploy")]
        with_deploy: bool,
        /// When versions match but content differs, prefer this side
        #[arg(long, value_enum, default_value_t = ConflictPreference::Local)]
        prefer: ConflictPreference,
    },
}