pullfrog 0.1.34 → 0.1.36

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/internal.js CHANGED
@@ -50,37 +50,40 @@ var providers = {
50
50
  managedCredentials: ["CODEX_AUTH_JSON"],
51
51
  models: {
52
52
  gpt: {
53
- displayName: "GPT",
54
- resolve: "openai/gpt-5.5",
55
- openRouterResolve: "openrouter/openai/gpt-5.5",
53
+ displayName: "GPT Sol",
54
+ resolve: "openai/gpt-5.6-sol",
55
+ openRouterResolve: "openrouter/openai/gpt-5.6-sol",
56
56
  preferred: true,
57
- subagentModel: "gpt-5.4"
57
+ subagentModel: "gpt-terra"
58
58
  },
59
+ // Sol served at reasoning.mode=pro — same $/token as Sol, just more tokens
60
+ // burned; not a pricier premium tier. models.dev has no -pro id, so direct-key
61
+ // (BYOK) resolves to plain Sol; only the Router/OpenRouter path gets sol-pro.
59
62
  "gpt-pro": {
60
- displayName: "GPT Pro",
61
- resolve: "openai/gpt-5.5-pro",
62
- openRouterResolve: "openrouter/openai/gpt-5.5-pro",
63
+ displayName: "GPT Sol Pro",
64
+ description: "Maximum reasoning effort",
65
+ resolve: "openai/gpt-5.6-sol",
66
+ openRouterResolve: "openrouter/openai/gpt-5.6-sol-pro",
63
67
  subagentModel: "gpt"
64
68
  },
65
- // hidden subagent target `gpt` lenses run against this. surfacing
66
- // it in the picker would just confuse users (it's the prior-flagship,
67
- // and they already have `gpt` and `gpt-mini` to choose from).
68
- "gpt-5.4": {
69
- displayName: "GPT 5.4",
70
- resolve: "openai/gpt-5.4",
71
- openRouterResolve: "openrouter/openai/gpt-5.4",
72
- hidden: true
69
+ // gpt-5.6's balanced mid-tier (Sol/Terra/Luna are durable capability tiers,
70
+ // not version bumps). selectable on its own and doubles as `gpt`'s cheaper
71
+ // lens-fanout subagent replaces the old hidden gpt-5.4 subagent target.
72
+ "gpt-terra": {
73
+ displayName: "GPT Terra",
74
+ resolve: "openai/gpt-5.6-terra",
75
+ openRouterResolve: "openrouter/openai/gpt-5.6-terra"
73
76
  },
74
77
  "gpt-mini": {
75
- displayName: "GPT Mini",
76
- resolve: "openai/gpt-5.4-mini",
77
- openRouterResolve: "openrouter/openai/gpt-5.4-mini"
78
+ displayName: "GPT Luna",
79
+ resolve: "openai/gpt-5.6-luna",
80
+ openRouterResolve: "openrouter/openai/gpt-5.6-luna"
78
81
  },
79
82
  // legacy aliases — openai unified the codex line into the main GPT family
80
83
  // and is shutting down every "-codex" snapshot on 2026-07-23. transparently
81
84
  // upgrade existing users via the fallback chain. UI display sites resolve
82
85
  // to the terminal alias's label (so dropdown trigger + PR footers show
83
- // "GPT" / "GPT Mini", not the historical name).
86
+ // "GPT Sol" / "GPT Luna", not the historical name).
84
87
  "gpt-codex": {
85
88
  displayName: "GPT Codex",
86
89
  resolve: "openai/gpt-5.3-codex",
@@ -93,6 +96,13 @@ var providers = {
93
96
  openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini",
94
97
  fallback: "openai/gpt-mini"
95
98
  },
99
+ // dropped hidden subagent tier — folds stored pins forward to gpt (Sol).
100
+ "gpt-5.4": {
101
+ displayName: "GPT 5.4",
102
+ resolve: "openai/gpt-5.4",
103
+ openRouterResolve: "openrouter/openai/gpt-5.4",
104
+ fallback: "openai/gpt"
105
+ },
96
106
  o3: {
97
107
  displayName: "O3",
98
108
  resolve: "openai/o3",
@@ -222,28 +232,29 @@ var providers = {
222
232
  openRouterResolve: "openrouter/anthropic/claude-haiku-4.5"
223
233
  },
224
234
  gpt: {
225
- displayName: "GPT",
226
- resolve: "opencode/gpt-5.5",
227
- openRouterResolve: "openrouter/openai/gpt-5.5",
228
- subagentModel: "gpt-5.4"
235
+ displayName: "GPT Sol",
236
+ resolve: "opencode/gpt-5.6-sol",
237
+ openRouterResolve: "openrouter/openai/gpt-5.6-sol",
238
+ subagentModel: "gpt-terra"
229
239
  },
240
+ // see openai/gpt-pro — Zen has no -pro id, so direct resolves to plain Sol.
230
241
  "gpt-pro": {
231
- displayName: "GPT Pro",
232
- resolve: "opencode/gpt-5.5-pro",
233
- openRouterResolve: "openrouter/openai/gpt-5.5-pro",
242
+ displayName: "GPT Sol Pro",
243
+ description: "Maximum reasoning effort",
244
+ resolve: "opencode/gpt-5.6-sol",
245
+ openRouterResolve: "openrouter/openai/gpt-5.6-sol-pro",
234
246
  subagentModel: "gpt"
235
247
  },
236
- // hidden subagent target — see openai provider above for context.
237
- "gpt-5.4": {
238
- displayName: "GPT 5.4",
239
- resolve: "opencode/gpt-5.4",
240
- openRouterResolve: "openrouter/openai/gpt-5.4",
241
- hidden: true
248
+ // gpt-5.6 balanced mid-tierselectable + `gpt`'s subagent. see openai above.
249
+ "gpt-terra": {
250
+ displayName: "GPT Terra",
251
+ resolve: "opencode/gpt-5.6-terra",
252
+ openRouterResolve: "openrouter/openai/gpt-5.6-terra"
242
253
  },
243
254
  "gpt-mini": {
244
- displayName: "GPT Mini",
245
- resolve: "opencode/gpt-5.4-mini",
246
- openRouterResolve: "openrouter/openai/gpt-5.4-mini"
255
+ displayName: "GPT Luna",
256
+ resolve: "opencode/gpt-5.6-luna",
257
+ openRouterResolve: "openrouter/openai/gpt-5.6-luna"
247
258
  },
248
259
  // legacy aliases — see openai provider above for context.
249
260
  "gpt-codex": {
@@ -258,6 +269,13 @@ var providers = {
258
269
  openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini",
259
270
  fallback: "opencode/gpt-mini"
260
271
  },
272
+ // dropped hidden subagent tier — folds stored pins forward to gpt (Sol).
273
+ "gpt-5.4": {
274
+ displayName: "GPT 5.4",
275
+ resolve: "opencode/gpt-5.4",
276
+ openRouterResolve: "openrouter/openai/gpt-5.4",
277
+ fallback: "opencode/gpt"
278
+ },
261
279
  "gemini-pro": {
262
280
  displayName: "Gemini Pro",
263
281
  resolve: "opencode/gemini-3.1-pro",
@@ -374,29 +392,34 @@ var providers = {
374
392
  resolve: "openrouter/~anthropic/claude-haiku-latest",
375
393
  openRouterResolve: "openrouter/~anthropic/claude-haiku-latest"
376
394
  },
395
+ // pinned to the explicit gpt-5.6 tiers (not the ~openai/gpt-latest rolling
396
+ // alias): after the Sol/Terra/Luna rename, ~gpt-mini-latest no longer maps
397
+ // to Luna, so rolling aliases would silently diverge `gpt`/`gpt-mini` from
398
+ // the chosen tiers across funding paths.
377
399
  gpt: {
378
- displayName: "GPT",
379
- resolve: "openrouter/~openai/gpt-latest",
380
- openRouterResolve: "openrouter/~openai/gpt-latest",
381
- subagentModel: "gpt-5.4"
400
+ displayName: "GPT Sol",
401
+ resolve: "openrouter/openai/gpt-5.6-sol",
402
+ openRouterResolve: "openrouter/openai/gpt-5.6-sol",
403
+ subagentModel: "gpt-terra"
382
404
  },
405
+ // see openai/gpt-pro. openrouter serves sol-pro directly on both routes.
383
406
  "gpt-pro": {
384
- displayName: "GPT Pro",
385
- resolve: "openrouter/openai/gpt-5.5-pro",
386
- openRouterResolve: "openrouter/openai/gpt-5.5-pro",
407
+ displayName: "GPT Sol Pro",
408
+ description: "Maximum reasoning effort",
409
+ resolve: "openrouter/openai/gpt-5.6-sol-pro",
410
+ openRouterResolve: "openrouter/openai/gpt-5.6-sol-pro",
387
411
  subagentModel: "gpt"
388
412
  },
389
- // hidden subagent target — see openai provider above for context.
390
- "gpt-5.4": {
391
- displayName: "GPT 5.4",
392
- resolve: "openrouter/openai/gpt-5.4",
393
- openRouterResolve: "openrouter/openai/gpt-5.4",
394
- hidden: true
413
+ // gpt-5.6 balanced mid-tierselectable + `gpt`'s subagent. see openai above.
414
+ "gpt-terra": {
415
+ displayName: "GPT Terra",
416
+ resolve: "openrouter/openai/gpt-5.6-terra",
417
+ openRouterResolve: "openrouter/openai/gpt-5.6-terra"
395
418
  },
396
419
  "gpt-mini": {
397
- displayName: "GPT Mini",
398
- resolve: "openrouter/~openai/gpt-mini-latest",
399
- openRouterResolve: "openrouter/~openai/gpt-mini-latest"
420
+ displayName: "GPT Luna",
421
+ resolve: "openrouter/openai/gpt-5.6-luna",
422
+ openRouterResolve: "openrouter/openai/gpt-5.6-luna"
400
423
  },
401
424
  // legacy aliases — see openai provider for context.
402
425
  "gpt-codex": {
@@ -411,6 +434,13 @@ var providers = {
411
434
  openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini",
412
435
  fallback: "openrouter/gpt-mini"
413
436
  },
437
+ // dropped hidden subagent tier — folds stored pins forward to gpt (Sol).
438
+ "gpt-5.4": {
439
+ displayName: "GPT 5.4",
440
+ resolve: "openrouter/openai/gpt-5.4",
441
+ openRouterResolve: "openrouter/openai/gpt-5.4",
442
+ fallback: "openrouter/gpt"
443
+ },
414
444
  "o4-mini": {
415
445
  displayName: "O4 Mini",
416
446
  resolve: "openrouter/openai/o4-mini",
@@ -499,6 +529,7 @@ var modelAliases = Object.entries(providers).flatMap(
499
529
  slug: `${providerKey}/${modelId}`,
500
530
  provider: providerKey,
501
531
  displayName: def.displayName,
532
+ description: def.description,
502
533
  resolve: def.resolve,
503
534
  openRouterResolve: def.openRouterResolve,
504
535
  preferred: def.preferred ?? false,
@@ -837,20 +868,15 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
837
868
  - **immediately** call \`${t("resolve_review_thread")}\` with that thread's \`thread=\` value as \`thread_id\`. Resolve every thread where you (a) made the requested code change in full \u2014 partial fixes leave the thread open \u2014 OR (b) replied with a substantive answer the user explicitly asked for. Do NOT resolve threads where you pushed back on the request and the disagreement is unresolved; leave those open for the human to mediate.
838
869
  - call \`${t("report_progress")}\` with a brief summary`
839
870
  },
840
- // Review and IncrementalReview use a 0-or-2+ lens pattern. The default is
841
- // 0 lenses (orchestrator handles the review solo). Multi-lens (2+
842
- // reviewfrog subagents in parallel) only fires for substantive PRs or
843
- // high-stakes-subsystem touches — and when it fires, ALL lenses must
844
- // dispatch in a single assistant turn or the parallelism win disappears.
845
- // We never dispatch exactly one lens: a single lens is just a worse,
846
- // slower version of doing the work yourself.
871
+ // Review and IncrementalReview route the minimum reviewfrog specialists
872
+ // needed to cover unresolved, disposition-changing hypotheses. Most runs
873
+ // use zero or one; multiple orthogonal hypotheses dispatch in parallel.
847
874
  //
848
875
  // Build mode self-review is a different problem shape: the orchestrator
849
876
  // wrote the code, so bias-mitigation comes from delegating to one
850
877
  // fresh-eyes subagent that doesn't share the implementation context. A
851
- // single subagent there is appropriate; the 0-or-2+ rule applies only to
852
- // the Review/IncrementalReview lens fan-out where independence between
853
- // perspectives is what's being purchased.
878
+ // single subagent there is appropriate. Review-mode specialist routing
879
+ // instead scales with the unresolved hypotheses in someone else's diff.
854
880
  //
855
881
  // Severity categorization is split across two surfaces: the opening
856
882
  // callout (CAUTION/IMPORTANT/ℹ️/✅) sets the review's overall tier, and
@@ -865,11 +891,11 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
865
891
 
866
892
  1. **task list**: create your task list for this run as your first action.
867
893
 
868
- 2. **checkout**: call \`${t("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.
894
+ 2. **checkout**: call \`${t("checkout_pr")}\` \u2014 this returns PR metadata, a \`diffPath\`, and a supplemental \`impactPath\` when change-impact extraction is enabled. read the complete raw diff end-to-end, beginning with the TOC and using its file line ranges as your coverage checklist. only after that, use \`impactPath\` as an explicitly incomplete list of reference leads; it never replaces raw-diff reading or establishes coverage.
869
895
 
870
896
  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). pull as much context as you need to render a confident, well-grounded review: read related files, grep for callers of changed symbols, check tests that exercise the touched paths, fetch related GitHub state. **you are the synthesizer** \u2014 never delegate understanding to subagents.
871
897
 
872
- if the PR is **genuinely trivial**, skip the fan-out entirely and submit a \`No new issues found.\` review per step 7.
898
+ if the PR is **genuinely trivial**, skip specialists entirely and submit a \`No new issues found.\` review per step 7.
873
899
 
874
900
  "Genuinely trivial" (skip):
875
901
  - single-word doc typo, whitespace/format-only, comment-only across any number of files
@@ -888,22 +914,24 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
888
914
  - any "typo fix" in user-facing copy that changes meaning ("approved" \u2192 "denied")
889
915
  - mixed diffs where a semantic 1-liner is buried in whitespace/formatting changes
890
916
 
891
- 4. **lens decision \u2014 0 or 2+, NEVER 1**.
917
+ 4. **specialist decision \u2014 minimum hypothesis coverage**.
892
918
 
893
- The default is **0 lenses**: handle the review yourself end-to-end. Most PRs land here.
919
+ After full-diff coverage and triage, identify the load-bearing questions you still cannot resolve confidently yourself. A specialist hypothesis is load-bearing only when its answer could yield an actionable finding that changes the review disposition and warrants independent investigation, and falsifiable only when the specialist can return evidence that supports or refutes it. Generic requests for extra confidence, polish, or "another look" do not qualify.
894
920
 
895
- Dispatch **2+ \`${REVIEWER_AGENT_NAME}\` lenses in parallel** ONLY when ALL of the following are true:
896
- - the PR is substantive (>5 files changed AND >200 net lines), OR touches a high-stakes subsystem (auth, billing, payments, schema migration, webhooks, secrets, RBAC, multi-tenant isolation, cron/scheduling)
897
- - you can name 2+ distinct concrete failure modes that warrant independent lenses (one lens per failure mode; orthogonal, not overlapping)
898
- - parallel-orchestrated independent perspectives meaningfully outperform what you'd find solo
921
+ Route the **minimum number of \`${REVIEWER_AGENT_NAME}\` specialists** needed to cover those unresolved hypotheses. Most reviews need **0 or 1**:
922
+ - dispatch 0 when you can resolve every disposition-changing question directly
923
+ - dispatch 1 when exactly one falsifiable, load-bearing hypothesis warrants independent investigation
924
+ - dispatch 2+ in parallel when multiple orthogonal load-bearing hypotheses remain, or when the user explicitly requests an exhaustive or multi-angle review
899
925
 
900
- **NEVER dispatch exactly one lens.** A single lens is just a more expensive version of doing the work yourself with a worse model \u2014 it adds wall time and a context-handoff for no orthogonality benefit. Either you have at least two genuinely independent failure-mode hypotheses (dispatch all in one turn), or you don't (do the review yourself).
926
+ **There is NO one-specialist cap or fixed maximum.** Cover every orthogonal load-bearing hypothesis that remains; do not collapse multiple real questions into one broad prompt just to reduce the count. There is also no file-count, line-count, schema, quota, or hard-budget threshold \u2014 diff size is not a proxy for review uncertainty.
901
927
 
902
- When you do go multi-lens, lens framings come in two flavors:
928
+ The primary reviewer remains responsible for reading the complete raw diff, investigating surrounding code, validating every returned finding, and synthesizing the final review. Specialist reads supplement that work; they never replace it or satisfy the primary's diff-coverage obligation.
929
+
930
+ Specialist hypotheses can draw on two kinds of framing:
903
931
  - **themed lenses** \u2014 a perspective applied across the whole diff (correctness, security, user-journey, performance, etc.).
904
932
  - **subsystem lenses** \u2014 a domain-scoped frame for high-stakes subsystems the PR touches (e.g. "the auth lens", "the billing lens", "the schema-migration lens"). **for high-stakes domains, lead with the subsystem lens rather than the generic themed equivalent** \u2014 "billing-subsystem" outperforms "correctness on billing code" because the framing primes the subagent to remember domain-specific failure modes (double-charges, refund races, currency rounding, dispute flows) the generic lens misses.
905
933
 
906
- starter menu (combine, omit, or invent your own):
934
+ starter menu for identifying hypotheses (combine, omit, or invent your own; do not dispatch a bare menu label without a falsifiable question):
907
935
  - **correctness & invariants** \u2014 bugs, races, error handling, edge cases, state-machine boundaries
908
936
  - **impact** \u2014 stale references in code/tests/docs/configs/UI after rename/remove
909
937
  - **research-validated assumptions** \u2014 third-party API contracts, SDK semantics, framework directives, version-gated behavior. **only pick when the PR's correctness depends on the contract behaving a specific way** \u2014 not when the API is merely used. The bar is "if the third-party contract differs from what the diff assumes, the PR is incorrect." When dispatched, the subagent must verify load-bearing claims via web search and quote source URLs.
@@ -918,30 +946,27 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
918
946
 
919
947
  The only subagent type is \`${REVIEWER_AGENT_NAME}\` \u2014 used for lens judgment work ("is this safe / correct / well-tested?"), runs on a mid-tier model.
920
948
 
921
- 5. **fan out (only if step 4 said 2+ lenses)**: dispatch every \`${REVIEWER_AGENT_NAME}\` subagent for this run **IN A SINGLE ASSISTANT TURN, AS MULTIPLE PARALLEL TASK TOOL_USE BLOCKS IN ONE MESSAGE.**
922
-
923
- \u26A0\uFE0F CRITICAL \u2014 PARALLELISM IS THE ONLY REASON LENSES EXIST. \u26A0\uFE0F
924
- The default tool-call behavior of Claude Code (and most agent runtimes) is **serial dispatch**: emit one Task call, await result, emit next, await, etc. This collapses your fan-out into a sequential review where each lens adds N \xD7 (orchestrator-think-time + lens-execution-time) to wall time. **YOU MUST OVERRIDE THIS DEFAULT.** Emit ALL of your Task tool_use blocks in the SAME assistant message, BEFORE you read ANY result from ANY of them. If you find yourself emitting one Task call, then thinking about the result, then emitting another \u2014 STOP and re-issue them all together. The whole point of going multi-lens is the wall-clock speedup from parallel execution; serial dispatch defeats it entirely.
949
+ 5. **dispatch specialists (only if step 4 found unresolved hypotheses)**: dispatch one \`${REVIEWER_AGENT_NAME}\` for one hypothesis. For 2+ hypotheses, emit every Task tool_use block **IN A SINGLE ASSISTANT TURN** before reading any result so the investigations run in parallel rather than serially.
925
950
 
926
- \u2705 Right pattern: one assistant turn with N Task tool_use blocks \u2192 wait \u2192 N results arrive together \u2192 aggregate.
927
- \u274C Wrong pattern: turn 1 = Task(lens A) \u2192 turn 2 (after A's result) = Task(lens B) \u2192 turn 3 (after B's result) = Task(lens C). This is the failure mode. Do not do this.
951
+ \u2705 Right multi-specialist pattern: one assistant turn with N Task tool_use blocks \u2192 wait \u2192 N results arrive together \u2192 aggregate.
952
+ \u274C Wrong multi-specialist pattern: Task(hypothesis A) \u2192 wait for A \u2192 Task(hypothesis B).
928
953
 
929
954
  You can also include your own \`read\` / \`grep\` / \`webfetch\` calls in the SAME turn as the parallel \`${REVIEWER_AGENT_NAME}\` dispatches \u2014 concurrent context-pulling on the orchestrator side runs in parallel with the lens fan-out and costs zero extra wall time.
930
955
 
931
- 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 the fan-out entirely on a single subagent failure. each subagent gets:
956
+ if a specialist errors out, times out, or returns nothing usable, retry it once with the same hypothesis. if it still fails, attempt to resolve the hypothesis yourself; if it remains disposition-changing and unresolved, surface the limitation and do not approve. each specialist gets:
932
957
  - **the absolute \`diffPath\` (and \`incrementalDiffPath\` if available) from step 2's \`${t("checkout_pr")}\` return, named verbatim in the dispatch prompt** (e.g. \`diffPath: /tmp/pullfrog-XXXX/pr-NNN-SHA.diff\`). the reviewer's baked-in system prompt selects its FIRST action on this token \u2014 paraphrasing ("review the diff", "look at this PR") sends it down the \`git diff origin/<base>\` fallback, which fails on shallow GHA checkouts. the subagent \`read\`s those files for scope; it must NOT re-derive the diff via \`git diff\` (bare \`git diff origin/<base>\` is symmetric and pulls in the inverse of any commits that landed on \`<base>\` since the branch forked \u2014 pure noise, and the git tool rejects it). reading and codebase exploration are still its job.
933
- - **only one lens** \u2014 never a multi-section "review for X, Y, and Z" prompt
934
- - **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\`.
958
+ - **exactly one falsifiable hypothesis with explicit scope boundaries** \u2014 ask for evidence that supports or refutes it, never a broad "review for X, Y, and Z" prompt
959
+ - **a Task \`description\` set to a short hypothesis label** (e.g. \`"webhook-replay"\`, \`"billing-rounding"\`) \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\`.
935
960
  - if the lens touches external contracts, instruct the subagent to verify load-bearing claims via web search rather than trust training data, and to quote source URLs in its reasoning. action runs are non-interactive \u2014 there's no human in the loop to catch "I'm pretty sure Stripe does X."
936
- - ask the subagent to report findings with file paths and NEW line numbers from the diff so you can anchor inline comments without re-reading the entire diff.
961
+ - ask the subagent to report findings with file paths and NEW line numbers from the diff so you can validate and anchor them. you must still read the complete diff yourself.
937
962
 
938
963
  delegation discipline:
939
- - do NOT summarize the PR for them (biases toward a validation frame)
964
+ - do NOT summarize the PR for them (a lossy summary biases toward a validation frame; the raw diff is the source)
940
965
  - do NOT hand them a curated reading list (let them discover scope)
941
966
  - do NOT pre-shape their output with a finding schema
942
967
  - do NOT mention the other lenses (independence is the point \u2014 overlapping findings are a strong signal)
943
968
 
944
- 6. **aggregate & draft**: when the fan-out lands, 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.
969
+ 6. **aggregate & draft**: when specialist results land, merge findings; de-dup overlaps (two specialists 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.
945
970
 
946
971
  **Hunt for non-anchored concerns before drafting.** After collecting your anchored findings, deliberately scan for concerns that have no specific line to point at \u2014 typically: deletion / cleanup plans for code the diff replaces or shadows; rollout sequencing (what happens to in-flight state during deploy / revert?); coverage gaps the diff implies but doesn't add; scope questions that only the human can answer (e.g. is the legacy path going away or is this a long-term dual track?); architectural risks the diff opens up that aren't a single-line bug. On substantial PRs (migrations, refactors, multi-file rewrites, version bumps that change runtime semantics), at least one such concern almost always exists; if you can't think of any, your bar is probably too high.
947
972
 
@@ -975,10 +1000,10 @@ For simple, well-defined tasks, skip the plan phase and go straight to build.`
975
1000
 
976
1001
  ${PR_SUMMARY_FORMAT}`
977
1002
  },
978
- // IncrementalReview shares Review's 0-or-2+ lens pattern AND its body
979
- // format (PR_SUMMARY_FORMAT), scoped to the incremental delta against the
980
- // prior pullfrog review. The "issues must be NEW since the last Pullfrog
981
- // review" filter lives at aggregation time (step 8), NOT in the subagent
1003
+ // IncrementalReview shares Review's minimum hypothesis-covering specialist
1004
+ // routing and body format, scoped to the incremental delta against the
1005
+ // prior Pullfrog review. The "issues must be NEW since the last Pullfrog
1006
+ // review" filter lives at aggregation time, NOT in the subagent
982
1007
  // prompt — pushing the filter into subagents matches the canonical anneal
983
1008
  // anti-pattern of "list known pre-existing failures — don't flag these"
984
1009
  // and suppresses signal on regressions the new commits amplified. A
@@ -993,9 +1018,9 @@ ${PR_SUMMARY_FORMAT}`
993
1018
 
994
1019
  1. **task list**: create your task list for this run as your first action.
995
1020
 
996
- 2. **checkout**: call \`${t("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.
1021
+ 2. **checkout**: call \`${t("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), \`incrementalDiffPath\` (changes since last reviewed version, if available), and a supplemental \`impactPath\` when change-impact extraction is enabled.
997
1022
 
998
- 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.
1023
+ 3. **incremental scope**: if \`incrementalDiffPath\` is present, read it FIRST to see what changed since the last review. this is a range-diff that isolates the net changes, filtering out base branch noise. then read the authoritative full diff end-to-end, beginning with the TOC and using its line ranges as your coverage checklist. if no incremental diff is present, start with the full-diff TOC, determine what changed since Pullfrog's most recent review, and complete the raw-diff read. only after establishing that authoritative scope and completing raw-diff coverage, use \`impactPath\` as an explicitly incomplete list of reference leads; it never replaces raw-diff reading or establishes coverage.
999
1024
 
1000
1025
  4. **prior feedback \u2014 read AND retire it**: fetch previous reviews via \`${t("list_pull_request_reviews")}\`, then call \`${t("get_review_comments")}\` on each prior Pullfrog review. Each thread renders as a section whose first line is a fenced tag \`comment author=<login> id=<fullDatabaseId> review=<reviewId> thread=<graphqlId>\`; section headers carry \`[RESOLVED]\` / \`[OUTDATED]\` when relevant. For every **open, Pullfrog-originated** thread, decide and act:
1001
1026
 
@@ -1009,49 +1034,48 @@ ${PR_SUMMARY_FORMAT}`
1009
1034
 
1010
1035
  5. **triage**: orient on the *incremental* changes \u2014 domain, seams, external contracts, user-facing surfaces. pull as much context as you need to render a confident review: read related files, grep for callers of changed symbols, check tests that exercise the touched paths. **you are the synthesizer.**
1011
1036
 
1012
- if the incremental changes are **genuinely trivial**, skip the fan-out entirely and jump to step 10's non-substantive path (do NOT submit a review).
1037
+ if the incremental changes are **genuinely trivial**, skip specialists entirely and jump to step 10's non-substantive path (do NOT submit a review).
1013
1038
 
1014
1039
  "Genuinely trivial" (skip): formatting/comment tweaks, import reordering, lockfile regen, mechanical rename of import paths, whitespace-only.
1015
1040
  "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.
1016
1041
  When unsure, treat as non-trivial.
1017
1042
 
1018
- 6. **lens decision \u2014 0 or 2+, NEVER 1**.
1043
+ 6. **specialist decision \u2014 minimum hypothesis coverage**.
1019
1044
 
1020
- The default is **0 lenses**: handle the re-review yourself end-to-end. Most incremental reviews land here \u2014 especially thread-reply re-reviews where the user is asking "did you address X?" rather than "review the diff again."
1045
+ After full-diff coverage and triage, identify the load-bearing questions about the incremental changes that you still cannot resolve confidently yourself. A specialist hypothesis is load-bearing only when its answer could yield an actionable new finding that changes the review disposition and warrants independent investigation, and falsifiable only when the specialist can return evidence that supports or refutes it. Generic requests for extra confidence, polish, or "another look" do not qualify.
1021
1046
 
1022
- Dispatch **2+ \`${REVIEWER_AGENT_NAME}\` lenses in parallel** ONLY when ALL of the following are true:
1023
- - the incremental changes are substantive (>5 files changed AND >200 net new lines), OR touch a high-stakes subsystem (auth, billing, payments, schema migration, webhooks, secrets, RBAC, multi-tenant isolation, cron/scheduling)
1024
- - you can name 2+ distinct concrete failure modes the new commits plausibly introduce that warrant independent lenses
1025
- - parallel-orchestrated independent perspectives meaningfully outperform what you'd find solo
1047
+ Route the **minimum number of \`${REVIEWER_AGENT_NAME}\` specialists** needed to cover those unresolved hypotheses. Most incremental reviews need **0 or 1**, especially thread-reply re-reviews:
1048
+ - dispatch 0 when you can resolve every disposition-changing question directly
1049
+ - dispatch 1 when exactly one falsifiable, load-bearing hypothesis warrants independent investigation
1050
+ - dispatch 2+ in parallel when multiple orthogonal load-bearing hypotheses remain, or when the user explicitly requests an exhaustive or multi-angle review
1026
1051
 
1027
- **NEVER dispatch exactly one lens.** Single-lens dispatch adds wall time and cost for no orthogonality benefit. Either go multi-lens (\u22652 in parallel) or do the re-review yourself.
1052
+ **There is NO one-specialist cap or fixed maximum.** Cover every orthogonal load-bearing hypothesis that remains; do not collapse multiple real questions into one broad prompt just to reduce the count. There is also no file-count, line-count, schema, quota, or hard-budget threshold \u2014 diff size is not a proxy for review uncertainty.
1028
1053
 
1029
- Lens framing follows Review mode: themed lenses (correctness, security, etc.) and subsystem lenses (auth, billing, schema-migration, etc.) \u2014 for high-stakes domains lead with the subsystem lens.
1054
+ The primary reviewer remains responsible for reading the complete raw full diff plus the incremental diff, investigating surrounding code, validating every returned finding, and synthesizing the final review. Specialist reads supplement that work; they never replace it or satisfy the primary's diff-coverage obligation.
1030
1055
 
1031
- 7. **fan out (only if step 6 said 2+ lenses)**: dispatch every \`${REVIEWER_AGENT_NAME}\` subagent for this run **IN A SINGLE ASSISTANT TURN, AS MULTIPLE PARALLEL TASK TOOL_USE BLOCKS IN ONE MESSAGE.**
1056
+ Specialist hypotheses can draw on Review mode's themed or subsystem framings, but every dispatch must turn the framing into one falsifiable question with explicit scope boundaries.
1032
1057
 
1033
- \u26A0\uFE0F CRITICAL \u2014 PARALLELISM IS THE ONLY REASON LENSES EXIST. \u26A0\uFE0F
1034
- Default tool-call behavior is **serial dispatch**: emit one Task call, await result, emit next, await, etc. This collapses your fan-out into a sequential review where each lens adds N \xD7 (orchestrator-think-time + lens-execution-time) to wall time. **YOU MUST OVERRIDE THIS DEFAULT.** Emit ALL of your Task tool_use blocks in the SAME assistant message, BEFORE you read ANY result from ANY of them.
1058
+ 7. **dispatch specialists (only if step 6 found unresolved hypotheses)**: dispatch one \`${REVIEWER_AGENT_NAME}\` for one hypothesis. For 2+ hypotheses, emit every Task tool_use block **IN A SINGLE ASSISTANT TURN** before reading any result so the investigations run in parallel rather than serially.
1035
1059
 
1036
- \u2705 Right pattern: one assistant turn with N Task tool_use blocks \u2192 wait \u2192 N results arrive together \u2192 aggregate.
1037
- \u274C Wrong pattern: turn 1 = Task(lens A) \u2192 turn 2 (after A's result) = Task(lens B). This is the failure mode.
1060
+ \u2705 Right multi-specialist pattern: one assistant turn with N Task tool_use blocks \u2192 wait \u2192 N results arrive together \u2192 aggregate.
1061
+ \u274C Wrong multi-specialist pattern: Task(hypothesis A) \u2192 wait for A \u2192 Task(hypothesis B).
1038
1062
 
1039
1063
  You can also include your own \`read\` / \`grep\` / \`webfetch\` calls in the SAME turn as the parallel \`${REVIEWER_AGENT_NAME}\` dispatches.
1040
1064
 
1041
- 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. each subagent gets:
1065
+ if a specialist errors out, times out, or returns nothing usable, retry it once with the same hypothesis. if it still fails, attempt to resolve the hypothesis yourself; if it remains disposition-changing and unresolved, surface the limitation and do not approve. each specialist gets:
1042
1066
  - **the absolute diff path(s) from step 2's \`${t("checkout_pr")}\` return, named verbatim in the dispatch prompt.** when \`incrementalDiffPath\` is present, name BOTH (\`incrementalDiffPath: /tmp/.../pr-NNN-SHA-incremental.diff\` then \`diffPath: /tmp/.../pr-NNN-SHA.diff\`) \u2014 the reviewer's baked-in prompt reads incremental first and uses full for context; when only \`diffPath\` exists, name it alone. the subagent \`read\`s those files; it must NOT re-derive via \`git diff\` (bare \`git diff origin/<base>\` is symmetric and pulls in the inverse of base-branch progress \u2014 pure noise, and the git tool rejects it), and paraphrasing ("review the new commits") sends it down that fallback, which also fails on shallow GHA checkouts. 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 8), not in the subagent prompt.
1043
- - **only one lens** \u2014 never a multi-section "review for X, Y, and Z" prompt
1044
- - **a Task \`description\` set to the lens name** \u2014 the harness reads this field to label log lines so parallel runs can be told apart.
1067
+ - **exactly one falsifiable hypothesis with explicit scope boundaries** \u2014 ask for evidence that supports or refutes it, never a broad "review for X, Y, and Z" prompt
1068
+ - **a Task \`description\` set to a short hypothesis label** \u2014 the harness reads this field to label log lines so parallel runs can be told apart.
1045
1069
  - if the lens touches external contracts, instruct the subagent to verify load-bearing claims via web search and quote source URLs.
1046
- - ask the subagent to report findings with file paths and NEW line numbers from the full PR diff so you can anchor inline comments.
1070
+ - ask the subagent to report findings with file paths and NEW line numbers from the full PR diff so you can validate and anchor them. you must still read the complete incremental and full diff scope yourself.
1047
1071
 
1048
1072
  delegation discipline:
1049
- - do NOT summarize the changes for them (biases toward validation frame)
1073
+ - do NOT summarize the changes for them (a lossy summary biases toward a validation frame; the raw diff is the source)
1050
1074
  - do NOT hand them a curated reading list (let them discover scope)
1051
1075
  - do NOT pre-shape their output with a finding schema
1052
1076
  - do NOT mention the other lenses (independence is the point)
1053
1077
 
1054
- 8. **aggregate, draft, self-critique**: merge findings (yours + any subagent output if you went multi-lens); 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 \`${t("list_pull_request_reviews")}\` in step 4) and run \`git diff <prior-review-sha>..HEAD\` to isolate the lines added since that review.
1078
+ 8. **aggregate, draft, self-critique**: merge findings (yours + output from every specialist you dispatched); 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 \`${t("list_pull_request_reviews")}\` in step 4) and run \`git diff <prior-review-sha>..HEAD\` to isolate the lines added since that review.
1055
1079
 
1056
1080
  **Hunt for non-anchored concerns before drafting.** After collecting your anchored findings, deliberately scan for concerns that have no specific line to point at \u2014 typically: deletion / cleanup plans for code the new commits replace or shadow; rollout sequencing (what happens to in-flight state during deploy / revert?); coverage gaps the new commits imply but don't add; scope questions that only the human can answer (e.g. is the legacy path going away or is this a long-term dual track?); architectural risks the new commits open up that aren't a single-line bug. On substantial incremental diffs (migrations, refactors, multi-file rewrites, version bumps that change runtime semantics), at least one such concern almost always exists; if you can't think of any, your bar is probably too high.
1057
1081
 
@@ -1148,7 +1172,7 @@ ${PR_SUMMARY_FORMAT}`
1148
1172
 
1149
1173
  1. **task list**: create your task list for this run as your first action.
1150
1174
 
1151
- 2. Analyze the task. For simple operations (labeling, commenting, answering questions, running a single command), handle directly \u2014 but your answer only reaches the user through \`${t("report_progress")}\` (step 4); raw assistant text is discarded.
1175
+ 2. Analyze the task. For simple operations (labeling, answering questions, running a single command), handle directly \u2014 but your answer only reaches the user through \`${t("report_progress")}\` (step 4); raw assistant text is discarded. If a standalone comment on the current issue/PR is the task's sole requested deliverable, create that comment directly and skip \`${t("report_progress")}\`.
1152
1176
 
1153
1177
  3. For substantial work \u2014 code changes across multiple files, multi-step investigations:
1154
1178
  - plan your approach before starting
@@ -1158,8 +1182,8 @@ ${PR_SUMMARY_FORMAT}`
1158
1182
 
1159
1183
  4. Finalize:
1160
1184
  - if code changes were made, get them onto a pull request (new or existing) using ${signedCommits ? `\`${t("commit_changes")}\`` : `\`${t("push_branch")}\``} and \`${t("create_pull_request")}\` as needed. \`git status\` must be clean before you finish (see *SYSTEM* Git rules if this fails).
1161
- - call \`${t("report_progress")}\` once with results \u2014 include exact tool errors if push or PR creation failed
1162
- - if the task involved labeling, commenting, or other GitHub operations, perform those directly`
1185
+ - call \`${t("report_progress")}\` once with results \u2014 include exact tool errors if push or PR creation failed. skip this only when a standalone comment on the current target was the task's sole requested deliverable
1186
+ - if the task involved labeling or other GitHub operations, perform those directly`
1163
1187
  }
1164
1188
  ];
1165
1189
  }
@@ -31,6 +31,7 @@ export type CheckoutPrResult = {
31
31
  url: string;
32
32
  headRepo: string;
33
33
  diffPath: string;
34
+ impactPath?: string | undefined;
34
35
  incrementalDiffPath?: string | undefined;
35
36
  toc: string;
36
37
  commitCount: number;
@@ -9,7 +9,9 @@ export declare function getHttpStatus(err: unknown): number | undefined;
9
9
  * the single source of truth the subagent deny list derives from (see
10
10
  * action/agents/subagentToolGates.ts). general-purpose execution tools (`git`,
11
11
  * `shell`) are deliberately left unmarked: they can mutate but are allowed for
12
- * subagents and gated by command-arg validation instead.
12
+ * subagents and gated by command-arg validation instead. `readOnlyHint` is
13
+ * independent scheduler metadata and must only follow an implementation audit;
14
+ * the absence of `mutates` does not imply that a tool is read-only.
13
15
  */
14
16
  export type PullfrogTool = Tool<any, any> & {
15
17
  mutates?: boolean;
package/dist/models.d.ts CHANGED
@@ -28,6 +28,9 @@ export interface ModelAlias {
28
28
  provider: string;
29
29
  /** human-readable name shown in dropdowns */
30
30
  displayName: string;
31
+ /** optional one-line picker sub-label clarifying what the alias is (e.g.
32
+ * gpt-pro = "Maximum reasoning effort"). undefined for most aliases. */
33
+ description: string | undefined;
31
34
  /** concrete models.dev specifier, e.g. "anthropic/claude-opus-4-6". sentinel for routing entries — never passed to a CLI directly. */
32
35
  resolve: string;
33
36
  /** full models.dev specifier for the OpenRouter equivalent (undefined for free models and routing entries) */
@@ -48,12 +51,14 @@ export interface ModelAlias {
48
51
  * use as its lens-fanout subagent. e.g. claude-opus → "claude-sonnet". */
49
52
  subagentModel: string | undefined;
50
53
  /** hide from selectable lists (UI dropdowns, CLI pickers). does NOT affect
51
- * resolution — for that use `fallback`. used for internal-only tier targets
52
- * (e.g. gpt-5.4 as a subagent target without exposing it to users). */
54
+ * resolution — for that use `fallback`. used to keep a redundant alias out of
55
+ * pickers (e.g. the free `minimax-m2.5-free` duplicate). */
53
56
  hidden: boolean;
54
57
  }
55
58
  interface ModelDef {
56
59
  displayName: string;
60
+ /** optional one-line picker sub-label (e.g. "Maximum reasoning effort"). */
61
+ description?: string;
57
62
  /** concrete models.dev specifier, e.g. "anthropic/claude-opus-4-6" */
58
63
  resolve: string;
59
64
  /** full models.dev specifier for the OpenRouter equivalent, e.g. "openrouter/anthropic/claude-opus-4.6" */
@@ -119,7 +119,6 @@ export interface ToolState {
119
119
  lastProgressBody?: string;
120
120
  wasUpdated?: boolean;
121
121
  finalSummaryWritten?: boolean;
122
- answerCommentPosted?: boolean;
123
122
  existingPlanCommentId?: number;
124
123
  previousPlanBody?: string;
125
124
  summaryFilePath?: string;
@@ -0,0 +1,18 @@
1
+ import type { RestEndpointMethodTypes } from "@octokit/rest";
2
+ import type { ToolContext } from "../mcp/server.ts";
3
+ type PullFile = RestEndpointMethodTypes["pulls"]["listFiles"]["response"]["data"][number];
4
+ type CreateChangeImpactParams = {
5
+ files: PullFile[];
6
+ pullNumber: number;
7
+ baseSha: string;
8
+ headSha: string;
9
+ };
10
+ type ChangeImpactResult = {
11
+ path: string;
12
+ candidateCount: number;
13
+ renderedAtomCount: number;
14
+ referenceCount: number;
15
+ bytes: number;
16
+ };
17
+ export declare function createChangeImpactArtifact(ctx: ToolContext, params: CreateChangeImpactParams): Promise<ChangeImpactResult>;
18
+ export {};
@@ -31,7 +31,6 @@ export type DiffCoverageState = {
31
31
  tocEntries: DiffTocEntry[];
32
32
  coveredRanges: DiffLineRange[];
33
33
  coveragePreflightRan: boolean;
34
- lastBreakdown?: string | undefined;
35
34
  };
36
35
  export declare function countLines(params: {
37
36
  content: string;
@@ -54,10 +53,6 @@ export declare function recordDiffReadFromToolUse(params: {
54
53
  export declare function getDiffCoverageBreakdown(params: {
55
54
  state: DiffCoverageState;
56
55
  }): DiffCoverageBreakdown;
57
- export declare function renderDiffCoverageBreakdown(params: {
58
- diffPath: string;
59
- breakdown: DiffCoverageBreakdown;
60
- }): string;
61
56
  export declare function countLinesInRanges(params: {
62
57
  ranges: DiffLineRange[];
63
58
  }): number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.34",
3
+ "version": "0.1.36",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pullfrog": "dist/cli.mjs",