prreviewbuddy 0.39.3 → 0.42.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,67 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.42.0
4
+
5
+ **A review page no longer says "Review up to date" when it isn't.** The status beside **Check for
6
+ updates** used to be worked out once, when the page opened. It now checks again on its own when you
7
+ come back to the tab and once a minute while you're looking at it, so a commit to the branch you're
8
+ reviewing shows as **Code changed** without pressing anything. These checks never contact GitHub and
9
+ are silent: nothing flashes, and a warning you dismissed stays dismissed until something newer
10
+ arrives.
11
+
12
+ **Pull request pushes show on the page, not just in notifications.** The workspace server already
13
+ checked your pull requests on GitHub every few minutes and told you when one moved. The page itself
14
+ could only see commits your repository had fetched, so it could say "up to date", and **Check for
15
+ updates** could say **No change**, right after a **PR updated** notification. The page now shows
16
+ **PR updated** as soon as the server has seen the push, with **Update review** beside it, whether or
17
+ not anything has been fetched. The server watches the pull requests of reviews that are queued,
18
+ saved, or not yet marked reviewed, so those are the ones this applies to. Once **Update review** finishes, the page and the card on the
19
+ Reviews page stop saying the pull request moved straight away; an unread **PR updated** notification
20
+ clears at the next check a few minutes later, as before. If the author pushes again while the update
21
+ is running, the page says so rather than hiding the newer push.
22
+
23
+ ## 0.41.0
24
+
25
+ **Your place is kept when a page reloads itself.** The Reviews page reloads whenever something on it
26
+ changes, such as a review finishing or the review queue changing in another tab, and a review
27
+ reloads when its analysis produces something new. Both used to jump back to the top. They now come
28
+ back where you were. Opening a page from a link still starts at the top, and each view of a review
29
+ and each filter of the list keeps its own place.
30
+
31
+ **Add a selection to the review queue.** In selection mode, the bar has **Add to review queue**,
32
+ which adds every ticked review in the order the list shows them. Reviews already queued keep their
33
+ place. The queue holds fifty reviews; anything that does not fit stays ticked, and the bar says why.
34
+ Reviews deleted since they were queued no longer take up any of those fifty places.
35
+
36
+ **Save a review from its own page.** The review page's **...** menu has **Save review**, which does
37
+ what the star on the Reviews page does. When the review is saved it reads **Remove from saved**.
38
+
39
+ **Copy an issue or a question, ready to post.** Every issue and question has a **Copy** button that
40
+ puts it on the clipboard as Markdown. An issue copies its title in bold, its description, then the
41
+ file and line. A question copies the question in bold, why it matters, then the file. Both list any
42
+ unchanged files they rely on. The severity, confidence and theme labels are left out, so it pastes
43
+ straight into a pull request comment.
44
+
45
+ ## 0.40.0
46
+
47
+ **See which reviewed pull requests have merged.** The workspace server's regular pull request check
48
+ now also reads whether each pull request is open, merged or closed, from GitHub, in the same request
49
+ it already made. A card whose pull request has merged says **Merged** beside its status, and one
50
+ closed without merging says **PR closed**. Neither changes the review's own status: a review stays
51
+ **In progress** until you mark it reviewed, and a reviewed card keeps its **Merged** badge. When a
52
+ pull request you are watching merges or closes you get one notification, **PR merged** or **PR
53
+ closed**, and never a repeat. Pull requests that had already merged before this version are marked
54
+ quietly, without a notification each. A merged pull request is not checked again.
55
+
56
+ **A PR filter on the Reviews page.** Beside **Status**, **PR** narrows the list to pull requests that
57
+ are **Open**, **Merged** or **Closed**. It combines with Status, so **In progress** with **Merged**
58
+ lists the merged reviews you have not finished. Reviews with no pull request only appear under
59
+ **Any**.
60
+
61
+ **Select all in selection mode.** The bar at the bottom has **Select all**, which ticks every review
62
+ the search and filters are showing, on every page. To clear out merged reviews: set PR to Merged,
63
+ press Select, **Select all**, then **Mark reviewed**.
64
+
3
65
  ## 0.39.3
4
66
 
5
67
  **Reviews of a branch now notice when the branch moves, not only reviews of a pull request.** The
@@ -3885,6 +3885,29 @@ function queueAdd(id) {
3885
3885
  if (current.includes(id)) return current;
3886
3886
  return writeQueue([...current, id]);
3887
3887
  }
