prreviewbuddy 0.43.0 → 0.44.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,35 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.44.0
4
+
5
+ **`prreviewbuddy review --queue` puts the reviews it starts on your review queue.** Add `--queue`
6
+ to any review command, such as `prreviewbuddy review --pr <url> <url> --queue`, and each pull request's
7
+ review goes on your review queue as soon as the review exists, without waiting for the analysis to
8
+ finish. The cards are already waiting under **Queue** on the Reviews page while they are still being
9
+ analysed, and a Reviews page you have open picks them up by itself. It works for one pull request or
10
+ several, for `--branch` and `--commit`, and beside `--fresh`, `--update` and `--reanalyse`. A review
11
+ already in the queue keeps its place. A review you have marked as reviewed is left out and keeps its
12
+ status: the command says so, and you can move it back to In progress on the page to queue it. When
13
+ you review several pull requests at once, the summary at the end lists what is in your queue, and
14
+ they are queued in the order their reviews were created, which isn't always the order you typed
15
+ them. Without `--queue`, nothing changes.
16
+
17
+ **The review queue no longer loses changes made at the same time.** With `--queue` there are two
18
+ things writing your review queue, the terminal and the Reviews page, and a change made in one while
19
+ the other was saving could be lost, or in the worst case empty the queue. Queue changes now happen
20
+ one at a time across every process on your machine, and the queue file is never left half-written.
21
+
22
+ **Copy has moved to the top of issue and question cards.** The button that copies a finding as
23
+ Markdown, ready to paste into a GitHub comment, used to sit at the bottom of the card beside
24
+ **Dismiss** and **Mark answered**. It is now a copy icon at the right of the card's title, because it
25
+ copies the whole finding, and hovering it says **Copy finding** or **Copy question**. After you
26
+ click it, it says **Copied** for a moment. The bottom of a card now holds only the actions that
27
+ change your review: **Investigate**, **Dismiss** and **Mark answered**.
28
+
29
+ **A clearer error when a pull request comes after another flag.** `prreviewbuddy review --pr <url>
30
+ --queue <url>` used to answer with a message about the old command syntax. It now says which flag
31
+ the second URL came after, and to put all the URLs together after `--pr`.
32
+
3
33
  ## 0.43.0
4
34
 
5
35
  **A review page shows when its pull request has merged or closed.** The **Merged** and **PR closed**
@@ -3802,6 +3802,133 @@ function doneVerb(counts) {
3802
3802
  return counts.reviewerClosed > 0 ? "dealt with" : "resolved";
3803
3803
  }
3804
3804
  //#endregion
