prreviewbuddy 0.48.0 → 0.50.0

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/CHANGELOG.md CHANGED
@@ -1,5 +1,75 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.50.0
4
+
5
+ **Every review says which commits it covers.** Beside the branch at the top of a review is its
6
+ commit count. Open it to see where your branch split off from the base, every commit in the review,
7
+ and a reminder that uncommitted changes are never included. A review is always the combined change
8
+ across all of those commits, not just the latest one.
9
+
10
+ **Help, in the workspace.** A new **Help** place in the sidebar explains what gets reviewed: how
11
+ the base sets the split point, why your local main and origin/main can give different reviews, and
12
+ how to choose between them. The commit count on a review links to it.
13
+
14
+ **Choosing between local main and origin/main tells you what each would review.** When the two are
15
+ different commits and you have not named a base, PR Review Buddy still asks which one you mean,
16
+ and now lists under each choice how many commits it would review and which ones.
17
+
18
+ **`prreviewbuddy review --help` works.** It used to say `review` does not take `--help`. Every
19
+ command now prints the help, which has a new section on what gets reviewed, with examples.
20
+
21
+ ## 0.49.2
22
+
23
+ **Buddy AI remembers which theme you asked in.** Your earlier questions now reach Buddy with the
24
+ theme you had open when you asked them, or a note that they were about the whole change. Asking
25
+ "what is risky here?" in one theme and then "how does that compare?" in another no longer reads to
26
+ Buddy as two questions about the same thing.
27
+
28
+ ## 0.49.1
29
+
30
+ **Buddy AI says what your question is about.** A line under **Ask Buddy AI** now reads **Asking
31
+ about: the whole change**, or the name of the theme you have open in the Review Path. Buddy always
32
+ sees the whole change; an open theme tells it where to focus, so "what is the risk here?" is
33
+ answered about that theme. The line used to be a small grey label beside the title, cut short, with
34
+ its meaning only in a tooltip. Theme names are now shown in full.
35
+
36
+ ## 0.49.0
37
+
38
+ **Change a review from the review itself.** The **⋯** menu at the top of a review now has
39
+ everything the card's menu on the reviews list has: **Mark reviewed** or **Mark in progress**,
40
+ **Mark waiting**, **Mark done**, **Move to active**, and **Add to review queue** or **Remove from
41
+ review queue**, as well as Save, Copy review ID and Delete. You no longer have to go back to the
42
+ list to park or queue the review you are reading. Waiting, Done and the queue belong to the branch,
43
+ so they are offered on its newest review; an older review of the same branch points you to it.
44
+
45
+ **The top bar says where the review stands.** It shows the files, the size of the change, whether
46
+ the review is up to date, and two labels: **Reviewed** or **In progress**, and **Waiting** or
47
+ **Done** when the review is parked. None of them are buttons. **Check for updates** has moved into
48
+ the **⋯** menu, because the page already checks by itself every minute while you are looking at it
49
+ and whenever you come back to the tab. Its answer, such as "No change", appears where the review's
50
+ freshness is shown.
51
+
52
+ **Check for updates works more than once.** After one check it stayed greyed out until you reloaded
53
+ the page. It now becomes available again as soon as the check finishes, and pressing it while the
54
+ page is checking by itself no longer does nothing: it waits for that check and then runs its own.
55
+
56
+ **The review page keeps up while it is open.** If a Waiting review's branch gets new commits while
57
+ you are looking at it, the page now says **Changed while waiting** within a minute, without a
58
+ reload, and offers **Mark waiting** again. Changes made from the reviews list in another tab show
59
+ up the same way. Adding a review to a full review queue from its page now says the queue is full
60
+ instead of appearing to work.
61
+
62
+ **Reviewed reviews stay in your queue.** Marking a review **Reviewed** no longer takes it out of
63
+ the review queue, from the reviews list or from the review itself. Reviewed records that you have
64
+ been through it, not that you are finished with it; **Waiting** and **Done** are how you say that,
65
+ and they still take a review out of the queue. `prreviewbuddy review --queue` now queues a Reviewed
66
+ review instead of refusing it, and, like adding from the workspace, moves a Waiting or Done review
67
+ back to Active when it goes in.
68
+
69
+ **Buddy AI has its own tab.** It is no longer a button in the top bar. When Buddy AI's panel is
70
+ closed, a **Buddy AI** tab sits on the right edge of the window; press it to open the panel. The
71
+ double arrow in the panel's header closes it.
72
+
3
73
  ## 0.48.0
4
74
 
5
75
  **Active, Waiting and Done.** A review can now be marked **Waiting**, for when you have done your
