nx-rs builds nx, a Rust CLI for working with a personal Nix configuration
repo.
nx sits between raw nix, darwin-rebuild, Homebrew, and your config repo.
It knows where packages, services, secrets, Homebrew manifests, and host
settings live, then turns common maintenance workflows into short, repeatable
commands.
Use it when your machine is managed as code and you want one CLI for daily work:
- inspect what your Nix repo declares and where each package lives
- add or remove packages across nixpkgs, Homebrew, casks, MAS apps, and service manifests
- run repo preflight checks and rebuild the host configuration
- upgrade flake inputs, Homebrew packages, and the system in one flow
- recover from common Nix failures, such as lazy source cache misses and fixed-output hash drift
- inspect rebuild timings, prune old generations, and clean local development caches
nx does not replace Nix. It is a repo-aware command layer for people who
already keep their system and home configuration in a flake-backed Nix repo.
Most commands operate on a managed repo. nx finds it by walking up from the
current directory until it sees flake.nix. Set NX_REPO_ROOT only when you
want to target a different repo.
Run nx init once to scan the repo and write .nx/manifest.toml. The manifest
records the files nx should use for package lists, services, Homebrew
manifests, rebuild behavior, and local package alias hints. Run
nx init --refresh after moving files; refresh preserves hand-edited alias
entries while filling in known defaults for newly discovered packages.
Host maintenance commands under nx generations and nx clean-caches are
intentionally host-scoped and can run from any directory.
After installing nx, start with:
nx doctor # check repo discovery, manifest health, tools, and cache state
nx init # create .nx/manifest.toml for the current config repo
nx status # summarize declared packages by source
nx where ripgrep # jump to the file that declares a package
nx rebuild # run preflight checks and switch the system
nx upgrade # update inputs/Homebrew, rebuild, and commit changesFor a one-off install, use nix run or nix profile install. If you want
persistent session defaults like NX_REPO_ROOT, prefer the Home Manager module.
Run without installing:
nix run github:flowerornament/nx-rs -- --helpInstall into your profile:
nix profile install github:flowerornament/nx-rs?ref=refs/heads/releaseAdd nx-rs to your Nix configuration repository:
# flake.nix inputs
nx-rs = {
url = "github:flowerornament/nx-rs?ref=refs/heads/release";
inputs.nixpkgs.follows = "nixpkgs";
};
# package list/module
inputs.nx-rs.packages.${pkgs.system}.defaultThen rebuild:
nix flake update nx-rs
sudo /nix/var/nix/profiles/system/sw/bin/darwin-rebuild switch --flake .The release branch is a moving branch maintained by the release process. It
points at the latest tagged release; the default branch is for development.
This is the recommended install path for declarative Nix users. The module
installs nx into home.packages and can export the same environment variables
that drive nx at runtime.
Add the flake input:
nx-rs = {
url = "github:flowerornament/nx-rs?ref=refs/heads/release";
inputs.nixpkgs.follows = "nixpkgs";
};Then include the module in your Home Manager configuration:
{
imports = [
inputs.nx-rs.homeManagerModules.default
];
programs.nx = {
enable = true;
repoRoot = "/Users/alice/code/nix-config";
autoRefresh = false;
cleanCaches.skip = [ "huggingface" ];
};
}Available module options:
programs.nx.enableprograms.nx.packageprograms.nx.repoRootprograms.nx.autoRefreshprograms.nx.sops.packageprograms.nx.sops.binprograms.nx.cleanCaches.codeRootsprograms.nx.cleanCaches.scanDepthprograms.nx.cleanCaches.skip
The module only manages binary installation and environment variables. Repo structure, manifests, and command behavior stay in the target Nix config repo.
That boundary is intentional:
- the managed repo owns
nxbehavior over your Nix configuration programs.nxowns machine/session defaults only- CLI flags still own one-off overrides
If you use nx secret add, you can wire sops declaratively too:
{
imports = [
inputs.nx-rs.homeManagerModules.default
];
programs.nx = {
enable = true;
sops.package = pkgs.sops;
sops.bin = "${pkgs.sops}/bin/sops";
};
}nx auto-detects the managed repo by walking up from the current directory
until it finds flake.nix. Set NX_REPO_ROOT only when you want to override
that detection:
export NX_REPO_ROOT=/path/to/your/config-repoSome flows can call local AI CLIs when helpful:
install:--engine codex|claudeselects the engine (codexdefault).--model <name>selects the model for the chosen engine.--explainprints extra decision details during install resolution.
remove:--model <name>sets the Claude model used for AI fallback edits.
upgrade:- Prints optional AI-generated change summaries for flake/homebrew updates.
--no-aidisables AI summaries entirely.
Requirements:
codexand/orclaudemust be installed and available onPATHfor the corresponding features.- Authentication and provider-specific environment variables are managed by those CLIs.
nx reads these optional environment variables:
NX_REPO_ROOT: target repo override; defaults to walking up fromcwduntilflake.nix.NX_PROFILE_PATH: rebuild/upgrade timing file; defaults to~/.local/state/nx/timings.jsonl.NX_RS_SOPS_BIN:sopsexecutable fornx secret add; defaults tosops.NX_RS_AUTO_REFRESH: auto-refresh local cargo-installednxbinaries beforerebuildandupgrade; set to0,false, ornoto disable.NX_NO_AUTO_HASH_FIX: disable fixed-output hash repair duringnx rebuildandnx upgrade; set to1,true,yes, oron.NX_CODE_ROOTS: colon-separated roots scanned bynx clean-caches; defaults to~/code. Use an empty string to disable code-root scans.NX_CLEAN_SCAN_DEPTH: maximumnx clean-cachescode-root scan depth; defaults to3; values above8are clamped.NX_CLEAN_SKIP: comma-separated cache names fornx clean-cachesto skip.NO_COLOR: disable colored output when set.TERM: if set todumb, color output is disabled.
Repository setup and discovery:
# Scan the current repo and write .nx/manifest.toml
nx init
# Show the config location for a package
nx where ripgrep
# Show package distribution across nix/homebrew/mas sources
nx status
# Review packages with little local evidence of recent use
nx unusedPackage changes:
# Bare package names are treated as "install"
nx ripgrep
# Likely command typos are left as command errors with suggestions
nx rebulid
# Explicit install flows
nx install ripgrep fd
nx install --cask firefox
nx install --mas Tailscale
nx install --dry-run --bleeding-edge neovim
# Remove installed packages
nx remove ripgrep
nx remove --dry-run firefoxRead-only inspection:
nx search ripgrep
nx info ripgrep
nx list --plain
nx installed ripgrep fd
nx unused --since 30dSystem and host maintenance:
nx lint
nx rebuild --preflight
nx rebuild --timing
nx rebuild
nx profile --limit 20
nx upgrade
nx upgrade nx-rs
nx generations status
nx generations plan
nx generations prune --dry-run
nx clean-caches --dry-run
nx clean-caches rust-targets node-modules
nx clean-caches nix-gc --dry-runSecrets:
nx secret add github_token --value-stdin
printf '%s' "$TOKEN" | nx secret add github_token --value-stdinUse command help for the full flag surface:
nx --version
nx --help
nx help <topic>
nx <command> --helpShows the current nx version.
- Supports
nx --version,nx -V, andnx version. - Supports
nx version --json.
Shows hierarchical help for commands and flags. Use nx help install,
nx help secret add, or nx help -- --dry-run when you want the command tree.
Generates shell completion scripts to stdout.
- Supported shells:
bash,zsh,fish,elvish,powershell. - Example:
nx completion zsh > ~/.zsh/completions/_nx.
Diagnoses repo and host prerequisites that affect normal nx usage.
- Reports repo root resolution, manifest health, routing status, flake lock state, cache availability, and tool presence.
- Supports
--verbosefor extra detail. - Supports
--jsonfor automation.
Scans the current repo and writes .nx/manifest.toml. Use --refresh to
rescan and merge with an existing manifest. Init also seeds known package alias
hints, such as rg = "ripgrep", when matching packages are declared.
Installs one or more packages into the managed repo. Bare tokens become package
installs, so nx ripgrep means nx install ripgrep. Likely command typos such
as nx rebulid stay command errors so nx can suggest the intended command.
- Use
--dry-runto preview edits without writing files. - Use
--yesto skip interactive prompts and accept defaults. - Use
--caskor--masto force macOS package targets. - Use
--bleeding-edge,--nur, or--sourceto bias source selection. - Use
--serviceto offer service scaffolding after install. - Use
--rebuildto rebuild after successful changes. - Use
--engine,--model, and--explainfor AI-assisted routing/edit flows. - Use
--verboseto surface cache and query timing diagnostics. - Source resolution shows live loading feedback while package queries run.
Removes installed packages from the managed repo.
- Supports
--dry-runand--yes. - Supports
--modelfor AI-assisted fallback removal. rmanduninstallare aliases ofremove.
Adds or updates an encrypted secret via sops.
- Accepts a positional key or
--name. - Accepts
--valueor--value-stdin. - Prefer
--value-stdinto avoid leaving secrets in shell history. - Top-level command aliases:
secret,secrets.
Searches package sources without editing the repo.
- Good first step when you are not sure which backend will provide a package.
- Supports
--bleeding-edge,--nur,--source,--json, and--verbose. - Shows live loading feedback while package sources are queried.
Shows where a package is declared in the managed repo. Use it to jump to the owning file before a manual edit or review.
Lists installed packages by source bucket.
- Optional source filter:
nix,homebrew, ormas. - Supports
--verbose,--json, and--plain.
Shows package metadata plus candidate source information.
- Useful when deciding between nixpkgs, NUR, flake input, Homebrew, or MAS routes.
- Supports
--json,--bleeding-edge,--nur,--source, and--verbose. - Shows live loading feedback while package metadata and source candidates are collected.
Shows a read-only package distribution summary for the managed repo.
- Supports
--json.
Runs a read-only advisory audit for declared packages with little local evidence of recent use.
- Supports
--since,--source,--limit,--include-protected,--json,--verbose,--history,--no-history, and--no-spotlight. - Scans shell history locally; timestamped entries are stronger evidence, while untimestamped entries are shown as evidence without claiming recency. It does not store raw commands, send telemetry, or auto-remove packages.
- Uses
.nx/manifest.toml[aliases]as local command evidence hints.nx initseeds known aliases, and hand-written entries are preserved on refresh. For example,rg = "ripgrep"makesrg ...count as evidence forripgrep. - Rows are review candidates, not proof. Use
nx where <name>andnx remove --dry-run <name>before removing anything.
Checks whether one or more packages are currently installed.
- Supports
--json. - Supports
--show-locationto include file and line matches. - Returns success only when all requested packages are installed.
Checks # nx: routing annotations and keyword overlap across routable .nix
files. Use this before reorganizing manifests or adding new routable files.
- Supports
--json.
Reverts modified tracked files via git checkout. Use --yes to skip the
confirmation prompt.
Runs nix flake update. Additional args after -- pass through to the
underlying flake update invocation.
Runs repo quality checks. This validates the managed Nix config repo itself.
Runs darwin-rebuild switch for the managed repo.
- Use
--preflightto stop after lint, git, and flake checks without switching. - Use
--timingto print phase timings after recording them locally. - Use
--verboseto ask Nix forbar-with-logsoutput during rebuild phases. - Darwin split rebuilds raise Nix's file descriptor limit and retry bounded source-cache failures before surfacing an error.
- Interactive split builds capture Nix build stdout and a bounded stderr tail
while teeing Nix's
barprogress UI. Legacydarwin-rebuildfallback also receives--log-format barby default.--verboseusesbar-with-logs. - If split activation would need an interactive sudo prompt and passwordless
sudo darwin-rebuildis available,nxfalls back to that path. - Interactive activation output passes through directly with
NIX_CONFIGsetting Nix's log format, so Nix, Homebrew, and Home Manager keep native progress behavior. Non-interactive runs and--timingcapture output for parsing and suppress repetitive Nix fetch/build chatter while keeping activation milestones visible. - If rebuild reports a fixed-output hash mismatch,
nx rebuildupdates the unique clean matching hash in a tracked.nixfile and retries. It prints the file, line, old hash, and new hash. SetNX_NO_AUTO_HASH_FIX=1to require a manual edit. - Additional args after
--pass through to the underlyingdarwin-rebuildcommand.
Shows recent local rebuild and upgrade timing records.
- Defaults to
~/.local/state/nx/timings.jsonl. - Set
NX_PROFILE_PATHto read/write a different timing file. - Use
--jsonfor machine-readable records.
Runs the upgrade flow for either the whole repo or named flake inputs.
- Use
--dry-runto preview without mutating files. nx upgradewith no positional inputs runs the full repo-wide flow: flake update, Homebrew update/upgrade, rebuild, and git commit.nx upgrade <input...>updates only the named flake inputs vianix flake update <input...>.- After changed GitHub-backed inputs are written to
flake.lock,nx upgradeprefetches those exact revisions so lazy source caches are warm before flake check and rebuild. - If rebuild reports a fixed-output hash mismatch,
nxupdates the unique clean matching hash in a tracked.nixfile, retries, and includes that file in the upgrade commit. It prints the file, line, old hash, and new hash. If repair is unsafe, it prints the exact next action. - Homebrew update checks show live loading feedback before upgrade details are rendered.
- Targeted input upgrades skip the Homebrew phase by default.
- Use
--skip-brew,--skip-rebuild, or--skip-committo trim the flow. - Use
--no-aito disable AI-generated summaries and recovery prompts. - Additional args after
--pass through to the underlyingnix flake updatecommand.
Examples:
nx upgrade
nx upgrade --dry-run
nx upgrade nx-rs
nx upgrade nx-rs anneal -- --show-traceHost-scoped retention and garbage-collection commands. They work from any directory and do not require a managed repo.
nx generations statusshows discovered nix-darwin and Home Manager generations plus the active retention policy.nx generations planrenders the exact prune and garbage-collection plan without mutating the host.nx generations pruneexecutes that plan, prompting by default.- Important flags:
--keep,--kind,--no-gc,--yes,--dry-run. nx generations prune --dry-runrenders the same plan asnx generations plan.
Scans host cache directories and common build artifacts, reports sizes, and cleans them after confirmation.
- Supports
--dry-runand--yes. - Code-root scans default to
~/codeand depth3; scan depth is capped at8. - Nix store GC is excluded by default because it can force future downloads or
local source builds; select
nix-gcexplicitly when that is intended. - Agent session history is excluded by default so routine cleanup does not
remove resume/history data. Select
codex-sessions,codex-logs, orclaude-file-historyexplicitly when you want to clean those directories. - Large code-root scans show live per-bucket loading feedback while sizes are computed.
- If selected, Nix GC sizing runs last because dead-store estimation can be slower than normal cache directory sizing.
- Confirmed cleaning shows live per-cache loading feedback before final success or warning lines are printed.
- Positional cache names or
--onlylimit the scan and clean plan to selected caches. - Code-root build artifacts report how many directories were discovered.
- Set
NX_CODE_ROOTS,NX_CLEAN_SCAN_DEPTH, andNX_CLEAN_SKIPdirectly, or configure them declaratively withprograms.nx.cleanCaches. - Valid skip names:
cargo-registry,uv,npm,homebrew,huggingface,puppeteer,playwright,xcode-derived,core-simulator,codex-sessions,codex-logs,claude-telemetry,claude-file-history,nix-gc,rust-targets,elixir-builds,node-modules.
help,where,list,info,status,installed,search,generations status, andgenerations planare read-only.version,completion, anddoctorare also read-only.installandremovesupport--dry-runto preview changes before writing files.generationscommands are host-scoped and work from any directory.- Most commands operate on the current repo root; set
NX_REPO_ROOTonly when you want to override auto-discovery.
Rust is pinned in rust-toolchain.toml (1.94.0).
This repo is jj-first for local development. Git remains the GitHub, CI, and release-tag transport layer, but day-to-day work uses Jujutsu:
jj git fetch
jj new main -m "task: short description"
jj status
jj diff
just ci
jj commit -m "area: describe the change"
jj git push --change @-For direct publication to main, move the bookmark explicitly after committing:
jj bookmark move main --to @-
jj git push --bookmark mainSee AGENTS.md for the full jj workflow, agent workspace guidance, and
recovery commands.
just --list
just doctor
just hooks-install
just compile
just ciLocal install for ad hoc testing:
cargo install --path .Standard gate:
just ciAdditional strict checks:
cargo clippy --workspace --all-targets --all-features -- -D warnings -W clippy::pedantic
just test-systemReleases are local-first and tag-driven. The release helper updates all version
files, verifies the committed release state, pushes the annotated tag, and moves
origin/release to the tagged commit for downstream flake consumers.
jj git fetch
jj new main -m "release: prepare vX.Y.Z"
just release-bump X.Y.Z
# Fill CHANGELOG.md and update user-facing docs for shipped behavior.
jj status
jj diff
jj commit -m "Release vX.Y.Z"
just release-verify
jj bookmark move main --to @-
jj git push --bookmark main
just release-tag X.Y.Z
git ls-remote origin refs/heads/release 'refs/tags/vX.Y.Z^{}'just release-verify must run from a clean jj working copy, then runs the full
CI gate, system matrix, release build, Home Manager and package consumer smoke
tests, and Nix build/run checks. just release-tag still uses Git internally
for the annotated tag and origin/release update because GitHub release
publishing is tag-driven. It prompts before running; use just --yes release-tag X.Y.Z only for explicit automation.
Work is tracked in bd (./.beads):
bd ready
bd create --title="<task>" --type=task --priority=2
bd close <id>
bd dolt pushMIT.