32 Commits
Author SHA1 Message Date
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Colin McDonnell
f49d4206aa chore(models): bump resolved versions (#790)
* chore(models): bump resolved versions

* fix(models): keep google gemini-3.5-flash bump, revert invalid openrouter ids

openrouter/google/gemini-3.5-flash is not on models.dev yet; only the direct
google/gemini-flash resolve should move to 3.5-flash.

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
2026-05-20 02:25:50 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
0abaaa1e37 chore(oss): add yamcodes/arkenv to OSS program (#776)
* chore(oss): add yamcodes/arkenv to OSS program

* fix(test): strip CODEX_AUTH_JSON in apiKeys auto-select test

The beforeEach strip list omitted CODEX_AUTH_JSON, which is in
`knownApiKeys` via the openai provider's managedCredentials. When the
env has CODEX_AUTH_JSON set, the auto-select "throws when no provider
keys are present" assertion finds it and fails to throw.

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-05-17 20:26:26 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
851e49e2d7 action: retry transient GitHub 422 "internal error" on review submission (#610)
* action: retry transient GitHub 422 "internal error" on review submission

GitHub sometimes 422s POST /pulls/{n}/reviews with body
"An internal error occurred, please try again." — a server-side hiccup
that the existing 422 handler framed with the generic
"likely causes (1)(2)(3)" prompt listing affected comments. the agent
dutifully refetched the diff, dropped comments, and resubmitted, hitting
the same transient error on a shifting affected-comments list until
GitHub accepted. some runs logged 8+ spurious retries with ~11 minutes
of wall-clock, dropping valid inline comments along the way.

detect the transient body explicitly, retry in-tool twice with 1s/3s
backoff, and surface a distinct error on exhaustion that tells the agent
this is a GitHub-side issue — do not modify inline comments, wait and
retry or fall back to a body-only review. closes #584.

* action: use retry util for transient review 422, drop isTransientReviewError tests

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-05-08 20:33:01 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>David Blass
6f76a6a9da fix(action): tighten provider error detection and propagate agent error events (#580)
* fix(action): tighten provider error detection and propagate agent error events

Both bugs from #562:

1. detectProviderError used substring matches against "429", "rate limit",
   etc. — false-positives on commit SHAs containing 429 and on x-ratelimit-*
   response headers in dumped 401 error JSON. rewrote with anchored regexes:
   numeric status codes only match adjacent to a recognised status key, and
   `\brate[_ ]limit(?=[_ ]|\b)` rejects ratelimit-* headers (no separator).
   word-boundary anchors on INTERNAL / UNAVAILABLE / quota / limit:0 reject
   INTERNAL_SERVER_ERROR / time_limit:0 substrings. added 11-case regression
   test.

2. opencode 401s slipped through `eventCount === 0 && lastProviderError`
   because opencode's own type=error event increments eventCount before
   the guard runs. added an explicit `error:` handler that captures the
   event and propagates it to a non-success AgentResult. opencode emits
   the message under `error.data.message`, not the top level. mirror fix
   in claude.ts: error_max_turns / error_during_execution / any error*
   subtype on the result event now flips success: false.

* fix(action): match quota inside identifiers like insufficient_quota

\bquota\b missed insufficient_quota / quota_exceeded / quotaExceeded
because _ is a word character and camelCase has no boundary. quota is
specific enough to be matched as a plain substring.

* fix(action): match `rate limited` and `rate limits exceeded`

Drop the trailing `(?=[_ ]|\b)` lookahead from the rate-limit regex. The
lookahead failed when `limit` was followed by another word character
(`limited`, `limits`), so `rate limited` and `rate limits exceeded` were
slipping past detection. The leading `\b` plus `[_ ]` separator already
rejects `x-ratelimit-*` / `anthropic-ratelimit-*` headers without it.

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: David Blass <david@arktype.io>
2026-05-07 16:31:05 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>David Blass
366af55f19 fix(action): sweep stale .git/*.lock and deepen-retry shallow git_fetch (#564) (#578)
- checkoutPrBranch now removes .git/shallow.lock, .git/index.lock, and
  .git/objects/maintenance.lock when older than 30s before the first fetch.
  prior runs that crashed mid-fetch left these behind on self-hosted runners,
  causing checkout_pr to abort with `Unable to create '.git/shallow.lock':
  File exists` until the agent shelled out to rm -f.

- GitFetchTool catches `Could not read <sha>` and `remote did not send all
  necessary objects` on shallow clones and retries once with --deepen=1000
  instead of bouncing the failure back to the agent. agents previously had
  to fall back to checking out FETCH_HEAD, losing branch context.

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: David Blass <david@arktype.io>
2026-05-06 22:38:41 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>David BlassCursor
4c1413d925 fix(action): flip wasUpdated on substantive MCP write tools (#563) (#577)
* fix(action): flip wasUpdated on substantive MCP write tools (#563)

Review/Respond/etc. agents that submit a `create_pull_request_review`,
`create_issue_comment`, or `update_pull_request_body` and exit without
calling `report_progress` were being marked as workflow failures by the
strict completion check in handleAgentResult. Extend the set of tools
that flip toolState.wasUpdated so a substantive user-visible artifact
satisfies the check. The isReviewMode bypass is retained for
IncrementalReview's non-substantive path.

Flag is set BEFORE patchWorkflowRunFields / deleteProgressComment in
each tool so a best-effort cleanup failure does not undo the signal.

* fix(action): use finalSummaryWritten for stranded progress cleanup

The stranded-progress-comment cleanup at the end of main() previously
fired only when toolState.wasUpdated was false (or the tracker was the
last writer). With wasUpdated now set by additional MCP write tools
(create_issue_comment, update_pull_request_body), an agent that produced
a substantive artifact via one of those tools and skipped report_progress
would leave the placeholder "Leaping into action" comment intact — the
post-script then converted it into an error message on a successful run.

Key the cleanup off finalSummaryWritten instead. That flag is only set
when report_progress actually wrote the progress comment, so it cleanly
distinguishes "comment is finalized" from "agent did other work but
never touched the progress comment".

* refactor(mcp): extract markSubstantiveArtifact() helper

replaces 4 inline `ctx.toolState.wasUpdated = true` flips in CreateCommentTool, UpdatePullRequestBodyTool, and CreatePullRequestReviewTool with a single helper in mcp/server.ts. JSDoc on the helper documents the contract (call BEFORE downstream patch/cleanup; gates the strict completion check and stranded-comment cleanup) so future MCP write tool authors only need to grep for one symbol.

no behavioral change.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(mcp): only flip finalSummaryWritten after non-skipped write

Previously the flag was set unconditionally on any non-plan call,
including paths where reportProgress skipped (silent events, deleted
comment, no issue/PR target). The cleanup check in main.ts is
safeguarded by toolState.progressComment so the bug doesn't manifest
today, but aligning the flag with actual writes matches the wasUpdated
pattern and the design intent in the cleanup plan.

* refactor(mcp): inline markSubstantiveArtifact helper

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: David Blass <david@arktype.io>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-06 21:05:53 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>David Blass
6db4a6d02e fix(mcp): preserve coveragePreflightRan across checkout_pr refreshes (#576)
checkout_pr unconditionally rebuilds ctx.toolState.diffCoverage via
createDiffCoverageState, which initialised coveragePreflightRan to false.
a second checkout_pr therefore reset the "one-time nudge per review
session" guarantee in runDiffCoveragePreflight, and the next
create_pull_request_review threw the diff-coverage pre-flight error
again — even after the agent had already gone through the
read-and-resubmit dance once.

createDiffCoverageState now accepts an optional previous state and
carries forward coveragePreflightRan. coveredRanges are intentionally
not carried because their line numbers are tied to the previous diff's
content (especially under incremental diffs).

closes #566

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: David Blass <david@arktype.io>
2026-05-06 20:54:34 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
8bac460177 fix: add concurrency protection to action sync workflows (#451)
* fix: add concurrency protection to action sync workflows

* style: fix formatting in action/modes.ts

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-03-05 23:44:55 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Mateusz Burzyński
734e8197db tighten Plan mode guidance to prevent file creation and require full plan in progress comment (#432)
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
2026-03-05 17:29:01 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Mateusz Burzyński
cc46af0d47 Share GitHub rate limit tracking between the action and the worker (#326)
* share GitHub rate limit tracking between the action and the worker

The action now counts all GitHub API requests and captures the latest
`x-ratelimit-remaining`/`x-ratelimit-reset` headers via a global
request hook on every Octokit instance.

On exit, the usage summary is written atomically to a path specified
by `PULLFROG_USAGE_SUMMARY_PATH`. The worker sets this env var before
sandbox execution, reads the file afterward, and feeds the data into
the Durable Object's rate limit state.

This closes the visibility gap where the worker had no insight into
API calls made by the sandboxed action process.

* address review: refactor rate limit state, randomize usage summary path

* track actual rate limit cost using x-ratelimit-remaining delta

* refactor usage summary writing to use onExitSignal API

Replace the monolithic registerUsageSummaryHandler with direct use of
onExitSignal in main.ts and a writeGitHubUsageSummaryToFile utility
in github.ts. This keeps exitHandler.ts as a pure signal handler
registry (from #299) and also writes the summary on normal exit.

* tweak

* unify

* deduplicate stuff

* improve error handling

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
2026-03-03 12:45:36 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Mateusz BurzyńskiColin McDonnell
c456fae716 Standardize on top-level triggerer property (#361)
* Standardize on top-level `triggerer` property

- Rename `triggeringUser` → `triggerer` as the single top-level payload property
- Remove redundant `triggerer` from `FixReviewEvent`, add `approvedOnly` boolean
- Auto-apply `approved_by` filtering in `get_review_comments` when `approvedOnly` is set
- Auto-assign created PRs to the triggerer
- Simplify `AddressReviews` mode prompt
- Accept both `triggerer` and `triggeringUser` in schema for backward compat

* Address review feedback: simplify approved_only, remove addAssignees, drop approved_by param

* Fix formatting in `action/mcp/pr.ts`

* re-add triggeringUser backward-compat fallback in payload schema

* auto-assign created PRs to the triggerer

* Revert "auto-assign created PRs to the triggerer"

This reverts commit c088c425fea33793eb299a001ffd253798d2c674.

* Revert "re-add triggeringUser backward-compat fallback in payload schema"

This reverts commit ae5b3cb3f1377cd4a634b2d48962c785c31013f3.

* backend compat

* tweak prompt to ensure compat

* Address review feedback

* chore: remove triggeringUser fallback

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
2026-02-27 12:43:37 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>CursorMateusz Burzyński
20b08b5321 Add "Rerun failed job ➔" link to error comment footer (#355)
* add "Rerun failed job" link to error comment footer

* Remove issueNumber guard from rerun link

The rerun action only needs run_id — the issue number in the trigger
URL path is just a route segment requirement. Use 0 as a fallback
so the link is always shown when a run ID is available.

* Overload `[number]` path segment as `runId` for the rerun action

For the rerun trigger, the `[number]` path param now carries the
workflow run ID instead of an issue number. The rerun link changes
from `/trigger/o/r/ISSUE?action=rerun&run_id=RID` to
`/trigger/o/r/RID?action=rerun`.

- Remove `run_id` query param from page.tsx and searchParams type
- Add error handling around `reRunWorkflow` for invalid run IDs
- Drop `issueNumber` from `BuildErrorCommentBodyParams` and all
  rerun link builders (errorReport, exitHandler, postCleanup)

* Reorder validation: action first, then rerun, then issueNumber

* address review: remove early toolState assignment, restructure rerun into dedicated block

* revert self-contained rerun block, share auth logic via issueOrRunId

* improve error handling

* normalize runid early

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
2026-02-27 12:32:06 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Mateusz Burzyński
c0fd69560f Add "Fix it" link for body-only PR reviews (#338)
* Add "Fix it" link for body-only PR reviews

When a PR review has only body-level feedback (no inline comments),
the footer now includes a "Fix it" link that triggers the fix flow.

Also fetches the review body in the fix action's prompt so the agent
can address body-level feedback even when there are no inline comments.

* Move review body fetching into `get_review_comments` tool

Instead of fetching the review body in the trigger page and appending
it to the prompt, the `get_review_comments` MCP tool now fetches the
review body via the GitHub API and includes it in its markdown output
under a "Review Body" section. This keeps the trigger page simple and
lets the tool provide all review context in one place.

* fetch body early

* get reviewer from a better place

* cleanup structure to reuse more in test

* simplify

* simplify

* typecheck

* fetch review body via REST API; skip listFiles for body-only reviews

* update snapshot

* formatting

* cleanup

* fix line counting with `countNewlines` utility using `indexOf` loop

* rename `countNewlines` to `countLines` with 1-based line counting

* suppress biome lint warning for assignment in while condition

* remove unused `body` field from GraphQL review query and type

* add `approved` parameter to `create_pull_request_review` and skip fix links for approvals

* vibe instructions

* tighten up prompting

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
2026-02-27 12:18:51 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Mateusz Burzyński
e0bd984975 Restrict create_pull_request_review comments to PR diff (#339)
* restrict review comments to PR diff in tool description

* fix constraint text: only files (not lines) are restricted to the diff

* Revert "fix constraint text: only files (not lines) are restricted to the diff"

This reverts commit 3f2e3d05e41c308f6640a468230fb0d69c0cc3e1.

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
2026-02-27 11:23:56 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>CursorAnna Bocharova
6311138132 fix: report errors to progress comment when agent fails without throwing (#376)
* fix: report errors to progress comment when agent fails without throwing

when an agent returns success: false without throwing (e.g., opencode exits
with code 0 despite provider errors), the catch block in main.ts is never
reached, leaving the progress comment stuck on "leaping into action."

two fixes:
- opencode: return success: false when 0 events processed and a provider
  error was detected (converts silent failures to explicit failures)
- main.ts: after handleAgentResult, check if the progress comment was ever
  updated — if not, report the error to the comment as a safety net

Co-authored-by: Cursor <cursoragent@cursor.com>

* address review: use result directly and force failure on unreported progress

* refactor: move safety-net logic into handleAgentResult

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Anna Bocharova <robin_tail@me.com>
2026-02-26 08:36:36 +00:00
pullfrog[bot]pullfrog[bot]Mateusz BurzyńskiCursorpullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
ee100354da fix: replace domain-specific exit handler with generic signal handler registry (#299)
* fix: replace domain-specific exit handler with generic signal handler registry

Rewrite exitHandler.ts as a generic, domain-agnostic exit signal module
that exports onExitSignal(handler) returning a dispose function.

- subprocess.ts now registers via onExitSignal instead of direct
  process.on(SIGINT/SIGTERM) calls
- resolveTokens registers a signal handler that captures tokens by
  closure, fixing the race condition where the exit handler would
  read the wrong token after disposal
- Remove setupExitHandler and runCleanup — domain cleanup is handled
  by post.ts + await using

Co-authored-by: Cursor <cursoragent@cursor.com>

* tweaks

* simplify handler installation

* extract to util

* fix race in dispose

* wrap dispose body in try/finally to ensure disposingRef always settles

---------

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-02-20 10:23:56 +00:00
pullfrog[bot]pullfrog[bot]Mateusz BurzyńskiCursorpullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Colin McDonnell
70f1c47a28 Audit core.warning/core.error usage (#269)
* Stop using command-based logs for warnings and errors

Co-authored-by: Cursor <cursoragent@cursor.com>

* revert

* tweak

* de-noise

* Remove redundant ts() timestamp prefix from log calls

* Restore timestamped logging and refine debug output routing.

Bring back timestamp prefixes for standard logs and make log.debug emit via core.debug when runner debug is enabled, while still surfacing debug lines for --debug runs.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Mateusz Burzyński <mateuszburzynski@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
2026-02-19 23:11:47 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Colin McDonnellCursor
9948c08e7d run post action cleanup in play.ts (#344)
* run post action cleanup in play script after main completes

* clarify that GITHUB_RUN_ID is the actual bail-out gate in play context

* treat GITHUB_RUN_ID as optional in post cleanup

* replace dynamic import with static import of `runPostCleanup`

Export `runPostCleanup` from post.ts and guard the top-level
execution with `import.meta.url` so it only auto-runs as an
entry point. play.ts now statically imports and calls it.

* move runPostCleanup into finally block and let failures propagate

* refactor post cleanup into utility module

move post cleanup logic into a dedicated utility and keep post.ts as a pure script entrypoint. update play.ts to import the shared utility directly and normalize direct-execution detection.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-18 18:32:24 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
30812435f9 Fix file upload 401 by conditionally signing content-disposition header (#289)
* Fix file upload 401 by conditionally signing content-disposition header

* Bump version to 0.0.162

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-02-13 19:14:32 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
d7759734f2 Clarify issue comment semantics and strengthen report_progress guidance (#292)
- Add `comment_type: "issue"` to `IssueCommentCreatedEvent` interface and
  dispatch sites so agents can distinguish issue comments from PR review
  comments
- Add dedicated "Progress reporting" section to system prompt making
  `report_progress` the mandatory tool for sharing results
- Update `reply_to_review_comment` description to clarify it only works
  for inline review comments on PR diffs, not issue comments

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-02-13 14:55:26 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
dda1d6b1de Reorder mode instructions to test before committing (#274)
Update Build, AddressReviews, and Prompt modes to ensure tests are run
BEFORE committing and pushing code. This prevents redundant workflow
triggers when tests fail and need fixes.

Previously, the Build and Prompt modes would:
1. Make code changes
2. Commit and push
3. Test (oops, too late!)

Now all modes follow the correct order:
1. Make code changes
2. Test (if tests fail, fix and repeat)
3. Commit and push

This addresses the inefficiency observed in #268 where the agent pushed
code before verifying it worked, then had to fix and push again.

Closes #273

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-02-12 16:37:15 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Robin Tail
b6e6a8976c Replace Date.now() with performance.now() for duration measurements (#258)
* Replace Date.now() with performance.now() for duration measurements

- Import performance from node:perf_hooks in all affected files
- Update Timer and ThinkingTimer classes to use performance.now()
- Update activity tracking (markActivity, getIdleMs) to use performance.now()
- Update cache duration measurements to use performance.now()
- Update agent execution timing (cursor, opencode) to use performance.now()
- Update subprocess execution timing to use performance.now()
- Update API performance monitoring to use performance.now()
- Update prep phase timing to use performance.now()
- Update timer.test.ts to mock performance.now() instead of Date.now()

Benefits:
- Monotonic clock immune to system clock adjustments
- Higher precision (microsecond vs millisecond resolution)
- Purpose-built for performance measurement

Fixes #245

* fix lint.

* Round float durations to integers in logging

Preserve original behavior by rounding performance.now() float values
to integers when displaying/logging millisecond durations.

* fix lint.

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Robin Tail <robin_tail@me.com>
2026-02-12 15:26:28 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Robin Tail
a442f766aa feat: Resolve threads in AddressReviews mode (#266)
* Add review thread resolution to AddressReviews mode

- Add thread_id to comment metadata in buildThreadBlocks
- Implement ResolveReviewThreadTool with GraphQL mutation
- Register new tool in MCP server
- Update AddressReviews mode to resolve threads after addressing feedback

Closes #227

* Fix typo: use log.warning instead of log.warn

* refactor: DRY up catch block by extracting isResolved condition

* fix lint.

* fix: avoid using any.

* fix: combining log statements around the message.

* fix(test): Adjusting snapshot.

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Robin Tail <robin_tail@me.com>
2026-02-12 15:25:21 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Robin TailColin McDonnellCursor
fb80343ffd feat(agents): add thinking time logging between tool calls (#244)
* feat(agents): add thinking time logging between tool calls

Adds a ThinkingTimer utility that tracks the gap between tool results and
the next tool call. When the gap exceeds 3 seconds, it logs the duration
with a stopwatch emoji (⏱️ 4.2s).

Uses performance.now() for high-resolution timing and Intl.NumberFormat
for rendering duration in seconds with optional fraction digits.

Integrated across all 5 agents: Claude, Codex, Cursor, Gemini, OpenCode.

Closes #127

* fix: adjusting tests for mocking performance.now.

* fix: reducing diff for claude.

* fix: rm unused args for claude.

* rm unused args for codex.

* fix: rm unused args for gemini.

* fix: rm unused args for opencode.

* mv THINKING_THRESHOLD.

* rev: I decided to pospone node:perf_hooks integration since it requires more comprehensive refactoring.

* fix: using Intl unit formatting.

* tests for ThinkingTimer.

* fix: narrow unit.

* fix: making durationFormatter a class instance property since using one agent per run.

* fix: inverting condition in markToolCall.

* thinking timer improvements and fix actions/checkout v6 auth

- thinking timer: use » chevron and "thought for X seconds" format
- thinking timer: add debug timestamps for sanity checking
- demote PID namespace isolation logs to debug
- remove redundant "setting up git authentication" log
- fix duplicate Authorization header with actions/checkout v6: clean up
  includeIf credential entries that v6 persists via external config files

Co-authored-by: Cursor <cursoragent@cursor.com>

* standardize tool call log prefix to » double chevron

Co-authored-by: Cursor <cursoragent@cursor.com>

* update timer tests for new thinking log format

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Robin Tail <robin_tail@me.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-10 06:17:43 +00:00
1d59fd3d21 feat: Lifecycle hooks (#219)
* flatten lifecycle hooks into RepoSettings string fields

replace the separate LifecycleHook model with setupScript and
postCheckoutScript string fields directly on RepoSettings. move the UI
into the Agent settings section alongside environment variables and
custom instructions. delete the standalone lifecycle-hooks API route,
component, and schema since the existing settings PATCH endpoint
handles the new fields automatically.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix: pass env to lifecycle hook spawn so scripts can use package managers

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-06 07:16:14 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
cac9b0e645 Strengthen PR body instructions to auto-close issues (#186)
Update Build and Prompt mode instructions to explicitly reference
`issue_number` from EVENT DATA and instruct agents to include
"Closes #<issue_number>" in PR bodies when working in the context
of an issue (where `is_pr` is not true).

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-01-28 04:30:35 +00:00
pullfrog[bot]andGitHub 90945a9481 chore: update pullfrog.yml workflow 2026-01-28 00:51:37 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Robin TailColin McDonnell
a200d07370 feat: Immediate Leaping into action (#146)
* feat: post "Leaping..." comment immediately without polling GitHub API

This change makes the initial comment response much faster by avoiding
the expensive GitHub API polling that was waiting for workflows to
dequeue (up to 12s+ in some cases).

New architecture:
1. Create WorkflowRun record BEFORE dispatching (no runId yet)
2. Post "Leaping into action..." comment with shortlink URL immediately
3. Dispatch workflow and return
4. workflow_run.requested webhook fills in runId when GitHub dequeues

Shortlink redirect at /api/workflow-run/[id]/logs:
- If runId available: redirects to GitHub workflow run
- If runId null: shows polling page that checks DB every 1.5s

Changes:
- Make `runId` optional on WorkflowRun model (filled in via webhook)
- Add `workflow_run` to expected webhook events
- Add handler for workflow_run.requested to update DB with runId
- Create /api/workflow-run/[id]/logs shortlink redirect route
- Refactor triggerWorkflow.ts to use eager comment pattern
- Update trigger page to use new pattern

Closes #141

* chore: add migration for nullable runId in WorkflowRun

* fix: rm dead code.

* fix: Adjusting the issueNumber prop usage comment.

* fix: shortening and JSDoc for createWorkflowRunRecord.

* fix: Reducing diff, reducing confusion on naming the id.

* Revert "fix: Adjusting the issueNumber prop usage comment."

This reverts commit 34d87c2f8bc58782a53ce5eb14a40935232a4924.

* refactor: reuse buildShortlinkUrl in trigger page

* fix: Reducing confusion on param naming.

* fix: shorter JSDoc.

* Apply suggestion from @RobinTail

* chore: remove unnecessary JSDoc comment from buildShortlinkUrl

* Revert "chore: remove unnecessary JSDoc comment from buildShortlinkUrl"

This reverts commit 491ba6ba3f31c874c9f391871739efa50adb0446.

* fix: confusing naming of var.

* fix: redundant 'let'.

* refactor: move route from `/api/workflow-run/[id]/logs` to `/api/workflow-run-logs/[id]`

Avoids confusion with existing `/api/workflow-run/[runId]` route which uses
GitHub's runId, whereas this new route uses the internal WorkflowRun record id.

* chore: remove old route directory

* refactor: reuse `buildShortlinkUrl()` with `shouldPoll` param

* refactor: add script prop to generateLeapingLoaderHtml

Instead of string-replacing to inject scripts, the function now
accepts an optional script prop that gets wrapped in <script> tags.

* mv script into new LeapingLoaderHtmlProps.

* refactor: extract `buildGithubUrl` helper to avoid repetition

* fix: shorening.

* fix: More clear subtitle.

* refactor: reuse `WORKFLOW_FILENAME` from `app/globals.ts`

* fix: shortening.

* feat: add integrity_id for reliable workflow matching

Pass WorkflowRun record id as integrity_id when dispatching workflows.
The webhook handler parses integrity_id from display_title (via run-name)
for reliable matching, with fallback to repo/owner lookup when missing.

Note: workflow template changes (.github/workflows/pullfrog.yml) need to be
applied manually as the GitHub App lacks workflows permission.

* Revert "feat: add integrity_id for reliable workflow matching"

This reverts commit dbc601233a0dd85ac5f0d608a221e7015e265aaa.

* Add todo for consideration later.

* docs: add plan for action-initiated workflow run correlation

Addresses review feedback requesting research into secure alternatives
to exposing HOOKDECK_API_KEY. Proposes leveraging existing OIDC token
exchange to pass WorkflowRun record ID and correlate with run_id.

* Revert "docs: add plan for action-initiated workflow run correlation"

This reverts commit b9279d0e99db4d85e4144675634afa941858333a.

* FEAT: Add optional integrity_id input, used by run-name, set with partial record id, read by handler for lookup.

* Add integrity_id to app/trigger/[owner]/[repo]/[number]/page.tsx

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>

* feat: Extracting INTEGRITY_ID_LENGTH.

* Add integrity_id to the workflow files of the repo.

* fix: Use Vercel Preview deployment URL into account in buildShortlinkUrl().

* fix: add missing `.rest` prefix in Octokit API call

* revert: remove unnecessary escaping of backticks in comment

* fix: add polling timeout and wrap DB update in try-catch

- Add 3-minute timeout to polling page to prevent indefinite polling
- Wrap updateWorkflowRunComment in try-catch to prevent orphaned comments

* feat: restore job-level deep linking for workflow runs

Adds jobId to WorkflowRun model and captures the first job ID via
listJobsForWorkflowRun() when the workflow_run_in_progress event fires.
The shortlink redirect now appends /job/{jobId} when available, providing
a direct link to the job rather than just the workflow run.

* fix: handle only `workflow_run.in_progress` to avoid race condition

Combine the handling of `runId` and `jobId` into a single update when
`workflow_run.in_progress` fires, avoiding the race condition where
`in_progress` could arrive before `requested` was processed.

* Renae integrity_id -> name

* Clean up

* Clean up

* Shorter timeout

* Add fallback

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Robin Tail <robin_tail@me.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
2026-01-28 00:40:56 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
97001d7d88 fix: make PR creation conditional on user intent (#131)
- Build mode: rewrote steps 8-9 to consolidate PR creation logic into step 8
  with explicit default/branch-only behaviors, removing the false claim that
  create_pull_request is needed for commit attribution
- Prompt mode: updated step 2 with the same conditional PR creation logic

Fixes #84

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
2026-01-20 10:39:04 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Colin McDonnell
159e937d0d Check for API key existence when selecting agent in dashboard (#115)
* add api key existence check when selecting agent

- create getSecretNames utility to fetch GitHub Actions secret names
- add /api/repo/[owner]/[repo]/secrets endpoint to check secrets
- update AgentSettings to fetch and display secret validation status
- show green check when required API key exists
- show amber warning when required API key is missing
- show loading state while checking secrets

* Add API key checking

* Fix null agent test

* Tweaks

* Switch to getrepoorgsecretes

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
2026-01-19 23:18:44 +00:00
pullfrog[bot]pullfrog[bot]pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>Colin McDonnell
71feba0a76 fix: prevent log.writeSummary from overwriting reportProgress content (#87)
* fix: prevent log.writeSummary from overwriting reportProgress content

The run summary was showing logs instead of the final reportProgress content
because log.writeSummary() was called after reportProgress. Now
log.writeSummary() checks if the summary was already overwritten by
reportProgress and skips if so.

Fixes #86

* refactor: replace dynamic import with static import in cli.ts

Replace unnecessary dynamic import of wasSummaryOverwritten with
static import. No circular dependency exists since comment.ts doesn't
import from cli.ts.

* Fix run summary writing

---------

Co-authored-by: pullfrog[bot] <226033991+pullfrog[bot]@users.noreply.github.com>
Co-authored-by: Colin McDonnell <colinmcd94@gmail.com>
2026-01-15 00:55:42 +00:00
3b880eb478 Implement GitHub suggestion format instructions (#79)
* implement github suggestion format instructions

add instructions for agents to use github's suggestion format (```suggestion blocks) when providing code suggestions in comments. this enables one-click apply for suggested changes.

updated:
- action/mcp/review.ts: added suggestion format guidance to create_pull_request_review tool description and comment body parameter
- action/mcp/comment.ts: added suggestion format guidance to all comment tools with clarification that suggestions only work on pr line-level review comments
- action/modes.ts: added detailed example in review mode and reminder in address reviews mode

fixes #70

* Address PR review feedback

- Remove suggestion format guidance from report_progress (not applicable)
- De-duplicate description across Comment, EditComment, ReplyToReviewComment
- Drop outer fence in suggestion format example
- Clarify that suggestions only work for self-contained changes
- Remove useless example comment from review tool description

---------

Co-authored-by: pullfrog <team@pullfrog.com>
2026-01-14 20:41:43 +00:00