precommitEU
Catch EU regulatory violations at PR time, on your own machine, with zero data egress.
One local scanner, eight EU regulations: GDPR, the EU AI Act, NIS2, DORA, the Cyber Resilience Act, the Digital Services Act, the Digital Markets Act and the Data Act.
Documentation · Install · CLI reference · Model bundle · precommit.eu
A real scan, unedited: about 80 seconds on an M-series MacBook, played back at
5×. models.py holds only a dataclass, so the detector clears it and the
validator is never called. user_store.py mentions a sibling file, so the
orchestrator takes over: it resolves UserProfile into models.py, finds that
audit_line() interpolates a national ID and a home address, and confirms a
GDPR Art. 32 violation on a log line that looks harmless on its own. Grep
cannot find that one.
Replay this scan in your browser. No install, nothing to upload.
export PRECOMMITEU_MODELS_DIR=~/.precommiteu/models
precommiteu scan src/ --fail-on-findings
The Zen of EU Code
- Put purpose before collection.
- Collect less than you could, and keep it for less time.
- Let people know what the system knows.
- Make consent a choice, not a trap.
- Give people working controls over their data.
- Protect what you keep, from design to update.
- Children deserve stronger defaults.
- Explain automated decisions before they become consequences.
- Build systems that fail safely, recover clearly, and report harm responsibly.
- Make switching, portability, and interoperability real.
Run precommiteu this to print it.
Quick start
brew install llama.cpp
pip install precommiteu "huggingface_hub[cli]"
hf download AlexandruGirlea/precommiteu-models base.gguf gdpr/detector-adapter.gguf --local-dir ~/.precommiteu/models
export PRECOMMITEU_MODELS_DIR=~/.precommiteu/models
precommiteu scan src/
Linux, source and GPU builds, downloading more packs, air-gapped installs and uninstall: Installation.
hf download writes the bundle in the layout the scanner expects. Each
adapter must sit in a directory named exactly after its pack, because the
scanner resolves <models-dir>/<regulation>/detector-adapter.gguf:
~/.precommiteu/models/
├── base.gguf # shared by every regulation
├── gdpr/detector-adapter.gguf
└── eu_ai_act/detector-adapter.gguf # one directory per pack you downloaded
A flat directory does not work: a missing adapter is not an error, the scan just continues on the base model alone in degraded mode and logs a warning.
precommiteu scan src/ --regulations gdpr,eu_ai_act # several regulations
precommiteu scan --ci --fail-on-findings # changed files only, gate the build
From Python
import os
from precommiteu import scan_paths
# Required unless you pass orchestrator_model_path / detector_adapter_path
# explicitly. Without it, scan_paths raises ValueError.
os.environ["PRECOMMITEU_MODELS_DIR"] = "~/.precommiteu/models"
result = scan_paths(["src/"], regulations=("gdpr",))
for finding in result.findings:
print(finding.regulation, finding.file,
finding.probable_article_id, finding.description)
Full API reference, every parameter, the result schemas and streaming callbacks: Python library.
How it works
- Your code never leaves your machine. All analysis runs on local
llama-serverprocesses the scanner starts and stops itself. No cloud, no telemetry, no network egress. - No finding without proof. A detector proposes candidates; a validator then has to locate the quoted evidence verbatim in your file, or the candidate is dropped. Confirmed findings cite the line. Candidates that fail validation are reported as advisories: clearly labeled, never gating. Only confirmed findings can fail a build.
- Deterministic where it matters. Grammar-constrained model output, fixed budgets per file, suppression rules with auditable reasons.
Model bundle
precommitEU ships as two artifacts. The scanner is a pure-Python wheel on
PyPI; the weights are published separately on Hugging Face at
AlexandruGirlea/precommiteu-models
(public, no token needed). pip install does not fetch them.
The repository holds seven files: one shared 4.36 GiB base.gguf used by every
regulation, plus a 77 MiB detector adapter per pack. You choose which
adapters to download. Name the files you want, or omit them to take
everything:
# base + GDPR only, about 4.5 GB
hf download AlexandruGirlea/precommiteu-models base.gguf gdpr/detector-adapter.gguf --local-dir ~/.precommiteu/models
# add another pack later, the base is already there
hf download AlexandruGirlea/precommiteu-models eu_ai_act/detector-adapter.gguf --local-dir ~/.precommiteu/models
# everything, about 4.9 GB
hf download AlexandruGirlea/precommiteu-models --local-dir ~/.precommiteu/models
--regulations then picks which of the downloaded packs to run. The two are
independent, and that is worth knowing: scanning with a regulation whose
adapter you never downloaded is not an error. The run continues on the base
model alone in degraded mode, logs a warning, and detects noticeably less. If a
scan seems weak, check the adapter is actually on disk.
Air-gapped installs, checksum verification and uninstall: Installation.
Regulation packs
Eight EU regulations, shipped as six adapter packs. gdpr is the default and
the sensible one for almost any product repo.
--regulations value |
Covers |
|---|---|
gdpr (default) |
General Data Protection Regulation |
eu_ai_act |
Artificial Intelligence Act |
eu_data_act |
Data Act |
dora |
Digital Operational Resilience Act |
dsa |
Digital Services Act |
cra_dma_nis2 |
Cyber Resilience Act, Digital Markets Act and NIS2 |
precommiteu scan src/ --regulations gdpr,eu_ai_act
Each adapter is trained on code its own regulation governs, so scanning with all six is noisier rather than more thorough. Application dates, what each pack is for, and how several packs run in one scan are in Regulation packs.
Detection quality
precommitEU is not built to catch everything. The bar it aims for is: flag more than half of the real violations, and be right about nine out of ten things it flags.
From local evaluation runs on my own codebases, measured on confirmed findings (advisories are excluded):
| Pack | Precision | Recall | False positive rate |
|---|---|---|---|
gdpr |
97% | 98% | 6% |
eu_ai_act |
97% | 93% | 5% |
eu_data_act |
97% | 93% | 5% |
cra_dma_nis2 |
97% | 93% | 5% |
dsa |
90% | 90% | 15% |
dora |
100% | 73% | 0% |
Every pack clears both bars. dora is the most conservative and dsa the
noisiest, but nothing here is a compliance guarantee - treat a clean scan as
one useful signal, not as sign-off.
Numbers are for the shipped setup: shared base model plus the per-regulation LoRA adapter, loaded at runtime, exactly as a normal scan runs them.
In CI
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: AlexandruGirlea/precommiteu@v0.1.0
with:
regulations: gdpr,eu_ai_act
--ci scans only the files changed against the merge-target branch. Exit code
1 on confirmed findings with --fail-on-findings, 0 when clean. GitLab,
Azure DevOps, the full input list and every exit code are in
CI integration.
Documentation
| Guide | Contents |
|---|---|
| Installation | Requirements, per-platform install, model bundle download, CPU vs GPU, verify, uninstall |
| CLI reference | CLI usage, complete flag reference, reports, suppressions |
| Regulation packs | The six packs, application dates, choosing packs, multi-regulation runs |
| CI integration | GitHub Actions, GitLab, exit codes, caching the model bundle |
| Python library | Python API: scan_paths, scan_diff, schemas, callbacks |
| Report reference | Report formats: JSON field reference, SARIF, summary, ledger |
| Ignoring and suppressing | .eu-ignore, inline eu-ignore directives, audited precommiteu-ignore markers |
| Examples | Worked scans, real output, common flag combinations |
| Troubleshooting | Model paths, llama-server startup, empty scans, error messages |
Rendered documentation site: https://alexandrugirlea.github.io/precommiteu/ Project site: https://precommit.eu
Disclaimer
precommitEU is free and fully open source under the Apache License 2.0, and is provided as is, without warranty or condition of any kind, express or implied, as set out in sections 7 and 8 of that license. Use is entirely at your own risk. There is no service level, no availability commitment and no guarantee of accuracy or fitness for any purpose.
It produces a compliance signal, not legal advice: it can report findings that are not violations and can miss violations that are present. Nothing it outputs establishes, certifies or evidences compliance with any regulation.
The author is not a lawyer and provides no legal, regulatory or compliance advice. This software does not replace legal analysis. To the maximum extent permitted by applicable law, Alexandru Girlea accepts no liability for any damages, losses, costs, regulatory outcome or misrepresentation arising from use of this software or reliance on its output. Have findings reviewed by qualified legal counsel before acting on them.
License
Apache License 2.0, see LICENSE, NOTICE and THIRD_PARTY_NOTICES.md. Copyright (c) 2026 Alexandru Girlea.
Everything precommitEU produces is Apache-2.0: the scanner, the
per-regulation LoRA detector adapters, the GBNF grammars and the regulation
knowledge packs, including everything in the
model bundle.
The base model weights (Qwen 2.5 Coder, Apache-2.0) and the llama-server
binary (llama.cpp, MIT) are delivered separately and carry their own
licenses.
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file precommiteu-0.1.0.tar.gz.
File metadata
- Download URL: precommiteu-0.1.0.tar.gz
- Upload date:
- Size: 2.6 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
06668fe077a8d2b69010762e91dfc46a304a02acddd11e36ff46381ef63420ea
|
|
| MD5 |
199a91d9df80a58000f6d61930d9a5bd
|
|
| BLAKE2b-256 |
77b7bf00f03533e87b18ac9bb6c255d41ec0ca3bdd5f7f839df8f1a0bea83331
|
File details
Details for the file precommiteu-0.1.0-py3-none-any.whl.
File metadata
- Download URL: precommiteu-0.1.0-py3-none-any.whl
- Upload date:
- Size: 1.6 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.10.10 {"installer":{"name":"uv","version":"0.10.10","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
929070dcf003caddf486246e4b67a842d82521229c2c6752ca86811cb6c926d0
|
|
| MD5 |
c6492910e1bed99101de4fa8d5c227f7
|
|
| BLAKE2b-256 |
29f72a6d7629f160cd8a07d6004925255acdb2d64bc07339233866dadcaf167b
|