Natural-language linting. Write your rules in English, let an LLM enforce them.
Today: Python source only (*.py), Google Gemini the only provider.
Some conventions are too subjective for a regex or an AST — "comments should explain why, not narrate what the code does," "user-facing errors should explain what went wrong and how to recover." Write those rules in plain English and houserules enforces them with an LLM.
A detector model flags violations; a verifier pass re-checks each one and drops
the false positives. It's advisory by default: reports findings, exits 0,
never blocks. Set mode: strict to exit 1 on findings.
Data, privacy & cost
Each run sends your rule text plus the full numbered source of the files it
checks to Google's Gemini API. With --base, that's the files containing
changed lines — still whole files, not just the changed lines.
On Gemini's unpaid tier, inputs may be used to improve Google's products and may be reviewed by humans; paid-tier inputs are not. Read the Gemini API terms before pointing it at anything sensitive.
Cost and latency are unmeasured so far. Expect roughly a per-PR token estimate in the low thousands and a few seconds to low-tens-of-seconds of latency per run — a ballpark, not a benchmark.
Install
pip install houserules
Create a .env file with a Gemini API key. In a source checkout, start from
the tracked example:
cp .env.example .env
The CLI finds the nearest .env from the current directory upward. An already
exported GEMINI_API_KEY takes precedence over the file.
Configure
Generate a starter .houserules from your repo's own conventions:
houserules init
...or drop one in by hand. houserules needs a .houserules file at your
repo root (JSON or YAML; .houserules.json, .houserules.yaml, and
.houserules.yml also work). See
.houserules.example.yaml
for a full example.
If you pass a single explicit directory path and do not override discovery
with --config, houserules walks from the current directory through that
target and uses the deepest config it finds. That lets houserules examples/basic-demo/ load examples/basic-demo/.houserules.yaml.
model: gemini-3.1-flash-lite
mode: advisory
paths:
- src
rules:
- id: comments-explain-why
description: >
Comments should explain intent, rationale, or non-obvious constraints—not
restate what the code makes clear.
- id: actionable-error-messages
description: >
User-facing error messages should explain what went wrong and how to
recover without exposing implementation details.
Keep mechanically checkable conventions such as TODO markers, import order, and unused variables in Ruff. Use houserules for conventions that require context or judgment.
The examples/basic-demo/ directory shows the same
idea in a runnable miniature. Its README has the exact command and sample
findings.
Run
houserules # lint the configured default paths (verified)
houserules src/ tests/ # lint specific paths
houserules --skip-verification # detector only, no second-pass verifier
houserules --mode strict # exit 1 if anything is found
houserules --base main # lint only lines changed since a git ref
houserules --dry-run # plan work and count model calls, no API calls
Without GEMINI_API_KEY in the environment or a .env file, houserules prints
an error and exits 2 — a missing key is a configuration error, not a quiet
skip. For CI, gate the job on a same-repo if: condition so fork pull requests
(which have no access to the secret) don't fail; see
docs/github-actions.md.
How it works
flowchart LR
A["📝 Rules + 📄 source"] --> B["🔍 Detector"] --> C["✅ Verifier"] --> D["📢 Report"]
- Rules + source go in — your plain-English rules and the files to check.
- Detector flags candidate violations.
- Verifier (on by default) re-checks each one and drops the false positives.
- Report what survives — terminal diagnostics or inline PR annotations.
A surviving finding renders as one path:line:column: rule-id message line:
src/app.py:42:5: comments-explain-why Remove the comment; it restates the assignment below.
(PR annotation screenshot: follow-up.)
Scope to git-changed lines with --base.
Run on pull requests
Lint each PR's changed lines and surface findings as inline annotations on the
diff. Copy-paste setup: docs/github-actions.md.
Full command, flag, config, and exit-code reference: docs/cli.md.
Use as a library
The framework depends only on python-dotenv and PyYAML, and is importable
directly:
from houserules import Rule, StyleLintConfig, run_advisory_lint
Develop
make install # uv sync
make test # pytest
make lint # ruff
Metadata
Release files for houserules 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| houserules-0.1.1.tar.gz | 46.2 kB | Without provenance | Details for houserules-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| houserules-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for houserules-0.1.1-py3-none-any.whl |
Total release size: 87.9 kB
Release files / houserules-0.1.1.tar.gz
| Download URL | houserules-0.1.1.tar.gz |
|---|---|
| Size | 46.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8d17c661c20209fff23c1cfd7c8020116edbdadf76f5ac89478703f64bb20916
|
|
BLAKE2b-256 checksum How to use checksums |
2b5244cf190e421338857c15fdb3e0e99f18442973fadb6fb84ebb7e5e66dcd2
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / houserules-0.1.1-py3-none-any.whl
| Download URL | houserules-0.1.1-py3-none-any.whl |
|---|---|
| Size | 41.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
8bd107cdf9042cfb899864ce3981322e4c9a2c1c85edffa7b1d665a2bda60eac
|
|
BLAKE2b-256 checksum How to use checksums |
06ae38bd4cc93749e11a80bd9e44c1d1832c0b691d520f09763284185910c35c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.11.28 {"installer":{"name":"uv","version":"0.11.28","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Security for houserules 0.1.1
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.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.