loupe

module
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 23, 2026 License: MIT

README

loupe

ci release

loupe collects a review agent's findings into a local draft so you decide each one and publish a single GitHub review.

Deciding each finding, then writing the review's opening sentence into the body where it will appear and sending it with one key.

Why loupe

  • Nothing posts under your name unread. You accept or drop each finding yourself, you write the review's opening in your own words, and one confirmation sends it.
  • Findings are files on disk. One directory per run. No daemon, no database, no service to sign up for.
  • Any review agent works. loupe has no opinion on how findings are found; it takes them through a CLI.

How it works

  1. An agent captures the pull request into a local draft and files findings.
  2. You decide each finding in loupe review.
  3. loupe publish shows the review it would send, you type its opening sentence into the body, and one key posts exactly one GitHub review.

Install

mise use -g github:eriksaulnier/loupe@latest
loupe --version

[!NOTE] A release cut today fails with no versions found for github:eriksaulnier/loupe matching date filter, because mise hides releases younger than its minimum_release_age. Wait, or exempt loupe: mise settings add minimum_release_age_excludes "github:eriksaulnier/loupe".

Try it

In a clone, mise run demo opens loupe review on seeded runs against an in-memory GitHub, so the interface and the whole publish flow, y included, can be tried end to end. Nothing leaves the machine. mise run demo -- <loupe args> runs any other command: acme/widgets#42 is mid-review, #43 is ready to publish, and #44 is ready with a moved head.

The board, one finding, and the last screen before anything is sent, where you write the review's opening:

And the review it posts, as GitHub renders it:

Setting up an agent

Plugin

One plugin for Claude Code, Codex and Pi. Its human-review skill is the workflow a review skill or agent follows: capture the pull request, file findings, hand the run to you, and answer your send-back notes. It brings no review method of its own.

Sending a finding back is how you use that last part: the agent answers, you resolve the note, and only then does the finding count as decided.

A finding with an open send-back note, the agent's reply to it, resolving the note and accepting the finding.

# Claude Code
claude plugin marketplace add eriksaulnier/loupe
claude plugin install loupe@loupe

# Codex
codex plugin marketplace add eriksaulnier/loupe
codex plugin add loupe@loupe

# Pi, pinned to the release; use the version loupe --version prints
pi install git:github.com/eriksaulnier/loupe@v0.10.0 # x-release-please-version

[!IMPORTANT] Keep plugin and binary together. Claude Code and Codex install the plugin from main, so a skill can name a flag an older binary lacks.

[!IMPORTANT] Inside the Codex sandbox, loupe cannot reach GitHub, the clone's .git, its data directory or the terminal host's socket. Approve the skill's requests to run loupe outside the sandbox. Pi has no sandbox.

Handing off in a terminal pane

Inside Herdr or Orca, loupe handoff opens loupe review in a split beside the agent's pane, and the pane closes when review exits cleanly. Elsewhere, or when the split fails, the skill asks you to run loupe review yourself.

To skip the approval prompt at each hand-off, allow that one command:

  • Claude Code: Bash(loupe handoff:*)
  • Codex, in ~/.codex/rules/default.rules: prefix_rule(pattern=["loupe", "handoff"], decision="allow")

loupe builds the pane's command from the run it resolves, so the rule lets an agent open review and nothing else. Do not allow herdr pane split, herdr pane run or orca terminal split: a blanket rule for any of them lets any command run in a new shell.

Commands

loupe --help is the reference, and every command's --help shows its JSON input and result.

Run by Command What it does
agent capture Capture a pull request into a new review round
agent add File findings into the draft
agent summary Set the summary that orients you while you sort findings
agent handoff Open review for the human in a new terminal pane
agent wait Block until the human sends a finding back or publishes
agent edit Change, withdraw or restore a finding
agent reply Answer a send-back note
agent feedback Read the human's notes, dispositions and readiness
anyone show Show the draft, dispositions and readiness
anyone list List every run with its state and counts
human review Decide each finding in the review interface
human publish Confirm and post the review to GitHub

Environment

Variable Meaning
LOUPE_HOME Where runs are stored; the default is $XDG_DATA_HOME/loupe, else ~/.local/share/loupe
LOUPE_RUN The run a command acts on when it names none
LOUPE_LOCK_TIMEOUT_MS How long to wait for a run's lock; default 3000, maximum 60000
LOUPE_ICONS ascii, unicode (default) or nerd (needs a Nerd Font); a non-UTF-8 locale always gets ASCII
NO_COLOR, TERM, LANG/LC_ALL Honored for color, plain-mode fallback and glyph selection
GH_TOKEN, GITHUB_TOKEN The github.com token, checked in that order; without either, loupe uses the one gh auth login stored

contracts/cli.md has the exact rules and refusals for the LOUPE_ variables.

Unattended publish