3805
+ //#region ../../packages/review-harness/src/workspace/write_lock.ts
3806
+ /**
3807
+ * One lock file per name, taken exclusively, so a read-change-write in one process cannot land in
3808
+ * the middle of another's.
3809
+ *
3810
+ * Lifted out of `store.ts` unchanged, so the review queue can use it too. `queue.ts` cannot import
3811
+ * the store (the store's prune imports the queue, and the reverse import would close the cycle), and
3812
+ * a second lock written for the queue alone would be a second set of rules about stale holders to
3813
+ * get right. This module depends on `store_root.ts` and on Node, nothing else, for the same reason.
3814
+ *
3815
+ * Names share one directory. A review is locked under its id, a UUID, so a fixed word such as
3816
+ * `queue` can never collide with one.
3817
+ */
3818
+ /** Raised when another process has held a review's write lock for longer than any write takes. */
3819
+ var WorkspaceWriteBusyError = class extends Error {
3820
+ constructor() {
3821
+ super("This review is being written by another process. Try again in a moment.");
3822
+ this.name = "WorkspaceWriteBusyError";
3823
+ }
3824
+ };
3825
+ var WRITE_LOCKS_DIR = join(STORE_ROOT, "locks");
3826
+ /** Every write under the lock is one read and one rename, milliseconds. This is a hang, not a queue. */
3827
+ var WRITE_LOCK_WAIT_MS = 5e3;
3828
+ /**
3829
+ * A lock this old is broken whoever holds it.
3830
+ *
3831
+ * Every hold is synchronous and lasts one read and one rename, so no live writer is anywhere near
3832
+ * this. It is the fallback for what a pid cannot say: a holder on another machine, a lock not yet
3833
+ * written out, and a pid that died and was reused by some unrelated long-lived process, which a
3834
+ * liveness check alone would read as the holder still working, for ever.
3835
+ */
3836
+ var WRITE_LOCK_STALE_MS = 3e4;
3837
+ var heldWriteLocks = /* @__PURE__ */ new Map();
3838
+ var sleeper = new Int32Array(new SharedArrayBuffer(4));
3839
+ /**
3840
+ * One review's writes, one at a time across every process on the machine.
3841
+ *
3842
+ * The CLI and the workspace server are separate processes over one store. Without this, each could
3843
+ * load the same review, change its own fields and rename its result into place, and whichever
3844
+ * renamed last would discard the other: an update finishing in the terminal would put back a
3845
+ * question the reviewer had just ticked on the page. A file created exclusively is the lock, so it
3846
+ * holds between processes and needs nothing running to arbitrate.
3847
+ *
3848
+ * Synchronous, including the wait, because every write it guards is. It is held for a read and a
3849
+ * rename, so a wait is milliseconds; one that runs to `WRITE_LOCK_WAIT_MS` means a holder has hung,
3850
+ * and that is raised rather than written through. Re-entrant within a process, so a change made
3851
+ * under it can save without deadlocking itself. A lock left by a process that died is broken: at
3852
+ * once when its holder is on this machine and gone, and after `WRITE_LOCK_STALE_MS` in every case,
3853
+ * because a pid can be reused and a live one proves nothing about who took the lock.
3854
+ */
3855
+ function withWriteLock(id, fn) {
3856
+ const depth = heldWriteLocks.get(id) ?? 0;
3857
+ if (depth > 0) {
3858
+ heldWriteLocks.set(id, depth + 1);
3859
+ try {
3860
+ return fn();
3861
+ } finally {
3862
+ heldWriteLocks.set(id, depth);
3863
+ }
3864
+ }
3865
+ mkdirSync(WRITE_LOCKS_DIR, { recursive: true });
3866
+ const path = join(WRITE_LOCKS_DIR, `${id}.lock`);
3867
+ const mine = {
3868
+ token: randomUUID(),
3869
+ pid: process.pid,
3870
+ host: hostname(),
3871
+ at: Date.now()
3872
+ };
3873
+ const deadline = Date.now() + WRITE_LOCK_WAIT_MS;
3874
+ for (;;) {
3875
+ try {
3876
+ const fd = openSync(path, "wx", 384);
3877
+ try {
3878
+ writeSync(fd, JSON.stringify(mine));
3879
+ } finally {
3880
+ closeSync(fd);
3881
+ }
3882
+ break;
3883
+ } catch (error) {
3884
+ if (error.code !== "EEXIST") throw error;
3885
+ }
3886
+ if (breakIfAbandoned(path)) continue;
3887
+ if (Date.now() >= deadline) throw new WorkspaceWriteBusyError();
3888
+ Atomics.wait(sleeper, 0, 0, 2);
3889
+ }
3890
+ heldWriteLocks.set(id, 1);
3891
+ try {
3892
+ return fn();
3893
+ } finally {
3894
+ heldWriteLocks.delete(id);
3895
+ if (readHolder(path)?.token === mine.token) try {
3896
+ unlinkSync(path);
3897
+ } catch {}
3898
+ }
3899
+ }
3900
+ function readHolder(path) {
3901
+ try {
3902
+ return JSON.parse(readFileSync(path, "utf8"));
3903
+ } catch {
3904
+ return null;
3905
+ }
3906
+ }
3907
+ /** Removes a lock whose holder is gone, and says whether it did. */
3908
+ function breakIfAbandoned(path) {
3909
+ const holder = readHolder(path);
3910
+ let age = 0;
3911
+ try {
3912
+ age = Date.now() - Math.min(statSync(path).mtimeMs, holder?.at ?? Infinity);
3913
+ } catch {
3914
+ return true;
3915
+ }
3916
+ if (!(age > WRITE_LOCK_STALE_MS || holder !== null && holder.host === hostname() && !processAlive(holder.pid))) return false;
3917
+ if (readHolder(path)?.token !== holder?.token) return true;
3918
+ try {
3919
+ unlinkSync(path);
3920
+ } catch {}
3921
+ return true;
3922
+ }
3923
+ function processAlive(pid) {
3924
+ try {
3925
+ process.kill(pid, 0);
3926
+ return true;
3927
+ } catch (error) {
3928
+ return error.code === "EPERM";
3929
+ }
3930
+ }
3931
+ //#endregion
3805
3932
  //#region ../../packages/review-harness/src/workspace/queue.ts
