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 · Local UI · 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
Local UI
New in 0.2. A local web UI ships with the package. Point it at a folder and watch the run: readiness checks, the plan, per-file progress and ETA, then confirmed findings and advisories. It drives the same scanner as a subprocess and writes nothing into the folder being scanned.
pip install "precommiteu[ui]"
precommiteu ui
That serves http://127.0.0.1:8787 and nothing else. You pick what to scan
from the Target screen: a folder on this machine, or a repository URL it
clones for you. The scanner never touches anything until you do.
Nothing else to install by hand either. The readiness screen installs what is missing where it can, and the regulation screen downloads adapter packs on demand, verifying each against the bundle checksums. One pack is active at a time; picking another switches it.
The Target screen knows what was already analysed: scan only the changed files, start clean and rescan everything, or forget the cached findings for that folder. The Settings screen shows where this machine keeps the model bundle, the scan cache and the reports, and moves any of the three without a restart. Available in English, Romanian, German, French and Italian.
Every screen, the download flow, pause versus stop, and what the UI sends over the network: Local UI.
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.
- Only what changed. Every file that completes cleanly is recorded, and the
next scan of the same folder replays it instead of analysing it again, so a
repository that took hours the first time takes minutes when a handful of
files moved. The record is one small JSON file per folder and regulation
under
~/.precommiteu/scans/; nothing is written into the folder you scan.--rescan-allforces a full pass. Reuse rules, change detection and CI behaviour: Incremental rescans.
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.2.3
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 |
| Local UI | The six steps, downloading packs, pause vs stop, settings, what leaves the machine |
| 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.
Metadata
Release files for precommiteu 0.2.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| precommiteu-0.2.3.tar.gz | 3.3 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| precommiteu-0.2.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 4.9 MB
Release files / precommiteu-0.2.3.tar.gz
| Download URL | precommiteu-0.2.3.tar.gz |
|---|---|
| Size | 3.3 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
807902e19dc59b9c661ad8f4666a64747fe45471b9242d18097205f745e3426e
|
|
BLAKE2b-256 checksum How to use checksums |
1947c91b04c3732f2b405369cbc0a3ac59799c94a4036afa08a81948f8fe0df3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency logRelease files / precommiteu-0.2.3-py3-none-any.whl
| Download URL | precommiteu-0.2.3-py3-none-any.whl |
|---|---|
| Size | 1.6 MB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
1f9fa013821d3ea928ffd440faa128d09da4c0febee2cfa74cb284c40364cfb8
|
|
BLAKE2b-256 checksum How to use checksums |
ff01372115187c26260f1bb0044230b279a5e9a24fe164982c163aef2acd85b1
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 23, 2026.
Transparency log