celesto
Secure and persistent computer for AI agents
A local TUI for code-review annotations over git and jj. You annotate a commit, hand it to your coding agent, and read back what it did. The notes live in the repository and survive amends and rebases
Developer toolsGitHub @matzeOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
Repository information has not been collected yet.
No updates detected yet. GitHub releases will appear here.
TUI tool for quick local reviews
Translation pending
Loading the full README.
Read the full README ↗A local TUI for code-review annotations over git/jj.
Agentic development turns you into a reviewer, but the review loop is stuck in
chat. margin lets you step through a change in the terminal, pin comments to
lines or ranges, and hand them to a coding agent through a small CLI.
Most terminal review tools stop at an export you paste into a chat window.
margin keeps the review open, and the agent writes its answers back into it
through the CLI. Annotations follow the commit through the amend or rebase that
fixes them. The loop is laid out on
margin.matze.lol.
Pre-built binaries are available on the releases
page and can be placed anywhere on
$PATH. binge automates this:
binge install matze/margin
Build from source with an existing Rust toolchain:
cargo install --git https://github.com/matze/margin
Run inside a repository:
margin # open the TUI on the commits in <base>..@
margin --base develop # set the base ref explicitly
margin -n 100 # with no base, list this many recent commits (default 50)
margin --theme dark # force a theme
margin --vcs git # force a backend
Navigate the diff, mark a line or range, and type an annotation; c switches
commits. Annotations persist in .margin/annotations.ndjson.
Uncommitted work is reviewable too: under jj the working copy is the @
revision and is always listed; under git it appears as (uncommitted changes)
at the top of the list whenever tracked files differ from HEAD.
The diff owns the screen and always has the keyboard. Above it, a one-line
context header names the loaded commit, that commit's place in the review, the
file the cursor is in, and how many annotations are still open. Each counter
carries the key that steps through it — J/K commit 2/7, N/P 3 of 5 open —
so the motion is visible where its position is read.
The commit, file and annotation lists open as pickers over the diff, each on its
own key — there is no focus to toggle and no view to cycle. Moving a picker's
cursor previews the target in the diff behind it: Enter keeps that preview and
closes the picker, Esc discards it and puts the diff back where it was. A
list's own key toggles it, closing it on the preview like Enter; a different
list key switches lists without closing first.
| Key | Action |
|---|---|
c | toggle the commit picker (Ctrl-u / Ctrl-d scroll the message beside it) |
f | toggle the file picker |
A | toggle the annotation overview |
j / k, ↓ / ↑ | move in the open picker, else in the diff |
Enter | keep the picker's preview / annotate the line |
Esc | dismiss the picker / drop a line selection |
? | show every binding; the help bar carries only the common ones. Any key closes it again |
H | hand the review off: release every open annotation to a waiting agent |
R | reload revisions, diff, and annotations from disk |
q | close whatever is open over the diff, else quit |
Ctrl-c | quit from anywhere |
q only leaves margin from the bare diff: with a picker, the timeline, the key
reference or the agent log up, it closes that instead, so reaching for it cannot
end a review by accident. In the annotation editor it is plain text like any
other key.
R reloads the state an agent wrote while margin stayed open (resolutions,
edits, new commits); the same reload also runs automatically as soon as the
annotation log changes on disk.
In the diff:
| Key | Action |
|---|---|
n / p | next / previous change |
/ | search the diff text (see below) |
N / P | next / previous annotation (crosses into adjacent commits) |
J / K | next / previous commit |
Ctrl-d / Ctrl-u | half-page down / up |
+ / - | expand / collapse context |
s | toggle split / unified view |
S | show / collapse resolved annotations |
v (or Space) | start / stop a line-range selection |
a (or Enter) | annotate the current line or selection |
Esc | drop the selection |
/ opens a search bar over the diff. Typing jumps the cursor to the first
matching line or file header after where the search began, wrapping to the top;
the match count sits beside the query and the matched substring is highlighted.
Ctrl-n / Ctrl-p step to the next / previous match, Enter keeps the current
match and closes the bar, and Esc closes it and puts the diff back where it
was. Matching is case-insensitive.
An annotation the agent closed — resolved or declined — keeps only its gutter
icon (✓) on the lines it covers; its block stays out of the diff so the review
reads as what is left to do. S draws every closed annotation's block again (and
collapses them back). Collapsed or not, the keys below still act on the
annotation under the cursor, and the overview (A) lists it either way.
While hovering an annotation:
This is the beginning of the README. Read the rest at the original. Read the full README ↗