prreviewbuddy 0.27.2 → 0.29.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/CHANGELOG.md CHANGED
@@ -1,5 +1,55 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.29.1
4
+
5
+ **Icons in the review sidebar.** Every item in a review's sidebar now has an icon: Review, KISS,
6
+ Issues, Questions, Files, PR context, Diagrams, Recommendations, and the links at the bottom. All
7
+ reviews and Settings had small text symbols before; they now have full-size icons like the rest.
8
+ The themes in the review path keep their numbers.
9
+
10
+ **Review path themes start collapsed.** On a review's path page, each theme now opens folded to
11
+ its title, priority and size, so you can see the whole path at a glance. Click a theme to open it,
12
+ or choose it in the sidebar to jump straight to it opened.
13
+
14
+ ## 0.29.0
15
+
16
+ **Filter your reviews by author.** The reviews list has a dropdown beside the search that lists
17
+ everyone whose pull requests you have reviewed. Choose someone and the list shows only their
18
+ reviews. It works together with the filters beside it, so you can see, say, everything of one
19
+ person's that is still in progress. Reviews of a local branch with no pull request have no GitHub
20
+ username, so they show under Everyone only.
21
+
22
+ **Mark a review reviewed from the list.** Each review's menu in the reviews list now has Mark
23
+ reviewed, so you no longer have to open a review to mark it. Once it is reviewed, the same menu
24
+ offers Mark in progress to undo it. As before, marking a review reviewed takes it out of your
25
+ review queue.
26
+
27
+ **Delete a review from its own page.** The menu at the top of a review now has Delete review,
28
+ with the same confirmation the list uses. It deletes only the review you are looking at, not
29
+ earlier reviews of the same branch, and then takes you back to your reviews. A review that is
30
+ still being analysed cannot be deleted.
31
+
32
+ **Easier to scan sidebar.** In a review's sidebar, Issues, Questions, Files, PR context and the
33
+ links at the bottom are now white, so the review path above them stands out as its own list.
34
+
35
+ ## 0.28.0
36
+
37
+ **Save a review, and work through a queue.** The star on each review in your reviews list now
38
+ means Saved. A saved review still sits at the top of the list, and the new Saved filter shows only
39
+ those. For a batch you are working through right now, choose Add to review queue from a review's
40
+ menu: the Queue filter lists them in the order you added them, and Continue reviewing at the top of
41
+ the list opens the first one. Marking a review Reviewed takes it out of the queue. Moving it back
42
+ to In progress does not put it back, so the queue only ever holds what you chose to put there.
43
+
44
+ **Saved and queued reviews are kept for ninety days.** Other reviews are still kept for thirty
45
+ days. The ninety days count from the last time you opened the review, so one you keep coming back
46
+ to stays. A saved review is not kept forever: after ninety days without being opened it is removed
47
+ like any other.
48
+
49
+ **A saved review no longer disappears from the list.** The list shows your fifty most recent
50
+ branches, and a review saved or queued further back than that used to be missing from it
51
+ altogether. Saved and queued reviews are now always listed, in addition to the fifty most recent.
52
+
3
53
  ## 0.27.2
4
54
 
5
55
  **Choose the model a review runs under.** `prreviewbuddy review --model <model>` asks the agent for
@@ -3651,10 +3651,135 @@ function doneVerb(counts) {
3651
3651
  return counts.reviewerClosed > 0 ? "dealt with" : "resolved";
3652
3652
  }
3653
3653
  //#endregion
