Use when adding/editing .pre-commit-config.yaml, wiring pre-commit hooks into a repo, or scaffolding a new repo's lint/hook setup. Covers the canonical nix-devenv/dryvist-.github architecture, profiles, and consumer patterns.
Scanned 9/23/2026
npx -y skills add dryvist/claude-code-plugins --skill pre-commit-architecture --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Pre Commit Architecture?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/dryvist-pre-commit-architecture)More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.
---
name: pre-commit-architecture
description: Use when adding/editing .pre-commit-config.yaml, wiring pre-commit hooks into a repo, or scaffolding a new repo's lint/hook setup. Covers the canonical nix-devenv/dryvist-.github architecture, profiles, and consumer patterns.
---
# Pre-Commit Architecture
Single canonical home per artifact. No per-repo duplication of hook
definitions or shared lint configs.
| Artifact | Canonical home | How consumers pull it |
| --- | --- | --- |
| Pre-commit hook definitions (Nix consumers) | [`dryvist/nix-devenv`](https://github.com/dryvist/nix-devenv) — `lib/pre-commit-hooks.nix` + `flake-modules/profiles/<name>.nix` | `imports = [ inputs.nix-devenv.flakeModules.<profile> ]` in the consumer's `flake.nix` |
| Pre-commit hook definitions (non-Nix consumers) | [`dryvist/.github`](https://github.com/dryvist/.github) `precommit/templates/<profile>.yaml` | Copy at scaffold; Renovate keeps `rev:` pins fresh |
| Shared lint configs (`.markdownlint-cli2.yaml`, `.tflint.hcl`, `.ansible-lint`, `.yamllint.yml`) | `dryvist/.github` — `.markdownlint-cli2.yaml` at root for backward compat; the rest in `precommit/configs/` | Nix path: `nix-devenv.lib.fetch-shared-configs` materializes nix-store paths; non-Nix path: copy at scaffold |
| `zizmor.yml` workflow-security policy | `dryvist/.github` at the root | Same as above — passed as `--config` by the base profile's zizmor hook |
## Profiles
`nix-devenv` exports six profile names. Pick one per consumer repo.
| Profile | Adds on top of base hygiene | Inventory match |
| --- | --- | --- |
| `base` | Nothing — exposes the base wiring (deadnix, statix, check-yaml/toml/json, check-merge-conflict, check-added-large-files=500k, end-of-file-fixer, trim-trailing-whitespace, detect-private-keys, markdownlint-cli2, zizmor, treefmt) | Generic repos |
| `nix` | Alias for `base` (deadnix and statix file-glob to `.nix` automatically) | nix-* repos |
| `markdown` | Alias for `base` (markdownlint-cli2 file-globs to `.md` automatically) | Markdown-heavy repos |
| `terraform` | `terraform-format`, `terraform-validate`, `tflint` | `terraform-*`, `tofu-*` |
| `ansible` | `ansible-lint`, `yamllint` | `ansible-*` |
| `python` | `ruff`, `ruff-format`, `mypy` | python-template, mlx-benchmarks, etc. |
The base wiring already covers Nix lints (`deadnix`, `statix`) and
markdown (`markdownlint-cli2`); `git-hooks.nix`'s file-glob filters
make those hooks inert on repos that don't have the matching file
types. So `base`, `nix`, and `markdown` are deliberately identical
modules — the name signals intent to the reader, not behavior.
## Consumer pattern (Nix path, preferred)
```nix
# flake.nix in a consumer repo
{
inputs = {
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-25.11-darwin";
flake-parts.url = "github:hercules-ci/flake-parts";
nix-devenv = {
url = "github:dryvist/nix-devenv";
inputs.nixpkgs.follows = "nixpkgs";
};
};
outputs =
inputs@{ flake-parts, nix-devenv, ... }:
flake-parts.lib.mkFlake { inherit inputs; } {
systems = [
"aarch64-darwin"
"x86_64-darwin"
"x86_64-linux"
"aarch64-linux"
];
imports = [ nix-devenv.flakeModules.terraform ];
perSystem =
{ system, ... }:
{
devShells.default = nix-devenv.devShells.${system}.terraform;
};
};
}
```
Scaffold a new repo with this layout via
`nix flake init -t github:JacobPEvans/nix-devenv#with-hooks`.
## Consumer pattern (non-Nix path)
```bash
# Pick the matching profile template
gh api repos/dryvist/.github/contents/precommit/templates/terraform.yaml \
-H "Accept: application/vnd.github.raw" > .pre-commit-config.yaml
# Materialize the configs the hooks need
gh api repos/dryvist/.github/contents/precommit/configs/tflint.hcl \
-H "Accept: application/vnd.github.raw" > .tflint.hcl
gh api repos/dryvist/.github/contents/.markdownlint-cli2.yaml \
-H "Accept: application/vnd.github.raw" > .markdownlint-cli2.yaml
gh api repos/dryvist/.github/contents/zizmor.yml \
-H "Accept: application/vnd.github.raw" > zizmor.yml
pre-commit install
```
## Rules for AI agents
- Do NOT add hook definitions to consumer-repo `.pre-commit-config.yaml`
files. If the canonical profile doesn't cover something, add it to
`nix-devenv`'s base profile (in `lib/pre-commit-hooks.nix`) or the
matching profile (in `flake-modules/profiles/<name>.nix`), then pull
it through everywhere on the next `nix flake update`.
- Do NOT duplicate shared lint config files (`.markdownlint`,
`.tflint.hcl`, `.ansible-lint`, `.yamllint`) into a new repo. Pull
them via the Nix path (`fetch-shared-configs`) or copy from
`dryvist/.github` at scaffold (non-Nix path).
- When opening a PR that adopts the architecture in a new consumer
repo, the diff is approximately: add `flake.nix` + `flake.lock`,
modify `.envrc`, delete `.pre-commit-config.yaml`, delete the
duplicated lint config files that the canonical now covers.
- Hook versions are pinned via `flake.lock` (Nix path) or `rev:`
strings in the static template (non-Nix path). Do NOT pin versions
per-repo unless overriding a specific consumer.
- See [`precommit/README.md` in `dryvist/.github`](https://github.com/dryvist/.github/blob/main/precommit/README.md)
for the architecture rationale, canonical-config choices, and the
trade-offs behind why certain hooks (checkov, bandit, detect-secrets)
stay opt-in per repo.
## Known limitations (as of 2026-05-31)
- `cachix/git-hooks.nix`'s built-in `tflint` wrapper drops args beyond
`$1`, so the `terraform` profile's `--config <sharedConfigs.tflint>`
plumbing doesn't reach tflint. Consumers either keep a synced copy of
the canonical `.tflint.hcl` in the repo (tflint's local-config
discovery still finds it) or override `tflint.args` to `lib.mkForce
[ ]`. An upstream fix to the wrapper would let
`fetch-shared-configs` drive tflint config directly.
- `terraform-validate` hooks need network access to `tofu init`
external modules. `nix flake check` runs hooks in a sandboxed
environment without network. Repos with external module references
override `terraform-validate.enable = lib.mkForce false` for the
flake-check path and rely on CI's offline `tofu validate` gate to cover the
check. Credentialed plans run in the homelab Terrakube workspace.
- `gitleaks` is not in `cachix/git-hooks.nix`'s built-in hook set yet.
Consumers that want it wire it as a custom hook locally; a follow-up
adds it to the base profile.
## Related Skills
- **troubleshoot-precommit** (git-workflows) — Troubleshoot pre-commit hook failures and auto-fixes
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!