Clients
Code Review Server ships with a web client, an Emacs client, and a TUI client, but you can build your own using the Protocol.
Web Client (bun)
The web client is packaged with bun, and has a bun backend with a react frontend. If you build and run the bun backend, you'll get a working webserver on localhost:3000 which lists all of your PRs.
For a full inventory of what this client does — architecture, HTTP surface, every feature, and the protocol details a reimplementation needs to get right — see Web Client Features.
Installation
cdtobun_clientbun install && bun run buildbun start
Preferences
The gear icon next to the header opens Preferences, which has two tabs:
- Appearance — theme, preferred review location, and diff font size. These are client-side settings stored in the browser's local storage.
- Server Configuration — an editor for the server's TOML config (
~/.config/codereviewserver.toml), covering the global settings, the workflow list, the plugins and the AI features.
In the Server Configuration tab, each workflow is a collapsible card you can expand to edit, reorder with the arrow buttons, or delete. + Add workflow appends a new one. The workflow type and filter dropdowns are populated from the registries the server sends with the config, so they always offer what the running server supports; filters that need an argument (FilterByLabel and friends) get an extra input for it, and fields that don't apply to the selected workflow type are hidden.
Plugins are cards in the same style: the command to run, which PR data it is passed (--diff, --headers, --comments, --branch), whether it runs only on demand, and the LLM provider and model it is handed through CRS_LLM_PROVIDER / CRS_LLM_MODEL.
Under AI Features, the defaults every feature inherits ([AI]: provider, command, model) sit above one card per feature the server has registered. The checkbox on a card switches the feature on; expanding it sets whether it runs automatically, its mode (for features with more than one), and a provider, command or model of its own. Each card says which provider the feature would run on, worked out from the settings as they stand, the same way the server does. A feature that a legacy key such as ExperimentalLLMReviewEase still switches on shows as on, with a note; changing it writes an [[AIFeatures]] entry, which takes precedence over the key.
The editor validates as you go and blocks a save while anything is wrong — a missing section title, a duplicate workflow name, a repository that isn't in owner/repo form, an OpenRouter plugin or feature with no model, an enabled feature that would run a command when none is set. The server validates again before writing and reports anything the client missed against the field that caused it, so a config that can't run never reaches disk. See Configuration for what saving does to the file and when the changes take effect.
TUI Client
The TUI client (tui_client/) is a fast, keyboard-driven terminal interface built in Rust. It's well-suited for quick access to your review queue from any terminal.
Installation
Prerequisites: The codereviewserver binary must be installed and in your $PATH (see Quickstart).
Running
The client automatically spawns codereviewserver --server as a child process, so no separate server process is needed.
Keybindings
The TUI uses vim-style keybindings throughout.
PR List (Sections View)
| Key(s) | Action |
|---|---|
j / ↓ |
Move cursor down |
k / ↑ |
Move cursor up |
d |
Jump down 10 items |
u |
Jump up 10 items |
g |
Jump to top |
G |
Jump to bottom |
Enter / l |
Open PR detail view |
o |
Open PR in browser |
r |
Refresh from server |
q |
Quit |
PR Detail View
| Key(s) | Action |
|---|---|
j / ↓ |
Scroll down 1 line |
k / ↑ |
Scroll up 1 line |
d |
Page down 20 lines |
u |
Page up 20 lines |
g |
Jump to top |
G |
Jump to bottom |
o |
Open PR in browser |
r |
Sync PR from GitHub |
q / Esc / h / Backspace |
Back to PR list |
Running a Background Sync Daemon
The TUI client spawns a fresh server process each time it starts, which means it won't have any locally cached PR data until a sync completes. The first load can be slow if the cache is cold.
More importantly, GitHub API rate limits mean that frequent short-lived syncs are wasteful. The server uses lock files to prevent concurrent workflow runs from over-querying the API, so having a single long-running sync process in the background is the recommended approach.
Run a persistent sync daemon alongside your normal workflow:
# Run as a background daemon (workflows repeat on schedule, default every 10 min)
codereviewserver &
# Or use a more persistent approach via systemd, tmux, etc.
With a daemon running, the TUI client will find an already-warm cache on startup and load reviews nearly instantly. The daemon's lock files also ensure the TUI's own r (refresh) calls don't race with background syncs.
See Configuration for WorkflowInterval and other scheduling options.
Emacs Client
The emacs client lives in the client.el/ directory. It is split into
several modules; crs-client.el is the entry point that loads them all
(crs-vars, crs-html, crs-rpc, crs-render, crs-list-mode,
crs-review, crs-comments, crs-review-actions, crs-plugins, crs-ai).
Installation
Spacemacs
;; in dotspacemacs-additional-packages
(code-review-server :location (recipe
:fetcher github
:repo "C-Hipple/gtdbot"
:files ("*.el")))
Usage
- Open
client.el/crs-client.el&& evaluate the buffer (itrequires the other modules) - Run commands:
(crs-start-server) ;; to start the processing
(crs-get-reviews) ;; Load your required reviews into an ephermeral org-mode buffer
;; To start a review
(crs-start-review-at-point) ;; when your cursor is on a github URL
(crs-get-review "C-Hipple" "code-review-server" 1) ;; Start it directly.
In the reviews buffer, each PR's headline carries its tags: the repository,
draft or merged, conflict when GitHub reports that the PR's branch conflicts
with its base (drawn in crs-list-conflict-face), and the
review-ease rating when that feature is on.
Starting a review will then load a new code-review buffer which you can read the review, make comments, and submit your review.
Submitting (C-c C-c, crs-submit-review) runs in the background: the command
returns as soon as the request is sent, so you can read on or move to the next
PR while GitHub takes the review and the server refetches the PR. When the
server replies, a message says what went out — Approved acme/widgets #42,
naming any replies GitHub refused — and the review buffer is redrawn from the
reply. A PR takes one submit at a time, and a failed submit leaves your review
feedback in place to submit again.
When the server has an AI feature enabled, C in a review
buffer (crs-get-ai-output) opens its report for the PR — for example, which
review comments are still outstanding — running it first when it never ran or its
report is stale. The report buffer updates itself while a run is pending; r
refreshes it, R forces a re-run and q quits.
The change diagram has a command of its own:
M in a review buffer, or on a PR's line in the reviews buffer, runs
crs-show-change-diagram, which shows the diagram's raw Mermaid source in a
buffer of its own — in mermaid-mode
when it is installed, so C-c C-b renders it with mmdc and C-c C-o opens it
in the Mermaid live editor. Called with M-x anywhere else, it asks for the PR as
a URL or owner/repo#number. The same r, R and q apply, and the header line
says whether the diagram still describes the PR.
Embedded screenshots
Descriptions, reviews and comments are rendered with shr, and screenshots in
them display inline in graphical Emacs. The client does not fetch those images
itself: an attachment on a private repository is served only to a request
carrying a GitHub token, which shr has no way to send. The server downloads
them instead and the reply's images field says where each one landed, so the
client points the <img> tag at that local file. An image the server did not
cache — anything hosted off GitHub, or one that has not been downloaded yet — is
left for shr to fetch as before.
Configuration
The following customizable variables control client behavior. Set them in your Emacs init file or via M-x customize-group RET crs RET.
crs-include-comments-tree
Default: nil
When set to a non-nil value, a *** Comments sub-tree is included under each PR heading in the reviews buffer. Each comment is rendered as a fourth-level org heading showing the author and, for inline comments, the file path.
This is disabled by default because rendering comment threads for every PR in the list can noticeably slow down the reviews buffer. The server always returns comment data from its local DB cache — no extra GitHub API calls are involved — but the additional org structure adds rendering overhead proportional to the total number of comments across all listed PRs. Setting this to nil strips the comments before handing the content to org-mode.
Enable it if you want a quick overview of comment activity without opening each PR individually.