3654
+ //#region ../../packages/review-harness/src/workspace/queue.ts
3655
+ /**
3656
+ * The review queue: the set the reviewer is working through now, in the order they chose.
3657
+ *
3658
+ * **Its own file, not a field on `StoredWorkspace`.** The reason is already written at the top of
3659
+ * `store.ts`'s save path: every caller puts back a record it loaded earlier, `runJob` holds one for
3660
+ * an entire analysis, and the write clobbers whatever changed in between without saying so. Queue
3661
+ * membership changed from the index while an analysis was running would simply vanish. A record
3662
+ * also carries the whole diff inline, so a reorder would rewrite megabytes to move one string.
3663
+ *
3664
+ * **Not a key in `ui.json` either**, despite being reviewer state of exactly the kind that file
3665
+ * describes. `preferences.ts` read-modify-writes that file whole, and one unparsable byte in there
3666
+ * currently costs a pane width. Sharing it would make the same byte cost the working set.
3667
+ *
3668
+ * `STORE_ROOT` comes from `store_root.ts` rather than from `store.ts`, which is the import
3669
+ * `preferences.ts` uses. That one is safe only because nothing in `store.ts` imports preferences
3670
+ * back -- and the prune in there *does* import this module, so taking the same shortcut here would
3671
+ * close the cycle.
3672
+ */
3673
+ var QUEUE_PATH = join(STORE_ROOT, "queue.json");
3674
+ /**
3675
+ * An object rather than a bare array, because an array is the one JSON shape that cannot grow a
3676
+ * sibling field, and "when was this queued" is a plausible second column.
3677
+ *
3678
+ * `version` is not branched on today. It exists so that a later shape change is a decision rather
3679
+ * than a guess about what an old file meant.
3680
+ */
3681
+ var VERSION = 1;
3682
+ /**
3683
+ * What is queued, in order.
3684
+ *
3685
+ * Best-effort in the same way as `readPreferences`, and for the same reason: missing, unreadable
3686
+ * and corrupt all mean "nothing queued", and none of them is worth an error standing between the
3687
+ * reviewer and the list. Position is the array index throughout -- never stored per entry, because
3688
+ * two representations of one order eventually disagree about it.
3689
+ */
3690
+ function readQueue() {
3691
+ try {
3692
+ const parsed = JSON.parse(readFileSync(QUEUE_PATH, "utf8"));
3693
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) return [];
3694
+ const ids = parsed.ids;
3695
+ return Array.isArray(ids) ? normalise(ids) : [];
3696
+ } catch {
3697
+ return [];
3698
+ }
3699
+ }
3700
+ /**
3701
+ * Store a queue, and say what was stored.
3702
+ *
3703
+ * The return is what landed on disk, which is not necessarily what was handed in -- duplicates are
3704
+ * collapsed and the cap applies. Same contract as the prompt bank: the caller gets the list as
3705
+ * stored so a page can paint the truth rather than its own request.
3706
+ *
3707
+ * `null` on a write failure, and that is the point at which this stops resembling `preferences.ts`.
3708
+ * A pane width that fails to save costs one session and nothing depends on it. A queue entry that
3709
+ * fails to save is contradicted by a pill showing a count and by a button that navigates somewhere,
3710
+ * so the caller has to be able to tell the reviewer it did not happen.
3711
+ */
3712
+ function writeQueue(ids) {
3713
+ const next = normalise(ids);
3714
+ try {
3715
+ mkdirSync(STORE_ROOT, { recursive: true });
3716
+ writeFileSync(QUEUE_PATH, `${JSON.stringify({
3717
+ version: VERSION,
3718
+ ids: next
3719
+ }, null, 2)}\n`);
3720
+ return next;
3721
+ } catch {
3722
+ return null;
3723
+ }
3724
+ }
3725
+ /**
3726
+ * Append, and leave a review already queued exactly where it is.
3727
+ *
3728
+ * Adding something twice is a no-op rather than a move. Nothing in this UI reorders a queue, so a
3729
+ * second Add that silently promoted a review would be the only control in the product that did --
3730
+ * and it would do it without ever saying so.
3731
+ */
3732
+ function queueAdd(id) {
3733
+ const current = readQueue();
3734
+ if (current.includes(id)) return current;
3735
+ return writeQueue([...current, id]);
3736
+ }
3737
+ function queueRemove(id) {
3738
+ const current = readQueue();
3739
+ if (!current.includes(id)) return current;
3740
+ return writeQueue(current.filter((queued) => queued !== id));
3741
+ }
3742
+ /**
3743
+ * The invariant, as one named thing: the queue holds unfinished work only.
3744
+ *
3745
+ * Removing on completion is half of it. The other half is that this **does nothing** when
3746
+ * `complete` is false, and that half only exists as a written rule -- there is no line of code for
3747
+ * it to be visible in. Re-adding there would make membership something inferred from status rather
3748
+ * than chosen, so a reviewer who took a review out of the queue and later moved it back to In
3749
+ * progress would find it queued again with no idea why.
3750
+ */
3751
+ function dequeueOnComplete(id, complete) {
3752
+ if (!complete) return;
3753
+ queueRemove(id);
3754
+ }
3755
+ /**
3756
+ * A token for the index's revision, equality only.
3757
+ *
3758
+ * The queue changes only when a person deliberately changes it, so unlike a running job's progress
3759
+ * this cannot make the page reload itself in a loop. Absent reads as a stable empty string rather
3760
+ * than as a change, for the reason `refreshAction` gives: a missing field that reads as "different"
3761
+ * is a reload that asks again and reloads again.
3762
+ */
3763
+ function queueStamp() {
3764
+ try {
3765
+ return String(statSync(QUEUE_PATH).mtimeMs);
3766
+ } catch {
3767
+ return "";
3768
+ }
3769
+ }
3770
+ /** Strings only, first position wins, and never more than the cap. */
3771
+ function normalise(ids) {
3772
+ const seen = [];
3773
+ for (const id of ids) {
3774
+ if (typeof id !== "string" || id === "" || seen.includes(id)) continue;
3775
+ seen.push(id);
3776
+ if (seen.length === 50) break;
3777
+ }
3778
+ return seen;
3779
+ }
3780
+ //#endregion
3654
3781
  //#region ../../packages/review-harness/src/workspace/store.ts