3806
3933
  /**
3807
3934
  * The review queue: the set the reviewer is working through now, in the order they chose.
@@ -3823,6 +3950,11 @@ function doneVerb(counts) {
3823
3950
  */
3824
3951
  var QUEUE_PATH = join(STORE_ROOT, "queue.json");
3825
3952
  /**
3953
+ * The name the queue is locked under. Reviews are locked under their UUIDs in the same directory,
3954
+ * so a plain word cannot collide with one.
3955
+ */
3956
+ var QUEUE_LOCK = "queue";
3957
+ /**
3826
3958
  * An object rather than a bare array, because an array is the one JSON shape that cannot grow a
3827
3959
  * sibling field, and "when was this queued" is a plausible second column.
3828
3960
  *
@@ -3849,31 +3981,65 @@ function readQueue() {
3849
3981
  }
3850
3982
  }
3851
3983
  /**
3852
- * Store a queue, and say what was stored.
3984
+ * Change the queue as it is on disk now: read, change, write, with nothing able to land in between.
3985
+ *
3986
+ * **The only way the queue is written.** The workspace server and the CLI are separate processes
3987
+ * over this one file, and a `--queue` from the terminal can arrive while somebody is adding or
3988
+ * removing on the page. Each of them reading the queue, changing its copy and writing it back would
3989
+ * keep whichever wrote last and drop the other's change -- while both reported success. So the
3990
+ * whole read-change-write runs under the queue's cross-process lock, the same lock a review's own
3991
+ * writes take (see `write_lock.ts`).
3853
3992
  *
3854
- * The return is what landed on disk, which is not necessarily what was handed in -- duplicates are
3855
- * collapsed and the cap applies. Same contract as the prompt bank: the caller gets the list as
3856
- * stored so a page can paint the truth rather than its own request.
3993
+ * One lock, taken once, here. Every mutation below is one call to this and nothing inside `change`
3994
+ * writes, so the transaction is visibly this function's body and nothing depends on the lock being
3995
+ * re-entrant. `change` may read other files -- `queue_live.ts` asks which reviews still exist --
3996
+ * but must not write the queue or take another lock.
3857
3997
  *
3858
- * `null` on a write failure, and that is the point at which this stops resembling `preferences.ts`.
3859
- * A pane width that fails to save costs one session and nothing depends on it. A queue entry that
3860
- * fails to save is contradicted by a pill showing a count and by a button that navigates somewhere,
3861
- * so the caller has to be able to tell the reviewer it did not happen.
3998
+ * `change` returns the queue to store, or null to store nothing, in which case the queue as it was
3999
+ * read is the answer. Null from this function means the write failed, including a lock held past
4000
+ * any real write, which is the same thing to the caller: it did not happen.
3862
4001
  */
