![bem-te-vi](web/assets/bemtevi.svg) # notevi A code reader that remembers. Read files and grep through `notevi`, pin notes to line ranges, and every one of those calls is appended as a single JSON line to an **append-only JSONL log** kept in a **private jj commit**. Not your working copy. Not your history. Two front ends over that one log — a CLI and a local web app. Both read it, both write to it. Use either. Use only one. The bem-te-vi calls all day and keeps a bright crown patch hidden under a plain black cap. Same idea: the notes are always there, never in the way. ## Install ```sh go build -o ~/.local/bin/notevi . ``` Needs `jj` (the repo must be a jj workspace) and `rg` for working-copy greps. ## From the shell ```sh notevi read src/lib.rs # whole file, line-numbered notevi read src/lib.rs:120-180 # just the range notevi grep 'fn resolve' src/ # regex over the working copy notevi note -f src/lib.rs:143 -t invariant "holds only under the bank lock" notevi note -reply 12 "no — see the early return above" notevi query -op note # replay the log, oldest first notevi query -file accounts_db # everything touching a path ``` Add `-r REV` to any of them to work at another revision — any jj revset, default `@`. Every entry records the full change **and** commit id, so a note stays pinned to the exact bytes it was about even after the change is rewritten. Notes take an optional `-t KIND` (`fn`, `struct`, `invariant`, whatever you want) and can reply to each other by number. ## From the browser ```sh notevi web # dashboard: every sidecar under ~ notevi web -repo . # one repo, http://127.0.0.1:4200 ``` **This is not a viewer.** Click a line, type, and the note is appended to the same JSONL log `notevi note` writes to — same numbering, same permalinks, at the exact change and commit you are looking at, attributed to your username. Reply to notes, thread them, filter by file, text, kind, author, model, session, change or date range. Never open the CLI and you lose nothing. What it shows: - the repo tree with per-reader coverage, and sources highlighted by tree-sitter with coverage-tinted line numbers — the colors mix where readers overlap - every note pinned to its lines, plus a notes-only mode across the whole repo - who is reading right now, refreshed every few seconds - ad-hoc jj queries over the repo - every Zed and Helix theme installed on the machine, previewed live as you arrow through the picker The dashboard scans `~` for repositories, and can start a sidecar in a jj repo or add colocated jj to a Git one — so a project can begin in the browser too. It writes no registry and no scan cache. ```sh notevi web -repo . -out site # static export: hand it to someone ``` The export is a plain directory of HTML. Tree, coverage, highlighting, notes, filters and the file finder all still work with no server; writing notes, live activity and jj queries do not, because there is nothing running. ## The log One file, `notevi-log.jsonl`, only ever appended to. Lines are never edited and never deleted: a note you got wrong is superseded by a later note, not rewritten, so the trace stays a faithful record of what was believed when. Which also means you can skip the tooling entirely: ```sh jj file show -r 'exactly(notevi_log, 1)' -- notevi-log.jsonl | jq -c 'select(.op == "note") | {file, start, text}' ``` That file lives in one anonymous commit whose parent is `root()`, described `private: notevi log`. The repo-local revset alias `notevi_log` names it and `git.private-commits` keeps it from being pushed. Each append is a new revision of that commit, so the evolog is the log's own history: ```sh jj evolog -r 'exactly(notevi_log, 1)' --reversed -p --git ``` Appends are serialized and go through an isolated `jj run`; your `@` is never snapshotted or moved. There is no bookmark and no push — **a fresh clone will not have your notes.** Back up the jj repo, or export the site. ## If a coding agent is the one running it `notevi` notices. It reads the harness environment (Claude Code, Codex, Hermes), falling back to walking the process tree, and adds session id, model and reasoning effort to every entry it writes. Nothing to configure; a human at a shell just gets entries without those fields. That turns the trace into a record of what an agent actually looked at before it told you something. The web view colors coverage per reader, so overlapping work is visible, and each note links to the conversation turn it came from. `constrain/` goes further and makes `notevi` the *only* way to read: a Claude Code `PreToolUse` hook and a codex execpolicy ruleset that deny `cat`/`rg`/`sed`/`jj file show`/`git show`/… and point the agent at `notevi -doc`. Agents converge on it with zero instructions. See `constrain/README.txt`. ## Coming from `vr` / `vrsite` Same tool, one binary. Old sidecars (`vr_log`, `vr-log.jsonl`) are still recognized and renamed in place on first write; `notevi migrate DIR...` does it up front.