pullfrog 0.1.68 → 0.1.69
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 +44 -20
- package/dist/external.d.ts +9 -0
- package/dist/index.js +42 -18
- package/dist/internal.js +13 -10
- package/dist/mcp/reviewComments.d.ts +42 -0
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -110773,10 +110773,9 @@ var providers = {
|
|
|
110773
110773
|
// was the catalog's only GLM route, and Grok had no Zen route at all.
|
|
110774
110774
|
glm: {
|
|
110775
110775
|
displayName: "GLM",
|
|
110776
|
-
resolve: "opencode/glm-5.
|
|
110777
|
-
effort: ["high", "max"],
|
|
110778
|
-
|
|
110779
|
-
openRouterResolve: "openrouter/z-ai/glm-5.2"
|
|
110776
|
+
resolve: "opencode/glm-5.3",
|
|
110777
|
+
effort: ["low", "high", "max"],
|
|
110778
|
+
openRouterResolve: "openrouter/z-ai/glm-5.3"
|
|
110780
110779
|
},
|
|
110781
110780
|
grok: {
|
|
110782
110781
|
displayName: "Grok",
|
|
@@ -110823,10 +110822,11 @@ var providers = {
|
|
|
110823
110822
|
// OpenCode Go is a separate $10/mo subscription from Zen, served on its own
|
|
110824
110823
|
// base URL (`https://opencode.ai/zen/go/v1`) but authenticated with the SAME
|
|
110825
110824
|
// `OPENCODE_API_KEY`. it carries the open-weight coding models plus a couple
|
|
110826
|
-
// of frontier ones,
|
|
110827
|
-
//
|
|
110828
|
-
//
|
|
110829
|
-
//
|
|
110825
|
+
// of frontier ones, six of which are served ONLY here — Zen's `/v1/models`
|
|
110826
|
+
// does not list qwen3.7/3.8-*, mimo-v2.5-pro, longcat-2.0 or hy3. (it listed
|
|
110827
|
+
// no glm-5.3* either until 2026-09, which is why `opencode/glm` trailed on
|
|
110828
|
+
// 5.2.) so for a Go subscriber this provider is not a duplicate route to Zen,
|
|
110829
|
+
// it is the only route to a large part of what they pay for.
|
|
110830
110830
|
// like `opencode` and `openrouter` this is a ROUTER, not a vendor: slugs and
|
|
110831
110831
|
// display names mirror the upstream brand tier, and the picker groups them
|
|
110832
110832
|
// under the upstream vendor.
|
|
@@ -110895,7 +110895,9 @@ var providers = {
|
|
|
110895
110895
|
// `high` the Go route does not.
|
|
110896
110896
|
effort: ["low", "medium", "xhigh"],
|
|
110897
110897
|
openRouterEffort: ["minimal", "low", "medium", "high", "xhigh"],
|
|
110898
|
-
|
|
110898
|
+
// OpenRouter retired the rolling `qwen3.8-max` for a dated snapshot, so
|
|
110899
|
+
// this side has to pin one; the Go route still carries the rolling id.
|
|
110900
|
+
openRouterResolve: "openrouter/qwen/qwen3.8-max-0902"
|
|
110899
110901
|
},
|
|
110900
110902
|
"qwen-plus": {
|
|
110901
110903
|
displayName: "Qwen Plus",
|
|
@@ -112252,7 +112254,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
112252
112254
|
// package.json
|
|
112253
112255
|
var package_default = {
|
|
112254
112256
|
name: "pullfrog",
|
|
112255
|
-
version: "0.1.
|
|
112257
|
+
version: "0.1.69",
|
|
112256
112258
|
type: "module",
|
|
112257
112259
|
bin: {
|
|
112258
112260
|
pullfrog: "dist/cli.mjs",
|
|
@@ -113059,6 +113061,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
113059
113061
|
- record what was done (or why nothing was done)
|
|
113060
113062
|
|
|
113061
113063
|
5. Quality check:
|
|
113064
|
+
- re-call \`${t2("get_review_comments")}\` with \`fresh: true\` before committing: reviewers keep commenting while you work, and any thread it now returns that you have not addressed is in scope for this run.
|
|
113062
113065
|
- test changes, then review the diff before committing \u2014 verify only intended changes are present, no debug artifacts remain, no fix turned out to be bloat in context (revert any that did), and the changes are clean enough that a senior engineer would approve without hesitation
|
|
113063
113066
|
- ${commitStep}
|
|
113064
113067
|
|
|
@@ -113068,7 +113071,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
113068
113071
|
- **once the fix is live on the remote**, for each thread you acted on:
|
|
113069
113072
|
- reply ONCE via \`${t2("reply_to_review_comment")}\`. The \`comment_id\` parameter takes the root comment's numeric \`id=\` (from the first \`comment author=...\` tag in the \`${t2("get_review_comments")}\` output) \u2014 NOT the \`thread=\` value; that's a separate GraphQL ID used by resolve. The runtime dedupes identical bodies within a session.
|
|
113070
113073
|
- **immediately** call \`${t2("resolve_review_thread")}\` with that thread's \`thread=\` value as \`thread_id\`. Resolve every thread where you (a) made the requested code change in full \u2014 partial fixes leave the thread open \u2014 OR (b) replied with a substantive answer the user explicitly asked for. Do NOT resolve threads where you pushed back on the request and the disagreement is unresolved; leave those open for the human to mediate.
|
|
113071
|
-
- call \`${t2("report_progress")}\` with a brief summary
|
|
113074
|
+
- call \`${t2("report_progress")}\` with a brief summary. If your last \`${t2("get_review_comments")}\` turned up threads you did not address, name them there \u2014 a review arriving this late is suppressed as a duplicate and nothing else will pick it up.`
|
|
113072
113075
|
},
|
|
113073
113076
|
// Review and IncrementalReview route the minimum reviewfrog specialists
|
|
113074
113077
|
// needed to cover unresolved, disposition-changing hypotheses. Most runs
|
|
@@ -199174,7 +199177,10 @@ function extractFromFilePatches(hunks, startLine, endLine, side) {
|
|
|
199174
199177
|
}
|
|
199175
199178
|
var GetReviewComments = type({
|
|
199176
199179
|
pull_number: type.number.describe("The pull request number"),
|
|
199177
|
-
review_id: type.number.describe("The review ID to get comments for")
|
|
199180
|
+
review_id: type.number.describe("The review ID to get comments for"),
|
|
199181
|
+
"fresh?": type.boolean.describe(
|
|
199182
|
+
"Bypass the 60s thread cache. Use for the re-read before committing, so comments that landed while you worked are actually seen; leave unset otherwise."
|
|
199183
|
+
)
|
|
199178
199184
|
});
|
|
199179
199185
|
function hasThumbsUpFrom(comment, username) {
|
|
199180
199186
|
if (!comment.reactionGroups) return false;
|
|
@@ -199208,7 +199214,7 @@ function formatReviewThreads(threadBlocks, header) {
|
|
|
199208
199214
|
}
|
|
199209
199215
|
const lines = [];
|
|
199210
199216
|
lines.push(
|
|
199211
|
-
`#
|
|
199217
|
+
`# Open Threads (${threadBlocks.length}) on PR #${header.pullNumber} - dispatched by Review ${header.reviewId} from ${header.reviewer}`
|
|
199212
199218
|
);
|
|
199213
199219
|
lines.push("");
|
|
199214
199220
|
if (threadBlocks.length > 0) {
|
|
@@ -199349,11 +199355,27 @@ async function getReviewThreads(input2) {
|
|
|
199349
199355
|
if (!thread?.comments?.nodes) return false;
|
|
199350
199356
|
return thread.comments.nodes.some((c2) => c2?.pullRequestReview?.databaseId === input2.reviewId);
|
|
199351
199357
|
});
|
|
199352
|
-
if (
|
|
199353
|
-
|
|
199358
|
+
if (input2.approvedBy) {
|
|
199359
|
+
const username = input2.approvedBy;
|
|
199360
|
+
return threadsForReview.filter((thread) => threadHasThumbsUpFrom(thread, username));
|
|
199354
199361
|
}
|
|
199355
|
-
|
|
199356
|
-
return threadsForReview
|
|
199362
|
+
if (!input2.addressScope) return threadsForReview;
|
|
199363
|
+
return [...threadsForReview, ...concurrentThreads(allThreads, threadsForReview, input2)];
|
|
199364
|
+
}
|
|
199365
|
+
function concurrentThreads(allThreads, threadsForReview, input2) {
|
|
199366
|
+
const alreadyIncluded = new Set(threadsForReview.map((thread) => thread.id));
|
|
199367
|
+
return allThreads.filter((thread) => {
|
|
199368
|
+
if (!thread?.comments?.nodes || alreadyIncluded.has(thread.id)) return false;
|
|
199369
|
+
if (thread.isResolved) return false;
|
|
199370
|
+
const comments = thread.comments.nodes.filter((c2) => c2 !== null);
|
|
199371
|
+
if (comments.length === 0) return false;
|
|
199372
|
+
if (comments.some((c2) => isPullfrog(c2.author?.login))) return false;
|
|
199373
|
+
return input2.addressScope === "all" || comments.some((c2) => mentionsPullfrog(c2.body));
|
|
199374
|
+
});
|
|
199375
|
+
}
|
|
199376
|
+
function mentionsPullfrog(body) {
|
|
199377
|
+
if (!body) return false;
|
|
199378
|
+
return body.split(/\r?\n/).filter((line) => !line.trim().startsWith(">")).join("\n").toLowerCase().includes("@pullfrog");
|
|
199357
199379
|
}
|
|
199358
199380
|
function formatReviewData(input2) {
|
|
199359
199381
|
const rawReviewBody = input2.review.body;
|
|
@@ -199450,6 +199472,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
199450
199472
|
parameters: GetReviewComments,
|
|
199451
199473
|
execute: execute(async (params) => {
|
|
199452
199474
|
const approvedBy = ctx.payload.event.trigger === "fix_review" && ctx.payload.event.approved_only ? ctx.payload.triggerer : void 0;
|
|
199475
|
+
if (params.fresh) invalidateReviewThreadCache(ctx.repo);
|
|
199453
199476
|
const result = await getReviewData({
|
|
199454
199477
|
octokit: ctx.octokit,
|
|
199455
199478
|
owner: ctx.repo.owner,
|
|
@@ -199457,6 +199480,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
199457
199480
|
pullNumber: params.pull_number,
|
|
199458
199481
|
reviewId: params.review_id,
|
|
199459
199482
|
approvedBy,
|
|
199483
|
+
addressScope: ctx.payload.event.address_scope,
|
|
199460
199484
|
tmpdir: ctx.tmpdir,
|
|
199461
199485
|
githubToken: ctx.githubInstallationToken
|
|
199462
199486
|
});
|
|
@@ -199487,7 +199511,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
199487
199511
|
threadCount: threadBlocks.length,
|
|
199488
199512
|
commentsPath,
|
|
199489
199513
|
toc: formatted.toc,
|
|
199490
|
-
instructions: `the file at commentsPath contains ${threadBlocks.length} review threads with full conversation history. comments marked with * are from the target review (${params.review_id}). the TOC shows each thread's file:line and the line number where it appears in the file. to read a specific thread, use: grep -A 50 "^## <file:line>" ${commentsPath} (replace <file:line> with the path from the TOC, e.g. "^## action/utils/foo.ts:42"). address each thread in order, working through one file at a time.`
|
|
199514
|
+
instructions: `the file at commentsPath contains ${threadBlocks.length} review threads with full conversation history. comments marked with * are from the target review (${params.review_id}); any other thread is open feedback on this PR that is also yours to address, including comments that landed after this run started. the TOC shows each thread's file:line and the line number where it appears in the file. to read a specific thread, use: grep -A 50 "^## <file:line>" ${commentsPath} (replace <file:line> with the path from the TOC, e.g. "^## action/utils/foo.ts:42"). address each thread in order, working through one file at a time.`
|
|
199491
199515
|
};
|
|
199492
199516
|
})
|
|
199493
199517
|
});
|
|
@@ -204932,7 +204956,7 @@ function resolveTarget(repo) {
|
|
|
204932
204956
|
return { owner: match3[1], repo: match3[2] };
|
|
204933
204957
|
}
|
|
204934
204958
|
function cliVersion() {
|
|
204935
|
-
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.
|
|
204959
|
+
const parts = /^(\d+)\.(\d+)\.(\d+)$/.exec("0.1.69");
|
|
204936
204960
|
if (!parts) return "0.0.0";
|
|
204937
204961
|
return `${Number(parts[1])}.${Number(parts[2])}.${Number(parts[3])}`;
|
|
204938
204962
|
}
|
|
@@ -205099,7 +205123,7 @@ async function runCli5(input2) {
|
|
|
205099
205123
|
}
|
|
205100
205124
|
|
|
205101
205125
|
// cli.ts
|
|
205102
|
-
var VERSION10 = "0.1.
|
|
205126
|
+
var VERSION10 = "0.1.69";
|
|
205103
205127
|
var bin = basename2(process.argv[1] || "");
|
|
205104
205128
|
var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
|
|
205105
205129
|
var rawArgs = process.argv.slice(2);
|
package/dist/external.d.ts
CHANGED
|
@@ -137,6 +137,15 @@ interface PullRequestReviewSubmittedEvent extends BasePayloadEvent {
|
|
|
137
137
|
body: string | null;
|
|
138
138
|
review_state: string;
|
|
139
139
|
branch: string;
|
|
140
|
+
/**
|
|
141
|
+
* How far past the dispatching review this run may read: `all` on a
|
|
142
|
+
* Pullfrog-authored PR, `mentions` when only `@pullfrog`-mentioning threads
|
|
143
|
+
* are in scope. The server already computes this to decide whether to
|
|
144
|
+
* dispatch; forwarding it is what lets `get_review_comments` pick up the
|
|
145
|
+
* comments that land while the run is working. Optional so a payload from an
|
|
146
|
+
* older server build still parses against a newer action.
|
|
147
|
+
*/
|
|
148
|
+
address_scope?: "all" | "mentions" | undefined;
|
|
140
149
|
}
|
|
141
150
|
interface PullRequestReviewCommentCreatedEvent extends BasePayloadEvent {
|
|
142
151
|
trigger: "pull_request_review_comment_created";
|
package/dist/index.js
CHANGED
|
@@ -108283,10 +108283,9 @@ var providers = {
|
|
|
108283
108283
|
// was the catalog's only GLM route, and Grok had no Zen route at all.
|
|
108284
108284
|
glm: {
|
|
108285
108285
|
displayName: "GLM",
|
|
108286
|
-
resolve: "opencode/glm-5.
|
|
108287
|
-
effort: ["high", "max"],
|
|
108288
|
-
|
|
108289
|
-
openRouterResolve: "openrouter/z-ai/glm-5.2"
|
|
108286
|
+
resolve: "opencode/glm-5.3",
|
|
108287
|
+
effort: ["low", "high", "max"],
|
|
108288
|
+
openRouterResolve: "openrouter/z-ai/glm-5.3"
|
|
108290
108289
|
},
|
|
108291
108290
|
grok: {
|
|
108292
108291
|
displayName: "Grok",
|
|
@@ -108333,10 +108332,11 @@ var providers = {
|
|
|
108333
108332
|
// OpenCode Go is a separate $10/mo subscription from Zen, served on its own
|
|
108334
108333
|
// base URL (`https://opencode.ai/zen/go/v1`) but authenticated with the SAME
|
|
108335
108334
|
// `OPENCODE_API_KEY`. it carries the open-weight coding models plus a couple
|
|
108336
|
-
// of frontier ones,
|
|
108337
|
-
//
|
|
108338
|
-
//
|
|
108339
|
-
//
|
|
108335
|
+
// of frontier ones, six of which are served ONLY here — Zen's `/v1/models`
|
|
108336
|
+
// does not list qwen3.7/3.8-*, mimo-v2.5-pro, longcat-2.0 or hy3. (it listed
|
|
108337
|
+
// no glm-5.3* either until 2026-09, which is why `opencode/glm` trailed on
|
|
108338
|
+
// 5.2.) so for a Go subscriber this provider is not a duplicate route to Zen,
|
|
108339
|
+
// it is the only route to a large part of what they pay for.
|
|
108340
108340
|
// like `opencode` and `openrouter` this is a ROUTER, not a vendor: slugs and
|
|
108341
108341
|
// display names mirror the upstream brand tier, and the picker groups them
|
|
108342
108342
|
// under the upstream vendor.
|
|
@@ -108405,7 +108405,9 @@ var providers = {
|
|
|
108405
108405
|
// `high` the Go route does not.
|
|
108406
108406
|
effort: ["low", "medium", "xhigh"],
|
|
108407
108407
|
openRouterEffort: ["minimal", "low", "medium", "high", "xhigh"],
|
|
108408
|
-
|
|
108408
|
+
// OpenRouter retired the rolling `qwen3.8-max` for a dated snapshot, so
|
|
108409
|
+
// this side has to pin one; the Go route still carries the rolling id.
|
|
108410
|
+
openRouterResolve: "openrouter/qwen/qwen3.8-max-0902"
|
|
108409
108411
|
},
|
|
108410
108412
|
"qwen-plus": {
|
|
108411
108413
|
displayName: "Qwen Plus",
|
|
@@ -109762,7 +109764,7 @@ var import_semver = __toESM(require_semver2(), 1);
|
|
|
109762
109764
|
// package.json
|
|
109763
109765
|
var package_default = {
|
|
109764
109766
|
name: "pullfrog",
|
|
109765
|
-
version: "0.1.
|
|
109767
|
+
version: "0.1.69",
|
|
109766
109768
|
type: "module",
|
|
109767
109769
|
bin: {
|
|
109768
109770
|
pullfrog: "dist/cli.mjs",
|
|
@@ -110569,6 +110571,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
110569
110571
|
- record what was done (or why nothing was done)
|
|
110570
110572
|
|
|
110571
110573
|
5. Quality check:
|
|
110574
|
+
- re-call \`${t("get_review_comments")}\` with \`fresh: true\` before committing: reviewers keep commenting while you work, and any thread it now returns that you have not addressed is in scope for this run.
|
|
110572
110575
|
- test changes, then review the diff before committing \u2014 verify only intended changes are present, no debug artifacts remain, no fix turned out to be bloat in context (revert any that did), and the changes are clean enough that a senior engineer would approve without hesitation
|
|
110573
110576
|
- ${commitStep}
|
|
110574
110577
|
|
|
@@ -110578,7 +110581,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
110578
110581
|
- **once the fix is live on the remote**, for each thread you acted on:
|
|
110579
110582
|
- reply ONCE via \`${t("reply_to_review_comment")}\`. The \`comment_id\` parameter takes the root comment's numeric \`id=\` (from the first \`comment author=...\` tag in the \`${t("get_review_comments")}\` output) \u2014 NOT the \`thread=\` value; that's a separate GraphQL ID used by resolve. The runtime dedupes identical bodies within a session.
|
|
110580
110583
|
- **immediately** call \`${t("resolve_review_thread")}\` with that thread's \`thread=\` value as \`thread_id\`. Resolve every thread where you (a) made the requested code change in full \u2014 partial fixes leave the thread open \u2014 OR (b) replied with a substantive answer the user explicitly asked for. Do NOT resolve threads where you pushed back on the request and the disagreement is unresolved; leave those open for the human to mediate.
|
|
110581
|
-
- call \`${t("report_progress")}\` with a brief summary
|
|
110584
|
+
- call \`${t("report_progress")}\` with a brief summary. If your last \`${t("get_review_comments")}\` turned up threads you did not address, name them there \u2014 a review arriving this late is suppressed as a duplicate and nothing else will pick it up.`
|
|
110582
110585
|
},
|
|
110583
110586
|
// Review and IncrementalReview route the minimum reviewfrog specialists
|
|
110584
110587
|
// needed to cover unresolved, disposition-changing hypotheses. Most runs
|
|
@@ -196757,7 +196760,10 @@ function extractFromFilePatches(hunks, startLine, endLine, side) {
|
|
|
196757
196760
|
}
|
|
196758
196761
|
var GetReviewComments = type({
|
|
196759
196762
|
pull_number: type.number.describe("The pull request number"),
|
|
196760
|
-
review_id: type.number.describe("The review ID to get comments for")
|
|
196763
|
+
review_id: type.number.describe("The review ID to get comments for"),
|
|
196764
|
+
"fresh?": type.boolean.describe(
|
|
196765
|
+
"Bypass the 60s thread cache. Use for the re-read before committing, so comments that landed while you worked are actually seen; leave unset otherwise."
|
|
196766
|
+
)
|
|
196761
196767
|
});
|
|
196762
196768
|
function hasThumbsUpFrom(comment, username) {
|
|
196763
196769
|
if (!comment.reactionGroups) return false;
|
|
@@ -196791,7 +196797,7 @@ function formatReviewThreads(threadBlocks, header) {
|
|
|
196791
196797
|
}
|
|
196792
196798
|
const lines = [];
|
|
196793
196799
|
lines.push(
|
|
196794
|
-
`#
|
|
196800
|
+
`# Open Threads (${threadBlocks.length}) on PR #${header.pullNumber} - dispatched by Review ${header.reviewId} from ${header.reviewer}`
|
|
196795
196801
|
);
|
|
196796
196802
|
lines.push("");
|
|
196797
196803
|
if (threadBlocks.length > 0) {
|
|
@@ -196932,11 +196938,27 @@ async function getReviewThreads(input2) {
|
|
|
196932
196938
|
if (!thread?.comments?.nodes) return false;
|
|
196933
196939
|
return thread.comments.nodes.some((c) => c?.pullRequestReview?.databaseId === input2.reviewId);
|
|
196934
196940
|
});
|
|
196935
|
-
if (
|
|
196936
|
-
|
|
196941
|
+
if (input2.approvedBy) {
|
|
196942
|
+
const username = input2.approvedBy;
|
|
196943
|
+
return threadsForReview.filter((thread) => threadHasThumbsUpFrom(thread, username));
|
|
196937
196944
|
}
|
|
196938
|
-
|
|
196939
|
-
return threadsForReview
|
|
196945
|
+
if (!input2.addressScope) return threadsForReview;
|
|
196946
|
+
return [...threadsForReview, ...concurrentThreads(allThreads, threadsForReview, input2)];
|
|
196947
|
+
}
|
|
196948
|
+
function concurrentThreads(allThreads, threadsForReview, input2) {
|
|
196949
|
+
const alreadyIncluded = new Set(threadsForReview.map((thread) => thread.id));
|
|
196950
|
+
return allThreads.filter((thread) => {
|
|
196951
|
+
if (!thread?.comments?.nodes || alreadyIncluded.has(thread.id)) return false;
|
|
196952
|
+
if (thread.isResolved) return false;
|
|
196953
|
+
const comments = thread.comments.nodes.filter((c) => c !== null);
|
|
196954
|
+
if (comments.length === 0) return false;
|
|
196955
|
+
if (comments.some((c) => isPullfrog(c.author?.login))) return false;
|
|
196956
|
+
return input2.addressScope === "all" || comments.some((c) => mentionsPullfrog(c.body));
|
|
196957
|
+
});
|
|
196958
|
+
}
|
|
196959
|
+
function mentionsPullfrog(body) {
|
|
196960
|
+
if (!body) return false;
|
|
196961
|
+
return body.split(/\r?\n/).filter((line) => !line.trim().startsWith(">")).join("\n").toLowerCase().includes("@pullfrog");
|
|
196940
196962
|
}
|
|
196941
196963
|
function formatReviewData(input2) {
|
|
196942
196964
|
const rawReviewBody = input2.review.body;
|
|
@@ -197033,6 +197055,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
197033
197055
|
parameters: GetReviewComments,
|
|
197034
197056
|
execute: execute(async (params) => {
|
|
197035
197057
|
const approvedBy = ctx.payload.event.trigger === "fix_review" && ctx.payload.event.approved_only ? ctx.payload.triggerer : void 0;
|
|
197058
|
+
if (params.fresh) invalidateReviewThreadCache(ctx.repo);
|
|
197036
197059
|
const result = await getReviewData({
|
|
197037
197060
|
octokit: ctx.octokit,
|
|
197038
197061
|
owner: ctx.repo.owner,
|
|
@@ -197040,6 +197063,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
197040
197063
|
pullNumber: params.pull_number,
|
|
197041
197064
|
reviewId: params.review_id,
|
|
197042
197065
|
approvedBy,
|
|
197066
|
+
addressScope: ctx.payload.event.address_scope,
|
|
197043
197067
|
tmpdir: ctx.tmpdir,
|
|
197044
197068
|
githubToken: ctx.githubInstallationToken
|
|
197045
197069
|
});
|
|
@@ -197070,7 +197094,7 @@ function GetReviewCommentsTool(ctx) {
|
|
|
197070
197094
|
threadCount: threadBlocks.length,
|
|
197071
197095
|
commentsPath,
|
|
197072
197096
|
toc: formatted.toc,
|
|
197073
|
-
instructions: `the file at commentsPath contains ${threadBlocks.length} review threads with full conversation history. comments marked with * are from the target review (${params.review_id}). the TOC shows each thread's file:line and the line number where it appears in the file. to read a specific thread, use: grep -A 50 "^## <file:line>" ${commentsPath} (replace <file:line> with the path from the TOC, e.g. "^## action/utils/foo.ts:42"). address each thread in order, working through one file at a time.`
|
|
197097
|
+
instructions: `the file at commentsPath contains ${threadBlocks.length} review threads with full conversation history. comments marked with * are from the target review (${params.review_id}); any other thread is open feedback on this PR that is also yours to address, including comments that landed after this run started. the TOC shows each thread's file:line and the line number where it appears in the file. to read a specific thread, use: grep -A 50 "^## <file:line>" ${commentsPath} (replace <file:line> with the path from the TOC, e.g. "^## action/utils/foo.ts:42"). address each thread in order, working through one file at a time.`
|
|
197074
197098
|
};
|
|
197075
197099
|
})
|
|
197076
197100
|
});
|
package/dist/internal.js
CHANGED
|
@@ -579,10 +579,9 @@ var providers = {
|
|
|
579
579
|
// was the catalog's only GLM route, and Grok had no Zen route at all.
|
|
580
580
|
glm: {
|
|
581
581
|
displayName: "GLM",
|
|
582
|
-
resolve: "opencode/glm-5.
|
|
583
|
-
effort: ["high", "max"],
|
|
584
|
-
|
|
585
|
-
openRouterResolve: "openrouter/z-ai/glm-5.2"
|
|
582
|
+
resolve: "opencode/glm-5.3",
|
|
583
|
+
effort: ["low", "high", "max"],
|
|
584
|
+
openRouterResolve: "openrouter/z-ai/glm-5.3"
|
|
586
585
|
},
|
|
587
586
|
grok: {
|
|
588
587
|
displayName: "Grok",
|
|
@@ -629,10 +628,11 @@ var providers = {
|
|
|
629
628
|
// OpenCode Go is a separate $10/mo subscription from Zen, served on its own
|
|
630
629
|
// base URL (`https://opencode.ai/zen/go/v1`) but authenticated with the SAME
|
|
631
630
|
// `OPENCODE_API_KEY`. it carries the open-weight coding models plus a couple
|
|
632
|
-
// of frontier ones,
|
|
633
|
-
//
|
|
634
|
-
//
|
|
635
|
-
//
|
|
631
|
+
// of frontier ones, six of which are served ONLY here — Zen's `/v1/models`
|
|
632
|
+
// does not list qwen3.7/3.8-*, mimo-v2.5-pro, longcat-2.0 or hy3. (it listed
|
|
633
|
+
// no glm-5.3* either until 2026-09, which is why `opencode/glm` trailed on
|
|
634
|
+
// 5.2.) so for a Go subscriber this provider is not a duplicate route to Zen,
|
|
635
|
+
// it is the only route to a large part of what they pay for.
|
|
636
636
|
// like `opencode` and `openrouter` this is a ROUTER, not a vendor: slugs and
|
|
637
637
|
// display names mirror the upstream brand tier, and the picker groups them
|
|
638
638
|
// under the upstream vendor.
|
|
@@ -701,7 +701,9 @@ var providers = {
|
|
|
701
701
|
// `high` the Go route does not.
|
|
702
702
|
effort: ["low", "medium", "xhigh"],
|
|
703
703
|
openRouterEffort: ["minimal", "low", "medium", "high", "xhigh"],
|
|
704
|
-
|
|
704
|
+
// OpenRouter retired the rolling `qwen3.8-max` for a dated snapshot, so
|
|
705
|
+
// this side has to pin one; the Go route still carries the rolling id.
|
|
706
|
+
openRouterResolve: "openrouter/qwen/qwen3.8-max-0902"
|
|
705
707
|
},
|
|
706
708
|
"qwen-plus": {
|
|
707
709
|
displayName: "Qwen Plus",
|
|
@@ -1383,6 +1385,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
1383
1385
|
- record what was done (or why nothing was done)
|
|
1384
1386
|
|
|
1385
1387
|
5. Quality check:
|
|
1388
|
+
- re-call \`${t("get_review_comments")}\` with \`fresh: true\` before committing: reviewers keep commenting while you work, and any thread it now returns that you have not addressed is in scope for this run.
|
|
1386
1389
|
- test changes, then review the diff before committing \u2014 verify only intended changes are present, no debug artifacts remain, no fix turned out to be bloat in context (revert any that did), and the changes are clean enough that a senior engineer would approve without hesitation
|
|
1387
1390
|
- ${commitStep}
|
|
1388
1391
|
|
|
@@ -1392,7 +1395,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
|
1392
1395
|
- **once the fix is live on the remote**, for each thread you acted on:
|
|
1393
1396
|
- reply ONCE via \`${t("reply_to_review_comment")}\`. The \`comment_id\` parameter takes the root comment's numeric \`id=\` (from the first \`comment author=...\` tag in the \`${t("get_review_comments")}\` output) \u2014 NOT the \`thread=\` value; that's a separate GraphQL ID used by resolve. The runtime dedupes identical bodies within a session.
|
|
1394
1397
|
- **immediately** call \`${t("resolve_review_thread")}\` with that thread's \`thread=\` value as \`thread_id\`. Resolve every thread where you (a) made the requested code change in full \u2014 partial fixes leave the thread open \u2014 OR (b) replied with a substantive answer the user explicitly asked for. Do NOT resolve threads where you pushed back on the request and the disagreement is unresolved; leave those open for the human to mediate.
|
|
1395
|
-
- call \`${t("report_progress")}\` with a brief summary
|
|
1398
|
+
- call \`${t("report_progress")}\` with a brief summary. If your last \`${t("get_review_comments")}\` turned up threads you did not address, name them there \u2014 a review arriving this late is suppressed as a duplicate and nothing else will pick it up.`
|
|
1396
1399
|
},
|
|
1397
1400
|
// Review and IncrementalReview route the minimum reviewfrog specialists
|
|
1398
1401
|
// needed to cover unresolved, disposition-changing hypotheses. Most runs
|
|
@@ -67,6 +67,7 @@ export declare function parseFilePatches(patch: string): ParsedHunk[];
|
|
|
67
67
|
export declare const GetReviewComments: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
68
68
|
pull_number: number;
|
|
69
69
|
review_id: number;
|
|
70
|
+
fresh?: boolean;
|
|
70
71
|
}, {}>;
|
|
71
72
|
/**
|
|
72
73
|
* formats thread blocks into markdown with TOC and line numbers.
|
|
@@ -94,6 +95,37 @@ export declare function buildThreadBlocks(threads: ReviewThread[], filePatchMap:
|
|
|
94
95
|
lineRange: string;
|
|
95
96
|
content: string[];
|
|
96
97
|
}[];
|
|
98
|
+
/**
|
|
99
|
+
* Open threads on the PR that this run should also address, beyond the review
|
|
100
|
+
* that dispatched it.
|
|
101
|
+
*
|
|
102
|
+
* A reviewer working through a PR emits one `pull_request_review_submitted` per
|
|
103
|
+
* submission, so a second review lands seconds after the first — and until this
|
|
104
|
+
* existed, the run already working on the PR could never see it: the filter
|
|
105
|
+
* above keeps only threads carrying a comment from the *dispatched* `reviewId`,
|
|
106
|
+
* so a new comment on a different line was invisible no matter how often the
|
|
107
|
+
* agent re-read. That is what made the in-flight cap in `handleWebhook.ts` a
|
|
108
|
+
* silent loss rather than the coalescing it was documented as (#1103).
|
|
109
|
+
*
|
|
110
|
+
* `fetchAllReviewThreads` already pulls every thread on the PR and caches it for
|
|
111
|
+
* 60s, so this costs no extra round trip and a re-read mid-run picks up whatever
|
|
112
|
+
* landed behind the run.
|
|
113
|
+
*
|
|
114
|
+
* Three exclusions, each load-bearing:
|
|
115
|
+
* - resolved threads are done, and re-opening that conversation is noise.
|
|
116
|
+
* - a thread Pullfrog has already spoken in was handled by an earlier run.
|
|
117
|
+
* That deliberately includes the pushbacks AddressReviews leaves open for a
|
|
118
|
+
* human to mediate — re-addressing one re-litigates a settled disagreement —
|
|
119
|
+
* and it is also what stops this mode replying to Pullfrog's own reviews.
|
|
120
|
+
* - under `mentions` a concurrent thread is in scope only if it actually asked
|
|
121
|
+
* for Pullfrog. Deliberately asymmetric with `threadsForReview`, which is
|
|
122
|
+
* returned whole and always was: the dispatching review is the one the
|
|
123
|
+
* server already adjudicated, and the agent needs its unmentioned threads as
|
|
124
|
+
* context for the comment that did mention it. Which of those to ACT on stays
|
|
125
|
+
* the mode prompt's job. A concurrent review has had no such adjudication, so
|
|
126
|
+
* the mention is the only thing that can authorize it.
|
|
127
|
+
*/
|
|
128
|
+
export declare function concurrentThreads(allThreads: (ReviewThread | null)[], threadsForReview: ReviewThread[], input: GetReviewDataInput): ReviewThread[];
|
|
97
129
|
interface GetReviewDataInput {
|
|
98
130
|
octokit: Octokit;
|
|
99
131
|
owner: string;
|
|
@@ -101,6 +133,14 @@ interface GetReviewDataInput {
|
|
|
101
133
|
pullNumber: number;
|
|
102
134
|
reviewId: number;
|
|
103
135
|
approvedBy?: string | undefined;
|
|
136
|
+
/**
|
|
137
|
+
* Widen the read past the dispatching review to the other open threads on the
|
|
138
|
+
* PR — `all` on a Pullfrog-authored PR, `mentions` when only `@pullfrog`-
|
|
139
|
+
* mentioning threads are in scope. Absent (console `fix_review`, and any
|
|
140
|
+
* payload from a server build that predates the field) keeps the read scoped
|
|
141
|
+
* to the dispatching review exactly as before.
|
|
142
|
+
*/
|
|
143
|
+
addressScope?: "all" | "mentions" | undefined;
|
|
104
144
|
tmpdir: string;
|
|
105
145
|
githubToken: string;
|
|
106
146
|
}
|
|
@@ -148,9 +188,11 @@ export declare function getReviewData(input: GetReviewDataInput): Promise<{
|
|
|
148
188
|
export declare function GetReviewCommentsTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
|
|
149
189
|
pull_number: number;
|
|
150
190
|
review_id: number;
|
|
191
|
+
fresh?: boolean;
|
|
151
192
|
}, {
|
|
152
193
|
pull_number: number;
|
|
153
194
|
review_id: number;
|
|
195
|
+
fresh?: boolean;
|
|
154
196
|
}>> & {
|
|
155
197
|
mutates?: boolean;
|
|
156
198
|
};
|