git-reviewer 2.12.0 → 2.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,14 +3,14 @@
3
3
  [![CI](https://github.com/dheerajjha/reviewer/actions/workflows/ci.yml/badge.svg)](https://github.com/dheerajjha/reviewer/actions/workflows/ci.yml)
4
4
  [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE)
5
5
  [![Node](https://img.shields.io/badge/node-%3E%3D18-brightgreen.svg)](package.json)
6
- [![Tests](https://img.shields.io/badge/tests-335-brightgreen.svg)](test/)
6
+ [![Tests](https://img.shields.io/badge/tests-359-brightgreen.svg)](test/)
7
7
  [![Dependencies](https://img.shields.io/badge/dependencies-3-brightgreen.svg)](package.json)
8
8
 
9
9
  When an agent writes the code, reading it becomes the bottleneck — and the tool
10
10
  built for reading a diff, the pull request, wants a branch, a remote and a push
11
11
  you are not ready to make yet.
12
12
 
13
- `reviewer` gives you the pull-request view of your working directory instead:
13
+ reviewer gives you the pull-request view of your working directory instead:
14
14
  inline comments with threaded follow-ups, each attached to the exact line you
15
15
  mean, with no branch, no remote, no push and no account. Then it hands the
16
16
  marked-up review back to the agent to act on.
@@ -19,15 +19,19 @@ marked-up review back to the agent to act on.
19
19
  npm install -g git-reviewer
20
20
 
21
21
  cd ~/your-project
22
- reviewer | claude -p "Apply this review."
22
+ git reviewer | claude -p "Apply this review."
23
23
  ```
24
24
 
25
- That is the whole loop. `reviewer` opens the review in your browser; when you
25
+ Installed as `git-reviewer`, it runs as `git reviewer` — git treats any
26
+ `git-<name>` command on your PATH as a subcommand, so the name you install is
27
+ the name you type. `reviewer` is the same program under a shorter name.
28
+
29
+ That is the whole loop. `git reviewer` opens the review in your browser; when you
26
30
  press **Submit Review**, the review is written to stdout and the command
27
31
  exits — so whatever is on the other end of the pipe receives it and starts
28
32
  work. Nothing to copy, no second command, no file to go and find.
29
33
 
30
- Without a pipe, `reviewer` is just a review tool: it tells you where the
34
+ Without a pipe, `git reviewer` is just a review tool: it tells you where the
31
35
  review was saved and keeps serving, because you may have more to read.
32
36
  Everything it says to you goes to stderr either way, so only the review is
33
37
  ever on stdout.
@@ -46,8 +50,8 @@ When the review and the hand-off happen at different times, the two-step form
46
50
  still works and is the one to reach for:
47
51
 
48
52
  ```bash
49
- reviewer # read the diff, comment on it
50
- reviewer export . --format prompt | claude -p "Apply this review."
53
+ git reviewer # read the diff, comment on it
54
+ git reviewer export . --format prompt | claude -p "Apply this review."
51
55
  ```
52
56
 
53
57
  Comments persist between sessions and survive the code moving underneath them —
@@ -64,8 +68,8 @@ comment they answer, and everything is saved as you type.
64
68
  ## Usage
65
69
 
66
70
  ```
67
- reviewer [repository] [options]
68
- reviewer export [repository] [--format json|prompt]
71
+ git reviewer [repository] [options]
72
+ git reviewer export [repository] [--format json|prompt]
69
73
 
70
74
  repository A git repository, or any directory inside one
71
75
  (default: the current directory)
@@ -79,10 +83,10 @@ reviewer export [repository] [--format json|prompt]
79
83
  ```
80
84
 
81
85
  ```bash
82
- reviewer # review the repository you are standing in
83
- reviewer ~/work/api # review another one
84
- reviewer . --no-open # print the URL, open it yourself
85
- reviewer . --port 8080 # somewhere other than 4500
86
+ git reviewer # review the repository you are standing in
87
+ git reviewer ~/work/api # review another one
88
+ git reviewer . --no-open # print the URL, open it yourself
89
+ git reviewer . --port 8080 # somewhere other than 4500
86
90
  ```
87
91
 
88
92
  Two at once is fine — the second one finds its own port.
@@ -110,19 +114,25 @@ To install it as a command rather than running it through `npx`:
110
114
 
111
115
  ```bash
112
116
  npm install -g git-reviewer
113
- reviewer .
117
+ git reviewer .
114
118
  ```
115
119
 
116
- The package is `git-reviewer` because `reviewer` and `code-reviewer` were both
117
- already taken on npm. It installs the command under both names, so whichever
118
- one you reach for is the one that is there:
120
+ The package is called `git-reviewer` because `reviewer` was already taken on
121
+ npm — and it turns out to be the right name. Git runs any `git-<name>` on your
122
+ PATH as a subcommand, so the name you install is the name you type. It also
123
+ installs `reviewer`, the same program under a shorter name, for anyone who
124
+ prefers it:
119
125
 
120
126
  ```bash
121
- reviewer # the command
122
- git-reviewer # the package name, if that is what you typed
123
- git reviewer # git dispatches to it like any other subcommand
127
+ git reviewer # the name it is installed as
128
+ reviewer # the same program, shorter
124
129
  ```
125
130
 
131
+ Whichever you use is the name it uses back: help and hints are printed in
132
+ terms of the command you actually ran, so they can be pasted as they are.
133
+ Under git, `git reviewer --help` opens the manual page — git reads `--help` on
134
+ any subcommand that way — and `git reviewer -h` prints the usage.
135
+
126
136
  From a clone, if you would rather not install anything:
127
137
 
128
138
  ```bash
@@ -132,7 +142,7 @@ cd reviewer && npm install && npm link
132
142
 
133
143
  ## What it shows you
134
144
 
135
- `reviewer` picks what to review based on the state of the repository, so there
145
+ reviewer picks what to review based on the state of the repository, so there
136
146
  is nothing to configure:
137
147
 
138
148
  | Repository state | What you review |
@@ -141,7 +151,7 @@ is nothing to configure:
141
151
  | Working directory clean | The last commit against its parent |
142
152
  | No commits yet | Every tracked and untracked file, as wholly new |
143
153
 
144
- ### Comparing branches
154
+ ### Comparing branches and commits
145
155
 
146
156
  Those defaults cover the common case with nothing to set up, but a branch you
147
157
  have worked on for a day is several commits deep, and the default scope only
@@ -178,6 +188,29 @@ commits that add a file and then delete it produce no diff between the ends —
178
188
  correctly — and that reads as *"2 commits undo each other"*, not as *"nothing
179
189
  to review"*.
180
190
 
191
+ #### Reviewing commit by commit
192
+
193
+ Whenever the review is made of commits, the sidebar lists them above the files:
194
+ the commits a comparison is made of, oldest first, or the branch's recent
195
+ history when you are looking at its last commit.
196
+
197
+ - **Click** a commit to review it on its own, with its full message above the
198
+ diff — often the only place the author says *why*.
199
+ - **Shift-click** a second to review the run between them, both ends included.
200
+ From the last commit, shift-clicking three rows down is "the last three
201
+ commits"; inside a comparison, it skips the ones you have already read.
202
+ - **`[`** and **`]`** step one commit older or newer, and **All commits** goes
203
+ back to the whole comparison.
204
+
205
+ That is the same gesture a pull request's commit picker uses, for the same
206
+ reasons: a large change reads better in the order it was written, and one
207
+ commit of renames is easier to skip than to read around.
208
+
209
+ A run has to be one unbroken line of history. To diff two points that are not
210
+ — a commit on one branch against another branch — use **Compare**; its pickers
211
+ list recent commits of whichever branch you are comparing, right after the
212
+ local branches.
213
+
181
214
  Comments are kept when you switch between comparisons. One on a file that is
182
215
  not in the current view stays in the comments list, marked **Not in this
183
216
  view**, and is back in place whenever you open a view that includes the file.
@@ -267,8 +300,8 @@ export` prints the review you saved — no server, no submit step — either as
267
300
  JSON or as instructions ready to pipe:
268
301
 
269
302
  ```bash
270
- reviewer export . --format prompt | claude -p "Apply this review to the repo."
271
- reviewer export . | jq '.comments[].file'
303
+ git reviewer export . --format prompt | claude -p "Apply this review to the repo."
304
+ git reviewer export . | jq '.comments[].file'
272
305
  ```
273
306
 
274
307
  Every comment carries an `anchor`: the exact text of the line it was left on.
@@ -305,8 +338,9 @@ The UI is a client of this; nothing is hidden from you.
305
338
  | Endpoint | Purpose |
306
339
  |---|---|
307
340
  | `GET /api/health` | Liveness, plus the number of open sessions |
308
- | `POST /api/load-repo` | Open a repository; returns a `repoId` and the changed files. Pass `base` (and optionally `head`) to compare two refs from their merge base |
309
- | `GET /api/refs/:repoId` | Branches, remote branches, tags and recent commits, for choosing what to compare |
341
+ | `POST /api/load-repo` | Open a repository; returns a `repoId` and the changed files. Pass `base` (and optionally `head`) to compare two refs from their merge base, or `commits: { from, to }` to review one commit or an unbroken run of them, both ends included |
342
+ | `GET /api/refs/:repoId` | Branches, remote branches, tags and recent commits, for choosing what to compare. `?head=` makes the commits those of another branch |
343
+ | `GET /api/log/:repoId` | Commits with their messages: `?base=&head=` for a comparison's commits, oldest first, or `?head=` alone for recent history, newest first |
310
344
  | `GET /api/commits/:repoId` | Recent commits only; kept for callers of 2.11 |
311
345
  | `GET /api/alive` | Held open by the page; when the last one closes, a terminal run stops |
312
346
  | `GET /api/file/:repoId/:path` | The file's diff, as structured lines |
@@ -338,7 +372,7 @@ rather than a public issue.
338
372
 
339
373
  ```bash
340
374
  npm install # 3 dependencies, no build step, ~6MB
341
- npm test # 335 tests
375
+ npm test # 359 tests
342
376
  npm run test:watch
343
377
  npm run test:coverage
344
378
  ```
package/bin/reviewer.js CHANGED
@@ -4,7 +4,7 @@
4
4
  const fs = require('fs').promises;
5
5
  const path = require('path');
6
6
 
7
- const { parseArgs, buildUrl, UsageError, USAGE } = require('../lib/cli');
7
+ const { parseArgs, buildUrl, UsageError, usage, invokedAs } = require('../lib/cli');
8
8
  const { homeRelative } = require('../lib/paths');
9
9
  const { openInBrowser } = require('../lib/browser');
10
10
  const { formatPrompt } = require('../lib/agent');
@@ -19,6 +19,10 @@ const {
19
19
  const { startServer, DEFAULT_PORT, DEFAULT_HOST } = require('../server');
20
20
  const { version } = require('../package.json');
21
21
 
22
+ // What to call this command when it talks about itself: whatever the person
23
+ // typed, so a hint can be pasted back as-is. See invokedAs in lib/cli.js.
24
+ const NAME = invokedAs(process.env, process.argv[1]);
25
+
22
26
  /**
23
27
  * The `reviewer` command: start the local server and open it in a browser.
24
28
  */
@@ -138,7 +142,7 @@ async function main() {
138
142
  }
139
143
 
140
144
  if (options.help) {
141
- process.stdout.write(USAGE);
145
+ process.stdout.write(usage(NAME));
142
146
  return;
143
147
  }
144
148
 
@@ -212,7 +216,7 @@ async function main() {
212
216
  note(`\n Review saved ${homeRelative(reviewPath)}`);
213
217
  note(` ${count}\n`);
214
218
  note(' Hand it to an agent with:');
215
- note(` reviewer export ${repoPath ? homeRelative(repoPath) : '.'} --format prompt | claude -p "Apply this review."\n`);
219
+ note(` ${NAME} export ${repoPath ? homeRelative(repoPath) : '.'} --format prompt | claude -p "Apply this review."\n`);
216
220
  return;
217
221
  }
218
222
 
@@ -265,7 +269,7 @@ async function main() {
265
269
  main().catch(error => {
266
270
  if (error instanceof UsageError) {
267
271
  console.error(`error: ${error.message}\n`);
268
- process.stderr.write(USAGE);
272
+ process.stderr.write(usage(NAME));
269
273
  process.exit(2);
270
274
  }
271
275
 
package/lib/agent.js CHANGED
@@ -205,6 +205,17 @@ function describeScope({ repository, mode, range }) {
205
205
  return `Reviewed: uncommitted changes in the working tree, against ${repository.branch ?? 'HEAD'}${at}.`;
206
206
  }
207
207
 
208
+ if (mode === 'range' && range?.kind === 'commits') {
209
+ // Commits themselves, picked from a list. Both ends are *in* the review,
210
+ // which is the difference from a comparison and worth saying: an agent
211
+ // told "between A and B" would reasonably assume A's own changes were
212
+ // outside it.
213
+ if (range.first === range.last) {
214
+ return `Reviewed: commit ${range.last}${range.subject ? ` ("${range.subject}")` : ''} on its own, against its parent.`;
215
+ }
216
+ return `Reviewed: ${range.commits} commits, ${range.first} through ${range.last} inclusive, as a single combined diff.`;
217
+ }
218
+
208
219
  if (mode === 'range' && range) {
209
220
  // Both ends, and the count, because the count is the only thing that
210
221
  // distinguishes "one commit of work" from "nine" once the diff has been
package/lib/cli.js CHANGED
@@ -9,19 +9,22 @@ const path = require('path');
9
9
  * without starting a server or opening a browser.
10
10
  */
11
11
 
12
- const USAGE = `reviewer — review local git changes in your browser
12
+ function usage(name = 'git reviewer') {
13
+ return `${name} — review local git changes in your browser
13
14
 
14
15
  Usage:
15
- reviewer [repository] [options]
16
- reviewer export [repository] [--format json|prompt] [--file <path>]
16
+ ${name} [repository] [options]
17
+ ${name} export [repository] [--format json|prompt] [--file <path>]
17
18
 
18
- Installed under both names: reviewer and git-reviewer. The second name also
19
- makes it reachable as a git subcommand: git reviewer
19
+ One program, two names. git reviewer is the name the package is installed as
20
+ -- git runs any git-<name> on your PATH as a subcommand -- and reviewer is the
21
+ short form. Under git, use -h for this text: git reviewer --help opens the
22
+ manual page instead.
20
23
 
21
24
  Handing a review to an agent:
22
- reviewer | claude -p "Apply this review."
25
+ ${name} | claude -p "Apply this review."
23
26
 
24
- With something reading its output, reviewer writes the finished review there
27
+ With something reading its output, it writes the finished review there
25
28
  the moment you submit it in the browser, and exits. On a terminal there is
26
29
  nothing waiting, so it says where the review was saved and keeps serving.
27
30
  Everything it tells you goes to stderr either way, so only the review is
@@ -46,15 +49,22 @@ Options:
46
49
  -v, --version Show the version
47
50
 
48
51
  Examples:
49
- reviewer review the repository you are standing in
50
- reviewer | claude -p "Apply this review."
51
- review it, then hand it straight to an agent
52
- reviewer ~/work/api review another repository
53
- reviewer . --no-open print the URL, open it yourself
54
- reviewer export . print the review as JSON
55
- reviewer export . --file src/auth.js
56
- reviewer export . --format prompt | claude -p "apply this review"
52
+ ${name}
53
+ review the repository you are standing in
54
+ ${name} | claude -p "Apply this review."
55
+ review it, then hand it straight to an agent
56
+ ${name} ~/work/api
57
+ review another repository
58
+ ${name} . --no-open
59
+ print the URL, open it yourself
60
+ ${name} export .
61
+ print the review as JSON
62
+ ${name} export . --file src/auth.js
63
+ only the comments on one file
64
+ ${name} export . --format prompt | claude -p "apply this review"
65
+ hand over a review written earlier
57
66
  `;
67
+ }
58
68
 
59
69
  /** Formats `reviewer export` can print. */
60
70
  const FORMATS = ['json', 'prompt'];
@@ -256,4 +266,26 @@ function buildUrl(origin, repoPath) {
256
266
  return `${origin}/?repo=${encodeURIComponent(repoPath).replace(/%2F/g, '/')}`;
257
267
  }
258
268
 
259
- module.exports = { parseArgs, buildUrl, UsageError, USAGE, FORMATS };
269
+ /**
270
+ * The name to show in help and hints: the one the person actually typed.
271
+ *
272
+ * The package is git-reviewer and installs two commands, reviewer and
273
+ * git-reviewer. Showing one while someone typed the other was the confusing
274
+ * part, so this answers with theirs. Git sets GIT_EXEC_PATH when it runs a
275
+ * subcommand, which is how `git reviewer` is told apart from `git-reviewer`
276
+ * typed directly; npx sets npm_command=exec, and for an npx run neither bare
277
+ * name is on the PATH, so the hint has to say npx too.
278
+ *
279
+ * @param {NodeJS.ProcessEnv} env
280
+ * @param {string|undefined} argv1 process.argv[1]
281
+ */
282
+ function invokedAs(env, argv1) {
283
+ if (env.npm_command === 'exec') return 'npx git-reviewer';
284
+ const bin = String(argv1 || '').split(/[\\/]/).pop().replace(/\.js$/, '');
285
+ if (bin === 'git-reviewer') return env.GIT_EXEC_PATH ? 'git reviewer' : 'git-reviewer';
286
+ return 'reviewer';
287
+ }
288
+
289
+ const USAGE = usage();
290
+
291
+ module.exports = { parseArgs, buildUrl, UsageError, USAGE, FORMATS, usage, invokedAs };
@@ -0,0 +1,80 @@
1
+ .\" Manual page for git-reviewer. git opens this for `git reviewer --help`,
2
+ .\" because it reads --help on any subcommand as a request for its man page.
3
+ .TH GIT-REVIEWER 1 "2026-09-25" "git-reviewer" "User Commands"
4
+ .SH NAME
5
+ git-reviewer, reviewer \- review local git changes in your browser, and hand the review to a coding agent
6
+ .SH SYNOPSIS
7
+ .B git reviewer
8
+ .RI [ repository ]
9
+ .RB [ \-p
10
+ .IR port ]
11
+ .RB [ \-\-no\-open ]
12
+ .br
13
+ .B git reviewer export
14
+ .RI [ repository ]
15
+ .RB [ \-\-format
16
+ .IR json | prompt ]
17
+ .RB [ \-\-file
18
+ .IR path ]
19
+ .SH DESCRIPTION
20
+ Opens the uncommitted changes of a git repository, or its last commit on a
21
+ clean tree, as a pull-request style review in your browser: inline comments
22
+ on any line, threaded follow-ups, and comparisons between branches, tags and
23
+ commits \(em reviewed together, one at a time, or as a run.
24
+ .PP
25
+ With something reading its output, it writes the finished review there the
26
+ moment you submit it, and exits:
27
+ .PP
28
+ .RS
29
+ .B git reviewer | claude \-p "Apply this review."
30
+ .RE
31
+ .PP
32
+ On a terminal it says where the review was saved and keeps serving until
33
+ the tab is closed.
34
+ .PP
35
+ The package is installed as
36
+ .BR git\-reviewer ,
37
+ which git runs as
38
+ .BR "git reviewer" .
39
+ .B reviewer
40
+ is the same program under a shorter name.
41
+ .SH COMMANDS
42
+ .TP
43
+ .B export
44
+ Print the saved review of a repository to stdout, as JSON or as a prompt for
45
+ a coding agent.
46
+ .SH OPTIONS
47
+ .TP
48
+ .BR \-p ", " \-\-port " \fIn\fR"
49
+ Port to listen on. The default is 4500; if it is taken, a free one is used.
50
+ .TP
51
+ .B \-\-no\-open
52
+ Print the address instead of opening a browser.
53
+ .TP
54
+ .BR \-f ", " \-\-format " \fIjson\fR|\fIprompt\fR"
55
+ Export format. The default is json.
56
+ .TP
57
+ .BI \-\-file " path"
58
+ Export only the comments on one repository-relative file.
59
+ .TP
60
+ .BR \-h ", " \-\-help
61
+ Print usage. Under git, use
62
+ .BR \-h :
63
+ git takes
64
+ .B \-\-help
65
+ to mean this page.
66
+ .TP
67
+ .BR \-v ", " \-\-version
68
+ Print the version.
69
+ .SH EXAMPLES
70
+ .TP
71
+ .B git reviewer
72
+ Review the repository you are standing in.
73
+ .TP
74
+ .B git reviewer | claude \-p """Apply this review."""
75
+ Review it, then hand it straight to an agent.
76
+ .TP
77
+ .B git reviewer export . \-\-format prompt | claude \-p """Apply this review."""
78
+ Hand over a review written earlier.
79
+ .SH SEE ALSO
80
+ https://github.com/dheerajjha/reviewer
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-reviewer",
3
- "version": "2.12.0",
3
+ "version": "2.13.1",
4
4
  "description": "Review your coding agent's changes like a pull request — locally, with inline comments and threaded follow-ups, then hand the marked-up review back to the agent",
5
5
  "author": "Dheeraj Jha",
6
6
  "license": "MIT",
@@ -38,7 +38,8 @@
38
38
  "bin/",
39
39
  "lib/",
40
40
  "public/",
41
- "server.js"
41
+ "server.js",
42
+ "man/"
42
43
  ],
43
44
  "scripts": {
44
45
  "start": "node bin/reviewer.js",
@@ -51,5 +52,8 @@
51
52
  "cors": "^2.8.5",
52
53
  "express": "^4.18.2",
53
54
  "simple-git": "^3.20.0"
54
- }
55
+ },
56
+ "man": [
57
+ "./man/git-reviewer.1"
58
+ ]
55
59
  }