| name | flox |
| description | Manage Flox packages in the chezmoi dotfiles repository. Use when adding, removing, upgrading, or troubleshooting Flox packages. |
| argument-hint | <action> [package] |
| disable-model-invocation | true |
Flox Package Management
How Flox works in this repo
The Flox manifest and lockfile live in the chezmoi source directory:
Source: ~/.local/share/chezmoi/dot_flox/env/manifest.toml
~/.local/share/chezmoi/dot_flox/env/private_manifest.lock
Deploy: ~/.flox/env/manifest.toml
~/.flox/env/manifest.lock
Always edit the chezmoi source manifest, then apply. Never edit ~/.flox/env/manifest.toml directly — chezmoi will overwrite it.
The lockfile is tracked in chezmoi so that exact resolved versions are preserved across machines. After any flox upgrade, copy the updated lockfile back into chezmoi:
chezmoi add ~/.flox/env/manifest.lock
Package group strategy
Packages are organized into pkg-groups so they can resolve against independent nixpkgs revisions. This prevents slow-moving or large packages from blocking upgrades of fast-moving ones.
| Group | Contents | When to use |
|---|
toplevel (default) | Stable CLI tools (ripgrep, fd, bat, jq, etc.) | No pkg-group needed — this is the implicit default |
vcs | git, gh, tig, delta, difftastic, git-absorb, git-credential-manager, jujutsu, gitu, gh-dash | Git ecosystem tools that should share a revision |
editors | neovim, lua-language-server, tree-sitter | Editor + language server ABI compatibility |
golang | go, golangci-lint | Linter must match Go version |
node | nodejs, bun | JS runtimes |
lua | luarocks, lua | Lua ecosystem |
python | uv | Standalone; updates frequently |
linters | typos, dotenv-linter | Standalone linters |
cloud | awscli2 | Large package, independent update cadence |
pinned | granted | Version-pinned packages |
claude | claude-code (flox/claude-code) | Fast-moving, unfree; sourced from the flox catalog (see Catalogs below) |
opencode | opencode | Fast-moving AI tooling; needs independent upgrades |
gui | aerospace, wezterm, obsidian, halloy | GUI apps — keep separate from CLI tools |
When to create a new group
Create a new group when:
- A package updates much faster or slower than its current group
- Two packages have ABI/version compatibility requirements with each other but not with their current group
- A package is blocking upgrades of unrelated packages in its group
- A large package (like awscli2) is slowing resolution for the whole group
Catalogs (nixpkgs vs flox)
Flox resolves packages from catalogs. Which one a package comes from is determined by its pkg-path:
- nixpkgs (default) — a bare
pkg-path like "claude-code" or "ripgrep" resolves from nixpkgs.
- flox — prefixing with
flox/ (e.g. "flox/claude-code") resolves from the flox catalog, curated by Flox. It often tracks fast-moving upstream tools ahead of nixpkgs.
flox search <pkg> lists both variants when they exist:
$ flox search claude-code
claude-code Agentic coding tool that lives in your terminal...
flox/claude-code Agentic coding tool from Anthropic
Compare their latest versions before choosing a source:
flox show claude-code
flox show flox/claude-code
To switch a package's source, change only its pkg-path (the install id / attribute name stays the same), then apply and re-lock:
claude-code = { pkg-path = "flox/claude-code", pkg-group = "claude" }
chezmoi apply ~/.flox/env/manifest.toml
flox activate -- claude --version
chezmoi add ~/.flox/env/manifest.lock
claude-code is sourced from the flox catalog for exactly this reason — it stays current faster than nixpkgs.
TOML style convention
The manifest uses a hybrid style:
- Dot notation for toplevel packages (no pkg-group, one line each)
- Inline tables for grouped packages (pkg-group visible at a glance)
ripgrep.pkg-path = "ripgrep"
git = { pkg-path = "git", pkg-group = "vcs" }
go = { pkg-path = "go", version = "1.25", pkg-group = "golang" }
Common operations
Add a package
mypackage.pkg-path = "mypackage"
mypackage = { pkg-path = "mypackage", pkg-group = "appropriate-group" }
Then apply and install:
chezmoi apply ~/.flox/env/manifest.toml
flox install mypackage
Remove a package
- Delete the relevant line(s) from
dot_flox/env/manifest.toml
- Apply:
chezmoi apply ~/.flox/env/manifest.toml
- Uninstall:
flox uninstall mypackage
Upgrade packages
Always dry-run first — flox may propose downgrades if a group resolves to an older nixpkgs revision:
flox upgrade --dry-run
flox upgrade --dry-run git
If the dry-run looks good, apply:
flox upgrade git
flox upgrade claude-code
After upgrading, always sync the lockfile back to chezmoi:
chezmoi add ~/.flox/env/manifest.lock
Avoid flox upgrade with no arguments unless you've verified the dry-run shows no downgrades. Prefer upgrading groups individually.
Pin a version
mypackage = { pkg-path = "mypackage", version = "1.2.3", pkg-group = "pinned" }
Search for available packages
flox search <query>
flox show <package>
Check current state
flox list
flox list -c
Workflow
- Edit the chezmoi source:
~/.local/share/chezmoi/dot_flox/env/manifest.toml
- Apply via chezmoi:
chezmoi apply ~/.flox/env/manifest.toml
- Verify resolution:
flox list
- Sync lockfile:
chezmoi add ~/.flox/env/manifest.lock
- Commit both manifest and lockfile in the chezmoi repo
Commit discipline for the lockfile
Flox uses a single lockfile for all packages. When making multiple changes (e.g., adding a package AND upgrading a group), perform each operation as a separate cycle:
- Make change A (e.g., add package to manifest)
- Apply, verify, sync lockfile
- Commit — lockfile reflects only change A
- Make change B (e.g.,
flox upgrade editors)
- Sync lockfile
- Commit — lockfile reflects only change B
This keeps each commit's lockfile diff attributable to that commit's manifest or upgrade change. Batching multiple operations before syncing the lockfile produces a single diff that conflates unrelated changes.
Troubleshooting
flox upgrade proposes downgrades
This happens when a group (especially toplevel) resolves to a different nixpkgs revision than what's in the lockfile. Splitting packages into groups changes which revision each group lands on — fewer constraints means flox has more freedom to pick a revision, and it may pick an older one.
Prevention: Always flox upgrade --dry-run first. The lockfile preserves the current good versions.
Fix if it happens: Restore the lockfile from chezmoi:
chezmoi apply ~/.flox/env/manifest.lock
flox upgrade doesn't pick up a new version
This usually means the package shares a nixpkgs revision with other packages (the toplevel group) and that revision doesn't have the newer version yet.
Fix: Move the package to its own pkg-group so it resolves independently:
mypackage = { pkg-path = "mypackage", pkg-group = "mypackage" }
Then: chezmoi apply ~/.flox/env/manifest.toml && flox upgrade mypackage
Resolution fails for a group
A group may fail to resolve if packages within it have conflicting version requirements.
Fix: Split the conflicting package into its own group, or relax version constraints.
Package not found
flox search <name>
flox show <name>
Some packages use different names in nixpkgs (e.g., _1password-cli not 1password-cli).
Flake-based packages
Packages pinned to specific git commits or repos use flake instead of pkg-path:
mypackage.flake = "github:owner/repo/ref#output"
These bypass pkg-groups entirely — each flake resolves independently.