prreviewbuddy 0.80.3 → 0.81.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,24 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.81.0
4
+
5
+ **Gemini CLI is turned off.** PR Review Buddy no longer runs reviews, updates, Buddy AI answers,
6
+ KISS reviews or repository maps with Gemini CLI. Google has ended Gemini CLI access for individual
7
+ accounts, and current Gemini CLI releases ignore the settings that kept it read-only, so a run could
8
+ no longer be guaranteed to leave your code alone. It is gone from every agent list. Naming it with
9
+ `--agent gemini`, or having it saved as your default, stops before anything starts and says to use
10
+ Claude Code or Codex instead.
11
+
12
+ Reviews Gemini made are kept as they are, still labelled Gemini CLI. They can't be updated or asked
13
+ about, because both continue with the agent that made the review. To carry one on, analyse it again
14
+ with `--reanalyse --agent claude` or `--agent codex`; the Gemini analysis stays as the one it
15
+ replaced. A Gemini review that was queued or stopped is refused before its checkout is made, without
16
+ a Retry button. If Settings has Gemini CLI saved as the default, it shows that and asks you to
17
+ choose another.
18
+
19
+ The Gemini provider for Buddy AI in the browser extension is unaffected: it calls Google's API with
20
+ your own key and never runs Gemini CLI.
21
+
3
22
  ## 0.80.3
4
23
 
5
24
  **Runs name the agent that did the work.** Updating a review, and the checkout Buddy AI answers
package/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  A local review workspace that helps you understand code changes before you approve them.
6
6
 
7
- Have Claude Code, Codex or Gemini CLI analyse a branch or pull request, then explore what it
7
+ Have Claude Code or Codex analyse a branch or pull request, then explore what it
8
8
  found, the evidence behind it and your own follow-up questions in your browser.
9
9
 
