jev-lsp WIP

Language server · classifier

Steer code with rules, not with token maxxing.

Jev is a classifier, not a chat model: one typed question about one line → a value and a probability, never prose. jev-lsp runs repo rules as ordinary LSP diagnostics, code actions, and code lens.

  • 1 line → 1 value + p
  • LSP diagnostics
  • local / cheap models

Surfaces

Findings on the line: same surfaces your client already has

On save, matching lines become diagnostics a harness or editor already understands. No custom jev/… methods.

harness · write loop OMP · ordinary diagnostic
$ agent write crates/jev-lsp/src/server.rs
… generating handler …
wrote 24 lines
fn handle_request(msg: Request) {
let body = msg.payload();
let data = body.unwrap(); // jev · fail · p=0.91
process(data);
}
← finding on generated line · harness reads it like any diagnostic
$ agent fix --from-diagnostics
Harness write → jev finding on the generated line → fix. Ordinary diagnostic.

Model shape

Classifier, not chat

  • One question → one answer: pass/fail or typed value + probability floor.
  • Rules in the repo: .jev/rules/*.json, versioned like code.
  • Semi-deterministic: pattern exact; judgement must clear the floor before publish.

prose · whole file · every turn

Here are several thoughts about this file… consider error handling on line 412, also naming, also whether the module boundary is right, also…

Jev rule answer typed · one line
{
  "value": "fail",
  "p": 0.91
}
typed value · one line · clears rule floor

Workflow

The loop

  1. 01
    Spec: what the code must do.
  2. 02
    Rules: .jev/rules/*.json (words + pattern for candidate lines).
  3. 03
    Steer: jev-lsp on save, human or harness.
  4. 04
    Stay on track: ordinary diagnostics.

Full schema → docs/GUIDE.md.

.jev/rules/no-unwrap.json minimal
{
  "id": "no-unwrap",
  "question": "Does this line unwrap a Result/Option unsafely?",
  "pattern": "\\.unwrap\\(\\)",
  "floor": 0.8
}

Economics

Why local / cheap models work here

Rules carry conventions; the model answers about one candidate line. No matching pattern → no model call.

cost · measured 2026-09-20 · this repo
rules pass chat review (same file)
server.rs ~2.6k lines ~5.3k in / 345 out · 2 findings · ~$0.00022 ~30.5k in / 9 out · 0 findings · ~$0.00306
30 Rust docs ~$0.0034 · 20 calls

decide jev-1.13 vs chat review gemini-2.5-flash-lite. PoC: accuracy from steering, not token maxxing.

Accuracy from steering, not from burning tokens.

Clients

Same findings in a harness, Neovim, or VS Code

No custom methods. Neovim is primary (nvim/). Anything that starts a language server works, harnesses included. VS Code / Cursor via editors/cursor/.

diagnostics code actions code lens

Harness agent · diagnostics
lsp publishDiagnostics · server.rs
let body = msg.payload();
let data = body.unwrap();
process(data);
jev · fail · p=0.91 · no-unwrap
harness reads Diagnostic[] → steers next write
Terminal / agent loop: finding on the generated line, ordinary diagnostic payload.
Neovim primary client
NORMAL server.rs utf-8 · rust
fn handle_request(msg: Request) {
let body = msg.payload();
let data = body.unwrap(); ■ jev: fail p=0.91
process(data);
}
Gutter sign + virtual text beside the line, next to clangd or your other LS.
VS Code editors/cursor/
server.rs mod.rs
fn handle_request(msg: Request) {
let body = msg.payload();
let data = body.unwrap(); 💡
process(data);
}
jev · no-unwrap
fail · p=0.91
jev: explain
Problems unwrap may fail · server.rs:412
Squiggle + hover + lightbulb; explain as code lens. Problems panel lists the finding.

Install

Start

Rust ≥1.75; decision-tier endpoint + key (hosted Jev default).

  1. Plugin: ln -s /path/to/jev-lsp/nvim ~/.local/share/nvim/site/pack/jev/start/jev then require('jev').setup({})
  2. Export TYPESAFE_API_KEY (or local decide: MODEL.md).
  3. Add .jev/rules/*.json, open a file, save → finding or no_rules.
install cargo · locked
cargo install --git https://github.com/makefunstuff/jev-lsp --locked jev-lsp jev
init.lua after symlink
require('jev').setup({})