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
use std::path::PathBuf;
use clap::{Parser, Subcommand};
#[derive(Parser)]
#[command(
name = "nex",
about = "Package manager for nix-darwin, NixOS, and homebrew"
)]
#[command(version)]
pub struct Cli {
#[command(subcommand)]
pub command: Command,
/// Path to the nix-darwin repo (overrides auto-discovery)
#[arg(long, global = true, env = "NEX_REPO")]
pub repo: Option<PathBuf>,
/// Hostname for system rebuild (overrides auto-detect)
#[arg(long, global = true, env = "NEX_HOSTNAME")]
pub hostname: Option<String>,
/// Show what would change without editing or switching
#[arg(long, global = true)]
pub dry_run: bool,
}
#[derive(Subcommand)]
pub enum Command {
/// Set up nix-darwin + homebrew (macOS) or NixOS (Linux) on this machine
Init {
/// Clone an existing nix-darwin repo instead of scaffolding
#[arg(long)]
from: Option<String>,
},
/// Move a system-owned config (e.g. /etc/nixos) into a user-writable
/// directory so nex no longer needs sudo to read or edit it.
Relocate {
/// Target path. Defaults to ~/nix-config.
#[arg(long)]
to: Option<PathBuf>,
},
/// Capture all installed brew packages into the nex config
Adopt,
/// Install packages
Install {
/// Force install as a Nix package (skip auto-resolution)
#[arg(long, conflicts_with_all = ["cask", "brew"])]
nix: bool,
/// Install as a Homebrew cask (GUI app)
#[arg(long, conflicts_with_all = ["nix", "brew"])]
cask: bool,
/// Install as a Homebrew formula
#[arg(long, conflicts_with_all = ["nix", "cask"])]
brew: bool,
/// Package names
packages: Vec<String>,
},
/// Remove packages
Remove {
/// Remove a Homebrew cask
#[arg(long)]
cask: bool,
/// Remove a Homebrew formula
#[arg(long)]
brew: bool,
/// Package names
packages: Vec<String>,
},
/// Search nixpkgs for a package
Search {
/// Search query
query: String,
},
/// List all declared packages
List,
/// Update flake inputs and switch
Update,
/// Rebuild and activate
Switch,
/// Rollback to previous generation
Rollback,
/// Try a package in an ephemeral nix shell
Try {
/// Package to try
package: String,
},
/// Identify brew packages that can migrate to nix
Migrate,
/// Apply a profile from a GitHub repo
Profile {
/// GitHub repo (user/repo) or URL
#[arg(value_name = "SOURCE")]
source: String,
},
/// Build a bootable NixOS installer USB, optionally with a baked-in profile
Forge {
/// Nex profile (GitHub user/repo) to bake in. Omit for generic styx installer.
#[arg(value_name = "PROFILE")]
profile: Option<String>,
/// Hostname default for the target system (user can override at install time)
#[arg(long)]
hostname: Option<String>,
/// Target USB device to flash (e.g. /dev/sdb, /dev/disk4). If omitted, builds bundle only.
#[arg(long)]
disk: Option<String>,
/// Output directory for the bundle (default: /tmp/nex-forge)
#[arg(long, short)]
output: Option<PathBuf>,
},
/// Interactive NixOS installer (runs on target machine after booting from USB)
Polymerize {
/// Path to the bundled defaults (written by nex forge). Auto-detected from USB.
#[arg(long)]
bundle: Option<PathBuf>,
},
/// Build an OCI container image from a profile
BuildImage {
/// Nex profile (GitHub user/repo) or local path to profile.toml
#[arg(value_name = "PROFILE")]
profile: String,
/// Image name (default: derived from profile name)
#[arg(long)]
name: Option<String>,
/// Image tag (default: "latest")
#[arg(long, default_value = "latest")]
tag: String,
},
/// Enter a dev shell from a flake (wraps nix develop)
Develop {
/// Flake reference (e.g. github:styrene-lab/nex, ., ./path/to/flake)
#[arg(value_name = "FLAKE")]
flake: String,
},
/// Open a project with omegon AI coding agent (requires omegon)
Dev {
/// Project flake reference or bare name (e.g. styrene-lab/nex, omegon, .)
#[arg(value_name = "PROJECT")]
project: String,
},
/// Manage Styrene identity (key generation, display)
Identity {
#[command(subcommand)]
action: IdentityAction,
},
/// Check and fix common configuration issues
Doctor,
/// Update nex itself to the latest release
SelfUpdate,
/// Preview what would change
Diff,
/// Garbage collect nix store
Gc,
}
#[derive(Subcommand)]
pub enum IdentityAction {
/// Generate a new Styrene identity
Init {
/// Path to the identity file (default: ~/.config/styrene/identity.key)
#[arg(long)]
path: Option<PathBuf>,
},
/// Display identity hash and derived public keys
Show {
/// Path to the identity file (default: ~/.config/styrene/identity.key)
#[arg(long)]
path: Option<PathBuf>,
},
/// Scan for all identities on this machine
List,
/// Export or manage SSH public keys derived from identity
Ssh {
/// Label for the SSH key (e.g. "github", "work")
label: Option<String>,
/// List all registered SSH key labels
#[arg(long, conflicts_with = "label")]
list: bool,
/// Register a new SSH key label
#[arg(long, conflicts_with_all = ["label", "list"])]
add: Option<String>,
},
/// Configure git commit signing with identity
Git {
/// Show current git signing configuration
#[arg(long)]
show: bool,
},
/// Link this identity to a Signum hub for SSO
Link {
/// Signum instance URL (e.g. https://signum.styrene.io)
url: String,
/// Enrollment invite code (if required by the hub)
#[arg(long)]
code: Option<String>,
/// Path to the identity file (default: ~/.config/styrene/identity.key)
#[arg(long)]
path: Option<PathBuf>,
},
}