1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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,
},
}