pullfrog 0.0.203 → 0.0.204

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.
@@ -9,4 +9,6 @@ export { modes } from "../modes.ts";
9
9
  export type { BuildPullfrogFooterParams, WorkflowRunFooterInfo, } from "../utils/buildPullfrogFooter.ts";
10
10
  export { buildPullfrogFooter, PULLFROG_DIVIDER, stripExistingFooter, } from "../utils/buildPullfrogFooter.ts";
11
11
  export type { ResourceUsage, UsageSummary } from "../utils/github.ts";
12
+ export type { CreateProgressCommentTarget, ProgressComment, ProgressCommentType, } from "../utils/progressComment.ts";
13
+ export { createLeapingProgressComment, deleteProgressCommentApi, getProgressComment, updateProgressComment, } from "../utils/progressComment.ts";
12
14
  export { isValidTimeString, parseTimeString, TIMEOUT_DISABLED, } from "../utils/time.ts";
package/dist/internal.js CHANGED
@@ -93,14 +93,14 @@ var providers = {
93
93
  models: {
94
94
  grok: {
95
95
  displayName: "Grok",
96
- resolve: "xai/grok-4",
97
- openRouterResolve: "openrouter/x-ai/grok-4",
96
+ resolve: "xai/grok-4.3",
97
+ openRouterResolve: "openrouter/x-ai/grok-4.3",
98
98
  preferred: true
99
99
  },
100
100
  "grok-fast": {
101
101
  displayName: "Grok Fast",
102
- resolve: "xai/grok-4-fast",
103
- openRouterResolve: "openrouter/x-ai/grok-4-fast"
102
+ resolve: "xai/grok-4-1-fast",
103
+ openRouterResolve: "openrouter/x-ai/grok-4.1-fast"
104
104
  },
105
105
  "grok-code-fast": {
106
106
  displayName: "Grok Code Fast",
@@ -307,8 +307,8 @@ var providers = {
307
307
  },
308
308
  grok: {
309
309
  displayName: "Grok",
310
- resolve: "openrouter/x-ai/grok-4",
311
- openRouterResolve: "openrouter/x-ai/grok-4"
310
+ resolve: "openrouter/x-ai/grok-4.3",
311
+ openRouterResolve: "openrouter/x-ai/grok-4.3"
312
312
  },
313
313
  "deepseek-pro": {
314
314
  displayName: "DeepSeek Pro",
@@ -857,6 +857,103 @@ function stripExistingFooter(body) {
857
857
  return body.substring(0, dividerIndex).trimEnd();
858
858
  }
859
859
 
860
+ // utils/progressComment.ts
861
+ async function getProgressComment(ctx, comment) {
862
+ const result = await (comment.type === "review" ? ctx.octokit.rest.pulls.getReviewComment({
863
+ owner: ctx.owner,
864
+ repo: ctx.repo,
865
+ comment_id: comment.id
866
+ }) : ctx.octokit.rest.issues.getComment({
867
+ owner: ctx.owner,
868
+ repo: ctx.repo,
869
+ comment_id: comment.id
870
+ }));
871
+ return {
872
+ id: result.data.id,
873
+ body: result.data.body ?? void 0,
874
+ html_url: result.data.html_url
875
+ };
876
+ }
877
+ async function updateProgressComment(ctx, comment, body) {
878
+ const result = await (comment.type === "review" ? ctx.octokit.rest.pulls.updateReviewComment({
879
+ owner: ctx.owner,
880
+ repo: ctx.repo,
881
+ comment_id: comment.id,
882
+ body
883
+ }) : ctx.octokit.rest.issues.updateComment({
884
+ owner: ctx.owner,
885
+ repo: ctx.repo,
886
+ comment_id: comment.id,
887
+ body
888
+ }));
889
+ return {
890
+ id: result.data.id,
891
+ body: result.data.body ?? void 0,
892
+ html_url: result.data.html_url,
893
+ node_id: result.data.node_id
894
+ };
895
+ }
896
+ async function deleteProgressCommentApi(ctx, comment) {
897
+ if (comment.type === "review") {
898
+ await ctx.octokit.rest.pulls.deleteReviewComment({
899
+ owner: ctx.owner,
900
+ repo: ctx.repo,
901
+ comment_id: comment.id
902
+ });
903
+ return;
904
+ }
905
+ await ctx.octokit.rest.issues.deleteComment({
906
+ owner: ctx.owner,
907
+ repo: ctx.repo,
908
+ comment_id: comment.id
909
+ });
910
+ }
911
+ async function createLeapingProgressComment(ctx, target, body) {
912
+ if (target.kind === "reviewReply") {
913
+ try {
914
+ const result2 = await ctx.octokit.rest.pulls.createReplyForReviewComment({
915
+ owner: ctx.owner,
916
+ repo: ctx.repo,
917
+ pull_number: target.pullNumber,
918
+ comment_id: target.replyToCommentId,
919
+ body
920
+ });
921
+ return {
922
+ comment: { id: result2.data.id, type: "review" },
923
+ body: result2.data.body ?? void 0,
924
+ html_url: result2.data.html_url
925
+ };
926
+ } catch (error) {
927
+ console.warn(
928
+ `[progressComment] review reply failed (parent ${target.replyToCommentId} on PR #${target.pullNumber}), falling back to issue comment:`,
929
+ error
930
+ );
931
+ const fallback = await ctx.octokit.rest.issues.createComment({
932
+ owner: ctx.owner,
933
+ repo: ctx.repo,
934
+ issue_number: target.pullNumber,
935
+ body
936
+ });
937
+ return {
938
+ comment: { id: fallback.data.id, type: "issue" },
939
+ body: fallback.data.body ?? void 0,
940
+ html_url: fallback.data.html_url
941
+ };
942
+ }
943
+ }
944
+ const result = await ctx.octokit.rest.issues.createComment({
945
+ owner: ctx.owner,
946
+ repo: ctx.repo,
947
+ issue_number: target.issueNumber,
948
+ body
949
+ });
950
+ return {
951
+ comment: { id: result.data.id, type: "issue" },
952
+ body: result.data.body ?? void 0,
953
+ html_url: result.data.html_url
954
+ };
955
+ }
956
+
860
957
  // utils/time.ts
861
958
  var TIMEOUT_DISABLED = "none";
862
959
  var TIME_STRING_REGEX = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
@@ -875,8 +972,11 @@ export {
875
972
  PULLFROG_DIVIDER,
876
973
  TIMEOUT_DISABLED,
877
974
  buildPullfrogFooter,
975
+ createLeapingProgressComment,
976
+ deleteProgressCommentApi,
878
977
  getModelEnvVars,
879
978
  getModelProvider,
979
+ getProgressComment,
880
980
  getProviderDisplayName,
881
981
  isValidTimeString,
882
982
  modelAliases,
@@ -889,5 +989,6 @@ export {
889
989
  resolveDisplayAlias,
890
990
  resolveModelSlug,
891
991
  resolveOpenRouterModel,
892
- stripExistingFooter
992
+ stripExistingFooter,
993
+ updateProgressComment
893
994
  };
@@ -39,12 +39,15 @@ export declare const ReportProgress: import("arktype/internal/variants/object.ts
39
39
  /**
40
40
  * Report progress to a GitHub comment.
41
41
  *
42
- * progressCommentId has three states:
42
+ * progressComment has three states:
43
43
  * - undefined: no comment yet — will create one if an issue/PR target exists
44
- * - number: active comment — will update it in place
44
+ * - object: active comment — will update it in place via the right REST endpoint for its type
45
45
  * - null: deliberately deleted (e.g. after submitting a PR review) — skips silently
46
46
  *
47
47
  * The body is always tracked in lastProgressBody for the job summary regardless of comment state.
48
+ *
49
+ * The "existing plan comment" path always targets a top-level issue comment (plan comments are
50
+ * created by create_issue_comment with type:"Plan", never as review-thread replies).
48
51
  */
49
52
  export declare function reportProgress(ctx: ToolContext, params: {
50
53
  body: string;
@@ -66,7 +69,7 @@ export declare function ReportProgressTool(ctx: ToolContext): import("fastmcp").
66
69
  * Delete the progress comment if it exists.
67
70
  * Used by main.ts for stranded-comment cleanup (orphaned "Leaping into action" or
68
71
  * checklist left by the todo tracker when the agent didn't call report_progress).
69
- * Sets progressCommentId to null so subsequent report_progress calls are no-ops.
72
+ * Sets progressComment to null so subsequent report_progress calls are no-ops.
70
73
  */
71
74
  export declare function deleteProgressComment(ctx: ToolContext): Promise<boolean>;
72
75
  export declare const ReplyToReviewComment: import("arktype/internal/variants/object.ts").ObjectType<{
package/dist/mcp/git.d.ts CHANGED
@@ -6,6 +6,8 @@ export declare const PushBranch: import("arktype/internal/variants/object.ts").O
6
6
  force: import("arktype/internal/attributes.ts").Default<boolean, false>;
7
7
  branchName?: string;
8
8
  }, {}>;
9
+ export type PushErrorKind = "concurrent-push" | "transient" | "unknown";
10
+ export declare function classifyPushError(msg: string): PushErrorKind;
9
11
  export declare function PushBranchTool(ctx: ToolContext): import("fastmcp").Tool<any, import("@standard-schema/spec").StandardSchemaV1<{
10
12
  branchName?: string;
11
13
  force?: boolean;
@@ -97,6 +97,35 @@ interface GetReviewDataInput {
97
97
  reviewId: number;
98
98
  approvedBy?: string | undefined;
99
99
  }
100
+ export interface FormatReviewDataInput {
101
+ review: ReviewResponse;
102
+ threads: ReviewThread[];
103
+ prFiles: ReviewPrFile[];
104
+ pullNumber: number;
105
+ reviewId: number;
106
+ }
107
+ export type ReviewResponse = {
108
+ body: string | null | undefined;
109
+ user: {
110
+ login: string;
111
+ } | null | undefined;
112
+ };
113
+ export type ReviewPrFile = {
114
+ filename: string;
115
+ patch?: string | undefined;
116
+ };
117
+ export declare function formatReviewData(input: FormatReviewDataInput): {
118
+ threadBlocks: Array<{
119
+ path: string;
120
+ lineRange: string;
121
+ content: string[];
122
+ }>;
123
+ reviewer: string;
124
+ formatted: {
125
+ toc: string;
126
+ content: string;
127
+ };
128
+ } | undefined;
100
129
  export declare function getReviewData(input: GetReviewDataInput): Promise<{
101
130
  threadBlocks: Array<{
102
131
  path: string;
@@ -6,6 +6,8 @@ import type { PrepResult } from "../prep/index.ts";
6
6
  import type { DiffCoverageState } from "../utils/diffCoverage.ts";
7
7
  import type { OctokitWithPlugins } from "../utils/github.ts";
8
8
  import type { ResolvedPayload } from "../utils/payload.ts";
9
+ import { type ProgressComment, type ProgressCommentType } from "../utils/progressComment.ts";
10
+ import type { AccountPlan } from "../utils/runContext.ts";
9
11
  import type { RunContextData } from "../utils/runContextData.ts";
10
12
  import type { TodoTracker } from "../utils/todoTracking.ts";
11
13
  import type { CommentableLines } from "./review.ts";
@@ -48,7 +50,7 @@ export interface ToolState {
48
50
  promise: Promise<PrepResult[]> | undefined;
49
51
  results: PrepResult[] | undefined;
50
52
  };
51
- progressCommentId: number | null | undefined;
53
+ progressComment: ProgressComment | null | undefined;
52
54
  hadProgressComment: boolean;
53
55
  lastProgressBody?: string;
54
56
  wasUpdated?: boolean;
@@ -63,7 +65,10 @@ export interface ToolState {
63
65
  diffCoverage?: DiffCoverageState | undefined;
64
66
  }
65
67
  interface InitToolStateParams {
66
- progressCommentId: string | undefined;
68
+ progressComment: {
69
+ id: string;
70
+ type: ProgressCommentType;
71
+ } | undefined;
67
72
  }
68
73
  export declare function initToolState(params: InitToolStateParams): ToolState;
69
74
  export interface ToolContext {
@@ -84,6 +89,8 @@ export interface ToolContext {
84
89
  jobId: string | undefined;
85
90
  mcpServerUrl: string;
86
91
  tmpdir: string;
92
+ oss: boolean;
93
+ plan: AccountPlan;
87
94
  resolvedModel: string | undefined;
88
95
  }
89
96
  type JsonSchema = Record<string, unknown>;
@@ -9,7 +9,10 @@ export declare const JsonPayload: import("arktype/internal/variants/object.ts").
9
9
  eventInstructions?: string;
10
10
  event?: object;
11
11
  timeout?: string | undefined;
12
- progressCommentId?: string | undefined;
12
+ progressComment?: {
13
+ id: string;
14
+ type: "issue" | "review";
15
+ } | undefined;
13
16
  }, {}>;
14
17
  export declare const Inputs: import("arktype/internal/variants/object.ts").ObjectType<{
15
18
  prompt: string;
@@ -33,7 +36,10 @@ export declare function resolvePayload(resolvedPromptInput: ResolvedPromptInput,
33
36
  event: PayloadEvent;
34
37
  timeout: string | undefined;
35
38
  cwd: string | undefined;
36
- progressCommentId: string | undefined;
39
+ progressComment: {
40
+ id: string;
41
+ type: "issue" | "review";
42
+ } | undefined;
37
43
  push: import("../external.ts").PushPermission;
38
44
  shell: import("../external.ts").ShellPermission;
39
45
  proxyModel: string | undefined;
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Single source of truth for reading, updating, deleting, and creating "progress comments" —
3
+ * the GitHub comments Pullfrog uses to surface a run's status.
4
+ *
5
+ * A progress comment can be one of two distinct GitHub entities with non-overlapping IDs and
6
+ * distinct REST endpoints:
7
+ * - "issue": a top-level issue/PR timeline comment (octokit.rest.issues.*Comment)
8
+ * - "review": an inline PR review-thread comment (octokit.rest.pulls.*ReviewComment)
9
+ *
10
+ * Callers carry a `ProgressComment` (id + type) value end-to-end so the right endpoint is always
11
+ * picked. Adding a third comment type later means one new branch in this file, not six.
12
+ */
13
+ export type ProgressCommentType = "issue" | "review";
14
+ export type ProgressComment = {
15
+ id: number;
16
+ type: ProgressCommentType;
17
+ };
18
+ /**
19
+ * Parse the on-the-wire `{ id: string; type }` shape (the form carried in `JsonPayload`)
20
+ * into the in-memory `ProgressComment` shape. Returns undefined when the id isn't a
21
+ * positive integer so callers can short-circuit cleanly. Callers handle logging.
22
+ */
23
+ export declare function parseProgressComment(raw: {
24
+ id: string;
25
+ type: ProgressCommentType;
26
+ } | null | undefined): ProgressComment | undefined;
27
+ interface CommentResponse {
28
+ data: {
29
+ id: number;
30
+ body?: string | null | undefined;
31
+ html_url: string;
32
+ node_id?: string;
33
+ };
34
+ }
35
+ export interface ProgressCommentOctokit {
36
+ rest: {
37
+ issues: {
38
+ createComment: (params: {
39
+ owner: string;
40
+ repo: string;
41
+ issue_number: number;
42
+ body: string;
43
+ }) => Promise<CommentResponse>;
44
+ getComment: (params: {
45
+ owner: string;
46
+ repo: string;
47
+ comment_id: number;
48
+ }) => Promise<CommentResponse>;
49
+ updateComment: (params: {
50
+ owner: string;
51
+ repo: string;
52
+ comment_id: number;
53
+ body: string;
54
+ }) => Promise<CommentResponse>;
55
+ deleteComment: (params: {
56
+ owner: string;
57
+ repo: string;
58
+ comment_id: number;
59
+ }) => Promise<unknown>;
60
+ };
61
+ pulls: {
62
+ createReplyForReviewComment: (params: {
63
+ owner: string;
64
+ repo: string;
65
+ pull_number: number;
66
+ comment_id: number;
67
+ body: string;
68
+ }) => Promise<CommentResponse>;
69
+ getReviewComment: (params: {
70
+ owner: string;
71
+ repo: string;
72
+ comment_id: number;
73
+ }) => Promise<CommentResponse>;
74
+ updateReviewComment: (params: {
75
+ owner: string;
76
+ repo: string;
77
+ comment_id: number;
78
+ body: string;
79
+ }) => Promise<CommentResponse>;
80
+ deleteReviewComment: (params: {
81
+ owner: string;
82
+ repo: string;
83
+ comment_id: number;
84
+ }) => Promise<unknown>;
85
+ };
86
+ };
87
+ }
88
+ interface ApiCtx {
89
+ octokit: ProgressCommentOctokit;
90
+ owner: string;
91
+ repo: string;
92
+ }
93
+ /**
94
+ * Fetch a progress comment via the appropriate REST endpoint for its type.
95
+ * Returns the common subset of fields callers actually use.
96
+ */
97
+ export declare function getProgressComment(ctx: ApiCtx, comment: ProgressComment): Promise<{
98
+ id: number;
99
+ body: string | undefined;
100
+ html_url: string;
101
+ }>;
102
+ /**
103
+ * Update a progress comment in place via the appropriate REST endpoint.
104
+ * Returns the common subset of fields callers actually use.
105
+ */
106
+ export declare function updateProgressComment(ctx: ApiCtx, comment: ProgressComment, body: string): Promise<{
107
+ id: number;
108
+ body: string | undefined;
109
+ html_url: string;
110
+ node_id: string | undefined;
111
+ }>;
112
+ /**
113
+ * Delete a progress comment via the appropriate REST endpoint.
114
+ * Lower-level than `deleteProgressComment` in mcp/comment.ts — that one also clears
115
+ * tool state. Callers that don't have a ToolContext (post cleanup, error handlers)
116
+ * should use this directly; the higher-level wrapper delegates here.
117
+ */
118
+ export declare function deleteProgressCommentApi(ctx: ApiCtx, comment: ProgressComment): Promise<void>;
119
+ /**
120
+ * Discriminated target for `createLeapingProgressComment`. The two variants map to the two
121
+ * distinct GitHub create endpoints; review-reply additionally needs the parent comment ID.
122
+ */
123
+ export type CreateProgressCommentTarget = {
124
+ kind: "issue";
125
+ issueNumber: number;
126
+ } | {
127
+ kind: "reviewReply";
128
+ pullNumber: number;
129
+ replyToCommentId: number;
130
+ };
131
+ export interface CreatedProgressComment {
132
+ comment: ProgressComment;
133
+ body: string | undefined;
134
+ html_url: string;
135
+ }
136
+ /**
137
+ * Create the initial "Leaping into action..." progress comment.
138
+ *
139
+ * Reliability: when `kind: "reviewReply"` fails (e.g. the parent comment was deleted or the
140
+ * thread is otherwise unreachable), falls back to a top-level issue comment on the same PR
141
+ * rather than leaving the run with no progress surface. The fallback is logged.
142
+ *
143
+ * (PR # === issue # in GitHub's number space, so `pullNumber` doubles as the fallback target.)
144
+ */
145
+ export declare function createLeapingProgressComment(ctx: ApiCtx, target: CreateProgressCommentTarget, body: string): Promise<CreatedProgressComment>;
146
+ export {};
@@ -20,10 +20,26 @@ export interface RepoSettings {
20
20
  learnings: string | null;
21
21
  envAllowlist: string | null;
22
22
  }
23
+ /**
24
+ * Account-level billing plan. Orthogonal to repo-level OSS status. Mirrors
25
+ * the server's `AccountPlan` in `utils/billing.ts`. `"none"` = free tier,
26
+ * `"payg"` = card on file / pay-as-you-go.
27
+ */
28
+ export type AccountPlan = "none" | "payg";
29
+ /**
30
+ * "Is Pullfrog absorbing marginal infra cost for this repo?" — composite
31
+ * predicate over the two orthogonal dimensions (repo-level OSS, account-level
32
+ * plan). Mirrors `isInfraCovered` in the server's `utils/billing.ts`.
33
+ */
34
+ export declare function isInfraCovered(params: {
35
+ isOss: boolean;
36
+ plan: AccountPlan;
37
+ }): boolean;
23
38
  export interface RunContext {
24
39
  settings: RepoSettings;
25
40
  apiToken: string;
26
41
  oss: boolean;
42
+ plan: AccountPlan;
27
43
  proxyModel?: string | undefined;
28
44
  dbSecrets?: Record<string, string> | undefined;
29
45
  }
@@ -1,6 +1,6 @@
1
1
  import type { Octokit } from "@octokit/rest";
2
2
  import { type OctokitWithPlugins } from "./github.ts";
3
- import { type RepoSettings } from "./runContext.ts";
3
+ import { type AccountPlan, type RepoSettings } from "./runContext.ts";
4
4
  export interface RunContextData {
5
5
  repo: {
6
6
  owner: string;
@@ -10,6 +10,7 @@ export interface RunContextData {
10
10
  repoSettings: RepoSettings;
11
11
  apiToken: string;
12
12
  oss: boolean;
13
+ plan: AccountPlan;
13
14
  proxyModel?: string | undefined;
14
15
  dbSecrets?: Record<string, string> | undefined;
15
16
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pullfrog",
3
- "version": "0.0.203",
3
+ "version": "0.0.204",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "pullfrog": "dist/cli.mjs",