3863
- function writeQueue(ids) {
3864
- const next = normalise(ids);
4002
+ function updateQueue(change) {
3865
4003
  try {
3866
- mkdirSync(STORE_ROOT, { recursive: true });
3867
- writeFileSync(QUEUE_PATH, `${JSON.stringify({
3868
- version: VERSION,
3869
- ids: next
3870
- }, null, 2)}\n`);
3871
- return next;
4004
+ return withWriteLock(QUEUE_LOCK, () => {
4005
+ const current = readQueue();
4006
+ const next = change(current);
4007
+ return next === null ? current : writeQueueUnlocked(next);
4008
+ });
3872
4009
  } catch {
3873
4010
  return null;
3874
4011
  }
3875
4012
  }
3876
4013
  /**
4014
+ * The write itself, for `updateQueue` only: the caller holds the lock.
4015
+ *
4016
+ * To a temporary file and renamed into place, because a rename is atomic and a write is not.
4017
+ * `writeFileSync` empties the file before it fills it, and `readQueue` reads a file it cannot parse
4018
+ * as "nothing queued" -- so a reader in another process that caught it half-written would take the
4019
+ * queue to be empty and, on its next write, make it so. Found doing exactly that: four processes
4020
+ * adding twelve reviews each left one in the queue. The temporary name carries the pid so two
4021
+ * processes never share one, although under the lock they never write at once anyway.
4022
+ */
4023
+ function writeQueueUnlocked(ids) {
4024
+ const next = normalise(ids);
4025
+ mkdirSync(STORE_ROOT, { recursive: true });
4026
+ const stored = {
4027
+ version: VERSION,
4028
+ ids: next
4029
+ };
4030
+ const temporary = `${QUEUE_PATH}.${process.pid}.tmp`;
4031
+ try {
4032
+ writeFileSync(temporary, `${JSON.stringify(stored, null, 2)}\n`);
4033
+ renameSync(temporary, QUEUE_PATH);
4034
+ } catch (error) {
4035
+ try {
4036
+ unlinkSync(temporary);
4037
+ } catch {}
4038
+ throw error;
4039
+ }
4040
+ return next;
4041
+ }
4042
+ /**
3877
4043
  * Append, and leave a review already queued exactly where it is.
3878
4044
  *
3879
4045
  * Adding something twice is a no-op rather than a move. Nothing in this UI reorders a queue, so a
@@ -3881,9 +4047,7 @@ function writeQueue(ids) {
3881
4047
  * and it would do it without ever saying so.
3882
4048
  */
3883
4049
  function queueAdd(id) {
3884
- const current = readQueue();
3885
- if (current.includes(id)) return current;
3886
- return writeQueue([...current, id]);
4050
+ return updateQueue((current) => current.includes(id) ? null : [...current, id]);
3887
4051
  }
3888
4052
  /**
3889
4053
  * Append several, in the order given, for the reviews index's bulk bar.
@@ -3894,24 +4058,20 @@ function queueAdd(id) {
3894
4058
  * named in `full`, and the caller has something true to say about it.
3895
4059
  */
3896
4060
  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)]);
4061
+ let full = [];
4062
+ const queue = updateQueue((current) => {
4063
+ const fresh = ids.filter((id, at) => !current.includes(id) && ids.indexOf(id) === at);
4064
+ const room = Math.max(0, 50 - current.length);
4065
+ full = fresh.slice(room);
4066
+ return fresh.length <= full.length ? null : [...current, ...fresh.slice(0, room)];
4067
+ });
3906
4068
  return queue === null ? null : {
3907
4069
  queue,
3908
4070
  full
3909
4071
  };
3910
4072
  }
3911
4073
  function queueRemove(id) {
3912
- const current = readQueue();
3913
- if (!current.includes(id)) return current;
3914
- return writeQueue(current.filter((queued) => queued !== id));
4074
+ return updateQueue((current) => current.includes(id) ? current.filter((queued) => queued !== id) : null);
3915
4075
  }
