Tooling
This page covers the developer tooling and package-management details of the configuration.
Flake formatter
flake.nix exposes a formatter output (nixfmt-tree, a treefmt wrapper around the official nixfmt) so you can run nix fmt <file>.nix on any Nix file, or nix fmt . on the whole tree.
The Justfile provides just fmt.
prek
prek.toml configures prek (a Rust reimplementation of pre-commit) with local hooks that run:
nix fmton staged Nix files.rumdl checkon staged Markdown and MDX files (including thedocs/*.mdxsources of the docs site).shellcheckon staged shell scripts.bun teston JavaScript/TypeScript changes (frompi-extensions/).
prek itself is installed via home.nix.
# install the git pre-commit hook
prek install
# run all hooks on all files
prek run -a
Updating pinned third-party binaries
Some tools are not pulled from nixpkgs but pinned directly as fetchurl/fetchFromGitHub derivations in pkgs/default.nix, so nix flake update does not bump them.
These packages are re-exported from the flake as packages.<system>.<name> for aarch64-darwin, x86_64-linux, and aarch64-linux.
This structure allows nix-update (installed via home.nix) to find and update them in place:
nix-update --flake filen-cli
nix-update --flake git-sv
nix-update --flake jarl
nix-update --flake arf
These packages select the appropriate prebuilt binaries per host platform.
After any bump, run home-manager switch --flake .#philipp (macOS) or home-manager switch --flake .#philipp-linux (Linux) to activate.
Swift toolchain
modules/swift.nix installs an official Swift toolchain (swiftc, SwiftPM, sourcekit-lsp, swift-format) from the
swift-overlay flake input, which repackages the prebuilt swift.org releases for aarch64-darwin and x86_64-linux.
Swift is not taken from nixpkgs because nixpkgs builds it from source and lags swift.org: the pinned nixpkgs ships 6.2.4 and nixpkgs HEAD only reaches 6.3.3, while this setup pins the current 6.4.1.
The release is selected by the swiftVersion string at the top of the module, and the release table and hashes come from the swift-overlay input, both pinned by flake.lock.
Move Swift with nix flake update swift-overlay to refresh the release table, bump swiftVersion in modules/swift.nix, and run just switch.
R Installation Manager (rig)
rig is a custom package for managing R versions on macOS only (pkgs/rig/default.nix).
It downloads the official macOS arm64 .pkg installer from GitHub, as rig provides no Linux binaries.
- Version: 0.8.0.
- Platform:
aarch64-darwin. - Source: r-lib/rig.
Since rig requires system-wide installation via the macOS .pkg format:
# after home-manager activation, install rig system-wide
sudo installer -pkg "$(dirname "$(which rig)")/rig-0.8.0-macOS-arm64.pkg" -target /
# then use rig to manage R versions
rig add release # install the latest R release
rig list # list installed R versions
rig switch 4.3 # switch to R 4.3
uv-managed Python tools
modules/python.nix manages Python CLI tools via uv instead of nixpkgs, so versions are pinned independently of the nixpkgs release cycle.
uvitself comes from nixpkgs, but the CLI tools it manages live outside the Nix store under$XDG_DATA_HOME/uv/tools(i.e.~/.local/share/uv/tools), with a stable bin directory at.../uv/tool-bin.- Versions are pinned in the
uvToolsattrset at the top of the module (currentlyruffandpixelrag). - Bumping a version triggers an idempotent reinstall on the next
home-manager switchvia a per-tool marker file (.hm-version). home.sessionVariablesandhome.sessionPathonly land inhm-session-vars.sh, sourced by login shells. To cover non-login and non-interactive shells, the module re-exports theUV_*vars and prependstool-bintoPATHviaprograms.zsh.envExtra.
ub-mode quality gates and Clojure
The ub-mode skill ships with its four companion tools, which are the deterministic quality gates the skill tells an agent to run.
All four upstream repos tag no releases, so each is pinned by commit and hash rather than through nix-update.
| Tool | What it does | Package |
|---|---|---|
crapper |
Cyclomatic complexity, coverage and CRAP scores per function (Clojure, Java, Go, TypeScript, Rust, Python). | pkgs/crapper |
dryer |
Structural duplication scores. | pkgs/dryer |
mutator |
Mutation testing; reads its function names from crapper. |
pkgs/mutator |
uml-viewer |
Quil/Processing viewer for the namespace tree with the .metrics/ snapshots overlaid. |
pkgs/uml-viewer |
modules/ub-tools.nixputs the four tools on PATH.modules/clojure.nixprovides the Clojure CLI and JDK 21 thatuml-viewerneeds (there was no Clojure module before).- The skill itself (
modules/ai.nix) is pinned as afetchurlof upstream’sSKILL.mdand shipped verbatim, because the upstream file already carries the frontmatter pi reads.pi-agent/skills/ub-mode/PROVENANCE.mdrecords the source, the pin and the missing upstream licence. pkgs/uml-viewerinstalls two wrappers,uml-viewer(the:runentry point) anduml-viewer-ir(the:irpolicy generator). Both run against the project in the current directory and resolve quil and the Clojure runtime from Maven Central on first use, exactly as upstream’s./umllauncher does, so the first run needs network access.
# CRAP scores and complexity for the current project (no coverage run)
crapper --no-coverage
# structural duplication
dryer
# list mutation sites without running tests
mutator --scan src
# write the UML IR from a policy, then view it
uml-viewer-ir examples/myproject.policy.edn
uml-viewer examples/myproject.edn
Bump the pins with scripts/bump-ub-tools.sh (all four tools, or one by name) and the skill with scripts/bump-ub-mode-skill.sh.
nix-direnv
modules/dev-tools.nix enables nix-direnv for an improved nix-shell experience and automatic loading of per-project environments in zsh.
In a project directory:
# for a nix-shell based project
echo 'use nix' > .envrc && direnv allow
# for a flake-based project
echo 'use flake' > .envrc && direnv allow