improve agents external integration
This commit is contained in:
@@ -6,7 +6,6 @@ import { agent, installFromNpmTarball } from "./shared.ts";
|
||||
|
||||
export const claude = agent({
|
||||
name: "claude",
|
||||
inputKeys: ["anthropic_api_key"],
|
||||
install: async () => {
|
||||
const versionRange = packageJson.dependencies["@anthropic-ai/claude-agent-sdk"] || "latest";
|
||||
return await installFromNpmTarball({
|
||||
|
||||
@@ -6,7 +6,6 @@ import { agent, type ConfigureMcpServersParams, installFromNpmTarball } from "./
|
||||
|
||||
export const codex = agent({
|
||||
name: "codex",
|
||||
inputKeys: ["openai_api_key"],
|
||||
install: async () => {
|
||||
return await installFromNpmTarball({
|
||||
packageName: "@openai/codex",
|
||||
|
||||
@@ -7,7 +7,6 @@ import { agent, type ConfigureMcpServersParams, installFromCurl } from "./shared
|
||||
|
||||
export const cursor = agent({
|
||||
name: "cursor",
|
||||
inputKeys: ["cursor_api_key"],
|
||||
install: async () => {
|
||||
return await installFromCurl({
|
||||
installUrl: "https://cursor.com/install",
|
||||
|
||||
@@ -6,7 +6,6 @@ import { agent, type ConfigureMcpServersParams, installFromNpmTarball } from "./
|
||||
|
||||
export const gemini = agent({
|
||||
name: "gemini",
|
||||
inputKeys: ["google_api_key", "gemini_api_key"],
|
||||
install: async () => {
|
||||
return await installFromNpmTarball({
|
||||
packageName: "@google/gemini-cli",
|
||||
|
||||
@@ -11,5 +11,3 @@ export const agents = {
|
||||
cursor,
|
||||
gemini,
|
||||
} satisfies Record<AgentName, Agent>;
|
||||
|
||||
export type AgentInputKey = (typeof agents)[keyof typeof agents]["inputKeys"][number];
|
||||
|
||||
+13
-7
@@ -3,7 +3,8 @@ import { chmodSync, createWriteStream, existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { pipeline } from "node:stream/promises";
|
||||
import type { McpStdioServerConfig } from "@anthropic-ai/claude-agent-sdk";
|
||||
import type { Payload } from "../external.ts";
|
||||
import type { show } from "@ark/util";
|
||||
import { type AgentManifest, type AgentName, agentsManifest, type Payload } from "../external.ts";
|
||||
import { log } from "../utils/cli.ts";
|
||||
|
||||
/**
|
||||
@@ -232,13 +233,18 @@ export async function installFromCurl({
|
||||
return cliPath;
|
||||
}
|
||||
|
||||
export const agent = <const agent extends Agent>(agent: agent): agent => {
|
||||
return agent;
|
||||
export const agent = <const input extends AgentInput>(input: input): defineAgent<input> => {
|
||||
return { ...input, ...agentsManifest[input.name] } as never;
|
||||
};
|
||||
|
||||
export type Agent = {
|
||||
name: string;
|
||||
inputKeys: string[];
|
||||
export interface AgentInput {
|
||||
name: AgentName;
|
||||
install: () => Promise<string>;
|
||||
run: (config: AgentConfig) => Promise<AgentResult>;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Agent extends AgentInput, AgentManifest {}
|
||||
|
||||
type agentManifest<name extends AgentName> = (typeof agentsManifest)[name];
|
||||
|
||||
type defineAgent<input extends AgentInput> = show<input & agentManifest<input["name"]>>;
|
||||
|
||||
@@ -24,7 +24,7 @@ async function run(): Promise<void> {
|
||||
prompt: core.getInput("prompt", { required: true }),
|
||||
agent: core.getInput("agent") ? AgentName.assert(core.getInput("agent")) : undefined,
|
||||
...flatMorph(agents, (_, agent) =>
|
||||
agent.inputKeys.map((inputKey) => [inputKey, core.getInput(inputKey)])
|
||||
agent.apiKeyNames.map((inputKey) => [inputKey, core.getInput(inputKey)])
|
||||
),
|
||||
};
|
||||
|
||||
|
||||
+17
-10
@@ -7,31 +7,38 @@
|
||||
import type { Mode } from "./modes.ts";
|
||||
|
||||
// mcp name constant
|
||||
export const ghPullfrogMcpName = "gh-pullfrog";
|
||||
export const ghPullfrogMcpName = "gh_pullfrog";
|
||||
|
||||
export interface AgentManifest {
|
||||
displayName: string;
|
||||
apiKeyNames: string[];
|
||||
}
|
||||
|
||||
// agent manifest - static metadata about available agents
|
||||
export const agentsManifest = {
|
||||
claude: {
|
||||
name: "Claude Code",
|
||||
apiKeys: ["anthropic_api_key"],
|
||||
displayName: "Claude Code",
|
||||
apiKeyNames: ["anthropic_api_key"],
|
||||
},
|
||||
codex: {
|
||||
name: "Codex CLI",
|
||||
apiKeys: ["openai_api_key"],
|
||||
displayName: "Codex CLI",
|
||||
apiKeyNames: ["openai_api_key"],
|
||||
},
|
||||
cursor: {
|
||||
name: "Cursor CLI",
|
||||
apiKeys: ["cursor_api_key"],
|
||||
displayName: "Cursor CLI",
|
||||
apiKeyNames: ["cursor_api_key"],
|
||||
},
|
||||
gemini: {
|
||||
name: "Gemini CLI",
|
||||
apiKeys: ["google_api_key", "gemini_api_key"],
|
||||
displayName: "Gemini CLI",
|
||||
apiKeyNames: ["google_api_key", "gemini_api_key"],
|
||||
},
|
||||
} as const;
|
||||
} as const satisfies Record<string, AgentManifest>;
|
||||
|
||||
// agent name type - union of agent slugs
|
||||
export type AgentName = keyof typeof agentsManifest;
|
||||
|
||||
export type AgentApiKeyName = (typeof agentsManifest)[AgentName]["apiKeyNames"][number];
|
||||
|
||||
// payload type for agent execution
|
||||
export type Payload = {
|
||||
"~pullfrog": true;
|
||||
|
||||
@@ -24,12 +24,12 @@ import { setupGitAuth, setupGitConfig } from "./utils/setup.ts";
|
||||
export const AgentName = type.enumerated(...Object.values(agents).map((agent) => agent.name));
|
||||
|
||||
export const AgentInputKey = type.enumerated(
|
||||
...Object.values(agents).flatMap((agent) => agent.inputKeys)
|
||||
...Object.values(agents).flatMap((agent) => agent.apiKeyNames)
|
||||
);
|
||||
export type AgentInputKey = typeof AgentInputKey.infer;
|
||||
|
||||
const keyInputDefs = flatMorph(agents, (_, agent) =>
|
||||
agent.inputKeys.map((inputKey) => [inputKey, "string | undefined?"] as const)
|
||||
agent.apiKeyNames.map((inputKey) => [inputKey, "string | undefined?"] as const)
|
||||
);
|
||||
|
||||
export const Inputs = type({
|
||||
@@ -102,7 +102,7 @@ function throwMissingApiKeyError({
|
||||
|
||||
// Find which agents have inputKeys that match the provided inputs
|
||||
const availableAgents = Object.values(agents).filter((agent) =>
|
||||
agent.inputKeys.some((inputKey) => inputs[inputKey])
|
||||
agent.apiKeyNames.some((inputKey) => inputs[inputKey])
|
||||
);
|
||||
|
||||
let message = `Pullfrog is configured to use ${agentName}, but the associated API key was not provided.
|
||||
@@ -230,13 +230,13 @@ async function installAgentCli(ctx: MainContext): Promise<void> {
|
||||
}
|
||||
|
||||
function validateApiKey(ctx: MainContext): void {
|
||||
const matchingInputKey = ctx.agent.inputKeys.find(
|
||||
const matchingInputKey = ctx.agent.apiKeyNames.find(
|
||||
(inputKey: string) => ctx.inputs[inputKey as AgentInputKey]
|
||||
);
|
||||
if (!matchingInputKey) {
|
||||
throwMissingApiKeyError({
|
||||
agentName: ctx.agentName,
|
||||
inputKeys: ctx.agent.inputKeys,
|
||||
inputKeys: ctx.agent.apiKeyNames,
|
||||
repoContext: ctx.repoContext,
|
||||
inputs: ctx.inputs,
|
||||
});
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
[] test if home directory mcp.json works if mcp.json is specified in repo
|
||||
[] add footer to the working comment ("executed by {agent}", link to pullfrog (homepage) w/ small logo?, feedback (create github issue), link to workflow run)- see https://github.com/colinhacks/zod/issues/5459#issuecomment-3548382991
|
||||
[] avoid passing all of process.env into agents: minimum # of vars
|
||||
[] external.ts align to agents
|
||||
[] toon encode in prompt
|
||||
|
||||
## MAYBE
|
||||
|
||||
@@ -32,4 +34,5 @@
|
||||
[x] standardize mcp server
|
||||
[x] entry.js
|
||||
[x] split up prompts, load dynamically based on mode
|
||||
[x] log.txt to stdout
|
||||
[x] log.txt to stdout
|
||||
[x] rename mcp to use underscore
|
||||
Reference in New Issue
Block a user