@plannotator/opencode 0.19.10 → 0.19.11

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/dist/index.js CHANGED
@@ -42405,9 +42405,9 @@ var Thread = class {
42405
42405
  stderrChunks.push(data);
42406
42406
  });
42407
42407
  }
42408
- const exitPromise = new Promise((resolve5) => {
42408
+ const exitPromise = new Promise((resolve6) => {
42409
42409
  child.once("exit", (code, signal) => {
42410
- resolve5({ code, signal });
42410
+ resolve6({ code, signal });
42411
42411
  });
42412
42412
  });
42413
42413
  const rl2 = readline.createInterface({
@@ -42910,8 +42910,8 @@ class PiProcess {
42910
42910
  }
42911
42911
  sendAndWait(command) {
42912
42912
  const id = `req_${++this.nextId}`;
42913
- return new Promise((resolve5, reject) => {
42914
- this.pendingRequests.set(id, { resolve: resolve5, reject });
42913
+ return new Promise((resolve6, reject) => {
42914
+ this.pendingRequests.set(id, { resolve: resolve6, reject });
42915
42915
  this.send({ ...command, id });
42916
42916
  });
42917
42917
  }
@@ -43052,15 +43052,15 @@ var init_pi_sdk = __esm(() => {
43052
43052
  }
43053
43053
  const effectivePrompt = buildEffectivePrompt(prompt, this.config.systemPrompt, this._firstQuerySent);
43054
43054
  const queue = [];
43055
- let resolve5 = null;
43055
+ let resolve6 = null;
43056
43056
  let done = false;
43057
43057
  const push = (msg) => {
43058
43058
  queue.push(msg);
43059
- resolve5?.();
43059
+ resolve6?.();
43060
43060
  };
43061
43061
  const finish = () => {
43062
43062
  done = true;
43063
- resolve5?.();
43063
+ resolve6?.();
43064
43064
  };
43065
43065
  const unsubscribe = this.process.onEvent((event) => {
43066
43066
  const mapped = mapPiEvent(event, this.id);
@@ -43092,9 +43092,9 @@ var init_pi_sdk = __esm(() => {
43092
43092
  yield queue.shift();
43093
43093
  } else {
43094
43094
  await new Promise((r9) => {
43095
- resolve5 = r9;
43095
+ resolve6 = r9;
43096
43096
  });
43097
- resolve5 = null;
43097
+ resolve6 = null;
43098
43098
  }
43099
43099
  }
43100
43100
  } finally {
@@ -43130,7 +43130,7 @@ var init_types_gen = () => {};
43130
43130
  // ../../node_modules/.bun/@opencode-ai+sdk@1.3.5/node_modules/@opencode-ai/sdk/dist/gen/core/serverSentEvents.gen.js
43131
43131
  var createSseClient = ({ onSseError, onSseEvent, responseTransformer, responseValidator, sseDefaultRetryDelay, sseMaxRetryAttempts, sseMaxRetryDelay, sseSleepFn, url, ...options }) => {
43132
43132
  let lastEventId;
43133
- const sleep = sseSleepFn ?? ((ms) => new Promise((resolve5) => setTimeout(resolve5, ms)));
43133
+ const sleep = sseSleepFn ?? ((ms) => new Promise((resolve6) => setTimeout(resolve6, ms)));
43134
43134
  const createStream = async function* () {
43135
43135
  let retryDelay = sseDefaultRetryDelay ?? 3000;
43136
43136
  let attempt = 0;
@@ -44531,7 +44531,7 @@ async function createOpencodeServer(options) {
44531
44531
  OPENCODE_CONFIG_CONTENT: JSON.stringify(options.config ?? {})
44532
44532
  }
44533
44533
  });
44534
- const url = await new Promise((resolve5, reject) => {
44534
+ const url = await new Promise((resolve6, reject) => {
44535
44535
  const id = setTimeout(() => {
44536
44536
  reject(new Error(`Timeout waiting for server to start after ${options.timeout}ms`));
44537
44537
  }, options.timeout);
@@ -44547,7 +44547,7 @@ async function createOpencodeServer(options) {
44547
44547
  throw new Error(`Failed to parse server url from output: ${line}`);
44548
44548
  }
44549
44549
  clearTimeout(id);
44550
- resolve5(match[1]);
44550
+ resolve6(match[1]);
44551
44551
  return;
44552
44552
  }
44553
44553
  }
@@ -61846,6 +61846,33 @@ async function startPlannotatorServer(options) {
61846
61846
 
61847
61847
  // ../../packages/shared/review-core.ts
61848
61848
  import { resolve as resolvePath } from "path";
61849
+ var JJ_TRUNK_REVSET = "trunk()";
61850
+ function parseRemoteBookmark(target) {
61851
+ const at = target.lastIndexOf("@");
61852
+ if (at <= 0 || at === target.length - 1)
61853
+ return null;
61854
+ return { name: target.slice(0, at), remote: target.slice(at + 1) };
61855
+ }
61856
+ function jjCompareTargetRevset(target) {
61857
+ const remoteBookmark = parseRemoteBookmark(target);
61858
+ if (remoteBookmark) {
61859
+ return `remote_bookmarks(exact:${quoteJjString(remoteBookmark.name)}, exact:${quoteJjString(remoteBookmark.remote)})`;
61860
+ }
61861
+ const localBookmark = parseJjBookmarkName(target);
61862
+ return localBookmark ? `bookmarks(exact:${quoteJjString(localBookmark)})` : target;
61863
+ }
61864
+ function jjLineBaseRevset(target) {
61865
+ const compareTarget = jjCompareTargetRevset(target);
61866
+ return `heads(::@ & ::(${compareTarget}))`;
61867
+ }
61868
+ function parseJjBookmarkName(target) {
61869
+ if (!target || target.startsWith("@") || /[()\s]/.test(target))
61870
+ return null;
61871
+ return target;
61872
+ }
61873
+ function quoteJjString(value) {
61874
+ return JSON.stringify(value);
61875
+ }
61849
61876
  async function getCurrentBranch2(runtime, cwd) {
61850
61877
  const result = await runtime.runGit(["rev-parse", "--abbrev-ref", "HEAD"], { cwd });
61851
61878
  return result.exitCode === 0 ? result.stdout.trim() || "HEAD" : "HEAD";
@@ -61972,7 +61999,21 @@ async function getGitContext(runtime, cwd) {
61972
61999
  diffOptions,
61973
62000
  worktrees: worktrees.filter((wt) => wt.path !== currentTreePath),
61974
62001
  availableBranches,
61975
- cwd
62002
+ compareTarget: {
62003
+ diffTypes: ["branch", "merge-base"],
62004
+ fallback: "main",
62005
+ picker: {
62006
+ rowLabel: "compare against",
62007
+ triggerLabel: "base",
62008
+ triggerTitlePrefix: "Review base",
62009
+ searchPlaceholder: "Search branches\u2026",
62010
+ emptyText: "No branches match.",
62011
+ localGroupLabel: "Local",
62012
+ remoteGroupLabel: "Remote"
62013
+ }
62014
+ },
62015
+ cwd,
62016
+ vcsType: "git"
61976
62017
  };
61977
62018
  }
61978
62019
  async function getUntrackedFileDiffs(runtime, srcPrefix = "a/", dstPrefix = "b/", cwd, options) {
@@ -62171,9 +62212,6 @@ async function runGitDiff(runtime, diffType, defaultBranch = "main", externalCwd
62171
62212
  }
62172
62213
  return { patch, label };
62173
62214
  }
62174
- async function runGitDiffWithContext(runtime, diffType, gitContext, options) {
62175
- return runGitDiff(runtime, diffType, gitContext.defaultBranch, gitContext.cwd, options);
62176
- }
62177
62215
  async function getFileContentsForDiff(runtime, diffType, defaultBranch, filePath, oldPath, cwd) {
62178
62216
  const oldFilePath = oldPath || filePath;
62179
62217
  let effectiveDiffType = diffType;
@@ -62263,53 +62301,507 @@ function parseP4DiffType(diffType) {
62263
62301
  return null;
62264
62302
  }
62265
62303
 
62266
- // ../../packages/server/git.ts
62267
- async function runGit(args, options) {
62268
- const proc = Bun.spawn(["git", "-c", "core.quotePath=false", ...args], {
62269
- cwd: options?.cwd,
62270
- stdout: "pipe",
62271
- stderr: "pipe"
62272
- });
62273
- let timer;
62274
- if (options?.timeoutMs) {
62275
- timer = setTimeout(() => proc.kill(), options.timeoutMs);
62304
+ // ../../packages/shared/vcs-core.ts
62305
+ import { isAbsolute as isAbsolute2, relative, resolve as resolve5 } from "path";
62306
+
62307
+ // ../../packages/shared/jj-core.ts
62308
+ import { basename as basename2 } from "path";
62309
+ async function detectJjWorkspace(runtime, cwd) {
62310
+ const result = await runtime.runJj(["workspace", "root"], { cwd });
62311
+ return result.exitCode === 0 ? result.stdout.trim() || null : null;
62312
+ }
62313
+ async function getJjContext(runtime, cwd) {
62314
+ const root3 = await detectJjWorkspace(runtime, cwd);
62315
+ const targets = await listJjCompareTargets(runtime, root3 ?? cwd);
62316
+ const defaultTarget = selectDefaultJjCompareTarget(targets);
62317
+ const contextCwd = root3 ?? cwd;
62318
+ return {
62319
+ currentBranch: "",
62320
+ defaultBranch: defaultTarget,
62321
+ diffOptions: [
62322
+ { id: "jj-current", label: "Current change" },
62323
+ { id: "jj-last", label: "Last change" },
62324
+ { id: "jj-line", label: "Line of work" },
62325
+ { id: "jj-all", label: "All files" }
62326
+ ],
62327
+ worktrees: [],
62328
+ availableBranches: targets,
62329
+ compareTarget: {
62330
+ diffTypes: ["jj-line"],
62331
+ fallback: defaultTarget,
62332
+ picker: {
62333
+ rowLabel: "from revision",
62334
+ triggerLabel: "revision",
62335
+ triggerTitlePrefix: "Compare against",
62336
+ searchPlaceholder: "Search bookmarks...",
62337
+ emptyText: "No bookmarks match.",
62338
+ localGroupLabel: "Bookmarks",
62339
+ remoteGroupLabel: "Remote bookmarks"
62340
+ }
62341
+ },
62342
+ repository: contextCwd ? { displayFallback: basename2(contextCwd) } : undefined,
62343
+ cwd: contextCwd,
62344
+ vcsType: "jj"
62345
+ };
62346
+ }
62347
+ async function runJjDiff(runtime, diffType, defaultBranch, cwd, options) {
62348
+ const compareTarget = defaultBranch.length > 0 ? defaultBranch : JJ_TRUNK_REVSET;
62349
+ const args = getJjDiffArgs(diffType, compareTarget, options);
62350
+ if (!args)
62351
+ return { patch: "", label: "Unknown diff type" };
62352
+ const result = await runtime.runJj(args.args, { cwd });
62353
+ if (result.exitCode !== 0) {
62354
+ return { patch: "", label: args.label, error: firstErrorLine(result.stderr) };
62276
62355
  }
62277
- const [stdout, stderr, exitCode] = await Promise.all([
62278
- new Response(proc.stdout).text(),
62279
- new Response(proc.stderr).text(),
62280
- proc.exited
62281
- ]);
62282
- if (timer)
62283
- clearTimeout(timer);
62284
- return { stdout, stderr, exitCode };
62356
+ const patch = options?.hideWhitespace ? dropHunklessGitDiffChunks(result.stdout) : result.stdout;
62357
+ return { patch, label: args.label };
62285
62358
  }
62286
- var runtime = {
62287
- runGit,
62288
- async readTextFile(path2) {
62289
- try {
62290
- return await Bun.file(path2).text();
62291
- } catch {
62292
- return null;
62359
+ function dropHunklessGitDiffChunks(patch) {
62360
+ if (!patch.includes("diff --git "))
62361
+ return patch;
62362
+ const chunks = patch.split(/^diff --git /m);
62363
+ const prefix = chunks.shift() ?? "";
62364
+ const filtered = chunks.filter(hasReviewableGitDiffChunk).map((chunk) => `diff --git ${chunk}`).join("");
62365
+ return `${prefix}${filtered}`;
62366
+ }
62367
+ function hasReviewableGitDiffChunk(chunk) {
62368
+ if (/^@@@? /m.test(chunk))
62369
+ return true;
62370
+ return /^(new file mode|deleted file mode|old mode|new mode|rename from|rename to|copy from|copy to|GIT binary patch|Binary files |similarity index|dissimilarity index)/m.test(chunk);
62371
+ }
62372
+ async function getJjFileContentsForDiff(runtime, diffType, defaultBranch, filePath, oldPath, cwd) {
62373
+ validateFilePath(filePath);
62374
+ if (oldPath)
62375
+ validateFilePath(oldPath);
62376
+ const oldFilePath = oldPath === undefined || oldPath.length === 0 ? filePath : oldPath;
62377
+ const root3 = await detectJjWorkspace(runtime, cwd);
62378
+ const fileCwd = root3 ?? cwd;
62379
+ switch (diffType) {
62380
+ case "jj-current":
62381
+ return {
62382
+ oldContent: await jjFileContent(runtime, "@-", oldFilePath, fileCwd),
62383
+ newContent: await jjFileContent(runtime, "@", filePath, fileCwd)
62384
+ };
62385
+ case "jj-last": {
62386
+ const parentRev = await resolveJjParent(runtime, "@-", fileCwd);
62387
+ return {
62388
+ oldContent: parentRev ? await jjFileContent(runtime, parentRev, oldFilePath, fileCwd) : null,
62389
+ newContent: await jjFileContent(runtime, "@-", filePath, fileCwd)
62390
+ };
62391
+ }
62392
+ case "jj-line": {
62393
+ const compareTarget = defaultBranch.length > 0 ? defaultBranch : JJ_TRUNK_REVSET;
62394
+ return {
62395
+ oldContent: await jjFileContent(runtime, jjLineBaseRevset(compareTarget), oldFilePath, fileCwd),
62396
+ newContent: await jjFileContent(runtime, "@", filePath, fileCwd)
62397
+ };
62293
62398
  }
62399
+ case "jj-all":
62400
+ return {
62401
+ oldContent: null,
62402
+ newContent: await jjFileContent(runtime, "@", filePath, fileCwd)
62403
+ };
62404
+ default:
62405
+ return { oldContent: null, newContent: null };
62294
62406
  }
62295
- };
62296
- function getGitContext2(cwd) {
62297
- return getGitContext(runtime, cwd);
62298
62407
  }
62299
- function runGitDiff2(diffType, defaultBranch = "main", cwd, options) {
62300
- return runGitDiff(runtime, diffType, defaultBranch, cwd, options);
62408
+ function getJjDiffArgs(diffType, compareTarget, options) {
62409
+ const whitespaceArgs = options?.hideWhitespace ? ["-w"] : [];
62410
+ switch (diffType) {
62411
+ case "jj-current":
62412
+ return { args: ["diff", "--git", ...whitespaceArgs, "-r", "@"], label: "Current change" };
62413
+ case "jj-last":
62414
+ return { args: ["diff", "--git", ...whitespaceArgs, "-r", "@-"], label: "Last change" };
62415
+ case "jj-line":
62416
+ return {
62417
+ args: ["diff", "--git", ...whitespaceArgs, "--from", jjLineBaseRevset(compareTarget), "--to", "@"],
62418
+ label: `Line of work vs ${compareTarget}`
62419
+ };
62420
+ case "jj-all":
62421
+ return { args: ["diff", "--git", ...whitespaceArgs, "--from", "root()", "--to", "@"], label: "All files" };
62422
+ default:
62423
+ return null;
62424
+ }
62425
+ }
62426
+ function selectDefaultJjCompareTarget(targets) {
62427
+ const preferredNames = ["main", "develop", "master", "trunk"];
62428
+ for (const name of preferredNames) {
62429
+ const preferredRemote = [
62430
+ `${name}@origin`,
62431
+ `${name}@upstream`,
62432
+ `${name}@git`,
62433
+ ...targets.remote.filter((target) => target.startsWith(`${name}@`)).sort()
62434
+ ].find((target) => targets.remote.includes(target));
62435
+ if (preferredRemote)
62436
+ return preferredRemote;
62437
+ if (targets.local.includes(name))
62438
+ return name;
62439
+ }
62440
+ return JJ_TRUNK_REVSET;
62441
+ }
62442
+ async function listJjCompareTargets(runtime, cwd) {
62443
+ const [localResult, remoteResult] = await Promise.all([
62444
+ runtime.runJj([
62445
+ "bookmark",
62446
+ "list",
62447
+ "--sort",
62448
+ "committer-date-",
62449
+ "--sort",
62450
+ "name",
62451
+ "-T",
62452
+ "if(remote, '', if(present, json(name) ++ '\\n', ''))"
62453
+ ], { cwd }),
62454
+ runtime.runJj([
62455
+ "bookmark",
62456
+ "list",
62457
+ "--all-remotes",
62458
+ "--sort",
62459
+ "committer-date-",
62460
+ "--sort",
62461
+ "name",
62462
+ "-T",
62463
+ "if(remote, if(present, json(name) ++ '\\t' ++ json(remote) ++ '\\n', ''), '')"
62464
+ ], { cwd })
62465
+ ]);
62466
+ const local = localResult.exitCode === 0 ? parseJjBookmarkList(localResult.stdout) : [];
62467
+ const remote = remoteResult.exitCode === 0 ? parseJjRemoteBookmarkList(remoteResult.stdout) : [];
62468
+ return {
62469
+ local,
62470
+ remote
62471
+ };
62472
+ }
62473
+ function parseJjBookmarkList(stdout) {
62474
+ const seen = new Set;
62475
+ const bookmarks = [];
62476
+ for (const rawLine of splitJjTemplateRecords(stdout)) {
62477
+ const line = rawLine.trim();
62478
+ if (!line)
62479
+ continue;
62480
+ const bookmark = parseSerializedJjString(line);
62481
+ if (!bookmark || seen.has(bookmark))
62482
+ continue;
62483
+ seen.add(bookmark);
62484
+ bookmarks.push(bookmark);
62485
+ }
62486
+ return bookmarks;
62487
+ }
62488
+ function parseJjRemoteBookmarkList(stdout) {
62489
+ const seen = new Set;
62490
+ const bookmarks = [];
62491
+ for (const rawLine of splitJjTemplateRecords(stdout)) {
62492
+ const line = rawLine.trim();
62493
+ if (!line)
62494
+ continue;
62495
+ const fields = splitJjTemplateFields(line);
62496
+ if (!fields)
62497
+ continue;
62498
+ const [nameField, remoteField] = fields;
62499
+ const name = parseSerializedJjString(nameField);
62500
+ const remote = parseSerializedJjString(remoteField);
62501
+ if (!name || !remote)
62502
+ continue;
62503
+ const bookmark = `${name}@${remote}`;
62504
+ if (seen.has(bookmark))
62505
+ continue;
62506
+ seen.add(bookmark);
62507
+ bookmarks.push(bookmark);
62508
+ }
62509
+ return bookmarks;
62510
+ }
62511
+ function splitJjTemplateRecords(stdout) {
62512
+ return stdout.split(/\n|\\n/g);
62513
+ }
62514
+ function splitJjTemplateFields(line) {
62515
+ const literalTab = line.indexOf("\t");
62516
+ if (literalTab !== -1)
62517
+ return [line.slice(0, literalTab), line.slice(literalTab + 1)];
62518
+ const escapedTab = line.indexOf("\\t");
62519
+ if (escapedTab !== -1)
62520
+ return [line.slice(0, escapedTab), line.slice(escapedTab + 2)];
62521
+ return null;
62522
+ }
62523
+ function parseSerializedJjString(value) {
62524
+ try {
62525
+ const parsed = JSON.parse(value);
62526
+ return typeof parsed === "string" ? parsed : null;
62527
+ } catch {
62528
+ return null;
62529
+ }
62530
+ }
62531
+ async function jjFileContent(runtime, rev, filePath, cwd) {
62532
+ const result = await runtime.runJj(["file", "show", "-r", rev, "--", filePath], { cwd });
62533
+ return result.exitCode === 0 ? result.stdout : null;
62534
+ }
62535
+ async function resolveJjParent(runtime, rev, cwd) {
62536
+ const result = await runtime.runJj(["log", "-r", rev, "--no-graph", "-T", "parents.map(|p| p.change_id()).join(' ')", "--limit", "1"], { cwd });
62537
+ const parent = result.stdout.trim().split(/\s+/).find(Boolean);
62538
+ return result.exitCode === 0 && parent ? parent : null;
62539
+ }
62540
+ function firstErrorLine(stderr) {
62541
+ const line = stderr.split(`
62542
+ `).find((value) => value.trim().length > 0)?.trim();
62543
+ if (!line)
62544
+ return;
62545
+ return line.length > 200 ? line.slice(0, 200) + "..." : line;
62546
+ }
62547
+
62548
+ // ../../packages/shared/vcs-core.ts
62549
+ var GIT_DIFF_TYPES = new Set(["uncommitted", "staged", "unstaged", "last-commit", "branch", "merge-base", "all"]);
62550
+ var JJ_DIFF_TYPES = new Set(["jj-current", "jj-last", "jj-line", "jj-all"]);
62551
+ function selectNearestProvider(candidates, cwd) {
62552
+ if (candidates.length === 0)
62553
+ return null;
62554
+ const effectiveCwd = resolve5(cwd ?? process.cwd());
62555
+ const ranked = candidates.map((candidate) => ({
62556
+ ...candidate,
62557
+ rootDepth: candidate.root ? vcsRootDepth(candidate.root) : -1,
62558
+ containsCwd: candidate.root ? isSameOrAncestor(candidate.root, effectiveCwd) : false
62559
+ })).sort((a2, b3) => {
62560
+ if (a2.containsCwd !== b3.containsCwd)
62561
+ return a2.containsCwd ? -1 : 1;
62562
+ if (a2.rootDepth !== b3.rootDepth)
62563
+ return b3.rootDepth - a2.rootDepth;
62564
+ return a2.order - b3.order;
62565
+ });
62566
+ return ranked[0]?.provider ?? null;
62567
+ }
62568
+ function isSameOrAncestor(root3, child) {
62569
+ const relativePath = relative(resolve5(root3), child);
62570
+ return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute2(relativePath);
62301
62571
  }
62302
- function runGitDiffWithContext2(diffType, gitContext, options) {
62303
- return runGitDiffWithContext(runtime, diffType, gitContext, options);
62572
+ function vcsRootDepth(root3) {
62573
+ return resolve5(root3).split(/[\\/]+/).filter(Boolean).length;
62304
62574
  }
62305
- function getFileContentsForDiff2(diffType, defaultBranch, filePath, oldPath, cwd) {
62306
- return getFileContentsForDiff(runtime, diffType, defaultBranch, filePath, oldPath, cwd);
62575
+ function createGitProvider(runtime) {
62576
+ return {
62577
+ id: "git",
62578
+ async detect(cwd) {
62579
+ try {
62580
+ const result = await runtime.runGit(["rev-parse", "--is-inside-work-tree"], { cwd });
62581
+ return result.exitCode === 0;
62582
+ } catch {
62583
+ return false;
62584
+ }
62585
+ },
62586
+ async getRoot(cwd) {
62587
+ const result = await runtime.runGit(["rev-parse", "--show-toplevel"], { cwd });
62588
+ return result.exitCode === 0 ? result.stdout.trim() || null : null;
62589
+ },
62590
+ ownsDiffType(diffType) {
62591
+ return GIT_DIFF_TYPES.has(diffType) || diffType.startsWith("worktree:");
62592
+ },
62593
+ canStageFiles(diffType) {
62594
+ const effectiveDiffType = parseWorktreeDiffType(diffType)?.subType ?? diffType;
62595
+ return effectiveDiffType === "uncommitted" || effectiveDiffType === "unstaged";
62596
+ },
62597
+ getContext(cwd) {
62598
+ return getGitContext(runtime, cwd);
62599
+ },
62600
+ runDiff(diffType, defaultBranch, cwd, options) {
62601
+ return runGitDiff(runtime, diffType, defaultBranch, cwd, options);
62602
+ },
62603
+ getFileContents(diffType, defaultBranch, filePath, oldPath, cwd) {
62604
+ return getFileContentsForDiff(runtime, diffType, defaultBranch, filePath, oldPath, cwd);
62605
+ },
62606
+ stageFile(filePath, cwd) {
62607
+ return gitAddFile(runtime, filePath, cwd);
62608
+ },
62609
+ unstageFile(filePath, cwd) {
62610
+ return gitResetFile(runtime, filePath, cwd);
62611
+ },
62612
+ detectRemoteDefaultCompareTarget(cwd) {
62613
+ return detectRemoteDefaultBranch(runtime, cwd);
62614
+ },
62615
+ resolveCwd(diffType, fallbackCwd) {
62616
+ const parsed = parseWorktreeDiffType(diffType);
62617
+ return parsed?.path ?? fallbackCwd;
62618
+ }
62619
+ };
62307
62620
  }
62308
- function gitAddFile2(filePath, cwd) {
62309
- return gitAddFile(runtime, filePath, cwd);
62621
+ function createJjProvider(runtime) {
62622
+ return {
62623
+ id: "jj",
62624
+ async detect(cwd) {
62625
+ return await detectJjWorkspace(runtime, cwd) !== null;
62626
+ },
62627
+ getRoot(cwd) {
62628
+ return detectJjWorkspace(runtime, cwd);
62629
+ },
62630
+ ownsDiffType(diffType) {
62631
+ return JJ_DIFF_TYPES.has(diffType);
62632
+ },
62633
+ getContext(cwd) {
62634
+ return getJjContext(runtime, cwd);
62635
+ },
62636
+ runDiff(diffType, defaultBranch, cwd, options) {
62637
+ return runJjDiff(runtime, diffType, defaultBranch, cwd, options);
62638
+ },
62639
+ getFileContents(diffType, defaultBranch, filePath, oldPath, cwd) {
62640
+ return getJjFileContentsForDiff(runtime, diffType, defaultBranch, filePath, oldPath, cwd);
62641
+ }
62642
+ };
62310
62643
  }
62311
- function gitResetFile2(filePath, cwd) {
62312
- return gitResetFile(runtime, filePath, cwd);
62644
+ function createVcsApi(providers) {
62645
+ const providerList = [...providers];
62646
+ const defaultProvider = providerList.find((provider) => provider.id === "git") ?? providerList[0];
62647
+ const vcsCache = new Map;
62648
+ if (!defaultProvider) {
62649
+ throw new Error("createVcsApi requires at least one provider");
62650
+ }
62651
+ async function detectVcs(cwd) {
62652
+ const key2 = cwd ?? process.cwd();
62653
+ const cached = vcsCache.get(key2);
62654
+ if (cached)
62655
+ return cached;
62656
+ const candidates = [];
62657
+ for (const provider of providerList) {
62658
+ let root3 = null;
62659
+ let detected2 = false;
62660
+ try {
62661
+ if (provider.getRoot) {
62662
+ root3 = await provider.getRoot(cwd);
62663
+ detected2 = root3 !== null;
62664
+ } else {
62665
+ detected2 = await provider.detect(cwd);
62666
+ }
62667
+ } catch {
62668
+ continue;
62669
+ }
62670
+ if (detected2) {
62671
+ candidates.push({ provider, root: root3, order: candidates.length });
62672
+ }
62673
+ }
62674
+ const detected = selectNearestProvider(candidates, cwd) ?? defaultProvider;
62675
+ vcsCache.set(key2, detected);
62676
+ return detected;
62677
+ }
62678
+ function getProviderForDiffType(diffType) {
62679
+ for (const provider of providerList) {
62680
+ if (provider.ownsDiffType(diffType)) {
62681
+ return provider;
62682
+ }
62683
+ }
62684
+ return null;
62685
+ }
62686
+ function getProviderById(id) {
62687
+ return providerList.find((provider) => provider.id === id) ?? null;
62688
+ }
62689
+ function formatVcsName(id) {
62690
+ switch (id) {
62691
+ case "git":
62692
+ return "Git";
62693
+ case "jj":
62694
+ return "JJ";
62695
+ case "p4":
62696
+ return "P4";
62697
+ }
62698
+ }
62699
+ async function getProviderForSelection(vcsType, cwd) {
62700
+ if (!vcsType || vcsType === "auto") {
62701
+ return detectVcs(cwd);
62702
+ }
62703
+ const provider = getProviderById(vcsType);
62704
+ const vcsName = formatVcsName(vcsType);
62705
+ if (!provider) {
62706
+ throw new Error(`${vcsName} support is not available in this runtime.`);
62707
+ }
62708
+ if (!await provider.detect(cwd)) {
62709
+ throw new Error(`${vcsName} workspace not found.`);
62710
+ }
62711
+ return provider;
62712
+ }
62713
+ async function getProviderForOperation(diffType, cwd) {
62714
+ return getProviderForDiffType(diffType) ?? detectVcs(cwd);
62715
+ }
62716
+ async function getContextWithProvider(cwd, vcsType) {
62717
+ const provider = await getProviderForSelection(vcsType, cwd);
62718
+ return { provider, gitContext: await provider.getContext(cwd) };
62719
+ }
62720
+ function resolveRequestedDiffType(provider, gitContext, requestedDiffType, configuredDiffType) {
62721
+ if (requestedDiffType && provider.ownsDiffType(requestedDiffType)) {
62722
+ return requestedDiffType;
62723
+ }
62724
+ return resolveInitialDiffType(gitContext, configuredDiffType);
62725
+ }
62726
+ function resolveInitialBase(gitContext, diffType, requestedBase, ownsRequestedDiffType) {
62727
+ if (gitContext.vcsType === "jj") {
62728
+ if (diffType === "jj-line" && ownsRequestedDiffType && requestedBase) {
62729
+ return requestedBase;
62730
+ }
62731
+ return gitContext.defaultBranch;
62732
+ }
62733
+ return requestedBase ?? gitContext.defaultBranch;
62734
+ }
62735
+ return {
62736
+ detectVcs,
62737
+ async getVcsContext(cwd, vcsType) {
62738
+ return (await getContextWithProvider(cwd, vcsType)).gitContext;
62739
+ },
62740
+ async detectRemoteDefaultCompareTarget(cwd, vcsType) {
62741
+ const provider = await getProviderForSelection(vcsType, cwd);
62742
+ return provider.detectRemoteDefaultCompareTarget?.(cwd) ?? null;
62743
+ },
62744
+ async prepareLocalReviewDiff(options) {
62745
+ const { provider, gitContext } = await getContextWithProvider(options.cwd, options.vcsType);
62746
+ const ownsRequestedDiffType = options.requestedDiffType !== undefined && provider.ownsDiffType(options.requestedDiffType);
62747
+ const diffType = resolveRequestedDiffType(provider, gitContext, options.requestedDiffType, options.configuredDiffType);
62748
+ const base = resolveInitialBase(gitContext, diffType, options.requestedBase, ownsRequestedDiffType);
62749
+ const result = await provider.runDiff(diffType, base, gitContext.cwd ?? options.cwd, {
62750
+ hideWhitespace: options.hideWhitespace
62751
+ });
62752
+ return {
62753
+ gitContext,
62754
+ diffType,
62755
+ base,
62756
+ rawPatch: result.patch,
62757
+ gitRef: result.label,
62758
+ error: result.error
62759
+ };
62760
+ },
62761
+ async runVcsDiff(diffType, defaultBranch = "main", cwd, options) {
62762
+ const provider = await getProviderForOperation(diffType, cwd);
62763
+ return provider.runDiff(diffType, defaultBranch, cwd, options);
62764
+ },
62765
+ async getVcsFileContentsForDiff(diffType, defaultBranch, filePath, oldPath, cwd) {
62766
+ const provider = await getProviderForOperation(diffType, cwd);
62767
+ return provider.getFileContents(diffType, defaultBranch, filePath, oldPath, cwd);
62768
+ },
62769
+ async canStageFiles(diffType, cwd) {
62770
+ const provider = await getProviderForOperation(diffType, cwd);
62771
+ return provider.stageFile !== undefined && (provider.canStageFiles?.(diffType) ?? false);
62772
+ },
62773
+ async stageFile(diffType, filePath, cwd) {
62774
+ const provider = await getProviderForOperation(diffType, cwd);
62775
+ if (!provider.stageFile || !(provider.canStageFiles?.(diffType) ?? false)) {
62776
+ throw new Error(`Staging not available for ${provider.id}`);
62777
+ }
62778
+ return provider.stageFile(filePath, cwd);
62779
+ },
62780
+ async unstageFile(diffType, filePath, cwd) {
62781
+ const provider = await getProviderForOperation(diffType, cwd);
62782
+ if (!provider.unstageFile || !(provider.canStageFiles?.(diffType) ?? false)) {
62783
+ throw new Error(`Unstaging not available for ${provider.id}`);
62784
+ }
62785
+ return provider.unstageFile(filePath, cwd);
62786
+ },
62787
+ resolveVcsCwd(diffType, fallbackCwd) {
62788
+ const provider = getProviderForDiffType(diffType);
62789
+ return provider?.resolveCwd?.(diffType, fallbackCwd) ?? fallbackCwd;
62790
+ }
62791
+ };
62792
+ }
62793
+ function resolveInitialDiffType(gitContext, configuredDiffType) {
62794
+ if (gitContext.vcsType === "p4") {
62795
+ return "p4-default";
62796
+ }
62797
+ if (gitContext.vcsType === "jj") {
62798
+ return "jj-current";
62799
+ }
62800
+ if (gitContext.diffOptions.some((option2) => option2.id === configuredDiffType)) {
62801
+ return configuredDiffType;
62802
+ }
62803
+ const fallback = gitContext.diffOptions[0]?.id;
62804
+ return fallback ? fallback : configuredDiffType;
62313
62805
  }
62314
62806
 
62315
62807
  // ../../packages/server/p4.ts
@@ -62605,43 +63097,66 @@ async function getP4FileContentsForDiff(diffType, filePath, cwd) {
62605
63097
  };
62606
63098
  }
62607
63099
 
62608
- // ../../packages/server/vcs.ts
62609
- var GIT_DIFF_TYPES = new Set(["uncommitted", "staged", "unstaged", "last-commit", "branch", "merge-base", "all"]);
62610
- var gitProvider = {
62611
- id: "git",
62612
- async detect(cwd) {
63100
+ // ../../packages/server/git.ts
63101
+ async function runGit(args, options) {
63102
+ const proc = Bun.spawn(["git", "-c", "core.quotePath=false", ...args], {
63103
+ cwd: options?.cwd,
63104
+ stdout: "pipe",
63105
+ stderr: "pipe"
63106
+ });
63107
+ let timer;
63108
+ if (options?.timeoutMs) {
63109
+ timer = setTimeout(() => proc.kill(), options.timeoutMs);
63110
+ }
63111
+ const [stdout, stderr, exitCode] = await Promise.all([
63112
+ new Response(proc.stdout).text(),
63113
+ new Response(proc.stderr).text(),
63114
+ proc.exited
63115
+ ]);
63116
+ if (timer)
63117
+ clearTimeout(timer);
63118
+ return { stdout, stderr, exitCode };
63119
+ }
63120
+ var runtime = {
63121
+ runGit,
63122
+ async readTextFile(path2) {
62613
63123
  try {
62614
- const proc = Bun.spawn(["git", "rev-parse", "--is-inside-work-tree"], {
62615
- cwd: cwd ?? undefined,
62616
- stdout: "ignore",
62617
- stderr: "ignore"
62618
- });
62619
- return await proc.exited === 0;
63124
+ return await Bun.file(path2).text();
62620
63125
  } catch {
62621
- return false;
63126
+ return null;
62622
63127
  }
62623
- },
62624
- ownsDiffType(diffType) {
62625
- return GIT_DIFF_TYPES.has(diffType) || diffType.startsWith("worktree:");
62626
- },
62627
- getContext: getGitContext2,
62628
- runDiff(diffType, defaultBranch, cwd, options) {
62629
- return runGitDiff2(diffType, defaultBranch, cwd, options);
62630
- },
62631
- getFileContents(diffType, defaultBranch, filePath, oldPath, cwd) {
62632
- return getFileContentsForDiff2(diffType, defaultBranch, filePath, oldPath, cwd);
62633
- },
62634
- stageFile: gitAddFile2,
62635
- unstageFile: gitResetFile2,
62636
- resolveCwd(diffType, fallbackCwd) {
62637
- if (diffType.startsWith("worktree:")) {
62638
- const parsed = parseWorktreeDiffType(diffType);
62639
- if (parsed)
62640
- return parsed.path;
63128
+ }
63129
+ };
63130
+
63131
+ // ../../packages/server/jj.ts
63132
+ async function runJj(args, options) {
63133
+ try {
63134
+ const proc = Bun.spawn(["jj", ...args], {
63135
+ cwd: options?.cwd,
63136
+ stdout: "pipe",
63137
+ stderr: "pipe"
63138
+ });
63139
+ let timer;
63140
+ if (options?.timeoutMs) {
63141
+ timer = setTimeout(() => proc.kill(), options.timeoutMs);
62641
63142
  }
62642
- return fallbackCwd;
63143
+ const [stdout, stderr, exitCode] = await Promise.all([
63144
+ new Response(proc.stdout).text(),
63145
+ new Response(proc.stderr).text(),
63146
+ proc.exited
63147
+ ]);
63148
+ if (timer)
63149
+ clearTimeout(timer);
63150
+ return { stdout, stderr, exitCode };
63151
+ } catch {
63152
+ return { stdout: "", stderr: "jj not found", exitCode: 1 };
62643
63153
  }
63154
+ }
63155
+ var runtime2 = {
63156
+ runJj
62644
63157
  };
63158
+
63159
+ // ../../packages/server/vcs.ts
62645
63160
  var p4Provider = {
62646
63161
  id: "p4",
62647
63162
  async detect(cwd) {
@@ -62658,63 +63173,23 @@ var p4Provider = {
62658
63173
  return getP4FileContentsForDiff(diffType, filePath, cwd);
62659
63174
  }
62660
63175
  };
62661
- var providers = [gitProvider, p4Provider];
62662
- var vcsCache = new Map;
62663
- async function detectVcs(cwd) {
62664
- const key2 = cwd ?? process.cwd();
62665
- const cached = vcsCache.get(key2);
62666
- if (cached)
62667
- return cached;
62668
- for (const provider of providers) {
62669
- if (await provider.detect(cwd)) {
62670
- vcsCache.set(key2, provider);
62671
- return provider;
62672
- }
62673
- }
62674
- vcsCache.set(key2, gitProvider);
62675
- return gitProvider;
62676
- }
62677
- function getProviderForDiffType(diffType) {
62678
- for (const provider of providers) {
62679
- if (provider.ownsDiffType(diffType))
62680
- return provider;
62681
- }
62682
- return gitProvider;
62683
- }
62684
- async function getVcsContext(cwd) {
62685
- const provider = await detectVcs(cwd);
62686
- return provider.getContext(cwd);
62687
- }
62688
- async function runVcsDiff(diffType, defaultBranch = "main", cwd, options) {
62689
- const provider = getProviderForDiffType(diffType);
62690
- return provider.runDiff(diffType, defaultBranch, cwd, options);
62691
- }
62692
- async function getVcsFileContentsForDiff(diffType, defaultBranch, filePath, oldPath, cwd) {
62693
- const provider = getProviderForDiffType(diffType);
62694
- return provider.getFileContents(diffType, defaultBranch, filePath, oldPath, cwd);
62695
- }
62696
- function canStageFiles(diffType) {
62697
- const provider = getProviderForDiffType(diffType);
62698
- return provider.stageFile !== undefined;
62699
- }
62700
- async function stageFile(diffType, filePath, cwd) {
62701
- const provider = getProviderForDiffType(diffType);
62702
- if (!provider.stageFile) {
62703
- throw new Error(`Staging not available for ${provider.id}`);
62704
- }
62705
- return provider.stageFile(filePath, cwd);
62706
- }
62707
- async function unstageFile(diffType, filePath, cwd) {
62708
- const provider = getProviderForDiffType(diffType);
62709
- if (!provider.unstageFile) {
62710
- throw new Error(`Unstaging not available for ${provider.id}`);
62711
- }
62712
- return provider.unstageFile(filePath, cwd);
62713
- }
62714
- function resolveVcsCwd(diffType, fallbackCwd) {
62715
- const provider = getProviderForDiffType(diffType);
62716
- return provider.resolveCwd?.(diffType, fallbackCwd) ?? fallbackCwd;
62717
- }
63176
+ var api = createVcsApi([
63177
+ createJjProvider(runtime2),
63178
+ createGitProvider(runtime),
63179
+ p4Provider
63180
+ ]);
63181
+ var {
63182
+ detectVcs,
63183
+ getVcsContext,
63184
+ detectRemoteDefaultCompareTarget,
63185
+ prepareLocalReviewDiff,
63186
+ runVcsDiff,
63187
+ getVcsFileContentsForDiff,
63188
+ canStageFiles,
63189
+ stageFile,
63190
+ unstageFile,
63191
+ resolveVcsCwd
63192
+ } = api;
62718
63193
 
62719
63194
  // ../../packages/shared/pr-stack.ts
62720
63195
  function branchNameIsSafe(branch) {
@@ -62763,17 +63238,17 @@ function getPRDiffScopeOptions(metadata, hasLocalCheckout) {
62763
63238
  }
62764
63239
  ];
62765
63240
  }
62766
- async function resolvePRFullStackBaseRef(runtime2, defaultBranch, cwd) {
63241
+ async function resolvePRFullStackBaseRef(runtime3, defaultBranch, cwd) {
62767
63242
  const remoteRef = `origin/${defaultBranch}`;
62768
- const remote = await runtime2.runGit(["show-ref", "--verify", "--quiet", `refs/remotes/${remoteRef}`], { cwd });
63243
+ const remote = await runtime3.runGit(["show-ref", "--verify", "--quiet", `refs/remotes/${remoteRef}`], { cwd });
62769
63244
  if (remote.exitCode === 0)
62770
63245
  return remoteRef;
62771
- const local = await runtime2.runGit(["show-ref", "--verify", "--quiet", `refs/heads/${defaultBranch}`], { cwd });
63246
+ const local = await runtime3.runGit(["show-ref", "--verify", "--quiet", `refs/heads/${defaultBranch}`], { cwd });
62772
63247
  if (local.exitCode === 0)
62773
63248
  return defaultBranch;
62774
63249
  return null;
62775
63250
  }
62776
- async function runPRFullStackDiff(runtime2, metadata, cwd) {
63251
+ async function runPRFullStackDiff(runtime3, metadata, cwd) {
62777
63252
  const defaultBranch = metadata.defaultBranch;
62778
63253
  if (!defaultBranch || !branchNameIsSafe(defaultBranch)) {
62779
63254
  return {
@@ -62782,7 +63257,7 @@ async function runPRFullStackDiff(runtime2, metadata, cwd) {
62782
63257
  error: "Could not determine a safe default branch for this review."
62783
63258
  };
62784
63259
  }
62785
- const baseRef = await resolvePRFullStackBaseRef(runtime2, defaultBranch, cwd);
63260
+ const baseRef = await resolvePRFullStackBaseRef(runtime3, defaultBranch, cwd);
62786
63261
  if (!baseRef) {
62787
63262
  return {
62788
63263
  patch: "",
@@ -62798,7 +63273,7 @@ async function runPRFullStackDiff(runtime2, metadata, cwd) {
62798
63273
  "--end-of-options",
62799
63274
  `${baseRef}...HEAD`
62800
63275
  ];
62801
- const diff = await runtime2.runGit(diffArgs, { cwd });
63276
+ const diff = await runtime3.runGit(diffArgs, { cwd });
62802
63277
  if (diff.exitCode !== 0) {
62803
63278
  const message = diff.stderr.trim() || `git ${diffArgs.join(" ")} failed`;
62804
63279
  return {
@@ -62813,21 +63288,21 @@ async function runPRFullStackDiff(runtime2, metadata, cwd) {
62813
63288
  label: `Full stack diff vs ${baseRef}`
62814
63289
  };
62815
63290
  }
62816
- async function checkoutPRHead(runtime2, metadata, cwd) {
63291
+ async function checkoutPRHead(runtime3, metadata, cwd) {
62817
63292
  const refSpec = metadata.platform === "github" ? `refs/pull/${metadata.number}/head` : `refs/merge-requests/${metadata.iid}/head`;
62818
- const fetch2 = await runtime2.runGit(["fetch", "origin", refSpec], { cwd });
63293
+ const fetch2 = await runtime3.runGit(["fetch", "origin", refSpec], { cwd });
62819
63294
  if (fetch2.exitCode !== 0)
62820
63295
  return false;
62821
- const checkout = await runtime2.runGit(["checkout", "FETCH_HEAD"], { cwd });
63296
+ const checkout = await runtime3.runGit(["checkout", "FETCH_HEAD"], { cwd });
62822
63297
  return checkout.exitCode === 0;
62823
63298
  }
62824
63299
 
62825
63300
  // ../../packages/server/path-utils.ts
62826
- import { relative } from "path";
63301
+ import { relative as relative2 } from "path";
62827
63302
  function toRelativePath2(absolutePath, cwd) {
62828
63303
  if (!cwd)
62829
63304
  return absolutePath;
62830
- const rel = relative(cwd, absolutePath);
63305
+ const rel = relative2(cwd, absolutePath);
62831
63306
  if (rel.startsWith(".."))
62832
63307
  return absolutePath;
62833
63308
  return rel.replace(/\\/g, "/");
@@ -62877,7 +63352,7 @@ project guidance files explicitly expand your scope.
62877
63352
  ## Pipeline
62878
63353
 
62879
63354
  Step 1: Gather context
62880
- - Retrieve the PR diff (gh pr diff or git diff)
63355
+ - Retrieve the PR diff or local diff (gh pr diff, git diff, or jj diff)
62881
63356
  - Read CLAUDE.md and REVIEW.md at the repo root and in every directory
62882
63357
  containing modified files
62883
63358
  - Build a map of which rules apply to which file paths
@@ -62995,6 +63470,13 @@ function buildClaudeCommand(prompt, model = "claude-opus-4-7", effort) {
62995
63470
  "Bash(git remote:*)",
62996
63471
  "Bash(git rev-parse:*)",
62997
63472
  "Bash(git show-ref:*)",
63473
+ "Bash(jj status:*)",
63474
+ "Bash(jj diff:*)",
63475
+ "Bash(jj log:*)",
63476
+ "Bash(jj show:*)",
63477
+ "Bash(jj file show:*)",
63478
+ "Bash(jj cat:*)",
63479
+ "Bash(jj bookmark list:*)",
62998
63480
  "Bash(wc:*)"
62999
63481
  ].join(",");
63000
63482
  const disallowedTools = [
@@ -63610,64 +64092,6 @@ Ignore non-blocking issues such as style, formatting, typos, documentation, and
63610
64092
 
63611
64093
  FORMATTING GUIDELINES:
63612
64094
  The finding description should be one paragraph.`;
63613
- function buildCodexReviewUserMessage(patch, diffType, options, prMetadata) {
63614
- if (prMetadata) {
63615
- if (options?.prDiffScope === "full-stack") {
63616
- return [
63617
- `Full-stack review of ${prMetadata.url}`,
63618
- "",
63619
- "This is a stacked PR. The diff below shows ALL accumulated changes from the repository default branch through this PR's head (not just this PR's own layer).",
63620
- "Review the complete diff for issues that span the stack.",
63621
- "",
63622
- "```diff",
63623
- patch,
63624
- "```"
63625
- ].join(`
63626
- `);
63627
- }
63628
- if (options?.hasLocalAccess) {
63629
- return [
63630
- prMetadata.url,
63631
- "",
63632
- "You are in a local worktree checked out at the PR head. The code is available locally.",
63633
- `To see the PR changes, diff against the remote base branch: git diff origin/${prMetadata.baseBranch}...HEAD`,
63634
- "Do NOT diff against the local `main` branch \u2014 it may be stale. Always use origin/."
63635
- ].join(`
63636
- `);
63637
- }
63638
- return prMetadata.url;
63639
- }
63640
- const effectiveDiffType = diffType.startsWith("worktree:") ? diffType.split(":").pop() || "uncommitted" : diffType;
63641
- switch (effectiveDiffType) {
63642
- case "uncommitted":
63643
- return "Review the current code changes (staged, unstaged, and untracked files) and provide prioritized findings.";
63644
- case "staged":
63645
- return "Review the currently staged code changes (`git diff --staged`) and provide prioritized findings.";
63646
- case "unstaged":
63647
- return "Review the unstaged code changes (tracked modifications and untracked files) and provide prioritized findings.";
63648
- case "last-commit":
63649
- return "Review the code changes introduced in the last commit (`git diff HEAD~1..HEAD`) and provide prioritized findings.";
63650
- case "branch": {
63651
- const base = options?.defaultBranch || "main";
63652
- return `Review the code changes against the base branch '${base}'. Run \`git diff ${base}..HEAD\` to inspect the changes. Provide prioritized, actionable findings.`;
63653
- }
63654
- case "merge-base": {
63655
- const base = options?.defaultBranch || "main";
63656
- return `Review the PR-style diff against base '${base}'. First find the common ancestor with \`git merge-base ${base} HEAD\`, then run \`git diff <merge-base>..HEAD\` using that commit to inspect only the changes introduced on this branch (matches GitHub's PR view). Provide prioritized, actionable findings.`;
63657
- }
63658
- case "all":
63659
- return "Review every file in the repository (all files shown as additions, diffed against an empty tree). Provide prioritized, actionable findings.";
63660
- default:
63661
- return [
63662
- "Review the following code changes and provide prioritized findings.",
63663
- "",
63664
- "```diff",
63665
- patch,
63666
- "```"
63667
- ].join(`
63668
- `);
63669
- }
63670
- }
63671
64095
  async function buildCodexCommand(options) {
63672
64096
  const { cwd, outputPath, prompt, model, reasoningEffort, fastMode } = options;
63673
64097
  const schemaPath = await ensureSchemaFile();
@@ -63760,6 +64184,124 @@ ${f3.body}`.trim(),
63760
64184
  return annotations;
63761
64185
  }
63762
64186
 
64187
+ // ../../packages/server/agent-review-message.ts
64188
+ function buildAgentReviewUserMessage(patch, diffType, options, prMetadata) {
64189
+ if (prMetadata) {
64190
+ if (options?.prDiffScope === "full-stack") {
64191
+ return [
64192
+ `Full-stack review of ${prMetadata.url}`,
64193
+ "",
64194
+ "This is a stacked PR. The diff below shows ALL accumulated changes from the repository default branch through this PR's head (not just this PR's own layer).",
64195
+ "Review the complete diff for issues that span the stack.",
64196
+ "",
64197
+ "```diff",
64198
+ patch,
64199
+ "```"
64200
+ ].join(`
64201
+ `);
64202
+ }
64203
+ if (options?.hasLocalAccess) {
64204
+ return [
64205
+ prMetadata.url,
64206
+ "",
64207
+ "You are in a local worktree checked out at the PR head. The code is available locally.",
64208
+ `To see the PR changes, diff against the remote base branch: git diff origin/${prMetadata.baseBranch}...HEAD`,
64209
+ "Do NOT diff against the local `main` branch; it may be stale. Always use origin/."
64210
+ ].join(`
64211
+ `);
64212
+ }
64213
+ return prMetadata.url;
64214
+ }
64215
+ const instruction = getLocalDiffInstruction(diffType, options?.defaultBranch);
64216
+ if (instruction) {
64217
+ return `Review ${instruction.target}. ${instruction.inspect} Provide prioritized, actionable findings.`;
64218
+ }
64219
+ return [
64220
+ "Review the following code changes and provide prioritized findings.",
64221
+ "",
64222
+ "```diff",
64223
+ patch,
64224
+ "```"
64225
+ ].join(`
64226
+ `);
64227
+ }
64228
+ function getLocalDiffInstruction(diffType, defaultBranch) {
64229
+ const effectiveDiffType = normalizeLocalDiffType(diffType);
64230
+ switch (effectiveDiffType) {
64231
+ case "uncommitted":
64232
+ return {
64233
+ target: "the current code changes (staged, unstaged, and untracked files)",
64234
+ inspect: "Inspect the working tree changes locally."
64235
+ };
64236
+ case "staged":
64237
+ return {
64238
+ target: "the currently staged code changes",
64239
+ inspect: "Run `git diff --staged` to inspect the changes."
64240
+ };
64241
+ case "unstaged":
64242
+ return {
64243
+ target: "the unstaged code changes (tracked modifications and untracked files)",
64244
+ inspect: "Inspect the unstaged working tree changes locally."
64245
+ };
64246
+ case "last-commit":
64247
+ return {
64248
+ target: "the code changes introduced in the last commit",
64249
+ inspect: "Run `git diff HEAD~1..HEAD` to inspect the changes."
64250
+ };
64251
+ case "branch": {
64252
+ const base = defaultBranch || "main";
64253
+ return {
64254
+ target: `the code changes against the base branch '${base}'`,
64255
+ inspect: `Run \`git diff ${base}..HEAD\` to inspect the changes.`
64256
+ };
64257
+ }
64258
+ case "merge-base": {
64259
+ const base = defaultBranch || "main";
64260
+ return {
64261
+ target: `the PR-style diff against base '${base}'`,
64262
+ inspect: `First find the common ancestor with \`git merge-base ${base} HEAD\`, then run \`git diff <merge-base>..HEAD\` using that commit to inspect only the changes introduced on this branch (matches GitHub's PR view).`
64263
+ };
64264
+ }
64265
+ case "all":
64266
+ return {
64267
+ target: "every file in the repository",
64268
+ inspect: "All files are shown as additions, diffed against an empty tree."
64269
+ };
64270
+ case "jj-current":
64271
+ return {
64272
+ target: "the current JJ change",
64273
+ inspect: "Run `jj diff --git -r @` to inspect the changes."
64274
+ };
64275
+ case "jj-last":
64276
+ return {
64277
+ target: "the previous JJ change",
64278
+ inspect: "Run `jj diff --git -r @-` to inspect the changes."
64279
+ };
64280
+ case "jj-line": {
64281
+ const base = defaultBranch || JJ_TRUNK_REVSET;
64282
+ const baseRevset = jjLineBaseRevset(base);
64283
+ return {
64284
+ target: `the JJ line of work against \`${base}\``,
64285
+ inspect: `Run \`jj diff --git --from ${shellQuote(baseRevset)} --to @\` to inspect the changes.`
64286
+ };
64287
+ }
64288
+ case "jj-all":
64289
+ return {
64290
+ target: "all files in the JJ workspace",
64291
+ inspect: "Run `jj diff --git --from 'root()' --to @` to inspect the changes."
64292
+ };
64293
+ default:
64294
+ return null;
64295
+ }
64296
+ }
64297
+ function normalizeLocalDiffType(diffType) {
64298
+ const worktree = parseWorktreeDiffType(diffType);
64299
+ return worktree?.subType ?? diffType;
64300
+ }
64301
+ function shellQuote(value) {
64302
+ return `'${value.replaceAll("'", "'\\''")}'`;
64303
+ }
64304
+
63763
64305
  // ../../packages/server/tour/tour-review.ts
63764
64306
  import { join as join10 } from "path";
63765
64307
  import { homedir as homedir6, tmpdir as tmpdir3 } from "os";
@@ -63992,7 +64534,7 @@ should reference at least one stop.
63992
64534
 
63993
64535
  ## Pipeline
63994
64536
 
63995
- 1. Read the full diff (git diff or inlined patch).
64537
+ 1. Read the full diff (git diff, jj diff, or inlined patch).
63996
64538
  2. Read CLAUDE.md and README.md for project context.
63997
64539
  3. Read commit messages (git log --oneline) and PR title/body if available.
63998
64540
  4. Identify logical groupings of change (cross-file when appropriate). These
@@ -64062,36 +64604,18 @@ function buildTourUserMessage(patch, diffType, options, prMetadata) {
64062
64604
  return [prMetadata.url, "", "Walk the reviewer through this PR as a guided tour."].join(`
64063
64605
  `);
64064
64606
  }
64065
- const effectiveDiffType = diffType.startsWith("worktree:") ? diffType.split(":").pop() || "uncommitted" : diffType;
64066
- switch (effectiveDiffType) {
64067
- case "uncommitted":
64068
- return "Walk the reviewer through the current code changes (staged, unstaged, and untracked files) as a guided tour.";
64069
- case "staged":
64070
- return "Walk the reviewer through the currently staged code changes (`git diff --staged`) as a guided tour.";
64071
- case "unstaged":
64072
- return "Walk the reviewer through the unstaged code changes (tracked modifications and untracked files) as a guided tour.";
64073
- case "last-commit":
64074
- return "Walk the reviewer through the code changes introduced in the last commit (`git diff HEAD~1..HEAD`) as a guided tour.";
64075
- case "branch": {
64076
- const base = options?.defaultBranch || "main";
64077
- return `Walk the reviewer through the code changes against the base branch '${base}' as a guided tour. Run \`git diff ${base}..HEAD\` to inspect the changes.`;
64078
- }
64079
- case "merge-base": {
64080
- const base = options?.defaultBranch || "main";
64081
- return `Walk the reviewer through the PR-style diff against base '${base}' as a guided tour. First find the common ancestor with \`git merge-base ${base} HEAD\`, then run \`git diff <merge-base>..HEAD\` using that commit to inspect only the changes introduced on this branch (matches GitHub's PR view).`;
64082
- }
64083
- case "all":
64084
- return "Walk the reviewer through every file in the repository as a guided tour. All files are shown as additions (diffed against an empty tree).";
64085
- default:
64086
- return [
64087
- "Walk the reviewer through the following code changes as a guided tour.",
64088
- "",
64089
- "```diff",
64090
- patch,
64091
- "```"
64092
- ].join(`
64093
- `);
64607
+ const instruction = getLocalDiffInstruction(diffType, options?.defaultBranch);
64608
+ if (instruction) {
64609
+ return `Walk the reviewer through ${instruction.target} as a guided tour. ${instruction.inspect}`;
64094
64610
  }
64611
+ return [
64612
+ "Walk the reviewer through the following code changes as a guided tour.",
64613
+ "",
64614
+ "```diff",
64615
+ patch,
64616
+ "```"
64617
+ ].join(`
64618
+ `);
64095
64619
  }
64096
64620
  function buildTourClaudeCommand(prompt, model = "sonnet", effort) {
64097
64621
  const allowedTools = [
@@ -64112,6 +64636,13 @@ function buildTourClaudeCommand(prompt, model = "sonnet", effort) {
64112
64636
  "Bash(git remote:*)",
64113
64637
  "Bash(git rev-parse:*)",
64114
64638
  "Bash(git show-ref:*)",
64639
+ "Bash(jj status:*)",
64640
+ "Bash(jj diff:*)",
64641
+ "Bash(jj log:*)",
64642
+ "Bash(jj show:*)",
64643
+ "Bash(jj file show:*)",
64644
+ "Bash(jj cat:*)",
64645
+ "Bash(jj bookmark list:*)",
64115
64646
  "Bash(gh pr view:*)",
64116
64647
  "Bash(gh pr diff:*)",
64117
64648
  "Bash(gh pr list:*)",
@@ -64315,8 +64846,8 @@ function hostnameArgs(host, args) {
64315
64846
  }
64316
64847
  return args;
64317
64848
  }
64318
- async function checkGhAuth(runtime2, host) {
64319
- const result = await runtime2.runCommand("gh", hostnameArgs(host, ["auth", "status"]));
64849
+ async function checkGhAuth(runtime3, host) {
64850
+ const result = await runtime3.runCommand("gh", hostnameArgs(host, ["auth", "status"]));
64320
64851
  if (result.exitCode !== 0) {
64321
64852
  const stderr = result.stderr.trim();
64322
64853
  const hostHint = host !== "github.com" ? ` --hostname ${host}` : "";
@@ -64324,9 +64855,9 @@ async function checkGhAuth(runtime2, host) {
64324
64855
  ${stderr}`);
64325
64856
  }
64326
64857
  }
64327
- async function getGhUser(runtime2, host) {
64858
+ async function getGhUser(runtime3, host) {
64328
64859
  try {
64329
- const result = await runtime2.runCommand("gh", hostnameArgs(host, ["api", "user", "--jq", ".login"]));
64860
+ const result = await runtime3.runCommand("gh", hostnameArgs(host, ["api", "user", "--jq", ".login"]));
64330
64861
  if (result.exitCode === 0 && result.stdout.trim()) {
64331
64862
  return result.stdout.trim();
64332
64863
  }
@@ -64335,17 +64866,17 @@ async function getGhUser(runtime2, host) {
64335
64866
  return null;
64336
64867
  }
64337
64868
  }
64338
- async function fetchGhPR(runtime2, ref) {
64869
+ async function fetchGhPR(runtime3, ref) {
64339
64870
  const repo = repoFlag(ref);
64340
64871
  const [diffResult, viewResult, repoResult] = await Promise.all([
64341
- runtime2.runCommand("gh", [
64872
+ runtime3.runCommand("gh", [
64342
64873
  "pr",
64343
64874
  "diff",
64344
64875
  String(ref.number),
64345
64876
  "--repo",
64346
64877
  repo
64347
64878
  ]),
64348
- runtime2.runCommand("gh", [
64879
+ runtime3.runCommand("gh", [
64349
64880
  "pr",
64350
64881
  "view",
64351
64882
  String(ref.number),
@@ -64354,7 +64885,7 @@ async function fetchGhPR(runtime2, ref) {
64354
64885
  "--json",
64355
64886
  "id,title,author,baseRefName,headRefName,baseRefOid,headRefOid,url"
64356
64887
  ]),
64357
- runtime2.runCommand("gh", [
64888
+ runtime3.runCommand("gh", [
64358
64889
  "repo",
64359
64890
  "view",
64360
64891
  repo,
@@ -64373,7 +64904,7 @@ async function fetchGhPR(runtime2, ref) {
64373
64904
  const raw2 = JSON.parse(viewResult.stdout);
64374
64905
  let mergeBaseSha;
64375
64906
  try {
64376
- const compareResult = await runtime2.runCommand("gh", hostnameArgs(ref.host, [
64907
+ const compareResult = await runtime3.runCommand("gh", hostnameArgs(ref.host, [
64377
64908
  "api",
64378
64909
  `repos/${ref.owner}/${ref.repo}/compare/${raw2.baseRefOid}...${raw2.headRefOid}`,
64379
64910
  "--jq",
@@ -64460,9 +64991,9 @@ function parseGhPRContext(raw2) {
64460
64991
  }))
64461
64992
  };
64462
64993
  }
64463
- async function fetchGhPRContext(runtime2, ref) {
64994
+ async function fetchGhPRContext(runtime3, ref) {
64464
64995
  const repo = repoFlag(ref);
64465
- const result = await runtime2.runCommand("gh", [
64996
+ const result = await runtime3.runCommand("gh", [
64466
64997
  "pr",
64467
64998
  "view",
64468
64999
  String(ref.number),
@@ -64477,7 +65008,7 @@ async function fetchGhPRContext(runtime2, ref) {
64477
65008
  const raw2 = JSON.parse(result.stdout);
64478
65009
  const context = parseGhPRContext(raw2);
64479
65010
  try {
64480
- context.reviewThreads = await fetchGhReviewThreads(runtime2, ref);
65011
+ context.reviewThreads = await fetchGhReviewThreads(runtime3, ref);
64481
65012
  } catch {
64482
65013
  context.reviewThreads = [];
64483
65014
  }
@@ -64511,8 +65042,8 @@ query($owner: String!, $repo: String!, $number: Int!) {
64511
65042
  }
64512
65043
  }
64513
65044
  }`;
64514
- async function fetchGhReviewThreads(runtime2, ref) {
64515
- const result = await runtime2.runCommand("gh", hostnameArgs(ref.host, [
65045
+ async function fetchGhReviewThreads(runtime3, ref) {
65046
+ const result = await runtime3.runCommand("gh", hostnameArgs(ref.host, [
64516
65047
  "api",
64517
65048
  "graphql",
64518
65049
  "-f",
@@ -64548,8 +65079,8 @@ async function fetchGhReviewThreads(runtime2, ref) {
64548
65079
  })) : []
64549
65080
  }));
64550
65081
  }
64551
- async function fetchGhPRFileContent(runtime2, ref, sha, filePath) {
64552
- const result = await runtime2.runCommand("gh", hostnameArgs(ref.host, [
65082
+ async function fetchGhPRFileContent(runtime3, ref, sha, filePath) {
65083
+ const result = await runtime3.runCommand("gh", hostnameArgs(ref.host, [
64553
65084
  "api",
64554
65085
  `repos/${ref.owner}/${ref.repo}/contents/${encodeApiFilePath(filePath)}?ref=${sha}`,
64555
65086
  "--jq",
@@ -64567,7 +65098,7 @@ async function fetchGhPRFileContent(runtime2, ref, sha, filePath) {
64567
65098
  return null;
64568
65099
  }
64569
65100
  }
64570
- async function fetchGhPRViewedFiles(runtime2, ref) {
65101
+ async function fetchGhPRViewedFiles(runtime3, ref) {
64571
65102
  const query = `
64572
65103
  query($owner: String!, $repo: String!, $number: Int!, $cursor: String) {
64573
65104
  repository(owner: $owner, name: $repo) {
@@ -64604,7 +65135,7 @@ async function fetchGhPRViewedFiles(runtime2, ref) {
64604
65135
  if (cursor) {
64605
65136
  args.push("-F", `cursor=${cursor}`);
64606
65137
  }
64607
- const res = await runtime2.runCommand("gh", args);
65138
+ const res = await runtime3.runCommand("gh", args);
64608
65139
  if (res.exitCode !== 0) {
64609
65140
  throw new Error(`Failed to fetch PR viewed files: ${res.stderr.trim() || `exit code ${res.exitCode}`}`);
64610
65141
  }
@@ -64622,7 +65153,7 @@ async function fetchGhPRViewedFiles(runtime2, ref) {
64622
65153
  } while (cursor !== null);
64623
65154
  return result;
64624
65155
  }
64625
- async function markGhFilesViewed(runtime2, ref, prNodeId, filePaths, viewed) {
65156
+ async function markGhFilesViewed(runtime3, ref, prNodeId, filePaths, viewed) {
64626
65157
  if (filePaths.length === 0)
64627
65158
  return;
64628
65159
  const mutationName = viewed ? "markFileAsViewed" : "unmarkFileAsViewed";
@@ -64633,7 +65164,7 @@ async function markGhFilesViewed(runtime2, ref, prNodeId, filePaths, viewed) {
64633
65164
  }
64634
65165
  }
64635
65166
  `;
64636
- const results = await Promise.allSettled(filePaths.map((path2) => runtime2.runCommandWithInput ? runtime2.runCommand("gh", hostnameArgs(ref.host, [
65167
+ const results = await Promise.allSettled(filePaths.map((path2) => runtime3.runCommandWithInput ? runtime3.runCommand("gh", hostnameArgs(ref.host, [
64637
65168
  "api",
64638
65169
  "graphql",
64639
65170
  "-f",
@@ -64648,7 +65179,7 @@ async function markGhFilesViewed(runtime2, ref, prNodeId, filePaths, viewed) {
64648
65179
  throw new Error(`Failed to ${mutationName} all files: ${failures[0].reason}`);
64649
65180
  }
64650
65181
  }
64651
- async function submitGhPRReview(runtime2, ref, headSha, action, body3, fileComments) {
65182
+ async function submitGhPRReview(runtime3, ref, headSha, action, body3, fileComments) {
64652
65183
  const payload = JSON.stringify({
64653
65184
  commit_id: headSha,
64654
65185
  body: body3,
@@ -64657,8 +65188,8 @@ async function submitGhPRReview(runtime2, ref, headSha, action, body3, fileComme
64657
65188
  });
64658
65189
  const endpoint = `repos/${ref.owner}/${ref.repo}/pulls/${ref.number}/reviews`;
64659
65190
  let result;
64660
- if (runtime2.runCommandWithInput) {
64661
- result = await runtime2.runCommandWithInput("gh", hostnameArgs(ref.host, ["api", endpoint, "--method", "POST", "--input", "-"]), payload);
65191
+ if (runtime3.runCommandWithInput) {
65192
+ result = await runtime3.runCommandWithInput("gh", hostnameArgs(ref.host, ["api", endpoint, "--method", "POST", "--input", "-"]), payload);
64662
65193
  } else {
64663
65194
  throw new Error("Runtime does not support stdin input; cannot submit PR review");
64664
65195
  }
@@ -64679,9 +65210,9 @@ query($owner: String!, $repo: String!, $${varName}: String!) {
64679
65210
  }
64680
65211
  }`;
64681
65212
  }
64682
- async function queryPRsByRef(runtime2, ref, kind, refName) {
65213
+ async function queryPRsByRef(runtime3, ref, kind, refName) {
64683
65214
  const varName = kind === "head" ? "headRefName" : "baseRefName";
64684
- const result = await runtime2.runCommand("gh", hostnameArgs(ref.host, [
65215
+ const result = await runtime3.runCommand("gh", hostnameArgs(ref.host, [
64685
65216
  "api",
64686
65217
  "graphql",
64687
65218
  "-f",
@@ -64699,7 +65230,7 @@ async function queryPRsByRef(runtime2, ref, kind, refName) {
64699
65230
  const prs = data?.data?.repository?.pullRequests?.nodes;
64700
65231
  return Array.isArray(prs) ? prs : [];
64701
65232
  }
64702
- async function fetchGhPRStack(runtime2, ref, metadata) {
65233
+ async function fetchGhPRStack(runtime3, ref, metadata) {
64703
65234
  if (metadata.platform !== "github")
64704
65235
  return null;
64705
65236
  const defaultBranch = metadata.defaultBranch;
@@ -64719,7 +65250,7 @@ async function fetchGhPRStack(runtime2, ref, metadata) {
64719
65250
  for (let i3 = 0;i3 < maxDepth; i3++) {
64720
65251
  if (nextHead === defaultBranch)
64721
65252
  break;
64722
- const prs = await queryPRsByRef(runtime2, ref, "head", nextHead);
65253
+ const prs = await queryPRsByRef(runtime3, ref, "head", nextHead);
64723
65254
  if (prs.length === 0) {
64724
65255
  ancestors.push({ branch: nextHead, isCurrent: false, isDefaultBranch: false });
64725
65256
  break;
@@ -64739,7 +65270,7 @@ async function fetchGhPRStack(runtime2, ref, metadata) {
64739
65270
  const descendants = [];
64740
65271
  let nextBase = metadata.headBranch;
64741
65272
  for (let i3 = 0;i3 < maxDepth; i3++) {
64742
- const prs = await queryPRsByRef(runtime2, ref, "base", nextBase);
65273
+ const prs = await queryPRsByRef(runtime3, ref, "base", nextBase);
64743
65274
  if (prs.length === 0)
64744
65275
  break;
64745
65276
  const pr = prs[0];
@@ -64762,8 +65293,8 @@ async function fetchGhPRStack(runtime2, ref, metadata) {
64762
65293
  ];
64763
65294
  return { nodes };
64764
65295
  }
64765
- async function fetchGhPRList(runtime2, ref) {
64766
- const result = await runtime2.runCommand("gh", [
65296
+ async function fetchGhPRList(runtime3, ref) {
65297
+ const result = await runtime3.runCommand("gh", [
64767
65298
  "pr",
64768
65299
  "list",
64769
65300
  "--repo",
@@ -64831,12 +65362,12 @@ ${d2.diff}`);
64831
65362
  }
64832
65363
  return parts.join("");
64833
65364
  }
64834
- async function checkGlAuth(runtime2, host) {
65365
+ async function checkGlAuth(runtime3, host) {
64835
65366
  const args = ["auth", "status"];
64836
65367
  if (host !== "gitlab.com") {
64837
65368
  args.push("--hostname", host);
64838
65369
  }
64839
- const result = await runtime2.runCommand("glab", args);
65370
+ const result = await runtime3.runCommand("glab", args);
64840
65371
  if (result.exitCode !== 0) {
64841
65372
  const stderr = result.stderr.trim();
64842
65373
  const hostHint = host !== "gitlab.com" ? ` --hostname ${host}` : "";
@@ -64844,9 +65375,9 @@ async function checkGlAuth(runtime2, host) {
64844
65375
  ${stderr}`);
64845
65376
  }
64846
65377
  }
64847
- async function getGlUser(runtime2, host) {
65378
+ async function getGlUser(runtime3, host) {
64848
65379
  try {
64849
- const result = await runtime2.runCommand("glab", apiArgs(host, "/user"));
65380
+ const result = await runtime3.runCommand("glab", apiArgs(host, "/user"));
64850
65381
  if (result.exitCode === 0 && result.stdout.trim()) {
64851
65382
  const user = JSON.parse(result.stdout);
64852
65383
  return user.username ?? null;
@@ -64856,11 +65387,11 @@ async function getGlUser(runtime2, host) {
64856
65387
  return null;
64857
65388
  }
64858
65389
  }
64859
- async function fetchGlMR(runtime2, ref) {
65390
+ async function fetchGlMR(runtime3, ref) {
64860
65391
  const encoded = encodeProject(ref.projectPath);
64861
65392
  const [diffResult, viewResult] = await Promise.all([
64862
- runtime2.runCommand("glab", apiArgs(ref.host, `projects/${encoded}/merge_requests/${ref.iid}/diffs?per_page=100`, ["--paginate"])),
64863
- runtime2.runCommand("glab", apiArgs(ref.host, `projects/${encoded}/merge_requests/${ref.iid}`))
65393
+ runtime3.runCommand("glab", apiArgs(ref.host, `projects/${encoded}/merge_requests/${ref.iid}/diffs?per_page=100`, ["--paginate"])),
65394
+ runtime3.runCommand("glab", apiArgs(ref.host, `projects/${encoded}/merge_requests/${ref.iid}`))
64864
65395
  ]);
64865
65396
  if (diffResult.exitCode !== 0) {
64866
65397
  throw new Error(`Failed to fetch MR diff: ${diffResult.stderr.trim() || `exit code ${diffResult.exitCode}`}`);
@@ -64877,7 +65408,7 @@ async function fetchGlMR(runtime2, ref) {
64877
65408
  let defaultBranch;
64878
65409
  const projectEndpoint = typeof raw2.target_project_id === "number" ? `projects/${raw2.target_project_id}` : `projects/${encoded}`;
64879
65410
  try {
64880
- const projectResult = await runtime2.runCommand("glab", apiArgs(ref.host, projectEndpoint));
65411
+ const projectResult = await runtime3.runCommand("glab", apiArgs(ref.host, projectEndpoint));
64881
65412
  if (projectResult.exitCode === 0 && projectResult.stdout.trim()) {
64882
65413
  const project = JSON.parse(projectResult.stdout);
64883
65414
  defaultBranch = project.default_branch;
@@ -64899,15 +65430,15 @@ async function fetchGlMR(runtime2, ref) {
64899
65430
  };
64900
65431
  return { metadata, rawPatch };
64901
65432
  }
64902
- async function fetchGlMRContext(runtime2, ref) {
65433
+ async function fetchGlMRContext(runtime3, ref) {
64903
65434
  const encoded = encodeProject(ref.projectPath);
64904
65435
  const mrEndpoint = `projects/${encoded}/merge_requests/${ref.iid}`;
64905
65436
  const [mrResult, notesResult, approvalsResult, pipelinesResult, issuesResult] = await Promise.all([
64906
- runtime2.runCommand("glab", apiArgs(ref.host, mrEndpoint)),
64907
- runtime2.runCommand("glab", apiArgs(ref.host, `${mrEndpoint}/notes?sort=asc&per_page=100`)),
64908
- runtime2.runCommand("glab", apiArgs(ref.host, `${mrEndpoint}/approvals`)),
64909
- runtime2.runCommand("glab", apiArgs(ref.host, `${mrEndpoint}/pipelines?per_page=5`)),
64910
- runtime2.runCommand("glab", apiArgs(ref.host, `${mrEndpoint}/closes_issues`))
65437
+ runtime3.runCommand("glab", apiArgs(ref.host, mrEndpoint)),
65438
+ runtime3.runCommand("glab", apiArgs(ref.host, `${mrEndpoint}/notes?sort=asc&per_page=100`)),
65439
+ runtime3.runCommand("glab", apiArgs(ref.host, `${mrEndpoint}/approvals`)),
65440
+ runtime3.runCommand("glab", apiArgs(ref.host, `${mrEndpoint}/pipelines?per_page=5`)),
65441
+ runtime3.runCommand("glab", apiArgs(ref.host, `${mrEndpoint}/closes_issues`))
64911
65442
  ]);
64912
65443
  const str = (v2) => typeof v2 === "string" ? v2 : "";
64913
65444
  const arr = (v2) => Array.isArray(v2) ? v2 : [];
@@ -64990,7 +65521,7 @@ async function fetchGlMRContext(runtime2, ref) {
64990
65521
  const pipelines = JSON.parse(pipelinesResult.stdout);
64991
65522
  if (pipelines.length > 0) {
64992
65523
  const latest = pipelines[0];
64993
- const jobsResult = await runtime2.runCommand("glab", apiArgs(ref.host, `projects/${encoded}/pipelines/${latest.id}/jobs?per_page=100`));
65524
+ const jobsResult = await runtime3.runCommand("glab", apiArgs(ref.host, `projects/${encoded}/pipelines/${latest.id}/jobs?per_page=100`));
64994
65525
  if (jobsResult.exitCode === 0) {
64995
65526
  try {
64996
65527
  const jobs = JSON.parse(jobsResult.stdout);
@@ -65044,30 +65575,30 @@ async function fetchGlMRContext(runtime2, ref) {
65044
65575
  linkedIssues
65045
65576
  };
65046
65577
  }
65047
- async function fetchGlFileContent(runtime2, ref, sha, filePath) {
65578
+ async function fetchGlFileContent(runtime3, ref, sha, filePath) {
65048
65579
  const encoded = encodeProject(ref.projectPath);
65049
65580
  const encodedPath = encodeApiFilePath(filePath);
65050
- const result = await runtime2.runCommand("glab", apiArgs(ref.host, `projects/${encoded}/repository/files/${encodedPath}/raw?ref=${sha}`));
65581
+ const result = await runtime3.runCommand("glab", apiArgs(ref.host, `projects/${encoded}/repository/files/${encodedPath}/raw?ref=${sha}`));
65051
65582
  if (result.exitCode !== 0)
65052
65583
  return null;
65053
65584
  return result.stdout;
65054
65585
  }
65055
- async function submitGlMRReview(runtime2, ref, headSha, action, body3, fileComments) {
65056
- if (!runtime2.runCommandWithInput) {
65586
+ async function submitGlMRReview(runtime3, ref, headSha, action, body3, fileComments) {
65587
+ if (!runtime3.runCommandWithInput) {
65057
65588
  throw new Error("Runtime does not support stdin input; cannot submit MR review");
65058
65589
  }
65059
65590
  const encoded = encodeProject(ref.projectPath);
65060
65591
  const mrEndpoint = `projects/${encoded}/merge_requests/${ref.iid}`;
65061
65592
  if (body3 && body3.trim()) {
65062
65593
  const notePayload = JSON.stringify({ body: body3.trim() });
65063
- const noteResult = await runtime2.runCommandWithInput("glab", apiArgs(ref.host, `${mrEndpoint}/notes`, ["--method", "POST", "--input", "-", "-H", "Content-Type:application/json"]), notePayload);
65594
+ const noteResult = await runtime3.runCommandWithInput("glab", apiArgs(ref.host, `${mrEndpoint}/notes`, ["--method", "POST", "--input", "-", "-H", "Content-Type:application/json"]), notePayload);
65064
65595
  if (noteResult.exitCode !== 0) {
65065
65596
  const msg = noteResult.stderr.trim() || noteResult.stdout.trim() || `exit code ${noteResult.exitCode}`;
65066
65597
  throw new Error(`Failed to post MR note: ${msg}`);
65067
65598
  }
65068
65599
  }
65069
65600
  if (fileComments.length > 0) {
65070
- const mrResult = await runtime2.runCommand("glab", apiArgs(ref.host, mrEndpoint));
65601
+ const mrResult = await runtime3.runCommand("glab", apiArgs(ref.host, mrEndpoint));
65071
65602
  let baseSha = headSha;
65072
65603
  let startSha = headSha;
65073
65604
  if (mrResult.exitCode === 0 && mrResult.stdout.trim()) {
@@ -65110,7 +65641,7 @@ async function submitGlMRReview(runtime2, ref, headSha, action, body3, fileComme
65110
65641
  position.line_range = { start: startEntry, end: endEntry };
65111
65642
  }
65112
65643
  const payload = JSON.stringify({ body: comment2.body, position });
65113
- const res = await runtime2.runCommandWithInput("glab", apiArgs(ref.host, `${mrEndpoint}/discussions`, ["--method", "POST", "--input", "-", "-H", "Content-Type:application/json"]), payload);
65644
+ const res = await runtime3.runCommandWithInput("glab", apiArgs(ref.host, `${mrEndpoint}/discussions`, ["--method", "POST", "--input", "-", "-H", "Content-Type:application/json"]), payload);
65114
65645
  if (res.exitCode !== 0) {
65115
65646
  const msg = res.stderr.trim() || res.stdout.trim() || `exit code ${res.exitCode}`;
65116
65647
  throw new Error(`${comment2.path}:${comment2.line}: ${msg}`);
@@ -65133,7 +65664,7 @@ ${errors.join(`
65133
65664
  }
65134
65665
  }
65135
65666
  if (action === "approve") {
65136
- const approveResult = await runtime2.runCommandWithInput("glab", apiArgs(ref.host, `${mrEndpoint}/approve`, ["--method", "POST", "--input", "-", "-H", "Content-Type:application/json"]), "{}");
65667
+ const approveResult = await runtime3.runCommandWithInput("glab", apiArgs(ref.host, `${mrEndpoint}/approve`, ["--method", "POST", "--input", "-", "-H", "Content-Type:application/json"]), "{}");
65137
65668
  if (approveResult.exitCode !== 0) {
65138
65669
  const msg = approveResult.stderr.trim() || approveResult.stdout.trim() || `exit code ${approveResult.exitCode}`;
65139
65670
  throw new Error(`Failed to approve MR: ${msg}`);
@@ -65202,58 +65733,58 @@ function parsePRUrl(url) {
65202
65733
  }
65203
65734
  return null;
65204
65735
  }
65205
- async function checkAuth(runtime2, ref) {
65736
+ async function checkAuth(runtime3, ref) {
65206
65737
  if (ref.platform === "github")
65207
- return checkGhAuth(runtime2, ref.host);
65208
- return checkGlAuth(runtime2, ref.host);
65738
+ return checkGhAuth(runtime3, ref.host);
65739
+ return checkGlAuth(runtime3, ref.host);
65209
65740
  }
65210
- async function getUser(runtime2, ref) {
65741
+ async function getUser(runtime3, ref) {
65211
65742
  if (ref.platform === "github")
65212
- return getGhUser(runtime2, ref.host);
65213
- return getGlUser(runtime2, ref.host);
65743
+ return getGhUser(runtime3, ref.host);
65744
+ return getGlUser(runtime3, ref.host);
65214
65745
  }
65215
- async function fetchPR(runtime2, ref) {
65746
+ async function fetchPR(runtime3, ref) {
65216
65747
  if (ref.platform === "github")
65217
- return fetchGhPR(runtime2, ref);
65218
- return fetchGlMR(runtime2, ref);
65748
+ return fetchGhPR(runtime3, ref);
65749
+ return fetchGlMR(runtime3, ref);
65219
65750
  }
65220
- async function fetchPRContext(runtime2, ref) {
65751
+ async function fetchPRContext(runtime3, ref) {
65221
65752
  if (ref.platform === "github")
65222
- return fetchGhPRContext(runtime2, ref);
65223
- return fetchGlMRContext(runtime2, ref);
65753
+ return fetchGhPRContext(runtime3, ref);
65754
+ return fetchGlMRContext(runtime3, ref);
65224
65755
  }
65225
- async function fetchPRFileContent(runtime2, ref, sha, filePath) {
65756
+ async function fetchPRFileContent(runtime3, ref, sha, filePath) {
65226
65757
  if (ref.platform === "github")
65227
- return fetchGhPRFileContent(runtime2, ref, sha, filePath);
65228
- return fetchGlFileContent(runtime2, ref, sha, filePath);
65758
+ return fetchGhPRFileContent(runtime3, ref, sha, filePath);
65759
+ return fetchGlFileContent(runtime3, ref, sha, filePath);
65229
65760
  }
65230
- async function submitPRReview(runtime2, ref, headSha, action, body3, fileComments) {
65761
+ async function submitPRReview(runtime3, ref, headSha, action, body3, fileComments) {
65231
65762
  if (ref.platform === "github")
65232
- return submitGhPRReview(runtime2, ref, headSha, action, body3, fileComments);
65233
- return submitGlMRReview(runtime2, ref, headSha, action, body3, fileComments);
65763
+ return submitGhPRReview(runtime3, ref, headSha, action, body3, fileComments);
65764
+ return submitGlMRReview(runtime3, ref, headSha, action, body3, fileComments);
65234
65765
  }
65235
- async function fetchPRViewedFiles(runtime2, ref) {
65766
+ async function fetchPRViewedFiles(runtime3, ref) {
65236
65767
  if (ref.platform === "github")
65237
- return fetchGhPRViewedFiles(runtime2, ref);
65768
+ return fetchGhPRViewedFiles(runtime3, ref);
65238
65769
  return {};
65239
65770
  }
65240
- async function markPRFilesViewed(runtime2, ref, prNodeId, filePaths, viewed) {
65771
+ async function markPRFilesViewed(runtime3, ref, prNodeId, filePaths, viewed) {
65241
65772
  if (ref.platform === "github")
65242
- return markGhFilesViewed(runtime2, ref, prNodeId, filePaths, viewed);
65773
+ return markGhFilesViewed(runtime3, ref, prNodeId, filePaths, viewed);
65243
65774
  }
65244
- async function fetchPRStack(runtime2, ref, metadata) {
65775
+ async function fetchPRStack(runtime3, ref, metadata) {
65245
65776
  if (ref.platform === "github")
65246
- return fetchGhPRStack(runtime2, ref, metadata);
65777
+ return fetchGhPRStack(runtime3, ref, metadata);
65247
65778
  return null;
65248
65779
  }
65249
- async function fetchPRList(runtime2, ref) {
65780
+ async function fetchPRList(runtime3, ref) {
65250
65781
  if (ref.platform === "github")
65251
- return fetchGhPRList(runtime2, ref);
65782
+ return fetchGhPRList(runtime3, ref);
65252
65783
  return [];
65253
65784
  }
65254
65785
 
65255
65786
  // ../../packages/server/pr.ts
65256
- var runtime2 = {
65787
+ var runtime3 = {
65257
65788
  async runCommand(cmd, args) {
65258
65789
  const proc = Bun.spawn([cmd, ...args], {
65259
65790
  stdout: "pipe",
@@ -65284,34 +65815,34 @@ var runtime2 = {
65284
65815
  };
65285
65816
  var parsePRUrl2 = parsePRUrl;
65286
65817
  function checkPRAuth(ref) {
65287
- return checkAuth(runtime2, ref);
65818
+ return checkAuth(runtime3, ref);
65288
65819
  }
65289
65820
  function getPRUser(ref) {
65290
- return getUser(runtime2, ref);
65821
+ return getUser(runtime3, ref);
65291
65822
  }
65292
65823
  function fetchPR2(ref) {
65293
- return fetchPR(runtime2, ref);
65824
+ return fetchPR(runtime3, ref);
65294
65825
  }
65295
65826
  function fetchPRContext2(ref) {
65296
- return fetchPRContext(runtime2, ref);
65827
+ return fetchPRContext(runtime3, ref);
65297
65828
  }
65298
65829
  function fetchPRFileContent2(ref, sha, filePath) {
65299
- return fetchPRFileContent(runtime2, ref, sha, filePath);
65830
+ return fetchPRFileContent(runtime3, ref, sha, filePath);
65300
65831
  }
65301
65832
  function submitPRReview2(ref, headSha, action, body3, fileComments) {
65302
- return submitPRReview(runtime2, ref, headSha, action, body3, fileComments);
65833
+ return submitPRReview(runtime3, ref, headSha, action, body3, fileComments);
65303
65834
  }
65304
65835
  function fetchPRViewedFiles2(ref) {
65305
- return fetchPRViewedFiles(runtime2, ref);
65836
+ return fetchPRViewedFiles(runtime3, ref);
65306
65837
  }
65307
65838
  function markPRFilesViewed2(ref, prNodeId, filePaths, viewed) {
65308
- return markPRFilesViewed(runtime2, ref, prNodeId, filePaths, viewed);
65839
+ return markPRFilesViewed(runtime3, ref, prNodeId, filePaths, viewed);
65309
65840
  }
65310
65841
  function fetchPRStack2(ref, metadata) {
65311
- return fetchPRStack(runtime2, ref, metadata);
65842
+ return fetchPRStack(runtime3, ref, metadata);
65312
65843
  }
65313
65844
  function fetchPRList2(ref) {
65314
- return fetchPRList(runtime2, ref);
65845
+ return fetchPRList(runtime3, ref);
65315
65846
  }
65316
65847
 
65317
65848
  // ../../packages/ai/index.ts
@@ -65569,6 +66100,7 @@ async function startReviewServer(options) {
65569
66100
  let prMetadata = options.prMetadata;
65570
66101
  const isPRMode = !!prMetadata;
65571
66102
  const hasLocalAccess = !!gitContext;
66103
+ const sessionVcsType = gitContext?.vcsType;
65572
66104
  let draftKey = contentHash(options.rawPatch);
65573
66105
  const editorAnnotations = createEditorAnnotationHandler();
65574
66106
  const externalAnnotations = createExternalAnnotationHandler("review");
@@ -65588,10 +66120,14 @@ async function startReviewServer(options) {
65588
66120
  if (isPRMode && prMetadata)
65589
66121
  prSwitchCache.set(prMetadata.url, { metadata: prMetadata, rawPatch: options.rawPatch });
65590
66122
  const prStackTreeCache = new Map;
65591
- let currentBase = options.initialBase || gitContext?.defaultBranch || "main";
66123
+ const detectedCompareTarget = () => gitContext?.defaultBranch || gitContext?.compareTarget?.fallback || "main";
66124
+ let currentBase = options.initialBase || detectedCompareTarget();
65592
66125
  let baseEverSwitched = false;
66126
+ const resolveReviewBase = (requestedBase) => {
66127
+ return resolveBaseBranch(requestedBase, detectedCompareTarget());
66128
+ };
65593
66129
  if (gitContext && !options.initialBase && !isPRMode) {
65594
- detectRemoteDefaultBranch(runtime, gitContext.cwd).then((remote) => {
66130
+ detectRemoteDefaultCompareTarget(gitContext.cwd, sessionVcsType).then((remote) => {
65595
66131
  if (remote && !baseEverSwitched)
65596
66132
  currentBase = remote;
65597
66133
  });
@@ -65632,7 +66168,7 @@ async function startReviewServer(options) {
65632
66168
  });
65633
66169
  return built ? { ...built, prUrl: launchPrUrl, diffScope: launchDiffScope, diffContext } : built;
65634
66170
  }
65635
- const userMessage = buildCodexReviewUserMessage(currentPatch, currentDiffType, userMessageOptions, prMetadata);
66171
+ const userMessage = buildAgentReviewUserMessage(currentPatch, currentDiffType, userMessageOptions, prMetadata);
65636
66172
  if (provider === "codex") {
65637
66173
  const model = typeof config?.model === "string" && config.model ? config.model : undefined;
65638
66174
  const reasoningEffort = typeof config?.reasoningEffort === "string" && config.reasoningEffort ? config.reasoningEffort : undefined;
@@ -65785,6 +66321,12 @@ async function startReviewServer(options) {
65785
66321
  const wslFlag = await isWSL();
65786
66322
  const gitUser = detectGitUser();
65787
66323
  let repoInfo = isPRMode && prMetadata ? { display: getDisplayRepo(prMetadata), branch: `${getMRLabel(prMetadata)} ${getMRNumberLabel(prMetadata)}` } : await getRepoInfo();
66324
+ if (gitContext?.repository?.displayFallback) {
66325
+ repoInfo = {
66326
+ ...repoInfo,
66327
+ display: repoInfo?.display || gitContext.repository.displayFallback
66328
+ };
66329
+ }
65788
66330
  let prRef = isPRMode && prMetadata ? prRefFromMetadata(prMetadata) : null;
65789
66331
  const platformUser = prRef ? await getPRUser(prRef) : null;
65790
66332
  let prStackInfo = prMetadata ? getPRStackInfo(prMetadata) : null;
@@ -65809,8 +66351,8 @@ async function startReviewServer(options) {
65809
66351
  } catch {}
65810
66352
  }
65811
66353
  let resolveDecision;
65812
- const decisionPromise = new Promise((resolve5) => {
65813
- resolveDecision = resolve5;
66354
+ const decisionPromise = new Promise((resolve6) => {
66355
+ resolveDecision = resolve6;
65814
66356
  });
65815
66357
  let server2 = null;
65816
66358
  for (let attempt = 1;attempt <= MAX_RETRIES2; attempt++) {
@@ -65879,9 +66421,8 @@ async function startReviewServer(options) {
65879
66421
  if (typeof body3.hideWhitespace === "boolean") {
65880
66422
  currentHideWhitespace = body3.hideWhitespace;
65881
66423
  }
65882
- const detectedBase = gitContext?.defaultBranch || "main";
65883
66424
  const requestedBase = typeof body3.base === "string" ? body3.base : undefined;
65884
- const base = resolveBaseBranch(requestedBase, detectedBase);
66425
+ const base = resolveReviewBase(requestedBase);
65885
66426
  const defaultCwd = gitContext?.cwd;
65886
66427
  const result = await runVcsDiff(newDiffType, base, defaultCwd, {
65887
66428
  hideWhitespace: currentHideWhitespace
@@ -65896,7 +66437,7 @@ async function startReviewServer(options) {
65896
66437
  if (gitContext) {
65897
66438
  try {
65898
66439
  const effectiveCwd = resolveVcsCwd(newDiffType, gitContext.cwd);
65899
- updatedContext = await getVcsContext(effectiveCwd);
66440
+ updatedContext = await getVcsContext(effectiveCwd, sessionVcsType);
65900
66441
  } catch {}
65901
66442
  }
65902
66443
  return Response.json({
@@ -66094,8 +66635,8 @@ async function startReviewServer(options) {
66094
66635
  return Response.json(result);
66095
66636
  }
66096
66637
  if (hasLocalAccess) {
66097
- const detectedBase = gitContext?.defaultBranch || "main";
66098
- const base = resolveBaseBranch(url.searchParams.get("base") ?? undefined, detectedBase);
66638
+ const requestedBase = url.searchParams.get("base") ?? undefined;
66639
+ const base = resolveReviewBase(requestedBase);
66099
66640
  const defaultCwd = gitContext?.cwd;
66100
66641
  const result = await getVcsFileContentsForDiff(currentDiffType, base, filePath, oldPath, defaultCwd);
66101
66642
  return Response.json(result);
@@ -66111,7 +66652,8 @@ async function startReviewServer(options) {
66111
66652
  return Response.json({ error: "No file access available" }, { status: 400 });
66112
66653
  }
66113
66654
  if (url.pathname === "/api/git-add" && req.method === "POST") {
66114
- if (isPRMode || !canStageFiles(currentDiffType)) {
66655
+ const stageCwd = resolveVcsCwd(currentDiffType, gitContext?.cwd);
66656
+ if (isPRMode || !await canStageFiles(currentDiffType, stageCwd)) {
66115
66657
  return Response.json({ error: "Staging not available" }, { status: 400 });
66116
66658
  }
66117
66659
  try {
@@ -66119,11 +66661,10 @@ async function startReviewServer(options) {
66119
66661
  if (!body3.filePath) {
66120
66662
  return Response.json({ error: "Missing filePath" }, { status: 400 });
66121
66663
  }
66122
- const cwd = resolveVcsCwd(currentDiffType, gitContext?.cwd);
66123
66664
  if (body3.undo) {
66124
- await unstageFile(currentDiffType, body3.filePath, cwd);
66665
+ await unstageFile(currentDiffType, body3.filePath, stageCwd);
66125
66666
  } else {
66126
- await stageFile(currentDiffType, body3.filePath, cwd);
66667
+ await stageFile(currentDiffType, body3.filePath, stageCwd);
66127
66668
  }
66128
66669
  return Response.json({ ok: true });
66129
66670
  } catch (err) {
@@ -66344,8 +66885,8 @@ async function startAnnotateServer(options) {
66344
66885
  const externalAnnotations = createExternalAnnotationHandler("plan");
66345
66886
  const repoInfo = await getRepoInfo();
66346
66887
  let resolveDecision;
66347
- const decisionPromise = new Promise((resolve5) => {
66348
- resolveDecision = resolve5;
66888
+ const decisionPromise = new Promise((resolve6) => {
66889
+ resolveDecision = resolve6;
66349
66890
  });
66350
66891
  let server2 = null;
66351
66892
  for (let attempt = 1;attempt <= MAX_RETRIES3; attempt++) {
@@ -66514,8 +67055,8 @@ var PLAN_TOOL_NAMES = {
66514
67055
  codex: "ExitPlanMode",
66515
67056
  "gemini-cli": "exit_plan_mode"
66516
67057
  };
66517
- function getPlanToolName(runtime3) {
66518
- return runtime3 && PLAN_TOOL_NAMES[runtime3] || "ExitPlanMode";
67058
+ function getPlanToolName(runtime4) {
67059
+ return runtime4 && PLAN_TOOL_NAMES[runtime4] || "ExitPlanMode";
66519
67060
  }
66520
67061
  function buildPlanFileRule(toolName, planFilePath) {
66521
67062
  if (!planFilePath)
@@ -66573,11 +67114,11 @@ function getConfiguredPrompt(options) {
66573
67114
  const runtimeFallback = options.runtime ? options.runtimeFallbacks?.[options.runtime] : undefined;
66574
67115
  return runtimePrompt ?? genericPrompt ?? runtimeFallback ?? options.fallback;
66575
67116
  }
66576
- function getReviewApprovedPrompt(runtime3, config) {
67117
+ function getReviewApprovedPrompt(runtime4, config) {
66577
67118
  return getConfiguredPrompt({
66578
67119
  section: "review",
66579
67120
  key: "approved",
66580
- runtime: runtime3,
67121
+ runtime: runtime4,
66581
67122
  config,
66582
67123
  fallback: DEFAULT_REVIEW_APPROVED_PROMPT
66583
67124
  });
@@ -66590,21 +67131,21 @@ Please address this feedback.`,
66590
67131
 
66591
67132
  Please address this feedback.`
66592
67133
  };
66593
- function getReviewDeniedSuffix(runtime3, config) {
67134
+ function getReviewDeniedSuffix(runtime4, config) {
66594
67135
  return getConfiguredPrompt({
66595
67136
  section: "review",
66596
67137
  key: "denied",
66597
- runtime: runtime3,
67138
+ runtime: runtime4,
66598
67139
  config,
66599
67140
  fallback: DEFAULT_REVIEW_DENIED_SUFFIX,
66600
67141
  runtimeFallbacks: REVIEW_DENIED_RUNTIME_DEFAULTS
66601
67142
  });
66602
67143
  }
66603
- function getPlanDeniedPrompt(runtime3, config, vars) {
67144
+ function getPlanDeniedPrompt(runtime4, config, vars) {
66604
67145
  const template = getConfiguredPrompt({
66605
67146
  section: "plan",
66606
67147
  key: "denied",
66607
- runtime: runtime3,
67148
+ runtime: runtime4,
66608
67149
  config,
66609
67150
  fallback: DEFAULT_PLAN_DENIED_PROMPT
66610
67151
  });
@@ -66613,11 +67154,11 @@ function getPlanDeniedPrompt(runtime3, config, vars) {
66613
67154
  var PLAN_APPROVED_RUNTIME_DEFAULTS = {
66614
67155
  opencode: "Plan approved!{{doneMsg}}"
66615
67156
  };
66616
- function getPlanApprovedPrompt(runtime3, config, vars) {
67157
+ function getPlanApprovedPrompt(runtime4, config, vars) {
66617
67158
  const template = getConfiguredPrompt({
66618
67159
  section: "plan",
66619
67160
  key: "approved",
66620
- runtime: runtime3,
67161
+ runtime: runtime4,
66621
67162
  config,
66622
67163
  fallback: DEFAULT_PLAN_APPROVED_PROMPT,
66623
67164
  runtimeFallbacks: PLAN_APPROVED_RUNTIME_DEFAULTS
@@ -66634,32 +67175,32 @@ The user approved your plan but added the following notes to consider during imp
66634
67175
 
66635
67176
  {{feedback}}{{proceedSuffix}}`
66636
67177
  };
66637
- function getPlanApprovedWithNotesPrompt(runtime3, config, vars) {
67178
+ function getPlanApprovedWithNotesPrompt(runtime4, config, vars) {
66638
67179
  const template = getConfiguredPrompt({
66639
67180
  section: "plan",
66640
67181
  key: "approvedWithNotes",
66641
- runtime: runtime3,
67182
+ runtime: runtime4,
66642
67183
  config,
66643
67184
  fallback: DEFAULT_PLAN_APPROVED_WITH_NOTES_PROMPT,
66644
67185
  runtimeFallbacks: PLAN_APPROVED_WITH_NOTES_RUNTIME_DEFAULTS
66645
67186
  });
66646
67187
  return resolveTemplate(template, { proceedSuffix: "", ...vars });
66647
67188
  }
66648
- function getAnnotateFileFeedbackPrompt(runtime3, config, vars) {
67189
+ function getAnnotateFileFeedbackPrompt(runtime4, config, vars) {
66649
67190
  const template = getConfiguredPrompt({
66650
67191
  section: "annotate",
66651
67192
  key: "fileFeedback",
66652
- runtime: runtime3,
67193
+ runtime: runtime4,
66653
67194
  config,
66654
67195
  fallback: DEFAULT_ANNOTATE_FILE_FEEDBACK_PROMPT
66655
67196
  });
66656
67197
  return resolveTemplate(template, vars ?? {});
66657
67198
  }
66658
- function getAnnotateMessageFeedbackPrompt(runtime3, config, vars) {
67199
+ function getAnnotateMessageFeedbackPrompt(runtime4, config, vars) {
66659
67200
  const template = getConfiguredPrompt({
66660
67201
  section: "annotate",
66661
67202
  key: "messageFeedback",
66662
- runtime: runtime3,
67203
+ runtime: runtime4,
66663
67204
  config,
66664
67205
  fallback: DEFAULT_ANNOTATE_MESSAGE_FEEDBACK_PROMPT
66665
67206
  });
@@ -66711,6 +67252,73 @@ function parseAnnotateArgs(raw2) {
66711
67252
  return { filePath: stripAtPrefix(rawFilePath), rawFilePath, ...flags };
66712
67253
  }
66713
67254
 
67255
+ // ../../packages/shared/review-args.ts
67256
+ function parseReviewArgs(input) {
67257
+ const tokens = Array.isArray(input) ? input.map((token2) => stripWrappingQuotes(token2.trim())).filter(Boolean) : tokenizeReviewArgs(input ?? "");
67258
+ let vcsType;
67259
+ let useLocal = true;
67260
+ const positional = [];
67261
+ for (const token2 of tokens) {
67262
+ switch (token2) {
67263
+ case "--git":
67264
+ vcsType = "git";
67265
+ break;
67266
+ case "--local":
67267
+ useLocal = true;
67268
+ break;
67269
+ case "--no-local":
67270
+ useLocal = false;
67271
+ break;
67272
+ default:
67273
+ positional.push(token2);
67274
+ break;
67275
+ }
67276
+ }
67277
+ const target = positional[0];
67278
+ return {
67279
+ prUrl: target && isReviewUrl(target) ? target : undefined,
67280
+ vcsType,
67281
+ useLocal
67282
+ };
67283
+ }
67284
+ function isReviewUrl(value) {
67285
+ return value.startsWith("http://") || value.startsWith("https://");
67286
+ }
67287
+ function tokenizeReviewArgs(input) {
67288
+ const raw2 = input.trim();
67289
+ if (!raw2)
67290
+ return [];
67291
+ const tokens = [];
67292
+ let current = "";
67293
+ let quote;
67294
+ for (let i4 = 0;i4 < raw2.length; i4++) {
67295
+ const char = raw2[i4];
67296
+ if (quote) {
67297
+ if (char === quote) {
67298
+ quote = undefined;
67299
+ } else {
67300
+ current += char;
67301
+ }
67302
+ continue;
67303
+ }
67304
+ if (char === "'" || char === '"') {
67305
+ quote = char;
67306
+ continue;
67307
+ }
67308
+ if (/\s/.test(char)) {
67309
+ if (current) {
67310
+ tokens.push(current);
67311
+ current = "";
67312
+ }
67313
+ continue;
67314
+ }
67315
+ current += char;
67316
+ }
67317
+ if (current)
67318
+ tokens.push(current);
67319
+ return tokens.map((token2) => stripWrappingQuotes(token2.trim())).filter(Boolean);
67320
+ }
67321
+
66714
67322
  // ../../packages/shared/url-to-markdown.ts
66715
67323
  var isConvertedSource = (source) => source === "jina" || source === "fetch+turndown";
66716
67324
  var FETCH_TIMEOUT_MS = 30000;
@@ -66950,8 +67558,9 @@ import { statSync as statSync5 } from "fs";
66950
67558
  import path3 from "path";
66951
67559
  async function handleReviewCommand(event, deps) {
66952
67560
  const { client: client3, reviewHtmlContent, getSharingEnabled, getShareBaseUrl, directory } = deps;
66953
- const urlArg = event.properties?.arguments || "";
66954
- const isPRMode = urlArg?.startsWith("http://") || urlArg?.startsWith("https://");
67561
+ const reviewArgs = parseReviewArgs(event.properties?.arguments || "");
67562
+ const urlArg = reviewArgs.prUrl;
67563
+ const isPRMode = urlArg !== undefined;
66955
67564
  let rawPatch;
66956
67565
  let gitRef;
66957
67566
  let diffError;
@@ -66983,14 +67592,17 @@ async function handleReviewCommand(event, deps) {
66983
67592
  }
66984
67593
  } else {
66985
67594
  client3.app.log({ level: "info", message: "Opening code review UI..." });
66986
- gitContext = await getGitContext2(directory);
66987
67595
  const config = loadConfig();
66988
- userDiffType = resolveDefaultDiffType(config);
66989
- const diffResult = await runGitDiffWithContext2(userDiffType, gitContext, {
67596
+ const diffResult = await prepareLocalReviewDiff({
67597
+ cwd: directory,
67598
+ vcsType: reviewArgs.vcsType,
67599
+ configuredDiffType: resolveDefaultDiffType(config),
66990
67600
  hideWhitespace: config.diffOptions?.hideWhitespace ?? false
66991
67601
  });
66992
- rawPatch = diffResult.patch;
66993
- gitRef = diffResult.label;
67602
+ gitContext = diffResult.gitContext;
67603
+ userDiffType = diffResult.diffType;
67604
+ rawPatch = diffResult.rawPatch;
67605
+ gitRef = diffResult.gitRef;
66994
67606
  diffError = diffResult.error;
66995
67607
  }
66996
67608
  const server2 = await startReviewServer({
@@ -67716,14 +68328,14 @@ Use /plannotator-last or /plannotator-annotate for manual review, or set workflo
67716
68328
  });
67717
68329
  const timeoutSeconds = getPlanTimeoutSeconds();
67718
68330
  const timeoutMs = timeoutSeconds === null ? null : timeoutSeconds * 1000;
67719
- const result = timeoutMs === null ? await server2.waitForDecision() : await new Promise((resolve5) => {
67720
- const timeoutId = setTimeout(() => resolve5({
68331
+ const result = timeoutMs === null ? await server2.waitForDecision() : await new Promise((resolve6) => {
68332
+ const timeoutId = setTimeout(() => resolve6({
67721
68333
  approved: false,
67722
68334
  feedback: `[Plannotator] No response within ${timeoutSeconds} seconds. Port released automatically. Please call submit_plan again.`
67723
68335
  }), timeoutMs);
67724
68336
  server2.waitForDecision().then((r9) => {
67725
68337
  clearTimeout(timeoutId);
67726
- resolve5(r9);
68338
+ resolve6(r9);
67727
68339
  });
67728
68340
  });
67729
68341
  await Bun.sleep(1500);