3655
3782
  var WORKSPACES_DIR = join(STORE_ROOT, "workspaces");
3656
- /** Reviews older than this are cleared on write. A review of a branch from a month ago is history. */
3657
- var MAX_AGE_MS = 2592e6;
3658
3783
  function workspacePath(id) {
3659
3784
  return join(WORKSPACES_DIR, `${id}.json`);
3660
3785
  }
@@ -3944,14 +4069,38 @@ function followedRefName(branch, followedRef) {
3944
4069
  return followedRef;
3945
4070
  }
3946
4071
  /**
3947
- * The index's list: one entry per branch, each carrying its own history.
4072
+ * The index's list: recent branches, plus the ones the reviewer explicitly kept.
4073
+ *
4074
+ * `recentReviewGroups` above is a pure recency window, and it stays that way because other callers
4075
+ * want exactly that. This is the index's own question, and it is a different one, because Saved and
4076
+ * Queue exist to defeat recency: a review saved three weeks ago has to be reachable from the list
4077
+ * or the pill that claims to show it is simply broken.
4078
+ *
4079
+ * **Two windows, and the page shows their sum.** Drawing both from one limit would mean saving
4080
+ * eleven reviews pushed eleven recent ones off the page -- the working set eating the list it was
4081
+ * added to. So `limit` bounds recency, `keptLimit` bounds what was kept, and the most cards this
4082
+ * can return is the two added together. Neither constant says that on its own, which is why it is
4083
+ * said here.
4084
+ */
4085
+ function indexReviewGroups(limit, keptLimit, keptIds) {
4086
+ return selectIndexGroups(groupByLineage(recentWorkspaces(Number.MAX_SAFE_INTEGER)), limit, keptLimit, keptIds);
4087
+ }
4088
+ /**
4089
+ * The choosing, with the disk left out, in the same spirit as `groupByLineage` next door.
4090
+ *
4091
+ * Membership is tested against a group's **whole history** rather than its newest review. A pin is
4092
+ * written on whichever review the star was clicked on, so a review saved and then re-analysed
4093
+ * carries its pin on the older entry; asking only `latest` would lose the card at exactly the
4094
+ * moment the reviewer was most likely to want it.
3948
4095
  *
3949
- * The limit counts branches rather than reviews, and the grouping happens before it rather than
3950
- * after. Limiting first and grouping second would show a card claiming "2 reviews" for a branch
3951
- * that has five, which is worse than showing no count at all.
4096
+ * The kept side is capped for the same reason the recent side is: `queue.json` is a file on a disk
4097
+ * somebody can edit, and an uncapped read of it is an index with five thousand cards on it.
3952
4098
  */