10
10
  ![A review in the workspace: why the change was made, what it changes, its risk and progress, a suggested review path, and Buddy AI beside it](https://prreviewbuddy.com/npm/workspace.png)
@@ -69,9 +69,8 @@ being reviewed, so your working tree is never touched and you can carry on worki
69
69
  ## Local by design
70
70
 
71
71
  PR Review Buddy uses a coding agent already installed on your machine, and that agent's existing
72
- authentication: [Claude Code](https://claude.com/claude-code),
73
- [Codex CLI](https://developers.openai.com/codex/cli) or
74
- [Gemini CLI](https://github.com/google-gemini/gemini-cli). There is no PR Review Buddy account and no
72
+ authentication: [Claude Code](https://claude.com/claude-code) or
73
+ [Codex CLI](https://developers.openai.com/codex/cli). There is no PR Review Buddy account and no
75
74
  model API key.
76
75
 
77
76
  Your code goes to the agent you chose, exactly as if you had run it yourself. It does not pass
@@ -1274,22 +1274,115 @@ var AGENTS = {
1274
1274
  [GEMINI_AGENT_ID]: geminiAgent,
1275
1275
  [CODEX_AGENT_ID]: codexAgent
1276
1276
  };
1277
+ /**
1278
+ * Adapters this build ships and will not run, each with the sentence a person is shown instead.
1279
+ *
1280
+ * A parked id is still known: reviews it made keep their name and their history, telemetry still
1281
+ * recognises it, and a job queued under it is refused by name rather than treated as an agent this
1282
+ * build has never heard of. What it loses is everything that would start it. `agentById` throws
1283
+ * before an adapter is handed out, so no run site can reach one, and every list a person chooses
1284
+ * from is built from `AGENT_IDS`, which leaves it out.
1285
+ *
1286
+ * Gemini CLI, since 0.81.0. Its read-only boundary is not applied on current releases (the system
1287
+ * settings file that carried it is skipped unless root owns it), and Google has ended Gemini CLI
1288
+ * for individual accounts. `index.test.ts` holds a tripwire against unparking it before the adapter
1289
+ * enforces its boundary again.
1290
+ */
1291
+ var PARKED = { [GEMINI_AGENT_ID]: "Gemini CLI is turned off in this version of PR Review Buddy. Google has ended Gemini CLI access for individual accounts, and its read-only mode can't be guaranteed on current versions." };
1277
1292
  var DEFAULT_AGENT_ID = CLAUDE_AGENT_ID;
1278
1293
  /**
1279
1294
  * Every agent this build can run, for a surface that has to say which ones those are.
1280
1295
  *
1281
1296
  * Derived from the registry rather than written out, so a second adapter appears in the sentence a
1282
- * user reads by being registered, and nobody has to remember a list in another package.
1297
+ * user reads by being registered, and nobody has to remember a list in another package. Parked
1298
+ * adapters are left out, which is how they leave every picker, listing and help sentence at once.
1299
+ */
1300
+ var AGENT_IDS = Object.keys(AGENTS).filter((id) => !(id in PARKED));
1301
+ /**
1302
+ * Whether a string names an agent this build ships, answered by the registry rather than a list.
1303
+ *
1304
+ * True for a parked agent too. This answers "do we know this id", which is what a stored review, a
1305
+ * job record and telemetry need; "can it run" is `AGENT_IDS` and `agentById`.
1283
1306
  */
1284
- var AGENT_IDS = Object.keys(AGENTS);
1285
- /** Whether a string names an agent this build ships, answered by the registry rather than a list. */
1286
1307
  function isAgentId(value) {
1287
1308
  return value !== void 0 && value in AGENTS;
1288
1309
  }
1310
+ /**
1311
+ * Why this agent will not run in this build, or undefined when nothing stops it here.
1312
+ *
1313
+ * For a surface that has to tell a person something more useful than "unknown agent" when they
1314
+ * name one that is parked.
1315
+ */
1316
+ function parkedReason(id) {
1317
+ if (id === void 0 || !Object.hasOwn(PARKED, id)) return void 0;
1318
+ return `${PARKED[id]} Use ${runnableAgents().names} instead.`;
1319
+ }
1320
+ /**
1321
+ * The agents that can run, as a person reads them ("Claude Code or Codex") and as `--agent` takes
1322
+ * them ("`--agent claude` or `--agent codex`"), for a refusal that has to say what to do instead.
1323
+ *
1324
+ * Built from `AGENT_IDS` so no module outside this folder spells an agent out, and so the advice
1325
+ * stays true when an adapter is parked or added.
1326
+ */
1327
+ function runnableAgents() {
1328
+ const ids = [...AGENT_IDS].sort();
1329
+ return {
1330
+ names: orList(ids.map((id) => AGENTS[id].displayName)),
1331
+ flags: orList(ids.map((id) => `\`--agent ${id}\``))
1332
+ };
1333
+ }
1334
+ function orList(items) {
1335
+ if (items.length <= 1) return items[0] ?? "";
1336
+ return `${items.slice(0, -1).join(", ")} or ${items[items.length - 1]}`;
1337
+ }
1338
+ /**
1339
+ * A run was asked of a parked agent. An `AgentUnavailableError`, so every caller that already
1340
+ * answers "you have not got this agent" answers this with the parked sentence instead.
1341
+ */
1342
+ var AgentParkedError = class extends AgentUnavailableError {
1343
+ agentId;
1344
+ constructor(agentId, reason) {
1345
+ super(reason);
1346
+ this.agentId = agentId;
1347
+ }
1348
+ };
1349
+ /**
1350
+ * Who an agent is, without the means to run it. For a parked agent, which `agentById` refuses, when
1351
+ * a surface has to name it: a refusal that says "Gemini CLI" rather than "gemini".
1352
+ */
1353
+ function agentIdentityOf(id) {
1354
+ const { displayName, provider, modelSelectionHelp, setupNotes } = AGENTS[id];
1355
+ return {
1356
+ id,
1357
+ displayName,
1358
+ provider,
1359
+ ...modelSelectionHelp ? { modelSelectionHelp } : {},
1360
+ ...setupNotes ? { setupNotes } : {}
1361
+ };
1362
+ }
1363
+ /**
1364
+ * The environment a review made by this agent is pinned to, captured now.
1365
+ *
1366
+ * Works for a parked agent, because capturing starts nothing: it reads variables. A review made by
1367
+ * one still has a forge to watch and a conversation to re-read, under the account that made it, and
1368
+ * an agent run started from what this returns is refused at `agentFor` like any other.
1369
+ */
1370
+ function captureEnvFor(id, source) {
1371
+ return AGENTS[id].captureEnv(source);
1372
+ }
1289
1373
  function defaultAgent() {
1290
1374
  return agentById(DEFAULT_AGENT_ID);
1291
1375
  }
1376
+ /**
1377
+ * The adapter for this id, and the one way any run reaches one.
1378
+ *
1379
+ * A parked id throws here rather than handing out something that refuses later. Several callers
1380
+ * write before they run (a new job, a retired analysis, a KISS run), so a refusal at `run()` would
1381
+ * already have replaced what the reviewer was reading with a failure.
1382
+ */
1292
1383
  function agentById(id) {
1384
+ const reason = parkedReason(id);
1385
+ if (reason) throw new AgentParkedError(id, reason);
1293
1386
  const agent = AGENTS[id];
1294
1387
  if (!agent) throw new AgentUnavailableError(`This review was made with an agent called "${id}", which this version does not know how to run.`);
1295
1388
  return agent;
@@ -1346,7 +1439,7 @@ function captureEnv(source) {
1346
1439
  * against fakes that fail in ways no installed binary would.
1347
1440
  */
1348
1441
  async function detectAgents() {
1349
- return detectAgents$1(Object.values(AGENTS));
1442
+ return detectAgents$1(AGENT_IDS.map((id) => AGENTS[id]));
1350
1443
  }
1351
1444
  //#endregion
1352
1445
  //#region ../../packages/review-harness/src/workspace/store_root.ts
@@ -1724,7 +1817,7 @@ function countDiscussion(context) {
1724
1817
  async function resolveAgent(request) {
1725
1818
  if (request.flag) return named(request.flag, "flag");
1726
1819
  const fromReview = request.review ? agentIdOf(request.review.session) : null;
1727
- if (fromReview && AGENT_IDS.includes(fromReview)) return named(fromReview, "review");
1820
+ if (fromReview && isAgentId(fromReview)) return named(fromReview, "review");
1728
1821
  const preferred = readAgentPreference();
1729
1822
  if (preferred) return named(preferred, "preference");
1730
1823
  const installed = (await detectAgents()).filter((agent) => agent.state === "installed");
@@ -1736,6 +1829,13 @@ async function resolveAgent(request) {
1736
1829
  };
1737
1830
  }
1738
1831
  async function named(id, because) {
1832
+ const parked = parkedReason(id);
1833
+ if (parked && isAgentId(id)) return {
1834
+ kind: "unavailable",
1835
+ agent: agentIdentityOf(id),
1836
+ reason: parked,
1837
+ parked: true
1838
+ };
1739
1839
  if (!AGENT_IDS.includes(id)) return {
1740
1840
  kind: "unknown-id",
1741
1841
  id
@@ -7177,7 +7277,7 @@ function advance(job, phase, progress) {
7177
7277
  ...phase === "done" ? { finishedAt: Date.now() } : {}
7178
7278
  };
7179
7279
  }
7180
- function fail(job, message, kind, detail) {
7280
+ function fail(job, message, kind, detail, parked) {
7181
7281
  return {
7182
7282
  ...job,
7183
7283
  phase: "failed",
@@ -7185,7 +7285,8 @@ function fail(job, message, kind, detail) {
7185
7285
  phase: job.phase,
7186
7286
  message,
7187
7287
  ...detail && detail !== message ? { detail } : {},
7188
- ...kind ? { kind } : {}
7288
+ ...kind ? { kind } : {},
7289
+ ...parked ? { parked } : {}
7189
7290
  },
7190
7291
  updatedAt: Date.now(),
7191
7292
  finishedAt: Date.now()
@@ -7212,11 +7313,11 @@ function fail(job, message, kind, detail) {
7212
7313
  * last thing the agent said survives being failed. Only a driver whose own claim has gone is
7213
7314
  * refused, so a job nobody holds -- a crashed driver's, a resumed run's -- is still failable.
7214
7315
  */
7215
- function recordFailure(job, message, driver, kind, detail) {
7316
+ function recordFailure(job, message, driver, kind, detail, parked) {
7216
7317
  const current = loadJob(job.id) ?? job;
7217
7318
  const claim = liveClaim(job.id);
7218
7319
  if (claim && claim.holder !== driver) return current;
7219
- const failed = fail(current, message, kind, detail);
7320
+ const failed = fail(current, message, kind, detail, parked);
7220
7321
  saveJob(failed);
7221
7322
  return failed;
7222
7323
  }
@@ -12970,7 +13071,7 @@ function agentEnvOf(workspace) {
12970
13071
  const job = workspace.jobId ? loadJob(workspace.jobId) : null;
12971
13072
  if (job?.env) return job.env;
12972
13073
  const id = agentIdOf(workspace.session);
12973
- return (AGENT_IDS.includes(id) ? agentById(id) : defaultAgent()).captureEnv();
13074
+ return isAgentId(id) ? captureEnvFor(id) : defaultAgent().captureEnv();
12974
13075
  }
12975
13076
  //#endregion
12976
13077
  //#region ../../packages/review-harness/src/domain/discussion/discussion_attribution.ts
@@ -16370,6 +16471,8 @@ async function drive(jobId, workspaceId, runners, driver, signal, source) {
16370
16471
  saveJob(job);
16371
16472
  try {
16372
16473
  signal?.throwIfAborted();
16474
+ const parked = parkedReason(job.env.agentId);
16475
+ if (parked) throw new AgentParkedError(job.env.agentId, parked);
16373
16476
  if (needs(from, "preparing")) {
16374
16477
  job = update(job, "preparing", "Preparing an isolated checkout");
16375
16478
  const path = await createWorktree({
@@ -16494,7 +16597,7 @@ async function drive(jobId, workspaceId, runners, driver, signal, source) {
16494
16597
  }
16495
16598
  const kind = error instanceof GuideRejected || error instanceof KissRejected ? "agent_output_invalid" : error instanceof AgentRunError ? error.kind : void 0;
16496
16599
  const raw = message(error);
16497
- job = recordFailure(job, explain(job, raw), driver, kind, raw);
16600
+ job = recordFailure(job, explain(job, raw), driver, kind, raw, error instanceof AgentParkedError ? true : void 0);
16498
16601
  if (job.reviewType === "kiss") failCurrentKissRun(workspaceId, job, job.failure?.message ?? raw, kind, error instanceof KissRejected ? error.answer : void 0);
16499
16602
  finished(job, workspaceId, startedAt, false, now);
16500
16603
  announceJob(job);
@@ -17885,7 +17988,7 @@ async function recordKissRun(input) {
17885
17988
  if (!workspace) throw new Error("No such review.");
17886
17989
  const sha = reviewedCommit(workspace);
17887
17990
  if (!sha) throw new Error("This review does not record which commit it describes, so KISS cannot run on it.");
17888
- const agent = input.agentId ? agentById(input.agentId) : workspace.jobId ? agentById(agentIdOf(workspace.session)) : defaultAgent();
17991
+ const agent = agentById(kissAgentId(workspace, input.agentId));
17889
17992
  const sameAgent = !workspace.jobId || agent.id === agentIdOf(workspace.session);
17890
17993
  const requestedModel = input.requestedModel ?? (sameAgent ? resolveModel({ review: workspace }) : null);
17891
17994
  const started = await withReviewLock(workspace.id, () => {
@@ -17937,6 +18040,16 @@ async function recordKissRun(input) {
17937
18040
  function startKissJob(input, runners = {}) {
17938
18041
  return track(recordKissRun(input).then((job) => runJob(job.workspaceId, runners, "kiss")));
17939
18042
  }
18043
+ /**
18044
+ * Which agent a KISS run of this review runs with: the one named, else the standard review's own,
18045
+ * else the default when there has been no standard review.
18046
+ *
18047
+ * Its own function so the page can refuse a run before answering. The route starts the run and
18048
+ * replies without waiting for it, so a refusal thrown inside the run never reached the page.
18049
+ */
18050
+ function kissAgentId(workspace, agentId) {
18051
+ return agentId ?? (workspace.jobId ? agentIdOf(workspace.session) : DEFAULT_AGENT_ID);
18052
+ }
17940
18053
  //#endregion
17941
18054
  //#region ../../packages/review-harness/src/domain/ai_review/reconciliation.ts
17942
18055
  /**
@@ -18829,6 +18942,17 @@ var RefreshUnavailableError = class extends Error {};
18829
18942
  var FETCH_TIMEOUT_MS = 6e4;
18830
18943
  /** Said wherever an update is asked of a commit review. One sentence, so both surfaces agree. */
18831
18944
  var COMMIT_REVIEW_CANNOT_UPDATE = "This review is of one commit, and a commit cannot move, so there is nothing to bring it up to date with. To analyse the same commit again, reanalyse it instead.";
18945
+ /**
18946
+ * Why this review cannot be updated in this build, when the agent it would continue with is parked.
18947
+ *
18948
+ * Read from `agentEnvOf`, which is what the update's job would be pinned to, so this refuses
18949
+ * exactly the updates that would otherwise be refused at the run.
18950
+ */
18951
+ function parkedUpdateRefusal(workspace) {
18952
+ const reason = parkedReason(agentEnvOf(workspace).agentId);
18953
+ if (!reason) return null;
18954
+ return `${reason} An update continues with the agent that made this review, so this one can't be brought up to date. To carry on with another agent, analyse it again with ${runnableAgents().names}.`;
18955
+ }
18832
18956
  /** The installed agent's own answer, through the `agentFor` this module imports. */
18833
18957
  var installedAgentVersion = (env) => agentVersionOf(agentFor(env), env);
18834
18958
  /** What the notice says after a cancel, and what the terminal would print if it could cancel. */
@@ -18864,6 +18988,8 @@ async function updateReview(workspace, runners = {}) {
18864
18988
  const startedAt = now();
18865
18989
  const reviewedSha = workspace.session.metadata?.commitSha || null;
18866
18990
  if (workspace.changeSet.targetType === "commit") return refuse(reviewedSha, false, COMMIT_REVIEW_CANNOT_UPDATE);
18991
+ const parked = parkedUpdateRefusal(workspace);
18992
+ if (parked) return refuse(reviewedSha, false, parked);
18867
18993
  const held = await claimCheckoutJob(workspace, reviewedSha ?? "", {
18868
18994
  purpose: "update",
18869
18995
  ...runners.source ? { source: runners.source } : {},
@@ -19753,4 +19879,4 @@ function lineageIds(id) {
19753
19879
  return [.../* @__PURE__ */ new Set([id, ...lineage.map((review) => review.id)])];
19754
19880
  }
19755
19881
  //#endregion
19756
- export { resolveWorkspacePort as $, AgentUnavailableError as $i, attemptIdOf as $n, isIssueOutstanding as $r, moveIds as $t, parsePullRequestLink as A, resolveTarget as Ai, modelHelpLines as An, recentWorkspaces as Ar, readArchitecture as At, ensureServer as B, resolveAgent as Bi, requestJobStop as Bn, isLifecycle as Br, writeJsonAtomically as Bt, completeRun as C, followUpFindingId as Ci, hasAuthorContent as Cn, lineagePosition as Cr, RUNTIME_WORDS as Ct, relativeTime as D, describeAuthorship as Di, CODE_COPY_CLASS as Dn, positionInLineage as Dr, checkArchitecture as Dt, stopRun as E, objectWithAnyOf as Ei, CODE_BLOCK_CLASS as En, mutateWorkspace as Er, ARCHITECTURE_DIR as Et, queueForReview as F, AmbiguousRefError as Fi, createWorktree as Fn, workspaceRevision as Fr, ownersOf as Ft, stopChild as G, CONFIG_PATH as Gi, checkReview as Gn, parseLifecycle as Gr, createArchitectureNotification as Gt, reviewsUrl as H, clearAgentPreference as Hi, describeSubject as Hn, lifecycleItemLabel as Hr, branchOf as Ht, settleQueue as I, shortNameOf as Ii, holdWorktree as In, workspaceStamps as Ir, QUOTE_LIMITS as It, writeServerRecord as J, agentById as Ji, fetchHead as Jn, queueAddMany as Jr, eventName as Jt, stopServer as K, STORE_ROOT as Ki, followedBranch as Kn, sectionOf as Kr, createPrState as Kt, readIndexToken as L, shorthandOf as Li, isWorktreeHeld as Ln, LIFECYCLES as Lr, REPOSITORY_SCOPE as Lt, runBatch as M, DetachedHeadError as Mi, isBusy as Mn, reviewedRepositories as Mr, storeArchitecture as Mt, activateQueued as N, isCommitReview as Ni, withReviewLock as Nn, summarise as Nr, storedScopes as Nt, batchIdentity as O, WITHHELD_REASON as Oi, escapeHtml as On, presentedKissRunOf as Or, loadArchitecture as Ot, compactQueue as P, resolveBase as Pi, MANAGED_ROOT as Pn, touchWorkspace as Pr, trackedFiles as Pt, parseWorkspacePort as Q, AgentCancelledError as Qi, allJobs as Qn, doneVerb as Qr, markReadForReview as Qt, bootstrapUrl as R, git as Ri, readMarker as Rn, activityAttributes as Rr, ancestorsOf as Rt, beginRun as S, wasReassessed as Si, authorPreview as Sn, latestKissRun as Sr, recordRuntimeBestEffort as St, reportRun as T, extractJsonText as Ti, renderMarkdown as Tn, matchingWorkspaceIds as Tr, formatDuration as Tt, serverEntryExists as U, readAgentPreference as Ui, findExistingReview as Un, lifecyclePill as Ur, subjectOf as Ut, readServerRecord as V, resolveModel as Vi, describeMovement as Vn, lifecycleChip as Vr, announceJob as Vt, spawnServer as W, writeAgentPreference as Wi, checkCodeFreshness as Wn, offersLifecycle as Wr, categoryOf as Wt, MAX_WORKSPACE_PORT as X, detectAgents as Xi, fillFileUrlTemplate as Xn, queueStamp as Xr, markAllRead as Xt, DEFAULT_WORKSPACE_PORT as Y, agentFor as Yi, forgeResolver as Yn, queueRemove as Yr, listNotifications as Yt, MIN_WORKSPACE_PORT as Z, isAgentId as Zi, RUN_RECORD_RETENTION_MS as Zn, readQueue as Zr, markRead as Zt, recordKissRun as _, gradeScale as _i, isUnchanged as _n, groupByLineage as _r, BUILD_VERSION as _t, wasBlocked as a, processDiscussion as ai, toneOf as an, saveJob as ar, settleReview as at, track as b, reassessIn as bi, chatConversationSection as bn, kissDecisionRefusal as br, waitingHistory as bt, RefreshUnavailableError as c, explanationOf as ci, withUsageRecorded as cn, isTerminalJobPhase as cr, startJob as ct, mergeUpdateInto as d, ANALYSIS_ONLY as di, adoptLegacyBaseline as dn, purposeOf as dr, record as dt, isQuestionOutstanding as ei, pruneNotifications as en, endedAt as er, statedWorkspacePort as et, updateReview as f, analysisRefOf as fi, prepareConversationWithin as fn, clearClaim as fr, stamp as ft, runningJobs as g, findingIn as gi, refreshPrContext as gn, followedRefName as gr, telemetryUploadEnabled as gt, liveUpdateFor as h, findingAssessment as hi, unforeseenFailure as hn, decideKissRecommendation as hr, telemetryUploadConsent as ht, liveJobsFor as i, reviewerDispositions as ii, retireNotification as in, pruneJobResidue as ir, prepareReview as it, unsupportedReason as j, originRemote as ji, ReviewBeingDeletedError as jn, reviewedCommit as jr, repositoryKey as jt, extractPullRequestLinks as k, kissOutcome as ki, renderInlineText as kn, previousKissResult as kr, partFiles as kt, cancelUpdate as l, isExplanationAction as li, existingPaths as ln, phasesFor as lr, append as lt, UpdateAlreadyRunningError as m, currentGradeIn as mi, storedConversation as mn, CLAIM_LEASE_MS as mr, recordTelemetryUploadConsent as mt, deleteReview as n, questionsOutstanding as ni, readStateOf as nn, isTerminal as nr, failureOf as nt, discardJob as o, recommendationsOf as oi, unreadAround as on, PHASES as or, reanalyseReview as ot, askCheckout as p, assessmentLookup as pi, saveRefresh as pn, isClaimed as pr, deliver as pt, workspaceUrl as q, AGENT_IDS as qi, reviewedShaOf as qn, queueAdd as qr, createPrUpdate as qt, lineageIds as r, resolvedByConversation as ri, removeNotification as rn, loadJob as rr, observeReview as rt, FETCH_TIMEOUT_MS as s, explanationActionOf as si, unreadCount as sn, isLiveJob as sr, runJob as st, deleteLineage as t, issuesOutstanding as ti, readNotification as tn, fail as tr, writePortPreference as tt, cancellableHere as u, withExplanation as ui, ASK_REREAD_CEILING_MS as un, progressSteps as ur, readEvents as ut, startKissJob as v, isGradeFor as vi, agentEnvOf as vn, indexKeptIds as vr, PACKAGE_NAME as vt, endRun as w, asString$1 as wi, GITHUB_MARKDOWN as wn, loadWorkspace as wr, describeRuntimes as wt, RUN_KINDS as x, sourcePhrase as xi, UNESTABLISHED_MOTIVATION as xn, kissDecisionsOf as xr, mapRuntimes as xt, isDriving as y, promptGrade as yi, EXPLAIN_SIMPLY_PROMPT as yn, indexReviewGroups as yr, feedbackUrl as yt, clearServerRecordOwnedBy as z, refExists as zi, removeWorktree as zn, activityLabel as zr, isArchitectureId as zt };
19882
+ export { MIN_WORKSPACE_PORT as $, agentIdentityOf as $i, RUN_RECORD_RETENTION_MS as $n, readQueue as $r, markRead as $t, batchIdentity as A, WITHHELD_REASON as Ai, escapeHtml as An, presentedKissRunOf as Ar, loadArchitecture as At, bootstrapUrl as B, git as Bi, readMarker as Bn, activityAttributes as Br, ancestorsOf as Bt, RUN_KINDS as C, sourcePhrase as Ci, UNESTABLISHED_MOTIVATION as Cn, kissDecisionsOf as Cr, mapRuntimes as Ct, reportRun as D, extractJsonText as Di, renderMarkdown as Dn, matchingWorkspaceIds as Dr, formatDuration as Dt, endRun as E, asString$1 as Ei, GITHUB_MARKDOWN as En, loadWorkspace as Er, describeRuntimes as Et, activateQueued as F, isCommitReview as Fi, withReviewLock as Fn, summarise as Fr, storedScopes as Ft, serverEntryExists as G, clearAgentPreference as Gi, findExistingReview as Gn, lifecyclePill as Gr, subjectOf as Gt, ensureServer as H, resolveAgent as Hi, requestJobStop as Hn, isLifecycle as Hr, writeJsonAtomically as Ht, compactQueue as I, resolveBase as Ii, MANAGED_ROOT as In, touchWorkspace as Ir, trackedFiles as It, stopServer as J, CONFIG_PATH as Ji, followedBranch as Jn, sectionOf as Jr, createPrState as Jt, spawnServer as K, readAgentPreference as Ki, checkCodeFreshness as Kn, offersLifecycle as Kr, categoryOf as Kt, queueForReview as L, AmbiguousRefError as Li, createWorktree as Ln, workspaceRevision as Lr, ownersOf as Lt, parsePullRequestLink as M, resolveTarget as Mi, modelHelpLines as Mn, recentWorkspaces as Mr, readArchitecture as Mt, unsupportedReason as N, originRemote as Ni, ReviewBeingDeletedError as Nn, reviewedCommit as Nr, repositoryKey as Nt, stopRun as O, objectWithAnyOf as Oi, CODE_BLOCK_CLASS as On, mutateWorkspace as Or, ARCHITECTURE_DIR as Ot, runBatch as P, DetachedHeadError as Pi, isBusy as Pn, reviewedRepositories as Pr, storeArchitecture as Pt, MAX_WORKSPACE_PORT as Q, agentFor as Qi, fillFileUrlTemplate as Qn, queueStamp as Qr, markAllRead as Qt, settleQueue as R, shortNameOf as Ri, holdWorktree as Rn, workspaceStamps as Rr, QUOTE_LIMITS as Rt, track as S, reassessIn as Si, chatConversationSection as Sn, kissDecisionRefusal as Sr, waitingHistory as St, completeRun as T, followUpFindingId as Ti, hasAuthorContent as Tn, lineagePosition as Tr, RUNTIME_WORDS as Tt, readServerRecord as U, resolveModel as Ui, describeMovement as Un, lifecycleChip as Ur, announceJob as Ut, clearServerRecordOwnedBy as V, refExists as Vi, removeWorktree as Vn, activityLabel as Vr, isArchitectureId as Vt, reviewsUrl as W, agentIdOf as Wi, describeSubject as Wn, lifecycleItemLabel as Wr, branchOf as Wt, writeServerRecord as X, AGENT_IDS as Xi, fetchHead as Xn, queueAddMany as Xr, eventName as Xt, workspaceUrl as Y, STORE_ROOT as Yi, reviewedShaOf as Yn, queueAdd as Yr, createPrUpdate as Yt, DEFAULT_WORKSPACE_PORT as Z, agentById as Zi, forgeResolver as Zn, queueRemove as Zr, listNotifications as Zt, runningJobs as _, findingAssessment as _i, unforeseenFailure as _n, decideKissRecommendation as _r, telemetryUploadConsent as _t, wasBlocked as a, AgentUnavailableError as aa, resolvedByConversation as ai, removeNotification as an, loadJob as ar, observeReview as at, startKissJob as b, isGradeFor as bi, agentEnvOf as bn, indexKeptIds as br, PACKAGE_NAME as bt, RefreshUnavailableError as c, recommendationsOf as ci, unreadAround as cn, PHASES as cr, reanalyseReview as ct, mergeUpdateInto as d, isExplanationAction as di, existingPaths as dn, phasesFor as dr, append as dt, detectAgents as ea, doneVerb as ei, markReadForReview as en, allJobs as er, parseWorkspacePort as et, parkedUpdateRefusal as f, withExplanation as fi, ASK_REREAD_CEILING_MS as fn, progressSteps as fr, readEvents as ft, liveUpdateFor as g, currentGradeIn as gi, storedConversation as gn, CLAIM_LEASE_MS as gr, recordTelemetryUploadConsent as gt, UpdateAlreadyRunningError as h, assessmentLookup as hi, saveRefresh as hn, isClaimed as hr, deliver as ht, liveJobsFor as i, AgentCancelledError as ia, questionsOutstanding as ii, readStateOf as in, isTerminal as ir, failureOf as it, extractPullRequestLinks as j, kissOutcome as ji, renderInlineText as jn, previousKissResult as jr, partFiles as jt, relativeTime as k, describeAuthorship as ki, CODE_COPY_CLASS as kn, positionInLineage as kr, checkArchitecture as kt, cancelUpdate as l, explanationActionOf as li, unreadCount as ln, isLiveJob as lr, runJob as lt, askCheckout as m, analysisRefOf as mi, prepareConversationWithin as mn, clearClaim as mr, stamp as mt, deleteReview as n, parkedReason as na, isQuestionOutstanding as ni, pruneNotifications as nn, endedAt as nr, statedWorkspacePort as nt, discardJob as o, reviewerDispositions as oi, retireNotification as on, pruneJobResidue as or, prepareReview as ot, updateReview as p, ANALYSIS_ONLY as pi, adoptLegacyBaseline as pn, purposeOf as pr, record as pt, stopChild as q, writeAgentPreference as qi, checkReview as qn, parseLifecycle as qr, createArchitectureNotification as qt, lineageIds as r, runnableAgents as ra, issuesOutstanding as ri, readNotification as rn, fail as rr, writePortPreference as rt, FETCH_TIMEOUT_MS as s, processDiscussion as si, toneOf as sn, saveJob as sr, settleReview as st, deleteLineage as t, isAgentId as ta, isIssueOutstanding as ti, moveIds as tn, attemptIdOf as tr, resolveWorkspacePort as tt, cancellableHere as u, explanationOf as ui, withUsageRecorded as un, isTerminalJobPhase as ur, startJob as ut, kissAgentId as v, findingIn as vi, refreshPrContext as vn, followedRefName as vr, telemetryUploadEnabled as vt, beginRun as w, wasReassessed as wi, authorPreview as wn, latestKissRun as wr, recordRuntimeBestEffort as wt, isDriving as x, promptGrade as xi, EXPLAIN_SIMPLY_PROMPT as xn, indexReviewGroups as xr, feedbackUrl as xt, recordKissRun as y, gradeScale as yi, isUnchanged as yn, groupByLineage as yr, BUILD_VERSION as yt, readIndexToken as z, shorthandOf as zi, isWorktreeHeld as zn, LIFECYCLES as zr, REPOSITORY_SCOPE as zt };
package/dist/main.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { Ai as resolveTarget, An as modelHelpLines, Ar as recentWorkspaces, B as ensureServer, Bi as resolveAgent, D as relativeTime, Dr as positionInLineage, Er as mutateWorkspace, F as queueForReview, Gi as CONFIG_PATH, H as reviewsUrl, Hn as describeSubject, Ji as agentById, K as stopServer, Ki as STORE_ROOT, L as readIndexToken, M as runBatch, Mr as reviewedRepositories, Nr as summarise, O as batchIdentity, Oi as WITHHELD_REASON, Pn as MANAGED_ROOT, Q as parseWorkspacePort, Qn as allJobs, R as bootstrapUrl, Ri as git, Rn as readMarker, Tr as matchingWorkspaceIds, Ui as readAgentPreference, Un as findExistingReview, Vi as resolveModel, Vn as describeMovement, Wi as writeAgentPreference, X as MAX_WORKSPACE_PORT, Xi as detectAgents, Y as DEFAULT_WORKSPACE_PORT, Yn as forgeResolver, Z as MIN_WORKSPACE_PORT, _ as recordKissRun, _t as BUILD_VERSION, a as wasBlocked, at as settleReview, ct as startJob, d as mergeUpdateInto, dr as purposeOf, dt as record$1, et as statedWorkspacePort, f as updateReview, fr as clearClaim, ft as stamp, g as runningJobs, gr as followedRefName, gt as telemetryUploadEnabled, h as liveUpdateFor, ht as telemetryUploadConsent, i as liveJobsFor, it as prepareReview, ki as kissOutcome, lt as append, m as UpdateAlreadyRunningError, mt as recordTelemetryUploadConsent, n as deleteReview$1, or as PHASES, ot as reanalyseReview, pt as deliver, q as workspaceUrl, qi as AGENT_IDS, rr as loadJob, tt as writePortPreference, ur as progressSteps, vt as PACKAGE_NAME, wr as loadWorkspace, zn as removeWorktree } from "./delete_review-CYiIfV5A.js";
2
+ import { $ as MIN_WORKSPACE_PORT, A as batchIdentity, Ai as WITHHELD_REASON, B as bootstrapUrl, Bi as git, Bn as readMarker, Dr as matchingWorkspaceIds, Er as loadWorkspace, Fr as summarise, Gn as findExistingReview, H as ensureServer, Hi as resolveAgent, In as MANAGED_ROOT, J as stopServer, Ji as CONFIG_PATH, Ki as readAgentPreference, L as queueForReview, Mi as resolveTarget, Mn as modelHelpLines, Mr as recentWorkspaces, Or as mutateWorkspace, P as runBatch, Pr as reviewedRepositories, Q as MAX_WORKSPACE_PORT, Ui as resolveModel, Un as describeMovement, Vn as removeWorktree, W as reviewsUrl, Wn as describeSubject, Xi as AGENT_IDS, Y as workspaceUrl, Yi as STORE_ROOT, Z as DEFAULT_WORKSPACE_PORT, Zi as agentById, Zn as forgeResolver, _ as runningJobs, _t as telemetryUploadConsent, a as wasBlocked, ar as loadJob, bt as PACKAGE_NAME, cr as PHASES, ct as reanalyseReview, d as mergeUpdateInto, dt as append, ea as detectAgents, er as allJobs, et as parseWorkspacePort, f as parkedUpdateRefusal, fr as progressSteps, g as liveUpdateFor, gt as recordTelemetryUploadConsent, h as UpdateAlreadyRunningError, ht as deliver, i as liveJobsFor, ji as kissOutcome, k as relativeTime, kr as positionInLineage, mr as clearClaim, mt as stamp, n as deleteReview$1, na as parkedReason, nt as statedWorkspacePort, ot as prepareReview, p as updateReview, pr as purposeOf, pt as record$1, qi as writeAgentPreference, ra as runnableAgents, rt as writePortPreference, st as settleReview, ut as startJob, vr as followedRefName, vt as telemetryUploadEnabled, y as recordKissRun, yt as BUILD_VERSION, z as readIndexToken } from "./delete_review-CFVFygqb.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";
@@ -1150,6 +1150,11 @@ function unknownSettingValue(key, value, known) {
1150
1150
  */
1151
1151
  function unresolvedAgent(resolution) {
1152
1152
  if (resolution.kind === "unknown-id") return unknownAgent(resolution.id, AGENT_IDS);
1153
+ if (resolution.kind === "unavailable" && resolution.parked) return [
1154
+ resolution.reason,
1155
+ "",
1156
+ `Nothing was started. Name another agent with ${runnableAgents().flags}, or change the default with \`prreviewbuddy config set agent <agent>\`.`
1157
+ ].join("\n");
1153
1158
  if (resolution.kind === "unavailable") return `${resolution.agent?.displayName} cannot be run here. ${resolution.reason}`;
1154
1159
  if (resolution.kind === "none") return [
1155
1160
  "No coding agent was found, so there is nothing to review with.",
@@ -2380,6 +2385,11 @@ async function operate(command, workspace) {
2380
2385
  err(commitReviewCannotUpdate(workspace.changeSet.data.pullRequest.head.sha));
2381
2386
  return 1;
2382
2387
  }
2388
+ const parked = command.operation === "update" ? parkedUpdateRefusal(workspace) : null;
2389
+ if (parked) {
2390
+ err(parked);
2391
+ return 1;
2392
+ }
2383
2393
  enqueue(command, workspace.id);
2384
2394
  if (command.operation === "reanalyse") {
2385
2395
  const agent = await chooseFor(command, workspace);
@@ -2718,6 +2728,11 @@ async function agents(command) {
2718
2728
  out(agentList(detected, preferred));
2719
2729
  return 0;
2720
2730
  }
2731
+ const parked = parkedReason(command.id);
2732
+ if (parked) {
2733
+ err(parked);
2734
+ return 1;
2735
+ }
2721
2736
  if (!AGENT_IDS.includes(command.id)) {
2722
2737
  err(unknownAgent(command.id, AGENT_IDS));
2723
2738
  return 1;
@@ -2802,6 +2817,11 @@ function agent(command) {
2802
2817
  return 0;
2803
2818
  }
2804
2819
  const id = command.value;
2820
+ const parked = parkedReason(id);
2821
+ if (parked) {
2822
+ err(parked);
2823
+ return 1;
2824
+ }
2805
2825
  if (!AGENT_IDS.includes(id)) {
2806
2826
  err(unknownAgent(id, AGENT_IDS));
2807
2827
  return 1;
package/dist/server.js CHANGED
@@ -1,4 +1,4 @@
1
- import { $ as resolveWorkspacePort, $i as AgentUnavailableError, $n as attemptIdOf, $r as isIssueOutstanding, $t as moveIds, A as parsePullRequestLink, Ai as resolveTarget, Ar as recentWorkspaces, At as readArchitecture, Bi as resolveAgent, Bn as requestJobStop, Br as isLifecycle, Bt as writeJsonAtomically, C as completeRun, Ci as followUpFindingId, Cn as hasAuthorContent, Cr as lineagePosition, Ct as RUNTIME_WORDS, D as relativeTime$1, Di as describeAuthorship, Dn as CODE_COPY_CLASS, Dt as checkArchitecture, E as stopRun, Ei as objectWithAnyOf, En as CODE_BLOCK_CLASS, Er as mutateWorkspace, Et as ARCHITECTURE_DIR, Fi as AmbiguousRefError, Fn as createWorktree, Fr as workspaceRevision, Ft as ownersOf, G as stopChild, Gn as checkReview, Gr as parseLifecycle, Gt as createArchitectureNotification, Hi as clearAgentPreference, Hr as lifecycleItemLabel, Ht as branchOf, I as settleQueue, Ii as shortNameOf, In as holdWorktree, Ir as workspaceStamps, It as QUOTE_LIMITS, J as writeServerRecord, Ji as agentById, Jn as fetchHead, Jr as queueAddMany, Jt as eventName, Ki as STORE_ROOT, Kn as followedBranch, Kr as sectionOf, Kt as createPrState, L as readIndexToken, Li as shorthandOf, Ln as isWorktreeHeld, Lr as LIFECYCLES, Lt as REPOSITORY_SCOPE, M as runBatch, Mi as DetachedHeadError, Mn as isBusy, Mt as storeArchitecture, N as activateQueued, Ni as isCommitReview, Nn as withReviewLock, Nt as storedScopes, O as batchIdentity, Oi as WITHHELD_REASON, On as escapeHtml$1, Or as presentedKissRunOf, Ot as loadArchitecture, P as compactQueue, Pi as resolveBase, Pn as MANAGED_ROOT, Pr as touchWorkspace, Pt as trackedFiles, Q as parseWorkspacePort, Qi as AgentCancelledError, Qn as allJobs, Qr as doneVerb, Qt as markReadForReview, Ri as git, Rn as readMarker, Rr as activityAttributes, Rt as ancestorsOf, S as beginRun, Si as wasReassessed, Sn as authorPreview, Sr as latestKissRun, St as recordRuntimeBestEffort, T as reportRun, Ti as extractJsonText, Tn as renderMarkdown, Tt as formatDuration, U as serverEntryExists, Ui as readAgentPreference, Un as findExistingReview, Ur as lifecyclePill, Ut as subjectOf, V as readServerRecord, Vi as resolveModel, Vr as lifecycleChip, Vt as announceJob, W as spawnServer, Wi as writeAgentPreference, Wn as checkCodeFreshness, Wr as offersLifecycle, Wt as categoryOf, X as MAX_WORKSPACE_PORT, Xi as detectAgents, Xn as fillFileUrlTemplate, Xr as queueStamp, Xt as markAllRead, Y as DEFAULT_WORKSPACE_PORT, Yi as agentFor$1, Yn as forgeResolver, Yr as queueRemove, Yt as listNotifications, Z as MIN_WORKSPACE_PORT, Zi as isAgentId, Zn as RUN_RECORD_RETENTION_MS, Zr as readQueue, Zt as markRead, _i as gradeScale, _n as isUnchanged, _r as groupByLineage, _t as BUILD_VERSION, a as wasBlocked, ai as processDiscussion, an as toneOf, ar as saveJob, at as settleReview, b as track, bi as reassessIn, bn as chatConversationSection, br as kissDecisionRefusal, bt as waitingHistory, c as RefreshUnavailableError, ci as explanationOf, cn as withUsageRecorded, cr as isTerminalJobPhase, d as mergeUpdateInto, di as ANALYSIS_ONLY, dn as adoptLegacyBaseline, dr as purposeOf, dt as record, ei as isQuestionOutstanding, en as pruneNotifications, er as endedAt, et as statedWorkspacePort, f as updateReview, fi as analysisRefOf, fn as prepareConversationWithin, gi as findingIn, gn as refreshPrContext, gr as followedRefName, h as liveUpdateFor, hi as findingAssessment, hn as unforeseenFailure, hr as decideKissRecommendation, ht as telemetryUploadConsent, i as liveJobsFor, ii as reviewerDispositions, in as retireNotification, ir as pruneJobResidue, it as prepareReview, j as unsupportedReason, ji as originRemote, jn as ReviewBeingDeletedError, jr as reviewedCommit, jt as repositoryKey, k as extractPullRequestLinks, ki as kissOutcome, kn as renderInlineText, kr as previousKissResult, kt as partFiles, l as cancelUpdate, li as isExplanationAction, ln as existingPaths, lr as phasesFor, m as UpdateAlreadyRunningError, mi as currentGradeIn, mn as storedConversation, mt as recordTelemetryUploadConsent, n as deleteReview, ni as questionsOutstanding, nn as readStateOf, nr as isTerminal, nt as failureOf$1, o as discardJob, oi as recommendationsOf, on as unreadAround, ot as reanalyseReview, p as askCheckout, pi as assessmentLookup, pn as saveRefresh, pr as isClaimed, qi as AGENT_IDS, qn as reviewedShaOf, qr as queueAdd, qt as createPrUpdate, r as lineageIds, ri as resolvedByConversation, rn as removeNotification, rr as loadJob, rt as observeReview, s as FETCH_TIMEOUT_MS, si as explanationActionOf, sn as unreadCount$1, sr as isLiveJob, st as runJob, t as deleteLineage, ti as issuesOutstanding, tn as readNotification, tr as fail$1, tt as writePortPreference, u as cancellableHere, ui as withExplanation, un as ASK_REREAD_CEILING_MS, ur as progressSteps, ut as readEvents, v as startKissJob, vi as isGradeFor, vn as agentEnvOf, vr as indexKeptIds, w as endRun, wi as asString, wn as GITHUB_MARKDOWN, wr as loadWorkspace, wt as describeRuntimes, x as RUN_KINDS, xi as sourcePhrase, xn as UNESTABLISHED_MOTIVATION, xr as kissDecisionsOf, xt as mapRuntimes, y as isDriving, yi as promptGrade, yn as EXPLAIN_SIMPLY_PROMPT, yr as indexReviewGroups, yt as feedbackUrl, z as clearServerRecordOwnedBy, zi as refExists, zn as removeWorktree, zr as activityLabel, zt as isArchitectureId } from "./delete_review-CYiIfV5A.js";
1
+ import { $ as MIN_WORKSPACE_PORT, $i as agentIdentityOf, $n as RUN_RECORD_RETENTION_MS, $r as readQueue, $t as markRead, A as batchIdentity, Ai as WITHHELD_REASON, An as escapeHtml$1, Ar as presentedKissRunOf, At as loadArchitecture, Bi as git, Bn as readMarker, Br as activityAttributes, Bt as ancestorsOf, C as RUN_KINDS, Ci as sourcePhrase, Cn as UNESTABLISHED_MOTIVATION, Cr as kissDecisionsOf, Ct as mapRuntimes, D as reportRun, Di as extractJsonText, Dn as renderMarkdown, Dt as formatDuration, E as endRun, Ei as asString, En as GITHUB_MARKDOWN, Er as loadWorkspace, Et as describeRuntimes, F as activateQueued, Fi as isCommitReview, Fn as withReviewLock, Ft as storedScopes, G as serverEntryExists, Gi as clearAgentPreference, Gn as findExistingReview, Gr as lifecyclePill, Gt as subjectOf, Hi as resolveAgent, Hn as requestJobStop, Hr as isLifecycle, Ht as writeJsonAtomically, I as compactQueue, Ii as resolveBase, In as MANAGED_ROOT, Ir as touchWorkspace, It as trackedFiles, Jn as followedBranch, Jr as sectionOf, Jt as createPrState, K as spawnServer, Ki as readAgentPreference, Kn as checkCodeFreshness, Kr as offersLifecycle, Kt as categoryOf, Li as AmbiguousRefError, Ln as createWorktree, Lr as workspaceRevision, Lt as ownersOf, M as parsePullRequestLink, Mi as resolveTarget, Mr as recentWorkspaces, Mt as readArchitecture, N as unsupportedReason, Ni as originRemote, Nn as ReviewBeingDeletedError, Nr as reviewedCommit, Nt as repositoryKey, O as stopRun, Oi as objectWithAnyOf, On as CODE_BLOCK_CLASS, Or as mutateWorkspace, Ot as ARCHITECTURE_DIR, P as runBatch, Pi as DetachedHeadError, Pn as isBusy, Pt as storeArchitecture, Q as MAX_WORKSPACE_PORT, Qi as agentFor$1, Qn as fillFileUrlTemplate, Qr as queueStamp, Qt as markAllRead, R as settleQueue, Ri as shortNameOf, Rn as holdWorktree, Rr as workspaceStamps, Rt as QUOTE_LIMITS, S as track, Si as reassessIn, Sn as chatConversationSection, Sr as kissDecisionRefusal, St as waitingHistory, T as completeRun, Ti as followUpFindingId, Tn as hasAuthorContent, Tr as lineagePosition, Tt as RUNTIME_WORDS, U as readServerRecord, Ui as resolveModel, Ur as lifecycleChip, Ut as announceJob, V as clearServerRecordOwnedBy, Vi as refExists, Vn as removeWorktree, Vr as activityLabel, Vt as isArchitectureId, Wi as agentIdOf, Wr as lifecycleItemLabel, Wt as branchOf, X as writeServerRecord, Xi as AGENT_IDS, Xn as fetchHead, Xr as queueAddMany, Xt as eventName, Yi as STORE_ROOT, Yn as reviewedShaOf, Yr as queueAdd, Yt as createPrUpdate, Z as DEFAULT_WORKSPACE_PORT, Zi as agentById, Zn as forgeResolver, Zr as queueRemove, Zt as listNotifications, _i as findingAssessment, _n as unforeseenFailure, _r as decideKissRecommendation, _t as telemetryUploadConsent, a as wasBlocked, aa as AgentUnavailableError, ai as resolvedByConversation, an as removeNotification, ar as loadJob, at as observeReview, b as startKissJob, bi as isGradeFor, bn as agentEnvOf, br as indexKeptIds, c as RefreshUnavailableError, ci as recommendationsOf, cn as unreadAround, ct as reanalyseReview, d as mergeUpdateInto, di as isExplanationAction, dn as existingPaths, dr as phasesFor, ea as detectAgents, ei as doneVerb, en as markReadForReview, er as allJobs, et as parseWorkspacePort, fi as withExplanation, fn as ASK_REREAD_CEILING_MS, fr as progressSteps, ft as readEvents, g as liveUpdateFor, gi as currentGradeIn, gn as storedConversation, gt as recordTelemetryUploadConsent, h as UpdateAlreadyRunningError, hi as assessmentLookup, hn as saveRefresh, hr as isClaimed, i as liveJobsFor, ia as AgentCancelledError, ii as questionsOutstanding, in as readStateOf, ir as isTerminal, it as failureOf$1, j as extractPullRequestLinks, ji as kissOutcome, jn as renderInlineText, jr as previousKissResult, jt as partFiles, k as relativeTime$1, ki as describeAuthorship, kn as CODE_COPY_CLASS, kt as checkArchitecture, l as cancelUpdate, li as explanationActionOf, ln as unreadCount$1, lr as isLiveJob, lt as runJob, m as askCheckout, mi as analysisRefOf, mn as prepareConversationWithin, n as deleteReview, na as parkedReason, ni as isQuestionOutstanding, nn as pruneNotifications, nr as endedAt, nt as statedWorkspacePort, o as discardJob, oi as reviewerDispositions, on as retireNotification, or as pruneJobResidue, ot as prepareReview, p as updateReview, pi as ANALYSIS_ONLY, pn as adoptLegacyBaseline, pr as purposeOf, pt as record, q as stopChild, qi as writeAgentPreference, qn as checkReview, qr as parseLifecycle, qt as createArchitectureNotification, r as lineageIds, ra as runnableAgents, ri as issuesOutstanding, rn as readNotification, rr as fail$1, rt as writePortPreference, s as FETCH_TIMEOUT_MS, si as processDiscussion, sn as toneOf, sr as saveJob, st as settleReview, t as deleteLineage, ta as isAgentId, ti as isIssueOutstanding, tn as moveIds, tr as attemptIdOf, tt as resolveWorkspacePort, u as cancellableHere, ui as explanationOf, un as withUsageRecorded, ur as isTerminalJobPhase, v as kissAgentId, vi as findingIn, vn as refreshPrContext, vr as followedRefName, w as beginRun, wi as wasReassessed, wn as authorPreview, wr as latestKissRun, wt as recordRuntimeBestEffort, x as isDriving, xi as promptGrade, xn as EXPLAIN_SIMPLY_PROMPT, xr as indexReviewGroups, xt as feedbackUrl, yi as gradeScale, yn as isUnchanged, yr as groupByLineage, yt as BUILD_VERSION, z as readIndexToken, zi as shorthandOf, zn as isWorktreeHeld, zr as LIFECYCLES, zt as REPOSITORY_SCOPE } from "./delete_review-CFVFygqb.js";
2
2
  import { basename, isAbsolute, join, relative, resolve } from "node:path";
3
3
  import { execFile } from "node:child_process";
4
4
  import { existsSync, mkdirSync, readFileSync, readdirSync, realpathSync, rmSync, statSync, writeFileSync } from "node:fs";
@@ -4228,7 +4228,7 @@ function renderFailureBody(job, options = {}) {
4228
4228
  if (job.failure.stopped) return `<p>${options.lead === false ? "" : "<strong>Review stopped.</strong> "}Nothing went wrong, and what it had finished is kept.</p>
4229
4229
  <button type="button" class="prb-job-retry" data-job-retry>Carry on</button>
4230
4230
  <p class="prb-job-note">It picks up from the ${escapeHtml$1(noun)}, not from the start.</p>`;
4231
- const next = job.failure.kind === "agent_authentication_failed" ? `<p class="prb-job-note">Signing that agent in is what changes this. Once it is signed in, update the review.</p>` : `<button type="button" class="prb-job-retry" data-job-retry>Retry</button>
4231
+ const next = job.failure.parked ? "" : job.failure.kind === "agent_authentication_failed" ? `<p class="prb-job-note">Signing that agent in is what changes this. Once it is signed in, update the review.</p>` : `<button type="button" class="prb-job-retry" data-job-retry>Retry</button>
4232
4232
  <p class="prb-job-note">Retry picks up from the ${escapeHtml$1(noun)}, not from the start.</p>`;
4233
4233
  return `<p>${options.lead === false ? "" : "<strong>Review paused.</strong> "}${escapeHtml$1(job.failure.message)}</p>
4234
4234
  ${next}`;
@@ -7701,6 +7701,8 @@ function startArchitectureRun(workspace, scope, deps = {}) {
7701
7701
  const now = deps.now ?? Date.now;
7702
7702
  const startedAt = now();
7703
7703
  const env = agentEnvOf(workspace);
7704
+ const parked = parkedReason(env.agentId);
7705
+ if (parked) return { refused: parked };
7704
7706
  const agent = agentFor$1(env);
7705
7707
  const requestedModel = resolveModel({ review: { session: workspace.session } }) ?? void 0;
7706
7708
  const record = {
@@ -10489,6 +10491,7 @@ function readSettings() {
10489
10491
  },
10490
10492
  agent: readAgentPreference(),
10491
10493
  agents: AGENT_IDS,
10494
+ agentParked: parkedReason(readAgentPreference() ?? void 0) ?? null,
10492
10495
  usageCounts: telemetryUploadConsent(),
10493
10496
  theme: readPreferences().theme ?? "system"
10494
10497
  };
@@ -10513,6 +10516,11 @@ function saveSettings(change, minPort, maxPort) {
10513
10516
  error: `\`${change.port}\` is not a port PR Review Buddy can use. Choose one between ${minPort} and ${maxPort}.`
10514
10517
  };
10515
10518
  }
10519
+ const parked = change.agent ? parkedReason(change.agent) : void 0;
10520
+ if (parked) return {
10521
+ settings: readSettings(),
10522
+ error: parked
10523
+ };
10516
10524
  if (change.agent !== void 0 && change.agent !== "" && !AGENT_IDS.includes(change.agent)) return {
10517
10525
  settings: readSettings(),
10518
10526
  error: `There is no agent called \`${change.agent}\`.`
@@ -10668,11 +10676,15 @@ function renderReviewsSection(settings) {
10668
10676
  <h2>Reviews</h2>
10669
10677
  <label class="prb-field prb-settings-field">
10670
10678
  <span>Default coding agent</span>
10671
- <select data-settings-agent aria-describedby="prb-agent-help">${[`<option value=""${settings.agent ? "" : " selected"}>Ask each time</option>`, ...settings.agents.map((id) => `<option value="${escapeHtml$1(id)}"${settings.agent === id ? " selected" : ""}>${escapeHtml$1(id)}</option>`)].join("")}</select>
10679
+ <select data-settings-agent aria-describedby="prb-agent-help">${[
10680
+ `<option value=""${settings.agent ? "" : " selected"}>Ask each time</option>`,
10681
+ ...settings.agents.map((id) => `<option value="${escapeHtml$1(id)}"${settings.agent === id ? " selected" : ""}>${escapeHtml$1(id)}</option>`),
10682
+ ...settings.agentParked && settings.agent ? [`<option value="${escapeHtml$1(settings.agent)}" selected disabled>${escapeHtml$1(settings.agent)} (turned off)</option>`] : []
10683
+ ].join("")}</select>
10672
10684
  </label>
10673
10685
  <p class="prb-settings-help" id="prb-agent-help">Used when a review does not name one. PR Review
10674
10686
  Buddy uses an agent already installed on this machine and its existing sign-in, so this changes
10675
- which one runs and never asks you for a credential.</p>
10687
+ which one runs and never asks you for a credential.</p>${settings.agentParked ? `\n <p class="prb-settings-help" role="alert">${escapeHtml$1(settings.agentParked)} Choose another default, or Ask each time.</p>` : ""}
10676
10688
  </section>`;
10677
10689
  }
10678
10690
  /**
@@ -11602,11 +11614,7 @@ function modelHelpOf(agentId) {
11602
11614
  }
11603
11615
  }
11604
11616
  function agentNameOf(agentId) {
11605
- try {
11606
- return agentById(agentId).displayName;
11607
- } catch {
11608
- return agentId;
11609
- }
11617
+ return isAgentId(agentId) ? agentIdentityOf(agentId).displayName : agentId;
11610
11618
  }
11611
11619
  var refuse = (status, error, extra = {}) => ({
11612
11620
  status,
@@ -12793,7 +12801,7 @@ function reviewHref(review) {
12793
12801
  }
12794
12802
  /** The agent's name, only where the record names one this build knows. */
12795
12803
  function agentName(agentId) {
12796
- return agentId && isAgentId(agentId) ? agentById(agentId).displayName : void 0;
12804
+ return agentId && isAgentId(agentId) ? agentIdentityOf(agentId).displayName : void 0;
12797
12805
  }
12798
12806
  /** Who asked and who ran it, each only when known. Two facts, never one guessed from the other. */
12799
12807
  function provenance(source, agentId) {
@@ -15996,6 +16004,8 @@ async function handleReanalyse(req, res, workspace) {
15996
16004
  if (req.method !== "POST") return send(res, 405, { error: "Analysing a review again needs a POST." });
15997
16005
  const current = workspace.jobId ? loadJob(workspace.jobId) : null;
15998
16006
  if (current && (!isTerminal(current) || isClaimed(current.id))) return send(res, 409, { error: "This review is still being made. Wait for it to finish before analysing it again." });
16007
+ const parked = parkedReason(agentIdOf(workspace.session));
16008
+ if (parked) return send(res, 409, { error: `${parked} To analyse this review again with another agent, run \`prreviewbuddy review --review ${workspace.id} --reanalyse\` with ${runnableAgents().flags}.` });
15999
16009
  await new Promise((resolve) => {
16000
16010
  const run = reanalyseReview(workspace.id, void 0, () => {
16001
16011
  send(res, 200, { ok: true });
@@ -16036,6 +16046,8 @@ async function handleKiss(req, res, workspace) {
16036
16046
  if (!audience) return send(res, 400, { error: "A KISS review is written either for the person who wrote the code or for somebody reviewing it. Choose which before running it." });
16037
16047
  const current = latestKissRun(workspace);
16038
16048
  if (current && current.status === "generating" && isClaimed(current.jobId)) return send(res, 409, { error: "A KISS review of this change is already running." });
16049
+ const parked = parkedReason(kissAgentId(workspace));
16050
+ if (parked) return send(res, 409, { error: `${parked} A KISS review started here uses the agent this review was made with. To run one with another agent, start it from the terminal with \`--lens kiss\` and ${runnableAgents().flags}.` });
16039
16051
  startKissJob({
16040
16052
  workspaceId: workspace.id,
16041
16053
  audience,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "prreviewbuddy",
3
- "version": "0.80.3",
4
- "description": "Understand code changes before you approve them. Local branch and pull request reviews with Claude Code, Codex or Gemini CLI.",
3
+ "version": "0.81.0",
4
+ "description": "Understand code changes before you approve them. Local branch and pull request reviews with Claude Code or Codex.",
5
5
  "license": "UNLICENSED",
6
6
  "homepage": "https://prreviewbuddy.com",
7
7
  "type": "module",
@@ -24,8 +24,6 @@
24
24
  "claude",
25
25
  "claude-code",
26
26
  "codex",
27
- "gemini",
28
- "gemini-cli",
29
27
  "ai"
30
28
  ],
31
29
  "engines": {