pullfrog 0.1.2 → 0.1.4

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
@@ -107706,7 +107706,7 @@ function buildCommitPrompt(status) {
107706
107706
  ].join("\n");
107707
107707
  }
107708
107708
  function hasPostRunIssues(issues) {
107709
- return issues.stopHook !== void 0 || issues.dirtyTree !== void 0 || issues.summaryStale !== void 0;
107709
+ return issues.stopHook !== void 0 || issues.dirtyTree !== void 0 || issues.summaryStale !== void 0 || issues.unsubmittedReview !== void 0;
107710
107710
  }
107711
107711
  var agent = (input) => {
107712
107712
  return {
@@ -109474,13 +109474,38 @@ var ReplyToReviewComment = type({
109474
109474
  "extremely brief reply (1 sentence max) explaining what was fixed, e.g. 'Fixed by renaming to X' or 'Added null check'"
109475
109475
  )
109476
109476
  });
109477
+ function duplicateReplyDecision(params) {
109478
+ const existing = params.existing;
109479
+ if (!existing) return null;
109480
+ if (existing.bodyWithFooter !== params.bodyWithFooter) return null;
109481
+ return {
109482
+ kind: "already-replied",
109483
+ commentId: existing.commentId,
109484
+ url: existing.url,
109485
+ reason: `reply ${existing.commentId} with identical body was already posted in this session; ignoring duplicate call`
109486
+ };
109487
+ }
109477
109488
  function ReplyToReviewCommentTool(ctx) {
109478
109489
  return tool({
109479
109490
  name: "reply_to_review_comment",
109480
- description: "Reply to a PR review comment thread (NOT issue comments \u2014 this only works for inline review comments on PR diffs). Call this for EACH comment you address in AddressReviews mode. Keep replies extremely brief (1 sentence max).",
109491
+ description: "Reply to a PR review comment thread (NOT issue comments \u2014 this only works for inline review comments on PR diffs). Call exactly ONCE per parent comment you address in AddressReviews mode \u2014 duplicate calls with the same body are a no-op. Keep replies extremely brief (1 sentence max).",
109481
109492
  parameters: ReplyToReviewComment,
109482
109493
  execute: execute(async ({ pull_number, comment_id, body }) => {
109483
109494
  const bodyWithFooter = addFooter(ctx, body);
109495
+ const dup = duplicateReplyDecision({
109496
+ existing: ctx.toolState.reviewReplies?.get(comment_id),
109497
+ bodyWithFooter
109498
+ });
109499
+ if (dup) {
109500
+ log.info(`skipping duplicate review reply: ${dup.reason}`);
109501
+ return {
109502
+ success: true,
109503
+ skipped: true,
109504
+ reason: dup.reason,
109505
+ commentId: dup.commentId,
109506
+ url: dup.url
109507
+ };
109508
+ }
109484
109509
  const result = await ctx.octokit.rest.pulls.createReplyForReviewComment({
109485
109510
  owner: ctx.repo.owner,
109486
109511
  repo: ctx.repo.name,
@@ -109490,6 +109515,12 @@ function ReplyToReviewCommentTool(ctx) {
109490
109515
  });
109491
109516
  log.info(`\xBB created review comment ${result.data.id} (in reply to ${comment_id})`);
109492
109517
  ctx.toolState.wasUpdated = true;
109518
+ ctx.toolState.reviewReplies ??= /* @__PURE__ */ new Map();
109519
+ ctx.toolState.reviewReplies.set(comment_id, {
109520
+ commentId: result.data.id,
109521
+ url: result.data.html_url,
109522
+ bodyWithFooter
109523
+ });
109493
109524
  return {
109494
109525
  success: true,
109495
109526
  commentId: result.data.id,
@@ -110236,13 +110267,13 @@ var installNodeDependencies = {
110236
110267
  };
110237
110268
  }
110238
110269
  }
110239
- const resolved = resolveCommand(agent2, "frozen", []) || resolveCommand(agent2, "install", []);
110270
+ const resolved = resolveCommand(agent2, "frozen", []);
110240
110271
  if (!resolved) {
110241
110272
  return {
110242
110273
  language: "node",
110243
110274
  packageManager,
110244
110275
  dependenciesInstalled: false,
110245
- issues: [`no install command found for ${agent2}`]
110276
+ issues: [`no frozen-install command available for ${agent2}`]
110246
110277
  };
110247
110278
  }
110248
110279
  if (options.ignoreScripts) {
@@ -142558,7 +142589,7 @@ var import_semver = __toESM(require_semver2(), 1);
142558
142589
  // package.json
142559
142590
  var package_default = {
142560
142591
  name: "pullfrog",
142561
- version: "0.1.2",
142592
+ version: "0.1.4",
142562
142593
  type: "module",
142563
142594
  bin: {
142564
142595
  pullfrog: "dist/cli.mjs",
@@ -143835,7 +143866,7 @@ var CreatePullRequestReview = type({
143835
143866
  "1-2 sentence high-level summary with urgency level, critical callouts, and feedback about code outside the diff. Specific feedback on diff lines goes in 'comments' array."
143836
143867
  ).optional(),
143837
143868
  approved: type.boolean.describe(
143838
- "Set to true to submit as an approval. ONLY when the review contains no actionable feedback \u2014 neither inline comments nor actionable content in the body. Defaults to false (comment-only review). Rejections are not supported."
143869
+ "Set to true to submit as an approval. Use for both 'no issues found' and informational `> [!NOTE]` reviews where the PR is mergeable as-is and nothing in the body warrants code changes \u2014 approving also suppresses the Fix-button footer affordance so users don't dispatch a fix run on non-actionable feedback. Reserve approved: false for `> [!IMPORTANT]` (recommended changes) and `> [!CAUTION]` (critical) reviews. Defaults to false (comment-only review). Rejections are not supported."
143839
143870
  ).optional(),
143840
143871
  commit_id: type.string.describe("Optional SHA of the commit being reviewed. Defaults to latest.").optional(),
143841
143872
  comments: type({
@@ -144365,6 +144396,8 @@ async function ensureBeforeShaReachable(params) {
144365
144396
  }
144366
144397
  }
144367
144398
  var STALE_LOCK_AGE_MS = 3e4;
144399
+ var PULL_REF_RETRY_DELAYS_MS = [2e3, 5e3, 1e4];
144400
+ var PULL_REF_MISSING_PATTERN = /couldn't find remote ref pull\/\d+\/head/i;
144368
144401
  var GIT_LOCK_PATHS = [
144369
144402
  ".git/shallow.lock",
144370
144403
  ".git/index.lock",
@@ -144390,6 +144423,27 @@ function cleanupStaleGitLocks() {
144390
144423
  }
144391
144424
  }
144392
144425
  }
144426
+ async function isPullRequestStillDispatchable(args2) {
144427
+ try {
144428
+ const { data } = await args2.octokit.rest.pulls.get({
144429
+ owner: args2.owner,
144430
+ repo: args2.repo,
144431
+ pull_number: args2.pr.number
144432
+ });
144433
+ if (data.state !== "open") return false;
144434
+ if (data.head.sha !== args2.pr.headSha) return false;
144435
+ return true;
144436
+ } catch {
144437
+ return true;
144438
+ }
144439
+ }
144440
+ async function abortIfPullRequestMoved(args2) {
144441
+ const stillValid = await isPullRequestStillDispatchable(args2);
144442
+ if (stillValid) return;
144443
+ throw new Error(
144444
+ `PR #${args2.pr.number} is no longer in the state it was at dispatch (likely closed, merged, or force-pushed between webhook fire and run start). aborting checkout \u2014 re-trigger the run if this PR is still active.`
144445
+ );
144446
+ }
144393
144447
  async function checkoutPrBranch(pr, params) {
144394
144448
  const { octokit, owner, name, gitToken, toolState, beforeSha } = params;
144395
144449
  log.info(`\xBB checking out PR #${pr.number}...`);
@@ -144406,9 +144460,26 @@ async function checkoutPrBranch(pr, params) {
144406
144460
  if (!alreadyOnBranch) {
144407
144461
  $("git", ["checkout", "-B", pr.baseRef, `origin/${pr.baseRef}`], { log: false });
144408
144462
  log.debug(`\xBB fetching PR #${pr.number} (${localBranch})...`);
144409
- await $git("fetch", ["--no-tags", "origin", `+pull/${pr.number}/head:${localBranch}`], {
144410
- token: gitToken
144411
- });
144463
+ await retry(
144464
+ async () => {
144465
+ try {
144466
+ await $git("fetch", ["--no-tags", "origin", `+pull/${pr.number}/head:${localBranch}`], {
144467
+ token: gitToken
144468
+ });
144469
+ } catch (e) {
144470
+ const msg = e instanceof Error ? e.message : String(e);
144471
+ if (PULL_REF_MISSING_PATTERN.test(msg)) {
144472
+ await abortIfPullRequestMoved({ octokit, owner, repo: name, pr });
144473
+ }
144474
+ throw e;
144475
+ }
144476
+ },
144477
+ {
144478
+ delaysMs: PULL_REF_RETRY_DELAYS_MS,
144479
+ label: `pull/${pr.number}/head fetch`,
144480
+ shouldRetry: (e) => PULL_REF_MISSING_PATTERN.test(e instanceof Error ? e.message : String(e))
144481
+ }
144482
+ );
144412
144483
  $("git", ["checkout", localBranch], { log: false });
144413
144484
  log.debug(`\xBB checked out PR #${pr.number}`);
144414
144485
  toolState.checkoutSha = $("git", ["rev-parse", "HEAD"], { log: false }).trim();
@@ -145785,13 +145856,14 @@ function buildModeOverrides(t2) {
145785
145856
 
145786
145857
  An existing plan comment was found for this issue. Update that comment with the revised plan \u2014 do not create a new plan comment.
145787
145858
 
145788
- 1. Use \`previousPlanBody\` from this response as the plan to revise; do not call \`get_issue\` or \`get_issue_comments\`.
145789
- 2. Revise the plan based on the user's request:
145859
+ 1. **task list**: create your task list for this run as your first action.
145860
+ 2. Use \`previousPlanBody\` from this response as the plan to revise; do not call \`get_issue\` or \`get_issue_comments\`.
145861
+ 3. Revise the plan based on the user's request:
145790
145862
  - incorporate the current plan (\`previousPlanBody\`) and the user's revision request
145791
145863
  - gather relevant codebase context (file paths, architecture notes from AGENTS.md)
145792
145864
  - produce a structured plan with clear milestones
145793
- 3. Call \`${t2("report_progress")}\` with the full revised plan text and \`{ target_plan_comment: true }\` so it updates the existing plan comment (not the progress comment).
145794
- 4. Then post a short note to the progress comment (e.g. "Plan has been updated in the comment above.") via \`${t2("report_progress")}\` so it is not left as "Leaping...".`
145865
+ 4. Call \`${t2("report_progress")}\` with the full revised plan text and \`{ target_plan_comment: true }\` so it updates the existing plan comment (not the progress comment).
145866
+ 5. Then post a short note to the progress comment (e.g. "Plan has been updated in the comment above.") via \`${t2("report_progress")}\` so it is not left as "Leaping...".`
145795
145867
  };
145796
145868
  }
145797
145869
  var modeInstructionParent = {
@@ -146223,18 +146295,6 @@ function UploadFileTool(ctx) {
146223
146295
  }
146224
146296
 
146225
146297
  // mcp/server.ts
146226
- function initToolState(params) {
146227
- const resolved = parseProgressComment(params.progressComment);
146228
- if (resolved) {
146229
- log.info(`\xBB using pre-created progress comment: ${resolved.id} (${resolved.type})`);
146230
- }
146231
- return {
146232
- progressComment: resolved,
146233
- hadProgressComment: !!resolved,
146234
- backgroundProcesses: /* @__PURE__ */ new Map(),
146235
- usageEntries: []
146236
- };
146237
- }
146238
146298
  var mcpPortStart = 3764;
146239
146299
  var mcpPortAttempts = 100;
146240
146300
  var mcpHost = "127.0.0.1";
@@ -146475,18 +146535,20 @@ function computeModes(agentId) {
146475
146535
  description: "Implement, build, create, or develop code changes; make specific changes to files or features; execute a plan; or handle tasks with specific implementation details",
146476
146536
  prompt: `### Checklist
146477
146537
 
146478
- 1. **plan** (optional, for complex tasks): analyze requirements, read AGENTS.md and relevant code, produce a step-by-step implementation plan.
146538
+ 1. **task list**: create your task list for this run as your first action.
146539
+
146540
+ 2. **plan** (optional, for complex tasks): analyze requirements, read AGENTS.md and relevant code, produce a step-by-step implementation plan.
146479
146541
 
146480
- 2. **setup**: checkout or create the branch:
146542
+ 3. **setup**: checkout or create the branch:
146481
146543
  - **PR event, modifying the existing PR**: call \`${t2("checkout_pr")}\`
146482
146544
  - **new branch**: use \`${t2("git")}\` to create a branch (\`git checkout -b pullfrog/branch-name\`)
146483
146545
 
146484
- 3. **build**: implement changes using your native file and shell tools:
146546
+ 4. **build**: implement changes using your native file and shell tools:
146485
146547
  - follow the plan (if you ran a plan phase)
146486
146548
  - plan your approach before writing code: identify which files need to change, key design decisions, and edge cases. for non-trivial changes, consider whether there's a more elegant approach.
146487
146549
  - run relevant tests/lints before committing
146488
146550
 
146489
- 4. **self-review**: judgment call \u2014 does YOUR diff warrant a fresh-eyes pass?
146551
+ 5. **self-review**: judgment call \u2014 does YOUR diff warrant a fresh-eyes pass?
146490
146552
 
146491
146553
  Skip self-review (commit directly) when the diff is **genuinely trivial**:
146492
146554
  - doc typos, comment-only edits, whitespace/format-only, import reordering
@@ -146517,7 +146579,7 @@ function computeModes(agentId) {
146517
146579
 
146518
146580
  Review the findings, address valid points, and discard nitpicks or false positives. The reviewer is fallible \u2014 it biases toward *recommending additions* (defensive checks for impossible cases, extra logging, new abstractions used once, comments restating code, tests asserting tautologies, "just-in-case" guards). For each finding, ask: would applying it leave the code more sound, correct, AND elegant? Two-out-of-three is usually a signal to look harder for a fix that gets all three before settling for one that trades elegance for correctness. Reject bloat-shaped findings without applying them, and after applying the rest re-read your diff and be discerning about what *you just changed*: if any fix turned out to be bloat in context, revert it. The goal is code that is sound and correct *while remaining elegant*; the smallest diff that fixes the real defect almost always wins. Then verify only intended changes are present, no debug artifacts or commented-out code remain, no unrelated files were modified. Commit locally via shell (\`git add . && git commit -m "..."\`).
146519
146581
 
146520
- 5. **finalize**:
146582
+ 6. **finalize**:
146521
146583
  - confirm a clean working tree, then push via \`${t2("push_branch")}\` (see *SYSTEM* Git rules if this fails \u2014 prepush errors are usually the repo's tests/lint, not infra timeouts)
146522
146584
  - create a PR via \`${t2("create_pull_request")}\`
146523
146585
  - call \`${t2("report_progress")}\` with the PR link or the exact error if push/PR failed
@@ -146531,23 +146593,25 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
146531
146593
  description: "Address PR review feedback; respond to reviewer comments; make requested changes to an existing PR",
146532
146594
  prompt: `### Checklist
146533
146595
 
146534
- 1. Checkout the PR branch via \`${t2("checkout_pr")}\`.
146596
+ 1. **task list**: create your task list for this run as your first action.
146535
146597
 
146536
- 2. Fetch review comments via \`${t2("get_review_comments")}\`.
146598
+ 2. Checkout the PR branch via \`${t2("checkout_pr")}\`.
146537
146599
 
146538
- 3. For each comment:
146600
+ 3. Fetch review comments via \`${t2("get_review_comments")}\`.
146601
+
146602
+ 4. For each comment:
146539
146603
  - understand the feedback
146540
146604
  - evaluate whether applying it would leave the code more **sound, correct, AND elegant**. reviewers are fallible and bias toward *recommending additions* (defensive checks for impossible cases, extra abstractions, comments restating obvious code, tests asserting tautologies, "just-in-case" guards). if a request would add bloat \u2014 ceremony without commensurate correctness benefit \u2014 push back in your reply rather than mechanically applying it. two-out-of-three is usually a signal to look harder for a fix that gets all three before settling.
146541
146605
  - if the request stands, make the code change using your native tools; otherwise reply explaining why
146542
146606
  - record what was done (or why nothing was done)
146543
146607
 
146544
- 4. Quality check:
146608
+ 5. Quality check:
146545
146609
  - 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
146546
146610
  - commit locally via shell (\`git add . && git commit -m "..."\`)
146547
146611
 
146548
- 5. Finalize:
146612
+ 6. Finalize:
146549
146613
  - confirm a clean working tree, then push via \`${t2("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*)
146550
- - reply to each comment using \`${t2("reply_to_review_comment")}\`
146614
+ - reply to each comment **exactly once** using \`${t2("reply_to_review_comment")}\` \u2014 do not re-emit the same call (the runtime dedupes identical bodies and the second call is wasted)
146551
146615
  - resolve addressed threads via \`${t2("resolve_review_thread")}\`
146552
146616
  - call \`${t2("report_progress")}\` with a brief summary (or the exact push error if push failed)`
146553
146617
  },
@@ -146568,11 +146632,13 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
146568
146632
  description: "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
146569
146633
  prompt: `### Checklist
146570
146634
 
146571
- 1. **checkout**: call \`${t2("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`. read the diff TOC end-to-end and treat its file line ranges as your coverage checklist.
146635
+ 1. **task list**: create your task list for this run as your first action.
146636
+
146637
+ 2. **checkout**: call \`${t2("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`. read the diff TOC end-to-end and treat its file line ranges as your coverage checklist.
146572
146638
 
146573
- 2. **triage**: orient yourself on the PR \u2014 identify *what kind of thing this is* (domain it touches, seams it crosses, external contracts it depends on, user-facing surfaces it changes). orientation only \u2014 defer specific defect-hunting to the subagents; pre-reviewing biases the lenses you pick. use \`${t2("get_pull_request")}\` and other read-only GitHub tools for additional context if needed.
146639
+ 3. **triage**: orient yourself on the PR \u2014 identify *what kind of thing this is* (domain it touches, seams it crosses, external contracts it depends on, user-facing surfaces it changes). orientation only \u2014 defer specific defect-hunting to the subagents; pre-reviewing biases the lenses you pick. use \`${t2("get_pull_request")}\` and other read-only GitHub tools for additional context if needed.
146574
146640
 
146575
- if the PR is **genuinely trivial**, skip steps 3\u20134 entirely and submit a \`No new issues found.\` review per step 5. there's no value in dispatching even one lens for a typo.
146641
+ if the PR is **genuinely trivial**, skip steps 4\u20135 entirely and submit a \`No new issues found.\` review per step 6. there's no value in dispatching even one lens for a typo.
146576
146642
 
146577
146643
  "Genuinely trivial" (skip):
146578
146644
  - single-word doc typo, whitespace/format-only, comment-only across any number of files
@@ -146617,7 +146683,7 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
146617
146683
  - **holistic** \u2014 does the PR make sense as a whole? symmetric flows (delete for every create, rollback for every migration)?
146618
146684
  - **subsystem lenses** (invent as the PR demands) \u2014 auth, billing, payments, schema migration, webhooks, secrets, RBAC, multi-tenant isolation, cron/scheduling, etc.
146619
146685
 
146620
- 3. **fan out**: dispatch one \`${REVIEWER_AGENT_NAME}\` subagent per lens \u2014 its baked-in system prompt enforces the non-mutative + non-recursive contract (read-only file/search/web tools and read-only MCP queries; no writes, shell side effects, state-changing MCP calls, or nested subagent dispatch). when picking 2+ lenses, dispatch them in a **single assistant turn with multiple parallel subagent calls**; issuing one and awaiting reply before the next collapses the fan-out into a serial review. if a subagent errors out, times out, or returns nothing usable, retry once with the same lens; if it still fails, proceed with partial coverage and note the missing lens in the review body \u2014 do not skip step 3 entirely on a single subagent failure. each subagent gets:
146686
+ 4. **fan out**: dispatch one \`${REVIEWER_AGENT_NAME}\` subagent per lens \u2014 its baked-in system prompt enforces the non-mutative + non-recursive contract (read-only file/search/web tools and read-only MCP queries; no writes, shell side effects, state-changing MCP calls, or nested subagent dispatch). when picking 2+ lenses, dispatch them in a **single assistant turn with multiple parallel subagent calls**; issuing one and awaiting reply before the next collapses the fan-out into a serial review. if a subagent errors out, times out, or returns nothing usable, retry once with the same lens; if it still fails, proceed with partial coverage and note the missing lens in the review body \u2014 do not skip step 4 entirely on a single subagent failure. each subagent gets:
146621
146687
  - the diff path / target \u2014 reading the diff and the codebase is its job
146622
146688
  - **only one lens** \u2014 never a multi-section "review for X, Y, and Z" prompt
146623
146689
  - **a Task \`description\` set to the lens name** (e.g. \`"security"\`, \`"correctness"\`, \`"billing-subsystem"\`) \u2014 the harness reads this field to label the subagent's log lines so parallel runs can be told apart in CI output. without it, every subagent shows up as \`subagent#N\`.
@@ -146632,20 +146698,33 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
146632
146698
  - do NOT pre-shape their output with a finding schema
146633
146699
  - do NOT mention the other lenses (independence is the point \u2014 overlapping findings are a strong signal)
146634
146700
 
146635
- 4. **aggregate & draft**: merge findings; de-dup overlaps (two lenses catching the same issue = higher-confidence signal); trace each finding yourself before accepting it. drop praise, style preferences, speculative/unverified claims, findings about pre-existing code unrelated to the PR (heuristic: if the finding's root cause lives in lines this PR added or modified, it's in scope; otherwise drop unless the PR plausibly introduced or amplified the regression), and anything not actionable. also drop **bloat-shaped findings** \u2014 proposed fixes that would add defensive checks for cases that can't happen, abstractions used once, comments restating obvious code, tests asserting tautologies, or "just-in-case" guards. subagents are fallible and bias toward recommending changes; the bar for an actionable inline comment is sound + correct + elegant. recommending a change that improves only one of the three (or worse, degrades elegance to nominally improve correctness) makes the codebase worse, not better.
146701
+ 5. **aggregate & draft**: merge findings; de-dup overlaps (two lenses catching the same issue = higher-confidence signal); trace each finding yourself before accepting it. drop praise, style preferences, speculative/unverified claims, findings about pre-existing code unrelated to the PR (heuristic: if the finding's root cause lives in lines this PR added or modified, it's in scope; otherwise drop unless the PR plausibly introduced or amplified the regression), and anything not actionable. also drop **bloat-shaped findings** \u2014 proposed fixes that would add defensive checks for cases that can't happen, abstractions used once, comments restating obvious code, tests asserting tautologies, or "just-in-case" guards. subagents are fallible and bias toward recommending changes; the bar for an actionable inline comment is sound + correct + elegant. recommending a change that improves only one of the three (or worse, degrades elegance to nominally improve correctness) makes the codebase worse, not better.
146636
146702
 
146637
146703
  for surviving findings, draft inline comments with NEW line numbers from the diff. every comment must be actionable, 2-3 sentences max. use GitHub permalink format for code references. for impact-analysis findings (stale references after rename/remove), report them in the review body ordered by severity (runtime breakage > incorrect docs > stale comments) rather than as inline comments unless they're anchored to a specific line.
146638
146704
 
146639
- 5. **submit**: ALWAYS submit exactly one review via \`${t2("create_pull_request_review")}\`. Do NOT call \`report_progress\` \u2014 the review is the final record and the progress comment will be cleaned up automatically.
146705
+ 6. **submit**: ALWAYS submit exactly one review via \`${t2("create_pull_request_review")}\`. Do NOT call \`report_progress\` \u2014 the review is the final record and the progress comment will be cleaned up automatically.
146640
146706
 
146641
146707
  note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session.
146642
146708
 
146643
146709
  The review body is structured as: \`[optional alert blockquote]\` \u2192 \`[PR summary using the default format below]\`. Inline comments are passed via the \`comments\` parameter, not in the body.
146644
146710
 
146645
- - **critical issues** (blocks merge \u2014 bugs, security, data loss):
146711
+ GitHub alert blockquotes render at four visual intensities \u2014 the callout is what the author sees first, so pick the one that matches what you want them to do:
146712
+
146713
+ - \`[!CAUTION]\` \u2014 large red banner. Reads as "this will break something."
146714
+ - \`[!IMPORTANT]\` \u2014 large purple banner. Reads as "you need to look at this before merging."
146715
+ - \`[!NOTE]\` \u2014 small blue inline callout. Reads as "FYI, here's something worth noting."
146716
+ - no callout \u2014 plain text. Reads as routine review output.
146717
+
146718
+ Two reinforcing levers: callout intensity (above) and \`approved\` (which gates the footer Fix-button affordance \u2014 Fix renders on every non-approving review, so \`approved: true\` suppresses it). Wrapping mergeable feedback in \`[!IMPORTANT]\` trains users to click Fix on reviews that don't need fixing. Pick the tier the author's actual next action justifies.
146719
+
146720
+ - **critical issues** (blocks merge \u2014 bugs, security, data loss, broken core flows):
146646
146721
  \`approved: false\`. Body opens with \`> [!CAUTION]\\n> This PR introduces ...\`, followed by the PR summary. Include all inline comments via \`comments\`.
146647
- - **recommended changes** (non-critical):
146648
- \`approved: false\`. Body opens with \`> [!IMPORTANT]\\n> Consider ...\`, followed by the PR summary. Include all inline comments via \`comments\`.
146722
+ - **must-address non-critical findings** (real consequences if shipped \u2014 incorrect behavior in non-critical paths, missing validation on user input, regressions the author should fix before merge):
146723
+ \`approved: false\`. Body opens with \`> [!IMPORTANT]\\n> ...\`, followed by the PR summary. Reserve this tier for findings with concrete fallout \u2014 do NOT use \`[!IMPORTANT]\` for nits, style preferences, or "consider also" suggestions. Include all inline comments via \`comments\`.
146724
+ - **minor suggestions only** (single-line nits, doc/comment polish, defer-able observations, "rough edges"):
146725
+ \`approved: false\`. NO alert blockquote. Body opens directly with the PR summary. Include all inline comments via \`comments\`.
146726
+ - **informational observations** (mergeable as-is, nothing actionable \u2014 e.g. prior feedback addressed cleanly, surfacing a minor stale doc reference, calling out something noteworthy without recommending a change):
146727
+ \`approved: true\`. Body opens with \`> [!NOTE]\\n> ...\`, followed by the PR summary. Do NOT include inline \`comments\` \u2014 \`[!NOTE]\` signals "no action needed", which contradicts an actionable anchor; if a point is concrete enough to anchor to a line, downgrade the whole review to "minor suggestions only" (\`approved: false\`) instead.
146649
146728
  - **no actionable issues**:
146650
146729
  \`approved: true\`. Body opens with \`No new issues found.\` followed by the PR summary.
146651
146730
 
@@ -146654,7 +146733,7 @@ ${PR_SUMMARY_FORMAT}`
146654
146733
  // IncrementalReview shares Review's multi-lens orchestrator pattern but
146655
146734
  // scopes the target to the incremental diff. The "issues must be NEW
146656
146735
  // since the last Pullfrog review" filter lives at aggregation time
146657
- // (step 5), NOT in the subagent prompt — pushing the filter into
146736
+ // (step 6), NOT in the subagent prompt — pushing the filter into
146658
146737
  // subagents matches the canonical anneal anti-pattern of "list known
146659
146738
  // pre-existing failures — don't flag these" and suppresses signal on
146660
146739
  // regressions the new commits amplified. The review body is just
@@ -146667,15 +146746,17 @@ ${PR_SUMMARY_FORMAT}`
146667
146746
  description: "Re-review a PR after new commits are pushed; focus on new changes since the last review",
146668
146747
  prompt: `### Checklist
146669
146748
 
146670
- 1. **checkout**: call \`${t2("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available). read the diff TOC first and use its line ranges as your coverage checklist.
146749
+ 1. **task list**: create your task list for this run as your first action.
146750
+
146751
+ 2. **checkout**: call \`${t2("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available). read the diff TOC first and use its line ranges as your coverage checklist.
146671
146752
 
146672
- 2. **incremental scope**: if \`incrementalDiffPath\` is present, read it to see what changed since the last review. this is a range-diff that isolates the net changes, filtering out base branch noise. if not present, fall back to reviewing the full PR diff and determine what changed since Pullfrog's most recent review.
146753
+ 3. **incremental scope**: if \`incrementalDiffPath\` is present, read it to see what changed since the last review. this is a range-diff that isolates the net changes, filtering out base branch noise. if not present, fall back to reviewing the full PR diff and determine what changed since Pullfrog's most recent review.
146673
146754
 
146674
- 3. **prior feedback**: fetch previous reviews via \`${t2("list_pull_request_reviews")}\`. for the most recent Pullfrog review, call \`${t2("get_review_comments")}\` with the review ID to retrieve specific prior line-level feedback. you'll use this to filter your aggregation in step 5 \u2014 anything already flagged in a prior review and not changed by the new commits should not be re-raised. you do NOT need to render this in the review body; the rolling PR summary snapshot is the durable record of what's been addressed.
146755
+ 4. **prior feedback**: fetch previous reviews via \`${t2("list_pull_request_reviews")}\`. for the most recent Pullfrog review, call \`${t2("get_review_comments")}\` with the review ID to retrieve specific prior line-level feedback. you'll use this to filter your aggregation in step 6 \u2014 anything already flagged in a prior review and not changed by the new commits should not be re-raised. you do NOT need to render this in the review body; the rolling PR summary snapshot is the durable record of what's been addressed.
146675
146756
 
146676
- 4. **triage & fan out**: orient on the *incremental* changes \u2014 domain, seams, external contracts, user-facing surfaces.
146757
+ 5. **triage & fan out**: orient on the *incremental* changes \u2014 domain, seams, external contracts, user-facing surfaces.
146677
146758
 
146678
- if the incremental changes are **genuinely trivial**, skip the fan-out entirely and jump to step 7's non-substantive path (do NOT submit a review).
146759
+ if the incremental changes are **genuinely trivial**, skip the fan-out entirely and jump to step 8's non-substantive path (do NOT submit a review).
146679
146760
 
146680
146761
  "Genuinely trivial" (skip): formatting/comment tweaks, import reordering, lockfile regen, mechanical rename of import paths, whitespace-only.
146681
146762
  "Looks trivial but isn't" (do NOT skip \u2014 same anti-patterns as Review mode): 1-line changes to SQL/regex/auth/billing/permissions/signature-verification code; flipping feature-flag defaults or retry/timeout constants; money/tax/HTTP-method/redirect changes; tightening or loosening a comparison operator; mixed diffs with a semantic line buried in formatting.
@@ -146683,8 +146764,8 @@ ${PR_SUMMARY_FORMAT}`
146683
146764
 
146684
146765
  otherwise pick lenses by where the new commits concentrate risk \u2014 **there's no fixed count**, same calibration as Review mode (1 lens for pure refactor / isolated fix; 2\u20133 for typical features; 4\u20135 for high-stakes subsystem touches; 6+ is a smell). lens framing follows Review mode: themed lenses (correctness & invariants, impact when new commits remove/rename/deprecate things, research-validated assumptions, security, user-journey, operational readiness, integration & cross-cutting, test integrity, performance, holistic) and subsystem lenses (auth, billing, schema migration, etc.) \u2014 for high-stakes domains lead with the subsystem lens rather than the generic themed equivalent.
146685
146766
 
146686
- dispatch one \`${REVIEWER_AGENT_NAME}\` subagent per lens \u2014 its baked-in system prompt enforces the non-mutative + non-recursive contract (read-only file/search/web tools and read-only MCP queries; no writes, shell side effects, state-changing MCP calls, or nested subagent dispatch). dispatch them in a **single assistant turn with multiple parallel subagent calls** (serial dispatch collapses the fan-out). if a subagent errors out, times out, or returns nothing usable, retry once with the same lens; if it still fails, proceed with partial coverage and note the missing lens in the review body \u2014 do not skip step 4 entirely on a single subagent failure. each subagent gets:
146687
- - the diff scope (incremental diff path if available, full diff otherwise). do NOT tell them to skip pre-existing issues \u2014 that suppresses regressions the new commits amplified; the "issues must be NEW" filter lives at aggregation time (step 5), not in the subagent prompt
146767
+ dispatch one \`${REVIEWER_AGENT_NAME}\` subagent per lens \u2014 its baked-in system prompt enforces the non-mutative + non-recursive contract (read-only file/search/web tools and read-only MCP queries; no writes, shell side effects, state-changing MCP calls, or nested subagent dispatch). dispatch them in a **single assistant turn with multiple parallel subagent calls** (serial dispatch collapses the fan-out). if a subagent errors out, times out, or returns nothing usable, retry once with the same lens; if it still fails, proceed with partial coverage and note the missing lens in the review body \u2014 do not skip step 5 entirely on a single subagent failure. each subagent gets:
146768
+ - the diff scope (incremental diff path if available, full diff otherwise). do NOT tell them to skip pre-existing issues \u2014 that suppresses regressions the new commits amplified; the "issues must be NEW" filter lives at aggregation time (step 6), not in the subagent prompt
146688
146769
  - **only one lens** \u2014 never a multi-section "review for X, Y, and Z" prompt
146689
146770
  - **a Task \`description\` set to the lens name** (e.g. \`"security"\`, \`"correctness"\`, \`"billing-subsystem"\`) \u2014 the harness reads this field to label the subagent's log lines so parallel runs can be told apart in CI output. without it, every subagent shows up as \`subagent#N\`.
146690
146771
  - the read-only contract restated in your dispatch instructions so the rule is present twice (the subagent's system prompt also enforces it). The test: would this call still be a no-op if reverted? If not (PR comments, branch pushes, issue updates, set_output, label changes, dependency installs, etc.), don't make it.
@@ -146698,15 +146779,21 @@ ${PR_SUMMARY_FORMAT}`
146698
146779
  - do NOT pre-shape their output with a finding schema
146699
146780
  - do NOT mention the other lenses (independence is the point)
146700
146781
 
146701
- 5. **aggregate, draft, self-critique**: merge findings; de-dup overlaps; trace each finding yourself. drop praise, style preferences, speculative/unverified claims, findings about pre-existing code unrelated to the new commits, anything not actionable, and anything that re-states prior review feedback (heuristic: if the finding's root cause lives in lines the *new commits* added or modified, it's in scope; otherwise drop). also drop **bloat-shaped findings** \u2014 proposed fixes that would add defensive checks for cases that can't happen, abstractions used once, comments restating obvious code, tests asserting tautologies, or "just-in-case" guards. subagents are fallible and bias toward recommending changes; the bar for an actionable inline comment is sound + correct + elegant. recommending a change that improves only one of the three (or degrades elegance to nominally improve correctness) makes the codebase worse, not better. To compute "lines the new commits added or modified": if \`incrementalDiffPath\` from step 1 is present, use it directly. Otherwise, take the prior Pullfrog review's \`commit_id\` (returned alongside each entry from \`${t2("list_pull_request_reviews")}\` in step 3) and run \`git diff <prior-review-sha>..HEAD\` to isolate the lines added since that review. draft inline comments with NEW line numbers from the full PR diff \u2014 every comment must be actionable, 2-3 sentences max.
146782
+ 6. **aggregate, draft, self-critique**: merge findings; de-dup overlaps; trace each finding yourself. drop praise, style preferences, speculative/unverified claims, findings about pre-existing code unrelated to the new commits, anything not actionable, and anything that re-states prior review feedback (heuristic: if the finding's root cause lives in lines the *new commits* added or modified, it's in scope; otherwise drop). also drop **bloat-shaped findings** \u2014 proposed fixes that would add defensive checks for cases that can't happen, abstractions used once, comments restating obvious code, tests asserting tautologies, or "just-in-case" guards. subagents are fallible and bias toward recommending changes; the bar for an actionable inline comment is sound + correct + elegant. recommending a change that improves only one of the three (or degrades elegance to nominally improve correctness) makes the codebase worse, not better. To compute "lines the new commits added or modified": if \`incrementalDiffPath\` from step 2 is present, use it directly. Otherwise, take the prior Pullfrog review's \`commit_id\` (returned alongside each entry from \`${t2("list_pull_request_reviews")}\` in step 4) and run \`git diff <prior-review-sha>..HEAD\` to isolate the lines added since that review. draft inline comments with NEW line numbers from the full PR diff \u2014 every comment must be actionable, 2-3 sentences max.
146702
146783
 
146703
- 6. **build the review body** \u2014 a single "Reviewed changes" section: summarize at the logical-change level, not per-file. each bullet starts with a past-tense verb (e.g. \`- Extracted shared CLI runtime into a single module\`, \`- Renamed package to pullfrog\`). avoid file paths unless they add clarity. if the changes can be described in one sentence, use one sentence \u2014 no bullets needed. do NOT include a separate "Prior review feedback" checklist; that's tracked in the rolling PR summary snapshot for the next agent run, and surfacing it in the user-facing body is noise (changes that addressed prior feedback are already covered by the Reviewed-changes bullets). in some cases you may receive a complete diff for the whole pull request instead of an incremental one \u2014 when this happens, you will need to determine what changes have happened since Pullfrog's most recent review.
146784
+ 7. **build the review body** \u2014 a single "Reviewed changes" section: summarize at the logical-change level, not per-file. each bullet starts with a past-tense verb (e.g. \`- Extracted shared CLI runtime into a single module\`, \`- Renamed package to pullfrog\`). avoid file paths unless they add clarity. if the changes can be described in one sentence, use one sentence \u2014 no bullets needed. do NOT include a separate "Prior review feedback" checklist; that's tracked in the rolling PR summary snapshot for the next agent run, and surfacing it in the user-facing body is noise (changes that addressed prior feedback are already covered by the Reviewed-changes bullets). in some cases you may receive a complete diff for the whole pull request instead of an incremental one \u2014 when this happens, you will need to determine what changes have happened since Pullfrog's most recent review.
146704
146785
 
146705
- 7. Submit \u2014 Do NOT call \`report_progress\` or \`create_issue_comment\` \u2014 the review is the final record and the progress comment will be cleaned up automatically. Follow these rules:
146786
+ 8. Submit \u2014 every run must end with EXACTLY ONE of \`${t2("create_pull_request_review")}\` (substantive review) or \`${t2("report_progress")}\` (no-review acknowledgement). do NOT call \`create_issue_comment\` for review output.
146787
+
146788
+ Same callout-intensity ladder as Review mode \u2014 \`[!CAUTION]\` (large red, "will break") \u2192 \`[!IMPORTANT]\` (large purple, "must address before merging") \u2192 \`[!NOTE]\` (small blue, "FYI") \u2192 no callout (plain text). And the same Fix-button lever: the footer renders a Fix button on every non-approving review, so \`approved: true\` suppresses it. Wrapping mergeable feedback in \`[!IMPORTANT]\` trains users to click Fix on reviews that don't need fixing \u2014 pick the tier the author's actual next action justifies.
146789
+
146790
+ Follow these rules:
146706
146791
  - note: the first create_pull_request_review submission may error with a one-time diff-coverage nudge listing unread TOC regions. retry the same call to proceed \u2014 optionally after reading the listed ranges. the pre-flight will not block again this session.
146707
- - IF NO NEW ISSUES, NON-SUBSTANTIVE CHANGES ONLY (trivial formatting, import reordering, comment tweaks): do NOT submit a review. Do NOT call \`report_progress\`. Exit \u2014 the progress comment will be cleaned up automatically.
146708
- - ELSE IF NEW CRITICAL ISSUES (blocks merge): call \`${t2("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with a GitHub alert blockquote (e.g. \`> [!CAUTION]\\n> This PR introduces ...\`), then the Reviewed-changes summary.
146709
- - ELSE IF NEW RECOMMENDED CHANGES (non-critical): call \`${t2("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!IMPORTANT]\\n> ...\` alert, then the Reviewed-changes summary.
146792
+ - IF NO NEW ISSUES, NON-SUBSTANTIVE CHANGES ONLY (trivial formatting, import reordering, comment tweaks): do NOT submit a review. Instead call \`${t2("report_progress")}\` with a 1-2 sentence note explaining no review was warranted (e.g. "No new issues. Changes since last review are formatting-only."). this leaves a visible signal that the run completed.
146793
+ - ELSE IF NEW CRITICAL ISSUES (blocks merge \u2014 bugs, security, data loss, broken core flows): call \`${t2("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!CAUTION]\\n> This PR introduces ...\`, then the Reviewed-changes summary.
146794
+ - ELSE IF NEW MUST-ADDRESS NON-CRITICAL FINDINGS (real consequences if shipped \u2014 incorrect behavior, missing validation, regressions the author should fix before merge): call \`${t2("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!IMPORTANT]\\n> ...\`, then the Reviewed-changes summary. Do NOT use this tier for nits, style preferences, or "consider also" suggestions.
146795
+ - ELSE IF NEW MINOR SUGGESTIONS ONLY (single-line nits, doc/comment polish, defer-able observations, "rough edges"): call \`${t2("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens directly with \`Reviewed the following changes:\\n\` (NO alert blockquote), then the Reviewed-changes summary.
146796
+ - ELSE IF INFORMATIONAL OBSERVATIONS (mergeable as-is, but worth surfacing \u2014 e.g. prior feedback addressed cleanly with one minor stale doc reference, or a noteworthy positive observation): call \`${t2("create_pull_request_review")}\` with \`approved: true\`, NO inline comments, and the review body. body opens with \`> [!NOTE]\\n> ...\` alert, then the Reviewed-changes summary. If a point is concrete enough to anchor to a line, downgrade the whole review to "minor suggestions only" (\`approved: false\`) instead \u2014 \`[!NOTE]\` and inline comments don't mix.
146710
146797
  - ELSE IF NO NEW ISSUES, SUBSTANTIVE CHANGES (new functionality, behavior changes, or fixes to prior review feedback): call \`${t2("create_pull_request_review")}\` to create a PR review. If all previous reviews have been properly addressed and no new issues were discovered, you can set \`approved: true\`. body opens with \`No new issues. Reviewed the following changes:\\n\`, then the Reviewed-changes summary.`
146711
146798
  },
146712
146799
  {
@@ -146714,33 +146801,37 @@ ${PR_SUMMARY_FORMAT}`
146714
146801
  description: "Create plans, break down tasks, outline steps, analyze requirements, understand scope of work, or provide task breakdowns",
146715
146802
  prompt: `### Checklist
146716
146803
 
146717
- 1. Analyze the task and gather context:
146804
+ 1. **task list**: create your task list for this run as your first action.
146805
+
146806
+ 2. Analyze the task and gather context:
146718
146807
  - read AGENTS.md and relevant codebase files
146719
146808
  - understand the architecture and constraints
146720
146809
 
146721
- 2. Produce a structured, actionable plan with clear milestones.
146810
+ 3. Produce a structured, actionable plan with clear milestones.
146722
146811
 
146723
- 3. Call \`${t2("report_progress")}\` with the plan.`
146812
+ 4. Call \`${t2("report_progress")}\` with the plan.`
146724
146813
  },
146725
146814
  {
146726
146815
  name: "Fix",
146727
146816
  description: "Fix CI failures; debug failing tests or builds; investigate and resolve check suite failures",
146728
146817
  prompt: `### Checklist
146729
146818
 
146730
- 1. Checkout the PR branch via \`${t2("checkout_pr")}\`.
146819
+ 1. **task list**: create your task list for this run as your first action.
146731
146820
 
146732
- 2. Fetch check suite logs via \`${t2("get_check_suite_logs")}\`.
146821
+ 2. Checkout the PR branch via \`${t2("checkout_pr")}\`.
146733
146822
 
146734
- 3. **CRITICAL**: verify the failure was INTRODUCED BY THIS PR before fixing. If unrelated, abort and report.
146823
+ 3. Fetch check suite logs via \`${t2("get_check_suite_logs")}\`.
146735
146824
 
146736
- 4. Diagnose and fix:
146825
+ 4. **CRITICAL**: verify the failure was INTRODUCED BY THIS PR before fixing. If unrelated, abort and report.
146826
+
146827
+ 5. Diagnose and fix:
146737
146828
  - read the workflow file, reproduce locally with the EXACT same commands CI runs
146738
146829
  - fix the issue using your native file and shell tools
146739
146830
  - verify the fix by re-running the exact CI command
146740
146831
  - review the diff before committing \u2014 verify only the fix is present, no debug artifacts, no unrelated changes. the fix should be clean enough that a senior engineer would approve without hesitation.
146741
146832
  - commit locally via shell (\`git add . && git commit -m "..."\`)
146742
146833
 
146743
- 5. Finalize:
146834
+ 6. Finalize:
146744
146835
  - confirm a clean working tree, then push via \`${t2("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*)
146745
146836
  - call \`${t2("report_progress")}\` with the diagnosis and fix summary (or the exact push error if push failed)`
146746
146837
  },
@@ -146749,22 +146840,24 @@ ${PR_SUMMARY_FORMAT}`
146749
146840
  description: "Resolve merge conflicts in a PR branch against the base branch",
146750
146841
  prompt: `### Checklist
146751
146842
 
146752
- 1. **Setup**:
146843
+ 1. **task list**: create your task list for this run as your first action.
146844
+
146845
+ 2. **Setup**:
146753
146846
  - Call \`${t2("checkout_pr")}\` to get the PR branch.
146754
146847
  - Call \`${t2("get_pull_request")}\` to identify the base branch (e.g., 'main').
146755
146848
  - Call \`${t2("git_fetch")}\` to fetch the base branch.
146756
146849
 
146757
- 2. **Merge Attempt**:
146850
+ 3. **Merge Attempt**:
146758
146851
  - Run \`git merge origin/<base_branch>\` via shell.
146759
- - If it succeeds automatically, confirm a clean working tree, push via \`${t2("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*), and call \`${t2("report_progress")}\` with a brief success note or the exact push error if push failed \u2014 **then stop; do not run steps 3\u20134.**
146760
- - If it fails (conflicts), resolve them manually (continue to steps 3\u20134).
146852
+ - If it succeeds automatically, confirm a clean working tree, push via \`${t2("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*), and call \`${t2("report_progress")}\` with a brief success note or the exact push error if push failed \u2014 **then stop; do not run steps 4\u20135.**
146853
+ - If it fails (conflicts), resolve them manually (continue to steps 4\u20135).
146761
146854
 
146762
- 3. **Resolve Conflicts**:
146855
+ 4. **Resolve Conflicts**:
146763
146856
  - Run \`git status\` or parse the merge output to find the list of conflicting files.
146764
146857
  - For each conflicting file: read it, find the conflict markers (\`<<<<<<<\`, \`=======\`, \`>>>>>>>\`), understand the code context, and rewrite the file with the correct resolution. Remove all markers.
146765
146858
  - Verify the file syntax is correct after resolution.
146766
146859
 
146767
- 4. **Finalize**:
146860
+ 5. **Finalize**:
146768
146861
  - Run a final verification (build/test) to ensure the resolution works.
146769
146862
  - \`git add . && git commit -m "resolve merge conflicts"\`
146770
146863
  - confirm a clean working tree, then push via \`${t2("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*)
@@ -146775,15 +146868,17 @@ ${PR_SUMMARY_FORMAT}`
146775
146868
  description: "General-purpose tasks that don't fit other modes: answering questions, adding comments, labeling, running ad-hoc commands, or any direct request",
146776
146869
  prompt: `### Checklist
146777
146870
 
146778
- 1. Analyze the task. For simple operations (labeling, commenting, answering questions, running a single command), handle directly.
146871
+ 1. **task list**: create your task list for this run as your first action.
146779
146872
 
146780
- 2. For substantial work \u2014 code changes across multiple files, multi-step investigations:
146873
+ 2. Analyze the task. For simple operations (labeling, commenting, answering questions, running a single command), handle directly.
146874
+
146875
+ 3. For substantial work \u2014 code changes across multiple files, multi-step investigations:
146781
146876
  - plan your approach before starting
146782
146877
  - use native file and shell tools for local operations
146783
146878
  - use ${pullfrogMcpName} MCP tools for GitHub/git operations
146784
146879
  - if code changes are needed: review your own diff before committing \u2014 verify only intended changes are present, no debug artifacts remain, and the changes are clean enough that a senior engineer would approve without hesitation
146785
146880
 
146786
- 3. Finalize:
146881
+ 4. Finalize:
146787
146882
  - if code changes were made, push to a pull request (new or existing) using \`${t2("push_branch")}\` and \`${t2("create_pull_request")}\` as needed. \`git status\` must be clean before you finish (see *SYSTEM* Git rules if push fails).
146788
146883
  - call \`${t2("report_progress")}\` once with results \u2014 include exact tool errors if push or PR creation failed
146789
146884
  - if the task involved labeling, commenting, or other GitHub operations, perform those directly`
@@ -146791,6 +146886,25 @@ ${PR_SUMMARY_FORMAT}`
146791
146886
  ];
146792
146887
  }
146793
146888
  var modes = computeModes("opencode");
146889
+ var NON_COMMITTING_MODES = /* @__PURE__ */ new Set([
146890
+ "Review",
146891
+ "IncrementalReview",
146892
+ "Plan"
146893
+ ]);
146894
+
146895
+ // toolState.ts
146896
+ function initToolState(params) {
146897
+ const resolved = parseProgressComment(params.progressComment);
146898
+ if (resolved) {
146899
+ log.info(`\xBB using pre-created progress comment: ${resolved.id} (${resolved.type})`);
146900
+ }
146901
+ return {
146902
+ progressComment: resolved,
146903
+ hadProgressComment: !!resolved,
146904
+ backgroundProcesses: /* @__PURE__ */ new Map(),
146905
+ usageEntries: []
146906
+ };
146907
+ }
146794
146908
 
146795
146909
  // agents/claude.ts
146796
146910
  import { execFileSync as execFileSync3 } from "node:child_process";
@@ -147037,6 +147151,13 @@ var ThinkingTimer = class {
147037
147151
 
147038
147152
  // agents/postRun.ts
147039
147153
  import { readFile } from "node:fs/promises";
147154
+ function getUnsubmittedReview(toolState) {
147155
+ const mode = toolState.selectedMode;
147156
+ if (mode !== "Review" && mode !== "IncrementalReview") return null;
147157
+ if (toolState.review || toolState.finalSummaryWritten) return null;
147158
+ if (!toolState.hadProgressComment) return null;
147159
+ return mode;
147160
+ }
147040
147161
  var MAX_HOOK_OUTPUT_CHARS = 4096;
147041
147162
  function truncateHookOutput(raw2) {
147042
147163
  if (raw2.length <= MAX_HOOK_OUTPUT_CHARS) return raw2;
@@ -147098,23 +147219,57 @@ function buildSummaryStalePrompt(filePath) {
147098
147219
  "if the diff is genuinely too small or noisy to warrant rewriting (e.g. a one-line typo fix, a comment tweak, a formatting-only change), it's fine to leave the structure as-is \u2014 but at minimum confirm you considered it by appending one line to the appropriate section noting the run. silence is not an option; the snapshot is what the next review run reads as context."
147099
147220
  ].join("\n");
147100
147221
  }
147101
- async function collectPostRunIssues(params) {
147222
+ function buildUnsubmittedReviewPrompt(mode) {
147223
+ if (mode === "Review") {
147224
+ return [
147225
+ `MISSING REVIEW OUTPUT \u2014 you selected Review mode but stopped without calling \`create_pull_request_review\`. the user has no visible signal that this run produced anything; the progress comment will be deleted on exit and no review will appear on the PR.`,
147226
+ "",
147227
+ "call `create_pull_request_review` now with your aggregated review (body + inline comments). pick the tier per the mode prompt \u2014 Review mode has no no-submit exit, so even informational `> [!NOTE]` reviews and `No new issues found.` reviews must be submitted (both use `approved: true`). the first call may error once with a diff-coverage nudge \u2014 retry the same call to proceed.",
147228
+ "",
147229
+ "do NOT stop again until `create_pull_request_review` has been called successfully."
147230
+ ].join("\n");
147231
+ }
147232
+ return [
147233
+ `MISSING REVIEW OUTPUT \u2014 you selected IncrementalReview mode but stopped without calling \`create_pull_request_review\` or \`report_progress\`. the user has no visible signal that this run produced anything; the progress comment will be deleted on exit and no review will appear on the PR.`,
147234
+ "",
147235
+ "do exactly one of:",
147236
+ "- if you have findings: call `create_pull_request_review` now with your aggregated review (body + inline comments). the first call may error once with a diff-coverage nudge \u2014 retry the same call to proceed.",
147237
+ "- if there are genuinely no actionable findings since the last review (e.g. only formatting / comment / lockfile changes): call `report_progress` with a 1-2 sentence summary explaining that no review was warranted.",
147238
+ "",
147239
+ "do NOT stop again until one of those tools has been called successfully."
147240
+ ].join("\n");
147241
+ }
147242
+ async function collectPostRunIssues(ctx, options = {}) {
147102
147243
  const issues = {};
147103
- if (params.stopScript) {
147104
- const failure = await executeStopHook(params.stopScript);
147244
+ if (ctx.stopScript) {
147245
+ const failure = await executeStopHook(ctx.stopScript);
147105
147246
  if (failure) issues.stopHook = failure;
147106
147247
  }
147107
147248
  const status = getGitStatus();
147108
- if (status) issues.dirtyTree = status;
147109
- if (params.summaryFilePath && params.summarySeed !== void 0) {
147110
- const stale = await isSummaryUnchanged(params.summaryFilePath, params.summarySeed);
147111
- if (stale) issues.summaryStale = { filePath: params.summaryFilePath };
147249
+ const mode = ctx.toolState.selectedMode;
147250
+ if (status) {
147251
+ if (mode && NON_COMMITTING_MODES.has(mode)) {
147252
+ log.info(`\xBB dirty-tree gate suppressed: mode \`${mode}\` does not commit`);
147253
+ } else {
147254
+ issues.dirtyTree = status;
147255
+ }
147112
147256
  }
147257
+ const summaryFilePath2 = ctx.toolState.summaryFilePath;
147258
+ const summarySeed = ctx.toolState.summarySeed;
147259
+ if (!options.skipSummaryStale && summaryFilePath2 && summarySeed !== void 0) {
147260
+ const stale = await isSummaryUnchanged(summaryFilePath2, summarySeed);
147261
+ if (stale) issues.summaryStale = { filePath: summaryFilePath2 };
147262
+ }
147263
+ const unsubmittedMode = getUnsubmittedReview(ctx.toolState);
147264
+ if (unsubmittedMode) issues.unsubmittedReview = unsubmittedMode;
147113
147265
  return issues;
147114
147266
  }
147115
147267
  function buildPostRunPrompt(issues) {
147116
147268
  const parts = [];
147117
147269
  if (issues.stopHook) parts.push(buildStopHookPrompt(issues.stopHook));
147270
+ if (issues.unsubmittedReview) {
147271
+ parts.push(buildUnsubmittedReviewPrompt(issues.unsubmittedReview));
147272
+ }
147118
147273
  if (issues.dirtyTree) parts.push(buildCommitPrompt(issues.dirtyTree));
147119
147274
  if (issues.summaryStale) parts.push(buildSummaryStalePrompt(issues.summaryStale.filePath));
147120
147275
  return parts.join("\n\n---\n\n");
@@ -147141,10 +147296,8 @@ async function runPostRunRetryLoop(params) {
147141
147296
  let summaryStaleNudged = false;
147142
147297
  while (gateResumeCount < MAX_POST_RUN_RETRIES) {
147143
147298
  if (!result.success) break;
147144
- const issues = await collectPostRunIssues({
147145
- stopScript: params.stopScript,
147146
- summaryFilePath: summaryStaleNudged ? void 0 : params.summaryFilePath,
147147
- summarySeed: summaryStaleNudged ? void 0 : params.summarySeed
147299
+ const issues = await collectPostRunIssues(params.ctx, {
147300
+ skipSummaryStale: summaryStaleNudged
147148
147301
  });
147149
147302
  if (issues.summaryStale) summaryStaleNudged = true;
147150
147303
  finalIssues = issues;
@@ -147192,7 +147345,7 @@ async function runPostRunRetryLoop(params) {
147192
147345
  gateResumeCount++;
147193
147346
  }
147194
147347
  if (gateResumeCount > 0 && result.success && hasPostRunIssues(finalIssues)) {
147195
- finalIssues = await collectPostRunIssues({ stopScript: params.stopScript });
147348
+ finalIssues = await collectPostRunIssues(params.ctx, { skipSummaryStale: true });
147196
147349
  }
147197
147350
  if (result.success && finalIssues.stopHook) {
147198
147351
  const retryNote = gateResumeCount > 0 ? ` after ${gateResumeCount} retry ${gateResumeCount === 1 ? "attempt" : "attempts"}` : "";
@@ -147203,6 +147356,16 @@ async function runPostRunRetryLoop(params) {
147203
147356
  usage: aggregatedUsage
147204
147357
  };
147205
147358
  }
147359
+ if (result.success && finalIssues.unsubmittedReview) {
147360
+ const retryNote = gateResumeCount > 0 ? ` after ${gateResumeCount} retry ${gateResumeCount === 1 ? "attempt" : "attempts"}` : "";
147361
+ const expected = finalIssues.unsubmittedReview === "Review" ? "create_pull_request_review" : "create_pull_request_review or report_progress";
147362
+ return {
147363
+ ...result,
147364
+ success: false,
147365
+ error: `${finalIssues.unsubmittedReview} mode finished without calling ${expected}${retryNote}`,
147366
+ usage: aggregatedUsage
147367
+ };
147368
+ }
147206
147369
  return { ...result, usage: aggregatedUsage };
147207
147370
  }
147208
147371
 
@@ -147319,6 +147482,12 @@ function resolveEffort(model) {
147319
147482
  if (model?.includes("opus")) return "max";
147320
147483
  return "high";
147321
147484
  }
147485
+ function tailLines(text, maxCodeUnits) {
147486
+ if (text.length <= maxCodeUnits) return text;
147487
+ const tail = text.slice(-maxCodeUnits);
147488
+ const firstNewline = tail.indexOf("\n");
147489
+ return firstNewline > 0 && firstNewline < tail.length - 1 ? tail.slice(firstNewline + 1) : tail;
147490
+ }
147322
147491
  async function runClaude(params) {
147323
147492
  const startTime = performance6.now();
147324
147493
  let eventCount = 0;
@@ -147326,6 +147495,8 @@ async function runClaude(params) {
147326
147495
  let finalOutput = "";
147327
147496
  let sessionId;
147328
147497
  let resultErrorSubtype = null;
147498
+ let lastResultError = null;
147499
+ let syntheticStopFailure = false;
147329
147500
  let accumulatedTokens = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 };
147330
147501
  let accumulatedCostUsd = 0;
147331
147502
  let tokensLogged = false;
@@ -147408,6 +147579,16 @@ async function runClaude(params) {
147408
147579
  if (event.session_id) sessionId = event.session_id;
147409
147580
  const subtype = event.subtype || "unknown";
147410
147581
  const numTurns = event.num_turns || 0;
147582
+ if (event.is_error === true && subtype === "success") {
147583
+ const apiStatus = event.api_error_status;
147584
+ lastResultError = event.result?.trim() || `claude reported is_error=true with no result text (api_error_status=${apiStatus ?? "unknown"})`;
147585
+ resultErrorSubtype = subtype;
147586
+ syntheticStopFailure = true;
147587
+ log.info(
147588
+ `\xBB ${params.label} result error: subtype=${subtype}, api_error_status=${apiStatus ?? "unknown"}, message=${lastResultError}`
147589
+ );
147590
+ return;
147591
+ }
147411
147592
  if (subtype === "success") {
147412
147593
  const usage = event.usage;
147413
147594
  const inputTokens = usage?.input_tokens || 0;
@@ -147430,12 +147611,15 @@ async function runClaude(params) {
147430
147611
  }
147431
147612
  } else if (subtype === "error_max_turns") {
147432
147613
  resultErrorSubtype = subtype;
147614
+ lastResultError = event.errors?.join("\n").trim() || null;
147433
147615
  log.info(`\xBB ${params.label} max turns reached: ${JSON.stringify(event)}`);
147434
147616
  } else if (subtype === "error_during_execution") {
147435
147617
  resultErrorSubtype = subtype;
147618
+ lastResultError = event.errors?.join("\n").trim() || null;
147436
147619
  log.info(`\xBB ${params.label} execution error: ${JSON.stringify(event)}`);
147437
147620
  } else if (subtype.startsWith("error")) {
147438
147621
  resultErrorSubtype = subtype;
147622
+ lastResultError = event.errors?.join("\n").trim() || null;
147439
147623
  log.info(`\xBB ${params.label} result: subtype=${subtype}, data=${JSON.stringify(event)}`);
147440
147624
  } else {
147441
147625
  log.info(`\xBB ${params.label} result: subtype=${subtype}, data=${JSON.stringify(event)}`);
@@ -147543,14 +147727,15 @@ async function runClaude(params) {
147543
147727
  if (stderrContext) log.info(`\xBB last stderr output:
147544
147728
  ${stderrContext}`);
147545
147729
  }
147546
- if (!tokensLogged && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
147730
+ if (!tokensLogged && !syntheticStopFailure && (accumulatedTokens.input > 0 || accumulatedTokens.output > 0 || accumulatedTokens.cacheRead > 0 || accumulatedTokens.cacheWrite > 0)) {
147547
147731
  logTokenTable({ ...accumulatedTokens, costUsd: accumulatedCostUsd });
147548
147732
  tokensLogged = true;
147549
147733
  }
147550
147734
  const usage = buildUsage();
147551
147735
  if (result.exitCode !== 0) {
147552
147736
  const errorContext = lastProviderError ? ` (${lastProviderError})` : "";
147553
- const errorMessage = result.stderr || result.stdout || `unknown error - no output from Claude CLI${errorContext}`;
147737
+ const truncatedStdout = result.stdout ? tailLines(result.stdout, 2048) : "";
147738
+ const errorMessage = lastResultError || result.stderr || truncatedStdout || `unknown error - no output from Claude CLI${errorContext}`;
147554
147739
  log.error(
147555
147740
  `${params.label} exited with code ${result.exitCode}${errorContext}: ${errorMessage}`
147556
147741
  );
@@ -147577,7 +147762,7 @@ ${stderrContext}`);
147577
147762
  return {
147578
147763
  success: false,
147579
147764
  output: finalOutput || output,
147580
- error: `result subtype: ${resultErrorSubtype}`,
147765
+ error: lastResultError || `result subtype: ${resultErrorSubtype}`,
147581
147766
  usage,
147582
147767
  sessionId
147583
147768
  };
@@ -147707,12 +147892,10 @@ var claude = agent({
147707
147892
  args: [...baseArgs, "-p", ctx.instructions.full]
147708
147893
  });
147709
147894
  return runPostRunRetryLoop({
147895
+ ctx,
147710
147896
  initialResult: result,
147711
147897
  initialUsage: result.usage,
147712
- stopScript: ctx.stopScript,
147713
- summaryFilePath: ctx.summaryFilePath,
147714
- summarySeed: ctx.summarySeed,
147715
- reflectionPrompt: ctx.learningsFilePath ? buildLearningsReflectionPrompt(ctx.learningsFilePath) : void 0,
147898
+ reflectionPrompt: ctx.toolState.learningsFilePath ? buildLearningsReflectionPrompt(ctx.toolState.learningsFilePath) : void 0,
147716
147899
  canResume: (r) => Boolean(r.sessionId),
147717
147900
  resume: async (c2) => {
147718
147901
  const sessionId = c2.previousResult.sessionId;
@@ -147823,6 +148006,8 @@ async function installOpencodeCli() {
147823
148006
  });
147824
148007
  }
147825
148008
  var PULLFROG_OPENCODE_OUTPUT_LIMIT = 5e3;
148009
+ var GEMINI_3_DIRECT_THINKING_LEVEL = "medium";
148010
+ var GEMINI_3_DIRECT_API_IDS = ["gemini-3.1-pro-preview", "gemini-3-flash-preview"];
147826
148011
  function buildSecurityConfig(ctx, model) {
147827
148012
  const config3 = {
147828
148013
  permission: {
@@ -147836,7 +148021,21 @@ function buildSecurityConfig(ctx, model) {
147836
148021
  mcp: {
147837
148022
  [pullfrogMcpName]: { type: "remote", url: ctx.mcpServerUrl }
147838
148023
  },
147839
- agent: buildReviewerAgentConfig()
148024
+ agent: buildReviewerAgentConfig(),
148025
+ provider: {
148026
+ google: {
148027
+ models: Object.fromEntries(
148028
+ GEMINI_3_DIRECT_API_IDS.map((id) => [
148029
+ id,
148030
+ {
148031
+ options: {
148032
+ thinkingConfig: { thinkingLevel: GEMINI_3_DIRECT_THINKING_LEVEL }
148033
+ }
148034
+ }
148035
+ ])
148036
+ )
148037
+ }
148038
+ }
147840
148039
  };
147841
148040
  if (model) {
147842
148041
  config3.model = model;
@@ -148431,12 +148630,10 @@ var opencode = agent({
148431
148630
  args: [...baseArgs, ctx.instructions.full]
148432
148631
  });
148433
148632
  return runPostRunRetryLoop({
148633
+ ctx,
148434
148634
  initialResult: result,
148435
148635
  initialUsage: result.usage,
148436
- stopScript: ctx.stopScript,
148437
- summaryFilePath: ctx.summaryFilePath,
148438
- summarySeed: ctx.summarySeed,
148439
- reflectionPrompt: ctx.learningsFilePath ? buildLearningsReflectionPrompt(ctx.learningsFilePath) : void 0,
148636
+ reflectionPrompt: ctx.toolState.learningsFilePath ? buildLearningsReflectionPrompt(ctx.toolState.learningsFilePath) : void 0,
148440
148637
  resume: async (c2) => runOpenCode({
148441
148638
  ...runParams,
148442
148639
  args: [...baseArgs, "--continue", c2.prompt]
@@ -152362,8 +152559,10 @@ var checkRepositoryAccess = async (token, repoOwner, repoName) => {
152362
152559
  const response = await githubRequest("/installation/repositories", {
152363
152560
  headers: { Authorization: `token ${token}` }
152364
152561
  });
152562
+ const ownerLower = repoOwner.toLowerCase();
152563
+ const nameLower = repoName.toLowerCase();
152365
152564
  return response.repositories.some(
152366
- (repo) => repo.owner.login === repoOwner && repo.name === repoName
152565
+ (repo) => repo.owner.login.toLowerCase() === ownerLower && repo.name.toLowerCase() === nameLower
152367
152566
  );
152368
152567
  } catch {
152369
152568
  return false;
@@ -152765,6 +152964,7 @@ function buildRuntimeContext(ctx) {
152765
152964
  "~pullfrog": _2,
152766
152965
  prompt: _p,
152767
152966
  eventInstructions: _ei,
152967
+ previousRunsNote: _prn,
152768
152968
  event: _e2,
152769
152969
  ...payloadRest
152770
152970
  } = ctx.payload;
@@ -152845,14 +153045,16 @@ In case of conflict between instructions, follow this precedence (highest to low
152845
153045
  2. User prompt
152846
153046
  3. Event-level instructions`;
152847
153047
  function buildTaskSection(ctx) {
153048
+ const previousRunsNote = ctx.payload.previousRunsNote?.trim() ?? "";
152848
153049
  if (ctx.userQuoted) {
153050
+ const parts = [ctx.userQuoted, previousRunsNote].filter(Boolean);
152849
153051
  return `************* YOUR TASK *************
152850
153052
 
152851
- ${ctx.userQuoted}`;
153053
+ ${parts.join("\n\n")}`;
152852
153054
  }
152853
153055
  const eventInstructions = ctx.payload.eventInstructions ?? "";
152854
- if (eventInstructions) {
152855
- const parts = [ctx.eventTitle, eventInstructions].filter(Boolean);
153056
+ if (eventInstructions || previousRunsNote) {
153057
+ const parts = [ctx.eventTitle, eventInstructions, previousRunsNote].filter(Boolean);
152856
153058
  return `************* YOUR TASK *************
152857
153059
 
152858
153060
  ${parts.join("\n\n")}`;
@@ -153167,6 +153369,7 @@ var JsonPayload = type({
153167
153369
  prompt: "string",
153168
153370
  "triggerer?": "string | undefined",
153169
153371
  "eventInstructions?": "string",
153372
+ "previousRunsNote?": "string",
153170
153373
  "event?": "object",
153171
153374
  "timeout?": "string | undefined",
153172
153375
  "progressComment?": type({
@@ -153252,6 +153455,7 @@ function resolvePayload(resolvedPromptInput, repoSettings) {
153252
153455
  triggerer: jsonPayload?.triggerer ?? // it's not a common use case but GITHUB_ACTOR can be a user when the workflow is manually triggered by a user through GitHub Actions UI
153253
153456
  (!isPullfrog(process.env.GITHUB_ACTOR) ? process.env.GITHUB_ACTOR : void 0),
153254
153457
  eventInstructions: jsonPayload?.eventInstructions,
153458
+ previousRunsNote: jsonPayload?.previousRunsNote,
153255
153459
  event,
153256
153460
  timeout: inputs.timeout ?? jsonPayload?.timeout,
153257
153461
  cwd: resolveCwd(inputs.cwd),
@@ -154055,9 +154259,10 @@ async function persistSummary(ctx) {
154055
154259
  log.debug(`pr summary persist failed: ${err instanceof Error ? err.message : String(err)}`);
154056
154260
  });
154057
154261
  }
154058
- async function writeJobSummary(toolState) {
154262
+ async function writeJobSummary(toolState, finalOutput) {
154059
154263
  const usageSummary = formatUsageSummary(toolState.usageEntries);
154060
- const summaryParts = [toolState.lastProgressBody, usageSummary].filter(Boolean);
154264
+ const body = toolState.lastProgressBody || finalOutput;
154265
+ const summaryParts = [body, usageSummary].filter(Boolean);
154061
154266
  if (summaryParts.length > 0) {
154062
154267
  await writeSummary(summaryParts.join("\n\n"));
154063
154268
  }
@@ -154343,9 +154548,7 @@ ${instructions.user}` : null,
154343
154548
  instructions,
154344
154549
  todoTracker,
154345
154550
  stopScript: runContext.repoSettings.stopScript,
154346
- summaryFilePath: toolState.summaryFilePath,
154347
- summarySeed: toolState.summarySeed,
154348
- learningsFilePath: toolState.learningsFilePath,
154551
+ toolState,
154349
154552
  onActivityTimeout: onInnerActivityTimeout,
154350
154553
  onToolUse: (event) => {
154351
154554
  const wasTracked = recordDiffReadFromToolUse({
@@ -154406,12 +154609,24 @@ ${instructions.user}` : null,
154406
154609
  if (toolContext) {
154407
154610
  await persistLearnings(toolContext);
154408
154611
  }
154409
- if (toolContext && toolState.progressComment && !toolState.finalSummaryWritten) {
154612
+ if (!result.success && toolContext && toolState.progressComment) {
154613
+ await reportErrorToComment({
154614
+ toolState,
154615
+ error: result.error || "agent run failed"
154616
+ }).catch((error49) => {
154617
+ log.debug(`failure error report failed: ${error49}`);
154618
+ });
154619
+ }
154620
+ if (toolContext && result.success && toolState.progressComment && !toolState.finalSummaryWritten) {
154410
154621
  await deleteProgressComment(toolContext).catch((error49) => {
154411
154622
  log.debug(`stranded progress comment cleanup failed: ${error49}`);
154412
154623
  });
154413
154624
  }
154414
- await writeJobSummary(toolState);
154625
+ try {
154626
+ await writeJobSummary(toolState, result.output);
154627
+ } catch (error49) {
154628
+ log.debug(`job summary write failed: ${error49}`);
154629
+ }
154415
154630
  if (toolState.output) {
154416
154631
  log.info(`::pullfrog-output::${Buffer.from(toolState.output).toString("base64")}`);
154417
154632
  core6.setOutput("result", toolState.output);
@@ -156303,7 +156518,7 @@ async function run2() {
156303
156518
  }
156304
156519
 
156305
156520
  // cli.ts
156306
- var VERSION10 = "0.1.2";
156521
+ var VERSION10 = "0.1.4";
156307
156522
  var bin = basename2(process.argv[1] || "");
156308
156523
  var PROG = bin === "pf" || bin === "pullfrog" ? bin : "pullfrog";
156309
156524
  var rawArgs = process.argv.slice(2);