3953
- function recentReviewGroups(limit = 10) {
3954
- return groupByLineage(recentWorkspaces(Number.MAX_SAFE_INTEGER)).slice(0, limit);
4099
+ function selectIndexGroups(groups, limit, keptLimit, keptIds) {
4100
+ const kept = [];
4101
+ const rest = [];
4102
+ for (const group of groups) (group.history.some((review) => keptIds.has(review.id)) ? kept : rest).push(group);
4103
+ return [...rest.slice(0, limit), ...kept.slice(0, keptLimit)].sort((a, b) => b.latest.createdAt - a.latest.createdAt);
3955
4104
  }
3956
4105
  /**
3957
4106
  * The grouping itself, with the disk left out.
@@ -4076,7 +4225,8 @@ function summarise(workspace) {
4076
4225
  paths: files.slice(0, MAX_INDEXED_PATHS).map((file) => file.name),
4077
4226
  prNumber: workspace.changeSet.data.pullRequest?.number || workspace.prContext?.pullRequest.number,
4078
4227
  commitSha: workspace.session.metadata?.commitSha || void 0,
4079
- author: describeAuthorship(workspace.changeSet, workspace.prContext?.pullRequest)?.label
4228
+ author: describeAuthorship(workspace.changeSet, workspace.prContext?.pullRequest)?.label,
4229
+ authorHandle: workspace.prContext?.pullRequest.author?.handle || void 0
4080
4230
  };
4081
4231
  }
4082
4232
  /**
@@ -4185,14 +4335,53 @@ function touchWorkspace(workspace) {
4185
4335
  saveWorkspace(workspace);
4186
4336
  } catch {}
4187
4337
  }
4338
+ /**
4339
+ * Housekeeping, in two tiers.
4340
+ *
4341
+ * The cheap gate comes first: anything inside the ordinary window is not a candidate under either
4342
+ * tier, so the great majority of files never reach the read below. Only those already past thirty
4343
+ * days pay for a parse -- and a parse here is over a record holding an entire diff, which is why
4344
+ * that ordering is load-bearing rather than tidy.
4345
+ *
4346
+ * The loop walks the directory without filtering by extension, which is what makes the suffix rule
4347
+ * matter: `<id>.tokens.jsonl` ages on its own mtime, and stripping only `.json` would leave a saved
4348
+ * review holding its record while its usage history was quietly deleted from under it. The tier
4349
+ * belongs to the review, not to the file.
4350
+ *
4351
+ * Exported for `retention.test.ts`. It is a real unit with a rule worth stating in cases, and the
4352
+ * alternative -- reaching it only through `createWorkspace` -- would test the retention policy
4353
+ * through a function that exists to do something else.
4354
+ */
4188
4355
  function pruneOldWorkspaces() {
4189
4356
  try {
4357
+ const queued = new Set(readQueue());
4190
4358
  for (const entry of readdirSync(WORKSPACES_DIR)) {
4191
4359
  const path = join(WORKSPACES_DIR, entry);
4192
- if (Date.now() - statSync(path).mtimeMs > MAX_AGE_MS) unlinkSync(path);
4360
+ const age = Date.now() - statSync(path).mtimeMs;
4361
+ if (age <= 2592e6) continue;
4362
+ if (age <= 7776e6 && isKept(entry, queued)) continue;
4363
+ unlinkSync(path);
4193
4364
  }
4194
4365
  } catch {}
4195
4366
  }
4367
+ /**
4368
+ * Whether the reviewer said to keep this one, asked of a directory entry rather than of a record.
4369
+ *
4370
+ * A record that will not parse cannot claim to be saved, so it falls to the ordinary window. That
4371
+ * is the safe direction: the alternative keeps unreadable files for three months on the strength of
4372
+ * a question nobody could answer.
4373
+ */
4374
+ function isKept(entry, queued) {
4375
+ const id = workspaceIdFrom(entry);
4376
+ if (id === null) return false;
4377
+ if (queued.has(id)) return true;
4378
+ return loadWorkspace(id)?.pinned === true;
4379
+ }
4380
+ /** The review an entry belongs to, whether it is the record or the usage sidecar beside it. */
4381
+ function workspaceIdFrom(entry) {
4382
+ for (const suffix of [".tokens.jsonl", ".json"]) if (entry.endsWith(suffix)) return entry.slice(0, -suffix.length);
4383
+ return null;
4384
+ }
4196
4385
  //#endregion
4197
4386
  //#region ../../packages/review-harness/src/workspace/exclusive_claim.ts
4198
4387
  /**
@@ -11383,4 +11572,4 @@ function lineageIds(id) {
11383
11572
  return [.../* @__PURE__ */ new Set([id, ...lineage.map((review) => review.id)])];
11384
11573
  }
11385
11574
  //#endregion
