jev-lint (mizchi)
Checks whether a function does what its name says, whether a comment is still true, and whether a test verifies what it claims, with a cutoff per rule.
# jev-lint
[日本語](README-ja.md)
A linter for the things a linter could never check: whether a function does
what its name says, whether a comment is still true, whether a test verifies
what it claims.
Rules ship for **TypeScript, JavaScript, Rust, Python and Go**, plus
Markdown, `package.json`, sqlc `.sql` files and git commits. Your own rule
can target any language ast-grep parses -- Java, Kotlin, Swift, C, C++,
C#, Ruby, PHP, Lua, Dart, Scala, Elixir, Haskell, Solidity, Bash, HTML,
CSS, JSON, YAML -- since the matcher is ast-grep's, and any other grammar
you compile: a tree-sitter parser declared in the config, [MoonBit
included](docs/reference.md#a-language-ast-grep-does-not-have-built-in).
[`examples/cart.ts`](examples/cart.ts) is thirty-eight lines with three
lies in it: a doc comment that promises `null` above a body that throws, an
`isEmpty` that returns a string, an `applyDiscount` that also saves the
cart. [`examples/cart.test.ts`](examples/cart.test.ts) has a test that
would pass with its claim broken. Nothing a type checker minds.
```bash
$ export TYPESAFE_API_KEY=... # required: a key from https://typesafe.ai
$ npx -y jev-lint check examples
```
The key is what pays for the verdicts. It is read from the environment only
— `TYPESAFE_API_KEY`, or `TYPESAFEAI_API_KEY`, or the variable `apiKeyEnv:`
names — and never from the config file, which belongs in version control
and a key does not. Everything that does not ask the model works without
one: `--dry-run` prices a run, `jev-lint rules` lists what would run,
`jev-lint replay` re-scores a recorded run, `jev-lint eval --replay`
re-derives every number in [RULES.md](RULES.md) from the committed
baselines, and CI can lint from a committed cache with no key at all.
```
examples/cart.test.ts
17