* refactor: extract helpers out of action/main.ts so non-orchestration churn stops touching the file
main.ts had grown to ~1240 lines holding ~500 lines of helpers that have
nothing to do with the resolver pipeline — billing-error UI/copy, proxy
minting, summary/learnings persistence, log formatters, end-of-run
cleanup waterfalls. any PR adding a new billing code branch or a new
log line was forced to edit main.ts, and since main.ts is in
ALWAYS_RUN_ALL the entire 52-job LLM CI matrix fired on what should
have been a 0-job change (e.g. #748).
extractions:
- action/utils/billingErrors.ts — BillingError, TransientError, the
format*Summary renderers, billingConsoleUrl
- action/utils/proxy.ts — mintProxyKey, buildProxyTokenHeaders,
resolveProxyModel, plus runProxyResolution wrapper that renders +
rethrows BillingError/TransientError before the outer catch
- action/utils/prSummary.ts — fetchPreviousSnapshot, persistSummary
co-located with the existing seed/read file helpers
- action/utils/learnings.ts — persistLearnings co-located with the
existing seed/read file helpers
- action/utils/runStartupLog.ts — resolveOutputSchema + logRunStartup
(the model/agent/push/shell/timeout block)
- action/utils/runErrorRenderer.ts — renderRunError classifies
(BillingError reclassify / hang detect / API-key auth) and emits
{summary, comment} markdown bodies
- action/utils/runLifecycle.ts — persistRunArtifacts, finalizeSuccessRun,
writeRunErrorOutputs — the three end-of-run cleanup phases shared
between the success path and the error catch path
main.ts is now ~570 lines — the irreducible orchestrator: disposables
(`await using` for tokenRef / gitAuthServer / mcpHttpServer), the
toolContext construction, the agent-timeout race, the catch/finally
shape, and the named phase calls. behavior is preserved verbatim
(verified: pnpm -r typecheck + pnpm test 695/695 pass, action/test
596/596 pass).
wiki/main.md gets a new "file layout" section describing the split.
AGENTS.md gets a single line pointing future edits at the helpers
instead of main.ts.
* anneal: address review findings
- restore MainResult.result?: string (accidental removal in initial commit;
field was unused in current code but is part of the exported interface
surface — keep the diff truly behavior-preserving)
- move resolveOutputSchema from runStartupLog.ts to payload.ts (it's an
action-input resolver alongside resolvePromptInput / resolvePayload, not
a log helper — was placed in runStartupLog.ts for matrix-churn pragmatism
but the domain fit is in payload.ts)
- un-export resolveProxyModel (only used internally by runProxyResolution
in proxy.ts; no external importer)
- fix runErrorRenderer.ts JSDoc "Three classifications" → four (Billing,
hang, API-key, default)
- expand runLifecycle.ts module banner to note that finalizeSuccessRun
calls persistRunArtifacts first, and to explain why the catch path
splits writeRunErrorOutputs + persistRunArtifacts
- update billingErrors.ts header to point at proxy.ts and
runErrorRenderer.ts as the actual origin sites (was stale "main.ts")
- expand proxy.ts header to spell out the runProxyResolution entrypoint
contract (was stale "main.ts can render")
- update wiki/main.md resolver chain + dependency table to name
runProxyResolution as the actual call site and document the early
BillingError/TransientError rendering branch
- update wiki/main.md file-layout table to lead with runProxyResolution
and describe mintProxyKey/buildProxyTokenHeaders/resolveProxyModel
as internal helpers (was implying they were public surface)
Pullfrog is a GitHub bot that brings the full power of your favorite coding agents into GitHub. It's open source and powered by GitHub Actions.
Tag @pullfrog — Tag @pullfrog in a comment anywhere in your repo. It will pull in any relevant context using the action's internal MCP server and perform the appropriate task.
Prompt from the web — Trigger arbitrary tasks from the Pullfrog dashboard
Automated triggers — Configure Pullfrog to trigger agent runs in response to specific events. Each of these triggers can be associated with custom prompt instructions.
issue created
issue labeled
PR created
PR review created
PR review requested
and more...
Pullfrog is the bridge between your preferred coding agents and GitHub. Use it for:
🤖 Coding tasks — Tell @pullfrog to implement something and it'll spin up a PR. If CI fails, it'll read the logs and attempt a fix automatically. It'll automatically address any PR reviews too.
🔍 PR review — Coding agents are great at reviewing PRs. Using the "PR created" trigger, you can configure Pullfrog to auto-review new PRs.
🤙 Issue management — Via the "issue created" trigger, Pullfrog can automatically respond to common questions, create implementation plans, and link to related issues/PRs. Or (if you're feeling lucky) you can prompt it to immediately attempt a PR addressing new issues.
Literally whatever — Want to have the agent automatically add docs to all new PRs? Cut a new release with agent-written notes on every commit to main? Pullfrog lets you do it.
Standalone Usage
You can also use pullfrog/pullfrog as a step in your own workflows. The action exposes a result output that can be consumed by subsequent steps.
Example: Auto-generate release notes on new tags
name:Releaseon:push:tags:['v*']permissions:contents:writejobs:release:runs-on:ubuntu-lateststeps:- name:Checkoutuses:actions/checkout@v4with:fetch-depth:0- name:Generate release notesid:notesuses:pullfrog/pullfrog@v0with:prompt:| Generate release notes for ${{ github.ref_name }}.
Compare commits between this tag and the previous tag.
Format as markdown: summary paragraph, then ### Features, ### Fixes, ### Breaking Changes sections.
Omit empty sections. Be concise.env:ANTHROPIC_API_KEY:${{ secrets.ANTHROPIC_API_KEY }}# write to file to avoid shell escaping issues with special characters- name:Create GitHub releaserun:| notesfile="$RUNNER_TEMP/release-notes-$GITHUB_RUN_ID.md"
printf '%s' "$NOTES" > "$notesfile"
gh release create ${{ github.ref_name }} --title "${{ github.ref_name }}" --notes-file "$notesfile"env:GH_TOKEN:${{ github.token }}NOTES:${{ steps.notes.outputs.result }}
Example: Structured Output with Zod Schema
You can force the agent to return structured JSON output by providing a JSON schema. This allows you to reliably parse and use the agent's response in subsequent workflow steps.
You can define your JSON schema directly or uou can use any validation library that converts to JSON Schema. Here's an example using Zod:
name:Release Checkon:pull_request:types:[closed]jobs:check-release:if:github.event.pull_request.merged == trueruns-on:ubuntu-lateststeps:- uses:actions/checkout@v4- name:Install dependenciesrun:npm install --no-save --no-package-lock zod @actions/core- name:Generate Schemaid:schemarun:| node -e '
import { z } from "zod";
import { setOutput } from "@actions/core";
const schema = z.object({
version: z.string().describe("Semantic version number (e.g. 1.0.0)"),
isBreaking: z.boolean().describe("Whether this release contains breaking changes"),
changelog: z.array(z.string()).describe("List of changes in this release"),
});
setOutput("schema", JSON.stringify(z.toJSONSchema(schema)));
'- name:Analyze PRid:analysisuses:pullfrog/pullfrog@v0with:prompt:| Analyze this PR and determine semantic versioning impact.
Return a JSON object matching the provided schema.output_schema:${{ steps.schema.outputs.schema }}env:ANTHROPIC_API_KEY:${{ secrets.ANTHROPIC_API_KEY }}- name:Process Resultrun:| # Parse the JSON result using fromJSON()
echo "Version: ${{ fromJSON(steps.analysis.outputs.result).version }}"
echo "Breaking: ${{ fromJSON(steps.analysis.outputs.result).isBreaking }}"