refactor instructions to return object with full/system/user/event/runtime properties, fix duplicate modes and json prompt extraction (#110)
This commit is contained in:
committed by
pullfrog[bot]
parent
410b11db71
commit
cb925556e8
+7
-45
@@ -1,59 +1,21 @@
|
||||
import type { ToolState } from "../mcp/server.ts";
|
||||
import { createOctokit, parseRepoContext } from "./github.ts";
|
||||
import { getGitHubInstallationToken } from "./token.ts";
|
||||
import { fetchWorkflowRunInfo } from "./workflowRun.ts";
|
||||
|
||||
/**
|
||||
* Get progress comment ID from environment variable or database.
|
||||
*/
|
||||
function getProgressCommentIdFromEnv(): number | null {
|
||||
const envCommentId = process.env.PULLFROG_PROGRESS_COMMENT_ID;
|
||||
if (envCommentId) {
|
||||
const parsed = parseInt(envCommentId, 10);
|
||||
if (!Number.isNaN(parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function reportErrorToComment({
|
||||
error,
|
||||
title,
|
||||
}: {
|
||||
interface ReportErrorParams {
|
||||
toolState: ToolState;
|
||||
error: string;
|
||||
title?: string;
|
||||
}): Promise<void> {
|
||||
const formattedError = title ? `${title}\n\n${error}` : error;
|
||||
}
|
||||
|
||||
// try to get comment ID from env var first, then from database if needed
|
||||
let commentId = getProgressCommentIdFromEnv();
|
||||
export async function reportErrorToComment(ctx: ReportErrorParams): Promise<void> {
|
||||
const formattedError = ctx.title ? `${ctx.title}\n\n${ctx.error}` : ctx.error;
|
||||
|
||||
// if not in env var, try fetching from database using run ID
|
||||
if (!commentId) {
|
||||
const runId = process.env.GITHUB_RUN_ID;
|
||||
if (runId) {
|
||||
try {
|
||||
const workflowRunInfo = await fetchWorkflowRunInfo(runId);
|
||||
if (workflowRunInfo.progressCommentId) {
|
||||
const parsed = parseInt(workflowRunInfo.progressCommentId, 10);
|
||||
if (!Number.isNaN(parsed)) {
|
||||
commentId = parsed;
|
||||
// cache it in env var for future use
|
||||
process.env.PULLFROG_PROGRESS_COMMENT_ID = workflowRunInfo.progressCommentId;
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// database fetch failed, continue without comment ID
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no comment ID available, can't update comment
|
||||
const commentId = ctx.toolState.progressComment.id;
|
||||
if (!commentId) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update comment directly using GitHub API
|
||||
const repoContext = parseRepoContext();
|
||||
const octokit = createOctokit(getGitHubInstallationToken());
|
||||
|
||||
|
||||
+47
-39
@@ -1,20 +1,17 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import { encode as toonEncode } from "@toon-format/toon";
|
||||
import { ghPullfrogMcpName } from "../external.ts";
|
||||
import { computeModes, type Mode } from "../modes.ts";
|
||||
import type { Mode } from "../modes.ts";
|
||||
import type { ResolvedPayload } from "./payload.ts";
|
||||
import type { RepoData } from "./repoData.ts";
|
||||
|
||||
type BashPermission = "disabled" | "restricted" | "enabled";
|
||||
|
||||
interface InstructionsInput {
|
||||
prompt: string;
|
||||
event: { trigger: string; [key: string]: unknown };
|
||||
interface InstructionsContext {
|
||||
payload: ResolvedPayload;
|
||||
repoData: RepoData;
|
||||
modes: Mode[];
|
||||
bash: BashPermission;
|
||||
}
|
||||
|
||||
function buildRuntimeContext(input: InstructionsInput): string {
|
||||
function buildRuntimeContext(ctx: InstructionsContext): string {
|
||||
const lines: string[] = [];
|
||||
|
||||
lines.push(`working_directory: ${process.cwd()}`);
|
||||
@@ -27,8 +24,8 @@ function buildRuntimeContext(input: InstructionsInput): string {
|
||||
// git not available or not in a repo
|
||||
}
|
||||
|
||||
lines.push(`repo: ${input.repoData.owner}/${input.repoData.name}`);
|
||||
lines.push(`default_branch: ${input.repoData.repo.default_branch}`);
|
||||
lines.push(`repo: ${ctx.repoData.owner}/${ctx.repoData.name}`);
|
||||
lines.push(`default_branch: ${ctx.repoData.repo.default_branch}`);
|
||||
|
||||
const ghVars: Record<string, string | undefined> = {
|
||||
github_event_name: process.env.GITHUB_EVENT_NAME,
|
||||
@@ -47,7 +44,7 @@ function buildRuntimeContext(input: InstructionsInput): string {
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function getShellInstructions(bash: BashPermission): string {
|
||||
function getShellInstructions(bash: ResolvedPayload["bash"]): string {
|
||||
switch (bash) {
|
||||
case "disabled":
|
||||
return `**Shell commands**: Shell command execution is DISABLED. Do not attempt to run shell commands.`;
|
||||
@@ -62,21 +59,35 @@ function getShellInstructions(bash: BashPermission): string {
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveInstructions(input: InstructionsInput): string {
|
||||
let encodedEvent = "";
|
||||
export interface ResolvedInstructions {
|
||||
full: string;
|
||||
system: string;
|
||||
user: string;
|
||||
event: string;
|
||||
runtime: string;
|
||||
}
|
||||
|
||||
const eventKeys = Object.keys(input.event);
|
||||
if (eventKeys.length === 1 && eventKeys[0] === "trigger") {
|
||||
// no meaningful event data to encode
|
||||
} else {
|
||||
encodedEvent = toonEncode(input.event);
|
||||
}
|
||||
export function resolveInstructions(ctx: InstructionsContext): ResolvedInstructions {
|
||||
const event = toonEncode({
|
||||
agent: ctx.payload.agent,
|
||||
effort: ctx.payload.effort,
|
||||
permissions: {
|
||||
web: ctx.payload.web,
|
||||
search: ctx.payload.search,
|
||||
write: ctx.payload.write,
|
||||
bash: ctx.payload.bash,
|
||||
},
|
||||
event: ctx.payload.event,
|
||||
});
|
||||
|
||||
const runtimeContext = buildRuntimeContext(input);
|
||||
const runtime = buildRuntimeContext(ctx);
|
||||
|
||||
return (
|
||||
`
|
||||
***********************************************
|
||||
const user = ctx.payload.prompt
|
||||
.split("\n")
|
||||
.map((line) => `> ${line}`)
|
||||
.join("\n");
|
||||
|
||||
const system = `***********************************************
|
||||
************* SYSTEM INSTRUCTIONS *************
|
||||
***********************************************
|
||||
|
||||
@@ -121,7 +132,7 @@ Tool names may be formatted as \`(server name)/(tool name)\`, for example: \`${g
|
||||
|
||||
**Efficiency**: Trust the tools - do not repeatedly verify file contents or git status after operations. If a tool reports success, proceed to the next step. Only verify if you encounter an actual error.
|
||||
|
||||
${getShellInstructions(input.bash)}
|
||||
${getShellInstructions(ctx.payload.bash)}
|
||||
|
||||
**Command execution**: Never use \`sleep\` to wait for commands to complete. Commands run synchronously - when the bash tool returns, the command has finished.
|
||||
|
||||
@@ -142,33 +153,30 @@ ${getShellInstructions(input.bash)}
|
||||
|
||||
### Available modes
|
||||
|
||||
${[...computeModes({ hasProgressComment: true }), ...input.modes].map((w) => ` - "${w.name}": ${w.description}`).join("\n")}
|
||||
${ctx.modes.map((m) => `- "${m.name}": ${m.description}`).join("\n")}
|
||||
|
||||
### Following the mode instructions
|
||||
|
||||
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 full = `
|
||||
${system}
|
||||
|
||||
************* USER PROMPT *************
|
||||
|
||||
${input.prompt
|
||||
.split("\n")
|
||||
.map((line) => `> ${line}`)
|
||||
.join("\n")}
|
||||
${user}
|
||||
|
||||
${
|
||||
encodedEvent
|
||||
? `************* EVENT DATA *************
|
||||
************* EVENT DATA *************
|
||||
|
||||
The following is structured data about the GitHub event that triggered this run (e.g., issue body, PR details, comment content). Use this context to understand the full situation.
|
||||
The following is structured data about the context of this run (agent, effort level, permissions, and the GitHub event that triggered it). Use this context to understand the full situation.
|
||||
|
||||
${encodedEvent}`
|
||||
: ""
|
||||
}
|
||||
${event}
|
||||
|
||||
************* RUNTIME CONTEXT *************
|
||||
|
||||
${runtimeContext}`
|
||||
);
|
||||
${runtime}`;
|
||||
|
||||
return { full, system, user, event, runtime };
|
||||
}
|
||||
|
||||
+3
-1
@@ -108,7 +108,9 @@ export function resolvePayload(repoSettings: RepoSettings) {
|
||||
return {
|
||||
"~pullfrog": true as const,
|
||||
agent: resolvedAgent,
|
||||
prompt: inputs.prompt ?? jsonPayload?.prompt,
|
||||
// inverted: jsonPayload.prompt extracts the text from the JSON payload,
|
||||
// whereas inputs.prompt IS the raw JSON string when internally dispatched
|
||||
prompt: jsonPayload?.prompt ?? inputs.prompt,
|
||||
event,
|
||||
effort: inputs.effort ?? jsonPayload?.effort ?? "auto",
|
||||
cwd: resolveCwd(inputs.cwd),
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
import { execSync } from "node:child_process";
|
||||
import { mkdtemp } from "node:fs/promises";
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import type { PayloadEvent } from "../external.ts";
|
||||
@@ -16,8 +16,8 @@ export interface SetupOptions {
|
||||
/**
|
||||
* Create a shared temp directory for the action
|
||||
*/
|
||||
export async function createTempDirectory(): Promise<string> {
|
||||
const sharedTempDir = await mkdtemp(join(tmpdir(), "pullfrog-"));
|
||||
export function createTempDirectory(): string {
|
||||
const sharedTempDir = mkdtempSync(join(tmpdir(), "pullfrog-"));
|
||||
process.env.PULLFROG_TEMP_DIR = sharedTempDir;
|
||||
log.info(`» created temp dir at ${sharedTempDir}`);
|
||||
return sharedTempDir;
|
||||
|
||||
@@ -27,7 +27,6 @@ export async function resolveRun(params: ResolveRunParams): Promise<ResolveRunRe
|
||||
const workflowRunInfo = runId ? await fetchWorkflowRunInfo(runId) : { progressCommentId: null };
|
||||
|
||||
if (workflowRunInfo.progressCommentId) {
|
||||
process.env.PULLFROG_PROGRESS_COMMENT_ID = workflowRunInfo.progressCommentId;
|
||||
log.info(`» using pre-created progress comment: ${workflowRunInfo.progressCommentId}`);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user