pullfrog 0.1.103 → 0.1.104

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/cli.mjs CHANGED
@@ -112919,7 +112919,7 @@ var import_picocolors = __toESM(require_picocolors(), 1);
112919
112919
  // package.json
112920
112920
  var package_default = {
112921
112921
  name: "pullfrog",
112922
- version: "0.1.103",
112922
+ version: "0.1.104",
112923
112923
  cliContractVersion: "1",
112924
112924
  type: "module",
112925
112925
  bin: {
@@ -193647,14 +193647,18 @@ async function runGitLines(args2) {
193647
193647
  // utils/rangeDiff.ts
193648
193648
  function computeIncrementalDiff(params) {
193649
193649
  try {
193650
+ const tips = [`origin/${params.baseBranch}`, ...params.formerBaseTips];
193651
+ const oldBase = versionBase({ sha: params.beforeSha, tips });
193652
+ const newBase = versionBase({ sha: params.headSha, tips });
193650
193653
  const raw2 = $(
193651
193654
  "sh",
193652
193655
  [
193653
193656
  "-c",
193654
- 'old_base=$(git merge-base "$1" "origin/$2") && new_base=$(git merge-base "$3" "origin/$2") && git range-diff --no-color --creation-factor=1000 "$old_base..$(git commit-tree "$1^{tree}" -p "$old_base" -m x)" "$new_base..$(git commit-tree "$3^{tree}" -p "$new_base" -m x)"',
193657
+ 'git range-diff --no-color --creation-factor=1000 "$1..$(git commit-tree "$2^{tree}" -p "$1" -m x)" "$3..$(git commit-tree "$4^{tree}" -p "$3" -m x)"',
193655
193658
  "--",
193659
+ oldBase,
193656
193660
  params.beforeSha,
193657
- params.baseBranch,
193661
+ newBase,
193658
193662
  params.headSha
193659
193663
  ],
193660
193664
  { log: false }
@@ -193667,6 +193671,27 @@ function computeIncrementalDiff(params) {
193667
193671
  return { status: "unavailable", reason };
193668
193672
  }
193669
193673
  }
193674
+ function git(args2) {
193675
+ return $("git", args2, { log: false }).trim();
193676
+ }
193677
+ function versionBase(params) {
193678
+ let best;
193679
+ for (const tip of params.tips) {
193680
+ let sha;
193681
+ try {
193682
+ sha = git(["merge-base", params.sha, tip]);
193683
+ } catch {
193684
+ continue;
193685
+ }
193686
+ const commits = Number(git(["rev-list", "--count", `${sha}..${params.sha}`]));
193687
+ if (!best || commits < best.commits) best = { tip, sha, commits };
193688
+ }
193689
+ if (!best) throw new Error(`no merge base between ${params.sha} and the base branch`);
193690
+ if (best.tip !== params.tips[0]) {
193691
+ log2.info(`\xBB incremental diff: ${params.sha.slice(0, 7)} sits on former base tip ${best.tip}`);
193692
+ }
193693
+ return best.sha;
193694
+ }
193670
193695
  function isDiffPrefix(ch) {
193671
193696
  return ch === " " || ch === "+" || ch === "-";
193672
193697
  }
@@ -193694,14 +193719,28 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
193694
193719
  if (lastHunkHdr?.seq === line.seq) hunkHdrEmitted = true;
193695
193720
  }
193696
193721
  function flushBefore() {
193697
- if (lastOuterHdr && !outerHdrEmitted) emit2(lastOuterHdr);
193698
- if (lastFileHdr && !fileHdrEmitted) emit2(lastFileHdr);
193699
- if (lastHunkHdr && !hunkHdrEmitted) emit2(lastHunkHdr);
193700
- for (const line of beforeBuf) {
193701
- if (line.seq > lastEmittedSeq) emit2(line);
193722
+ const headers = [
193723
+ outerHdrEmitted ? null : lastOuterHdr,
193724
+ fileHdrEmitted ? null : lastFileHdr,
193725
+ hunkHdrEmitted ? null : lastHunkHdr
193726
+ ].filter((line) => line !== null);
193727
+ const pending = [...headers, ...beforeBuf.filter((line) => line.seq > lastEmittedSeq)].sort(
193728
+ (a2, b) => a2.seq - b.seq
193729
+ );
193730
+ for (const [i2, line] of pending.entries()) {
193731
+ if (pending[i2 - 1]?.seq !== line.seq) emit2(line);
193702
193732
  }
193703
193733
  beforeBuf.length = 0;
193704
193734
  }
193735
+ function isFileHeader(pos) {
193736
+ return raw2.startsWith("## ", pos) && !raw2.startsWith("## Commit message", pos);
193737
+ }
193738
+ function markFileHeader(line, emitted) {
193739
+ lastFileHdr = line;
193740
+ fileHdrEmitted = emitted;
193741
+ lastHunkHdr = null;
193742
+ hunkHdrEmitted = true;
193743
+ }
193705
193744
  let cursor3 = 0;
193706
193745
  while (cursor3 < raw2.length) {
193707
193746
  const eol = raw2.indexOf("\n", cursor3);
@@ -193711,7 +193750,8 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
193711
193750
  continue;
193712
193751
  }
193713
193752
  if (raw2.startsWith(" @@", cursor3)) {
193714
- lastOuterHdr = { prefix: "", from: cursor3 + 4, to: lineEnd, seq };
193753
+ const label = raw2.slice(cursor3 + 4, lineEnd);
193754
+ lastOuterHdr = label === "@@ Metadata" || label === "@@ Commit message" ? null : { prefix: "", from: cursor3 + 4, to: lineEnd, seq };
193715
193755
  outerHdrEmitted = false;
193716
193756
  lastFileHdr = null;
193717
193757
  fileHdrEmitted = true;
@@ -193732,13 +193772,13 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
193732
193772
  } else if (isDiffPrefix(raw2[contentPos])) {
193733
193773
  isChange = isOuterChange;
193734
193774
  line = { prefix, from: contentPos + 1, to: lineEnd, seq };
193775
+ if (raw2[contentPos] === " " && isFileHeader(contentPos + 1)) {
193776
+ markFileHeader(line, isChange);
193777
+ }
193735
193778
  } else {
193736
193779
  line = { prefix, from: contentPos, to: lineEnd, seq };
193737
- if (raw2.startsWith("## ", contentPos) && !raw2.startsWith("## Commit message", contentPos)) {
193738
- lastFileHdr = line;
193739
- fileHdrEmitted = false;
193740
- lastHunkHdr = null;
193741
- hunkHdrEmitted = true;
193780
+ if (isFileHeader(contentPos)) {
193781
+ markFileHeader(line, false);
193742
193782
  } else if (raw2.startsWith("@@", contentPos) && !raw2.startsWith("@@ Metadata", contentPos)) {
193743
193783
  lastHunkHdr = line;
193744
193784
  hunkHdrEmitted = false;
@@ -199731,6 +199771,7 @@ var STRING_KEYS = [
199731
199771
  "prNodeId",
199732
199772
  "issueNodeId",
199733
199773
  "reviewNodeId",
199774
+ "reviewedCommitSha",
199734
199775
  "planCommentNodeId",
199735
199776
  "summarySnapshot",
199736
199777
  "model",
@@ -200895,7 +200936,10 @@ async function createAndSubmitWithFooter(ctx, params, opts) {
200895
200936
  }
200896
200937
  }
200897
200938
  async function reportReviewNodeId(ctx, params) {
200898
- await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
200939
+ await patchWorkflowRunFields(ctx, {
200940
+ reviewNodeId: params.nodeId,
200941
+ reviewedCommitSha: params.reviewedSha
200942
+ });
200899
200943
  }
200900
200944
 
200901
200945
  // utils/setup.ts
@@ -201489,6 +201533,34 @@ function describeHead(h2) {
201489
201533
  if (h2.kind === "branch") return `branch \`${h2.name}\``;
201490
201534
  return `detached HEAD \`${h2.sha}\``;
201491
201535
  }
201536
+ var FORMER_BASE_TIPS_QUERY = `
201537
+ query ($owner: String!, $repo: String!, $number: Int!) {
201538
+ repository(owner: $owner, name: $repo) {
201539
+ pullRequest(number: $number) {
201540
+ timelineItems(itemTypes: [BASE_REF_FORCE_PUSHED_EVENT], last: 100) {
201541
+ nodes { ... on BaseRefForcePushedEvent { beforeCommit { oid } } }
201542
+ }
201543
+ }
201544
+ }
201545
+ }
201546
+ `;
201547
+ async function getFormerBaseTips(ctx, pullNumber) {
201548
+ try {
201549
+ const response = await ctx.octokit.graphql(FORMER_BASE_TIPS_QUERY, {
201550
+ owner: ctx.repo.owner,
201551
+ repo: ctx.repo.name,
201552
+ number: pullNumber
201553
+ });
201554
+ return response.repository.pullRequest.timelineItems.nodes.flatMap(
201555
+ (node3) => node3.beforeCommit ? [node3.beforeCommit.oid] : []
201556
+ );
201557
+ } catch (error63) {
201558
+ log2.warning(
201559
+ `\xBB former base tips unavailable: ${error63 instanceof Error ? error63.message : String(error63)}`
201560
+ );
201561
+ return [];
201562
+ }
201563
+ }
201492
201564
  function CheckoutPrTool(ctx) {
201493
201565
  const runCheckout = async (pull_number, signal) => {
201494
201566
  const prResponse = await ctx.octokit.rest.pulls.get({
@@ -201541,7 +201613,8 @@ function CheckoutPrTool(ctx) {
201541
201613
  const incremental = computeIncrementalDiff({
201542
201614
  baseBranch: pr.baseRef,
201543
201615
  beforeSha: primary.beforeSha,
201544
- headSha: checkoutSha
201616
+ headSha: checkoutSha,
201617
+ formerBaseTips: await getFormerBaseTips(ctx, pull_number)
201545
201618
  });
201546
201619
  if (incremental.status === "ok") {
201547
201620
  incrementalDiffPath = join17(
@@ -208136,7 +208209,7 @@ async function postReviewCleanup(ctx) {
208136
208209
  if (!review) {
208137
208210
  const checkoutSha = primaryRepoState(ctx.toolState).checkoutSha;
208138
208211
  if (ctx.toolState.selectedMode === "IncrementalReview" && checkoutSha) {
208139
- await patchWorkflowRunFields(ctx, { reviewSkipped: true });
208212
+ await patchWorkflowRunFields(ctx, { reviewSkipped: true, reviewedCommitSha: checkoutSha });
208140
208213
  await bestEffort(
208141
208214
  () => dispatchFollowUpReReview(ctx, checkoutSha),
208142
208215
  "follow-up re-review dispatch"
@@ -208145,7 +208218,7 @@ async function postReviewCleanup(ctx) {
208145
208218
  return;
208146
208219
  }
208147
208220
  delete ctx.toolState.review;
208148
- await bestEffort(() => reportReviewNodeId(ctx, { nodeId: review.nodeId }), "reportReviewNodeId");
208221
+ await bestEffort(() => reportReviewNodeId(ctx, review), "reportReviewNodeId");
208149
208222
  if (review.reviewedSha) {
208150
208223
  await bestEffort(
208151
208224
  () => dispatchFollowUpReReview(ctx, review.reviewedSha),
@@ -209915,7 +209988,7 @@ function resolveTarget2(repo) {
209915
209988
  return { owner: match3[1], repo: match3[2] };
209916
209989
  }
209917
209990
  function cliVersion() {
209918
- const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.103");
209991
+ const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.104");
209919
209992
  if (!parts) return "0.0.0";
209920
209993
  return `${Number(parts[1])}.${Number(parts[2])}.${Number(parts[3])}`;
209921
209994
  }
@@ -210082,7 +210155,7 @@ async function runCli8(input2) {
210082
210155
  }
210083
210156
 
210084
210157
  // cli.ts
210085
- var VERSION10 = "0.1.103";
210158
+ var VERSION10 = "0.1.104";
210086
210159
  var bin = basename2(process.argv[1] || "");
210087
210160
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
210088
210161
  var rawArgs = process.argv.slice(2);
package/dist/index.js CHANGED
@@ -112141,7 +112141,7 @@ var import_semver = __toESM(require_semver2(), 1);
112141
112141
  // package.json
112142
112142
  var package_default = {
112143
112143
  name: "pullfrog",
112144
- version: "0.1.103",
112144
+ version: "0.1.104",
112145
112145
  cliContractVersion: "1",
112146
112146
  type: "module",
112147
112147
  bin: {
@@ -190129,14 +190129,18 @@ async function runGitLines(args2) {
190129
190129
  // utils/rangeDiff.ts
190130
190130
  function computeIncrementalDiff(params) {
190131
190131
  try {
190132
+ const tips = [`origin/${params.baseBranch}`, ...params.formerBaseTips];
190133
+ const oldBase = versionBase({ sha: params.beforeSha, tips });
190134
+ const newBase = versionBase({ sha: params.headSha, tips });
190132
190135
  const raw2 = $(
190133
190136
  "sh",
190134
190137
  [
190135
190138
  "-c",
190136
- 'old_base=$(git merge-base "$1" "origin/$2") && new_base=$(git merge-base "$3" "origin/$2") && git range-diff --no-color --creation-factor=1000 "$old_base..$(git commit-tree "$1^{tree}" -p "$old_base" -m x)" "$new_base..$(git commit-tree "$3^{tree}" -p "$new_base" -m x)"',
190139
+ 'git range-diff --no-color --creation-factor=1000 "$1..$(git commit-tree "$2^{tree}" -p "$1" -m x)" "$3..$(git commit-tree "$4^{tree}" -p "$3" -m x)"',
190137
190140
  "--",
190141
+ oldBase,
190138
190142
  params.beforeSha,
190139
- params.baseBranch,
190143
+ newBase,
190140
190144
  params.headSha
190141
190145
  ],
190142
190146
  { log: false }
@@ -190149,6 +190153,27 @@ function computeIncrementalDiff(params) {
190149
190153
  return { status: "unavailable", reason };
190150
190154
  }
190151
190155
  }
190156
+ function git(args2) {
190157
+ return $("git", args2, { log: false }).trim();
190158
+ }
190159
+ function versionBase(params) {
190160
+ let best;
190161
+ for (const tip of params.tips) {
190162
+ let sha;
190163
+ try {
190164
+ sha = git(["merge-base", params.sha, tip]);
190165
+ } catch {
190166
+ continue;
190167
+ }
190168
+ const commits = Number(git(["rev-list", "--count", `${sha}..${params.sha}`]));
190169
+ if (!best || commits < best.commits) best = { tip, sha, commits };
190170
+ }
190171
+ if (!best) throw new Error(`no merge base between ${params.sha} and the base branch`);
190172
+ if (best.tip !== params.tips[0]) {
190173
+ log.info(`\xBB incremental diff: ${params.sha.slice(0, 7)} sits on former base tip ${best.tip}`);
190174
+ }
190175
+ return best.sha;
190176
+ }
190152
190177
  function isDiffPrefix(ch) {
190153
190178
  return ch === " " || ch === "+" || ch === "-";
190154
190179
  }
@@ -190176,14 +190201,28 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
190176
190201
  if (lastHunkHdr?.seq === line.seq) hunkHdrEmitted = true;
190177
190202
  }
190178
190203
  function flushBefore() {
190179
- if (lastOuterHdr && !outerHdrEmitted) emit(lastOuterHdr);
190180
- if (lastFileHdr && !fileHdrEmitted) emit(lastFileHdr);
190181
- if (lastHunkHdr && !hunkHdrEmitted) emit(lastHunkHdr);
190182
- for (const line of beforeBuf) {
190183
- if (line.seq > lastEmittedSeq) emit(line);
190204
+ const headers = [
190205
+ outerHdrEmitted ? null : lastOuterHdr,
190206
+ fileHdrEmitted ? null : lastFileHdr,
190207
+ hunkHdrEmitted ? null : lastHunkHdr
190208
+ ].filter((line) => line !== null);
190209
+ const pending = [...headers, ...beforeBuf.filter((line) => line.seq > lastEmittedSeq)].sort(
190210
+ (a, b) => a.seq - b.seq
190211
+ );
190212
+ for (const [i, line] of pending.entries()) {
190213
+ if (pending[i - 1]?.seq !== line.seq) emit(line);
190184
190214
  }
190185
190215
  beforeBuf.length = 0;
190186
190216
  }
190217
+ function isFileHeader(pos) {
190218
+ return raw2.startsWith("## ", pos) && !raw2.startsWith("## Commit message", pos);
190219
+ }
190220
+ function markFileHeader(line, emitted) {
190221
+ lastFileHdr = line;
190222
+ fileHdrEmitted = emitted;
190223
+ lastHunkHdr = null;
190224
+ hunkHdrEmitted = true;
190225
+ }
190187
190226
  let cursor = 0;
190188
190227
  while (cursor < raw2.length) {
190189
190228
  const eol = raw2.indexOf("\n", cursor);
@@ -190193,7 +190232,8 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
190193
190232
  continue;
190194
190233
  }
190195
190234
  if (raw2.startsWith(" @@", cursor)) {
190196
- lastOuterHdr = { prefix: "", from: cursor + 4, to: lineEnd, seq };
190235
+ const label = raw2.slice(cursor + 4, lineEnd);
190236
+ lastOuterHdr = label === "@@ Metadata" || label === "@@ Commit message" ? null : { prefix: "", from: cursor + 4, to: lineEnd, seq };
190197
190237
  outerHdrEmitted = false;
190198
190238
  lastFileHdr = null;
190199
190239
  fileHdrEmitted = true;
@@ -190214,13 +190254,13 @@ function postProcessRangeDiff(raw2, contextLines = 3) {
190214
190254
  } else if (isDiffPrefix(raw2[contentPos])) {
190215
190255
  isChange = isOuterChange;
190216
190256
  line = { prefix, from: contentPos + 1, to: lineEnd, seq };
190257
+ if (raw2[contentPos] === " " && isFileHeader(contentPos + 1)) {
190258
+ markFileHeader(line, isChange);
190259
+ }
190217
190260
  } else {
190218
190261
  line = { prefix, from: contentPos, to: lineEnd, seq };
190219
- if (raw2.startsWith("## ", contentPos) && !raw2.startsWith("## Commit message", contentPos)) {
190220
- lastFileHdr = line;
190221
- fileHdrEmitted = false;
190222
- lastHunkHdr = null;
190223
- hunkHdrEmitted = true;
190262
+ if (isFileHeader(contentPos)) {
190263
+ markFileHeader(line, false);
190224
190264
  } else if (raw2.startsWith("@@", contentPos) && !raw2.startsWith("@@ Metadata", contentPos)) {
190225
190265
  lastHunkHdr = line;
190226
190266
  hunkHdrEmitted = false;
@@ -196213,6 +196253,7 @@ var STRING_KEYS = [
196213
196253
  "prNodeId",
196214
196254
  "issueNodeId",
196215
196255
  "reviewNodeId",
196256
+ "reviewedCommitSha",
196216
196257
  "planCommentNodeId",
196217
196258
  "summarySnapshot",
196218
196259
  "model",
@@ -197377,7 +197418,10 @@ async function createAndSubmitWithFooter(ctx, params, opts) {
197377
197418
  }
197378
197419
  }
197379
197420
  async function reportReviewNodeId(ctx, params) {
197380
- await patchWorkflowRunFields(ctx, { reviewNodeId: params.nodeId });
197421
+ await patchWorkflowRunFields(ctx, {
197422
+ reviewNodeId: params.nodeId,
197423
+ reviewedCommitSha: params.reviewedSha
197424
+ });
197381
197425
  }
197382
197426
 
197383
197427
  // utils/setup.ts
@@ -197971,6 +198015,34 @@ function describeHead(h) {
197971
198015
  if (h.kind === "branch") return `branch \`${h.name}\``;
197972
198016
  return `detached HEAD \`${h.sha}\``;
197973
198017
  }
198018
+ var FORMER_BASE_TIPS_QUERY = `
198019
+ query ($owner: String!, $repo: String!, $number: Int!) {
198020
+ repository(owner: $owner, name: $repo) {
198021
+ pullRequest(number: $number) {
198022
+ timelineItems(itemTypes: [BASE_REF_FORCE_PUSHED_EVENT], last: 100) {
198023
+ nodes { ... on BaseRefForcePushedEvent { beforeCommit { oid } } }
198024
+ }
198025
+ }
198026
+ }
198027
+ }
198028
+ `;
198029
+ async function getFormerBaseTips(ctx, pullNumber) {
198030
+ try {
198031
+ const response = await ctx.octokit.graphql(FORMER_BASE_TIPS_QUERY, {
198032
+ owner: ctx.repo.owner,
198033
+ repo: ctx.repo.name,
198034
+ number: pullNumber
198035
+ });
198036
+ return response.repository.pullRequest.timelineItems.nodes.flatMap(
198037
+ (node3) => node3.beforeCommit ? [node3.beforeCommit.oid] : []
198038
+ );
198039
+ } catch (error63) {
198040
+ log.warning(
198041
+ `\xBB former base tips unavailable: ${error63 instanceof Error ? error63.message : String(error63)}`
198042
+ );
198043
+ return [];
198044
+ }
198045
+ }
197974
198046
  function CheckoutPrTool(ctx) {
197975
198047
  const runCheckout = async (pull_number, signal) => {
197976
198048
  const prResponse = await ctx.octokit.rest.pulls.get({
@@ -198023,7 +198095,8 @@ function CheckoutPrTool(ctx) {
198023
198095
  const incremental = computeIncrementalDiff({
198024
198096
  baseBranch: pr.baseRef,
198025
198097
  beforeSha: primary.beforeSha,
198026
- headSha: checkoutSha
198098
+ headSha: checkoutSha,
198099
+ formerBaseTips: await getFormerBaseTips(ctx, pull_number)
198027
198100
  });
198028
198101
  if (incremental.status === "ok") {
198029
198102
  incrementalDiffPath = join16(
@@ -204661,7 +204734,7 @@ async function postReviewCleanup(ctx) {
204661
204734
  if (!review) {
204662
204735
  const checkoutSha = primaryRepoState(ctx.toolState).checkoutSha;
204663
204736
  if (ctx.toolState.selectedMode === "IncrementalReview" && checkoutSha) {
204664
- await patchWorkflowRunFields(ctx, { reviewSkipped: true });
204737
+ await patchWorkflowRunFields(ctx, { reviewSkipped: true, reviewedCommitSha: checkoutSha });
204665
204738
  await bestEffort(
204666
204739
  () => dispatchFollowUpReReview(ctx, checkoutSha),
204667
204740
  "follow-up re-review dispatch"
@@ -204670,7 +204743,7 @@ async function postReviewCleanup(ctx) {
204670
204743
  return;
204671
204744
  }
204672
204745
  delete ctx.toolState.review;
204673
- await bestEffort(() => reportReviewNodeId(ctx, { nodeId: review.nodeId }), "reportReviewNodeId");
204746
+ await bestEffort(() => reportReviewNodeId(ctx, review), "reportReviewNodeId");
204674
204747
  if (review.reviewedSha) {
204675
204748
  await bestEffort(
204676
204749
  () => dispatchFollowUpReReview(ctx, review.reviewedSha),
package/dist/internal.js CHANGED
@@ -8,7 +8,7 @@ var __export = (target, all) => {
8
8
  // package.json
9
9
  var package_default = {
10
10
  name: "pullfrog",
11
- version: "0.1.103",
11
+ version: "0.1.104",
12
12
  cliContractVersion: "1",
13
13
  type: "module",
14
14
  bin: {
@@ -263,4 +263,5 @@ export declare function createAndSubmitWithFooter(ctx: ToolContext, params: Rest
263
263
  */
264
264
  export declare function reportReviewNodeId(ctx: ToolContext, params: {
265
265
  nodeId: string;
266
+ reviewedSha: string | undefined;
266
267
  }): Promise<void>;
@@ -4,6 +4,8 @@ import type { ToolContext } from "../mcp/server.ts";
4
4
  * String-valued PATCH fields (all serialized identically on the wire):
5
5
  * - artifact node IDs (`*NodeId`, `summarySnapshot`) — PATCHed incrementally
6
6
  * by MCP tools as GitHub entities are created during the run.
7
+ * - `reviewedCommitSha` — the PR head a review covered, sent with `reviewNodeId`
8
+ * or `reviewSkipped`; the next re-review's delta starts there.
7
9
  * - `model` — the resolved/effective model the run actually ran on (proxy spec
8
10
  * for router/oss, post-fallback slug otherwise; NOT the configured
9
11
  * `Repo.model` slug), PATCHed once at end-of-run so per-model cost analytics
@@ -13,7 +15,7 @@ import type { ToolContext } from "../mcp/server.ts";
13
15
  * workflow can start the action several times in one run, and the server keeps
14
16
  * one row per start, keyed by `START_KEY`, and sums them onto the run.
15
17
  */
16
- declare const STRING_KEYS: readonly ["prNodeId", "issueNodeId", "reviewNodeId", "planCommentNodeId", "summarySnapshot", "model", "agent", "credential"];
18
+ declare const STRING_KEYS: readonly ["prNodeId", "issueNodeId", "reviewNodeId", "reviewedCommitSha", "planCommentNodeId", "summarySnapshot", "model", "agent", "credential"];
17
19
  /**
18
20
  * Number-valued usage fields — aggregated across all agent calls and PATCHed
19
21
  * once at end-of-run. Token counts are Int4 on the DB side (ample for any
@@ -23,7 +25,7 @@ declare const STRING_KEYS: readonly ["prNodeId", "issueNodeId", "reviewNodeId",
23
25
  declare const NUMBER_KEYS: readonly ["inputTokens", "outputTokens", "cacheReadTokens", "cacheWriteTokens", "costUsd"];
24
26
  /** one-way flags, only ever sent as `true`. Keep in sync with `BOOLEAN_FIELDS` in the server route. */
25
27
  declare const FLAG_KEYS: readonly ["reviewSkipped"];
26
- export type WorkflowRunPatch = Partial<Record<(typeof STRING_KEYS)[number], string>> & Partial<Record<(typeof NUMBER_KEYS)[number], number>> & Partial<Record<(typeof FLAG_KEYS)[number], true>> & {
28
+ export type WorkflowRunPatch = Partial<Record<(typeof STRING_KEYS)[number], string | undefined>> & Partial<Record<(typeof NUMBER_KEYS)[number], number>> & Partial<Record<(typeof FLAG_KEYS)[number], true>> & {
27
29
  succeeded?: boolean;
28
30
  };
29
31
  /** PATCH workflow-run fields (Pullfrog JWT, not GitHub). */
@@ -2,6 +2,8 @@ type ComputeIncrementalDiffParams = {
2
2
  baseBranch: string;
3
3
  beforeSha: string;
4
4
  headSha: string;
5
+ /** tips the base branch was force-pushed away from, which either version may still be built on */
6
+ formerBaseTips: string[];
5
7
  };
6
8
  /**
7
9
  * computes the incremental diff between two versions of a PR using range-diff
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.103",
3
+ "version": "0.1.104",
4
4
  "cliContractVersion": "1",
5
5
  "type": "module",
6
6
  "bin": {