Open-source project
remorses/critique avatar
remorses/critique

critique: a diff viewer for terminals, web previews, and agents

TUI & website for reviewing git changes

1,279 stars38 forksTypeScriptMIT

At a glance

What is it?
critique is a Bun-based terminal diff viewer that follows the git diff mental model, from an unstaged working tree to a GitHub pull request URL, and it can render the same diff into a shareable web page on critique.work. Its most interesting feature is the commit list, which exists to catch the commit a rebase silently replayed into your branch.
Who is it for?
Adopt critique if you review diffs often and want one viewer for the terminal, a shareable web link, and machine-readable output that an agent can consume. Do not adopt it if your work is inside Node.js, because the tool requires Bun and does not run under Node.js.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 7 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

A diff viewer built on the git model, not around it

critique is a beautiful diff viewer for terminals, web previews, and agents, and the first design decision is that it follows the same mental model as git diff. Everything else in the tool is a consequence of that.

The installation is two options and a runtime requirement. critique requires Bun, and it does not run under Node.js. You can run it directly without installing with bunx critique, or install it globally with bun install -g critique.

The command surface mirrors git diff. A bare critique opens the current working tree diff in the terminal, including untracked files. critique --staged reviews staged changes. critique HEAD~1 or critique main shows changes since a ref. critique --commit HEAD~1 or --commit abc1234 shows a single commit. critique main HEAD or critique main feature-branch compares two refs, PR style. critique --watch watches the working tree and refreshes on changes. And critique --filter "src/**/*.ts" filters files by glob, and the filter can be repeated.

The URL form is the one that removes a step from the workflow. A GitHub PR URL fetches the pull request diff from the GitHub API, and public PRs work without login. For private PRs you use gh auth login, GH_TOKEN, or GITHUB_TOKEN. The example is critique https://github.com/remorses/sigillo/pull/19 --web "Sigillo PR #19", and the README notes the URL also works with --pdf, --image, --filter, or in the terminal viewer, which means you can point it at a pull request from any directory without checking it out.

The project is MIT licensed, written in TypeScript, and the home page is critique.work. The releases are small and frequent, with [email protected] on 2026-09-27, 0.2.1 on 2026-09-24, and an oddly numbered 0.1.140 in April 2026, and the last push to the repository was on 2026-09-27.

The commit list, and the rebase problem it solves

This is the part of critique that is not a diff viewer, and it is the part worth reading twice.

Whenever a diff spans commits, critique lists them before the diff or the URL:

code
3 commits, 22 files, +1573 -311

  2a09302  Correct the identity docs, and mark the release minor
  1fb8af7  Give every element a stable GPUI identity
  7998490  Launch a window without stealing focus
  base: f948f50  Reclaim the style table when the tree shrinks

  + uncommitted working tree changes

The reason this exists is stated plainly. A rebased branch can carry commits replayed from another branch, and the merge base is then too far back, so a shared link silently includes work you did not write. If a listed commit is not yours, you pass the first commit of your own work as the base instead:

bash
# Wrong: the merge base pulls in a commit a rebase replayed onto the branch
critique f948f50 --web "Element identity"

# Right: start from your own first commit
critique 7998490 --web "Element identity"

That is a real problem that only bites when you share a link. Locally you know what you changed. The moment you post a diff URL to a reviewer, the merge base quietly determines the scope of what they see, and after a rebase that scope can include a commit someone else's work.

Long ranges are truncated, but the oldest commits are always kept, precisely because a rebase replays a foreign commit right above the base, and that is the one you must not lose. In scripts you can silence the list with --no-commit-list, and with --json the list goes to stderr while the commits are added to the JSON payload instead, so the output stays parseable.

The diverged branch case is handled with the same care. critique A..B and critique <ref> compare two trees directly, and on diverged branches such a diff also undoes every commit that exists only on the base side, so those are listed too:

code
1 commit added, 1 commit reversed, 2 files, +1 -1

  added by right:
    a279fbe  Only on right

  reversed from left:
    d395611  Only on left

  ! left and right have diverged, so this diff also undoes the commits above.

And critique A...B and critique A B start at the merge base instead, so they never reverse anything, with the base line naming the merge base rather than the ref. Plain unstaged and --staged diffs contain no commits, so nothing is printed.

Web previews, and how the upload works

The --web flag is the second half of the tool, and the pipeline behind it is documented as an ASCII diagram in the README.

--web renders the diff with the same terminal renderer, uploads it to critique.work, and prints a shareable URL. The generated URLs look like critique.work/v/<id>. The title is the first positional argument to the flag, so critique --web "Fix auth retry" gives the preview a name, and the default when no title is given is Critique Diff.

The diagram is worth reading because it explains the design. The git diff or GitHub PR diff goes into the opentui test renderer, which produces HTML variants and a raw patch, and those go to the critique.work upload, and from there the URL comes out.

The key consequence is that the web preview is not a separate renderer. It is the same terminal renderer producing output, which means the web page and what you see in the terminal cannot drift apart, and it means a diff that looks right in one looks right in the other. The same reason the tool is called a viewer for agents, since the output is structured rather than a picture of a picture.

The options table shows how wide the surface is. --cols sets the desktop render width with a default of 240 and --mobile-cols sets the mobile render width with a default of 100, so the upload is a fixed-width render rather than a responsive page. --theme selects a fixed theme instead of the automatic light and dark mode, and --open opens the URL in your browser. --json prints a JSON object containing url, id, files, and commits, which is the hook for scripts and for agents.

The repository layout supports the two halves of the product. There is a cli/ directory for the terminal side, a comments-server/ directory for the hosted side with its own workspace package @critique.work/server, a skills/ directory, a docs/ directory, and a plans/ directory. The root package.json is a Bun workspace with a ./* glob, and it wires the two build steps, one for the comments widget and one for the comments server.

Call-stack diffs, and what they cost you

The --calldiff option is the experimental feature, and the README is clear about both what it does and what it does not support.

--calldiff uses the calldiff project to show how function calls changed. Each changed call tree appears below its source file in the top file tree. The examples cover the working tree, changes since a ref, a PR-style comparison with a web preview, and a single commit.

The trade-offs are stated. The feature is experimental. calldiff uses Tree-sitter and can install a language grammar through npm on first use, which means the first run of that feature pulls a parser from the network, and a different grammar per language. And --staged, --stdin, and --watch are not supported with --calldiff, so the three most convenient modes of the tool are unavailable in the one mode that is new.

It can be turned on by default with an environment variable:

bash
export CRITIQUE_CALLDIFF=1
critique

The feature is genuinely interesting on its own terms, because a diff that shows which call paths changed is a different thing from a diff that shows which lines changed, and for a refactor the second is often noise. Whether it earns the Tree-sitter dependency is a different question, and the experimental label plus the missing flags are the honest state of it.

The navigation table gives the rest of the terminal interface: up and down to scroll, p for a file selector dropdown, t for a theme picker, Option held for fast scroll at ten times speed, g g and G to jump to top and bottom, Ctrl+D and Ctrl+U for half page down and up, and q or Esc to quit or close an overlay.

The hosted side, and what the repository reveals

A few things in the repository are worth knowing if you intend to use the web preview, because they describe the data that leaves your machine.

The web preview uploads a rendered diff to a hosted service. That diff contains your code. The README does not describe a self-hosted option for the upload, and the .env.example file in the repository lists the configuration for the hosted service, which includes a Stripe secret key, a Stripe webhook secret, a PUBLIC_URL pointing at critique.work, and a Resend API key with a from address, which is a transactional email provider used for account mail.

So the web preview path is a hosted service with accounts and billing, not a local static file generator. If a diff contains a secret, a credential, or code you cannot send to a third party, the terminal viewer is the mode to use, and --json plus a local renderer is the way to script it.

The repository also separates concerns more than the version number suggests. The cli/ directory holds the terminal application, the comments-server/ directory holds the hosted backend, and the root package.json is only a workspace manifest with two build scripts. The .changeset/ directory means the project uses Changesets for versioning, which is what produces the [email protected] style package names, and AGENTS.md, docs/, and plans/ suggest a project that is being developed with a fair amount of agent involvement.

The versions themselves are the honest signal. 0.1.140 in April 2026, then 0.2.1 and 0.3.1 in September, is a project that is still finding its release cadence, and the surface area, with the terminal viewer, the web upload, the JSON output, the call-stack diffs, and the comments server, is wide for a tool at version 0.3.

Editorial conclusion

Adopt critique if you review diffs often and want one viewer for the terminal, a shareable web link, and machine-readable output that an agent can consume. Do not adopt it if your work is inside Node.js, because the tool requires Bun and does not run under Node.js. Verify first how it handles a rebased branch, since the commit list is the feature designed to catch exactly that case.

Frequently asked questions

What runtime does critique require?

critique requires Bun, and it does not run under Node.js. You can run it directly with bunx critique, or install it globally with bun install -g critique.

Why does critique list the commits contained in a diff?

Because a rebased branch can carry commits replayed from another branch, which puts the merge base too far back, so a shared link silently includes work you did not write. If a listed commit is not yours, you pass the first commit of your own work as the base. Long ranges are truncated but the oldest commits are always kept.

Can critique show a GitHub pull request without checking it out?

Yes. A GitHub PR URL fetches the pull request diff from the GitHub API, and public PRs work without login. For private pull requests you use gh auth login, GH_TOKEN, or GITHUB_TOKEN, and the URL also works with --pdf, --image, --filter, or in the terminal viewer.

What does the --calldiff option do and what are its limits?

It uses the calldiff project to show how function calls changed, with each changed call tree appearing below its source file. It is experimental, calldiff uses Tree-sitter and can install a language grammar through npm on first use, and --staged, --stdin, and --watch are not supported with it.

What does the --json output contain?

It prints a JSON object with url, id, files, and commits, for use in scripts. With --json the commit list goes to stderr and the commits are added to the JSON payload instead, so the output stays parseable.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. remorses/critique on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/remorses-critique.svg)](https://hysenlabs.com/projects/remorses-critique)

Was this useful?