diffohq/diffo
Local, live code review for the AI age — read what the agent wrote and send feedback straight back, or review a GitHub pull request with your agent beside you
Changelog
Notable changes to Diffo. Format follows Keep a Changelog; versions follow Semantic Versioning.
Until 1.0, minor versions may break things. When they do, the entry says how to adapt.
Unreleased
Nothing yet.
0.18.2 — 2026-10-29
Fixed
- No more flashing cmd windows on Windows. The background server has no
console, so every
gitit ran was given a new one that popped open and shut. Children now run hidden;--foregroundis no longer needed as a workaround.
0.18.1 — 2026-10-28
Fixed
- A
path:linelink lands on its line. Clicking a file reference in a thread or layer summary scrolled to the top of the hunk, so in a new file it always landed on line 1. It now centres the row, like a symbol usage does. - A suggested PR comment on a layer thread can be added. Add as PR comment draft failed with a 400. The draft now lands on the line of the decision the thread cites, else the layer's only file, else the review body.
0.18.0 — 2026-10-22
Added
- Read the change one commit at a time. On a branch review, the Layers
tab offers two orders: the agent's steps, or the commits, oldest first, plus
uncommitted edits. Picking a commit shows its
own diff, and
]/[step between them. Read marks are shared with the Files tab. See Commit by commit.
Fixed
- C++, Objective-C, GN and protobuf files are syntax-highlighted.
.cc,.cxx,.hh,.hxx,.m,.mm,.gn,.gniand.protorendered in a single colour;.hnow uses the C++ grammar instead of C.
0.17.2 — 2026-10-16
Fixed
- The agent starts polling in the same response that opens the review.
The poll step was the last line of the open command's output, after the
guide and layers nudges, and nothing later repeated it: an agent that
shared the URL and turned to other chat work never ran another diffo
command, and the review sat with no listener until you noticed — a layers
click meanwhile queued for a poll nobody was running. The open now prints
the poll as its first next step, and the skill and
diffo help agentsay it starts right after the URL, before the guide and before any chat work; the guide is written while it listens. The protocol also now says that edits you ask for in chat are yours to ask for and never a reason to put off attaching, and that a turn ending with the URL but no poll is broken.
0.17.1 — 2026-10-13
Fixed
- A pull request that caught up with its base no longer shows the base's changes as its own. When the PR head moved, Diffo fetched the new head but kept the base branch as it was at the open, so every commit the base gained in between landed in the diff — and in "Since your review" — as if the author had written it. The base is now fetched with the head.
0.17.0 — 2026-10-13
Changed
- The settings switch that hid the companion now stills the logo too. Switching it off stops the header mark's diff loop and the wordmark's retype along with the critter, so the page holds still. The menu item is now called Animations.
Fixed
- Text is crisp on standard-DPI screens. The page asked macOS for grayscale antialiasing, which drew text thinner and fuzzier on non-Retina and external monitors. It now leaves text rendering to the system default.
0.16.0 — 2026-10-08
Fixed
- Feedback no longer waits on an agent that can't hear it. An agent
whose background tasks don't wake it, such as Codex, ran
diffo pollin the background and ended its turn, so reviewer feedback sat unread until you typed in chat. The skill now allows a background poll only when its completion wakes the session on its own, and otherwise holds the poll in the foreground.
0.15.0 — 2026-10-06
Added
- Layers show their size. Each row in the Layers tab now carries the
lines added and removed across its files,
3 files · +243 −5, so you can see how big a step is before you take it. In layer mode the pane bar shows the active layer's size where it showed the whole change's.
Fixed
-
Your own pull request asks which review you meant.
diffo <PR>on a pull request you authored opened it as a reviewer's PR review: the agent treated the code as someone else's, drafted comments, and fixed nothing. It now stops before checking anything out and has the agent ask you one question — branch review (local, the agent fixes what you flag) or PR review (the GitHub pull request) — then runs your choice.diffo pr <PR>is the explicit PR review and skips the question. -
A notice click lands on its thread in layer view. Standing in a layer, clicking a notice for a changeset-level comment did nothing: that comment lives in the changeset strip, which a layer does not draw. It now goes to the Overview first, or to the Files tab when there is no Overview. A notice for a line in a mechanical layer you were not in also went nowhere, because entering the layer folded its files over the card; that file now stays open.
0.14.2 — 2026-10-05
Fixed
- The review summary box in Submit to GitHub stays full size. With enough pending comments to make the dialog scroll, the Write/Preview box collapsed to an empty strip and you couldn't type a summary. It now keeps its height and the dialog scrolls instead.
0.14.1 — 2026-10-05
Fixed
- The usages card stays while you use it. Clicking a row or scrolling the card's list closed it: the list's own scroll read as the page moving. It now stays while the pointer is on the name or anywhere in the card, closes half a second after you leave both, and crossing the next line on the way into the card no longer counts as hovering a new name. It also opens less eagerly, after the pointer rests on a name for 600 ms instead of 350, and a second name never opens instantly over a shown card. Escape, a click elsewhere, scrolling the page, or picking a usage still closes it at once.
0.14.0 — 2026-10-04
Changed
- Suggested PR comments read like a reviewer typed them. The agent is told to write one or two short sentences with the point first, no preamble and no "Suggest …" framing, and to lean toward attaching one when unsure. The button that takes a suggestion into the review now says Add as PR comment draft, and it no longer flashes green on hover.
0.13.0 — 2026-10-04
Added
- Hover a name to see where the change uses it. Rest the pointer on an identifier in the diff and every visible occurrence lights up; after a beat, a card counts the other usages in the changeset, one row per file with its lines on demand, and a click lands on the line. It is the editor's find all references, scoped to the review: a text search over the diff already in the browser, so it works in every language the highlighter knows and needs nothing installed. A name on a removed line is looked up in the old version, and the card says before. Comments, strings, keywords and literals don't light up.
0.12.0 — 2026-10-03
Changed
- The layer card is composed, not stacked. A meta row carries where the step sits and the card's one action; the title and summary follow; the decisions run under them as a ledger the width of the card, each row opening on a chevron like a file header. Comment on this layer moved from the card's foot to its corner, and the rule under the card appears only when there is a thread to show or one being written.
- The agent chip wears the companion's face. The chip's three-bar meter is gone; the companion's face sits in its place, and the label says the state as a verb with the detail muted beside it: Invite your agent, Listening, Reading the change, Working on db.ts:42, Paused, Suggests layers. A batch in flight fills the pill amber with its n/N instead of a separate bar, and the face hops when the batch is answered, even with the companion switched off. The companion now lives in the chip: it hops out onto the header when you settle or the agent gets to work, and back in when you type, a batch is done, or no agent is attached. Reduced motion, or a header too narrow to walk, keeps it home.
0.11.0 — 2026-10-03
Added
- The mark in the review header is alive. The Diffo logo is a diff — a
removed line beside a −, a kept line, an added line beside a + — and it now
plays as one, on a slow nine-second loop with a rest between plays: the top
line and its − slide out red, the bottom line rewrites itself green while the
+'s upright pops in, a new top line springs back, and the wordmark is retyped
letter by letter in the same two colours on the same beat. It is drawn at
36px, up from 19, and the loading screen carries the same mark. A reader
with
prefers-reduced-motionset sees the still mark. - The agent suggests the review comment, inside the private thread. On a
pull request, a private message that states a finding ("this resets the
streak on late completions") is a review comment in the making, and the
agent now writes it: its reply carries a Suggested PR comment under it,
the comment you would leave the author in your voice, with none of the
agent's evidence in it. Add to review makes it a blue draft on the same
lines (it posts when you submit, under your login, tagged from your
agent); Edit turns the block into the PR composer in place; ✕ passes
on it. The thread stays open either way, a receipt keeps the agent's
version a click away, Undo takes a dismissal back, discarding the draft
hands the suggestion back, a reply redrafts, and the rail marks a thread
carrying a live suggestion. For the agent:
--pr-comment "<text>"ondiffo replyanddiffo comment, pull requests only; the protocol says when to attach one, payloads quote your own review comments and the suggestions you edited under The reviewer's voice, and the submit notice says what became of each. See Reviewing a pull request. - Decisions in a layer. A layer may now carry up to five decisions: what
the agent chose, found, or ran into while making that step, one short line
each, under the layer's summary: a choice between alternatives a reviewer
might weigh differently, something the code now does that the diff does not
make obvious, or a constraint that shaped the change. Clicking one opens the
agent's one-sentence detail, a chip for each place it names (up to three, the
rule and the test that pins it) that goes there, and Comment, which opens
a comment on the layer with the decision quoted.
.walks a layer's decisions. Agents post them in the layers payload as"decisions": [{ "text", "detail", "at" }];diffo help layersand the poll's layers request carry the rule for what earns a line. Most layers have none, and nothing changes for an outline that posts none. - Comment on a layer. A thread can now anchor to a layer instead of a line, for a question about the step itself: why these files belong together, whether the order is right, a summary that says too much. The layer's summary card carries its threads and a composer, and the agent gets the layer as it was outlined (title, summary, files) in place of a diff. A thread follows its layer across re-posts by title; one whose layer the agent drops is filed with the threads the changeset left behind. Not offered on Since your review or a pull request's Overview, and never sent to GitHub, which has no layers.
- A companion in the header. A small character walks the header's empty stretch and acts out the agent: asleep while no agent is attached, reading along while it listens, inspecting or writing the fix while it works, and hopping with a check when the agent replies. It holds still while you scroll, type or move the mouse. Companion in the settings menu turns it off, for every review.
Fixed
- No more "no agent" while the agent writes its guide. An agent opens the review, hands you the URL, then reads the change and writes its guide before it ever polls — and for those minutes the header said no agent and offered Invite, which is wrong and confusing: the agent was there. The CLI now tells the server an agent opened the review, and until its first poll the chip reads agent · reading the change; a send made meanwhile queues for that poll instead of falling back to copying the prompt. A named agent session is watched, so one that dies before polling falls back to no agent; a human's own terminal open changes nothing.
Changed
- The green button is a tint, and the button beside it is a button. The primary action is now green type on a soft green wash instead of a solid green block with white text, everywhere in the review: Add comment, Finish review, Submit. The PR composer's blue and the agent composer's amber take the same treatment in their own hue. And Send to agent has a border for the first time: the outline style was declared before the button base that zeroes borders, so it rendered as a flat grey pill that read as disabled, and reviewers reported never noticing it next to the green block.
- The review's chrome says each thing once. The header is one place line,
repo · branch → base, with the worktree in its hover; the size of the change moved to the pane bar beside the coverage. The pane bar's filters are pills that report what they did once on —Hide 11 test filesbecomes11 test files hidden— and the sidebar lost its duplicate filter menu and tally, and gained a footer that saysN files hidden by filters · showwhenever the pills are hiding something. The Note button left the bar (the band already ends with+ Note on the changeset), the file header lost its five squares and shows the comment bubble only once a file has threads (starting one lives in ⋯ and on every line), the frame around the changeset threads is gone, and a fresh agent thread no longer carries aFrom the agentchip beside a head that already says Agent. - The primary button is green, the brand's and the same green as Submit review on GitHub; secondary actions stay ink. The favicon wears the diff's colours too: a red line over an ink line over a green line.
- Progress wears the diff's green. The viewed-files bar, the finish screen's
bar and the Viewed box fill in a new
--brandcolour — the diff's + colour worn by the chrome — instead of ink. Buttons, selection and focus stay ink; red is still only the diff's −. - Agent notices are cards, not a strip. A reply, a new agent thread or the guide used to raise a full-width amber strip under the header that covered the top of the diff, read like a warning, and collapsed a burst into a bare count. Each event is now its own card hanging under the agent's chip, newest on top, naming where it landed with the start of the message; a card opens its thread or dismisses on its own. A burst shows the newest three and folds the rest into N more threads · open the monitor. Cards fade by themselves, a line draining along the bottom shows how long is left, and on an unfocused tab they wait until you come back.
0.10.0 — 2026-10-01
Changed
- Opening a review no longer tidies every review on the machine first.
Every
diffocommand used to begin by asking git, for every branch Diffo had ever reviewed in every repo, whether that branch still existed — onegitprocess each, on every database open, and an open opens the database five times. On a database a few weeks old that was 1.5 seconds per open and close to 9 seconds per review opened, growing with use, and far worse on Windows. The check now runs where it is needed: a starting server checks only its own repo's branches before loading the review, and sweeps the rest in the background once it is serving.status,poll,replyand the other quick commands no longer ask git at all.diffo cleanruns the full sweep on demand; the 60-day expiry is unchanged. npx -y @diffohq/diffoinstalls 2 packages instead of 172. The review page's libraries (mermaid, shiki, marked, DOMPurify, beautiful-mermaid, TanStack Query) are bundled into the shipped client and were never loaded at run time, but they were declared as runtime dependencies, so every first run and every release fetched them: 256 MB and 172 packages where the CLI needshonoand@hono/node-server, 3 MB. A coldnpxnow takes seconds rather than minutes on a slow link.
Fixed
- A token that cannot read CI status no longer blocks opening a pull
request. Diffo asked GitHub for the head commit's check rollup in the same
query as everything else about the PR. A fine-grained or SAML-limited
personal access token is routinely allowed to read the pull request but not
its checks; GitHub answers that with an error on the one field,
ghturns any error into a failed call, and Diffo gave up with Resource not accessible by personal access token. CI status is now fetched on its own, in parallel, and a refusal costs only the chip: the card reads checks not visible to your token, the review opens, and submitting works as before. - The header no longer drops to no agent · Invite between polls. When a
diffo pollended on its own — the poll window closed, or the harness killed the process — the chip fell straight to no agent until the agent randiffo pollagain, and reviewers read that as the agent being gone while it was working. A poll that ends withoutdiffo endnow parks the agent the way a reply does: the chip keeps agent · working while the session's process is alive (for the fixed grace when the session could not be named), and reads disconnected only once it is gone or never re-polled.
0.9.0 — 2026-09-30
Added
- Anonymous usage data, off with one command. Diffo now reports two small
events per review,
review_openedandreview_finished: its version, your OS and Node major, the kind of review (working tree, branch, pull request), which agent opened it, counts, and one random id per machine, plus one lasttelemetry_disabledwhen a machine that has reported turns it off. Never code, paths, repository or branch names, or comment text. It is on by default, but nothing is sent until the review page has shown you the notice once, and the review that shows it reports nothing.diffo telemetry off,DIFFO_TELEMETRY_DISABLED=1, orDO_NOT_TRACK=1stops it;diffo telemetrysays what is on and why;DIFFO_TELEMETRY_DEBUG=1prints every payload to the server log. The full list, and where it goes, is on the new Usage data page; the previous "no telemetry" statements in the README, FAQ, and security model are updated to match.
0.8.0 — 2026-09-28
Changed
- A layer's files read as a tree. Opening a layer in the Layers tab lists its files the way the Files tab does — folders that fold, each with the roll-up mark — instead of a flat list. The reading pane still reads the layer in the order the agent listed it.
Fixed
- Diagram labels stay readable in dark mode. A node the agent colors
(
classDef/style … fill:) keeps its fill in both themes, but its label followed the theme, so dark mode drew near-white text on a pastel box. A label on a colored node now gets whichever ink reads better on its fill; an explicitcolor:is still kept. - Layer progress bars no longer look broken at 0%. An empty track read as something failing to render. A layer row now draws its bar only once something in it is marked, and the Overview, which has nothing to mark, draws none in the pane bar.
0.7.0 — 2026-09-26
Added
- Pull request review:
diffo <PR URL>(alsoowner/repo#N,#N, orNfrom a clone;/diffo <PR link>from your agent). The PR is fetched into a worktree Diffo owns (~/.diffo/worktrees/<repo>-<hash>/pr-<N>, on branchdiffo/pr-<N>) and reviewed there as an ordinary branch review, so your checkout is never touched. The description, reviews and conversation comments come in as threads at the top; inline GitHub threads sit at their lines with real logins and avatars. The header carries the PR title and one status chip (merged, changes requested, CI failing, draft, or CI running, by priority); click the title for author, branches, checks, reviews and the GitHub link. A push moves the worktree and the diff updates like any edit, read marks included. Every composer gains two tabs in its head and wears the side you pick as a stripe down its edge: Comment on PR drafts a review comment for GitHub (blue), Ask agent sends a private thread (amber dashed, one button, no intent chips). Public drafts, replies and resolves post as one review when you Submit review: GitHub's own dialog, body first, then the verdict (Comment / Approve / Request changes) with what each means, and every pending comment listed in full above the button; nothing leaves before then, and a failure midway never posts twice. The Threads list groups by side, GitHub then Private, each striped in its color with one badge (drafts pending, or threads on your turn), so a row's side is never in doubt; GitHub lists the threads you are part of, and the rest of the conversation folds behind "N from others". A public card offers nothing that reaches the agent; under a private one, Post as PR comment turns the agent's answer (or its ```suggestion block) into an editable draft. The agent works from the worktree as a copilot for code it did not write, never posts to GitHub, and gets asubmittednotice when you finish. Needs the GitHub CLI signed in: every GitHub call is your owngh, and Diffo holds no token. - A positional target.
diffo mainreviews againstmain(same as--base main); a pull-request reference is a target too.diffo pr <ref>is the explicit spelling. diffo cleanlists the worktrees Diffo made and removes the ones whose review is over (merged or closed, pruned, or gone);--forcetakes dirty ones,--allevery one.
Changed
- The pull request description is the Overview. Row 0 of Layers on a PR is the author's description, the way the agent's guide is on a local review; a guide the agent posts sits under it. A fresh PR review opens there.
- Other people's conversation comments start folded. On the pull request, the pane leads with what is yours: the description, your private threads, and the GitHub conversations you started, replied to, spoke in, or are @mentioned in. The rest, bots included, waits behind "N more from others", the rule the Threads list already applied; picking one of them from the list unfolds it.
- The network claim. Diffo still makes no network calls of its own; a
pull-request review is the one case where it talks to GitHub, and only
through your
gh. README, FAQ and the security page say so. - A bare word that is not a typo of a command is now a branch to review against, not an "unknown command".
Fixed
- The agent's
diffo pollfrom your checkout reaches the pull request's review. Every command except a plain open now follows a live PR server into its worktree when the checkout has no review of its own; before, it started a second, plain review of the checkout and listened there. - A submit of only resolves no longer fails. A pending review is opened and submitted only when something needs one (a line comment, an inline reply, a body, or a verdict); GitHub refuses an empty review, and used to leave one dangling.
- Session detection no longer mistakes the CLI's own launcher for the agent.
A
tsxornpxwrapper carrying a repo path with a harness word in it (a checkout under.claude/worktrees/…, a project calledexample) could pass for the session; it dies the momentdiffo pollreturns, so five seconds after every delivery the review showed "no agent" and marked the thread "the agent moved on" while the agent was still working. The walk now skips processes that carry the CLI's own invocation, and a harness name only counts at the start of a path segment. - A collapsed resolved thread no longer stretches the diff it sits in. The one-line summary carried the whole first message; a bot's review comment running to paragraphs made the code table thousands of pixels wide. The summary is now the first line, and it can no longer size the table.
- Opening a second pull request no longer removes the first one's checkout
while you are still reading it. The sweep that runs at every open treated a
merged or closed PR as done the moment GitHub said so; it now leaves a
worktree alone while a Diffo server is reviewing in it.
diffo clean --forceis the one thing that removes such a worktree, and it says so. - A pull request is fetched from the remote that names its repository. In
a clone of a fork,
originis the fork; the PR's head and base now come from the remote whose URL matches the PR, and Diffo refuses to guess when none does. - A failed submit never reaches the agent. A submit that GitHub refused outright (approving your own PR, say) still ran the agent's leg when it carried no drafts, so the agent heard a finish that did not happen and a second one on the retry.
- Resolving the description, a review body, or a conversation comment no longer breaks the submit. Only inline threads resolve on GitHub; the other kinds now resolve locally instead of queueing a call GitHub rejects at every retry.
- A pending review you submitted or discarded on github.com no longer blocks every later submit. Diffo now trusts what GitHub reports as pending at submit time and starts a fresh review when the stored one is gone.
- A draft written as several messages posts once. Every message of the draft now counts as posted; before, the ones after the first went out again as replies at the next submit.
- Reviews and conversation comments are paginated. A PR with more than a hundred of either lost the rest: a pending review beyond the first page went unseen (and the submit then tried to create a second one), approval counts were short, and imported comments stopped at a hundred.
- The pull request is fetched only when its head moved, not at every 45-second poll, and a fetch that keeps failing is logged once.
- A reply on a conversation comment no longer comes back as a second thread when GitHub's copy is imported.
- Removing a worktree by hand no longer leaks its branch and ref: the next sweep still cleans the git side.
- A URL that is not a pull request is an error, not a silent review of the working tree.
diffo cleansays when it could not check a worktree, and a worktree git failed to remove stays listed instead of being reported as removed.- The Submit review dialog's pending list refreshes after a partial GitHub failure, so it lists only what is still to post; it also submits when the preview itself failed to load, and a verdict that became blocked mid-dialog falls back to Comment.
- Public drafts no longer count as comments waiting for the agent, in the finish summary or the monitor.
- A queued resolve or reopen stays visible on a collapsed card and in its rail row ("resolves when you submit").
- A GitHub thread whose line went outdated says so ("outdated on GitHub: the line left the diff") instead of claiming the file left the changeset.
- The rail's mark on a public draft discards it, the way the card does, instead of resolving it out of the submit while it still read as settled.
- "Only mine" and "N resolved" folds honour a click even while the selected thread sits inside the fold.
0.6.0 — 2026-09-26
Added
- Edit your comments. Hover one of your messages and click the pencil. If the agent hasn't seen it yet, the edit just fixes the text in place. If it has, editing works like editing a chat message: everything after it is deleted, the messages that will go fade while you edit, and Save & resend hands the thread back (plain Save holds it). The agent is told its earlier replies were withdrawn, and that any code it changed for them is still in the tree, so it should keep or revert that code and say which. The pencil is off while the agent is answering the thread.
Changed
-
Connecting an agent is one line to paste. With no agent attached, the modal used to lead with installing the skill and hid the actual join prompt, seven lines of poll instructions, under "Already have it?". Now it offers a single ask,
connect to the diffo review in <repo>, which wakes the skill your agent already has. The repo path points it at this review from any session. The install command and the scope checkbox are gone. -
The guide opens with the problem, and its diagram draws what happens. The map-not-a-tour doctrine still let a guide explain a change through its own code names: "what it does" came out as a mechanism line, and "how the changed pieces talk to each other" as a chart of which constant feeds which function. It now asks for the problem the change solves and what it changes, written for a reader who never saw the session, and a diagram of the flow it changes — from what sets it off to what someone sees, people and screens when they are the flow, never which code references which. A plain node is anything existing, not only code.
help guide's example leads with the problem too, since agents copy examples. -
Layer summaries say what the step is about. Agents kept writing them as retellings of the files right below, so the card gave the reviewer no context. The doctrine now asks for what the layer is about and enough context to review it — short, free-form, never a retelling of the diff — and a small diagram when the step has a shape.
help layers,help agent, the open-time nudge, and the outline request all carry the same words. -
The Layers tab, before there are any, is a title, one line, and the button. The explainer paragraph, the dashed example, and the hint under the button are gone; an agent's suggestion to read in layers shows in the header chip, not as a state of the tab.
0.5.0 — 2026-09-26
Changed
- The guide is a map, not a tour. Since layers took over the reading
order, the agent's opening comment kept writing one anyway — 24 of the last
30 guides did. The doctrine now asks for what only the author knows: what
the change does and why, how the changed pieces talk to each other as a
runtime-flow diagram, what has to stay true (an invariant, a judgment call,
a known shortcoming) as checks for the reviewer to run, and what to skip.
No reading order, no file list, about a hundred words plus the diagram.
Diagrams tag new code
:::newand changed code:::changedwith twoclassDeflines, so the seam where new meets old reads the same on every review.diffo help guidecarries the doctrine and one worked example;help agent, the open-time nudge, and the cleared-round payload restate it.
Added
-
Hide tests works inside layers. The switch used to step aside with the other filters once an outline was up, so a layer showed its test files whatever you had chosen. It now follows you in: a layer shows its non-test files, the rail counts what it took (
3 files · 1 hidden), a layer left with nothing says so on its card, andn,]and the layer's roll-up mark skip what is hidden — excluded, not read, exactly as on the Files tab. The narrowing filters (reviewed, since, the typed word) still step aside there: the outline is the narrowing. -
The map is navigation. In the changeset threads — the guide above all — a file named as
`path`or`path:line`in prose, or in a diagram node, is now a jump to that file in the review, the same way a layer summary's references already were. Layer-card diagrams get the same. -
Agent-offered replies.
diffo commentanddiffo replytake--suggest-reply "<one line>": the answer the agent expects when its message ends in a decision that is the reviewer's ("want me to extract this?" → "yes, extract it"). In the composer it shows as ghost text in the reply box with a Tab chip; Tab or → takes it, typing or Shift+Tab withdraws it, and nothing is sent without ⌘↵ — an offer is a draft, never a hand-over. The help text and agent protocol say when to use it: only on a message that proposes something, never on one that only reports.
Fixed
- Hide tests now catches Cypress (
.cy.ts,cypress/), e2e layouts (.e2e.ts,e2e/, Nx*-e2e/), test support (__mocks__, snapshots,test-utils, runner configs likevitest.config.ts), Gradle source sets (androidTest/,integrationTest/,testFixtures/),*IT.java, Kotest/Spock*Spec, pytestconftest.py, RSpecspec/**/*.rb, Gotestdata/and mocks, Gherkin.featurefiles, and any.NET*.UnitTests/-style project. - Review page polish. Mermaid figures scale to the comment card instead
of clipping (below 60% they scroll); file headers keep the basename at
narrow widths, ellipsizing the directory from the left;
ccomments on the clicked line (a click marks it focused, the hunk's first changed line stays the fallback); the composer footer is a Change · Question · Agent decides control, posted cards and Threads rail rows carry the kind badge; quick actions are the three asks reviewers of agent code make most (Explain this, Clean up the comments, Simplify this); cards and rail share one status vocabulary (Draft → Sent → Answered / Addressed → Resolved); the pane bar reads "18 of 30 files" and "hunk 2 / 64" with the selected hunk marked by a rail and ring; below 800px the file list starts folded; layer rows pick on a click anywhere.
0.4.1 — 2026-09-24
Fixed
- Skill: the poll step reads as data again. Step 3 of
SKILL.mdsaid every poll payload "names your next step", which read as reviewer text steering the agent and brought back Snyk's W011 (third-party content). It now says the payload'snext_stepis fixed text the CLI writes, never reviewer-typed, and points at the trust rule from the poll step itself.
0.4.0 — 2026-09-22
Added
- Layers: the agent's reading plan. A diff arrives in alphabetical order,
which is almost never the order it should be read in. The agent that wrote
the change can now post an outline (
diffo layers --json, or--suggestat open to offer one): ordered steps, each with a title, a short summary, and its files. The review gains a Layers tab; picking a layer narrows the pane to its files under the summary, and]/[/nwalk the plan. The reviewer's Ask the agent to outline this reaches the agent throughdiffo pollas akind: "layers"payload. Layers come from the agent only, and with none posted the review is the flat file list it was. They resolve against the live changeset on every refresh, so files touched after posting gather in a Since your review layer until a re-post, which keeps the reviewer's place for every title that survives.
0.3.0 — 2026-09-19
Added
- Tab titles:
diffo poll --title "<the change, in 2-3 words>". A reviewer with several reviews open saw every tab titled "Diffo" and had to click through them to find one. The agent now names the change on the poll that starts the review, and that name becomes the tab's — just the name, since a tab shows about twenty characters and the favicon already says which app this is. The newest title wins, so a changeset that becomes something else can rename its own tab, and a poll without--titleleaves the name alone. Nothing changes until an agent sends one: the tab reads "Diffo" exactly as before. The unread-answer badge still rides in front ((2) tab titles), a review served from a source checkout still says so (dev · tab titles), and clearing the review drops the title along with the round.
Changed
- The review URL reaches you before the guide, not after. The agent used
to be told to post its guide comment first and hand over the URL last, so a
review with a diagram to draw opened only once the diagram was drawn. The
skill,
help agent, and the open-time nudge now all say the opposite: share the URL the moment it prints, then write the guide while the reviewer opens the page. The guide lands live at the top of the review; if the tab is focused and the reviewer has already scrolled on, the notification banner points at it, and stays until clicked or dismissed.
Fixed
- Replying to an agent comment is one click again. A thread the agent
started (
diffo comment) used to take Reply, then a second Send, before the reviewer's answer reached the agent. With an agent attached the composer now offers Reply & send, and the server hands the thread over on that reply. The ghost Reply beside it still writes the line in without handing it over, for a later Send or the finish batch.
0.2.0 — 2026-08-31
Added
- Diagrams in comments render beautifully. Mermaid fences for the common types (flowchart, sequence, state, class, ER, xychart) now render through beautiful-mermaid, themed with Diffo's own CSS variables so they re-color live when the theme flips. Stock mermaid remains the fallback for everything else (pie, gantt, gitGraph, …) — and those now re-render in place on a theme switch too, instead of keeping a stale palette until refresh. Nothing that rendered before stops rendering, and all diagram output still passes through the sanitizer.
- Interim agent replies:
diffo reply --more. An answer that only promises a follow-up ("I'll investigate and report back") used to read as final.--moremarks it interim: the typing indicator keeps running beneath it, the rail keeps the thread under "Waiting on the agent", and the agent's next plain reply settles it. Every way the agent can go quiet (detach, dead session, server restart) converts the promise into the ordinary unanswered state, so the indicator can never run forever. - Your theme choice follows you across repos. It was stored per-origin, and every repo serves from its own port — picking dark in one review never reached the next. The durable copy now lives in the shared DB.
Fixed
- The reading pane now renders files in the rail's tree order (folders first, then files, alphabetical by basename) instead of raw git order, so the pane, J/K navigation, "next unreviewed", and the finish-review coverage lists all read in the order the rail shows.
Changed
- The review loop spends far fewer of the agent's tokens. The full
"how to respond" protocol ships once per agent session — repeat deliveries
carry a compact form plus a
diffo help agentpointer. Finish no longer re-ships threads the agent already answered: they ride as one-line mentions, off the reply clock. And a re-delivered thread points at the current file instead of repeating its frozen diff snapshot. A typical delivery shrinks 30–50%. - SKILL.md is now a stub (9.3KB → 5.2KB). Invocation, start-up steps,
and the rules that cannot wait stay inline — the trust-model wording below
among them, now test-pinned — while the loop's details live in
diffo help agent, so installed copies carry less that can go stale. - The skill now states its trust model outright, for security scanners
and cold readers alike: the review URL is a plain
http://localhost:<port>address carrying no token or credential, and poll payloads are the local human reviewer's feedback — data to weigh, never instructions with the user's authority. Addresses the Snyk skill audit that read the old wording as credential exposure (W007) and third-party content injection (W011). The poll payload is likewise now described as the reviewer's threads as structured data, not "a prompt" the agent acts on — the phrasing the same audit read as external code controlling the agent (W012).
0.1.0 — 2026-08-29
Added
- Comment on multiple lines. Drag down the line-number gutter (or
shift-click) to select a range and comment on it. The scope chip's steppers
walk the free edge one line at a time, a sent range comment marks its lines
with a glyph and a spine, and the range travels to the agent as
path:12-20 (new side). Existing single-line comments need no migration. - Styled tooltips on every icon-only control, replacing the native
titletooltips that were slow enough to read as absent — shown on keyboard focus too, and live-updating labels like Copy path → Copied mid-hover. - Agent activity in the header presence chip — it narrates what the agent is doing, not just whether it's alive.
- A security model page in the docs: the operational properties (no egress, loopback-only with rebinding/CSRF guards, read-only git, data at rest, provenance, no install scripts) stated in a form a security reviewer can check against the source.
Changed
- Finish review now speaks in your words, not a verdict. The Comment / Request changes / Approve radios are gone; finish sends the outgoing comments plus the optional closing note, which is the verdict. One signal is derived instead of declared: an empty finish over a fully-read changeset is a green light to proceed. Nothing to adapt — a client or stored review still carrying a verdict is silently ignored.
- Comment threads freeze their anchored lines, so a thread keeps pointing at the code it was written about even after the code moves.
Fixed
- The CLI knows its own version again. Since the rename to
@diffohq/diffo, every published build reported0.0.0(diffo --version, the server handshake), so a newer CLI would reuse a running server from an older build instead of replacing it. - Hide tests now recognizes test files in any stack — PascalCase
Test/Testssuffixes,.Testsproject directories,test_prefixes,_test/_specsuffixes andtest/Testsdirectories — not just the JavaScript.test./.spec./__tests__conventions. Ambiguous cases resolve toward showing the file. - Split view no longer collapses into four equal columns on some diffs.
- The typed file filter narrows the reading pane, not just the file rail, and a chip in the pane bar names the active filter.
- A commit made while no server was watching is now caught: the review offers a fresh start instead of leaving the previous round's threads under the new changeset, and the reset tells the polling agent it happened.
- Finish no longer brands answered threads as unanswered.
0.0.2 — 2026-08-26
Changed
- Releases are now published to npm from GitHub Actions with provenance: every tarball carries a signed attestation binding it to the exact commit and workflow run that built it, verifiable on the npm package page. Nothing changes for users — same package, now with a checkable paper trail.
0.0.1 — 2026-08-25
The first release, published to npm as @diffohq/diffo. Everything below is what
shipped in it, written as a starting point rather than a history.
Added
- Live changeset review.
diffoin a git repo opens the browser on your working tree versusHEAD, and keeps watching: new files appear, stats tick, and a hunk you had marked read says changed since you read it once the agent edits it. - Changesets, not just pull requests. The default is uncommitted work;
--base <branch>reviews everything since a fork point. Untracked files are included, so brand-new agent output shows up as an addition. - Content-addressed hunk identity — a hash of path plus changed lines, deliberately excluding line numbers and surrounding context. Read marks survive a refresh, an edited hunk honestly loses its mark, and "what moved since I last finished" is a set subtraction.
- Reading tools: syntax-highlighted unified and split diffs, word-level intraline marks, keyboard-first navigation, context expansion around hunks, side-by-side image diffs, per-file read tracking, and click-to-load stubs for lockfiles, generated modules and diffs over 400 changed lines.
- Comment threads anchored to a line, a file, or the whole changeset, typed as a Change or a Question so the agent knows whether you want an edit or an answer. Markdown supported, rendered through DOMPurify.
- The agent loop:
diffo poll(a foreground long poll),diffo reply <threadId>,diffo comment(a thread in the agent's own voice, inert until the reviewer replies),diffo end, plusdiffo status/diffo stop/diffo help agent. Delivery is at-least-once, and what you sent lives in the review rather than in the poll, so it survives a killed poll or a restarted server. - Presence — waiting / listening / working, with a 5-minute stall threshold, so you always know whether a Send reaches a live agent or waits in a queue.
- Finish review, which hands the whole batch over with coverage attached (38/42 hunks read, 2 files skipped) and an optional closing note. The note travels as a thread on the whole changeset — it leads the agent's batch, and the agent replies to it like any other thread.
diffo setup, which registers Diffo with Claude Code, Cursor, VS Code and Copilot CLI, writes a shared copy into the cross-tool~/.agents/skillsdirectory that Codex, Gemini CLI, Amp, Goose and OpenCode read, and installs the generated Agent Skill.- A background server per repo, claimed through SQLite, that outlives the terminal or agent session that started it and stops itself after 30 minutes idle.
- Branch-scoped reviews: a checkout under a running server swaps both the review and the delivery queue to that branch's work.
Security
- The server binds loopback and rejects non-loopback
HostandOriginheaders, so a web page whose DNS is rebound to127.0.0.1cannot reach your repo through it. Static file serving refuses any resolved path that escapes the client directory. See SECURITY.md.
Known gaps
- Published as
@diffohq/diffo, notdiffo— the bare name on npm belongs to an unrelated package. The binary it installs is stilldiffo. - Pull requests are not a changeset source yet. Working tree and
--baseonly. - Guided reading — splitting a large change into an ordered sequence of small, reviewable sections — is designed but not built.