Add footer links
This commit is contained in:
@@ -226,7 +226,7 @@ function parsePayload(inputs: Inputs): Payload {
|
|||||||
|
|
||||||
function setupMcpServers(ctx: MainContext): void {
|
function setupMcpServers(ctx: MainContext): void {
|
||||||
const allModes = [...modes, ...(ctx.payload.modes || [])];
|
const allModes = [...modes, ...(ctx.payload.modes || [])];
|
||||||
ctx.mcpServers = createMcpConfigs(ctx.githubInstallationToken, allModes);
|
ctx.mcpServers = createMcpConfigs(ctx.githubInstallationToken, allModes, ctx.payload);
|
||||||
log.debug(`📋 MCP Config: ${JSON.stringify(ctx.mcpServers, null, 2)}`);
|
log.debug(`📋 MCP Config: ${JSON.stringify(ctx.mcpServers, null, 2)}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+41
-2
@@ -1,6 +1,39 @@
|
|||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
|
import type { Payload } from "../external.ts";
|
||||||
|
import { agentsManifest } from "../external.ts";
|
||||||
|
import { parseRepoContext } from "../utils/github.ts";
|
||||||
import { contextualize, tool } from "./shared.ts";
|
import { contextualize, tool } from "./shared.ts";
|
||||||
|
|
||||||
|
function buildCommentFooter(payload: Payload): string {
|
||||||
|
const repoContext = parseRepoContext();
|
||||||
|
const runId = process.env.GITHUB_RUN_ID;
|
||||||
|
|
||||||
|
const agentName = payload.agent;
|
||||||
|
const agentInfo = agentName ? agentsManifest[agentName] : null;
|
||||||
|
const agentDisplayName = agentInfo?.displayName || "Unknown Agent";
|
||||||
|
|
||||||
|
// agent URLs based on manifest
|
||||||
|
const agentUrls: Record<string, string> = {
|
||||||
|
claude: "https://claude.com/claude-code",
|
||||||
|
codex: "https://platform.openai.com/docs/guides/codex",
|
||||||
|
cursor: "https://cursor.com/",
|
||||||
|
gemini: "https://ai.google.dev/gemini-api/docs",
|
||||||
|
};
|
||||||
|
|
||||||
|
const agentUrl = agentName
|
||||||
|
? agentUrls[agentName] || "https://pullfrog.ai"
|
||||||
|
: "https://pullfrog.ai";
|
||||||
|
|
||||||
|
// build workflow run URL
|
||||||
|
const workflowRunUrl = runId
|
||||||
|
? `https://github.com/${repoContext.owner}/${repoContext.name}/actions/runs/${runId}`
|
||||||
|
: `https://github.com/${repoContext.owner}/${repoContext.name}`;
|
||||||
|
|
||||||
|
return `---
|
||||||
|
|
||||||
|
<img src="https://pullfrog.ai/pullfrog-logo.png" width="12px" height="12px" style="vertical-align: middle;" /> <sup>Triggered by [Pullfrog](https://pullfrog.ai) | Using [${agentDisplayName}](${agentUrl}) | [View workflow run](${workflowRunUrl}) | [](https://x.com/pullfrogai)</sup>`;
|
||||||
|
}
|
||||||
|
|
||||||
export const Comment = type({
|
export const Comment = type({
|
||||||
issueNumber: type.number.describe("the issue number to comment on"),
|
issueNumber: type.number.describe("the issue number to comment on"),
|
||||||
body: type.string.describe("the comment body content"),
|
body: type.string.describe("the comment body content"),
|
||||||
@@ -73,11 +106,14 @@ export const CreateWorkingCommentTool = tool({
|
|||||||
throw new Error("create_working_comment may not be called multiple times");
|
throw new Error("create_working_comment may not be called multiple times");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const footer = buildCommentFooter(ctx.payload);
|
||||||
|
const body = `${intent} <img src="https://pullfrog.ai/party-parrot.gif" width="14px" height="14px" style="vertical-align: middle; margin-left: 4px;" />${footer}`;
|
||||||
|
|
||||||
const result = await ctx.octokit.rest.issues.createComment({
|
const result = await ctx.octokit.rest.issues.createComment({
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
repo: ctx.name,
|
repo: ctx.name,
|
||||||
issue_number: issueNumber,
|
issue_number: issueNumber,
|
||||||
body: `${intent} <img src="https://pullfrog.ai/party-parrot.gif" width="14px" height="14px" style="vertical-align: middle; margin-left: 4px;" />`,
|
body,
|
||||||
});
|
});
|
||||||
|
|
||||||
workingCommentId = result.data.id;
|
workingCommentId = result.data.id;
|
||||||
@@ -104,11 +140,14 @@ export const UpdateWorkingCommentTool = tool({
|
|||||||
throw new Error("create_working_comment must be called before update_working_comment");
|
throw new Error("create_working_comment must be called before update_working_comment");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const footer = buildCommentFooter(ctx.payload);
|
||||||
|
const bodyWithFooter = `${body}${footer}`;
|
||||||
|
|
||||||
const result = await ctx.octokit.rest.issues.updateComment({
|
const result = await ctx.octokit.rest.issues.updateComment({
|
||||||
owner: ctx.owner,
|
owner: ctx.owner,
|
||||||
repo: ctx.name,
|
repo: ctx.name,
|
||||||
comment_id: workingCommentId,
|
comment_id: workingCommentId,
|
||||||
body,
|
body: bodyWithFooter,
|
||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
|||||||
+19
-6
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk";
|
import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk";
|
||||||
import { fromHere } from "@ark/fs";
|
import { fromHere } from "@ark/fs";
|
||||||
|
import type { Payload } from "../external.ts";
|
||||||
import { ghPullfrogMcpName } from "../external.ts";
|
import { ghPullfrogMcpName } from "../external.ts";
|
||||||
import type { Mode } from "../modes.ts";
|
import type { Mode } from "../modes.ts";
|
||||||
import { parseRepoContext } from "../utils/github.ts";
|
import { parseRepoContext } from "../utils/github.ts";
|
||||||
@@ -12,7 +13,11 @@ export type McpName = typeof ghPullfrogMcpName;
|
|||||||
|
|
||||||
export type McpConfigs = Record<McpName, McpStdioServerConfig>;
|
export type McpConfigs = Record<McpName, McpStdioServerConfig>;
|
||||||
|
|
||||||
export function createMcpConfigs(githubInstallationToken: string, modes: Mode[]): McpConfigs {
|
export function createMcpConfigs(
|
||||||
|
githubInstallationToken: string,
|
||||||
|
modes: Mode[],
|
||||||
|
payload: Payload
|
||||||
|
): McpConfigs {
|
||||||
const repoContext = parseRepoContext();
|
const repoContext = parseRepoContext();
|
||||||
const githubRepository = `${repoContext.owner}/${repoContext.name}`;
|
const githubRepository = `${repoContext.owner}/${repoContext.name}`;
|
||||||
|
|
||||||
@@ -20,15 +25,23 @@ export function createMcpConfigs(githubInstallationToken: string, modes: Mode[])
|
|||||||
// In development, server.ts is in the same directory as this file (config.ts)
|
// In development, server.ts is in the same directory as this file (config.ts)
|
||||||
const serverPath = process.env.GITHUB_ACTIONS ? fromHere("mcp-server") : fromHere("server.ts");
|
const serverPath = process.env.GITHUB_ACTIONS ? fromHere("mcp-server") : fromHere("server.ts");
|
||||||
|
|
||||||
|
const env: Record<string, string> = {
|
||||||
|
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
|
||||||
|
GITHUB_REPOSITORY: githubRepository,
|
||||||
|
PULLFROG_MODES: JSON.stringify(modes),
|
||||||
|
PULLFROG_PAYLOAD: JSON.stringify(payload),
|
||||||
|
};
|
||||||
|
|
||||||
|
// pass through GITHUB_RUN_ID if available (automatically set in GitHub Actions)
|
||||||
|
if (process.env.GITHUB_RUN_ID) {
|
||||||
|
env.GITHUB_RUN_ID = process.env.GITHUB_RUN_ID;
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
[ghPullfrogMcpName]: {
|
[ghPullfrogMcpName]: {
|
||||||
command: "node",
|
command: "node",
|
||||||
args: [serverPath],
|
args: [serverPath],
|
||||||
env: {
|
env,
|
||||||
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
|
|
||||||
GITHUB_REPOSITORY: githubRepository,
|
|
||||||
PULLFROG_MODES: JSON.stringify(modes),
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+20
-3
@@ -1,7 +1,7 @@
|
|||||||
import { cached } from "@ark/util";
|
|
||||||
import { Octokit } from "@octokit/rest";
|
import { Octokit } from "@octokit/rest";
|
||||||
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
import type { StandardSchemaV1 } from "@standard-schema/spec";
|
||||||
import type { FastMCP, Tool } from "fastmcp";
|
import type { FastMCP, Tool } from "fastmcp";
|
||||||
|
import type { Payload } from "../external.ts";
|
||||||
import { log } from "../utils/cli.ts";
|
import { log } from "../utils/cli.ts";
|
||||||
import { parseRepoContext, type RepoContext } from "../utils/github.ts";
|
import { parseRepoContext, type RepoContext } from "../utils/github.ts";
|
||||||
|
|
||||||
@@ -13,7 +13,22 @@ export interface ToolResult {
|
|||||||
isError?: boolean;
|
isError?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getMcpContext = cached((): McpContext => {
|
export function getPayload(): Payload {
|
||||||
|
const payloadEnv = process.env.PULLFROG_PAYLOAD;
|
||||||
|
if (!payloadEnv) {
|
||||||
|
throw new Error("PULLFROG_PAYLOAD environment variable is required");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(payloadEnv) as Payload;
|
||||||
|
} catch (error) {
|
||||||
|
throw new Error(
|
||||||
|
`Failed to parse PULLFROG_PAYLOAD: ${error instanceof Error ? error.message : String(error)}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getMcpContext(): McpContext {
|
||||||
const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
||||||
if (!githubInstallationToken) {
|
if (!githubInstallationToken) {
|
||||||
throw new Error("GITHUB_INSTALLATION_TOKEN environment variable is required");
|
throw new Error("GITHUB_INSTALLATION_TOKEN environment variable is required");
|
||||||
@@ -24,11 +39,13 @@ export const getMcpContext = cached((): McpContext => {
|
|||||||
octokit: new Octokit({
|
octokit: new Octokit({
|
||||||
auth: githubInstallationToken,
|
auth: githubInstallationToken,
|
||||||
}),
|
}),
|
||||||
|
payload: getPayload(),
|
||||||
};
|
};
|
||||||
});
|
}
|
||||||
|
|
||||||
export interface McpContext extends RepoContext {
|
export interface McpContext extends RepoContext {
|
||||||
octokit: Octokit;
|
octokit: Octokit;
|
||||||
|
payload: Payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const tool = <const params>(toolDef: Tool<any, StandardSchemaV1<params>>) => {
|
export const tool = <const params>(toolDef: Tool<any, StandardSchemaV1<params>>) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user