3916
4076
  /**
3917
4077
  * The invariant, as one named thing: the queue holds unfinished work only.
@@ -4042,119 +4202,6 @@ function saveWorkspace(workspace) {
4042
4202
  writeWorkspace(workspace);
4043
4203
  });
4044
4204
  }
4045
- /** Raised when another process has held a review's write lock for longer than any write takes. */
4046
- var WorkspaceWriteBusyError = class extends Error {
4047
- constructor() {
4048
- super("This review is being written by another process. Try again in a moment.");
4049
- this.name = "WorkspaceWriteBusyError";
4050
- }
4051
- };
4052
- var WRITE_LOCKS_DIR = join(STORE_ROOT, "locks");
4053
- /** Every write under the lock is one read and one rename, milliseconds. This is a hang, not a queue. */
4054
- var WRITE_LOCK_WAIT_MS = 5e3;
4055
- /**
4056
- * A lock this old is broken whoever holds it.
4057
- *
4058
- * Every hold is synchronous and lasts one read and one rename, so no live writer is anywhere near
4059
- * this. It is the fallback for what a pid cannot say: a holder on another machine, a lock not yet
4060
- * written out, and a pid that died and was reused by some unrelated long-lived process, which a
4061
- * liveness check alone would read as the holder still working, for ever.
4062
- */
4063
- var WRITE_LOCK_STALE_MS = 3e4;
4064
- var heldWriteLocks = /* @__PURE__ */ new Map();
4065
- var sleeper = new Int32Array(new SharedArrayBuffer(4));
4066
- /**
4067
- * One review's writes, one at a time across every process on the machine.
4068
- *
4069
- * The CLI and the workspace server are separate processes over one store. Without this, each could
4070
- * load the same review, change its own fields and rename its result into place, and whichever
4071
- * renamed last would discard the other: an update finishing in the terminal would put back a
4072
- * question the reviewer had just ticked on the page. A file created exclusively is the lock, so it
4073
- * holds between processes and needs nothing running to arbitrate.
4074
- *
4075
- * Synchronous, including the wait, because every write it guards is. It is held for a read and a
4076
- * rename, so a wait is milliseconds; one that runs to `WRITE_LOCK_WAIT_MS` means a holder has hung,
4077
- * and that is raised rather than written through. Re-entrant within a process, so a change made
4078
- * under it can save without deadlocking itself. A lock left by a process that died is broken: at
4079
- * once when its holder is on this machine and gone, and after `WRITE_LOCK_STALE_MS` in every case,
4080
- * because a pid can be reused and a live one proves nothing about who took the lock.
4081
- */
4082
- function withWriteLock(id, fn) {
4083
- const depth = heldWriteLocks.get(id) ?? 0;
4084
- if (depth > 0) {
4085
- heldWriteLocks.set(id, depth + 1);
4086
- try {
4087
- return fn();
4088
- } finally {
4089
- heldWriteLocks.set(id, depth);
4090
- }
4091
- }
4092
- mkdirSync(WRITE_LOCKS_DIR, { recursive: true });
4093
- const path = join(WRITE_LOCKS_DIR, `${id}.lock`);
4094
- const mine = {
4095
- token: randomUUID(),
4096
- pid: process.pid,
4097
- host: hostname(),
4098
- at: Date.now()
4099
- };
4100
- const deadline = Date.now() + WRITE_LOCK_WAIT_MS;
4101
- for (;;) {
4102
- try {
4103
- const fd = openSync(path, "wx", 384);
4104
- try {
4105
- writeSync(fd, JSON.stringify(mine));
4106
- } finally {
4107
- closeSync(fd);
4108
- }
4109
- break;
4110
- } catch (error) {
4111
- if (error.code !== "EEXIST") throw error;
4112
- }
4113
- if (breakIfAbandoned(path)) continue;
4114
- if (Date.now() >= deadline) throw new WorkspaceWriteBusyError();
4115
- Atomics.wait(sleeper, 0, 0, 2);
4116
- }
4117
- heldWriteLocks.set(id, 1);
4118
- try {
4119
- return fn();
4120
- } finally {
4121
- heldWriteLocks.delete(id);
4122
- if (readHolder(path)?.token === mine.token) try {
4123
- unlinkSync(path);
4124
- } catch {}
4125
- }
4126
- }
4127
- function readHolder(path) {
4128
- try {
4129
- return JSON.parse(readFileSync(path, "utf8"));
4130
- } catch {
4131
- return null;
4132
- }
4133
- }
4134
- /** Removes a lock whose holder is gone, and says whether it did. */
4135
- function breakIfAbandoned(path) {
4136
- const holder = readHolder(path);
4137
- let age = 0;
4138
- try {
4139
- age = Date.now() - Math.min(statSync(path).mtimeMs, holder?.at ?? Infinity);
4140
- } catch {
4141
- return true;
4142
- }
4143
- if (!(age > WRITE_LOCK_STALE_MS || holder !== null && holder.host === hostname() && !processAlive(holder.pid))) return false;
4144
- if (readHolder(path)?.token !== holder?.token) return true;
4145
- try {
4146
- unlinkSync(path);
4147
- } catch {}
4148
- return true;
4149
- }
4150
- function processAlive(pid) {
4151
- try {
4152
- process.kill(pid, 0);
4153
- return true;
4154
- } catch (error) {
4155
- return error.code === "EPERM";
4156
- }
4157
- }
4158
4205
  /**
4159
4206
  * Change a review as it is on disk now: read, change, write, with nothing able to run in between.
4160
4207
  *
@@ -7429,6 +7476,71 @@ function readIndexToken() {
7429
7476
  return replacement;
7430
7477
  }
7431
7478
  //#endregion
7479
+ //#region ../../packages/review-harness/src/workspace/queue_live.ts
7480
+ /**
7481
+ * The review queue as it relates to reviews that actually exist.
7482
+ *
7483
+ * `queue.ts` stores ids and must not learn to read the review store: `store.ts` imports it for the
7484
+ * prune, and the reverse import would close the cycle. Anything that has to ask both questions --
7485
+ * is this id queued, and is it a review -- lives here instead, which imports both and is imported by
7486
+ * neither. That used to be one function private to `http.ts`; the CLI's `--queue` needs the same
7487
+ * answers, and two copies of "which queued ids are still live" would drift.
7488
+ */
7489
+ /**
7490
+ * Drop queued ids whose review has been deleted or pruned, and store the result if that changed it.
7491
+ *
7492
+ * `stored` is what the caller last saw, and is only a shortcut: when every id in it is live there is
7493
+ * nothing to do. Otherwise the compaction is made against the queue as it is under the lock, not
7494
+ * against `stored`, so an id somebody queued since `stored` was read is not dropped by writing an
7495
+ * older list back.
7496
+ *
7497
+ * Falls back to what was handed in on a write failure: a stale id in the answer is a card the page
7498
+ * cannot draw, which it already tolerates, and is better than refusing an Add over it.
7499
+ */
7500
+ function compactQueue(stored) {
7501
+ if (stored.every(isLive)) return stored;
7502
+ return updateQueue((current) => livePart(current)) ?? stored;
7503
+ }
7504
+ /**
7505
+ * Put one review on the queue, the way the index's Add does, and say how that went.
7506
+ *
7507
+ * **A completed review is left out**, not queued and not reopened. The queue holds unfinished work
7508
+ * only -- `dequeueOnComplete` is the other half of that rule -- and quietly moving somebody's review
7509
+ * back to In progress because a command mentioned it is a status change nobody asked for.
7510
+ *
7511
+ * Already queued is `already`, not a move: the same rule `queueAdd` keeps, for the same reason.
7512
+ *
7513
+ * Compacted before adding, for the reason `addMany` in `http.ts` gives: a review deleted since it
7514
+ * was queued would otherwise hold a place, and a live review could be refused as "full" by it.
7515
+ */
7516
+ function queueForReview(id) {
7517
+ const workspace = loadWorkspace(id);
7518
+ if (!workspace) return "gone";
7519
+ if (workspace.completedAt !== void 0) return "complete";
7520
+ let result = "added";
7521
+ return updateQueue((current) => {
7522
+ const live = current.filter(isLive);
7523
+ const unchanged = live.length === current.length ? null : live;
7524
+ if (live.includes(id)) {
7525
+ result = "already";
7526
+ return unchanged;
7527
+ }
7528
+ if (live.length >= 50) {
7529
+ result = "full";
7530
+ return unchanged;
7531
+ }
7532
+ return [...live, id];
7533
+ }) === null ? "unsaved" : result;
7534
+ }
7535
+ function isLive(id) {
7536
+ return loadWorkspace(id) !== null;
7537
+ }
7538
+ /** The queue without its deleted reviews, or null when there are none to drop. */
7539
+ function livePart(queue) {
7540
+ const live = queue.filter(isLive);
7541
+ return live.length === queue.length ? null : live;
7542
+ }
7543
+ //#endregion
7432
7544
  //#region ../../packages/review-harness/src/workspace/relative_time.ts
