Commands
discover.py
Infers a project's conventions from its code and tooling config, with the evidence behind each one. Deterministic: no model, no network, nothing sent anywhere.
discover_conventions, then verifies and rewrites what it finds. Run this script yourself when you want the raw candidates saved as pending rules without an agent's judgement.Synopsis
python3 discover.py --name myapp # preview candidates, saves nothing
python3 discover.py --name myapp --save # save as PENDING rules
python3 discover.py --name myapp --save --activate
python3 discover.py --name myapp --min-ratio 0.8 --min-files 3
python3 discover.py --name myapp --markers extra.json
Options
| Option | Default | Meaning |
|---|---|---|
--name NAME required | The project. | |
--save | off | Store the candidates. Without it nothing is saved. |
--activate | off | With --save: make the rules active at once instead of pending. |
--min-ratio R | 0.9 | How lopsided the evidence must be (the share of files or occurrences that follow the convention). |
--min-files N | 5 | Minimum number of files of evidence. |
--markers FILE | A JSON file with extra pattern markers; see your own markers. |
What it looks at
Four kinds of evidence, each reported with its numbers:
config- Tooling config.
.editorconfig; manifests for PHP, JS/TS, Python, Go, Rust, JVM, .NET, Ruby, Dart, Elixir, Scala, C/C++;Makefileandjustfiletargets; about 20 linter and formatter configs (ESLint, Prettier, Biome, ruff, golangci-lint, rustfmt, RuboCop, phpcs, PHPStan and more); and about 90 well-known frameworks and tools recognised from dependency names. Runtime targets (PHP, Node, Python, Go, Rust, Java, .NET, Ruby versions) become "target X" rules. stats- Code statistics. Indentation, quote style, semicolons, line length and file-name casing per folder.
marker- Language markers. 120 framework-independent conventions across 19 languages (PHP, JavaScript, TypeScript, Vue, Svelte, Python, Go, Ruby, Java, Kotlin, C#, Rust, Scala, Dart, Elixir, Lua, Swift, C, C++): conventions of omission ("no
console.log", "no.unwrap()", "no bareexcept:") and style choices where one side dominates ("f-strings 95% against.format()"). framework- Framework markers. 81 markers for 19 frameworks, evaluated only when the project demonstrably uses the framework (a dependency in a manifest, or a signature in the source): Laravel, React, Express, Vue, SvelteKit, Angular, Django, Gin, Spring Boot, Ktor, ASP.NET Core, Axum, Rails, Phoenix, Play, Flutter, LÖVE, SwiftUI and Qt.
When something is reported
A convention becomes a candidate only when the evidence is lopsided:
| Marker kind | Reports a rule when | Enough evidence means |
|---|---|---|
FileMarker | at least min-ratio of the applicable files contain a pattern | at least min-files applicable files |
OccurrenceMarker | at least min-ratio of the occurrences of X are also Y | at least 15 occurrences |
AbsentMarker | at least 95% of non-test files do not contain a pattern | at least 8 files (or min-files if larger) |
ChoiceMarker | one of several styles has at least min-ratio of the occurrences | at least 15 occurrences |
Only the team's own code counts. Vendored, built, minified and generated files are ignored (vendor, node_modules, dist, build, public/js, *.min.*, lock files), as are files over 300 KB, and at most 3,000 files per language are read. Markers that ask for non-test code skip folders such as tests, migrations, config and scripts.
Example
$ python3 discover.py --name lore
== stats (3) ==
[glob:**/*.py] Indent python code with 4 spaces.
evidence: 93/95 (98%) of python files
[glob:**/*.py] Use double quotes for strings in python code.
evidence: 7641/7811 (98%) of string literals
[glob:tests/*.py] Name .py files in snake_case (tests).
evidence: tests: 12/12 (100%)
== marker (8) ==
[glob:**/*.py] Never use a bare `except:`; catch specific exceptions.
evidence: 98/98 (100%) of non-test files contain none [py-bare-except]
…
11 candidate rule(s). Re-run with --save to store them as pending.
Notes
- It reports what the code does, not what is right. A habit can be universal and still bad. Read the pending list and reject what you do not want enforced.
- Every rule carries its evidence (for example
93/95 (98%) of python files) and, for markers, the marker's id in brackets so you can find the definition. - Nothing is found? Fewer than
min-filesfiles of evidence, or no convention reaches the ratio. Try--min-ratio 0.8. - Saved rules start pending (the script has no agent to judge them). Near-identical rules are not duplicated; rules that contradict an existing one are reported.
Your own markers
For a one-off, pass a JSON list. Each object has a kind (file, occ, absent or choice), a glob, an id, and the fields of that kind:
[
{"kind": "absent", "id": "my-todo", "glob": "**/*.py",
"bad": "TODO\\(", "rule": "Do not leave TODO markers in application code."},
{"kind": "choice", "id": "my-quotes", "glob": "**/*.js",
"options": [
{"label": "single", "pattern": "'[^']*'", "rule": "Use single quotes."},
{"label": "double", "pattern": "\"[^\"]*\"", "rule": "Use double quotes."}]}
]
To make a convention part of Lore itself (so everyone gets it), add a marker to a language or framework module: see Extending Lore.