git-reviewer 2.9.0 → 2.9.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,7 +3,7 @@
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-291-brightgreen.svg)](test/)
6
+ [![Tests](https://img.shields.io/badge/tests-299-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
@@ -294,7 +294,7 @@ rather than a public issue.
294
294
 
295
295
  ```bash
296
296
  npm install # 3 dependencies, no build step, ~6MB
297
- npm test # 291 tests
297
+ npm test # 299 tests
298
298
  npm run test:watch
299
299
  npm run test:coverage
300
300
  ```
package/lib/agent.js CHANGED
@@ -176,6 +176,40 @@ function toAgentComment(comment, id) {
176
176
  return entry;
177
177
  }
178
178
 
179
+ /**
180
+ * What this review was of, in one line.
181
+ *
182
+ * An agent applying a review needs to know whether it is looking at
183
+ * uncommitted work or at a commit that is already in history -- "make this
184
+ * change" means a different thing in each. Both used to print
185
+ * `Reviewed at commit: <sha>`, which in `working` mode is not merely vague:
186
+ * the review is of changes that are *not* in that commit, and saying they
187
+ * are is the kind of authoritative-looking line this project exists to
188
+ * avoid.
189
+ *
190
+ * A null mode is a review exported from a file written before the mode was
191
+ * recorded. Reporting only where HEAD is, exactly as it always did, is the
192
+ * honest answer there -- nothing on disk says what was compared.
193
+ *
194
+ * @param {ReviewDocument} document
195
+ * @returns {string|null}
196
+ */
197
+ function describeScope({ repository, mode }) {
198
+ const at = repository.head ? ` (${repository.head})` : '';
199
+
200
+ if (mode === 'working') {
201
+ return `Reviewed: uncommitted changes in the working tree, against ${repository.branch ?? 'HEAD'}${at}.`;
202
+ }
203
+
204
+ if (mode === 'lastCommit') {
205
+ return repository.head
206
+ ? `Reviewed: commit ${repository.head}, against its parent.`
207
+ : 'Reviewed: the most recent commit, against its parent.';
208
+ }
209
+
210
+ return repository.head ? `Reviewed at commit: ${repository.head}` : null;
211
+ }
212
+
179
213
  /**
180
214
  * Render a review as instructions for a coding agent.
181
215
  *
@@ -192,7 +226,7 @@ function formatPrompt(document) {
192
226
  '# Code review to address',
193
227
  '',
194
228
  `Repository: ${repository.path}`,
195
- repository.head ? `Reviewed at commit: ${repository.head}` : null,
229
+ describeScope(document),
196
230
  `${summary.comments} comment(s) across ${summary.files} file(s), written ${document.generatedAt}.`,
197
231
  '',
198
232
  'Work through every comment below and make the change it asks for, or, if',
package/lib/export.js CHANGED
@@ -100,7 +100,7 @@ async function readGitPosition(repoPath) {
100
100
  * records a different repository than the one asked for
101
101
  */
102
102
  async function loadReviewDocument(reviewsDir, repoPath, { file = null } = {}) {
103
- const { comments } = await readSavedComments(reviewsDir, repoPath);
103
+ const { comments, mode } = await readSavedComments(reviewsDir, repoPath);
104
104
 
105
105
  if (comments.length === 0) throw new NoReviewError(repoPath);
106
106
 
@@ -121,7 +121,7 @@ async function loadReviewDocument(reviewsDir, repoPath, { file = null } = {}) {
121
121
  // same object reference when no filter is given. `test/agent.test.js`
122
122
  // asserts that identity with `assert.equal`.
123
123
  const document = filterReviewDocument(
124
- buildReviewDocument({ repoPath, comments, generatedAt: new Date(), head, branch }),
124
+ buildReviewDocument({ repoPath, comments, generatedAt: new Date(), head, branch, mode }),
125
125
  { file }
126
126
  );
127
127
 
package/lib/store.js CHANGED
@@ -74,7 +74,10 @@ async function readJson(file) {
74
74
  *
75
75
  * @param {string} reviewsDir
76
76
  * @param {string} repoPath
77
- * @returns {Promise<{comments: object[], adopted: boolean}>}
77
+ * @returns {Promise<{comments: object[], mode: string|null, adopted: boolean}>}
78
+ * `mode` is null for a file written before it was recorded -- there is
79
+ * nothing on disk that says what was compared, and guessing would be
80
+ * worse than admitting it.
78
81
  * @throws {ReviewOwnershipError} when the current file names another repository
79
82
  */
80
83
  async function readSavedComments(reviewsDir, repoPath) {
@@ -87,15 +90,15 @@ async function readSavedComments(reviewsDir, repoPath) {
87
90
  if (!belongsTo(saved.repoPath, repoPath)) {
88
91
  throw new ReviewOwnershipError(repoPath, saved.repoPath);
89
92
  }
90
- return { comments: saved.comments ?? [], adopted: false };
93
+ return { comments: saved.comments ?? [], mode: saved.mode ?? null, adopted: false };
91
94
  }
92
95
 
93
96
  const legacyName = legacyCommentsFilename(repoPath);
94
- if (legacyName === commentsFilename(repoPath)) return { comments: [], adopted: false };
97
+ if (legacyName === commentsFilename(repoPath)) return { comments: [], mode: null, adopted: false };
95
98
 
96
99
  const legacy = await readJson(path.join(reviewsDir, legacyName));
97
100
  if (legacy === null || !belongsTo(legacy.repoPath, repoPath)) {
98
- return { comments: [], adopted: false };
101
+ return { comments: [], mode: null, adopted: false };
99
102
  }
100
103
 
101
104
  // Move it under the new name so the next read is a plain hit and the old
@@ -107,7 +110,7 @@ async function readSavedComments(reviewsDir, repoPath) {
107
110
  /* the comments are already in hand; renaming is housekeeping */
108
111
  }
109
112
 
110
- return { comments: legacy.comments ?? [], adopted: true };
113
+ return { comments: legacy.comments ?? [], mode: legacy.mode ?? null, adopted: true };
111
114
  }
112
115
 
113
116
  module.exports = { readSavedComments, ReviewOwnershipError, belongsTo };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-reviewer",
3
- "version": "2.9.0",
3
+ "version": "2.9.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",
package/server.js CHANGED
@@ -463,9 +463,15 @@ function createApp(options = {}) {
463
463
 
464
464
  await fs.mkdir(reviewsDir, { recursive: true });
465
465
 
466
+ // `mode` is on the envelope rather than on each comment: it describes
467
+ // the review, not any one remark in it, and it is what lets `reviewer
468
+ // export` say what was compared. Without it an exported review could
469
+ // only report where HEAD is, which says nothing about whether the
470
+ // comments are about committed work or uncommitted work.
466
471
  const data = {
467
472
  repoPath: session.repoPath,
468
473
  lastUpdated: new Date().toISOString(),
474
+ mode: session.mode,
469
475
  comments: normalized
470
476
  };
471
477