Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 458bfe18a0 | |||
| 4cfb9b5008 | |||
| 06542e382a | |||
| bcdf6ab5fb | |||
| 314f669f10 | |||
| a24275e21b | |||
| 872e620342 |
+26
-31
@@ -1,4 +1,3 @@
|
|||||||
import { readFileSync } from "node:fs";
|
|
||||||
import { access, constants } from "node:fs/promises";
|
import { access, constants } from "node:fs/promises";
|
||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { createMcpConfig } from "../mcp/config.ts";
|
import { createMcpConfig } from "../mcp/config.ts";
|
||||||
@@ -50,7 +49,10 @@ export class ClaudeAgent implements Agent {
|
|||||||
try {
|
try {
|
||||||
const result = await spawn({
|
const result = await spawn({
|
||||||
cmd: "bash",
|
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 },
|
env: { ANTHROPIC_API_KEY: this.apiKey },
|
||||||
timeout: 120000, // 2 minute timeout
|
timeout: 120000, // 2 minute timeout
|
||||||
onStdout: () => {},
|
onStdout: () => {},
|
||||||
@@ -58,7 +60,9 @@ export class ClaudeAgent implements Agent {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (result.exitCode !== 0) {
|
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");
|
core.info("Claude Code installed successfully");
|
||||||
@@ -81,12 +85,15 @@ export class ClaudeAgent implements Agent {
|
|||||||
"--output-format",
|
"--output-format",
|
||||||
"stream-json",
|
"stream-json",
|
||||||
"--verbose",
|
"--verbose",
|
||||||
|
"--debug",
|
||||||
"--permission-mode",
|
"--permission-mode",
|
||||||
"bypassPermissions",
|
"bypassPermissions",
|
||||||
];
|
];
|
||||||
|
|
||||||
if (!process.env.GITHUB_INSTALLATION_TOKEN) {
|
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);
|
const mcpConfig = createMcpConfig(process.env.GITHUB_INSTALLATION_TOKEN);
|
||||||
@@ -152,7 +159,8 @@ export class ClaudeAgent implements Agent {
|
|||||||
try {
|
try {
|
||||||
core.endGroup();
|
core.endGroup();
|
||||||
} catch {}
|
} catch {}
|
||||||
const errorMessage = error instanceof Error ? error.message : "Unknown error";
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : "Unknown error";
|
||||||
return {
|
return {
|
||||||
success: false,
|
success: false,
|
||||||
error: `Failed to execute Claude Code: ${errorMessage}`,
|
error: `Failed to execute Claude Code: ${errorMessage}`,
|
||||||
@@ -178,7 +186,12 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
|||||||
["model", parsedChunk.model],
|
["model", parsedChunk.model],
|
||||||
["cwd", parsedChunk.cwd],
|
["cwd", parsedChunk.cwd],
|
||||||
["permission_mode", parsedChunk.permissionMode],
|
["permission_mode", parsedChunk.permissionMode],
|
||||||
["tools", parsedChunk.tools?.length ? `${parsedChunk.tools.length} tools` : "none"],
|
[
|
||||||
|
"tools",
|
||||||
|
parsedChunk.tools?.length
|
||||||
|
? `${parsedChunk.tools.length} tools`
|
||||||
|
: "none",
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"mcp_servers",
|
"mcp_servers",
|
||||||
parsedChunk.mcp_servers?.length
|
parsedChunk.mcp_servers?.length
|
||||||
@@ -193,29 +206,6 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
|||||||
],
|
],
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
|
|
||||||
// Check for failed MCP servers and show debug logs
|
|
||||||
if (parsedChunk.mcp_servers?.length > 0) {
|
|
||||||
const failedServers = parsedChunk.mcp_servers.filter(
|
|
||||||
(server: any) => server.status === "failed"
|
|
||||||
);
|
|
||||||
if (failedServers.length > 0) {
|
|
||||||
const failedNames = failedServers.map((server: any) => server.name).join(", ");
|
|
||||||
|
|
||||||
// Try to read MCP server debug logs
|
|
||||||
let debugInfo = "";
|
|
||||||
try {
|
|
||||||
const logContent = readFileSync("/tmp/mcp-server.log", "utf-8");
|
|
||||||
core.error("MCP Server Debug Log:");
|
|
||||||
core.error(logContent);
|
|
||||||
debugInfo = " (see debug log above)";
|
|
||||||
} catch (logError) {
|
|
||||||
debugInfo = ` (could not read debug log: ${logError instanceof Error ? logError.message : String(logError)})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(`MCP servers failed to start: ${failedNames}${debugInfo}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -228,7 +218,9 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
|||||||
for (const content of parsedChunk.message.content) {
|
for (const content of parsedChunk.message.content) {
|
||||||
if (content.type === "text") {
|
if (content.type === "text") {
|
||||||
if (content.text.trim()) {
|
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") {
|
} else if (content.type === "tool_use") {
|
||||||
if (agent) {
|
if (agent) {
|
||||||
@@ -313,7 +305,10 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void {
|
|||||||
if (parsedChunk.subtype === "success") {
|
if (parsedChunk.subtype === "success") {
|
||||||
core.info(
|
core.info(
|
||||||
tableString([
|
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],
|
["Input Tokens", parsedChunk.usage?.input_tokens || 0],
|
||||||
["Output Tokens", parsedChunk.usage?.output_tokens || 0],
|
["Output Tokens", parsedChunk.usage?.output_tokens || 0],
|
||||||
["Duration", `${parsedChunk.duration_ms}ms`],
|
["Duration", `${parsedChunk.duration_ms}ms`],
|
||||||
|
|||||||
@@ -25500,7 +25500,6 @@ var core4 = __toESM(require_core(), 1);
|
|||||||
var core2 = __toESM(require_core(), 1);
|
var core2 = __toESM(require_core(), 1);
|
||||||
|
|
||||||
// agents/claude.ts
|
// agents/claude.ts
|
||||||
var import_node_fs = require("node:fs");
|
|
||||||
var import_promises = require("node:fs/promises");
|
var import_promises = require("node:fs/promises");
|
||||||
var core = __toESM(require_core(), 1);
|
var core = __toESM(require_core(), 1);
|
||||||
|
|
||||||
@@ -25509,17 +25508,20 @@ var actionPath = process.env.GITHUB_ACTION_PATH || process.cwd();
|
|||||||
function createMcpConfig(githubInstallationToken) {
|
function createMcpConfig(githubInstallationToken) {
|
||||||
const githubRepository = process.env.GITHUB_REPOSITORY;
|
const githubRepository = process.env.GITHUB_REPOSITORY;
|
||||||
if (!githubRepository) {
|
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(
|
return JSON.stringify(
|
||||||
{
|
{
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
minimal_github_comment: {
|
minimal_github_comment: {
|
||||||
command: `${actionPath}/mcp/debug-server.sh`,
|
command: "node",
|
||||||
args: [`${actionPath}/mcp/server.ts`],
|
args: [`${actionPath}/mcp/server.ts`],
|
||||||
env: {
|
env: {
|
||||||
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
|
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
|
||||||
GITHUB_REPOSITORY: githubRepository
|
GITHUB_REPOSITORY: githubRepository,
|
||||||
|
LOG_LEVEL: "debug"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -25732,7 +25734,10 @@ var ClaudeAgent = class {
|
|||||||
try {
|
try {
|
||||||
const result = await spawn({
|
const result = await spawn({
|
||||||
cmd: "bash",
|
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 },
|
env: { ANTHROPIC_API_KEY: this.apiKey },
|
||||||
timeout: 12e4,
|
timeout: 12e4,
|
||||||
// 2 minute timeout
|
// 2 minute timeout
|
||||||
@@ -25741,7 +25746,9 @@ var ClaudeAgent = class {
|
|||||||
onStderr: (chunk) => process.stderr.write(chunk)
|
onStderr: (chunk) => process.stderr.write(chunk)
|
||||||
});
|
});
|
||||||
if (result.exitCode !== 0) {
|
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");
|
core.info("Claude Code installed successfully");
|
||||||
} catch (error2) {
|
} catch (error2) {
|
||||||
@@ -25761,11 +25768,14 @@ var ClaudeAgent = class {
|
|||||||
"--output-format",
|
"--output-format",
|
||||||
"stream-json",
|
"stream-json",
|
||||||
"--verbose",
|
"--verbose",
|
||||||
|
"--debug",
|
||||||
"--permission-mode",
|
"--permission-mode",
|
||||||
"bypassPermissions"
|
"bypassPermissions"
|
||||||
];
|
];
|
||||||
if (!process.env.GITHUB_INSTALLATION_TOKEN) {
|
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);
|
const mcpConfig = createMcpConfig(process.env.GITHUB_INSTALLATION_TOKEN);
|
||||||
console.log("\u{1F4CB} MCP Config:", mcpConfig);
|
console.log("\u{1F4CB} MCP Config:", mcpConfig);
|
||||||
@@ -25848,7 +25858,10 @@ function processJSONChunk(chunk, agent) {
|
|||||||
["model", parsedChunk.model],
|
["model", parsedChunk.model],
|
||||||
["cwd", parsedChunk.cwd],
|
["cwd", parsedChunk.cwd],
|
||||||
["permission_mode", parsedChunk.permissionMode],
|
["permission_mode", parsedChunk.permissionMode],
|
||||||
["tools", parsedChunk.tools?.length ? `${parsedChunk.tools.length} tools` : "none"],
|
[
|
||||||
|
"tools",
|
||||||
|
parsedChunk.tools?.length ? `${parsedChunk.tools.length} tools` : "none"
|
||||||
|
],
|
||||||
[
|
[
|
||||||
"mcp_servers",
|
"mcp_servers",
|
||||||
parsedChunk.mcp_servers?.length ? `${parsedChunk.mcp_servers.length} servers` : "none"
|
parsedChunk.mcp_servers?.length ? `${parsedChunk.mcp_servers.length} servers` : "none"
|
||||||
@@ -25859,24 +25872,6 @@ function processJSONChunk(chunk, agent) {
|
|||||||
]
|
]
|
||||||
])
|
])
|
||||||
);
|
);
|
||||||
if (parsedChunk.mcp_servers?.length > 0) {
|
|
||||||
const failedServers = parsedChunk.mcp_servers.filter(
|
|
||||||
(server) => server.status === "failed"
|
|
||||||
);
|
|
||||||
if (failedServers.length > 0) {
|
|
||||||
const failedNames = failedServers.map((server) => server.name).join(", ");
|
|
||||||
let debugInfo = "";
|
|
||||||
try {
|
|
||||||
const logContent = (0, import_node_fs.readFileSync)("/tmp/mcp-server.log", "utf-8");
|
|
||||||
core.error("MCP Server Debug Log:");
|
|
||||||
core.error(logContent);
|
|
||||||
debugInfo = " (see debug log above)";
|
|
||||||
} catch (logError) {
|
|
||||||
debugInfo = ` (could not read debug log: ${logError instanceof Error ? logError.message : String(logError)})`;
|
|
||||||
}
|
|
||||||
throw new Error(`MCP servers failed to start: ${failedNames}${debugInfo}`);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "assistant":
|
case "assistant":
|
||||||
@@ -25887,7 +25882,9 @@ function processJSONChunk(chunk, agent) {
|
|||||||
for (const content of parsedChunk.message.content) {
|
for (const content of parsedChunk.message.content) {
|
||||||
if (content.type === "text") {
|
if (content.type === "text") {
|
||||||
if (content.text.trim()) {
|
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") {
|
} else if (content.type === "tool_use") {
|
||||||
if (agent) {
|
if (agent) {
|
||||||
@@ -25954,7 +25951,10 @@ function processJSONChunk(chunk, agent) {
|
|||||||
if (parsedChunk.subtype === "success") {
|
if (parsedChunk.subtype === "success") {
|
||||||
core.info(
|
core.info(
|
||||||
tableString([
|
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],
|
["Input Tokens", parsedChunk.usage?.input_tokens || 0],
|
||||||
["Output Tokens", parsedChunk.usage?.output_tokens || 0],
|
["Output Tokens", parsedChunk.usage?.output_tokens || 0],
|
||||||
["Duration", `${parsedChunk.duration_ms}ms`],
|
["Duration", `${parsedChunk.duration_ms}ms`],
|
||||||
|
|||||||
+5
-2
@@ -6,18 +6,21 @@ const actionPath = process.env.GITHUB_ACTION_PATH || process.cwd();
|
|||||||
export function createMcpConfig(githubInstallationToken: string) {
|
export function createMcpConfig(githubInstallationToken: string) {
|
||||||
const githubRepository = process.env.GITHUB_REPOSITORY;
|
const githubRepository = process.env.GITHUB_REPOSITORY;
|
||||||
if (!githubRepository) {
|
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(
|
return JSON.stringify(
|
||||||
{
|
{
|
||||||
mcpServers: {
|
mcpServers: {
|
||||||
minimal_github_comment: {
|
minimal_github_comment: {
|
||||||
command: `${actionPath}/mcp/debug-server.sh`,
|
command: "node",
|
||||||
args: [`${actionPath}/mcp/server.ts`],
|
args: [`${actionPath}/mcp/server.ts`],
|
||||||
env: {
|
env: {
|
||||||
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
|
GITHUB_INSTALLATION_TOKEN: githubInstallationToken,
|
||||||
GITHUB_REPOSITORY: githubRepository,
|
GITHUB_REPOSITORY: githubRepository,
|
||||||
|
LOG_LEVEL: "debug",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Wrapper script to capture MCP server logs for debugging
|
|
||||||
LOG_FILE="/tmp/mcp-server.log"
|
|
||||||
|
|
||||||
echo "=== MCP Server Debug Wrapper Starting ===" >> "$LOG_FILE"
|
|
||||||
echo "Timestamp: $(date)" >> "$LOG_FILE"
|
|
||||||
echo "Working directory: $(pwd)" >> "$LOG_FILE"
|
|
||||||
echo "Environment variables:" >> "$LOG_FILE"
|
|
||||||
env | grep -E "(GITHUB_|MCP_|NODE_)" >> "$LOG_FILE"
|
|
||||||
echo "Node.js version: $(node --version)" >> "$LOG_FILE"
|
|
||||||
echo "Arguments: $@" >> "$LOG_FILE"
|
|
||||||
echo "=== Starting MCP Server ===" >> "$LOG_FILE"
|
|
||||||
|
|
||||||
# Execute the actual MCP server, capturing both stdout and stderr
|
|
||||||
exec node "$@" 2>&1 | tee -a "$LOG_FILE"
|
|
||||||
+14
-3
@@ -6,6 +6,7 @@ import { Octokit } from "@octokit/rest";
|
|||||||
import { type } from "arktype";
|
import { type } from "arktype";
|
||||||
import { z } from "zod";
|
import { z } from "zod";
|
||||||
import { resolveRepoContext } from "../utils/repo-context.ts";
|
import { resolveRepoContext } from "../utils/repo-context.ts";
|
||||||
|
import { writeFileSync } from "fs";
|
||||||
|
|
||||||
const server = new McpServer({
|
const server = new McpServer({
|
||||||
name: "Minimal GitHub Issue Comment Server",
|
name: "Minimal GitHub Issue Comment Server",
|
||||||
@@ -31,7 +32,9 @@ server.tool(
|
|||||||
|
|
||||||
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"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve repository context from environment
|
// Resolve repository context from environment
|
||||||
@@ -66,7 +69,8 @@ server.tool(
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
const errorMessage = error instanceof Error ? error.message : String(error);
|
const errorMessage =
|
||||||
|
error instanceof Error ? error.message : String(error);
|
||||||
return {
|
return {
|
||||||
content: [
|
content: [
|
||||||
{
|
{
|
||||||
@@ -89,4 +93,11 @@ async function runServer() {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
runServer().catch(console.error);
|
runServer().catch((error) => {
|
||||||
|
// Write error to file for GitHub Actions visibility
|
||||||
|
try {
|
||||||
|
writeFileSync('/tmp/mcp-error.log', `${new Date().toISOString()} MCP Server Error: ${error.message}\nStack: ${error.stack}\n`, { flag: 'a' });
|
||||||
|
} catch {}
|
||||||
|
console.error('MCP Server Error:', error);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@pullfrog/action",
|
"name": "@pullfrog/action",
|
||||||
"version": "0.0.19",
|
"version": "0.0.24",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"files": [
|
"files": [
|
||||||
"index.js",
|
"index.js",
|
||||||
|
|||||||
Reference in New Issue
Block a user