import { RPC, RpcError } from "./protocol"; import type { JsonObject, McpCaller } from "./protocol"; import { callTool } from "./tools"; import { untrustedBlock } from "./untrusted"; export const PROMPTS = [ { name: "review-pr", title: "Review a pull request", description: "Review a pull request's diff and submit a review with line comments.", arguments: [ { name: "owner", required: true }, { name: "repo", required: true }, { name: "number", description: "Pull request number", required: true }, ], }, { name: "fix-ci", title: "Fix failing CI", description: "Investigate a failing CI run, fix the code on a branch and re-run CI.", arguments: [ { name: "owner", required: true }, { name: "repo", required: true }, { name: "run", description: "Run number", required: true }, ], }, ]; const noHooks = { signal: new AbortController().signal, progress: () => undefined }; function argument(args: JsonObject, name: string): string { const value = args[name]; if (typeof value !== "string" || value.trim() === "") throw new RpcError(RPC.invalidParams, `Missing argument ${name}`); return value.trim(); } async function fetchStructured(caller: McpCaller, name: string, args: JsonObject): Promise { const result = await callTool(caller, { name, arguments: args }, noHooks); if (result.isError) { const content = Array.isArray(result.content) ? result.content[0] : null; throw new RpcError( RPC.invalidParams, typeof content === "object" && content ? String(Reflect.get(content, "text")) : "Not found", ); } return JSON.stringify(result.structuredContent, null, 2); } function message(text: string) { return { role: "user", content: { type: "text", text } }; } export async function getPrompt(caller: McpCaller, name: string, args: JsonObject): Promise { const owner = argument(args, "owner"); const repo = argument(args, "repo"); if (name === "review-pr") { const number = Number(argument(args, "number")); const pull = await fetchStructured(caller, "get_pull", { owner, repo, number }); return { description: `Review ${owner}/${repo}#${number}`, messages: [ message( [ `Review pull request ${owner}/${repo}#${number}.`, untrustedBlock("pull request metadata and diff", pull), "Read surrounding code with read_file when the diff lacks context. Look for bugs, regressions, missing tests and security issues.", `Then call review_pull with event "comment" or "request_changes", line comments on the RIGHT side, and expected_head set to head_sha.`, `Never call review_pull with event "approve" or merge_pull on your own initiative: approving is a decision the human makes after reading your review.`, ].join("\n"), ), ], }; } if (name === "fix-ci") { const run = Number(argument(args, "run")); const status = await fetchStructured(caller, "get_ci_run", { owner, repo, run }); return { description: `Fix CI run ${run} in ${owner}/${repo}`, messages: [ message( [ `CI run ${run} in ${owner}/${repo}.`, untrustedBlock("CI run status (and any logs you fetch)", status), "1. Call get_ci_logs for each failed job and find the root cause.", "2. create_workspace for the fix, then claim_files for every file you will change.", "3. commit_files with the fix, run_ci on your branch, and follow get_ci_logs until it passes.", "4. open_pull with a summary of the failure and the fix, then release_claims.", ].join("\n"), ), ], }; } throw new RpcError(RPC.invalidParams, `Unknown prompt: ${name}`); }