Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94e2b5f6e0 | |||
| 03810d574e | |||
| f52e94c612 | |||
| 9444a0e208 | |||
| 2296060d04 | |||
| 458bfe18a0 | |||
| 4cfb9b5008 | |||
| 06542e382a | |||
| bcdf6ab5fb | |||
| 314f669f10 |
@@ -34,7 +34,7 @@ jobs:
|
||||
registry-url: "https://registry.npmjs.org"
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --no-frozen-lockfile
|
||||
run: pnpm install --frozen-lockfile
|
||||
|
||||
- name: Get package version
|
||||
id: version
|
||||
|
||||
+6
-2
@@ -1,6 +1,10 @@
|
||||
# Ensure lockfile is up to date
|
||||
echo "🔒 Updating lockfile..."
|
||||
pnpm install --lockfile-only
|
||||
|
||||
# Build the action before committing
|
||||
echo "🔨 Building action..."
|
||||
npm run build
|
||||
|
||||
# Add the built files to the commit
|
||||
git add entry.cjs
|
||||
# Add the built files and lockfile to the commit
|
||||
git add entry.cjs pnpm-lock.yaml
|
||||
|
||||
+26
-7
@@ -49,7 +49,10 @@ export class ClaudeAgent implements Agent {
|
||||
try {
|
||||
const result = await spawn({
|
||||
cmd: "bash",
|
||||
args: ["-c", "curl -fsSL https://claude.ai/install.sh | bash -s 1.0.93"],
|
||||
args: [
|
||||
"-c",
|
||||
"curl -fsSL https://claude.ai/install.sh | bash -s 1.0.93",
|
||||
],
|
||||
env: { ANTHROPIC_API_KEY: this.apiKey },
|
||||
timeout: 120000, // 2 minute timeout
|
||||
onStdout: () => {},
|
||||
@@ -57,7 +60,9 @@ export class ClaudeAgent implements Agent {
|
||||
});
|
||||
|
||||
if (result.exitCode !== 0) {
|
||||
throw new Error(`Installation failed with exit code ${result.exitCode}: ${result.stderr}`);
|
||||
throw new Error(
|
||||
`Installation failed with exit code ${result.exitCode}: ${result.stderr}`
|
||||
);
|
||||
}
|
||||
|
||||
core.info("Claude Code installed successfully");
|
||||
@@ -80,12 +85,15 @@ export class ClaudeAgent implements Agent {
|
||||
"--output-format",
|
||||
"stream-json",
|
||||
"--verbose",
|
||||
"--debug",
|
||||
"--permission-mode",
|
||||
"bypassPermissions",
|
||||
];
|
||||
|
||||
if (!process.env.GITHUB_INSTALLATION_TOKEN) {
|
||||
throw new Error("GITHUB_INSTALLATION_TOKEN is required for GitHub integration");
|
||||
throw new Error(
|
||||
"GITHUB_INSTALLATION_TOKEN is required for GitHub integration"
|
||||
);
|
||||
}
|
||||
|
||||
const mcpConfig = createMcpConfig(process.env.GITHUB_INSTALLATION_TOKEN);
|
||||
@@ -151,7 +159,8 @@ export class ClaudeAgent implements Agent {
|
||||
try {
|
||||
core.endGroup();
|
||||
} catch {}
|
||||
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
||||
const errorMessage =
|
||||
error instanceof Error ? error.message : "Unknown error";
|
||||
return {
|
||||
success: false,
|
||||
error: `Failed to execute Claude Code: ${errorMessage}`,
|
||||
@@ -177,7 +186,12 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
||||
["model", parsedChunk.model],
|
||||
["cwd", parsedChunk.cwd],
|
||||
["permission_mode", parsedChunk.permissionMode],
|
||||
["tools", parsedChunk.tools?.length ? `${parsedChunk.tools.length} tools` : "none"],
|
||||
[
|
||||
"tools",
|
||||
parsedChunk.tools?.length
|
||||
? `${parsedChunk.tools.length} tools`
|
||||
: "none",
|
||||
],
|
||||
[
|
||||
"mcp_servers",
|
||||
parsedChunk.mcp_servers?.length
|
||||
@@ -204,7 +218,9 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
||||
for (const content of parsedChunk.message.content) {
|
||||
if (content.type === "text") {
|
||||
if (content.text.trim()) {
|
||||
core.info(boxString(content.text.trim(), { title: "Claude Code" }));
|
||||
core.info(
|
||||
boxString(content.text.trim(), { title: "Claude Code" })
|
||||
);
|
||||
}
|
||||
} else if (content.type === "tool_use") {
|
||||
if (agent) {
|
||||
@@ -289,7 +305,10 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
||||
if (parsedChunk.subtype === "success") {
|
||||
core.info(
|
||||
tableString([
|
||||
["Cost", `$${parsedChunk.total_cost_usd?.toFixed(4) || "0.0000"}`],
|
||||
[
|
||||
"Cost",
|
||||
`$${parsedChunk.total_cost_usd?.toFixed(4) || "0.0000"}`,
|
||||
],
|
||||
["Input Tokens", parsedChunk.usage?.input_tokens || 0],
|
||||
["Output Tokens", parsedChunk.usage?.output_tokens || 0],
|
||||
["Duration", `${parsedChunk.duration_ms}ms`],
|
||||
|
||||
@@ -7,10 +7,12 @@
|
||||
|
||||
import * as core from "@actions/core";
|
||||
import { type ExecutionInputs, type MainParams, main } from "./main.ts";
|
||||
import packageJson from "./package.json" with { type: "json" };
|
||||
import { setupGitHubInstallationToken } from "./utils/github.ts";
|
||||
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
console.log(`🐸 Running pullfrog/action@${packageJson.version}...`);
|
||||
const prompt = core.getInput("prompt", { required: true });
|
||||
const anthropic_api_key = core.getInput("anthropic_api_key");
|
||||
|
||||
@@ -44,7 +46,6 @@ async function run(): Promise<void> {
|
||||
|
||||
const result = await main(params);
|
||||
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(result.error || "Agent execution failed");
|
||||
}
|
||||
|
||||
+8
-2
@@ -1,12 +1,18 @@
|
||||
/**
|
||||
* Simple MCP configuration helper for adding our minimal GitHub comment server
|
||||
*/
|
||||
const actionPath = process.env.GITHUB_ACTION_PATH || process.cwd();
|
||||
// const actionPath = process.env.GITHUB_ACTION_PATH || process.cwd();
|
||||
|
||||
import { fromHere } from "@ark/fs";
|
||||
|
||||
const actionPath = fromHere("..");
|
||||
|
||||
export function createMcpConfig(githubInstallationToken: string) {
|
||||
const githubRepository = process.env.GITHUB_REPOSITORY;
|
||||
if (!githubRepository) {
|
||||
throw new Error('GITHUB_REPOSITORY environment variable is required for MCP GitHub integration');
|
||||
throw new Error(
|
||||
"GITHUB_REPOSITORY environment variable is required for MCP GitHub integration"
|
||||
);
|
||||
}
|
||||
|
||||
return JSON.stringify(
|
||||
|
||||
+134
-70
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env node
|
||||
import { writeFileSync } from "node:fs";
|
||||
// Minimal GitHub Issue Comment MCP Server
|
||||
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
||||
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
||||
@@ -7,10 +8,34 @@ import { type } from "arktype";
|
||||
import { z } from "zod";
|
||||
import { resolveRepoContext } from "../utils/repo-context.ts";
|
||||
|
||||
const server = new McpServer({
|
||||
name: "Minimal GitHub Issue Comment Server",
|
||||
version: "0.0.1",
|
||||
});
|
||||
// Simple error logging to file
|
||||
function logError(message: string, error?: any) {
|
||||
const timestamp = new Date().toISOString();
|
||||
const errorText = error ? `\nError: ${error.message}\nStack: ${error.stack}` : "";
|
||||
const logEntry = `[${timestamp}] ${message}${errorText}\n`;
|
||||
|
||||
try {
|
||||
writeFileSync("/tmp/mcp-error.log", logEntry, { flag: "a" });
|
||||
console.error(logEntry);
|
||||
} catch (writeError) {
|
||||
console.error(`Failed to write error log: ${writeError}`);
|
||||
console.error(logEntry);
|
||||
}
|
||||
}
|
||||
|
||||
let server: McpServer;
|
||||
|
||||
try {
|
||||
logError("Creating MCP server...");
|
||||
server = new McpServer({
|
||||
name: "Minimal GitHub Issue Comment Server",
|
||||
version: "0.0.1",
|
||||
});
|
||||
logError("MCP server created successfully");
|
||||
} catch (error) {
|
||||
logError("Failed to create MCP server", error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Define the schema for creating issue comments
|
||||
const Comment = type({
|
||||
@@ -18,75 +43,114 @@ const Comment = type({
|
||||
body: type.string.describe("the comment body content"),
|
||||
});
|
||||
|
||||
server.tool(
|
||||
"create_issue_comment",
|
||||
"Create a comment on a GitHub issue",
|
||||
{
|
||||
issueNumber: z.number().describe("the issue number to comment on"),
|
||||
body: z.string().describe("the comment body content"),
|
||||
},
|
||||
async ({ issueNumber, body }) => {
|
||||
try {
|
||||
Comment.assert({ issueNumber, body });
|
||||
try {
|
||||
logError("Registering create_issue_comment tool...");
|
||||
|
||||
const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
||||
if (!githubInstallationToken) {
|
||||
throw new Error("GITHUB_INSTALLATION_TOKEN environment variable is required");
|
||||
server.tool(
|
||||
"create_issue_comment",
|
||||
"Create a comment on a GitHub issue",
|
||||
{
|
||||
issueNumber: z.number().describe("the issue number to comment on"),
|
||||
body: z.string().describe("the comment body content"),
|
||||
},
|
||||
async ({ issueNumber, body }) => {
|
||||
try {
|
||||
Comment.assert({ issueNumber, body });
|
||||
|
||||
const githubInstallationToken = process.env.GITHUB_INSTALLATION_TOKEN;
|
||||
if (!githubInstallationToken) {
|
||||
throw new Error("GITHUB_INSTALLATION_TOKEN environment variable is required");
|
||||
}
|
||||
|
||||
// Resolve repository context from environment
|
||||
const repoContext = resolveRepoContext();
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: githubInstallationToken,
|
||||
});
|
||||
|
||||
const result = await octokit.rest.issues.createComment({
|
||||
owner: repoContext.owner,
|
||||
repo: repoContext.name,
|
||||
issue_number: issueNumber,
|
||||
body: body,
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
success: true,
|
||||
commentId: result.data.id,
|
||||
url: result.data.html_url,
|
||||
body: result.data.body,
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
logError("Tool execution failed", error);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error creating comment: ${errorMessage}`,
|
||||
},
|
||||
],
|
||||
error: errorMessage,
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
|
||||
// Resolve repository context from environment
|
||||
const repoContext = resolveRepoContext();
|
||||
|
||||
const octokit = new Octokit({
|
||||
auth: githubInstallationToken,
|
||||
});
|
||||
|
||||
const result = await octokit.rest.issues.createComment({
|
||||
owner: repoContext.owner,
|
||||
repo: repoContext.name,
|
||||
issue_number: issueNumber,
|
||||
body: body,
|
||||
});
|
||||
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: JSON.stringify(
|
||||
{
|
||||
success: true,
|
||||
commentId: result.data.id,
|
||||
url: result.data.html_url,
|
||||
body: result.data.body,
|
||||
},
|
||||
null,
|
||||
2
|
||||
),
|
||||
},
|
||||
],
|
||||
};
|
||||
} catch (error) {
|
||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||
return {
|
||||
content: [
|
||||
{
|
||||
type: "text",
|
||||
text: `Error creating comment: ${errorMessage}`,
|
||||
},
|
||||
],
|
||||
error: errorMessage,
|
||||
isError: true,
|
||||
};
|
||||
}
|
||||
}
|
||||
);
|
||||
);
|
||||
|
||||
async function runServer() {
|
||||
const transport = new StdioServerTransport();
|
||||
await server.connect(transport);
|
||||
process.on("exit", () => {
|
||||
server.close();
|
||||
});
|
||||
logError("Tool registered successfully");
|
||||
} catch (error) {
|
||||
logError("Failed to register tool", error);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
runServer().catch(console.error);
|
||||
async function runServer() {
|
||||
try {
|
||||
logError("Starting MCP server...");
|
||||
|
||||
const transport = new StdioServerTransport();
|
||||
logError("Transport created, attempting connection...");
|
||||
|
||||
await server.connect(transport);
|
||||
logError("MCP server connected successfully");
|
||||
|
||||
process.on("exit", () => {
|
||||
logError("Process exiting, closing server...");
|
||||
server.close();
|
||||
});
|
||||
|
||||
process.on("SIGTERM", () => {
|
||||
logError("SIGTERM received, closing server...");
|
||||
server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
|
||||
process.on("SIGINT", () => {
|
||||
logError("SIGINT received, closing server...");
|
||||
server.close();
|
||||
process.exit(0);
|
||||
});
|
||||
} catch (error) {
|
||||
logError("Server startup failed", error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
logError("Initializing MCP server process...");
|
||||
|
||||
runServer().catch((error) => {
|
||||
logError("Unhandled server error", error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
+5
-4
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@pullfrog/action",
|
||||
"version": "0.0.20",
|
||||
"version": "0.0.29",
|
||||
"type": "module",
|
||||
"files": [
|
||||
"index.js",
|
||||
@@ -27,6 +27,7 @@
|
||||
"createLockfile": "pnpm --ignore-workspace install"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ark/fs": "0.49.0",
|
||||
"@actions/core": "^1.11.1",
|
||||
"@modelcontextprotocol/sdk": "^1.17.5",
|
||||
"@octokit/rest": "^22.0.0",
|
||||
@@ -34,7 +35,8 @@
|
||||
"arktype": "^2.1.22",
|
||||
"dotenv": "^17.2.2",
|
||||
"execa": "^9.6.0",
|
||||
"table": "^6.9.0"
|
||||
"table": "^6.9.0",
|
||||
"zod": "^3.24.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^20.10.0",
|
||||
@@ -42,8 +44,7 @@
|
||||
"esbuild": "^0.25.9",
|
||||
"husky": "^9.0.0",
|
||||
"typescript": "^5.3.0",
|
||||
"zshy": "^0.4.1",
|
||||
"zod": "^3.24.4"
|
||||
"zshy": "^0.4.1"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -1,23 +1,23 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { dirname, extname, join, resolve } from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { extname, join, resolve } from "node:path";
|
||||
import { pathToFileURL } from "node:url";
|
||||
import { fromHere } from "@ark/fs";
|
||||
import arg from "arg";
|
||||
import { config } from "dotenv";
|
||||
import { main } from "./main.ts";
|
||||
import packageJson from "./package.json" with { type: "json" };
|
||||
import { runAct } from "./utils/act.ts";
|
||||
import { setupGitHubInstallationToken } from "./utils/github.ts";
|
||||
import { setupTestRepo } from "./utils/setup.ts";
|
||||
|
||||
config();
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
export async function run(
|
||||
prompt: string,
|
||||
options: { act?: boolean } = {}
|
||||
): Promise<{ success: boolean; output?: string | undefined; error?: string | undefined }> {
|
||||
try {
|
||||
console.log(`🐸 Running pullfrog/action@${packageJson.version}...`);
|
||||
if (options.act) {
|
||||
console.log("🐳 Running with Docker/act...");
|
||||
runAct(prompt);
|
||||
@@ -129,7 +129,7 @@ Examples:
|
||||
const ext = extname(filePath).toLowerCase();
|
||||
let resolvedPath: string;
|
||||
|
||||
const fixturesPath = join(__dirname, "fixtures", filePath);
|
||||
const fixturesPath = fromHere("fixtures", filePath);
|
||||
if (existsSync(fixturesPath)) {
|
||||
resolvedPath = fixturesPath;
|
||||
} else if (existsSync(filePath)) {
|
||||
|
||||
Generated
+11
-3
@@ -11,6 +11,9 @@ importers:
|
||||
'@actions/core':
|
||||
specifier: ^1.11.1
|
||||
version: 1.11.1
|
||||
'@ark/fs':
|
||||
specifier: 0.49.0
|
||||
version: 0.49.0
|
||||
'@modelcontextprotocol/sdk':
|
||||
specifier: ^1.17.5
|
||||
version: 1.19.1
|
||||
@@ -32,6 +35,9 @@ importers:
|
||||
table:
|
||||
specifier: ^6.9.0
|
||||
version: 6.9.0
|
||||
zod:
|
||||
specifier: ^3.24.4
|
||||
version: 3.25.76
|
||||
devDependencies:
|
||||
'@types/node':
|
||||
specifier: ^20.10.0
|
||||
@@ -48,9 +54,6 @@ importers:
|
||||
typescript:
|
||||
specifier: ^5.3.0
|
||||
version: 5.9.3
|
||||
zod:
|
||||
specifier: ^3.24.4
|
||||
version: 3.25.76
|
||||
zshy:
|
||||
specifier: ^0.4.1
|
||||
version: 0.4.3(typescript@5.9.3)
|
||||
@@ -69,6 +72,9 @@ packages:
|
||||
'@actions/io@1.1.3':
|
||||
resolution: {integrity: sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q==}
|
||||
|
||||
'@ark/fs@0.49.0':
|
||||
resolution: {integrity: sha512-AEjAQS/bu1CGIRiKK/XLaQ73cSJHixfexq28wNt+kBpQ0h1RwVIVzaGsn/+5IWw6DEbR7LB+3hil5gzrzEeyZQ==}
|
||||
|
||||
'@ark/schema@0.49.0':
|
||||
resolution: {integrity: sha512-GphZBLpW72iS0v4YkeUtV3YIno35Gimd7+ezbPO9GwEi9kzdUrPVjvf6aXSBAfHikaFc/9pqZOpv3pOXnC71tw==}
|
||||
|
||||
@@ -902,6 +908,8 @@ snapshots:
|
||||
|
||||
'@actions/io@1.1.3': {}
|
||||
|
||||
'@ark/fs@0.49.0': {}
|
||||
|
||||
'@ark/schema@0.49.0':
|
||||
dependencies:
|
||||
'@ark/util': 0.49.0
|
||||
|
||||
Reference in New Issue
Block a user