3888
+ /**
3889
+ * Append several, in the order given, for the reviews index's bulk bar.
3890
+ *
3891
+ * The same rules as `queueAdd` for each one, in one write. The cap is where this differs: one Add
3892
+ * at a time never meets it in practice, but a selection can hold a hundred cards, and `normalise`
3893
+ * would drop everything past fifty without a word. So what does not fit is left out *here* and
3894
+ * named in `full`, and the caller has something true to say about it.
3895
+ */
3896
+ function queueAddMany(ids) {
3897
+ const current = readQueue();
3898
+ const fresh = ids.filter((id, at) => !current.includes(id) && ids.indexOf(id) === at);
3899
+ const room = Math.max(0, 50 - current.length);
3900
+ const full = fresh.slice(room);
3901
+ if (fresh.length <= full.length) return {
3902
+ queue: current,
3903
+ full
3904
+ };
3905
+ const queue = writeQueue([...current, ...fresh.slice(0, room)]);
3906
+ return queue === null ? null : {
3907
+ queue,
3908
+ full
3909
+ };
3910
+ }
3888
3911
  function queueRemove(id) {
3889
3912
  const current = readQueue();
3890
3913
  if (!current.includes(id)) return current;
@@ -6475,14 +6498,22 @@ function identity(raw) {
6475
6498
  };
6476
6499
  }
6477
6500
  /**
6478
- * The watcher's query: one field.
6501
+ * The watcher's query: where the branch is, and whether the request is still open.
6479
6502
  *
6480
6503
  * Deliberately not `PR_FIELDS`. That list exists because a review needs all nine of them, and its
6481
6504
  * docblock warns that trimming it fails silently rather than loudly. Sharing it here would put a
6482
6505
  * nine-field query on a five-minute timer across the whole working set, and would put the watcher
6483
6506
  * in a position to "optimise" a list that must never shrink.
6507
+ *
6508
+ * `state` rides on the same call rather than being a second one, which keeps the watcher's rate
6509
+ * limit arithmetic in `MAX_WATCHED` true as written.
6484
6510
  */
6485
- var HEAD_FIELDS = "headRefOid";
6511
+ var HEAD_FIELDS = "headRefOid,state";
6512
+ var STATES = /* @__PURE__ */ new Map([
6513
+ ["OPEN", "open"],
6514
+ ["MERGED", "merged"],
6515
+ ["CLOSED", "closed"]
6516
+ ]);
6486
6517
  /**
6487
6518
  * The head sha out of `gh`'s answer, or a refusal.
6488
6519
  *
@@ -6501,7 +6532,8 @@ function readHeadSha(raw) {
6501
6532
  };
6502
6533
  return {
6503
6534
  kind: "head",
6504
- sha
6535
+ sha,
6536
+ state: STATES.get(text(raw?.state)) ?? null
6505
6537
  };
6506
6538
  }
6507
6539
  async function headOf(repo, number, env, cwd) {
@@ -9805,6 +9837,7 @@ var OPERATION_NAMES = {
9805
9837
  /** What happened, in the words the list uses: `Review ready`, `Review update failed`, `PR updated`. */
9806
9838
  function eventName(notification) {
9807
9839
  if (notification.kind === "pr-updated") return notification.source === "branch" ? "Branch updated" : "PR updated";
9840
+ if (notification.kind === "pr-state") return notification.state === "merged" ? "PR merged" : "PR closed";
9808
9841
  return `${OPERATION_NAMES[notification.operation]} ${notification.outcome}`;
9809
9842
  }
9810
9843
  /**
@@ -9825,7 +9858,7 @@ function headline(notification) {
9825
9858
  * so adding the next kind is a compiler error here instead of a silently untinted toast there.
9826
9859
  */
9827
9860
  function toneOf(notification) {
9828
- return notification.kind === "pr-updated" ? "updated" : notification.outcome;
9861
+ return notification.kind === "analysis" ? notification.outcome : "updated";
9829
9862
  }
9830
9863
  var OPERATIONS = [
9831
9864
  "review",
@@ -9867,6 +9900,18 @@ function createPrUpdate(input, now = Date.now()) {
9867
9900
  createdAt: now
9868
9901
  });
9869
9902
  }
