Skip to content

Commit 1abbd7f

Browse files
committed
Refactor action with agent interface system and make anthropic_api_key optional
- Created extensible agent interface with install() and execute() methods - Moved Claude Code logic to agents/claude.ts implementing Agent interface - Added utilities directory for reusable functions (exec, files) - Refactored index.ts to be minimal (35 lines) using agent abstraction - Made anthropic_api_key optional in action.yml - Updated Node.js imports to use node: prefix convention - Bumped version to 0.0.5 - Architecture now supports multiple agents (OpenAI, Gemini, etc.)
1 parent 6a0d9cc commit 1abbd7f

File tree

10 files changed

+364
-71
lines changed

10 files changed

+364
-71
lines changed

action.yml

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
name: 'Simple Message Action'
2-
description: 'A simple GitHub Action that prints a message to the console'
1+
name: 'Pullfrog Claude Code Action'
2+
description: 'Execute Claude Code with a prompt using Anthropic API'
33
author: 'Pullfrog'
44

55
inputs:
6-
message:
7-
description: 'Message to print to console'
6+
prompt:
7+
description: 'Prompt to send to Claude Code'
88
required: true
9-
default: 'Hello from Pullfrog Action!'
9+
default: 'Hello from Claude Code!'
10+
anthropic_api_key:
11+
description: 'Anthropic API key for Claude Code authentication'
12+
required: false
1013

1114
runs:
1215
using: 'node20'
1316
main: 'index.cjs'
1417

1518
branding:
16-
icon: 'message-circle'
17-
color: 'blue'
19+
icon: 'code'
20+
color: 'orange'

agents/claude.ts

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import * as core from "@actions/core";
2+
import { executeCommand } from "../utils/exec";
3+
import { createTempFile } from "../utils/files";
4+
import type { Agent, AgentConfig, AgentResult } from "./types";
5+
6+
/**
7+
* Claude Code agent implementation
8+
*/
9+
export class ClaudeAgent implements Agent {
10+
private apiKey: string;
11+
12+
constructor(config: AgentConfig) {
13+
if (!config.apiKey) {
14+
throw new Error("Claude agent requires an API key");
15+
}
16+
this.apiKey = config.apiKey;
17+
}
18+
19+
/**
20+
* Install Claude Code CLI
21+
*/
22+
async install(): Promise<void> {
23+
core.info("Installing Claude Code...");
24+
try {
25+
await executeCommand("curl -fsSL https://claude.ai/install.sh | bash -s 1.0.93");
26+
core.info("Claude Code installed successfully");
27+
} catch (error) {
28+
throw new Error(`Failed to install Claude Code: ${error}`);
29+
}
30+
}
31+
32+
/**
33+
* Execute Claude Code with the given prompt
34+
*/
35+
async execute(prompt: string): Promise<AgentResult> {
36+
core.info("Executing Claude Code...");
37+
38+
try {
39+
// Create a temporary file for the prompt
40+
const promptFile = createTempFile(prompt, "prompt.txt");
41+
42+
// Execute Claude Code with the prompt
43+
const command = `$HOME/.local/bin/claude --dangerously-skip-permissions "${promptFile}"`;
44+
core.info(`Executing: ${command}`);
45+
46+
const { stdout, stderr } = await executeCommand(command, {
47+
ANTHROPIC_API_KEY: this.apiKey,
48+
});
49+
50+
if (stderr) {
51+
core.warning(`Claude Code stderr: ${stderr}`);
52+
}
53+
54+
if (stdout) {
55+
core.info("Claude Code output:");
56+
console.log(stdout);
57+
}
58+
59+
core.info("Claude Code executed successfully");
60+
61+
return {
62+
success: true,
63+
output: stdout,
64+
error: stderr || undefined,
65+
metadata: {
66+
promptFile,
67+
command,
68+
},
69+
};
70+
} catch (error) {
71+
const errorMessage = error instanceof Error ? error.message : "Unknown error";
72+
return {
73+
success: false,
74+
error: `Failed to execute Claude Code: ${errorMessage}`,
75+
};
76+
}
77+
}
78+
}

agents/factory.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { ClaudeAgent } from "./claude";
2+
import type { Agent, AgentConfig } from "./types";
3+
4+
export type AgentType = "claude";
5+
6+
/**
7+
* Factory for creating agent instances
8+
*/
9+
export function createAgent(type: AgentType, config: AgentConfig): Agent {
10+
switch (type) {
11+
case "claude":
12+
return new ClaudeAgent(config);
13+
default:
14+
throw new Error(`Unsupported agent type: ${type}`);
15+
}
16+
}

agents/index.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export * from "./claude";
2+
export * from "./factory";
3+
export * from "./types";

agents/types.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/**
2+
* Standard interface for all Pullfrog agents
3+
*/
4+
export interface Agent {
5+
/**
6+
* Install the agent and any required dependencies
7+
*/
8+
install(): Promise<void>;
9+
10+
/**
11+
* Execute the agent with the given prompt
12+
* @param prompt The prompt to send to the agent
13+
* @param options Additional options specific to the agent
14+
*/
15+
execute(prompt: string, options?: Record<string, any>): Promise<AgentResult>;
16+
}
17+
18+
/**
19+
* Result returned by agent execution
20+
*/
21+
export interface AgentResult {
22+
success: boolean;
23+
output?: string;
24+
error?: string;
25+
metadata?: Record<string, any>;
26+
}
27+
28+
/**
29+
* Configuration for agent creation
30+
*/
31+
export interface AgentConfig {
32+
apiKey?: string;
33+
[key: string]: any;
34+
}

0 commit comments

Comments
 (0)