Desktop application to review your own changes in Linux and macOS
Overview
https://github.com/user-attachments/assets/ada04c50-9107-4f0d-a1ee-789016bbf2de A local code review tool for developers working with AI coding agents. When you use tools like Claude Code to generate code changes, you need to review those changes before accepting them. Today your options are: push to GitHub (exposing unfinished work to a remote server) or squint at diffs in the terminal. gives you GitHub's pull request review UI on your local machine, no remote, no account, no setup. Launch it from the terminal, review the diff, leave comments and suggestions, close the window. Your feedback is written to a file as structured XML that you feed directly back to your AI agent. Review plans from AI assistants and leave on specific sections. Feed the structured XML back to improve the plan. Plans generated by AI assistants are often long markdown documents.
README
self-review
https://github.com/user-attachments/assets/ada04c50-9107-4f0d-a1ee-789016bbf2de
A local code review tool for developers working with AI coding agents.
When you use tools like Claude Code to generate code changes, you need to review those changes before accepting them. Today your options are: push to GitHub (exposing unfinished work to a remote server) or squint at diffs in the terminal.
self-review gives you GitHub’s pull request review UI on your local machine, no remote, no account, no setup. Launch it from the terminal, review the diff, leave comments and suggestions, close the window. Your feedback is written to a file as structured XML that you feed directly back to your AI agent.
Use Cases
Reviewing AI Assistant Plans
Review rendered markdown plans from AI assistants and leave inline feedback on specific sections. Feed the structured XML back to improve the plan.
Reviewing AI-Generated Code
Review code diffs in a GitHub-style split view with inline comments and suggestions, all without leaving your machine.
Codebase Exploration
Open any directory and annotate the code with categorized comments, questions, improvements, documentation gaps, then feed the XML to an LLM to act on your notes.
AI-Assisted Review
Have an AI assistant pre-generate a review, then open it in self-review to validate, edit, or discard each comment before feeding it back.
Installation
macOS
Install with Homebrew:
brew install --cask e0ipso/self-review/self-review
To upgrade later:
brew upgrade --cask self-review
Because the app is not yet code-signed, macOS Gatekeeper will flag it as damaged. Remove the quarantine attribute before opening:
xattr -cr /Applications/Self\ Review.app
To use the self-review command from the terminal, create a wrapper script:
sudo tee /usr/local/bin/self-review > /dev/null << 'SCRIPT'
#!/bin/bash
exec "/Applications/Self Review.app/Contents/MacOS/Self Review" "$@"
SCRIPT
sudo chmod +x /usr/local/bin/self-review
Linux
Install with Homebrew:
brew install e0ipso/self-review/self-review
To upgrade later:
brew upgrade self-review
Upgrading from 1.x
- Menu Quit (Cmd/Ctrl+Q) now asks Save & Quit / Discard / Cancel, like closing the window. A save that fails keeps the window open and shows the error.
- An
output-filefrom a project’s.self-review.yamlmust stay inside the directory you launch from. Put an output path elsewhere in~/.config/self-review/config.yaml, or pick it in the save dialog. Projectdefault-diff-argsmay not use--output,--ext-diffor--textconv. self-review-serveprints a URL that carries a session key. Open that URL; API clients send the key asAuthorization: Bearer.@self-review/core,@self-review/reactand@self-review/servechanged their APIs. The 2.0.0 release notes list every change.
How it works
Instead of seeing your changes with git diff execute self-review . This
will open the app and will record all the feedback in an XML file (review.xml by default).
You can continue your saved review if you didn’t finish it:
# Resume a previous review
self-review --staged --resume-from review.xml
Arguments that are not self-review’s own go straight to git diff. --resume-from also
takes the --resume-from= form, a -- ends self-review’s own options (everything after it,
and the value of a git option such as -S, is passed to git as written), and options that change
what git prints (--stat, --name-only, --word-diff, …) are refused with the flag named,
because the review needs a patch it can parse. Git output the app cannot show faithfully, such as a
merge-conflict section, is reported in the window instead of being left out of the review.
Saving and quitting
Finish Review writes the review and exits. Closing the window, or choosing Quit from the menu
(Ctrl+Q / Cmd+Q), asks first: Save & Quit, Discard or Cancel. The app only exits after a
successful save or an explicit Discard. If the save fails, for example because the output path is a
directory, the disk is full or the file is not writable, the window stays open with all your
comments, a dialog says why, and any earlier review.xml is left exactly as it was. Fix the cause
or pick another path from the footer of the file tree and save again.
Examples
# Review staged changes, produces ./review.xml by default
self-review --staged
# Review changes between branches
self-review main
# Review the last commit
self-review HEAD^
# Review the current changes
self-review
# Review a remote pull/merge request by URL
self-review https://github.com/owner/repo/pull/42
self-review https://gitlab.com/group/project/-/merge_requests/7
Reviewing a remote PR/MR
Hand self-review a GitHub pull request or GitLab merge request URL (self-hosted GitLab works too —
the forge is detected from the URL shape) and review it in the same UI, existing discussion threads
included. The review is still written to a local review.xml; nothing is ever sent back to the
forge.
Under the hood the diff is materialized through local git: if you run the command from inside a
clone of that repository, self-review reuses it (it only fetches into refs of its own under
refs/self-review/, which it deletes when you close the app — your working tree and branches are
untouched, and several self-review sessions can share the clone); otherwise it creates a temporary
blobless clone under your system temp directory and removes it when you close the app. Each git
command is bounded (10 minutes), the desktop app gives the whole startup 45 seconds, a URL opened
from the welcome screen gets 10 minutes, and Ctrl+C on fetch-comments cancels the clone in
progress and removes it before exiting. Private repositories work through git’s own credentials (SSH
keys or credential helpers — gh auth setup-git / glab auth git-credential wire your forge CLI
login into git). The gh / glab CLIs are only needed to sync the PR/MR discussion threads;
without them the review itself still works at full fidelity, just without the threads.
There is also a headless subcommand that fetches the discussion threads into a review file without opening a window:
# Write the PR's discussion threads to ./review.xml (no window)
self-review fetch-comments https://github.com/owner/repo/pull/42
# Include threads GitLab marks resolved (default is unresolved only)
self-review fetch-comments https://gitlab.com/group/project/-/merge_requests/7 --all-threads
It needs no display. On Linux the command picks a headless graphics platform for itself, so it runs
over SSH, in CI and in a container with $DISPLAY unset and no xvfb-run wrapper.
Serve mode
The same review UI also runs in your browser, through self-review-serve, for when opening an
Electron window is not an option: a remote box, a container, or a machine with no display.
npx @self-review/serve --staged
It takes the same git diff arguments as self-review, plus -o/--output for where to write the
review and --resume-from to carry a previous one in:
npx @self-review/serve # unstaged changes
npx @self-review/serve --staged
npx @self-review/serve main..feature-branch
npx @self-review/serve --resume-from review.xml # continue a previous review
npx @self-review/serve -o my-review.xml --staged # write somewhere other than ./review.xml
The URL goes to stderr when the process starts. Open it in a browser exactly as printed: the part
after # is this session’s key, and the page does not work without it.
The output path is set once, at startup, by --output/-o or by output-file in
.self-review.yaml. No control in the browser changes it afterward. Finishing the review writes
that file and stops the process.
Closing the tab warns you first, once you have entered something. Comments live only in the page until you finish, so closing without finishing loses them. The desktop application asks the same question with more options, since it can offer to save on the way out and a browser cannot. Nothing is auto-saved either way.
Walkthrough guides work as they do in the desktop application. A review.guide.xml sitting next to
your output path is picked up at startup and the file tree opens in guided mode.
The listener binds to 127.0.0.1, will not answer a request that names anything but itself (so a
web page you happen to be visiting cannot reach it), and requires the session key on every API
request (so another account on the same host cannot either). The key is drawn fresh each start and
exists only in the printed URL’s fragment: a reloaded or retyped address does not have it and gets a
page saying so, and a lost URL means starting the process again. Reaching it over an ssh -L
forward works as you would expect — open the printed URL against the forwarded port, fragment and
all. Anyone who can reach that forwarded port and has the URL has the access you do, so treat the
URL like a password.
See packages/serve/README.md for the package itself.
Assistant Skill
self-review ships with an AI assistant skill that closes the feedback loop: it reads your review XML, prioritizes the feedback, and executes the changes. It works with any assistant that supports skill directories, such as Claude Code, Codex, OpenCode, and others.
The repo also ships two companion skills under .agents/skills/: self-review-critique
(pre-generates a review for you to curate) and self-review-guide (generates the walkthrough guide
sidecar that powers guided mode; critique runs it as its first step). Install them the same way as
self-review-apply below.
Install
Copy the skill directory into your project:
# From the self-review repo (or download the folder from GitHub)
cp -r .agents/skills/self-review-apply /path/to/your/project/.agents/skills/
Usage
After running self-review and producing a review.xml, invoke the skill in your assistant:
/self-review-apply review.xml
Configuration
Customize self-review with YAML configuration files:
- User config:
~/.config/self-review/config.yaml, personal preferences for all projects - Project config:
.self-review.yaml, per-project settings (committable)
Project config overrides user config, which overrides built-in defaults.
The two files are trusted differently, because a project file is committed by whoever owns the
repository while the user file is yours. An output-file from your user config is treated like a
path you typed and may point anywhere, such as a reviews directory outside the repository; an
output-file from a project config, or the default ./review.xml, must stay inside the directory
you launched from and may not be a symlink, so a repository cannot redirect where the review is
saved. Likewise default-diff-args from a project config may not contain --output, --ext-diff
or --textconv (git options that write a file or run an external program); the app refuses to start
and names the option and the file. Your own arguments and user config are not restricted.
Available options
theme: light, dark, or system (default: system)diff-view: split or unified (default: split)font-size: font size in pixels for diff code (default: 14; the row height grows with it, and a value that is not a positive number falls back to the default)output-file: path for the review XML output (default:./review.xml)guide-file: path to the walkthrough guide sidecar (default: derived fromoutput-fileas.guide.xml, e.g.review.guide.xml)ignore: file patterns to exclude from diff (gitignore-compatible syntax; defaults cover common vendor/build dirs and lock files)categories: custom comment tags (see example above)default-diff-args: default arguments passed togit diff, split with shell quoting rules, so-- "my file.txt"stays a single argumentshow-untracked: show new files not yet added to git (default: true)word-wrap: wrap long lines in the diff viewer (default: true)max-files: maximum number of files before the large payload guard triggers (default: 500). Set to0to disable.max-total-lines: maximum total diff lines before the large payload guard triggers (default: 100000). Set to0to disable.
When either threshold is exceeded, a confirmation dialog appears. Cancelling exits the app; continuing enters large-payload mode with lazy content loading: every file starts collapsed, and a file’s hunks are fetched when you open it. A fetch that fails shows its error with a Retry button and is not retried on its own.
See docs/PRD.md for complete documentation.
Design principles
- CLI-first. Launched from the terminal, writes review output to a file. Behaves like a Unix tool.
- One-shot. Open → review → close → done. No persistent state. Serve mode does run a listener, but only on loopback and only for the duration of one review: submitting writes the output file and stops the process, so the principle holds even there.
- Local-only. No network access, no accounts, no telemetry. Your code stays on your machine. The one opt-in exception: reviewing a remote PR/MR by URL fetches that repository through git and reads its discussion threads — and even then, nothing is ever sent to the forge.
- AI-native output. The XML format is designed to be parsed by LLMs, with an XSD schema they can reference for structure.
Recommended Tools
Try a different keyword or remove a filter.
Install
npx skillfish add e0ipso/self-review