/** * ⚠️ NO IMPORTS except modes.ts - this file is imported by Next.js and must avoid pulling in backend code. * All shared constants, types, and data used by both the Next.js app and the action runtime live here. * Other files in action/ re-export from this file for backward compatibility. */ import { type } from "arktype"; import type { Mode } from "./modes.ts"; // mcp name constant export const ghPullfrogMcpName = "gh_pullfrog"; export interface AgentManifest { displayName: string; apiKeyNames: string[]; url: string; } // agent manifest - static metadata about available agents export const agentsManifest = { claude: { displayName: "Claude Code", apiKeyNames: ["anthropic_api_key"], url: "https://claude.com/claude-code", }, codex: { displayName: "Codex CLI", apiKeyNames: ["openai_api_key"], url: "https://platform.openai.com/docs/guides/codex", }, cursor: { displayName: "Cursor CLI", apiKeyNames: ["cursor_api_key"], url: "https://cursor.com/", }, gemini: { displayName: "Gemini CLI", apiKeyNames: ["google_api_key", "gemini_api_key"], url: "https://ai.google.dev/gemini-api/docs", }, } as const satisfies Record; // agent name type - union of agent slugs export type AgentName = keyof typeof agentsManifest; export const AgentName = type.enumerated(...Object.keys(agentsManifest)); export type AgentApiKeyName = (typeof agentsManifest)[AgentName]["apiKeyNames"][number]; // discriminated union for payload event based on trigger // note: all events use issue_number for consistency (PRs are issues in GitHub's API) export type PayloadEvent = | { trigger: "pull_request_opened"; issue_number: number; pr_title: string; pr_body: string | null; branch: string; [key: string]: any; } | { trigger: "pull_request_review_requested"; issue_number: number; pr_title: string; pr_body: string | null; branch: string; [key: string]: any; } | { trigger: "pull_request_review_submitted"; issue_number: number; review_id: number; review_body: string | null; review_state: string; review_comments: any[]; context: any; branch: string; [key: string]: any; } | { trigger: "pull_request_review_comment_created"; issue_number: number; pr_title: string; comment_id: number; comment_body: string; thread?: any; branch: string; [key: string]: any; } | { trigger: "issues_opened"; issue_number: number; issue_title: string; issue_body: string | null; [key: string]: any; } | { trigger: "issues_assigned"; issue_number: number; issue_title: string; issue_body: string | null; [key: string]: any; } | { trigger: "issues_labeled"; issue_number: number; issue_title: string; issue_body: string | null; [key: string]: any; } | { trigger: "issue_comment_created"; comment_id: number; comment_body: string; issue_number: number; branch?: string; [key: string]: any; } | { trigger: "check_suite_completed"; issue_number: number; pr_title: string; pr_body: string | null; pull_request: any; branch: string; check_suite: { id: number; head_sha: string; head_branch: string | null; status: string | null; conclusion: string | null; url: string; }; [key: string]: any; } | { trigger: "workflow_dispatch"; [key: string]: any; }; // payload type for agent execution export type Payload = { "~pullfrog": true; /** * Agent slug identifier (e.g., "claude", "codex", "gemini") */ readonly agent: AgentName | null; /** * The prompt/instructions for the agent to execute */ readonly prompt: string; /** * Event data from webhook payload. * Discriminated union based on trigger field. */ readonly event: PayloadEvent; /** * Execution mode configuration */ modes: readonly Mode[]; /** * Optional IDs of the issue, PR, or comment that the agent is working on */ readonly comment_id?: number | null; readonly issue_id?: number | null; readonly pr_id?: number | null; };