Make prompt construction more disciplined (#173)
* Make prompt construction more disciplined * Clean up * Tweaks
This commit is contained in:
committed by
pullfrog[bot]
parent
54279e313b
commit
210084a3b6
+9
-1
@@ -33,7 +33,15 @@ export const agent = <const input extends AgentInput>(input: input): defineAgent
|
|||||||
const search = ctx.payload.search;
|
const search = ctx.payload.search;
|
||||||
const write = ctx.payload.write;
|
const write = ctx.payload.write;
|
||||||
log.info(`» running ${input.name} with effort=${ctx.payload.effort}...`);
|
log.info(`» running ${input.name} with effort=${ctx.payload.effort}...`);
|
||||||
log.box(ctx.instructions.user.trim() + "\n\n" + ctx.instructions.event.trim(), {
|
// build log box content: eventInstructions (if any) + user request (if any) + event data
|
||||||
|
const logParts = [
|
||||||
|
ctx.instructions.eventInstructions
|
||||||
|
? `EVENT-LEVEL INSTRUCTIONS:\n${ctx.instructions.eventInstructions}`
|
||||||
|
: null,
|
||||||
|
ctx.instructions.user ? `USER REQUEST:\n${ctx.instructions.user}` : null,
|
||||||
|
ctx.instructions.event,
|
||||||
|
].filter(Boolean);
|
||||||
|
log.box(logParts.join("\n\n---\n\n"), {
|
||||||
title: "Instructions",
|
title: "Instructions",
|
||||||
});
|
});
|
||||||
log.info(`» tool permissions: web=${web}, search=${search}, write=${write}, bash=${bash}`);
|
log.info(`» tool permissions: web=${web}, search=${search}, write=${write}, bash=${bash}`);
|
||||||
|
|||||||
@@ -138323,8 +138323,7 @@ function GetIssueCommentsTool(ctx) {
|
|||||||
comments: comments.map((comment) => ({
|
comments: comments.map((comment) => ({
|
||||||
id: comment.id,
|
id: comment.id,
|
||||||
body: comment.body,
|
body: comment.body,
|
||||||
user: comment.user?.login,
|
user: comment.user?.login
|
||||||
author_association: comment.author_association
|
|
||||||
})),
|
})),
|
||||||
count: comments.length
|
count: comments.length
|
||||||
};
|
};
|
||||||
@@ -139239,7 +139238,7 @@ function computeModes() {
|
|||||||
{
|
{
|
||||||
name: "Build",
|
name: "Build",
|
||||||
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",
|
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",
|
||||||
prompt: `Follow these steps. THINK HARDER.
|
prompt: `Follow these steps exactly.
|
||||||
1. Determine whether to work on the current branch or create a new one:
|
1. Determine whether to work on the current branch or create a new one:
|
||||||
- **PR event, modifying the existing PR**: The PR branch is probably already checked out. Continue on this branch.
|
- **PR event, modifying the existing PR**: The PR branch is probably already checked out. Continue on this branch.
|
||||||
- **PR event, but user wants a NEW branch/PR**: Use \`${ghPullfrogMcpName}/create_branch\` to create a new branch from the current HEAD.
|
- **PR event, but user wants a NEW branch/PR**: Use \`${ghPullfrogMcpName}/create_branch\` to create a new branch from the current HEAD.
|
||||||
@@ -139253,17 +139252,19 @@ function computeModes() {
|
|||||||
|
|
||||||
4. Understand the requirements and any existing plan
|
4. Understand the requirements and any existing plan
|
||||||
|
|
||||||
5. Make the necessary code changes using file operations. Then use ${ghPullfrogMcpName}/commit_files to commit your changes, and ${ghPullfrogMcpName}/push_branch to push the branch. Do NOT use git commands like \`git commit\` or \`git push\` directly.
|
5. Make the necessary code changes using file operations. You should change the minimum amount of code necessary to accomplish your task. Emphasize code quality and elegance.
|
||||||
|
|
||||||
6. Test your changes to ensure they work correctly
|
6. Then use ${ghPullfrogMcpName}/commit_files to commit your changes, and ${ghPullfrogMcpName}/push_branch to push the branch. Do NOT use git commands like \`git commit\` or \`git push\` directly.
|
||||||
|
|
||||||
7. ${reportProgressInstruction}
|
7. Test your changes to ensure they work correctly
|
||||||
|
|
||||||
8. Determine whether to create a PR:
|
8. ${reportProgressInstruction}
|
||||||
|
|
||||||
|
9. Determine whether to create a PR (if not already on a PR branch):
|
||||||
- **Default behavior**: Create a PR using ${ghPullfrogMcpName}/create_pull_request with an informative title and body. If relevant, indicate which issue the PR addresses (e.g. "Fixes #123").
|
- **Default behavior**: Create a PR using ${ghPullfrogMcpName}/create_pull_request with an informative title and body. If relevant, indicate which issue the PR addresses (e.g. "Fixes #123").
|
||||||
- **Branch-only request**: If the user explicitly asks for a branch without a PR (e.g. "don't create a PR", "branch only", "just create a branch"), do NOT create a PR. Simply push the branch and report the branch link.
|
- **Branch-only request**: If the user explicitly asks for a branch without a PR (e.g. "don't create a PR", "branch only", "just create a branch"), do NOT create a PR. Simply push the branch and report the branch link.
|
||||||
|
|
||||||
9. Call report_progress one final time ONLY if you haven't already included all the important information (PR links, branch links, summary) in a previous report_progress call. If you already called report_progress with complete information including PR links after creating the PR, you do NOT need to call it again. Only make a final call if you need to add missing information. When making the final call, ensure it includes:
|
10. Call report_progress one final time ONLY if you haven't already included all the important information (PR links, branch links, summary) in a previous report_progress call. If you already called report_progress with complete information including PR links after creating the PR, you do NOT need to call it again. Only make a final call if you need to add missing information. When making the final call, ensure it includes:
|
||||||
- A summary of what was accomplished
|
- A summary of what was accomplished
|
||||||
- Links to any artifacts created (PRs, branches, issues)
|
- Links to any artifacts created (PRs, branches, issues)
|
||||||
- If you created a PR, ALWAYS include the PR link. e.g.:
|
- If you created a PR, ALWAYS include the PR link. e.g.:
|
||||||
@@ -156190,7 +156191,14 @@ var agent = (input) => {
|
|||||||
const search2 = ctx.payload.search;
|
const search2 = ctx.payload.search;
|
||||||
const write = ctx.payload.write;
|
const write = ctx.payload.write;
|
||||||
log.info(`\xBB running ${input.name} with effort=${ctx.payload.effort}...`);
|
log.info(`\xBB running ${input.name} with effort=${ctx.payload.effort}...`);
|
||||||
log.box(ctx.instructions.user.trim() + "\n\n" + ctx.instructions.event.trim(), {
|
const logParts = [
|
||||||
|
ctx.instructions.eventInstructions ? `EVENT-LEVEL INSTRUCTIONS:
|
||||||
|
${ctx.instructions.eventInstructions}` : null,
|
||||||
|
ctx.instructions.user ? `USER REQUEST:
|
||||||
|
${ctx.instructions.user}` : null,
|
||||||
|
ctx.instructions.event
|
||||||
|
].filter(Boolean);
|
||||||
|
log.box(logParts.join("\n\n---\n\n"), {
|
||||||
title: "Instructions"
|
title: "Instructions"
|
||||||
});
|
});
|
||||||
log.info(`\xBB tool permissions: web=${web}, search=${search2}, write=${write}, bash=${bash}`);
|
log.info(`\xBB tool permissions: web=${web}, search=${search2}, write=${write}, bash=${bash}`);
|
||||||
@@ -157885,6 +157893,7 @@ function buildRuntimeContext(ctx) {
|
|||||||
const {
|
const {
|
||||||
"~pullfrog": _,
|
"~pullfrog": _,
|
||||||
prompt: _p,
|
prompt: _p,
|
||||||
|
eventInstructions: _ei,
|
||||||
repoInstructions: _r,
|
repoInstructions: _r,
|
||||||
event: _e,
|
event: _e,
|
||||||
...payloadRest
|
...payloadRest
|
||||||
@@ -157911,24 +157920,26 @@ function buildRuntimeContext(ctx) {
|
|||||||
const filtered = Object.fromEntries(Object.entries(data).filter(([_2, v]) => v !== void 0));
|
const filtered = Object.fromEntries(Object.entries(data).filter(([_2, v]) => v !== void 0));
|
||||||
return encode(filtered);
|
return encode(filtered);
|
||||||
}
|
}
|
||||||
function buildEventData(event) {
|
function buildEventTitleBody(event) {
|
||||||
const { title, body, trigger, ...rest } = event;
|
|
||||||
const sections = [];
|
const sections = [];
|
||||||
const trimmedTitle = typeof title === "string" ? title.trim() : "";
|
const trimmedTitle = typeof event.title === "string" ? event.title.trim() : "";
|
||||||
const trimmedBody = typeof body === "string" ? body.trim() : "";
|
const trimmedBody = typeof event.body === "string" ? event.body.trim() : "";
|
||||||
if (trimmedTitle) {
|
if (trimmedTitle) {
|
||||||
sections.push(`# ${trimmedTitle}`);
|
sections.push(`# ${trimmedTitle}`);
|
||||||
}
|
}
|
||||||
if (trimmedBody) {
|
if (trimmedBody) {
|
||||||
sections.push(trimmedBody);
|
sections.push(trimmedBody);
|
||||||
}
|
}
|
||||||
const restWithTrigger = trigger === "workflow_dispatch" ? rest : { trigger, ...rest };
|
|
||||||
if (Object.keys(restWithTrigger).length > 0) {
|
|
||||||
if (sections.length > 0) sections.push("---");
|
|
||||||
sections.push(encode(restWithTrigger));
|
|
||||||
}
|
|
||||||
return sections.join("\n\n");
|
return sections.join("\n\n");
|
||||||
}
|
}
|
||||||
|
function buildEventMetadata(event) {
|
||||||
|
const { title: _t, body: _b, trigger, ...rest } = event;
|
||||||
|
const restWithTrigger = trigger === "workflow_dispatch" ? rest : { trigger, ...rest };
|
||||||
|
if (Object.keys(restWithTrigger).length === 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return encode(restWithTrigger);
|
||||||
|
}
|
||||||
function getShellInstructions(bash) {
|
function getShellInstructions(bash) {
|
||||||
const backgroundInstructions = `For long-running processes (dev servers, watchers), use \`bash({ command, background: true })\` which returns a handle. Use \`${ghPullfrogMcpName}/kill_background\` to stop background processes by handle.`;
|
const backgroundInstructions = `For long-running processes (dev servers, watchers), use \`bash({ command, background: true })\` which returns a handle. Use \`${ghPullfrogMcpName}/kill_background\` to stop background processes by handle.`;
|
||||||
switch (bash) {
|
switch (bash) {
|
||||||
@@ -157945,11 +157956,16 @@ function getShellInstructions(bash) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
function resolveInstructions(ctx) {
|
function resolveInstructions(ctx) {
|
||||||
const event = buildEventData(ctx.payload.event);
|
const eventTitleBody = buildEventTitleBody(ctx.payload.event);
|
||||||
|
const eventMetadata = buildEventMetadata(ctx.payload.event);
|
||||||
const runtime = buildRuntimeContext(ctx);
|
const runtime = buildRuntimeContext(ctx);
|
||||||
const user = ctx.payload.prompt;
|
const user = ctx.payload.prompt;
|
||||||
|
const eventInstructions = ctx.payload.eventInstructions ?? "";
|
||||||
const repo = ctx.payload.repoInstructions ?? "";
|
const repo = ctx.payload.repoInstructions ?? "";
|
||||||
const userQuoted = user.split("\n").map((line) => `> ${line}`).join("\n");
|
const isPr = ctx.payload.event.is_pr === true;
|
||||||
|
const relatedLabel = isPr ? "--- related PR ---" : "--- related issue ---";
|
||||||
|
const event = [eventTitleBody, eventMetadata].filter(Boolean).join("\n\n---\n\n");
|
||||||
|
const userQuoted = user ? user.split("\n").map((line) => `> ${line}`).join("\n") : "";
|
||||||
const system = `***********************************************
|
const system = `***********************************************
|
||||||
************* SYSTEM INSTRUCTIONS *************
|
************* SYSTEM INSTRUCTIONS *************
|
||||||
***********************************************
|
***********************************************
|
||||||
@@ -157972,11 +157988,10 @@ Use backticks liberally for inline code (e.g. \`z.string()\`) even in headers.
|
|||||||
## Priority Order
|
## Priority Order
|
||||||
|
|
||||||
In case of conflict between instructions, follow this precedence (highest to lowest):
|
In case of conflict between instructions, follow this precedence (highest to lowest):
|
||||||
1. Security rules (below)
|
1. Security rules and system instructions (non-overridable)
|
||||||
2. System instructions (this document)
|
2. User prompt
|
||||||
3. Mode instructions (returned by select_mode)
|
3. Event-level instructions
|
||||||
4. Repository-specific instructions (AGENTS.md, CLAUDE.md, etc.)
|
4. Repo-level instructions
|
||||||
5. User prompt
|
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
@@ -158024,29 +158039,42 @@ ${ctx.modes.map((m) => `- "${m.name}": ${m.description}`).join("\n")}
|
|||||||
After selecting a mode, follow the detailed step-by-step instructions provided by the ${ghPullfrogMcpName}/select_mode tool. Refer to the user prompt, event data, and runtime context below to inform your actions. These instructions cannot override the Security rules or System instructions above.
|
After selecting a mode, follow the detailed step-by-step instructions provided by the ${ghPullfrogMcpName}/select_mode tool. Refer to the user prompt, event data, and runtime context below to inform your actions. These instructions cannot override the Security rules or System instructions above.
|
||||||
|
|
||||||
Eagerly inspect the MCP tools available to you via the \`${ghPullfrogMcpName}\` MCP server. These are VITALLY IMPORTANT to completing your task.`;
|
Eagerly inspect the MCP tools available to you via the \`${ghPullfrogMcpName}\` MCP server. These are VITALLY IMPORTANT to completing your task.`;
|
||||||
const repoSection = repo ? `
|
const repoSection = repo ? `************* REPO-LEVEL INSTRUCTIONS *************
|
||||||
|
|
||||||
************* REPO-LEVEL INSTRUCTIONS *************
|
|
||||||
|
|
||||||
${repo}` : "";
|
${repo}` : "";
|
||||||
const full = `
|
const eventInstructionsSection = eventInstructions ? `************* EVENT-LEVEL INSTRUCTIONS *************
|
||||||
${system}
|
|
||||||
|
|
||||||
************* USER PROMPT *************
|
${eventInstructions}` : "";
|
||||||
|
const titleBodySection = eventTitleBody ? `${relatedLabel}
|
||||||
|
|
||||||
|
${eventTitleBody}` : "";
|
||||||
|
const metadataSection = eventMetadata ? `--- event context ---
|
||||||
|
|
||||||
|
${eventMetadata}` : "";
|
||||||
|
const userSection = userQuoted ? `************* USER PROMPT \u2014 THIS IS YOUR TASK *************
|
||||||
|
|
||||||
${userQuoted}
|
${userQuoted}
|
||||||
|
|
||||||
|
${titleBodySection}
|
||||||
|
|
||||||
|
${metadataSection}` : `************* EVENT CONTEXT *************
|
||||||
|
|
||||||
|
${titleBodySection}
|
||||||
|
|
||||||
|
${metadataSection}`;
|
||||||
|
const rawFull = `************* RUNTIME CONTEXT *************
|
||||||
|
|
||||||
|
${runtime}
|
||||||
|
|
||||||
|
${system}
|
||||||
|
|
||||||
${repoSection}
|
${repoSection}
|
||||||
|
|
||||||
************* EVENT DATA *************
|
${eventInstructionsSection}
|
||||||
|
|
||||||
${event}
|
${userSection}`;
|
||||||
|
const full = rawFull.trim().replace(/\n{3,}/g, "\n\n");
|
||||||
************* RUNTIME CONTEXT *************
|
return { full, system, user, eventInstructions, repo, event, runtime };
|
||||||
|
|
||||||
The following contains the agent configuration (agent, effort, permissions) and runtime environment details.
|
|
||||||
|
|
||||||
${runtime}`;
|
|
||||||
return { full, system, user, repo, event, runtime };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// utils/normalizeEnv.ts
|
// utils/normalizeEnv.ts
|
||||||
@@ -158123,9 +158151,10 @@ var ToolPermissionInput = type.enumerated("disabled", "enabled");
|
|||||||
var BashPermissionInput = type.enumerated("disabled", "restricted", "enabled");
|
var BashPermissionInput = type.enumerated("disabled", "restricted", "enabled");
|
||||||
var JsonPayload = type({
|
var JsonPayload = type({
|
||||||
"~pullfrog": "true",
|
"~pullfrog": "true",
|
||||||
"version": "string",
|
version: "string",
|
||||||
"agent?": AgentName.or("undefined"),
|
"agent?": AgentName.or("undefined"),
|
||||||
"prompt?": "string",
|
"prompt?": "string",
|
||||||
|
"eventInstructions?": "string",
|
||||||
"repoInstructions?": "string",
|
"repoInstructions?": "string",
|
||||||
"event?": "object",
|
"event?": "object",
|
||||||
"effort?": Effort.or("undefined")
|
"effort?": Effort.or("undefined")
|
||||||
@@ -158204,6 +158233,7 @@ function resolvePayload(repoSettings) {
|
|||||||
// inverted: jsonPayload.prompt extracts the text from the JSON payload,
|
// inverted: jsonPayload.prompt extracts the text from the JSON payload,
|
||||||
// whereas inputs.prompt IS the raw JSON string when internally dispatched
|
// whereas inputs.prompt IS the raw JSON string when internally dispatched
|
||||||
prompt: jsonPayload?.prompt ?? inputs.prompt,
|
prompt: jsonPayload?.prompt ?? inputs.prompt,
|
||||||
|
eventInstructions: jsonPayload?.eventInstructions,
|
||||||
repoInstructions: jsonPayload?.repoInstructions,
|
repoInstructions: jsonPayload?.repoInstructions,
|
||||||
event,
|
event,
|
||||||
effort: inputs.effort ?? jsonPayload?.effort ?? "auto",
|
effort: inputs.effort ?? jsonPayload?.effort ?? "auto",
|
||||||
|
|||||||
+3
-1
@@ -245,8 +245,10 @@ export interface WriteablePayload {
|
|||||||
version: string;
|
version: string;
|
||||||
/** agent slug identifier (e.g., "claude", "codex", "gemini") */
|
/** agent slug identifier (e.g., "claude", "codex", "gemini") */
|
||||||
agent?: AgentName | undefined;
|
agent?: AgentName | undefined;
|
||||||
/** the prompt/instructions for the agent to execute (body if @pullfrog tagged + per-trigger instructions) */
|
/** the user's actual request (body if @pullfrog tagged) */
|
||||||
prompt: string;
|
prompt: string;
|
||||||
|
/** event-level instructions for this trigger type (macro-expanded server-side) */
|
||||||
|
eventInstructions?: string | undefined;
|
||||||
/** repo-level instructions (macro-expanded server-side) */
|
/** repo-level instructions (macro-expanded server-side) */
|
||||||
repoInstructions?: string | undefined;
|
repoInstructions?: string | undefined;
|
||||||
/** event data from webhook payload - discriminated union based on trigger field */
|
/** event data from webhook payload - discriminated union based on trigger field */
|
||||||
|
|||||||
@@ -28,7 +28,6 @@ export function GetIssueCommentsTool(ctx: ToolContext) {
|
|||||||
id: comment.id,
|
id: comment.id,
|
||||||
body: comment.body,
|
body: comment.body,
|
||||||
user: comment.user?.login,
|
user: comment.user?.login,
|
||||||
author_association: comment.author_association,
|
|
||||||
})),
|
})),
|
||||||
count: comments.length,
|
count: comments.length,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export function computeModes(): Mode[] {
|
|||||||
name: "Build",
|
name: "Build",
|
||||||
description:
|
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",
|
"Implement, build, create, or develop code changes; make specific changes to files or features; execute a plan; or handle tasks with specific implementation details",
|
||||||
prompt: `Follow these steps. THINK HARDER.
|
prompt: `Follow these steps exactly.
|
||||||
1. Determine whether to work on the current branch or create a new one:
|
1. Determine whether to work on the current branch or create a new one:
|
||||||
- **PR event, modifying the existing PR**: The PR branch is probably already checked out. Continue on this branch.
|
- **PR event, modifying the existing PR**: The PR branch is probably already checked out. Continue on this branch.
|
||||||
- **PR event, but user wants a NEW branch/PR**: Use \`${ghPullfrogMcpName}/create_branch\` to create a new branch from the current HEAD.
|
- **PR event, but user wants a NEW branch/PR**: Use \`${ghPullfrogMcpName}/create_branch\` to create a new branch from the current HEAD.
|
||||||
@@ -38,17 +38,19 @@ export function computeModes(): Mode[] {
|
|||||||
|
|
||||||
4. Understand the requirements and any existing plan
|
4. Understand the requirements and any existing plan
|
||||||
|
|
||||||
5. Make the necessary code changes using file operations. Then use ${ghPullfrogMcpName}/commit_files to commit your changes, and ${ghPullfrogMcpName}/push_branch to push the branch. Do NOT use git commands like \`git commit\` or \`git push\` directly.
|
5. Make the necessary code changes using file operations. You should change the minimum amount of code necessary to accomplish your task. Emphasize code quality and elegance.
|
||||||
|
|
||||||
6. Test your changes to ensure they work correctly
|
6. Then use ${ghPullfrogMcpName}/commit_files to commit your changes, and ${ghPullfrogMcpName}/push_branch to push the branch. Do NOT use git commands like \`git commit\` or \`git push\` directly.
|
||||||
|
|
||||||
7. ${reportProgressInstruction}
|
7. Test your changes to ensure they work correctly
|
||||||
|
|
||||||
8. Determine whether to create a PR:
|
8. ${reportProgressInstruction}
|
||||||
|
|
||||||
|
9. Determine whether to create a PR (if not already on a PR branch):
|
||||||
- **Default behavior**: Create a PR using ${ghPullfrogMcpName}/create_pull_request with an informative title and body. If relevant, indicate which issue the PR addresses (e.g. "Fixes #123").
|
- **Default behavior**: Create a PR using ${ghPullfrogMcpName}/create_pull_request with an informative title and body. If relevant, indicate which issue the PR addresses (e.g. "Fixes #123").
|
||||||
- **Branch-only request**: If the user explicitly asks for a branch without a PR (e.g. "don't create a PR", "branch only", "just create a branch"), do NOT create a PR. Simply push the branch and report the branch link.
|
- **Branch-only request**: If the user explicitly asks for a branch without a PR (e.g. "don't create a PR", "branch only", "just create a branch"), do NOT create a PR. Simply push the branch and report the branch link.
|
||||||
|
|
||||||
9. Call report_progress one final time ONLY if you haven't already included all the important information (PR links, branch links, summary) in a previous report_progress call. If you already called report_progress with complete information including PR links after creating the PR, you do NOT need to call it again. Only make a final call if you need to add missing information. When making the final call, ensure it includes:
|
10. Call report_progress one final time ONLY if you haven't already included all the important information (PR links, branch links, summary) in a previous report_progress call. If you already called report_progress with complete information including PR links after creating the PR, you do NOT need to call it again. Only make a final call if you need to add missing information. When making the final call, ensure it includes:
|
||||||
- A summary of what was accomplished
|
- A summary of what was accomplished
|
||||||
- Links to any artifacts created (PRs, branches, issues)
|
- Links to any artifacts created (PRs, branches, issues)
|
||||||
- If you created a PR, ALWAYS include the PR link. e.g.:
|
- If you created a PR, ALWAYS include the PR link. e.g.:
|
||||||
|
|||||||
+76
-36
@@ -12,10 +12,11 @@ interface InstructionsContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function buildRuntimeContext(ctx: InstructionsContext): string {
|
function buildRuntimeContext(ctx: InstructionsContext): string {
|
||||||
// extract payload fields excluding prompt/repoInstructions/event
|
// extract payload fields excluding prompt/instructions/event (those are rendered separately)
|
||||||
const {
|
const {
|
||||||
"~pullfrog": _,
|
"~pullfrog": _,
|
||||||
prompt: _p,
|
prompt: _p,
|
||||||
|
eventInstructions: _ei,
|
||||||
repoInstructions: _r,
|
repoInstructions: _r,
|
||||||
event: _e,
|
event: _e,
|
||||||
...payloadRest
|
...payloadRest
|
||||||
@@ -50,13 +51,12 @@ function buildRuntimeContext(ctx: InstructionsContext): string {
|
|||||||
return toonEncode(filtered);
|
return toonEncode(filtered);
|
||||||
}
|
}
|
||||||
|
|
||||||
function buildEventData(event: PayloadEvent): string {
|
function buildEventTitleBody(event: PayloadEvent): string {
|
||||||
const { title, body, trigger, ...rest } = event;
|
|
||||||
const sections: string[] = [];
|
const sections: string[] = [];
|
||||||
|
|
||||||
// render title + body as markdown
|
// render title + body as markdown
|
||||||
const trimmedTitle = typeof title === "string" ? title.trim() : "";
|
const trimmedTitle = typeof event.title === "string" ? event.title.trim() : "";
|
||||||
const trimmedBody = typeof body === "string" ? body.trim() : "";
|
const trimmedBody = typeof event.body === "string" ? event.body.trim() : "";
|
||||||
|
|
||||||
if (trimmedTitle) {
|
if (trimmedTitle) {
|
||||||
sections.push(`# ${trimmedTitle}`);
|
sections.push(`# ${trimmedTitle}`);
|
||||||
@@ -66,16 +66,20 @@ function buildEventData(event: PayloadEvent): string {
|
|||||||
sections.push(trimmedBody);
|
sections.push(trimmedBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return sections.join("\n\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildEventMetadata(event: PayloadEvent): string {
|
||||||
|
const { title: _t, body: _b, trigger, ...rest } = event;
|
||||||
|
|
||||||
// include trigger in rest unless it's workflow_dispatch (not informative)
|
// include trigger in rest unless it's workflow_dispatch (not informative)
|
||||||
const restWithTrigger = trigger === "workflow_dispatch" ? rest : { trigger, ...rest };
|
const restWithTrigger = trigger === "workflow_dispatch" ? rest : { trigger, ...rest };
|
||||||
|
|
||||||
// separator and toon-encoded remaining fields
|
if (Object.keys(restWithTrigger).length === 0) {
|
||||||
if (Object.keys(restWithTrigger).length > 0) {
|
return "";
|
||||||
if (sections.length > 0) sections.push("---");
|
|
||||||
sections.push(toonEncode(restWithTrigger));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return sections.join("\n\n");
|
return toonEncode(restWithTrigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getShellInstructions(bash: ResolvedPayload["bash"]): string {
|
function getShellInstructions(bash: ResolvedPayload["bash"]): string {
|
||||||
@@ -99,25 +103,41 @@ export interface ResolvedInstructions {
|
|||||||
full: string;
|
full: string;
|
||||||
system: string;
|
system: string;
|
||||||
user: string;
|
user: string;
|
||||||
|
eventInstructions: string;
|
||||||
repo: string;
|
repo: string;
|
||||||
event: string;
|
event: string;
|
||||||
runtime: string;
|
runtime: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function resolveInstructions(ctx: InstructionsContext): ResolvedInstructions {
|
export function resolveInstructions(ctx: InstructionsContext): ResolvedInstructions {
|
||||||
const event = buildEventData(ctx.payload.event);
|
const eventTitleBody = buildEventTitleBody(ctx.payload.event);
|
||||||
|
const eventMetadata = buildEventMetadata(ctx.payload.event);
|
||||||
const runtime = buildRuntimeContext(ctx);
|
const runtime = buildRuntimeContext(ctx);
|
||||||
|
|
||||||
// user prompt is constructed server-side (body if @pullfrog tagged + per-trigger instructions)
|
// user prompt is the user's actual request (body if @pullfrog tagged)
|
||||||
const user = ctx.payload.prompt;
|
const user = ctx.payload.prompt;
|
||||||
|
|
||||||
// repo-level instructions are macro-expanded server-side and passed separately
|
// event-level instructions are trigger-specific (macro-expanded server-side)
|
||||||
|
// note: server only sends these when there's no user prompt (user request has precedence)
|
||||||
|
const eventInstructions = ctx.payload.eventInstructions ?? "";
|
||||||
|
|
||||||
|
// repo-level instructions are macro-expanded server-side
|
||||||
const repo = ctx.payload.repoInstructions ?? "";
|
const repo = ctx.payload.repoInstructions ?? "";
|
||||||
|
|
||||||
|
// determine if this is a PR or issue for labeling
|
||||||
|
const isPr = ctx.payload.event.is_pr === true;
|
||||||
|
const relatedLabel = isPr ? "--- related PR ---" : "--- related issue ---";
|
||||||
|
|
||||||
|
// combined event data for backwards compatibility
|
||||||
|
const event = [eventTitleBody, eventMetadata].filter(Boolean).join("\n\n---\n\n");
|
||||||
|
|
||||||
|
// quote user prompt with "> " to distinguish user-written content
|
||||||
const userQuoted = user
|
const userQuoted = user
|
||||||
.split("\n")
|
? user
|
||||||
.map((line) => `> ${line}`)
|
.split("\n")
|
||||||
.join("\n");
|
.map((line) => `> ${line}`)
|
||||||
|
.join("\n")
|
||||||
|
: "";
|
||||||
|
|
||||||
const system = `***********************************************
|
const system = `***********************************************
|
||||||
************* SYSTEM INSTRUCTIONS *************
|
************* SYSTEM INSTRUCTIONS *************
|
||||||
@@ -141,11 +161,10 @@ Use backticks liberally for inline code (e.g. \`z.string()\`) even in headers.
|
|||||||
## Priority Order
|
## Priority Order
|
||||||
|
|
||||||
In case of conflict between instructions, follow this precedence (highest to lowest):
|
In case of conflict between instructions, follow this precedence (highest to lowest):
|
||||||
1. Security rules (below)
|
1. Security rules and system instructions (non-overridable)
|
||||||
2. System instructions (this document)
|
2. User prompt
|
||||||
3. Mode instructions (returned by select_mode)
|
3. Event-level instructions
|
||||||
4. Repository-specific instructions (AGENTS.md, CLAUDE.md, etc.)
|
4. Repo-level instructions
|
||||||
5. User prompt
|
|
||||||
|
|
||||||
## Security
|
## Security
|
||||||
|
|
||||||
@@ -194,32 +213,53 @@ After selecting a mode, follow the detailed step-by-step instructions provided b
|
|||||||
|
|
||||||
Eagerly inspect the MCP tools available to you via the \`${ghPullfrogMcpName}\` MCP server. These are VITALLY IMPORTANT to completing your task.`;
|
Eagerly inspect the MCP tools available to you via the \`${ghPullfrogMcpName}\` MCP server. These are VITALLY IMPORTANT to completing your task.`;
|
||||||
|
|
||||||
// build repo instructions section (only if non-empty)
|
// build optional sections (only if non-empty)
|
||||||
const repoSection = repo
|
const repoSection = repo
|
||||||
? `
|
? `************* REPO-LEVEL INSTRUCTIONS *************
|
||||||
|
|
||||||
************* REPO-LEVEL INSTRUCTIONS *************
|
|
||||||
|
|
||||||
${repo}`
|
${repo}`
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
const full = `
|
const eventInstructionsSection = eventInstructions
|
||||||
${system}
|
? `************* EVENT-LEVEL INSTRUCTIONS *************
|
||||||
|
|
||||||
************* USER PROMPT *************
|
${eventInstructions}`
|
||||||
|
: "";
|
||||||
|
|
||||||
|
// build the task/context section
|
||||||
|
// - if user gave direct @pullfrog request: show as USER PROMPT with event as context
|
||||||
|
// - if automatic trigger: show as EVENT CONTEXT (eventInstructions section has the task)
|
||||||
|
const titleBodySection = eventTitleBody ? `${relatedLabel}\n\n${eventTitleBody}` : "";
|
||||||
|
const metadataSection = eventMetadata ? `--- event context ---\n\n${eventMetadata}` : "";
|
||||||
|
|
||||||
|
const userSection = userQuoted
|
||||||
|
? `************* USER PROMPT — THIS IS YOUR TASK *************
|
||||||
|
|
||||||
${userQuoted}
|
${userQuoted}
|
||||||
|
|
||||||
|
${titleBodySection}
|
||||||
|
|
||||||
|
${metadataSection}`
|
||||||
|
: `************* EVENT CONTEXT *************
|
||||||
|
|
||||||
|
${titleBodySection}
|
||||||
|
|
||||||
|
${metadataSection}`;
|
||||||
|
|
||||||
|
const rawFull = `************* RUNTIME CONTEXT *************
|
||||||
|
|
||||||
|
${runtime}
|
||||||
|
|
||||||
|
${system}
|
||||||
|
|
||||||
${repoSection}
|
${repoSection}
|
||||||
|
|
||||||
************* EVENT DATA *************
|
${eventInstructionsSection}
|
||||||
|
|
||||||
${event}
|
${userSection}`;
|
||||||
|
|
||||||
************* RUNTIME CONTEXT *************
|
// normalize spacing: trim and collapse 3+ consecutive newlines to 2
|
||||||
|
const full = rawFull.trim().replace(/\n{3,}/g, "\n\n");
|
||||||
|
|
||||||
The following contains the agent configuration (agent, effort, permissions) and runtime environment details.
|
return { full, system, user, eventInstructions, repo, event, runtime };
|
||||||
|
|
||||||
${runtime}`;
|
|
||||||
|
|
||||||
return { full, system, user, repo, event, runtime };
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-1
@@ -15,9 +15,10 @@ const BashPermissionInput = type.enumerated("disabled", "restricted", "enabled")
|
|||||||
// permissions are derived from event.authorPermission instead
|
// permissions are derived from event.authorPermission instead
|
||||||
export const JsonPayload = type({
|
export const JsonPayload = type({
|
||||||
"~pullfrog": "true",
|
"~pullfrog": "true",
|
||||||
"version": "string",
|
version: "string",
|
||||||
"agent?": AgentName.or("undefined"),
|
"agent?": AgentName.or("undefined"),
|
||||||
"prompt?": "string",
|
"prompt?": "string",
|
||||||
|
"eventInstructions?": "string",
|
||||||
"repoInstructions?": "string",
|
"repoInstructions?": "string",
|
||||||
"event?": "object",
|
"event?": "object",
|
||||||
"effort?": Effort.or("undefined"),
|
"effort?": Effort.or("undefined"),
|
||||||
@@ -140,6 +141,7 @@ export function resolvePayload(repoSettings: RepoSettings) {
|
|||||||
// inverted: jsonPayload.prompt extracts the text from the JSON payload,
|
// inverted: jsonPayload.prompt extracts the text from the JSON payload,
|
||||||
// whereas inputs.prompt IS the raw JSON string when internally dispatched
|
// whereas inputs.prompt IS the raw JSON string when internally dispatched
|
||||||
prompt: jsonPayload?.prompt ?? inputs.prompt,
|
prompt: jsonPayload?.prompt ?? inputs.prompt,
|
||||||
|
eventInstructions: jsonPayload?.eventInstructions,
|
||||||
repoInstructions: jsonPayload?.repoInstructions,
|
repoInstructions: jsonPayload?.repoInstructions,
|
||||||
event,
|
event,
|
||||||
effort: inputs.effort ?? jsonPayload?.effort ?? "auto",
|
effort: inputs.effort ?? jsonPayload?.effort ?? "auto",
|
||||||
|
|||||||
Reference in New Issue
Block a user