7433
7545
  /**
7434
7546
  * Relative time, in the one wording this product uses.
@@ -12515,4 +12627,4 @@ function lineageIds(id) {
12515
12627
  return [.../* @__PURE__ */ new Set([id, ...lineage.map((review) => review.id)])];
12516
12628
  }
12517
12629
  //#endregion
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 };
12630
+ export { bootstrapUrl as $, recentWorkspaces as $t, createPrState as A, STORE_ROOT as An, fail as At, toneOf as B, driverGone as Bt, readEvents as C, git as Cn, fillFileUrlTemplate as Ct, announceJob as D, readAgentPreference as Dn, reviewedShaOf as Dt, telemetryUploadConsent as E, clearAgentPreference as En, followedBranch as Et, markAllRead as F, detectAgents as Fn, isLiveJob as Ft, MANAGED_ROOT as G, latestKissRun as Gt, withUsageRecorded as H, followedRefName as Ht, markRead as I, AgentCancelledError as In, phasesFor as It, modelHelpLines as J, loadWorkspace as Jt, readMarker as K, lineageKeyFor as Kt, pruneNotifications as L, AgentUnavailableError as Ln, progressSteps as Lt, eventName as M, DEFAULT_AGENT_ID as Mn, loadJob as Mt, headline as N, agentById as Nn, saveJob as Nt, branchOf as O, writeAgentPreference as On, allJobs as Ot, listNotifications as P, agentFor as Pn, PHASES as Pt, readIndexToken as Q, previousKissResult as Qt, readNotification as R, purposeOf as Rt, startJob as S, isCommitReview as Sn, remoteHost as St, recordTelemetryUploadConsent as T, resolveModel as Tn, checkReview as Tt, EXPLAIN_SIMPLY_PROMPT as U, groupByLineage as Ut, unreadCount as V, isClaimed as Vt, ReviewBeingDeletedError as W, indexReviewGroups as Wt, compactQueue as X, mutateWorkspace as Xt, relativeTime as Y, matchingWorkspaceIds as Yt, queueForReview as Z, positionInLineage as Zt, agentEnvOf as _, reviewerDispositions as _n, statedWorkspacePort as _t, wasBlocked as a, workspaceStamps as an, spawnServer as at, reanalyseReview as b, resolveTarget as bn, forgeResolver as bt, RefreshUnavailableError as c, queueAddMany as cn, writeServerRecord as ct, refreshPrContext as d, readQueue as dn, feedbackUrl as dt, reviewedCommit as en, clearServerRecord as et, isUnchanged as f, doneVerb as fn, DEFAULT_WORKSPACE_PORT as ft, runningJobs as g, questionsOutstanding as gn, resolveWorkspacePort as gt, liveUpdateFor as h, issuesOutstanding as hn, parseWorkspacePort as ht, liveJobsFor as i, workspaceRevision as in, serverEntryExists as it, createPrUpdate as j, AGENT_IDS as jn, isTerminal as jt, subjectOf as k, CONFIG_PATH as kn, endedAt as kt, mergeUpdateInto as l, queueRemove as ln, BUILD_VERSION as lt, UpdateAlreadyRunningError as m, isQuestionOutstanding as mn, MIN_WORKSPACE_PORT as mt, deleteReview as n, summarise as nn, readServerRecord as nt, discardJob as o, dequeueOnComplete as on, stopServer as ot, askCheckout as p, isIssueOutstanding as pn, MAX_WORKSPACE_PORT as pt, removeWorktree as q, lineagePosition as qt, lineageIds as r, touchWorkspace as rn, reviewsUrl as rt, FETCH_TIMEOUT_MS as s, queueAdd as sn, workspaceUrl as st, deleteLineage as t, reviewedRepositories as tn, ensureServer as tt, updateReview as u, queueStamp as un, PACKAGE_NAME as ut, recordKissRun as v, processDiscussion as vn, writePortPreference as vt, record as w, resolveAgent as wn, checkCodeFreshness as wt, runJob as x, displayRef as xn, forgeFor as xt, startKissJob as y, describeAuthorship as yn, claimRequestUrl as yt, removeNotification as z, clearClaim as zt };