project-standards/skills/nix-tool-policy/SKILL.md
Use when installing or choosing CLI tools in a Nix flake repo, editing flake.nix or home-manager config, or when tempted to pip/pipx/uv/brew/npm install anything. Tools come from the dev shell or nix shell — never ad-hoc package managers.
npx skillsauth add jacobpevans/claude-code-plugins nix-tool-policyInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Scope: All repositories with flake.nix + .envrc (Nix dev shell pattern)
pipx install <tool> / pipx reinstall <tool> — pipx virtualenvs reference Nix store Python; breaks after GCpip install <tool> / pip3 install <tool> — same problem, also pollutes global Pythonuv pip install <tool> — same Nix store Python problemuv run <tool> — generally unnecessary in Nix dev shells; prefer bare commands from the dev shell PATH.
Last-resort package runner (with confirmation) only when the dev shell does not provide the tool.brew install <tool> for tools already in the dev shell — creates version conflictsTools are on PATH: When a repo has flake.nix + .envrc, all project tools are provided by the Nix dev shell. Run them as bare commands.
If a tool is not found: Use direnv exec . <command> to run within the dev shell environment. This works even when direnv shell hooks haven't fired.
If direnv exec fails: Use nix develop --command <command> as a fallback. This is slower but always works.
If the tool truly isn't in the flake: Prefer adding the tool to flake.nix so it becomes part of the dev shell.
For one-off usage, run it via nix shell -p <tool> rather than installing with pipx/pip/uv.
Never install globally: Do not respond to a missing tool by installing it system-wide with pipx/pip/uv/brew;
use the dev shell or a temporary Nix invocation, and update flake.nix when the tool is needed long term.
| Task | Use This | Not This |
| --- | --- | --- |
| Config generation | Nix functions (lib.mkIf, lib.generators.*) | Shell/Python generator |
| Nix data processing | builtins.fromJSON, builtins.readFile, lib.strings.* | Python wrapper |
When a Nix script IS needed (committed artifact):
writeShellApplication with runtimeInputs, reference via ${./<relative-path>}exec patterns in writeShellScriptBin are acceptableNix manages Python interpreters in the Nix store (/nix/store/...).
Tools installed via pipx/pip create virtual environments that hardcode paths to these interpreters.
When nix-collect-garbage runs or a flake update changes the Python version,
those paths become invalid and every pipx/pip-installed tool breaks silently.
The Nix dev shell pattern avoids this by providing tools through the flake lockfile, ensuring consistent, reproducible environments that survive garbage collection.
All JacobPEvans repositories with flake.nix + .envrc, including:
testing
Use when creating or editing GitHub Actions workflows that call reusable workflows (uses: OWNER/repo/.github/workflows/...) — org owner references must be the literal current org, and shared-CI homes are under dryvist.
development
Use when adding or editing .pre-commit-config.yaml, wiring pre-commit hooks into a repo, scaffolding a new repo's lint/hook setup, or deciding where a hook or shared lint config should live. Covers the canonical nix-devenv/dryvist-.github architecture, profiles, and consumer patterns.
testing
Check PR merge readiness, sync local repo, cleanup stale worktrees; optional cross-repo sweep and stale-branch prune modes
tools
Local rebase-merge workflow for pull requests with signed commits