A pipeline reviews a pull request with no human and no terminal: capture, file, publish. Capture is the only step that needs the Git clone.

  1. loupe capture <pr-url> --json creates the run and prints its reference as run. Pass that reference to the later steps, either as --run <ref> or by exporting it as LOUPE_RUN; unattended publish will not fall back to the pull request of a branch, since a pipeline's checkout is usually not sitting on it.
  2. loupe show --diff > review/pr.diff writes the captured diff where the reviewer can read it, beside a checkout of the captured head. It is the supported way to get the diff out; the run directory's own layout is not a contract.
  3. Something files findings and a summary through the input any agent uses: one loupe add object or array, stored entirely or not at all, and one loupe summary. Set the summary even when there is nothing to report: with no human at a terminal it is the review's opening prose, which is the one thing an unattended round publishes that nobody read first. A finding whose location is not in the captured diff is refused with the nearest valid lines. loupe ships no adapter that turns a reviewer's output into this shape. A pipeline supplies its own, and this feature names no particular review action.
  4. loupe publish --unattended --json posts exactly one comment review, with no confirmation and no terminal, once the resolved token is a GitHub App installation token: the ghs_ prefix, which is what Actions' own GITHUB_TOKEN carries. The token needs permissions: pull-requests: write; a user token here refuses with token. Restoring the data root at a different path, even on a different machine, replays a receipt or reconciles an unknown attempt. A pipeline that persists LOUPE_HOME between steps can retry safely.

eriksaulnier/loupe-workflows is a worked example: a reusable workflow that installs a pinned loupe release, captures the pull request, runs an agent over the captured head and diff, and publishes unattended. .github/workflows/review.yml here is the caller, and .github/review-instructions.md is what loupe asks a reviewer of its own code to know. loupe ships neither; the workflow is one caller of the publication layer, not part of it.

Reference

Contributing

CONTRIBUTING.md covers setup, local development, tests and commits. Releases are cut by release-please and goreleaser; nothing is tagged or released by hand.

License

MIT. See LICENSE.

Directories

Path Synopsis
cmd
loupe command
loupe-demo command
Command loupe-demo runs loupe against seeded review runs and an in-memory GitHub, so the review interface and the whole publish flow can be tried by hand without a pull request, credentials or network.
Command loupe-demo runs loupe against seeded review runs and an in-memory GitHub, so the review interface and the whole publish flow can be tried by hand without a pull request, credentials or network.
internal
cli
Package cli maps loupe's commands onto cobra and turns domain results and refusals into the output contract.
Package cli maps loupe's commands onto cobra and turns domain results and refusals into the output contract.
diff
Package diff is the parsed pr.diff that both location validation and the review interface read.
Package diff is the parsed pr.diff that both location validation and the review interface read.
draft
Package draft holds the review draft: findings, human decisions, send-back notes and replies.
Package draft holds the review draft: findings, human decisions, send-back notes and replies.
findingid
Package findingid orders ids like f-001 so that f-999 sorts before f-1000.
Package findingid orders ids like f-001 so that f-999 sorts before f-1000.
gitenv
Package gitenv filters the environment handed to git, shared by gitx and the test repositories.
Package gitenv filters the environment handed to git, shared by gitx and the test repositories.
github
Package github is loupe's narrow view of the GitHub REST API, behind an interface so tests can fake it.
Package github is loupe's narrow view of the GitHub REST API, behind an interface so tests can fake it.
gitx
Package gitx runs the few git commands loupe needs against the user's clone.
Package gitx runs the few git commands loupe needs against the user's clone.
markdown
Package markdown checks authored Markdown against the allowlist in docs/comment-format.md, so a body or summary cannot restructure the <details> wrapper the renderer puts around it.
Package markdown checks authored Markdown against the allowlist in docs/comment-format.md, so a body or summary cannot restructure the <details> wrapper the renderer puts around it.
pane
Package pane opens loupe review for the human in a new terminal pane beside the agent's, in Herdr or Orca.
Package pane opens loupe review for the human in a new terminal pane beside the agent's, in Herdr or Orca.
publish
Package publish runs the publication state machine: gates, envelope, confirmation, one review request, receipt.
Package publish runs the publication state machine: gates, envelope, confirmation, one review request, receipt.
refusal
Package refusal is a leaf so every domain package can return refusals without importing internal/cli.
Package refusal is a leaf so every domain package can return refusals without importing internal/cli.
render
Package render composes what loupe shows and sends.
Package render composes what loupe shows and sends.
run
Package run owns the on-disk layout of review runs: paths, references, atomic writes and locking.
Package run owns the on-disk layout of review runs: paths, references, atomic writes and locking.
section
Package section places a finding in the review body's sections, so that every surface presents findings in the order the published review will.
Package section places a finding in the review body's sections, so that every surface presents findings in the order the published review will.
severity
Package severity orders loupe's four severity words so that every surface ranks findings the same way.
Package severity orders loupe's four severity words so that every surface ranks findings the same way.
style
Package style is the one place terminal appearance is decided: colors, glyphs and the small layout helpers every human-facing surface shares, so the review interface and the one-shot commands look like one program.
Package style is the one place terminal appearance is decided: colors, glyphs and the small layout helpers every human-facing surface shares, so the review interface and the one-shot commands look like one program.
testutil/fakegh
Package fakegh is an in-memory GitHub REST server for the endpoints loupe calls, reached through the production client so request encoding is exercised end to end.
Package fakegh is an in-memory GitHub REST server for the endpoints loupe calls, reached through the production client so request encoding is exercised end to end.
testutil/gitrepo
Package gitrepo builds a local stand-in for a GitHub repository: a bare remote, a user's clone of it, and a pull request head published at refs/pull/<number>/head.
Package gitrepo builds a local stand-in for a GitHub repository: a bare remote, a user's clone of it, and a pull request head published at refs/pull/<number>/head.
tui
Package tui is the human review interface: a full-screen Bubble Tea program and a line-by-line plain mode.
Package tui is the human review interface: a full-screen Bubble Tea program and a line-by-line plain mode.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL