Skip to content
| Marketplace
Sign in
Visual Studio Code>SCM Providers>Better ReviewNew to Visual Studio Code? Get it now.
Better Review

Better Review

CGQAQ

|
3 installs
| (1) | Free
Gerrit-style review for GitHub pull requests: patch sets, votes, submit requirements, no branch switching.
Installation
Launch VS Code Quick Open (Ctrl+P), paste the following command, and press enter.
Copied to clipboard
More Info

Better Review

Gerrit-style review for GitHub pull requests in VS Code: patch sets, per-label votes, submit requirements, one-shot Reply with local drafts, keyboard-first navigation, and no branch switching.

Status: early release (0.1). The specification is docs/DESIGN.md in the source repository.

What it does

  • Change screen with patch sets reconstructed from GitHub's own evidence (force pushes, check suites, reviews), numbered the same for every reviewer.
  • Votes Code-Review −2..+2 and Verified (from CI), mapped exactly onto GitHub review states, with Gerrit's stickiness rules across trivial rebases.
  • Submit requirements derived from branch protection and rulesets, next to GitHub's own merge state.
  • Reply: drafts stay on your machine until you send them all at once; publishing is journaled and resumable.
  • Attention signals: a count badge on the activity bar icon and a notification when one of your changes is reviewed or something else needs you, e.g. "@ada-l reviewed #42 engine: add throttle control: Code-Review-1, 3 comments".
  • Suggested changes, like Gerrit's Suggest edit and Apply fix: Suggest Edit in a comment puts the commented lines in a GitHub suggestion block for you to edit. Suggestions, also those written on github.com, are shown as diffs, and Apply Suggestion applies one to the file in your checkout after a preview, unless the lines have changed since. Reply warns when a suggestion cannot stay one on GitHub (left side, file comment, another patch set, the review message) and posts it as a diff.
  • Never touches your repository: code is read from a private sidecar object store that borrows your clone's objects; no refs, config, hooks, stash or checkout in your work tree. The one exception is Apply Suggestion, which you run and confirm; it edits the file in the editor and saves nothing.
  • Keyboard-first: ]/[ next/previous file, n/p next/previous change, c comment, a reply, r mark reviewed, Shift+M mark reviewed and open the next file, ? for all keys.

Requirements

  • VS Code 1.140 or later
  • GitHub CLI (gh), signed in with gh auth login (VS Code's GitHub sign-in is used as a fallback)
  • git (developed against 2.55)

Settings

All settings live under betterReview.*; see the Settings UI ("Better Review") for descriptions. Review caches, worktrees and drafts are stored in ~/.better-review (betterReview.storageRoot).

Behind a proxy, set betterReview.proxy (for example http://127.0.0.1:7890 or socks5://127.0.0.1:1080): GitHub API requests, git fetches and gh all use it. Without it, API requests follow VS Code's http.proxy and git and gh their own configuration.

Attention. When one of your changes is reviewed or commented on, its checks fail, it has a merge conflict or it is ready to submit, or when your review is requested, a change you reviewed gets a new patch set or someone replies to your comment, the Better Review icon in the activity bar counts the changes that need you and a notification says who did what, with Open Change. Opening the change (or reviewing it) clears it. Each event is announced once, across reloads and windows; a window without focus leaves its notifications to the next window you focus, a change is announced at most every 10 minutes, and more than three at once come as one summary. betterReview.attention.badge and betterReview.attention.notifications turn them off. While the badge is on, the Files view shows its unreviewed-file count in its title bar description, because VS Code adds up the badges of all views on one icon. The badge uses your theme's activity bar badge colour (extensions cannot make it red), and the notifications are VS Code's own: VS Code has no API for the operating system's notification center.

betterReview.gh.path, betterReview.proxy, betterReview.storageRoot and betterReview.git.extraClones are read from your user settings only: a repository's .vscode/settings.json cannot choose which executable runs, where your traffic goes or which folders are used. In untrusted (Restricted Mode) workspaces, workspace values of the other settings that pick hosts, LFS or GitHub writes (worktree.lfs, dashboard.host, files.syncViewedToGitHub, …) are ignored too.

Security notes

  • Review worktrees hold the pull request's code. A worktree file opened in the current window (w) runs under that window's workspace trust: language extensions may load configuration or tools the pull request committed. Better Review shows a one-time notice before the first one opens in a trusted window.
  • Do not put the storage root inside a folder you trust as a parent. A review worktree opened in a new window (Open Worktree in New Window) is trusted without VS Code's prompt when it lies inside such a folder. Workspace trust is never set by Better Review.
  • Credentials never appear on a command line. git asks for them only for the pull request's own host (gh auth git-credential, or a helper that answers only that host).

Development

npm install
npm run typecheck        # tsc for the extension and the webview
npm run build            # esbuild → dist/extension.js, dist/webview/change.js
npm test                 # vitest unit tests (test/unit)
npm run test:integration # downloads VS Code 1.143.1 and runs test/integration under mocha
BR_CLANGD=<path to clangd> npm run test:clangd   # comment completion through your installed vscode-clangd and that clangd
npm run package          # produces a .vsix

Press F5 ("Run Extension") to start an Extension Development Host.

Live tests

npm run test:live runs the real extension in VS Code 1.143.1 against real GitHub: it opens pull requests on the private throwaway repository CGQAQ/better-review-live-test (the only repository live tests may change), votes, replies and merges there, and checks every effect with independent gh api reads. It needs gh signed in with the repo and workflow scopes; no token is ever read or printed (git and the extension ask gh themselves). Neither npm test nor npm run test:integration runs these tests.

npm run live:seed                          # once: push the seed files (test/live/support/seed.ts) to main
npm run test:live                          # every test/live/**/*.live.test.ts
BR_LIVE_FILES=smoke npm run test:live      # one file (a bare name means <name>.live.test.ts; globs and comma lists work)
BR_LIVE_FILES='votes/*.live.test.ts' npm run test:live   # one area
BR_LIVE_WORKSPACE=clone npm run test:live  # open a temp clone of the repo as the workspace (borrowing mode)

Each run gets an id (BR_LIVE_RUN, generated by default) and its own build (out/live/<runId>/), user data directory, extensions directory and storage root (a temp directory; never ~/.better-review), so runs can go in parallel. Everything is removed afterwards unless BR_LIVE_KEEP=1; the extension's log is kept in out/live/<runId>.better-review.log. Other variables: BR_LIVE_SETTINGS (JSON merged into the instance's user settings; polling is off by default), BR_LIVE_GREP, BR_LIVE_BAIL=1, BR_LIVE_TIMEOUT (ms per test), BR_LIVE_KEEP_PRS=1 and BR_LIVE_MAX_DISPATCHES (bot pull requests per run, default 10).

The tests are grouped by area, one directory each. Run the areas one after another rather than all at once: GitHub puts secondary rate limits on an account that creates many reviews, comments and pull requests at the same time, and a whole-suite run needs more bot pull requests than one run's budget.

Area Files Bot PRs Notes
votes/ ladder, reply, replyextras, interrupt, outdated, headmoved, github, ownpr 7 every vote step, dismissals, interrupted Replies (resume, discard, crash), github.com interop, own PR
comments/ threads, patchsets, pending, vcflow, vdrift 0 anchors, fallbacks, porting, imported pending reviews, the c flow in a real diff editor
patchsets/ lifecycle, retarget, sidecar, polling, probe, reconstruct 1 probe runs only with BR_PSV_PROBE=1 (read-only, earlier PRs); reconstruct needs the BR_PS_HANDOFF=<file> a lifecycle run wrote (best in clone mode); BR_PS_VOTES=b picks the second vote order; sidecar checks borrowing only with BR_LIVE_WORKSPACE=clone; polling waits up to 5 minutes when the test window has no focus
labels/ labels, verified, requirements, dashboard 7 the ruleset test skips itself (403 on this plan)
submit/ merge, viewed, viewedPaths 10 run merge (8 bot PRs) apart from the other two; BR_LIVE_SUBMIT_VETO2=1 adds the open design question "a new −2 after Submit anyway" (submit-2, pending by default)

Some paths cannot run on this repository, by GitHub's rules or its plan, and are covered by unit tests only: Quick approve's publish path and any Code-Review requirement met without "Submit anyway" (one human account pushes every patch set, so its +2 never counts under user=non_uploader), votes from a second account or a non-writer, re-apply and GitHub's stale-approval dismissal (branch protection), ruleset requirement rows, admin merges and the merge queue. docs/DESIGN.md §0.4 lists what the live runs established about GitHub.

Layout: test/live/entry.ts wraps the real activate() and returns its Services (via the test hook servicesForTests() in src/extension.ts) plus a recorder that answers or cancels the extension's dialogs; test/live/support/ holds the helpers (gh.ts ground-truth gh api calls, truth.ts typed PR state, repo.ts branches, clones and pull requests, ext.ts sessions, content, drafts, Reply, labels and submit through the extension, seed.ts the repository's seed content). Branches are live/<area>/<runId>/<slug> and pull request titles carry [live <area> <runId>]. Pull requests that need Code-Review±2 are opened by github-actions[bot] (openBotPr, through the repository's open-pr workflow), since GitHub never lets authors approve their own. The repository is private on a plan without rulesets, branch protection or auto-merge (each answers HTTP 403 "Upgrade to GitHub Pro"), so those paths cannot be tested live.

Layout: src/model holds the frozen shared contracts, src/core the shared services, and each unit (src/github, src/git, src/session, src/votes, src/comments, src/views, src/webviewHost) exports one factory from its index.ts. Pure modules never import vscode, fs or child_process and are tested with vitest against the fakes in test/fakes.

Releasing

.github/workflows/release.yml builds, type-checks, unit-tests and packages the extension, then attaches the .vsix (and its .sha256) to a GitHub release and publishes it on the VS Code Marketplace. Bump version in package.json, add a ## [x.y.z] section to CHANGELOG.md (it becomes the release notes), push, then push the matching tag:

npm run --silent release:notes -- 0.2.0   # preview the notes: the section with its wrapped lines joined
git tag v0.2.0 && git push origin v0.2.0

GitHub shows every newline inside a release body's paragraphs as a line break, so scripts/release-notes.ts joins the soft line breaks of each paragraph, as a CommonMark + GFM parser (micromark) finds them, and leaves code, tables, HTML, headings, hard breaks and alert markers as written. It also appends the link and footnote definitions the section uses from elsewhere in the file.

The tag must equal v + the package.json version, and a version with a hyphen (0.2.0-beta.1) becomes a GitHub prerelease and is not published on the Marketplace, which takes x.y.z versions only. Running the workflow by hand from the Actions tab with "dry run" on (the default) only builds and keeps the .vsix as a workflow artifact.

The Marketplace is published to by Trusted Publishing: vsce publish --oidc trades the job's GitHub OIDC token for a Marketplace credential, so no token is stored (Azure DevOps personal access tokens for all organizations stop working on December 1, 2026). It needs a policy on the CGQAQ publisher's Trusted Publishing page at marketplace.visualstudio.com/manage: repository owner CGQAQ, repository better-review, workflow release.yml, no environment. A new policy is inactive until its first sign-in, which for a private repository must come within 48 hours: run the workflow from the Actions tab with check publishing on, which only signs in (scripts/marketplace-signin.mjs) and publishes nothing, or make a release.

gh workflow run release.yml --repo CGQAQ/better-review -f check_publishing=true   # activate a new policy

A release run again (after a failed step) skips the GitHub release and the Marketplace version that exist already (scripts/marketplace-has.mjs reads vsce show).

License

MIT © CGQAQ (see LICENSE)

  • Contact us
  • Jobs
  • Privacy
  • Manage cookies
  • Terms of use
  • Trademarks
  • Your Privacy Choices
  • Consumer Health Privacy
© 2026 Microsoft