pullfrog 0.1.15 → 0.1.16

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.
@@ -3,7 +3,7 @@
3
3
  * Re-exports shared types, values, and utilities needed by the Next.js app.
4
4
  */
5
5
  export type { AuthorPermission, ModelAlias, ModelProvider, Payload, PayloadEvent, ProviderConfig, PushPermission, ShellPermission, ToolPermission, WriteablePayload, } from "../external.ts";
6
- export { DEFAULT_PROXY_MODEL, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, modelAliases, parseModel, providers, pullfrogMcpName, resolveCliModel, resolveDisplayAlias, resolveModelSlug, resolveOpenRouterModel, } from "../external.ts";
6
+ export { DEFAULT_PROXY_MODEL, getAutoSelectHintModel, getModelEnvVars, getModelManagedCredentials, getModelProvider, getProviderDisplayName, modelAliases, parseModel, providers, pullfrogMcpName, resolveCliModel, resolveDisplayAlias, resolveModelSlug, resolveOpenRouterModel, } from "../external.ts";
7
7
  export type { Mode } from "../modes.ts";
8
8
  export { modes } from "../modes.ts";
9
9
  export type { BuildPullfrogFooterParams, WorkflowRunFooterInfo, } from "../utils/buildPullfrogFooter.ts";
package/dist/internal.js CHANGED
@@ -11,8 +11,8 @@ var providers = {
11
11
  models: {
12
12
  "claude-opus": {
13
13
  displayName: "Claude Opus",
14
- resolve: "anthropic/claude-opus-4-7",
15
- openRouterResolve: "openrouter/anthropic/claude-opus-4.7",
14
+ resolve: "anthropic/claude-opus-4-8",
15
+ openRouterResolve: "openrouter/anthropic/claude-opus-4.8",
16
16
  preferred: true,
17
17
  subagentModel: "claude-sonnet"
18
18
  },
@@ -190,8 +190,8 @@ var providers = {
190
190
  },
191
191
  "claude-opus": {
192
192
  displayName: "Claude Opus",
193
- resolve: "opencode/claude-opus-4-7",
194
- openRouterResolve: "openrouter/anthropic/claude-opus-4.7",
193
+ resolve: "opencode/claude-opus-4-8",
194
+ openRouterResolve: "openrouter/anthropic/claude-opus-4.8",
195
195
  subagentModel: "claude-sonnet"
196
196
  },
197
197
  "claude-sonnet": {
@@ -323,8 +323,8 @@ var providers = {
323
323
  models: {
324
324
  "claude-opus": {
325
325
  displayName: "Claude Opus",
326
- resolve: "openrouter/anthropic/claude-opus-4.7",
327
- openRouterResolve: "openrouter/anthropic/claude-opus-4.7",
326
+ resolve: "openrouter/anthropic/claude-opus-4.8",
327
+ openRouterResolve: "openrouter/anthropic/claude-opus-4.8",
328
328
  preferred: true,
329
329
  subagentModel: "claude-sonnet"
330
330
  },
@@ -481,6 +481,16 @@ if (!defaultProxyAlias?.openRouterResolve) {
481
481
  throw new Error("DEFAULT_PROXY_MODEL: moonshotai/kimi-k2 missing openRouterResolve");
482
482
  }
483
483
  var DEFAULT_PROXY_MODEL = defaultProxyAlias.openRouterResolve;
484
+ function getAutoSelectHintModel() {
485
+ const alias = defaultProxyAlias;
486
+ if (!alias) return "Kimi 2.6";
487
+ const modelId = alias.resolve.split("/")[1] ?? "kimi-k2.6";
488
+ const version = modelId.replace(/^kimi-k2\./, "");
489
+ if (version && version !== modelId) {
490
+ return `Kimi 2.${version}`;
491
+ }
492
+ return alias.displayName;
493
+ }
484
494
  function resolveModelSlug(slug) {
485
495
  return modelAliases.find((a) => a.slug === slug)?.resolve;
486
496
  }
@@ -704,7 +714,7 @@ function computeModes(agentId) {
704
714
 
705
715
  Otherwise delegate the \`${REVIEWER_AGENT_NAME}\` subagent to review your diff with fresh eyes against YOUR TASK. The subagent's baked-in system prompt enforces a non-mutative + non-recursive contract: read-only file/search/web tools and read-only MCP queries only; no writes, shell side effects, state-changing MCP calls, or nested subagent dispatch. Enforcement is prose-only \u2014 restate the constraint in your dispatch instructions and do not relax it.
706
716
 
707
- Before dispatching, ensure \`origin/<base>\` is locally available \u2014 the runner is often a shallow single-branch \`actions/checkout\` (depth=1, head-only refspec), and the reviewer's \`git diff --merge-base origin/<base>\` will fail with \`ambiguous argument\` or \`no merge base\` otherwise. Run \`git fetch --no-tags --deepen=1000 origin <base>\` once; it's a no-op if the ref already has enough history. (The reviewer is read-only by contract, so it cannot do this itself \u2014 fetching is the orchestrator's job.)
717
+ Before dispatching, ensure \`origin/<base>\` is locally available \u2014 the runner is often a shallow single-branch \`actions/checkout\` (depth=1, head-only refspec), and the reviewer's \`git diff --merge-base origin/<base>\` will fail with \`ambiguous argument\` or \`no merge base\` otherwise. Run \`git fetch --no-tags --deepen=1000 origin <base>:refs/remotes/origin/<base>\` once (the explicit destination refspec is required \u2014 a shallow single-branch checkout configures a head-only refspec, so a bare \`origin <base>\` only updates \`FETCH_HEAD\` and never creates the \`origin/<base>\` tracking ref); it's a no-op if the ref already has enough history. (The reviewer is read-only by contract, so it cannot do this itself \u2014 fetching is the orchestrator's job.)
708
718
 
709
719
  Compose your \`${REVIEWER_AGENT_NAME}\` dispatch prompt using this template verbatim, substituting the \`<...>\` placeholders. The preamble aligns the orchestrator side of the dispatch contract with the reviewer's baked-in system prompt \u2014 both ends say the same thing about where the work lives and what to do on an empty diff.
710
720
 
@@ -1087,7 +1097,7 @@ ${PR_SUMMARY_FORMAT}`
1087
1097
 
1088
1098
  1. **task list**: create your task list for this run as your first action.
1089
1099
 
1090
- 2. Analyze the task. For simple operations (labeling, commenting, answering questions, running a single command), handle directly.
1100
+ 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.
1091
1101
 
1092
1102
  3. For substantial work \u2014 code changes across multiple files, multi-step investigations:
1093
1103
  - plan your approach before starting
@@ -1389,6 +1399,7 @@ export {
1389
1399
  createLeapingProgressComment,
1390
1400
  decodeJwtExpMs,
1391
1401
  deleteProgressCommentApi,
1402
+ getAutoSelectHintModel,
1392
1403
  getModelEnvVars,
1393
1404
  getModelManagedCredentials,
1394
1405
  getModelProvider,
@@ -38,7 +38,8 @@ export declare const ReportProgress: import("arktype/internal/variants/object.ts
38
38
  * - object: active comment — will update it in place via the right REST endpoint for its type
39
39
  * - null: deliberately deleted (e.g. after submitting a PR review) — skips silently
40
40
  *
41
- * The body is always tracked in lastProgressBody for the job summary regardless of comment state.
41
+ * The body is tracked in lastProgressBody for the job summary regardless of comment state,
42
+ * EXCEPT for `liveProgress` (todo-tracker) writes — see the param note below.
42
43
  *
43
44
  * The "existing plan comment" path always targets a top-level issue comment (plan comments are
44
45
  * created by create_issue_comment with type:"Plan", never as review-thread replies).
@@ -46,6 +47,7 @@ export declare const ReportProgress: import("arktype/internal/variants/object.ts
46
47
  export declare function reportProgress(ctx: ToolContext, params: {
47
48
  body: string;
48
49
  target_plan_comment?: boolean;
50
+ liveProgress?: boolean;
49
51
  }): Promise<{
50
52
  commentId?: number;
51
53
  url?: string;
@@ -1,9 +1,10 @@
1
1
  import type { Octokit } from "@octokit/rest";
2
2
  import type { ToolContext } from "./server.ts";
3
- export declare const REVIEW_THREADS_QUERY = "\nquery ($owner: String!, $name: String!, $prNumber: Int!) {\n repository(owner: $owner, name: $name) {\n pullRequest(number: $prNumber) {\n reviewThreads(first: 100) {\n nodes {\n id\n path\n line\n startLine\n diffSide\n isResolved\n isOutdated\n comments(first: 50) {\n nodes {\n fullDatabaseId\n body\n createdAt\n diffHunk\n line\n startLine\n originalLine\n originalStartLine\n author { login }\n pullRequestReview {\n databaseId\n author { login }\n }\n reactionGroups {\n content\n reactors(first: 10) {\n nodes {\n ... on Actor { login }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n";
3
+ export declare const REVIEW_THREADS_QUERY = "\nquery ($owner: String!, $name: String!, $prNumber: Int!) {\n repository(owner: $owner, name: $name) {\n pullRequest(number: $prNumber) {\n reviewThreads(first: 100) {\n nodes {\n id\n path\n line\n startLine\n diffSide\n isResolved\n isOutdated\n comments(first: 50) {\n nodes {\n fullDatabaseId\n body\n bodyHTML\n createdAt\n diffHunk\n line\n startLine\n originalLine\n originalStartLine\n author { login }\n pullRequestReview {\n databaseId\n author { login }\n }\n reactionGroups {\n content\n reactors(first: 10) {\n nodes {\n ... on Actor { login }\n }\n }\n }\n }\n }\n }\n }\n }\n }\n}\n";
4
4
  export type ReviewThreadComment = {
5
5
  fullDatabaseId: string | null;
6
6
  body: string;
7
+ bodyHTML: string;
7
8
  createdAt: string;
8
9
  diffHunk: string;
9
10
  line: number | null;
@@ -96,6 +97,8 @@ interface GetReviewDataInput {
96
97
  pullNumber: number;
97
98
  reviewId: number;
98
99
  approvedBy?: string | undefined;
100
+ tmpdir: string;
101
+ githubToken: string;
99
102
  }
100
103
  export interface FormatReviewDataInput {
101
104
  review: ReviewResponse;
package/dist/models.d.ts CHANGED
@@ -104,6 +104,8 @@ export declare function getModelEnvVars(slug: string): string[];
104
104
  export declare function getModelManagedCredentials(slug: string): string[];
105
105
  export declare const modelAliases: ModelAlias[];
106
106
  export declare const DEFAULT_PROXY_MODEL: string;
107
+ /** short label for the model auto-select picks today (console hint copy). */
108
+ export declare function getAutoSelectHintModel(): string;
107
109
  /** resolve a model slug to its concrete models.dev specifier (e.g. "anthropic/claude-opus-4-6") */
108
110
  export declare function resolveModelSlug(slug: string): string | undefined;
109
111
  /**
@@ -20,6 +20,8 @@ export type PrepResult = NodePrepResult | PythonPrepResult | UnknownLanguagePrep
20
20
  export type PrepOptions = {
21
21
  /** when true, lifecycle scripts (postinstall, etc.) are suppressed */
22
22
  ignoreScripts: boolean;
23
+ /** directory the corepack shim is installed into (see `packageManagerBinDir`) */
24
+ binDir: string;
23
25
  };
24
26
  export interface PrepDefinition {
25
27
  name: string;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * downloads any github-hosted image/video assets referenced in `markdown` to
3
+ * `<tmpdir>/assets` and rewrites the urls to the local file paths, so the agent can
4
+ * read screenshots directly instead of relying on remote (often short-lived, signed)
5
+ * urls. unique urls are downloaded once and every occurrence is rewritten. assets that
6
+ * fail to download are left untouched.
7
+ */
8
+ export declare function downloadAssetsInMarkdown(markdown: string, tmpdir: string, githubToken: string): Promise<string>;
@@ -5,6 +5,8 @@ interface ResolveBodyContext {
5
5
  event: PayloadEvent;
6
6
  octokit: OctokitWithPlugins;
7
7
  repo: RunContextData["repo"];
8
+ tmpdir: string;
9
+ githubToken: string;
8
10
  }
9
11
  /**
10
12
  * resolves the body of an event by fetching body_html and converting to markdown.
@@ -13,4 +15,20 @@ interface ResolveBodyContext {
13
15
  * broken user-attachments URLs.
14
16
  */
15
17
  export declare function resolveBody(ctx: ResolveBodyContext): Promise<string | null>;
18
+ interface ResolveBodyAssetsContext {
19
+ body: string | null | undefined;
20
+ bodyHtml: string | null | undefined;
21
+ tmpdir: string;
22
+ githubToken: string;
23
+ }
24
+ /**
25
+ * downloads github-hosted image assets in a body to disk and rewrites the urls to local
26
+ * paths so the agent can read them. when the body has images and a rendered `bodyHtml`
27
+ * is supplied, the html is turndowned first: github only exposes attachments as signed,
28
+ * self-authenticating `*.githubusercontent.com` urls through body_html — the raw
29
+ * `github.com/user-attachments/...` urls in unrendered markdown 404 for the installation
30
+ * token. callers that fetch a body should request it with the `application/vnd.github.full+json`
31
+ * media type and pass `body_html` here.
32
+ */
33
+ export declare function resolveBodyAssets(ctx: ResolveBodyAssetsContext): Promise<string | null>;
16
34
  export {};
@@ -17,6 +17,10 @@ interface InstructionsContext {
17
17
  * inline into the LEARNINGS prompt section so the agent can `read_file`
18
18
  * targeted line ranges instead of pulling the whole file into context. */
19
19
  learningsHeadings: LearningsHeading[];
20
+ /** agent-facing description of a setup lifecycle hook failure (see
21
+ * `describeSetupFailure`), rendered as a SETUP HOOK FAILED banner. empty
22
+ * string when the hook succeeded, was skipped, or wasn't configured. */
23
+ setupHookFailure: string;
20
24
  }
21
25
  export interface ResolvedInstructions {
22
26
  full: string;
@@ -26,6 +26,10 @@ export type LifecycleHookFailure = {
26
26
  kind: "spawn";
27
27
  spawnError: string;
28
28
  };
29
+ /** one-line, agent-facing description of a hook failure. empty string when
30
+ * there was no failure, so callers can pass the result straight through to a
31
+ * prompt section that omits itself on empty. */
32
+ export declare function describeSetupFailure(failure: LifecycleHookFailure | undefined): string;
29
33
  export interface LifecycleHookResult {
30
34
  /**
31
35
  * human-readable warning when the hook failed. includes retry guidance:
@@ -46,8 +50,8 @@ export interface LifecycleHookResult {
46
50
  * execute a lifecycle hook script if one is configured.
47
51
  *
48
52
  * soft-fails: instead of throwing on hook errors, returns a warning string
49
- * (and structured failure info) so callers can choose whether to surface
50
- * it (mcp tools) or upgrade it to a fatal error (setup). timeouts are
51
- * flagged as non-retryable in the warning text.
53
+ * (and structured failure info) so callers can choose how to surface it
54
+ * (mcp tools relay it to the agent; setup logs it and adds a prompt banner).
55
+ * timeouts are flagged as non-retryable in the warning text.
52
56
  */
53
57
  export declare function executeLifecycleHook(params: ExecuteLifecycleHookParams): Promise<LifecycleHookResult>;
@@ -18,6 +18,18 @@ export interface PackageManagerSpec {
18
18
  * we warn on disagreement and stick with `devEngines`.
19
19
  */
20
20
  export declare function resolvePackageManagerSpec(cwd: string): Promise<PackageManagerSpec | null>;
21
+ /** the per-run directory the corepack shim is installed into. deliberately
22
+ * NOT the node bin dir: that's npm's `-g` target, and a corepack shim sitting
23
+ * there makes a customer setup script's `npm i -g pnpm` abort with EEXIST
24
+ * (npm refuses to clobber a binary it doesn't own). lives under the run
25
+ * tmpdir so it's cleaned up with everything else. */
26
+ export declare function packageManagerBinDir(tmpdir: string): string;
27
+ export interface EnsurePackageManagerParams {
28
+ spec: PackageManagerSpec;
29
+ /** directory to install the corepack shim into (see `packageManagerBinDir`).
30
+ * prepended to PATH so the pinned binary resolves by name. */
31
+ binDir: string;
32
+ }
21
33
  /**
22
34
  * ensure the requested package manager is on PATH at the declared version,
23
35
  * provisioning via corepack when applicable. returns true if PATH now
@@ -25,8 +37,13 @@ export declare function resolvePackageManagerSpec(cwd: string): Promise<PackageM
25
37
  * the caller should treat PATH as untrusted and may fall back to its
26
38
  * legacy install path).
27
39
  *
40
+ * the corepack shim is installed into `params.binDir` (prepended to PATH),
41
+ * not the node bin dir, so a later `npm i -g pnpm` in a setup hook can't
42
+ * collide with it. our dir wins the PATH lookup, so the pinned version is
43
+ * also what resolves even if that `npm i -g` succeeds into the node bin dir.
44
+ *
28
45
  * never throws: network failure, missing corepack, range-only versions —
29
46
  * all degrade to "log warning, return false". the existing PATH binary
30
47
  * still works; we just don't get our version guarantee.
31
48
  */
32
- export declare function ensurePackageManager(spec: PackageManagerSpec): Promise<boolean>;
49
+ export declare function ensurePackageManager(params: EnsurePackageManagerParams): Promise<boolean>;
@@ -1,9 +1,9 @@
1
1
  import type { AgentResult } from "../agents/shared.ts";
2
2
  import type { MainResult } from "../main.ts";
3
- import type { ToolState } from "../toolState.ts";
3
+ import type { ToolContext } from "../mcp/server.ts";
4
4
  export interface HandleAgentResultParams {
5
5
  result: AgentResult;
6
- toolState: ToolState;
6
+ toolContext: ToolContext;
7
7
  silent: boolean | undefined;
8
8
  }
9
9
  export declare function handleAgentResult(ctx: HandleAgentResultParams): Promise<MainResult>;
@@ -33,8 +33,10 @@
33
33
  * `toolState.agentDiagnostic`; `formatAgentHangBody` renders that as
34
34
  * a markdown block.
35
35
  *
36
- * 6. Default — a generic `❌ Pullfrog failed` block with the raw error
37
- * message in a fenced code block. Same body for both surfaces.
36
+ * 6. Default — a plain-English lead sentence explaining the run failed,
37
+ * followed by the raw error message in a fenced code block (so the user
38
+ * never sees a bare internal string). The job summary adds the
39
+ * `### ❌ Pullfrog failed` banner on top of the same body.
38
40
  *
39
41
  * The hang body and the API-key body diverge between the two surfaces only
40
42
  * in that the job summary wraps them in the `### ❌ Pullfrog failed` H3
@@ -46,11 +46,12 @@ export declare function persistRunArtifacts(toolContext: ToolContext): Promise<v
46
46
  * prepended below in step 4) — same classifier as the catch path so
47
47
  * the user sees it instead of a deleted-comment void / empty summary
48
48
  * tab
49
- * 3. when the run succeeded and the progress comment was never finalized
50
- * via `report_progress`, delete it (three sub-cases — orphan
51
- * "Leaping into action" comment, abandoned checklist, agent wrote
52
- * a substantive artifact via another MCP write tool but skipped
53
- * report_progress)
49
+ * 3. when the run succeeded, some write landed (`wasUpdated`), but the
50
+ * progress comment was never finalized via `report_progress`, delete
51
+ * the stranded comment (abandoned checklist, or a substantive artifact
52
+ * written via another MCP write tool that skipped report_progress). a
53
+ * run where NO write landed keeps its comment for handleAgentResult to
54
+ * salvage into — see the `wasUpdated` guard below and #868
54
55
  * 4. write the GitHub Actions step summary (best-effort — a write
55
56
  * failure must not throw past this point because we'd hit the outer
56
57
  * catch and clobber any progress comment we just wrote)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.1.15",
3
+ "version": "0.1.16",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pullfrog": "dist/cli.mjs",
@@ -26,7 +26,7 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "@actions/core": "^1.11.1",
29
- "@anthropic-ai/claude-code": "2.1.112",
29
+ "@anthropic-ai/claude-code": "2.1.150",
30
30
  "@ark/fs": "0.56.0",
31
31
  "@ark/util": "0.56.0",
32
32
  "@clack/prompts": "^1.2.0",
@@ -51,7 +51,7 @@
51
51
  "fastmcp": "^3.34.0",
52
52
  "file-type": "^21.3.0",
53
53
  "husky": "^9.0.0",
54
- "opencode-ai": "1.15.1",
54
+ "opencode-ai": "1.15.13",
55
55
  "package-manager-detector": "^1.6.0",
56
56
  "picocolors": "^1.1.1",
57
57
  "semver": "^7.7.3",
@@ -59,6 +59,7 @@
59
59
  "table": "^6.9.0",
60
60
  "turndown": "^7.2.0",
61
61
  "typescript": "^5.9.3",
62
+ "undici": "^7.22.0",
62
63
  "vitest": "^4.0.17",
63
64
  "yaml": "^2.8.2"
64
65
  },