9903
+ /**
9904
+ * Record that a watched pull request merged or closed, or return null because this is already
9905
+ * recorded. Same `wx` contract: the id names the state and the head.
9906
+ */
9907
+ function createPrState(input, now = Date.now()) {
9908
+ return create({
9909
+ version: 1,
9910
+ kind: "pr-state",
9911
+ ...input,
9912
+ createdAt: now
9913
+ });
9914
+ }
9870
9915
  function create(record) {
9871
9916
  if (!ID.test(record.id)) return null;
9872
9917
  try {
@@ -9993,6 +10038,7 @@ function isNotification(value) {
9993
10038
  if (!(record.version === 1 && typeof record.id === "string" && typeof record.reviewId === "string" && typeof record.subject === "string" && (record.branch === void 0 || typeof record.branch === "string") && typeof record.repo === "string" && typeof record.createdAt === "number" && (record.readAt === void 0 || typeof record.readAt === "number"))) return false;
9994
10039
  if (record.kind === "analysis") return OPERATIONS.includes(record.operation) && OUTCOMES.includes(record.outcome);
9995
10040
  if (record.kind === "pr-updated") return typeof record.headSha === "string" && typeof record.detail === "string" && (record.source === void 0 || record.source === "branch");
10041
+ if (record.kind === "pr-state") return (record.state === "merged" || record.state === "closed") && typeof record.headSha === "string";
9996
10042
  return false;
9997
10043
  }
9998
10044
  //#endregion
@@ -11834,7 +11880,9 @@ async function updateReview(workspace, runners = {}) {
11834
11880
  const current = () => loadJob(held.job.id) ?? held.job;
11835
11881
  const step = (phase) => saveJob(advance(current(), phase));
11836
11882
  try {
11883
+ const fetchStartedAt = Date.now();
11837
11884
  const outcome = await bringForward(workspace, runners, reviewedSha, held, step);
11885
+ if (outcome.ok && !outcome.fetchFailed && outcome.toSha) workspace.headSeenAt = fetchStartedAt;
11838
11886
  const ended = outcome.stop === "analysis-failed" ? fail(current(), outcome.message) : advance(current(), "done", outcome.message);
11839
11887
  saveJob(ended);
11840
11888
  announceJob(ended);
@@ -12015,7 +12063,8 @@ var UPDATE_OWNED_FIELDS = [
12015
12063
  "prContext",
12016
12064
  "lastPrRefresh",
12017
12065
  "dismissedFreshnessSha",
12018
- "lastUpdate"
12066
+ "lastUpdate",
12067
+ "headSeenAt"
12019
12068
  ];
12020
12069
  /**
12021
12070
  * What an update writes, laid over the review as it is on disk now rather than as it was when the
@@ -12466,4 +12515,4 @@ function lineageIds(id) {
12466
12515
  return [.../* @__PURE__ */ new Set([id, ...lineage.map((review) => review.id)])];
12467
12516
  }
12468
12517
  //#endregion
12469
- export { readServerRecord as $, summarise as $t, createPrUpdate as A, agentById as An, saveJob as At, unreadCount as B, groupByLineage as Bt, readEvents as C, clearAgentPreference as Cn, followedBranch as Ct, announceJob as D, STORE_ROOT as Dn, fail as Dt, telemetryUploadConsent as E, CONFIG_PATH as En, endedAt as Et, markRead as F, purposeOf as Ft, readMarker as G, loadWorkspace as Gt, EXPLAIN_SIMPLY_PROMPT as H, latestKissRun as Ht, pruneNotifications as I, clearClaim as It, relativeTime as J, positionInLineage as Jt, removeWorktree as K, matchingWorkspaceIds as Kt, readNotification as L, driverGone as Lt, headline as M, detectAgents as Mn, isLiveJob as Mt, listNotifications as N, AgentCancelledError as Nn, phasesFor as Nt, branchOf as O, AGENT_IDS as On, isTerminal as Ot, markAllRead as P, AgentUnavailableError as Pn, progressSteps as Pt, ensureServer as Q, reviewedRepositories as Qt, removeNotification as R, isClaimed as Rt, startJob as S, resolveModel as Sn, checkReview as St, recordTelemetryUploadConsent as T, writeAgentPreference as Tn, allJobs as Tt, ReviewBeingDeletedError as U, lineageKeyFor as Ut, withUsageRecorded as V, indexReviewGroups as Vt, MANAGED_ROOT as W, lineagePosition as Wt, bootstrapUrl as X, recentWorkspaces as Xt, readIndexToken as Y, previousKissResult as Yt, clearServerRecord as Z, reviewedCommit as Zt, agentEnvOf as _, resolveTarget as _n, forgeResolver as _t, wasBlocked as a, queueRemove as an, writeServerRecord as at, reanalyseReview as b, git as bn, fillFileUrlTemplate as bt, RefreshUnavailableError as c, writeQueue as cn, feedbackUrl as ct, refreshPrContext as d, isQuestionOutstanding as dn, MIN_WORKSPACE_PORT as dt, touchWorkspace as en, reviewsUrl as et, isUnchanged as f, issuesOutstanding as fn, parseWorkspacePort as ft, runningJobs as g, describeAuthorship as gn, claimRequestUrl as gt, liveUpdateFor as h, processDiscussion as hn, writePortPreference as ht, liveJobsFor as i, queueAdd as in, workspaceUrl as it, eventName as j, agentFor as jn, PHASES as jt, subjectOf as k, DEFAULT_AGENT_ID as kn, loadJob as kt, mergeUpdateInto as l, doneVerb as ln, DEFAULT_WORKSPACE_PORT as lt, UpdateAlreadyRunningError as m, reviewerDispositions as mn, statedWorkspacePort as mt, deleteReview as n, workspaceStamps as nn, spawnServer as nt, discardJob as o, queueStamp as on, BUILD_VERSION as ot, askCheckout as p, questionsOutstanding as pn, resolveWorkspacePort as pt, modelHelpLines as q, mutateWorkspace as qt, lineageIds as r, dequeueOnComplete as rn, stopServer as rt, FETCH_TIMEOUT_MS as s, readQueue as sn, PACKAGE_NAME as st, deleteLineage as t, workspaceRevision as tn, serverEntryExists as tt, updateReview as u, isIssueOutstanding as un, MAX_WORKSPACE_PORT as ut, recordKissRun as v, displayRef as vn, forgeFor as vt, record as w, readAgentPreference as wn, reviewedShaOf as wt, runJob as x, resolveAgent as xn, checkCodeFreshness as xt, startKissJob as y, isCommitReview as yn, remoteHost as yt, toneOf as z, followedRefName as zt };
12518
+ export { ensureServer as $, reviewedRepositories as $t, createPrState as A, AGENT_IDS as An, loadJob as At, toneOf as B, followedRefName as Bt, readEvents as C, resolveAgent as Cn, checkReview as Ct, announceJob as D, writeAgentPreference as Dn, endedAt as Dt, telemetryUploadConsent as E, readAgentPreference as En, allJobs as Et, markAllRead as F, AgentCancelledError as Fn, progressSteps as Ft, MANAGED_ROOT as G, lineagePosition as Gt, withUsageRecorded as H, indexReviewGroups as Ht, markRead as I, AgentUnavailableError as In, purposeOf as It, modelHelpLines as J, mutateWorkspace as Jt, readMarker as K, loadWorkspace as Kt, pruneNotifications as L, clearClaim as Lt, eventName as M, agentById as Mn, PHASES as Mt, headline as N, agentFor as Nn, isLiveJob as Nt, branchOf as O, CONFIG_PATH as On, fail as Ot, listNotifications as P, detectAgents as Pn, phasesFor as Pt, clearServerRecord as Q, reviewedCommit as Qt, readNotification as R, driverGone as Rt, startJob as S, git as Sn, checkCodeFreshness as St, recordTelemetryUploadConsent as T, clearAgentPreference as Tn, reviewedShaOf as Tt, EXPLAIN_SIMPLY_PROMPT as U, latestKissRun as Ut, unreadCount as V, groupByLineage as Vt, ReviewBeingDeletedError as W, lineageKeyFor as Wt, readIndexToken as X, previousKissResult as Xt, relativeTime as Y, positionInLineage as Yt, bootstrapUrl as Z, recentWorkspaces as Zt, agentEnvOf as _, processDiscussion as _n, claimRequestUrl as _t, wasBlocked as a, queueAdd as an, workspaceUrl as at, reanalyseReview as b, displayRef as bn, remoteHost as bt, RefreshUnavailableError as c, queueStamp as cn, PACKAGE_NAME as ct, refreshPrContext as d, doneVerb as dn, MAX_WORKSPACE_PORT as dt, summarise as en, readServerRecord as et, isUnchanged as f, isIssueOutstanding as fn, MIN_WORKSPACE_PORT as ft, runningJobs as g, reviewerDispositions as gn, writePortPreference as gt, liveUpdateFor as h, questionsOutstanding as hn, statedWorkspacePort as ht, liveJobsFor as i, dequeueOnComplete as in, stopServer as it, createPrUpdate as j, DEFAULT_AGENT_ID as jn, saveJob as jt, subjectOf as k, STORE_ROOT as kn, isTerminal as kt, mergeUpdateInto as l, readQueue as ln, feedbackUrl as lt, UpdateAlreadyRunningError as m, issuesOutstanding as mn, resolveWorkspacePort as mt, deleteReview as n, workspaceRevision as nn, serverEntryExists as nt, discardJob as o, queueAddMany as on, writeServerRecord as ot, askCheckout as p, isQuestionOutstanding as pn, parseWorkspacePort as pt, removeWorktree as q, matchingWorkspaceIds as qt, lineageIds as r, workspaceStamps as rn, spawnServer as rt, FETCH_TIMEOUT_MS as s, queueRemove as sn, BUILD_VERSION as st, deleteLineage as t, touchWorkspace as tn, reviewsUrl as tt, updateReview as u, writeQueue as un, DEFAULT_WORKSPACE_PORT as ut, recordKissRun as v, describeAuthorship as vn, forgeResolver as vt, record as w, resolveModel as wn, followedBranch as wt, runJob as x, isCommitReview as xn, fillFileUrlTemplate as xt, startKissJob as y, resolveTarget as yn, forgeFor as yt, removeNotification as z, isClaimed as zt };
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { $t as summarise, An as agentById, Bt as groupByLineage, Dn as STORE_ROOT, E as telemetryUploadConsent, En as CONFIG_PATH, Ft as purposeOf, G as readMarker, Gt as loadWorkspace, It as clearClaim, J as relativeTime, Jt as positionInLineage, K as removeWorktree, Kt as matchingWorkspaceIds, Lt as driverGone, Mn as detectAgents, On as AGENT_IDS, Ot as isTerminal, Pt as progressSteps, Q as ensureServer, Qt as reviewedRepositories, Rt as isClaimed, S as startJob, Sn as resolveModel, St as checkReview, T as recordTelemetryUploadConsent, Tn as writeAgentPreference, Tt as allJobs, Ut as lineageKeyFor, W as MANAGED_ROOT, X as bootstrapUrl, Xt as recentWorkspaces, Y as readIndexToken, _n as resolveTarget, _t as forgeResolver, a as wasBlocked, b as reanalyseReview, bn as git, dt as MIN_WORKSPACE_PORT, et as reviewsUrl, ft as parseWorkspacePort, g as runningJobs, h as liveUpdateFor, ht as writePortPreference, i as liveJobsFor, it as workspaceUrl, jt as PHASES, kn as DEFAULT_AGENT_ID, kt as loadJob, l as mergeUpdateInto, lt as DEFAULT_WORKSPACE_PORT, m as UpdateAlreadyRunningError, mt as statedWorkspacePort, n as deleteReview$1, ot as BUILD_VERSION, q as modelHelpLines, qt as mutateWorkspace, rt as stopServer, st as PACKAGE_NAME, u as updateReview, ut as MAX_WORKSPACE_PORT, v as recordKissRun, vn as displayRef, w as record$1, wn as readAgentPreference, x as runJob, xn as resolveAgent, zt as followedRefName } from "./delete_review-yHciIgD4.js";
2
+ import { $ as ensureServer, $t as reviewedRepositories, An as AGENT_IDS, At as loadJob, Bt as followedRefName, Cn as resolveAgent, Ct as checkReview, Dn as writeAgentPreference, E as telemetryUploadConsent, En as readAgentPreference, Et as allJobs, Ft as progressSteps, G as MANAGED_ROOT, It as purposeOf, J as modelHelpLines, Jt as mutateWorkspace, K as readMarker, Kt as loadWorkspace, Lt as clearClaim, Mn as agentById, Mt as PHASES, On as CONFIG_PATH, Pn as detectAgents, Rt as driverGone, S as startJob, Sn as git, T as recordTelemetryUploadConsent, Vt as groupByLineage, Wt as lineageKeyFor, X as readIndexToken, Y as relativeTime, Yt as positionInLineage, Z as bootstrapUrl, Zt as recentWorkspaces, a as wasBlocked, at as workspaceUrl, b as reanalyseReview, bn as displayRef, ct as PACKAGE_NAME, dt as MAX_WORKSPACE_PORT, en as summarise, ft as MIN_WORKSPACE_PORT, g as runningJobs, gt as writePortPreference, h as liveUpdateFor, ht as statedWorkspacePort, i as liveJobsFor, it as stopServer, jn as DEFAULT_AGENT_ID, kn as STORE_ROOT, kt as isTerminal, l as mergeUpdateInto, m as UpdateAlreadyRunningError, n as deleteReview$1, pt as parseWorkspacePort, q as removeWorktree, qt as matchingWorkspaceIds, st as BUILD_VERSION, tt as reviewsUrl, u as updateReview, ut as DEFAULT_WORKSPACE_PORT, v as recordKissRun, vt as forgeResolver, w as record$1, wn as resolveModel, x as runJob, yn as resolveTarget, zt as isClaimed } from "./delete_review-DPRL5IPt.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";