@@ -1876,6 +1876,11 @@ var AmbiguousRefError = class extends Error {
1876
1876
  divergence;
1877
1877
  /** Whether the name was named by the reviewer or discovered as the default. Changes the wording. */
1878
1878
  discovered;
1879
+ /**
1880
+ * What a review against each of the two would contain. Absent until `withReviewRanges` adds it,
1881
+ * which needs the head being reviewed and so cannot happen where the name is resolved.
1882
+ */
1883
+ ranges;
1879
1884
  constructor(detail) {
1880
1885
  super(describeAmbiguity(detail));
1881
1886
  this.name = "AmbiguousRefError";
@@ -1884,9 +1889,49 @@ var AmbiguousRefError = class extends Error {
1884
1889
  this.remote = detail.remote;
1885
1890
  if (detail.divergence) this.divergence = detail.divergence;
1886
1891
  this.discovered = detail.discovered;
1892
+ if (detail.ranges) this.ranges = detail.ranges;
1887
1893
  }
1888
1894
  };
1889
1895
  /**
1896
+ * The same refusal, now saying what choosing each ref would review.
1897
+ *
1898
+ * Counted from the split point, `git merge-base <ref> <head>`, and not as `<ref>..<head>`: the
1899
+ * review diffs from the merge-base, so this has to walk the same boundary or the refusal and the
1900
+ * review it leads to could disagree about which commits are in it. Added after resolution because
1901
+ * the resolver is shared with the review's own target, which has no head to measure against.
1902
+ */
1903
+ async function withReviewRanges(error, repoPath, head) {
1904
+ const [local, remote] = await Promise.all([reviewedCommits(repoPath, error.local.ref, head), reviewedCommits(repoPath, error.remote.ref, head)]);
1905
+ return new AmbiguousRefError({
1906
+ requested: error.requested,
1907
+ local: error.local,
1908
+ remote: error.remote,
1909
+ ...error.divergence ? { divergence: error.divergence } : {},
1910
+ discovered: error.discovered,
1911
+ ranges: {
1912
+ ...local ? { local } : {},
1913
+ ...remote ? { remote } : {}
1914
+ }
1915
+ });
1916
+ }
1917
+ async function reviewedCommits(repoPath, ref, head) {
1918
+ try {
1919
+ const split = (await git(repoPath, [
1920
+ "merge-base",
1921
+ ref,
1922
+ head
1923
+ ])).trim();
1924
+ if (!split) return void 0;
1925
+ return (await git(repoPath, [
1926
+ "rev-list",
1927
+ "--abbrev-commit",
1928
+ `${split}..${head}`
1929
+ ])).split("\n").filter(Boolean);
1930
+ } catch {
1931
+ return;
1932
+ }
1933
+ }
1934
+ /**
1890
1935
  * `refs/heads/feat/foo` and `refs/remotes/origin/feat/foo` are both `feat/foo`.
1891
1936
  *
1892
1937
  * Structural rather than asked of git: the first path segment under `refs/remotes/` is the
@@ -1996,10 +2041,19 @@ function describeAmbiguity(detail) {
1996
2041
  ...refs.map((one) => ` ${one.ref.padEnd(width)} ${one.sha.slice(0, 7)}`),
1997
2042
  ...detail.divergence ? ["", divergenceLine(name, shorthandOf(detail.remote.ref), detail.divergence)] : [],
1998
2043
  "",
2044
+ ...detail.ranges ? [SPLIT_POINT, ""] : [],
1999
2045
  "Name the one you mean:",
2000
- ...refs.map((one) => ` prreviewbuddy review --base ${shorthandOf(one.ref)}`)
2046
+ ...[[detail.local, detail.ranges?.local], [detail.remote, detail.ranges?.remote]].flatMap(([one, commits]) => [` prreviewbuddy review --base ${shorthandOf(one.ref)}`, ...commits ? [` ${rangeLine(commits)}`] : []])
2001
2047
  ].join("\n");
2002
2048
  }
2049
+ var SPLIT_POINT = "The base determines the split point. PR Review Buddy reviews the changes on your branch since that point.";
2050
+ /** "changes across", because a review is one combined diff and not a review of each commit. */
2051
+ function rangeLine(commits) {
2052
+ if (commits.length === 0) return "no commits: nothing on your branch is new against this base";
2053
+ const listed = commits.slice(0, 10).join(" ");
2054
+ const more = commits.length > 10 ? ` and ${commits.length - 10} more` : "";
2055
+ return `changes across ${commits.length} commit${commits.length === 1 ? "" : "s"}: ${listed}${more}`;
2056
+ }
2003
2057
  /**
2004
2058
  * How a person spells this ref: the shortest form that still says which one.
2005
2059
  *
@@ -2176,7 +2230,7 @@ async function buildChangeSet(options) {
2176
2230
  disagreement(files.length, base.display, options.expected);
2177
2231
  const blobShas = await headBlobShas(repoPath);
2178
2232
  for (const file of files) file.sha = blobShas.get(file.name) ?? "";
2179
- const { subjects, authors } = await commitLog(repoPath, base.exact && !await refExists(repoPath, mergeBase) ? "HEAD" : `${mergeBase}..HEAD`);
2233
+ const { subjects, authors, commits } = await commitLog(repoPath, base.exact && !await refExists(repoPath, mergeBase) ? "HEAD" : `${mergeBase}..HEAD`);
2180
2234
  return {
2181
2235
  data: {
2182
2236
  pullRequest: {
@@ -2205,7 +2259,8 @@ async function buildChangeSet(options) {
2205
2259
  includesUncommitted: false,
2206
2260
  isLinkedWorktree,
2207
2261
  checkoutName: basename(repoPath),
2208
- authors
2262
+ authors,
2263
+ commits
2209
2264
  };
2210
2265
  }
2211
2266
  /**
@@ -2316,21 +2371,22 @@ async function commitLog(repoPath, range) {
2316
2371
  const out = await git(repoPath, [
2317
2372
  "log",
2318
2373
  "--reverse",
2319
- "--format=%an%x1f%s",
2374
+ "--format=%H%x1f%an%x1f%s",
2320
2375
  range
2321
2376
  ]);
2322
2377
  const subjects = [];
2323
2378
  const counts = /* @__PURE__ */ new Map();
2379
+ const commits = [];
2324
2380
  for (const line of out.split("\n")) {
2325
- const separator = line.indexOf("");
2326
- if (separator === -1) continue;
2327
- const name = line.slice(0, separator).trim();
2328
- const subject = line.slice(separator + 1).trim();
2329
- if (subject) subjects.push(subject);
2330
- if (name) counts.set(name, (counts.get(name) ?? 0) + 1);
2381
+ const [sha, name, subject] = line.split("");
2382
+ if (!sha || name === void 0) continue;
2383
+ commits.push(sha.trim());
2384
+ if (subject?.trim()) subjects.push(subject.trim());
2385
+ if (name.trim()) counts.set(name.trim(), (counts.get(name.trim()) ?? 0) + 1);
2331
2386
  }
2332
2387
  return {
2333
2388
  subjects,
2389
+ commits: commits.reverse(),
2334
2390
  authors: [...counts].map(([name, commits]) => ({
2335
2391
  name,
2336
2392
  commits
@@ -2746,8 +2802,14 @@ async function peel(repoPath, named) {
2746
2802
  async function resolveTarget(input) {
2747
2803
  const root = await repositoryRoot(input.repoPath);
2748
2804
  if (input.commitRef) return commitTarget(root, input.commitRef);
2749
- if (input.base) await verifyBase(root, input.base);
2750
- else await verifyDefaultBase(root);
2805
+ try {
2806
+ if (input.base) await verifyBase(root, input.base);
2807
+ else await verifyDefaultBase(root);
2808
+ } catch (error) {
2809
+ if (!(error instanceof AmbiguousRefError)) throw error;
2810
+ const head = await headToMeasure(root, input);
2811
+ throw head ? await withReviewRanges(error, root, head) : error;
2812
+ }
2751
2813
  const named = input.base ? { base: input.base } : {};
2752
2814
  if (input.prUrl) {
2753
2815
  if (!input.resolveRequest) throw new TargetError(`${input.prUrl} is a pull request, but nothing here can look one up. Check the request out locally and review the branch instead.`);
@@ -2896,6 +2958,23 @@ async function verifyBase(root, base) {
2896
2958
  throw new TargetError(`There is nothing called \`${base}\` in this repository to compare against. Tried ${candidateRefs(base).map((one) => `\`${one}\``).join(" and ")}. Run \`git fetch\` if it is on the remote and this clone has not seen it yet.`);
2897
2959
  }
2898
2960
  /**
2961
+ * The commit the review would be of, as far as it is known before the target is resolved.
2962
+ *
2963
+ * A pull request's head is not known until the forge is asked, and the refusal exists to come
2964
+ * before that, so it gets none and its refusal lists no ranges. A named branch is resolved rather
2965
+ * than handed to git as typed, because a name that is itself ambiguous would be settled by git's own
2966
+ * preference, which is the guess this module refuses to make.
2967
+ */
2968
+ async function headToMeasure(root, input) {
2969
+ if (input.prUrl) return void 0;
2970
+ if (!input.branchRef) return "HEAD";
2971
+ try {
2972
+ return (await resolveBranchRef(root, input.branchRef))?.sha;
2973
+ } catch {
2974
+ return;
2975
+ }
2976
+ }
2977
+ /**
2899
2978
  * The same check for the base nobody named, which is the one the reported failure came down.
2900
2979
  *
2901
2980
  * A bare `prreviewbuddy review` has no base to verify, so this ran nothing and the refusal landed
@@ -4089,19 +4168,6 @@ function queueRemove(id) {
4089
4168
  return updateQueue((current) => current.includes(id) ? current.filter((queued) => queued !== id) : null);
4090
4169
  }
4091
4170
  /**
4092
- * The invariant, as one named thing: the queue holds unfinished work only.
4093
- *
4094
- * Removing on completion is half of it. The other half is that this **does nothing** when
4095
- * `complete` is false, and that half only exists as a written rule -- there is no line of code for
4096
- * it to be visible in. Re-adding there would make membership something inferred from status rather
4097
- * than chosen, so a reviewer who took a review out of the queue and later moved it back to In
4098
- * progress would find it queued again with no idea why.
4099
- */
4100
- function dequeueOnComplete(id, complete) {
4101
- if (!complete) return;
4102
- queueRemove(id);
4103
- }
4104
- /**
4105
4171
  * A token for the index's revision, equality only.
4106
4172
  *
4107
4173
  * The queue changes only when a person deliberately changes it, so unlike a running job's progress
@@ -4143,14 +4209,17 @@ function normalise(ids) {
4143
4209
  var LIFECYCLES = [
4144
4210
  {
4145
4211
  id: "active",
4212
+ name: "Active",
4146
4213
  action: "Move to active"
4147
4214
  },
4148
4215
  {
4149
4216
  id: "waiting",
4217
+ name: "Waiting",
4150
4218
  action: "Mark waiting"
4151
4219
  },
4152
4220
  {
4153
4221
  id: "done",
4222
+ name: "Done",
4154
4223
  action: "Mark done"
4155
4224
  }
4156
4225
  ];
@@ -4170,6 +4239,23 @@ function parseLifecycle(raw) {
4170
4239
  function lifecycleAction(lifecycle) {
4171
4240
  return LIFECYCLES.find((entry) => entry.id === lifecycle).action;
4172
4241
  }
4242
+ /**
4243
+ * The review page's lifecycle pill, or absent for Active, which says nothing (as an open pull
4244
+ * request says nothing). A parked review that has changed since reads as the card's chip does, so
4245
+ * the page and the list give one answer about the same review.
4246
+ */
4247
+ function lifecyclePill(lifecycle, activity) {
4248
+ if (!lifecycle) return void 0;
4249
+ const chip = lifecycleChip(lifecycle, activity);
4250
+ if (chip !== void 0) return {
4251
+ text: chip,
4252
+ changed: true
4253
+ };
4254
+ return {
4255
+ text: LIFECYCLES.find((entry) => entry.id === lifecycle.state).name,
4256
+ changed: false
4257
+ };
4258
+ }
4173
4259
  var ACTIVITY_LABELS = {
4174
4260
  created: "Created",
4175
4261
  updated: "Updated",
@@ -4658,9 +4744,8 @@ function groupByLineage(reviews) {
4658
4744
  * on an old review is "then what is the current one", and answering it by sending them to the
4659
4745
  * index costs them the page they were reading. See `RelatedReview` for what may travel and why.
4660
4746
  */
4661
- function lineagePosition(workspace) {
4747
+ function lineagePosition(workspace, all = recentWorkspaces(Number.MAX_SAFE_INTEGER)) {
4662
4748
  const review = summarise(workspace);
4663
- const all = recentWorkspaces(Number.MAX_SAFE_INTEGER);
4664
4749
  return {
4665
4750
  ...positionInLineage(review, all),
4666
4751
  reviews: relatedReviews(review, all)
@@ -7650,9 +7735,10 @@ function compactQueue(stored) {
7650
7735
  /**
7651
7736
  * Put one review on the queue, the way the index's Add does, and say how that went.
7652
7737
  *
7653
- * **A completed review is left out**, not queued and not reopened. The queue holds unfinished work
7654
- * only -- `dequeueOnComplete` is the other half of that rule -- and quietly moving somebody's review
7655
- * back to In progress because a command mentioned it is a status change nobody asked for.
7738
+ * **A Reviewed review is queued like any other.** Reviewed says what the reviewer has done with it,
7739
+ * not that the work is over; that is Done. A Waiting or Done review that goes in is put back in
7740
+ * Active, as it is from the page (`activateQueued`), because asking for it in the queue is asking
7741
+ * to work on it again.
7656
7742
  *
7657
7743
  * Already queued is `already`, not a move: the same rule `queueAdd` keeps, for the same reason.
7658
7744
  *
@@ -7660,11 +7746,9 @@ function compactQueue(stored) {
7660
7746
  * was queued would otherwise hold a place, and a live review could be refused as "full" by it.
7661
7747
  */
7662
7748
  function queueForReview(id) {
7663
- const workspace = loadWorkspace(id);
7664
- if (!workspace) return "gone";
7665
- if (workspace.completedAt !== void 0) return "complete";
7749
+ if (!loadWorkspace(id)) return "gone";
7666
7750
  let result = "added";
7667
- return updateQueue((current) => {
7751
+ const stored = updateQueue((current) => {
7668
7752
  const live = current.filter(isLive);
7669
7753
  const unchanged = live.length === current.length ? null : live;
7670
7754
  if (live.includes(id)) {
@@ -7676,7 +7760,29 @@ function queueForReview(id) {
7676
7760
  return unchanged;
7677
7761
  }
7678
7762
  return [...live, id];
7679
- }) === null ? "unsaved" : result;
7763
+ });
7764
+ if (stored === null) return "unsaved";
7765
+ if (result === "added") activateQueued([id], stored);
7766
+ return result;
7767
+ }
7768
+ /**
7769
+ * Queueing a review puts it back in Active.
7770
+ *
7771
+ * The queue is work the reviewer wants surfaced next, and Waiting and Done both say the opposite; a
7772
+ * queued review folded away in Done would be a contradiction the page then has to draw. Parking
7773
+ * takes a review out of the queue, and this is the same rule read the other way. Only what actually
7774
+ * went in: a review refused because the queue was full keeps the lifecycle it had.
7775
+ *
7776
+ * Every way in calls this: a card, the bulk bar, the review's own page and `--queue`.
7777
+ */
7778
+ function activateQueued(ids, queue) {
7779
+ const queued = new Set(queue);
7780
+ for (const one of ids) {
7781
+ if (!queued.has(one) || loadWorkspace(one)?.lifecycle === void 0) continue;
7782
+ mutateWorkspace(one, (current) => {
7783
+ current.lifecycle = void 0;
7784
+ });
7785
+ }
7680
7786
  }
7681
7787
  function isLive(id) {
7682
7788
  return loadWorkspace(id) !== null;
@@ -12775,4 +12881,4 @@ function lineageIds(id) {
12775
12881
  return [.../* @__PURE__ */ new Set([id, ...lineage.map((review) => review.id)])];
12776
12882
  }
12777
12883
  //#endregion
12778
- export { queueForReview as $, mutateWorkspace as $t, createPrState as A, describeAuthorship as An, allJobs as At, toneOf as B, CONFIG_PATH as Bn, purposeOf as Bt, readEvents as C, doneVerb as Cn, forgeFor as Ct, announceJob as D, questionsOutstanding as Dn, checkReview as Dt, telemetryUploadConsent as E, issuesOutstanding as En, checkCodeFreshness as Et, markAllRead as F, resolveAgent as Fn, saveJob as Ft, extractJsonText as G, agentFor as Gn, groupByLineage as Gt, withUsageRecorded as H, AGENT_IDS as Hn, driverGone as Ht, markRead as I, resolveModel as In, PHASES as It, readMarker as J, AgentUnavailableError as Jn, latestKissRun as Jt, ReviewBeingDeletedError as K, detectAgents as Kn, indexKeptIds as Kt, pruneNotifications as L, clearAgentPreference as Ln, isLiveJob as Lt, eventName as M, displayRef as Mn, fail as Mt, headline as N, isCommitReview as Nn, isTerminal as Nt, branchOf as O, reviewerDispositions as On, followedBranch as Ot, listNotifications as P, git as Pn, loadJob as Pt, compactQueue as Q, matchingWorkspaceIds as Qt, readNotification as R, readAgentPreference as Rn, phasesFor as Rt, startJob as S, readQueue as Sn, forgeResolver as St, recordTelemetryUploadConsent as T, isQuestionOutstanding as Tn, fillFileUrlTemplate as Tt, EXPLAIN_SIMPLY_PROMPT as U, DEFAULT_AGENT_ID as Un, isClaimed as Ut, unreadCount as V, STORE_ROOT as Vn, clearClaim as Vt, asString as W, agentById as Wn, followedRefName as Wt, modelHelpLines as X, lineagePosition as Xt, removeWorktree as Y, lineageKeyFor as Yt, relativeTime as Z, loadWorkspace as Zt, agentEnvOf as _, dequeueOnComplete as _n, parseWorkspacePort as _t, wasBlocked as a, summarise as an, reviewsUrl as at, reanalyseReview as b, queueRemove as bn, writePortPreference as bt, RefreshUnavailableError as c, workspaceStamps as cn, stopServer as ct, refreshPrContext as d, isLifecycle as dn, BUILD_VERSION as dt, positionInLineage as en, readIndexToken as et, isUnchanged as f, lifecycleAction as fn, PACKAGE_NAME as ft, runningJobs as g, sectionOf as gn, MIN_WORKSPACE_PORT as gt, liveUpdateFor as h, parseLifecycle as hn, MAX_WORKSPACE_PORT as ht, liveJobsFor as i, reviewedRepositories as in, readServerRecord as it, createPrUpdate as j, resolveTarget as jn, endedAt as jt, subjectOf as k, processDiscussion as kn, reviewedShaOf as kt, mergeUpdateInto as l, LIFECYCLES as ln, workspaceUrl as lt, UpdateAlreadyRunningError as m, offersLifecycle as mn, DEFAULT_WORKSPACE_PORT as mt, deleteReview as n, recentWorkspaces as nn, clearServerRecord as nt, discardJob as o, touchWorkspace as on, serverEntryExists as ot, askCheckout as p, lifecycleChip as pn, feedbackUrl as pt, MANAGED_ROOT as q, AgentCancelledError as qn, indexReviewGroups as qt, lineageIds as r, reviewedCommit as rn, ensureServer as rt, FETCH_TIMEOUT_MS as s, workspaceRevision as sn, spawnServer as st, deleteLineage as t, previousKissResult as tn, bootstrapUrl as tt, updateReview as u, activityLabel as un, writeServerRecord as ut, recordKissRun as v, queueAdd as vn, resolveWorkspacePort as vt, record as w, isIssueOutstanding as wn, remoteHost as wt, runJob as x, queueStamp as xn, claimRequestUrl as xt, startKissJob as y, queueAddMany as yn, statedWorkspacePort as yt, removeNotification as z, writeAgentPreference as zn, progressSteps as zt };
12884
+ export { compactQueue as $, matchingWorkspaceIds as $t, createPrState as A, processDiscussion as An, reviewedShaOf as At, toneOf as B, writeAgentPreference as Bn, progressSteps as Bt, readEvents as C, readQueue as Cn, forgeResolver as Ct, announceJob as D, issuesOutstanding as Dn, checkCodeFreshness as Dt, telemetryUploadConsent as E, isQuestionOutstanding as En, fillFileUrlTemplate as Et, markAllRead as F, git as Fn, loadJob as Ft, extractJsonText as G, agentById as Gn, followedRefName as Gt, withUsageRecorded as H, STORE_ROOT as Hn, clearClaim as Ht, markRead as I, resolveAgent as In, saveJob as It, readMarker as J, AgentCancelledError as Jn, indexReviewGroups as Jt, ReviewBeingDeletedError as K, agentFor as Kn, groupByLineage as Kt, pruneNotifications as L, resolveModel as Ln, PHASES as Lt, eventName as M, resolveTarget as Mn, endedAt as Mt, headline as N, displayRef as Nn, fail as Nt, branchOf as O, questionsOutstanding as On, checkReview as Ot, listNotifications as P, isCommitReview as Pn, isTerminal as Pt, activateQueued as Q, loadWorkspace as Qt, readNotification as R, clearAgentPreference as Rn, isLiveJob as Rt, startJob as S, queueStamp as Sn, claimRequestUrl as St, recordTelemetryUploadConsent as T, isIssueOutstanding as Tn, remoteHost as Tt, EXPLAIN_SIMPLY_PROMPT as U, AGENT_IDS as Un, driverGone as Ut, unreadCount as V, CONFIG_PATH as Vn, purposeOf as Vt, asString as W, DEFAULT_AGENT_ID as Wn, isClaimed as Wt, modelHelpLines as X, lineageKeyFor as Xt, removeWorktree as Y, AgentUnavailableError as Yn, latestKissRun as Yt, relativeTime as Z, lineagePosition as Zt, agentEnvOf as _, parseLifecycle as _n, MIN_WORKSPACE_PORT as _t, wasBlocked as a, reviewedRepositories as an, readServerRecord as at, reanalyseReview as b, queueAddMany as bn, statedWorkspacePort as bt, RefreshUnavailableError as c, workspaceRevision as cn, spawnServer as ct, refreshPrContext as d, activityLabel as dn, writeServerRecord as dt, mutateWorkspace as en, queueForReview as et, isUnchanged as f, isLifecycle as fn, BUILD_VERSION as ft, runningJobs as g, offersLifecycle as gn, MAX_WORKSPACE_PORT as gt, liveUpdateFor as h, lifecyclePill as hn, DEFAULT_WORKSPACE_PORT as ht, liveJobsFor as i, reviewedCommit as in, ensureServer as it, createPrUpdate as j, describeAuthorship as jn, allJobs as jt, subjectOf as k, reviewerDispositions as kn, followedBranch as kt, mergeUpdateInto as l, workspaceStamps as ln, stopServer as lt, UpdateAlreadyRunningError as m, lifecycleChip as mn, feedbackUrl as mt, deleteReview as n, previousKissResult as nn, bootstrapUrl as nt, discardJob as o, summarise as on, reviewsUrl as ot, askCheckout as p, lifecycleAction as pn, PACKAGE_NAME as pt, MANAGED_ROOT as q, detectAgents as qn, indexKeptIds as qt, lineageIds as r, recentWorkspaces as rn, clearServerRecord as rt, FETCH_TIMEOUT_MS as s, touchWorkspace as sn, serverEntryExists as st, deleteLineage as t, positionInLineage as tn, readIndexToken as tt, updateReview as u, LIFECYCLES as un, workspaceUrl as ut, recordKissRun as v, sectionOf as vn, parseWorkspacePort as vt, record as w, doneVerb as wn, forgeFor as wt, runJob as x, queueRemove as xn, writePortPreference as xt, startKissJob as y, queueAdd as yn, resolveWorkspacePort as yt, removeNotification as z, readAgentPreference as zn, phasesFor as zt };
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $ as queueForReview, $t as mutateWorkspace, At as allJobs, Bn as CONFIG_PATH, Bt as purposeOf, Dt as checkReview, E as telemetryUploadConsent, Fn as resolveAgent, Gt as groupByLineage, Hn as AGENT_IDS, Ht as driverGone, In as resolveModel, It as PHASES, J as readMarker, Kn as detectAgents, Mn as displayRef, Nt as isTerminal, Pn as git, Pt as loadJob, Qt as matchingWorkspaceIds, Rn as readAgentPreference, S as startJob, St as forgeResolver, T as recordTelemetryUploadConsent, Un as DEFAULT_AGENT_ID, Ut as isClaimed, Vn as STORE_ROOT, Vt as clearClaim, Wn as agentById, Wt as followedRefName, X as modelHelpLines, Y as removeWorktree, Yt as lineageKeyFor, Z as relativeTime, Zt as loadWorkspace, _t as parseWorkspacePort, a as wasBlocked, an as summarise, at as reviewsUrl, b as reanalyseReview, bt as writePortPreference, ct as stopServer, dt as BUILD_VERSION, en as positionInLineage, et as readIndexToken, ft as PACKAGE_NAME, g as runningJobs, gt as MIN_WORKSPACE_PORT, h as liveUpdateFor, ht as MAX_WORKSPACE_PORT, i as liveJobsFor, in as reviewedRepositories, jn as resolveTarget, l as mergeUpdateInto, lt as workspaceUrl, m as UpdateAlreadyRunningError, mt as DEFAULT_WORKSPACE_PORT, n as deleteReview$1, nn as recentWorkspaces, q as MANAGED_ROOT, rt as ensureServer, tt as bootstrapUrl, u as updateReview, v as recordKissRun, w as record$1, x as runJob, yt as statedWorkspacePort, zn as writeAgentPreference, zt as progressSteps } from "./delete_review-D52wkt66.js";
2
+ import { $t as matchingWorkspaceIds, Bn as writeAgentPreference, Bt as progressSteps, Ct as forgeResolver, E as telemetryUploadConsent, Fn as git, Ft as loadJob, Gn as agentById, Gt as followedRefName, Hn as STORE_ROOT, Ht as clearClaim, In as resolveAgent, J as readMarker, Kt as groupByLineage, Ln as resolveModel, Lt as PHASES, Mn as resolveTarget, Nn as displayRef, Ot as checkReview, Pt as isTerminal, Qt as loadWorkspace, S as startJob, T as recordTelemetryUploadConsent, Un as AGENT_IDS, Ut as driverGone, Vn as CONFIG_PATH, Vt as purposeOf, Wn as DEFAULT_AGENT_ID, Wt as isClaimed, X as modelHelpLines, Xt as lineageKeyFor, Y as removeWorktree, Z as relativeTime, _t as MIN_WORKSPACE_PORT, a as wasBlocked, an as reviewedRepositories, b as reanalyseReview, bt as statedWorkspacePort, en as mutateWorkspace, et as queueForReview, ft as BUILD_VERSION, g as runningJobs, gt as MAX_WORKSPACE_PORT, h as liveUpdateFor, ht as DEFAULT_WORKSPACE_PORT, i as liveJobsFor, it as ensureServer, jt as allJobs, l as mergeUpdateInto, lt as stopServer, m as UpdateAlreadyRunningError, n as deleteReview$1, nt as bootstrapUrl, on as summarise, ot as reviewsUrl, pt as PACKAGE_NAME, q as MANAGED_ROOT, qn as detectAgents, rn as recentWorkspaces, tn as positionInLineage, tt as readIndexToken, u as updateReview, ut as workspaceUrl, v as recordKissRun, vt as parseWorkspacePort, w as record$1, x as runJob, xt as writePortPreference, zn as readAgentPreference } from "./delete_review-BIXqUnpx.js";
3
3
  import { basename, dirname, join, resolve } from "node:path";
4
4
  import { spawn } from "node:child_process";
5
5
  import { existsSync, mkdirSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
@@ -115,6 +115,7 @@ function parse(argv) {
115
115
  name: "error",
116
116
  message: unknownCommand(head)
117
117
  };
118
+ if (rest.includes("--help") || rest.includes("-h")) return { name: "help" };
118
119
  const values = {};
119
120
  const flags = /* @__PURE__ */ new Set();
120
121
  const positional = [];
@@ -1064,6 +1065,45 @@ running the same command again carries each one on from where it got to.
1064
1065
 
1065
1066
  Reviews can also be deleted from the list in your browser, one at a time or a whole branch at once.
1066
1067
 
1068
+ What gets reviewed
1069
+
1070
+ A review is the combined change between a base and the branch you review, as one diff. It is not
1071
+ just the latest commit. The base determines the split point (git's merge-base of the base and your
1072
+ branch), and PR Review Buddy reviews the changes on your branch since that point.
1073
+
1074
+ main: A---B---C
1075
+ \\
1076
+ feature: D---E
1077
+
1078
+ prreviewbuddy review --base main reviews D and E together
1079
+
1080
+ C is on main but not on your branch, so it is not part of the review, and the review does not show
1081
+ its changes as removed. Commits already in the base are never reviewed again.
1082
+
1083
+ When your local main and origin/main are different commits, which one you name moves the split
1084
+ point:
1085
+
1086
+ origin/main: A---B
1087
+ \\
1088
+ local main: C---D
1089
+ \\
1090
+ feature: E
1091
+
1092
+ --base refs/heads/main reviews E
1093
+ --base origin/main reviews C, D and E together
1094
+
1095
+ Without --base, the repository's default branch is the base. If its local and remote copies are
1096
+ different commits, PR Review Buddy asks which one you mean and says what each would review.
1097
+ Uncommitted changes are never included: a review is a fixed snapshot of your commits.
1098
+
1099
+ Common examples
1100
+
1101
+ prreviewbuddy review --base refs/heads/main
1102
+ everything on this branch since it left your local main
1103
+ prreviewbuddy review --base origin/main
1104
+ everything on this branch not yet on origin/main
1105
+ prreviewbuddy review --commit <commit> one commit, against its parent
1106
+
1067
1107
  There is no account and no API key. PR Review Buddy uses a coding agent installed on your machine
1068
1108
  and that agent's existing authentication, and nothing here ever asks you for a credential.
1069
1109
 
@@ -1455,7 +1495,6 @@ function batchQueueLines(rows) {
1455
1495
  }
1456
1496
  function notQueuedBecause(subject, result) {
1457
1497
  switch (result) {
1458
- case "complete": return `${subject} is marked complete. Move it back to In progress in the workspace to queue it.`;
1459
1498
  case "full": return `${subject}, because the review queue is full. It holds 50 reviews at most.`;
1460
1499
  default: return `${subject}, because the review queue could not be saved.`;
1461
1500
  }
@@ -1468,7 +1507,6 @@ function reviewQueued(result) {
1468
1507
  switch (result) {
1469
1508
  case "added": return "Added to review queue.";
1470
1509
  case "already": return "Already in your review queue.";
1471
- case "complete": return "Not added to your review queue: this review is marked complete. Move it back to In progress in the workspace to queue it.";
1472
1510
  case "full": return `Not added to your review queue: it is full, and holds 50 reviews at most.`;
1473
1511
  default: return "Not added to your review queue: the queue could not be saved.";
1474
1512
  }