11386
- export { fillFileUrlTemplate as $, agentFor as $t, removeWorktree as A, workspaceRevision as At, workspaceUrl as B, resolveTarget as Bt, recordTelemetryUploadConsent as C, recentReviewGroups as Ct, ReviewBeingDeletedError as D, saveWorkspace as Dt, EXPLAIN_SIMPLY_PROMPT as E, reviewedRepositories as Et, clearServerRecord as F, issuesOutstanding as Ft, DEFAULT_WORKSPACE_PORT as G, clearAgentPreference as Gt, BUILD_VERSION as H, git as Ht, ensureServer as I, questionsOutstanding as It, parseWorkspacePort as J, CONFIG_PATH as Jt, MAX_WORKSPACE_PORT as K, readAgentPreference as Kt, readServerRecord as L, reviewerDispositions as Lt, relativeTime as M, doneVerb as Mt, readIndexToken as N, isIssueOutstanding as Nt, MANAGED_ROOT as O, summarise as Ot, bootstrapUrl as P, isQuestionOutstanding as Pt, forgeResolver as Q, agentById as Qt, reviewsUrl as R, processDiscussion as Rt, record as S, previousKissResult as St, withUsageRecorded as T, reviewedCommit as Tt, PACKAGE_NAME as U, resolveAgent as Ut, writeServerRecord as V, displayRef as Vt, feedbackUrl as W, resolveModel as Wt, statedWorkspacePort as X, AGENT_IDS as Xt, resolveWorkspacePort as Y, STORE_ROOT as Yt, writePortPreference as Z, DEFAULT_AGENT_ID as Zt, startKissJob as _, lineageKeyFor as _t, wasBlocked as a, isTerminal as at, startJob as b, matchingWorkspaceIds as bt, updateReview as c, PHASES as ct, askCheckout as d, purposeOf as dt, detectAgents as en, checkCodeFreshness as et, UpdateAlreadyRunningError as f, clearClaim as ft, recordKissRun as g, latestKissRun as gt, agentEnvOf as h, groupByLineage as ht, liveJobsFor as i, fail as it, modelHelpLines as j, workspaceStamps as jt, readMarker as k, touchWorkspace as kt, refreshPrContext as l, phasesFor as lt, runningJobs as m, followedRefName as mt, deleteReview as n, AgentUnavailableError as nn, allJobs as nt, discardJob as o, loadJob as ot, liveUpdateFor as p, isClaimed as pt, MIN_WORKSPACE_PORT as q, writeAgentPreference as qt, lineageIds as r, endedAt as rt, RefreshUnavailableError as s, saveJob as st, deleteLineage as t, AgentCancelledError as tn, checkFreshness as tt, isUnchanged as u, progressSteps as ut, reanalyseReview as v, lineagePosition as vt, telemetryUploadConsent as w, recentWorkspaces as wt, readEvents as x, positionInLineage as xt, runJob as y, loadWorkspace as yt, stopServer as z, describeAuthorship as zt };
11575
+ export { fillFileUrlTemplate as $, writeAgentPreference as $t, removeWorktree as A, workspaceRevision as At, workspaceUrl as B, isQuestionOutstanding as Bt, recordTelemetryUploadConsent as C, previousKissResult as Ct, ReviewBeingDeletedError as D, saveWorkspace as Dt, EXPLAIN_SIMPLY_PROMPT as E, reviewedRepositories as Et, clearServerRecord as F, queueStamp as Ft, DEFAULT_WORKSPACE_PORT as G, describeAuthorship as Gt, BUILD_VERSION as H, questionsOutstanding as Ht, ensureServer as I, readQueue as It, parseWorkspacePort as J, git as Jt, MAX_WORKSPACE_PORT as K, resolveTarget as Kt, readServerRecord as L, writeQueue as Lt, relativeTime as M, dequeueOnComplete as Mt, readIndexToken as N, queueAdd as Nt, MANAGED_ROOT as O, summarise as Ot, bootstrapUrl as P, queueRemove as Pt, forgeResolver as Q, readAgentPreference as Qt, reviewsUrl as R, doneVerb as Rt, record as S, positionInLineage as St, withUsageRecorded as T, reviewedCommit as Tt, PACKAGE_NAME as U, reviewerDispositions as Ut, writeServerRecord as V, issuesOutstanding as Vt, feedbackUrl as W, processDiscussion as Wt, statedWorkspacePort as X, resolveModel as Xt, resolveWorkspacePort as Y, resolveAgent as Yt, writePortPreference as Z, clearAgentPreference as Zt, startKissJob as _, latestKissRun as _t, wasBlocked as a, agentFor as an, isTerminal as at, startJob as b, loadWorkspace as bt, updateReview as c, AgentUnavailableError as cn, PHASES as ct, askCheckout as d, purposeOf as dt, CONFIG_PATH as en, checkCodeFreshness as et, UpdateAlreadyRunningError as f, clearClaim as ft, recordKissRun as g, indexReviewGroups as gt, agentEnvOf as h, groupByLineage as ht, liveJobsFor as i, agentById as in, fail as it, modelHelpLines as j, workspaceStamps as jt, readMarker as k, touchWorkspace as kt, refreshPrContext as l, phasesFor as lt, runningJobs as m, followedRefName as mt, deleteReview as n, AGENT_IDS as nn, allJobs as nt, discardJob as o, detectAgents as on, loadJob as ot, liveUpdateFor as p, isClaimed as pt, MIN_WORKSPACE_PORT as q, displayRef as qt, lineageIds as r, DEFAULT_AGENT_ID as rn, endedAt as rt, RefreshUnavailableError as s, AgentCancelledError as sn, saveJob as st, deleteLineage as t, STORE_ROOT as tn, checkFreshness as tt, isUnchanged as u, progressSteps as ut, reanalyseReview as v, lineageKeyFor as vt, telemetryUploadConsent as w, recentWorkspaces as wt, readEvents as x, matchingWorkspaceIds as xt, runJob as y, lineagePosition as yt, stopServer as z, isIssueOutstanding as zt };
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { A as removeWorktree, B as workspaceUrl, Bt as resolveTarget, C as recordTelemetryUploadConsent, Dt as saveWorkspace, Et as reviewedRepositories, G as DEFAULT_WORKSPACE_PORT, H as BUILD_VERSION, Ht as git, I as ensureServer, J as parseWorkspacePort, Jt as CONFIG_PATH, K as MAX_WORKSPACE_PORT, Kt as readAgentPreference, M as relativeTime, N as readIndexToken, O as MANAGED_ROOT, Ot as summarise, P as bootstrapUrl, Q as forgeResolver, Qt as agentById, R as reviewsUrl, S as record, U as PACKAGE_NAME, Ut as resolveAgent, Vt as displayRef, Wt as resolveModel, X as statedWorkspacePort, Xt as AGENT_IDS, Yt as STORE_ROOT, Z as writePortPreference, Zt as DEFAULT_AGENT_ID, _t as lineageKeyFor, a as wasBlocked, at as isTerminal, b as startJob, bt as matchingWorkspaceIds, c as updateReview, ct as PHASES, dt as purposeOf, en as detectAgents, f as UpdateAlreadyRunningError, ft as clearClaim, g as recordKissRun, ht as groupByLineage, i as liveJobsFor, j as modelHelpLines, k as readMarker, m as runningJobs, mt as followedRefName, n as deleteReview$1, nt as allJobs, ot as loadJob, p as liveUpdateFor, pt as isClaimed, q as MIN_WORKSPACE_PORT, qt as writeAgentPreference, tt as checkFreshness, ut as progressSteps, v as reanalyseReview, w as telemetryUploadConsent, wt as recentWorkspaces, xt as positionInLineage, y as runJob, yt as loadWorkspace, z as stopServer } from "./delete_review-B3xciJ03.js";
2
+ import { $t as writeAgentPreference, A as removeWorktree, B as workspaceUrl, C as recordTelemetryUploadConsent, Dt as saveWorkspace, Et as reviewedRepositories, G as DEFAULT_WORKSPACE_PORT, H as BUILD_VERSION, I as ensureServer, J as parseWorkspacePort, Jt as git, K as MAX_WORKSPACE_PORT, Kt as resolveTarget, M as relativeTime, N as readIndexToken, O as MANAGED_ROOT, Ot as summarise, P as bootstrapUrl, Q as forgeResolver, Qt as readAgentPreference, R as reviewsUrl, S as record, St as positionInLineage, U as PACKAGE_NAME, X as statedWorkspacePort, Xt as resolveModel, Yt as resolveAgent, Z as writePortPreference, a as wasBlocked, at as isTerminal, b as startJob, bt as loadWorkspace, c as updateReview, ct as PHASES, dt as purposeOf, en as CONFIG_PATH, f as UpdateAlreadyRunningError, ft as clearClaim, g as recordKissRun, ht as groupByLineage, i as liveJobsFor, in as agentById, j as modelHelpLines, k as readMarker, m as runningJobs, mt as followedRefName, n as deleteReview$1, nn as AGENT_IDS, nt as allJobs, on as detectAgents, ot as loadJob, p as liveUpdateFor, pt as isClaimed, q as MIN_WORKSPACE_PORT, qt as displayRef, rn as DEFAULT_AGENT_ID, tn as STORE_ROOT, tt as checkFreshness, ut as progressSteps, v as reanalyseReview, vt as lineageKeyFor, w as telemetryUploadConsent, wt as recentWorkspaces, xt as matchingWorkspaceIds, y as runJob, z as stopServer } from "./delete_review-DUCSC5Bc.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";