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
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
use std::path::PathBuf;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "dotling",
version,
about = "A dotfiles management CLI — track, link, and sync your config files across machines",
long_about = None,
arg_required_else_help = true,
)]
pub struct Cli {
/// Show verbose output with hints.
#[arg(short, long, global = true)]
pub verbose: bool,
#[command(subcommand)]
pub command: Command,
}
#[derive(Subcommand)]
pub enum Command {
/// Initialize a new dotfiles repo or adopt an existing one.
Init {
/// Path to create the repo at, or a git URL to clone.
#[arg(default_value = "~/dotfiles")]
path: String,
},
/// Add files or directories to tracking.
Add {
/// Paths to add (files or directories).
#[arg(required = true)]
paths: Vec<PathBuf>,
/// Encrypt the file(s) using the vault password.
#[arg(long)]
encrypt: bool,
/// Deploy as a copy instead of a symlink.
#[arg(long)]
copy: bool,
/// Track as a template: rendered on each sync with
/// machine-local variables from `~/.dotling/vars.toml`.
#[arg(long)]
template: bool,
/// Restrict to a specific OS (linux, macos, windows).
#[arg(long)]
os: Option<String>,
},
/// Remove entries from tracking.
Remove {
/// Source paths or target paths of entries to remove.
#[arg(required = true)]
entries: Vec<String>,
},
/// Synchronise tracked entries between the repo and the actual filesystem.
///
/// Pushes (repo → actual) entries that are missing or outdated,
/// and pulls (actual → repo) copy-mode entries that were modified locally.
/// When both sides differ, the user is prompted for resolution.
Sync {
/// Show what would change without modifying anything.
#[arg(long)]
dry_run: bool,
/// Overwrite conflicting files without prompting (repo wins).
#[arg(long)]
force: bool,
/// When both sides differ, prefer the actual (local) file over the repo
/// without prompting. Equivalent to always answering [k]eep-local.
#[arg(long, alias = "prefer-local")]
prefer_actual: bool,
/// Do not prompt for conflict resolution; skip conflicting entries and
/// print a warning. Useful in non-interactive environments (CI, scripts).
#[arg(long)]
no_interactive: bool,
/// Allow executing all hooks without prompting.
#[arg(long)]
allow_hooks: bool,
/// Disable executing any hooks.
#[arg(long)]
no_hooks: bool,
},
/// Show status of all tracked entries.
Status {
/// Show inline diffs for modified entries.
#[arg(long)]
diff: bool,
},
/// Edit a tracked entry in your $EDITOR.
///
/// For encrypted entries, dotling decrypts to a secure temp file, opens
/// your editor, then re-encrypts and writes back to the repo automatically.
/// For plain or template entries, the repo source file is opened directly.
Edit {
/// Source path, target path, or partial match of the entry to edit.
entry: String,
},
/// Encrypt or decrypt tracked entries.
Encrypt {
/// Paths to encrypt.
#[arg(required = true)]
paths: Vec<String>,
},
/// Decrypt encrypted entries back to plaintext in the repo.
Decrypt {
/// Paths to decrypt.
#[arg(required = true)]
paths: Vec<String>,
},
/// Manage the encryption vault.
Vault {
#[command(subcommand)]
action: VaultAction,
},
/// Audit repository health and report issues.
Doctor,
/// Manage machine-local template variables.
///
/// Variables are stored in `~/.dotling/vars.toml` and are never
/// committed to git. Shared defaults live in `dotling.toml [vars]`.
Vars {
#[command(subcommand)]
action: VarsAction,
},
/// Generate shell completion scripts.
#[command(
long_about = "Generate shell completion scripts for the given shell.\n\n\
The script is written to stdout. Redirect it to your shell's\n\
completion directory to activate.\n\n\
Examples:\n\
dotling completions bash > ~/.local/share/bash-completion/completions/dotling\n\
dotling completions zsh > ~/.zfunc/_dotling\n\
dotling completions fish > ~/.config/fish/completions/dotling.fish"
)]
Completions {
/// The shell to generate completions for.
shell: clap_complete::Shell,
},
}
#[derive(Subcommand)]
pub enum VaultAction {
/// Initialize a new vault with a password.
Init,
/// Show vault status and public info.
Show,
/// Export vault as a portable encrypted bundle.
Export {
/// Path to write the vault bundle.
path: PathBuf,
},
/// Import a vault bundle.
Import {
/// Path to the vault bundle to import.
path: PathBuf,
},
/// Change the vault password.
#[command(name = "change-password")]
ChangePassword,
}
#[derive(Subcommand)]
pub enum VarsAction {
/// Show all resolved variables (built-in, config defaults, and local).
List,
/// Set a machine-local variable in `~/.dotling/vars.toml`.
Set {
/// Variable key.
key: String,
/// Variable value.
value: String,
},
/// Print the resolved value of a single variable.
Get {
/// Variable key.
key: String,
},
/// Remove a variable from the local store.
Unset {
/// Variable key.
key: String,
},
/// Validate all template entries — find unresolved variables.
Check,
/// Bulk-import variables from a TOML or .env file.
Import {
/// Path to a TOML file with a `[vars]` section, or a `.env` file.
path: std::path::PathBuf,
},
/// Print local variables as TOML (useful for migrating to a new machine).
Export,
}