pullfrog 0.0.196 → 0.0.198
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/agents/claude.d.ts +1 -0
- package/dist/agents/index.d.ts +6 -0
- package/dist/agents/opentoad.d.ts +1 -0
- package/dist/agents/shared.d.ts +46 -0
- package/dist/cli.mjs +6 -9
- package/dist/external.d.ts +213 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +151703 -0
- package/dist/internal/index.d.ts +12 -0
- package/dist/internal.js +681 -0
- package/dist/lifecycle.d.ts +2 -0
- package/dist/main.d.ts +8 -0
- package/dist/mcp/arkConfig.d.ts +1 -0
- package/dist/mcp/checkSuite.d.ts +9 -0
- package/dist/mcp/checkout.d.ts +63 -0
- package/dist/mcp/comment.d.ts +87 -0
- package/dist/mcp/commitInfo.d.ts +9 -0
- package/dist/mcp/dependencies.d.ts +3 -0
- package/dist/mcp/git.d.ts +38 -0
- package/dist/mcp/issue.d.ts +18 -0
- package/dist/mcp/issueComments.d.ts +9 -0
- package/dist/mcp/issueEvents.d.ts +9 -0
- package/dist/mcp/issueInfo.d.ts +9 -0
- package/dist/mcp/labels.d.ts +12 -0
- package/dist/mcp/learnings.d.ts +6 -0
- package/dist/mcp/output.d.ts +12 -0
- package/dist/mcp/pr.d.ts +29 -0
- package/dist/mcp/prInfo.d.ts +9 -0
- package/dist/mcp/review.d.ts +47 -0
- package/dist/mcp/reviewComments.d.ts +135 -0
- package/dist/mcp/selectMode.d.ts +24 -0
- package/dist/mcp/server.d.ts +93 -0
- package/dist/mcp/shared.d.ts +21 -0
- package/dist/mcp/shell.d.ts +32 -0
- package/dist/mcp/upload.d.ts +6 -0
- package/dist/models.d.ts +69 -0
- package/dist/modes.d.ts +9 -0
- package/dist/prep/index.d.ts +7 -0
- package/dist/prep/installNodeDependencies.d.ts +2 -0
- package/dist/prep/installPythonDependencies.d.ts +2 -0
- package/dist/prep/types.d.ts +29 -0
- package/dist/utils/activity.d.ts +21 -0
- package/dist/utils/agent.d.ts +15 -0
- package/dist/utils/apiFetch.d.ts +19 -0
- package/dist/utils/apiKeys.d.ts +10 -0
- package/dist/utils/apiUrl.d.ts +9 -0
- package/dist/utils/body.d.ts +16 -0
- package/dist/utils/browser.d.ts +21 -0
- package/dist/utils/buildPullfrogFooter.d.ts +30 -0
- package/dist/utils/cli.d.ts +10 -0
- package/dist/utils/errorReport.d.ts +8 -0
- package/dist/utils/exitHandler.d.ts +8 -0
- package/dist/utils/fixDoubleEscapedString.d.ts +1 -0
- package/dist/utils/gitAuth.d.ts +47 -0
- package/dist/utils/gitAuthServer.d.ts +18 -0
- package/dist/utils/github.d.ts +78 -0
- package/dist/utils/globals.d.ts +3 -0
- package/dist/utils/install.d.ts +60 -0
- package/dist/utils/instructions.d.ts +22 -0
- package/dist/utils/lifecycle.d.ts +9 -0
- package/dist/utils/log.d.ts +92 -0
- package/dist/utils/normalizeEnv.d.ts +10 -0
- package/dist/utils/payload.d.ts +41 -0
- package/dist/utils/providerErrors.d.ts +1 -0
- package/dist/utils/rangeDiff.d.ts +51 -0
- package/dist/utils/retry.d.ts +7 -0
- package/dist/utils/reviewCleanup.d.ts +14 -0
- package/dist/utils/run.d.ts +9 -0
- package/dist/utils/runContext.d.ts +36 -0
- package/dist/utils/runContextData.d.ts +24 -0
- package/dist/utils/secrets.d.ts +17 -0
- package/dist/utils/setup.d.ts +33 -0
- package/dist/utils/shell.d.ts +32 -0
- package/dist/utils/skills.d.ts +6 -0
- package/dist/utils/subprocess.d.ts +32 -0
- package/dist/utils/time.d.ts +14 -0
- package/dist/utils/timer.d.ts +12 -0
- package/dist/utils/todoTracking.d.ts +14 -0
- package/dist/utils/token.d.ts +40 -0
- package/dist/utils/version.d.ts +2 -0
- package/dist/utils/versioning.d.ts +7 -0
- package/dist/utils/workflow.d.ts +14 -0
- package/package.json +5 -8
package/dist/internal.js
ADDED
|
@@ -0,0 +1,681 @@
|
|
|
1
|
+
import { createRequire as __createRequire } from 'module'; import { fileURLToPath as __fileURLToPath } from 'url'; import { dirname as __dirnameFn } from 'path'; const require = __createRequire(import.meta.url); const __filename = __fileURLToPath(import.meta.url); const __dirname = __dirnameFn(__filename);
|
|
2
|
+
|
|
3
|
+
// models.ts
|
|
4
|
+
function provider(config) {
|
|
5
|
+
return config;
|
|
6
|
+
}
|
|
7
|
+
var providers = {
|
|
8
|
+
anthropic: provider({
|
|
9
|
+
displayName: "Anthropic",
|
|
10
|
+
envVars: ["ANTHROPIC_API_KEY", "CLAUDE_CODE_OAUTH_TOKEN"],
|
|
11
|
+
models: {
|
|
12
|
+
"claude-opus": {
|
|
13
|
+
displayName: "Claude Opus",
|
|
14
|
+
resolve: "anthropic/claude-opus-4-6",
|
|
15
|
+
openRouterResolve: "openrouter/anthropic/claude-opus-4.6",
|
|
16
|
+
preferred: true
|
|
17
|
+
},
|
|
18
|
+
"claude-sonnet": {
|
|
19
|
+
displayName: "Claude Sonnet",
|
|
20
|
+
resolve: "anthropic/claude-sonnet-4-6",
|
|
21
|
+
openRouterResolve: "openrouter/anthropic/claude-sonnet-4.6"
|
|
22
|
+
},
|
|
23
|
+
"claude-haiku": {
|
|
24
|
+
displayName: "Claude Haiku",
|
|
25
|
+
resolve: "anthropic/claude-haiku-4-5",
|
|
26
|
+
openRouterResolve: "openrouter/anthropic/claude-haiku-4.5"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}),
|
|
30
|
+
openai: provider({
|
|
31
|
+
displayName: "OpenAI",
|
|
32
|
+
envVars: ["OPENAI_API_KEY"],
|
|
33
|
+
models: {
|
|
34
|
+
"gpt-codex": {
|
|
35
|
+
displayName: "GPT Codex",
|
|
36
|
+
resolve: "openai/gpt-5.3-codex",
|
|
37
|
+
openRouterResolve: "openrouter/openai/gpt-5.3-codex",
|
|
38
|
+
preferred: true
|
|
39
|
+
},
|
|
40
|
+
"gpt-codex-mini": {
|
|
41
|
+
displayName: "GPT Codex Mini",
|
|
42
|
+
resolve: "openai/codex-mini-latest",
|
|
43
|
+
openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini"
|
|
44
|
+
},
|
|
45
|
+
o3: {
|
|
46
|
+
displayName: "O3",
|
|
47
|
+
resolve: "openai/o3"
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}),
|
|
51
|
+
google: provider({
|
|
52
|
+
displayName: "Google",
|
|
53
|
+
envVars: ["GEMINI_API_KEY", "GOOGLE_GENERATIVE_AI_API_KEY"],
|
|
54
|
+
models: {
|
|
55
|
+
"gemini-pro": {
|
|
56
|
+
displayName: "Gemini Pro",
|
|
57
|
+
resolve: "google/gemini-3.1-pro-preview",
|
|
58
|
+
openRouterResolve: "openrouter/google/gemini-3.1-pro-preview",
|
|
59
|
+
preferred: true
|
|
60
|
+
},
|
|
61
|
+
"gemini-flash": {
|
|
62
|
+
displayName: "Gemini Flash",
|
|
63
|
+
resolve: "google/gemini-3-flash-preview",
|
|
64
|
+
openRouterResolve: "openrouter/google/gemini-3-flash-preview"
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}),
|
|
68
|
+
xai: provider({
|
|
69
|
+
displayName: "xAI",
|
|
70
|
+
envVars: ["XAI_API_KEY"],
|
|
71
|
+
models: {
|
|
72
|
+
grok: {
|
|
73
|
+
displayName: "Grok",
|
|
74
|
+
resolve: "xai/grok-4",
|
|
75
|
+
openRouterResolve: "openrouter/x-ai/grok-4",
|
|
76
|
+
preferred: true
|
|
77
|
+
},
|
|
78
|
+
"grok-fast": {
|
|
79
|
+
displayName: "Grok Fast",
|
|
80
|
+
resolve: "xai/grok-4-fast",
|
|
81
|
+
openRouterResolve: "openrouter/x-ai/grok-4-fast"
|
|
82
|
+
},
|
|
83
|
+
"grok-code-fast": {
|
|
84
|
+
displayName: "Grok Code Fast",
|
|
85
|
+
resolve: "xai/grok-code-fast-1",
|
|
86
|
+
openRouterResolve: "openrouter/x-ai/grok-code-fast-1"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}),
|
|
90
|
+
deepseek: provider({
|
|
91
|
+
displayName: "DeepSeek",
|
|
92
|
+
envVars: ["DEEPSEEK_API_KEY"],
|
|
93
|
+
models: {
|
|
94
|
+
"deepseek-reasoner": {
|
|
95
|
+
displayName: "DeepSeek Reasoner",
|
|
96
|
+
resolve: "deepseek/deepseek-reasoner",
|
|
97
|
+
openRouterResolve: "openrouter/deepseek/deepseek-v3.2",
|
|
98
|
+
preferred: true
|
|
99
|
+
},
|
|
100
|
+
"deepseek-chat": {
|
|
101
|
+
displayName: "DeepSeek Chat",
|
|
102
|
+
resolve: "deepseek/deepseek-chat",
|
|
103
|
+
openRouterResolve: "openrouter/deepseek/deepseek-v3.2"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}),
|
|
107
|
+
moonshotai: provider({
|
|
108
|
+
displayName: "Moonshot AI",
|
|
109
|
+
envVars: ["MOONSHOT_API_KEY"],
|
|
110
|
+
models: {
|
|
111
|
+
"kimi-k2": {
|
|
112
|
+
displayName: "Kimi K2",
|
|
113
|
+
resolve: "moonshotai/kimi-k2.5",
|
|
114
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k2.5",
|
|
115
|
+
preferred: true
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}),
|
|
119
|
+
opencode: provider({
|
|
120
|
+
displayName: "OpenCode",
|
|
121
|
+
envVars: ["OPENCODE_API_KEY"],
|
|
122
|
+
models: {
|
|
123
|
+
"big-pickle": {
|
|
124
|
+
displayName: "Big Pickle",
|
|
125
|
+
resolve: "opencode/big-pickle",
|
|
126
|
+
preferred: true,
|
|
127
|
+
envVars: [],
|
|
128
|
+
isFree: true
|
|
129
|
+
},
|
|
130
|
+
"claude-opus": {
|
|
131
|
+
displayName: "Claude Opus",
|
|
132
|
+
resolve: "opencode/claude-opus-4-6",
|
|
133
|
+
openRouterResolve: "openrouter/anthropic/claude-opus-4.6"
|
|
134
|
+
},
|
|
135
|
+
"claude-sonnet": {
|
|
136
|
+
displayName: "Claude Sonnet",
|
|
137
|
+
resolve: "opencode/claude-sonnet-4-6",
|
|
138
|
+
openRouterResolve: "openrouter/anthropic/claude-sonnet-4.6"
|
|
139
|
+
},
|
|
140
|
+
"claude-haiku": {
|
|
141
|
+
displayName: "Claude Haiku",
|
|
142
|
+
resolve: "opencode/claude-haiku-4-5",
|
|
143
|
+
openRouterResolve: "openrouter/anthropic/claude-haiku-4.5"
|
|
144
|
+
},
|
|
145
|
+
"gpt-codex": {
|
|
146
|
+
displayName: "GPT Codex",
|
|
147
|
+
resolve: "opencode/gpt-5.3-codex",
|
|
148
|
+
openRouterResolve: "openrouter/openai/gpt-5.3-codex"
|
|
149
|
+
},
|
|
150
|
+
"gpt-codex-mini": {
|
|
151
|
+
displayName: "GPT Codex Mini",
|
|
152
|
+
resolve: "opencode/gpt-5.1-codex-mini",
|
|
153
|
+
openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini"
|
|
154
|
+
},
|
|
155
|
+
"gemini-pro": {
|
|
156
|
+
displayName: "Gemini Pro",
|
|
157
|
+
resolve: "opencode/gemini-3.1-pro",
|
|
158
|
+
openRouterResolve: "openrouter/google/gemini-3.1-pro-preview"
|
|
159
|
+
},
|
|
160
|
+
"gemini-flash": {
|
|
161
|
+
displayName: "Gemini Flash",
|
|
162
|
+
resolve: "opencode/gemini-3-flash",
|
|
163
|
+
openRouterResolve: "openrouter/google/gemini-3-flash-preview"
|
|
164
|
+
},
|
|
165
|
+
"kimi-k2": {
|
|
166
|
+
displayName: "Kimi K2",
|
|
167
|
+
resolve: "opencode/kimi-k2.5",
|
|
168
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k2.5"
|
|
169
|
+
},
|
|
170
|
+
"gpt-5-nano": {
|
|
171
|
+
displayName: "GPT Nano",
|
|
172
|
+
resolve: "opencode/gpt-5-nano",
|
|
173
|
+
envVars: [],
|
|
174
|
+
isFree: true
|
|
175
|
+
},
|
|
176
|
+
"mimo-v2-pro-free": {
|
|
177
|
+
displayName: "MiMo V2 Pro",
|
|
178
|
+
resolve: "opencode/mimo-v2-pro-free",
|
|
179
|
+
envVars: [],
|
|
180
|
+
isFree: true,
|
|
181
|
+
fallback: "opencode/big-pickle"
|
|
182
|
+
},
|
|
183
|
+
"minimax-m2.5-free": {
|
|
184
|
+
displayName: "MiniMax M2.5",
|
|
185
|
+
resolve: "opencode/minimax-m2.5-free",
|
|
186
|
+
envVars: [],
|
|
187
|
+
isFree: true
|
|
188
|
+
},
|
|
189
|
+
"nemotron-3-super-free": {
|
|
190
|
+
displayName: "Nemotron 3 Super",
|
|
191
|
+
resolve: "opencode/nemotron-3-super-free",
|
|
192
|
+
envVars: [],
|
|
193
|
+
isFree: true
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}),
|
|
197
|
+
openrouter: provider({
|
|
198
|
+
displayName: "OpenRouter",
|
|
199
|
+
envVars: ["OPENROUTER_API_KEY"],
|
|
200
|
+
models: {
|
|
201
|
+
"claude-opus": {
|
|
202
|
+
displayName: "Claude Opus",
|
|
203
|
+
resolve: "openrouter/anthropic/claude-opus-4.6",
|
|
204
|
+
openRouterResolve: "openrouter/anthropic/claude-opus-4.6",
|
|
205
|
+
preferred: true
|
|
206
|
+
},
|
|
207
|
+
"claude-sonnet": {
|
|
208
|
+
displayName: "Claude Sonnet",
|
|
209
|
+
resolve: "openrouter/anthropic/claude-sonnet-4.6",
|
|
210
|
+
openRouterResolve: "openrouter/anthropic/claude-sonnet-4.6"
|
|
211
|
+
},
|
|
212
|
+
"claude-haiku": {
|
|
213
|
+
displayName: "Claude Haiku",
|
|
214
|
+
resolve: "openrouter/anthropic/claude-haiku-4.5",
|
|
215
|
+
openRouterResolve: "openrouter/anthropic/claude-haiku-4.5"
|
|
216
|
+
},
|
|
217
|
+
"gpt-codex": {
|
|
218
|
+
displayName: "GPT Codex",
|
|
219
|
+
resolve: "openrouter/openai/gpt-5.3-codex",
|
|
220
|
+
openRouterResolve: "openrouter/openai/gpt-5.3-codex"
|
|
221
|
+
},
|
|
222
|
+
"gpt-codex-mini": {
|
|
223
|
+
displayName: "GPT Codex Mini",
|
|
224
|
+
resolve: "openrouter/openai/gpt-5.1-codex-mini",
|
|
225
|
+
openRouterResolve: "openrouter/openai/gpt-5.1-codex-mini"
|
|
226
|
+
},
|
|
227
|
+
"o4-mini": {
|
|
228
|
+
displayName: "O4 Mini",
|
|
229
|
+
resolve: "openrouter/openai/o4-mini",
|
|
230
|
+
openRouterResolve: "openrouter/openai/o4-mini"
|
|
231
|
+
},
|
|
232
|
+
"gemini-pro": {
|
|
233
|
+
displayName: "Gemini Pro",
|
|
234
|
+
resolve: "openrouter/google/gemini-3.1-pro-preview",
|
|
235
|
+
openRouterResolve: "openrouter/google/gemini-3.1-pro-preview"
|
|
236
|
+
},
|
|
237
|
+
"gemini-flash": {
|
|
238
|
+
displayName: "Gemini Flash",
|
|
239
|
+
resolve: "openrouter/google/gemini-3-flash-preview",
|
|
240
|
+
openRouterResolve: "openrouter/google/gemini-3-flash-preview"
|
|
241
|
+
},
|
|
242
|
+
grok: {
|
|
243
|
+
displayName: "Grok",
|
|
244
|
+
resolve: "openrouter/x-ai/grok-4",
|
|
245
|
+
openRouterResolve: "openrouter/x-ai/grok-4"
|
|
246
|
+
},
|
|
247
|
+
"deepseek-chat": {
|
|
248
|
+
displayName: "DeepSeek Chat",
|
|
249
|
+
resolve: "openrouter/deepseek/deepseek-v3.2",
|
|
250
|
+
openRouterResolve: "openrouter/deepseek/deepseek-v3.2"
|
|
251
|
+
},
|
|
252
|
+
"kimi-k2": {
|
|
253
|
+
displayName: "Kimi K2",
|
|
254
|
+
resolve: "openrouter/moonshotai/kimi-k2.5",
|
|
255
|
+
openRouterResolve: "openrouter/moonshotai/kimi-k2.5"
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
})
|
|
259
|
+
};
|
|
260
|
+
function parseModel(slug) {
|
|
261
|
+
const slashIdx = slug.indexOf("/");
|
|
262
|
+
if (slashIdx === -1) {
|
|
263
|
+
throw new Error(`invalid model slug "${slug}" \u2014 expected "provider/model"`);
|
|
264
|
+
}
|
|
265
|
+
return { provider: slug.slice(0, slashIdx), model: slug.slice(slashIdx + 1) };
|
|
266
|
+
}
|
|
267
|
+
function getModelProvider(slug) {
|
|
268
|
+
return parseModel(slug).provider;
|
|
269
|
+
}
|
|
270
|
+
function getProviderDisplayName(slug) {
|
|
271
|
+
const parsed = parseModel(slug);
|
|
272
|
+
return providers[parsed.provider]?.displayName;
|
|
273
|
+
}
|
|
274
|
+
function getModelEnvVars(slug) {
|
|
275
|
+
const parsed = parseModel(slug);
|
|
276
|
+
const providerConfig = providers[parsed.provider];
|
|
277
|
+
if (!providerConfig) {
|
|
278
|
+
return [];
|
|
279
|
+
}
|
|
280
|
+
const modelConfig = providerConfig.models[parsed.model];
|
|
281
|
+
if (modelConfig?.envVars) {
|
|
282
|
+
return modelConfig.envVars.slice();
|
|
283
|
+
}
|
|
284
|
+
return providerConfig.envVars.slice();
|
|
285
|
+
}
|
|
286
|
+
var modelAliases = Object.entries(providers).flatMap(
|
|
287
|
+
([providerKey, config]) => Object.entries(config.models).map(([modelId, def]) => ({
|
|
288
|
+
slug: `${providerKey}/${modelId}`,
|
|
289
|
+
provider: providerKey,
|
|
290
|
+
displayName: def.displayName,
|
|
291
|
+
resolve: def.resolve,
|
|
292
|
+
openRouterResolve: def.openRouterResolve,
|
|
293
|
+
preferred: def.preferred ?? false,
|
|
294
|
+
isFree: def.isFree ?? false,
|
|
295
|
+
fallback: def.fallback
|
|
296
|
+
}))
|
|
297
|
+
);
|
|
298
|
+
function resolveModelSlug(slug) {
|
|
299
|
+
return modelAliases.find((a) => a.slug === slug)?.resolve;
|
|
300
|
+
}
|
|
301
|
+
var MAX_FALLBACK_DEPTH = 10;
|
|
302
|
+
function resolveCliModel(slug) {
|
|
303
|
+
let current = slug;
|
|
304
|
+
const visited = /* @__PURE__ */ new Set();
|
|
305
|
+
for (let i = 0; i < MAX_FALLBACK_DEPTH; i++) {
|
|
306
|
+
if (visited.has(current)) return void 0;
|
|
307
|
+
visited.add(current);
|
|
308
|
+
const alias = modelAliases.find((a) => a.slug === current);
|
|
309
|
+
if (!alias) return void 0;
|
|
310
|
+
if (!alias.fallback) return alias.resolve;
|
|
311
|
+
current = alias.fallback;
|
|
312
|
+
}
|
|
313
|
+
return void 0;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// external.ts
|
|
317
|
+
var pullfrogMcpName = "pullfrog";
|
|
318
|
+
function formatMcpToolRef(agentId, toolName) {
|
|
319
|
+
switch (agentId) {
|
|
320
|
+
case "claude":
|
|
321
|
+
return `mcp__${pullfrogMcpName}__${toolName}`;
|
|
322
|
+
case "opentoad":
|
|
323
|
+
return `${pullfrogMcpName}_${toolName}`;
|
|
324
|
+
default:
|
|
325
|
+
return agentId;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// modes.ts
|
|
330
|
+
var PR_SUMMARY_FORMAT = `### Default format
|
|
331
|
+
|
|
332
|
+
Follow this structure exactly:
|
|
333
|
+
|
|
334
|
+
<b>TL;DR</b> \u2014 1-3 sentences on what the PR does and why. Focus on intent, not mechanics.
|
|
335
|
+
NOTE: use HTML bold <b>TL;DR</b>, NOT markdown bold **TL;DR**.
|
|
336
|
+
|
|
337
|
+
### Key changes
|
|
338
|
+
|
|
339
|
+
- **Short human-readable title** \u2014 1 sentence per change. Write a short prose phrase (title case or sentence case); when you name a file, type, or function, put that name in backticks (e.g. **Add \`TodoTracker\` for live checklists**). A reviewer should understand the full PR from this list alone.
|
|
340
|
+
|
|
341
|
+
<sub><b>Summary</b> \uFF5C {file_count} files \uFF5C {commit_count} commits \uFF5C base: \`{base}\` \u2190 \`{head}\`</sub>
|
|
342
|
+
NOTE: the metadata line goes AFTER the bullet list, not before it.
|
|
343
|
+
|
|
344
|
+
Then for each key change, a ## section with a short descriptive title that reads like a documentation heading (e.g. ## Live todo checklist tracking).
|
|
345
|
+
|
|
346
|
+
<br/>
|
|
347
|
+
|
|
348
|
+
## Example readable section title
|
|
349
|
+
|
|
350
|
+
> **Before:** [old behavior/state]<br/>**After:** [new behavior/state]
|
|
351
|
+
IMPORTANT: Before and After MUST be on a SINGLE blockquote line with an inline <br/> between them. Two separate \`>\` lines creates a double line break.
|
|
352
|
+
|
|
353
|
+
1-2 sentences of explanation. Break up text with tables, blockquotes, or lists \u2014 NEVER 3+ plain paragraphs in a row.
|
|
354
|
+
|
|
355
|
+
If a change warrants deeper explanation, use a blockquoted details/summary framed as a question:
|
|
356
|
+
> <details><summary>How does X work?</summary>
|
|
357
|
+
> Extended explanation here.
|
|
358
|
+
> </details>
|
|
359
|
+
|
|
360
|
+
End each section with a file links trail (3-4 key files max):
|
|
361
|
+
[\`file.ts\`](https://github.com/{owner}/{repo}/pull/{number}/files#diff-{sha256hex_of_filepath}) \xB7 ...
|
|
362
|
+
|
|
363
|
+
Single-feature PRs: skip the ## sections. Fold before/after and explanation into the header after key changes.
|
|
364
|
+
|
|
365
|
+
CRITICAL \u2014 GitHub markdown rendering rule:
|
|
366
|
+
GitHub's markdown parser requires a blank line between ALL block-level elements. This includes transitions between: HTML tags (<br/>, <sub>, <details>, <b>, etc.) and markdown syntax (headings, lists, blockquotes, paragraphs). Without a blank line, GitHub treats the following content as a continuation of the HTML block and renders markdown syntax as literal text. ALWAYS separate block-level elements with a blank line.
|
|
367
|
+
|
|
368
|
+
Rules:
|
|
369
|
+
- \`##\` titles and key-change bullet lead-ins are plain-language summaries; backtick only actual code tokens (files, types, functions) where they appear in the title
|
|
370
|
+
- ALL variable names, identifiers, and file names in body text must be in backticks
|
|
371
|
+
- ALL file references MUST link to the PR Files Changed view. Compute anchors by running \`echo -n 'path/to/file.ts' | sha256sum\` via shell for each file. NEVER fabricate hex strings \u2014 run the actual command. If shell is unavailable, omit the #diff- anchor rather than guessing.
|
|
372
|
+
- Add <br/> before each ## heading for visual spacing. Do NOT use horizontal rules (---)
|
|
373
|
+
- Do NOT include raw diff stats like '+123 / -45' or line counts
|
|
374
|
+
- Do NOT include code blocks or repeat diff contents
|
|
375
|
+
- Do NOT include a changelog section \u2014 the key changes list serves this purpose
|
|
376
|
+
- Focus on *intent*, not *what* \u2014 the diff already shows what changed
|
|
377
|
+
- Get the file count and commit count from the checkout_pr metadata, not by counting manually`;
|
|
378
|
+
function learningsStep(t, n) {
|
|
379
|
+
return `${n}. **learnings** (only if high confidence): if you discovered something about repo setup, test commands, conventions, or patterns that you are confident is correct and would reliably help future runs, call \`${t("update_learnings")}\` to persist it. skip this step if you are unsure or the finding is speculative/one-off. format as a flat bullet list (\`- \` per line, one fact per bullet). merge with existing learnings from the prompt \u2014 pass the FULL merged list. deduplicate, and drop bullets that are clearly wrong or no longer relevant to the current codebase.`;
|
|
380
|
+
}
|
|
381
|
+
function computeModes(agentId) {
|
|
382
|
+
const t = (toolName) => formatMcpToolRef(agentId, toolName);
|
|
383
|
+
return [
|
|
384
|
+
{
|
|
385
|
+
name: "Build",
|
|
386
|
+
description: "Implement, build, create, or develop code changes; make specific changes to files or features; execute a plan; or handle tasks with specific implementation details",
|
|
387
|
+
prompt: `### Checklist
|
|
388
|
+
|
|
389
|
+
1. **plan** (optional, for complex tasks): analyze requirements, read AGENTS.md and relevant code, produce a step-by-step implementation plan.
|
|
390
|
+
|
|
391
|
+
2. **setup**: checkout or create the branch:
|
|
392
|
+
- **PR event, modifying the existing PR**: call \`${t("checkout_pr")}\`
|
|
393
|
+
- **new branch**: use \`${t("git")}\` to create a branch (\`git checkout -b pullfrog/branch-name\`)
|
|
394
|
+
|
|
395
|
+
3. **build**: implement changes using your native file and shell tools:
|
|
396
|
+
- follow the plan (if you ran a plan phase)
|
|
397
|
+
- plan your approach before writing code: identify which files need to change, key design decisions, and edge cases. for non-trivial changes, consider whether there's a more elegant approach.
|
|
398
|
+
- run relevant tests/lints before committing
|
|
399
|
+
|
|
400
|
+
4. **self-review**: delegate a read-only subagent to review your diff. the subagent must ONLY read files, grep, and search \u2014 no MCP tools, no writes, no shell commands, no side effects. provide it with the output of \`git diff\` and instruct it to look for bugs, logic errors, missing edge cases, and unintended changes. review its findings, address any valid points, and discard nitpicks or false positives. then:
|
|
401
|
+
- verify only intended changes are present, no debug artifacts or commented-out code remain, and no unrelated files were modified
|
|
402
|
+
- commit locally via shell (\`git add . && git commit -m "..."\`)
|
|
403
|
+
|
|
404
|
+
5. **finalize**:
|
|
405
|
+
- confirm a clean working tree, then push via \`${t("push_branch")}\` (see *SYSTEM* Git rules if this fails \u2014 prepush errors are usually the repo's tests/lint, not infra timeouts)
|
|
406
|
+
- create a PR via \`${t("create_pull_request")}\`
|
|
407
|
+
- call \`${t("report_progress")}\` with the PR link or the exact error if push/PR failed
|
|
408
|
+
|
|
409
|
+
${learningsStep(t, 6)}
|
|
410
|
+
|
|
411
|
+
### Notes
|
|
412
|
+
|
|
413
|
+
For simple, well-defined tasks, skip the plan phase and go straight to build.`
|
|
414
|
+
},
|
|
415
|
+
{
|
|
416
|
+
name: "AddressReviews",
|
|
417
|
+
description: "Address PR review feedback; respond to reviewer comments; make requested changes to an existing PR",
|
|
418
|
+
prompt: `### Checklist
|
|
419
|
+
|
|
420
|
+
1. Checkout the PR branch via \`${t("checkout_pr")}\`.
|
|
421
|
+
|
|
422
|
+
2. Fetch review comments via \`${t("get_review_comments")}\`.
|
|
423
|
+
|
|
424
|
+
3. For each comment:
|
|
425
|
+
- understand the feedback
|
|
426
|
+
- make the code change using your native tools
|
|
427
|
+
- record what was done
|
|
428
|
+
|
|
429
|
+
4. Quality check:
|
|
430
|
+
- test changes, then review the diff before committing \u2014 verify only intended changes are present, no debug artifacts remain, and the changes are clean enough that a senior engineer would approve without hesitation
|
|
431
|
+
- commit locally via shell (\`git add . && git commit -m "..."\`)
|
|
432
|
+
|
|
433
|
+
5. Finalize:
|
|
434
|
+
- confirm a clean working tree, then push via \`${t("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*)
|
|
435
|
+
- reply to each comment using \`${t("reply_to_review_comment")}\`
|
|
436
|
+
- resolve addressed threads via \`${t("resolve_review_thread")}\`
|
|
437
|
+
- call \`${t("report_progress")}\` with a brief summary (or the exact push error if push failed)
|
|
438
|
+
|
|
439
|
+
${learningsStep(t, 6)}`
|
|
440
|
+
},
|
|
441
|
+
{
|
|
442
|
+
name: "Review",
|
|
443
|
+
description: "Review code, PRs, or implementations; provide feedback or suggestions; identify issues; or check code quality, style, and correctness",
|
|
444
|
+
prompt: `### Checklist
|
|
445
|
+
|
|
446
|
+
1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`. Read the diff to identify the major areas of change.
|
|
447
|
+
|
|
448
|
+
2. For each area of change:
|
|
449
|
+
- read the diff and trace data flow, check boundaries, and verify assumptions
|
|
450
|
+
- plan your investigation: identify the highest-risk areas (tricky state transitions, boundary crossings, assumption chains) and prioritize depth over breadth
|
|
451
|
+
- use \`${t("get_pull_request")}\` and other read-only GitHub tools for additional context
|
|
452
|
+
- if the PR removes features, deletes exports, renames identifiers, or changes architectural patterns, run a dedicated impact analysis: list what changed, then use grep across code, tests, docs (\`docs/\`, \`wiki/\`), comments, configs, and UI to find stale references
|
|
453
|
+
- report impact-analysis findings in the summary body, ordered by severity (runtime breakage > incorrect docs > stale comments)
|
|
454
|
+
- draft inline comments with NEW line numbers from the diff \u2014 every comment must be actionable (2-3 sentences max)
|
|
455
|
+
- use GitHub permalink format for code references
|
|
456
|
+
- for large or cross-cutting PRs that touch disparate subsystems, consider delegating read-only subagents to investigate areas in parallel. subagents must ONLY read files, grep, and search \u2014 no MCP tools, no writes, no shell commands, no side effects. collect their findings and use them to draft comments.
|
|
457
|
+
|
|
458
|
+
3. Self-critique: review all drafted comments and drop any that are praise, style preferences, speculative/unverified claims, about pre-existing code unrelated to the PR, or not actionable.
|
|
459
|
+
|
|
460
|
+
4. Submit \u2014 ALWAYS submit exactly one review via \`${t("create_pull_request_review")}\`.
|
|
461
|
+
Do NOT call \`report_progress\` \u2014 the review is the final record and the progress
|
|
462
|
+
comment will be cleaned up automatically.
|
|
463
|
+
|
|
464
|
+
- **critical issues** (blocks merge \u2014 bugs, security, data loss):
|
|
465
|
+
\`approved: false\`. Body begins with a GitHub alert blockquote, e.g.:
|
|
466
|
+
\`> [!CAUTION]\\n> This PR introduces a race condition in ...\`
|
|
467
|
+
Follow with a brief summary if needed. Include all inline comments.
|
|
468
|
+
- **recommended changes** (non-critical):
|
|
469
|
+
\`approved: false\`. Body begins with a GitHub alert blockquote, e.g.:
|
|
470
|
+
\`> [!IMPORTANT]\\n> Consider adding input validation for ...\`
|
|
471
|
+
Follow with a brief summary if needed. Include all inline comments.
|
|
472
|
+
- **no actionable issues**:
|
|
473
|
+
\`approved: true\`, body: "Reviewed \u2014 no issues found."`
|
|
474
|
+
},
|
|
475
|
+
{
|
|
476
|
+
name: "IncrementalReview",
|
|
477
|
+
description: "Re-review a PR after new commits are pushed; focus on new changes since the last review",
|
|
478
|
+
prompt: `### Checklist
|
|
479
|
+
|
|
480
|
+
1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata, \`diffPath\` (full diff), and \`incrementalDiffPath\` (changes since last reviewed version, if available).
|
|
481
|
+
|
|
482
|
+
2. 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.
|
|
483
|
+
|
|
484
|
+
3. Fetch previous reviews via \`${t("list_pull_request_reviews")}\`. For the most recent Pullfrog review, call \`${t("get_review_comments")}\` with the review ID to retrieve specific prior line-level feedback.
|
|
485
|
+
|
|
486
|
+
4. For each area of the new changes:
|
|
487
|
+
- review the incremental diff while using the full diff for context
|
|
488
|
+
- check whether prior review feedback was addressed by the new commits
|
|
489
|
+
- trace data flow, check boundaries, verify assumptions, consider lifecycle, spot performance issues
|
|
490
|
+
- if the new commits remove, rename, or deprecate anything, run impact analysis with grep across code/tests/docs/comments/configs to find stale references and include those findings in the summary body
|
|
491
|
+
- never repeat prior feedback. only comment on genuinely new issues introduced by the new commits.
|
|
492
|
+
- draft inline comments with NEW line numbers from the full PR diff \u2014 every comment must be actionable (2-3 sentences max)
|
|
493
|
+
- for large or cross-cutting PRs, consider delegating read-only subagents for parallel investigation. subagents must ONLY read files, grep, and search \u2014 no MCP tools, no writes, no shell commands, no side effects. collect their findings and use them to draft comments.
|
|
494
|
+
|
|
495
|
+
5. Self-critique: drop any comments that are praise, style preferences, speculative, about pre-existing code, or not actionable.
|
|
496
|
+
|
|
497
|
+
6. **Summarize**: build two distinct sections for the review body:
|
|
498
|
+
a. **Reviewed changes**: summarize at the logical-change level, not per-file. each bullet starts with a past-tense verb (e.g. \`- Extracted shared CLI runtime into a single module\`, \`- Renamed package to pullfrog\`). avoid file paths unless they add clarity. if the changes can be described in one sentence, use one sentence \u2014 no bullets needed.
|
|
499
|
+
b. **Prior review feedback** (only if any were addressed): list only the prior review comments that WERE addressed by the new commits (\`- [x] safeParse instead of parse \u2014 addressed\`). omit unaddressed comments. omit this entire section if nothing was addressed. a change can appear in both sections.
|
|
500
|
+
- no headings, no tables, no prose paragraphs in either section \u2014 just bullets
|
|
501
|
+
- in some cases you may receive a complete diff for the whole pull request instead of an incremental one. when this happens, you will need to determine what changes have happened since Pullfrog's most recent review.
|
|
502
|
+
|
|
503
|
+
7. Submit \u2014 Do NOT call \`report_progress\` or \`create_issue_comment\` \u2014 the review is the final record and the progress comment will be cleaned up automatically. the review body always includes the reviewed changes from step 6a. append \`Prior review feedback:\\n\` with the checklist from step 6b only if any prior comments were addressed. Follow these rules:
|
|
504
|
+
- IF NO NEW ISSUES, NON-SUBSTANTIVE CHANGES ONLY (trivial formatting, import reordering, comment tweaks): do NOT submit a review. Do NOT call \`report_progress\`. Exit \u2014 the progress comment will be cleaned up automatically.
|
|
505
|
+
- ELSE IF NEW CRITICAL ISSUES (blocks merge): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with a GitHub alert blockquote (e.g. \`> [!CAUTION]\\n> This PR introduces ...\`), then the reviewed changes summary and prior feedback (if any).
|
|
506
|
+
- ELSE IF NEW RECOMMENDED CHANGES (non-critical): call \`${t("create_pull_request_review")}\` with \`approved: false\`, all comments, and the review body. body opens with \`> [!IMPORTANT]\\n> ...\` alert, then the reviewed changes summary and prior feedback (if any).
|
|
507
|
+
- ELSE IF NO NEW ISSUES, SUBSTANTIVE CHANGES (new functionality, behavior changes, or fixes to prior review feedback): call \`${t("create_pull_request_review")}\` to create a PR review. If all previous reviews have been properly addressed and no new issues were discovered, you can set \`approved: true\`. body opens with \`No new issues. Reviewed the following changes:\\n\`, then the reviewed changes summary and prior feedback (if any).`
|
|
508
|
+
},
|
|
509
|
+
{
|
|
510
|
+
name: "Plan",
|
|
511
|
+
description: "Create plans, break down tasks, outline steps, analyze requirements, understand scope of work, or provide task breakdowns",
|
|
512
|
+
prompt: `### Checklist
|
|
513
|
+
|
|
514
|
+
1. Analyze the task and gather context:
|
|
515
|
+
- read AGENTS.md and relevant codebase files
|
|
516
|
+
- understand the architecture and constraints
|
|
517
|
+
|
|
518
|
+
2. Produce a structured, actionable plan with clear milestones.
|
|
519
|
+
|
|
520
|
+
3. Call \`${t("report_progress")}\` with the plan.
|
|
521
|
+
|
|
522
|
+
${learningsStep(t, 4)}`
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
name: "Fix",
|
|
526
|
+
description: "Fix CI failures; debug failing tests or builds; investigate and resolve check suite failures",
|
|
527
|
+
prompt: `### Checklist
|
|
528
|
+
|
|
529
|
+
1. Checkout the PR branch via \`${t("checkout_pr")}\`.
|
|
530
|
+
|
|
531
|
+
2. Fetch check suite logs via \`${t("get_check_suite_logs")}\`.
|
|
532
|
+
|
|
533
|
+
3. **CRITICAL**: verify the failure was INTRODUCED BY THIS PR before fixing. If unrelated, abort and report.
|
|
534
|
+
|
|
535
|
+
4. Diagnose and fix:
|
|
536
|
+
- read the workflow file, reproduce locally with the EXACT same commands CI runs
|
|
537
|
+
- fix the issue using your native file and shell tools
|
|
538
|
+
- verify the fix by re-running the exact CI command
|
|
539
|
+
- review the diff before committing \u2014 verify only the fix is present, no debug artifacts, no unrelated changes. the fix should be clean enough that a senior engineer would approve without hesitation.
|
|
540
|
+
- commit locally via shell (\`git add . && git commit -m "..."\`)
|
|
541
|
+
|
|
542
|
+
5. Finalize:
|
|
543
|
+
- confirm a clean working tree, then push via \`${t("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*)
|
|
544
|
+
- call \`${t("report_progress")}\` with the diagnosis and fix summary (or the exact push error if push failed)
|
|
545
|
+
|
|
546
|
+
${learningsStep(t, 6)}`
|
|
547
|
+
},
|
|
548
|
+
{
|
|
549
|
+
name: "ResolveConflicts",
|
|
550
|
+
description: "Resolve merge conflicts in a PR branch against the base branch",
|
|
551
|
+
prompt: `### Checklist
|
|
552
|
+
|
|
553
|
+
1. **Setup**:
|
|
554
|
+
- Call \`${t("checkout_pr")}\` to get the PR branch.
|
|
555
|
+
- Call \`${t("get_pull_request")}\` to identify the base branch (e.g., 'main').
|
|
556
|
+
- Call \`${t("git_fetch")}\` to fetch the base branch.
|
|
557
|
+
|
|
558
|
+
2. **Merge Attempt**:
|
|
559
|
+
- Run \`git merge origin/<base_branch>\` via shell.
|
|
560
|
+
- If it succeeds automatically, confirm a clean working tree, push via \`${t("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*), and call \`${t("report_progress")}\` with a brief success note or the exact push error if push failed \u2014 **then stop; do not run steps 3\u20134.**
|
|
561
|
+
- If it fails (conflicts), resolve them manually (continue to steps 3\u20134).
|
|
562
|
+
|
|
563
|
+
3. **Resolve Conflicts**:
|
|
564
|
+
- Run \`git status\` or parse the merge output to find the list of conflicting files.
|
|
565
|
+
- For each conflicting file: read it, find the conflict markers (\`<<<<<<<\`, \`=======\`, \`>>>>>>>\`), understand the code context, and rewrite the file with the correct resolution. Remove all markers.
|
|
566
|
+
- Verify the file syntax is correct after resolution.
|
|
567
|
+
|
|
568
|
+
4. **Finalize**:
|
|
569
|
+
- Run a final verification (build/test) to ensure the resolution works.
|
|
570
|
+
- \`git add . && git commit -m "resolve merge conflicts"\`
|
|
571
|
+
- confirm a clean working tree, then push via \`${t("push_branch")}\` (same push/prepush guidance as Build mode in *SYSTEM*)
|
|
572
|
+
- Call \`${t("report_progress")}\` with a summary of what was resolved (or the exact push error if push failed)`
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
name: "Task",
|
|
576
|
+
description: "General-purpose tasks that don't fit other modes: answering questions, adding comments, labeling, running ad-hoc commands, or any direct request",
|
|
577
|
+
prompt: `### Checklist
|
|
578
|
+
|
|
579
|
+
1. Analyze the task. For simple operations (labeling, commenting, answering questions, running a single command), handle directly.
|
|
580
|
+
|
|
581
|
+
2. For substantial work \u2014 code changes across multiple files, multi-step investigations:
|
|
582
|
+
- plan your approach before starting
|
|
583
|
+
- use native file and shell tools for local operations
|
|
584
|
+
- use ${pullfrogMcpName} MCP tools for GitHub/git operations
|
|
585
|
+
- if code changes are needed: review your own diff before committing \u2014 verify only intended changes are present, no debug artifacts remain, and the changes are clean enough that a senior engineer would approve without hesitation
|
|
586
|
+
|
|
587
|
+
3. Finalize:
|
|
588
|
+
- if code changes were made, push to a pull request (new or existing) using \`${t("push_branch")}\` and \`${t("create_pull_request")}\` as needed. \`git status\` must be clean before you finish (see *SYSTEM* Git rules if push fails).
|
|
589
|
+
- call \`${t("report_progress")}\` once with results \u2014 include exact tool errors if push or PR creation failed
|
|
590
|
+
- if the task involved labeling, commenting, or other GitHub operations, perform those directly
|
|
591
|
+
|
|
592
|
+
${learningsStep(t, 4)}`
|
|
593
|
+
},
|
|
594
|
+
{
|
|
595
|
+
name: "Summarize",
|
|
596
|
+
description: "Summarize a PR with a structured comment that is updated in place on subsequent pushes",
|
|
597
|
+
prompt: `### Checklist
|
|
598
|
+
|
|
599
|
+
1. Checkout the PR via \`${t("checkout_pr")}\` \u2014 this returns PR metadata and a \`diffPath\`.
|
|
600
|
+
2. Read the diff using the TOC to selectively read relevant sections (not the entire file). Produce a structured summary. If EVENT INSTRUCTIONS specify a custom format, follow that instead of the default format below.
|
|
601
|
+
3. Call \`${t("create_issue_comment")}\` with \`type: "Summary"\` and the summary body.
|
|
602
|
+
4. Call \`${t("report_progress")}\` with a brief note (e.g., "Posted PR summary.").
|
|
603
|
+
|
|
604
|
+
${PR_SUMMARY_FORMAT}`
|
|
605
|
+
}
|
|
606
|
+
];
|
|
607
|
+
}
|
|
608
|
+
var modes = computeModes("opentoad");
|
|
609
|
+
|
|
610
|
+
// utils/buildPullfrogFooter.ts
|
|
611
|
+
var PULLFROG_DIVIDER = "<!-- PULLFROG_DIVIDER_DO_NOT_REMOVE_PLZ -->";
|
|
612
|
+
var FROG_LOGO = `<a href="https://pullfrog.com"><picture><source media="(prefers-color-scheme: dark)" srcset="https://pullfrog.com/logos/frog-white-full-18px.png"><img src="https://pullfrog.com/logos/frog-green-full-18px.png" width="9px" height="9px" style="vertical-align: middle; " alt="Pullfrog"></picture></a>`;
|
|
613
|
+
function formatModelLabel(slug) {
|
|
614
|
+
const alias = modelAliases.find((a) => a.slug === slug);
|
|
615
|
+
if (!alias) return `\`${slug}\``;
|
|
616
|
+
return alias.isFree ? `\`${alias.displayName}\` (free)` : `\`${alias.displayName}\``;
|
|
617
|
+
}
|
|
618
|
+
function buildPullfrogFooter(params) {
|
|
619
|
+
const parts = [];
|
|
620
|
+
if (params.customParts) {
|
|
621
|
+
parts.push(...params.customParts);
|
|
622
|
+
}
|
|
623
|
+
if (params.workflowRunUrl) {
|
|
624
|
+
parts.push(`[View workflow run](${params.workflowRunUrl})`);
|
|
625
|
+
} else if (params.workflowRun) {
|
|
626
|
+
const baseUrl = `https://github.com/${params.workflowRun.owner}/${params.workflowRun.repo}/actions/runs/${params.workflowRun.runId}`;
|
|
627
|
+
const url = params.workflowRun.jobId ? `${baseUrl}/job/${params.workflowRun.jobId}` : baseUrl;
|
|
628
|
+
parts.push(`[View workflow run](${url})`);
|
|
629
|
+
}
|
|
630
|
+
if (params.triggeredBy) {
|
|
631
|
+
parts.push("Triggered by [Pullfrog](https://pullfrog.com)");
|
|
632
|
+
}
|
|
633
|
+
if (params.model) {
|
|
634
|
+
parts.push(`Using ${formatModelLabel(params.model)}`);
|
|
635
|
+
}
|
|
636
|
+
const allParts = [...parts, "[\u{1D54F}](https://x.com/pullfrogai)"];
|
|
637
|
+
return `
|
|
638
|
+
|
|
639
|
+
${PULLFROG_DIVIDER}
|
|
640
|
+
<sup>${FROG_LOGO} \uFF5C ${allParts.join(" \uFF5C ")}</sup>`;
|
|
641
|
+
}
|
|
642
|
+
function stripExistingFooter(body) {
|
|
643
|
+
const dividerIndex = body.indexOf(PULLFROG_DIVIDER);
|
|
644
|
+
if (dividerIndex === -1) {
|
|
645
|
+
return body;
|
|
646
|
+
}
|
|
647
|
+
return body.substring(0, dividerIndex).trimEnd();
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
// utils/time.ts
|
|
651
|
+
var TIMEOUT_DISABLED = "none";
|
|
652
|
+
var TIME_STRING_REGEX = /^(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$/;
|
|
653
|
+
function parseTimeString(input) {
|
|
654
|
+
const match = input.match(TIME_STRING_REGEX);
|
|
655
|
+
if (!match || !match[1] && !match[2] && !match[3]) return null;
|
|
656
|
+
const hours = parseInt(match[1] || "0", 10);
|
|
657
|
+
const minutes = parseInt(match[2] || "0", 10);
|
|
658
|
+
const seconds = parseInt(match[3] || "0", 10);
|
|
659
|
+
return (hours * 3600 + minutes * 60 + seconds) * 1e3;
|
|
660
|
+
}
|
|
661
|
+
function isValidTimeString(input) {
|
|
662
|
+
return parseTimeString(input) !== null;
|
|
663
|
+
}
|
|
664
|
+
export {
|
|
665
|
+
PULLFROG_DIVIDER,
|
|
666
|
+
TIMEOUT_DISABLED,
|
|
667
|
+
buildPullfrogFooter,
|
|
668
|
+
getModelEnvVars,
|
|
669
|
+
getModelProvider,
|
|
670
|
+
getProviderDisplayName,
|
|
671
|
+
isValidTimeString,
|
|
672
|
+
modelAliases,
|
|
673
|
+
modes,
|
|
674
|
+
parseModel,
|
|
675
|
+
parseTimeString,
|
|
676
|
+
providers,
|
|
677
|
+
pullfrogMcpName,
|
|
678
|
+
resolveCliModel,
|
|
679
|
+
resolveModelSlug,
|
|
680
|
+
stripExistingFooter
|
|
681
|
+
};
|