From 9459803aaa0b477f9c182b5c3d2529f043c51ab0 Mon Sep 17 00:00:00 2001 From: ssalbdivad Date: Thu, 9 Oct 2025 16:33:11 -0400 Subject: [PATCH] cleanup comments --- agents/claude.ts | 86 +--------------------------- entry.cjs | 8 +-- entry.ts | 5 -- main.ts | 6 -- play.ts | 19 ------ utils/act.ts | 13 ----- utils/generate-installation-token.ts | 1 - utils/github.ts | 7 --- utils/setup.ts | 2 - utils/subprocess.ts | 11 +--- utils/table.ts | 34 +---------- 11 files changed, 7 insertions(+), 185 deletions(-) diff --git a/agents/claude.ts b/agents/claude.ts index c8dacf9..2424666 100644 --- a/agents/claude.ts +++ b/agents/claude.ts @@ -16,14 +16,12 @@ export class ClaudeAgent implements Agent { startTime: 0, }; - // $: ExecaMethod; constructor(config: AgentConfig) { if (!config.apiKey) { throw new Error("Claude agent requires an API key"); } this.apiKey = config.apiKey; - // Removed execa dependency - using spawn utility instead } /** @@ -43,7 +41,6 @@ export class ClaudeAgent implements Agent { * Install Claude Code CLI */ async install(): Promise { - // Check if Claude Code is already installed if (await this.isClaudeInstalled()) { core.info("Claude Code is already installed, skipping installation"); return; @@ -51,16 +48,12 @@ export class ClaudeAgent implements Agent { core.info("Installing Claude Code..."); try { - // Use shell execution to properly handle the pipe const result = await spawn({ cmd: "bash", 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: () => { - // no logs - // process.stdout.write(chunk) - }, + onStdout: () => {}, onStderr: (chunk) => process.stderr.write(chunk), }); @@ -79,14 +72,10 @@ export class ClaudeAgent implements Agent { */ async execute(prompt: string): Promise { core.info("Running Claude Code..."); - // printTable([[prompt]]); try { - // Execute Claude Code with the prompt directly using proper headless mode - // core.info(`Executing Claude Code with prompt: ${prompt.substring(0, 100)}...`); const claudePath = `${process.env.HOME}/.local/bin/claude`; - // console.log("Using Claude Code from:", claudePath); console.log(boxString(prompt, { title: "Prompt" })); const args = [ "--print", @@ -97,7 +86,6 @@ export class ClaudeAgent implements Agent { "bypassPermissions", ]; - // Add MCP configuration if GitHub credentials are available if ( process.env.GITHUB_INSTALLATION_TOKEN && process.env.REPO_OWNER && @@ -116,10 +104,8 @@ export class ClaudeAgent implements Agent { ANTHROPIC_API_KEY: this.apiKey, }; - // Start a collapsible log group for streaming output core.startGroup("🔄 Run details"); - // Initialize run statistics this.runStats = { toolsUsed: 0, turns: 0, @@ -129,7 +115,6 @@ export class ClaudeAgent implements Agent { const finalResult = ""; const totalCost = 0; - // run Claude Code with the prompt const result = await spawn({ cmd: claudePath, args, @@ -137,37 +122,21 @@ export class ClaudeAgent implements Agent { input: prompt, timeout: 10 * 60 * 1000, // 10 minutes onStdout: (_chunk) => { - // console.log(chunk); processJSONChunk(_chunk, this); }, onStderr: (_chunk) => { if (_chunk.trim()) { - // core.warning(`[warn] ${chunk}`); processJSONChunk(_chunk, this); } }, }); - // throw on non-zero exit code if (result.exitCode !== 0) { throw new Error( `Command failed with exit code ${result.exitCode}\n\nStdout: ${result.stdout}\n\nStderr: ${result.stderr}` ); } - // Process the complete buffered stdout to extract final results - // if (result.stdout.trim()) { - // const lines = result.stdout.trim().split("\n"); - // for (const line of lines) { - // if (line.trim()) { - // const chunkResult = processJsonChunk(line); - // if (chunkResult.finalResult) finalResult = chunkResult.finalResult; - // if (chunkResult.totalCost) totalCost = chunkResult.totalCost; - // } - // } - // } - - // Log run summary const duration = Date.now() - this.runStats.startTime; core.info( `📊 Run Summary: ${this.runStats.toolsUsed} tools used, ${this.runStats.turns} turns, ${duration}ms duration` @@ -187,11 +156,9 @@ export class ClaudeAgent implements Agent { }, }; } catch (error: any) { - // Ensure group is closed even if error occurs before group is started try { core.endGroup(); } catch { - // Group might not have been started, ignore } const errorMessage = error instanceof Error ? error.message : "Unknown error"; return { @@ -202,34 +169,11 @@ export class ClaudeAgent implements Agent { } } -/** - * Process a JSON chunk line and extract result data - */ -// function processJsonChunk(line: string): { finalResult?: string; totalCost?: number } { -// try { -// const chunk = JSON.parse(line.trim()); -// processJSONChunk(chunk); - -// // Collect final result and cost data -// if (chunk.type === "result" && chunk.result) { -// return { -// finalResult: chunk.result, -// totalCost: chunk.total_cost_usd || 0, -// }; -// } -// return {}; -// } catch { -// core.debug(`Failed to parse JSON line: ${line}`); -// return {}; -// } -// } - /** * Pretty print a JSON chunk based on its type */ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { try { - // Parse the JSON string first console.log(chunk); const parsedChunk = JSON.parse(chunk.trim()); @@ -237,8 +181,6 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { case "system": if (parsedChunk.subtype === "init") { core.info(`🚀 Starting Claude Code session...`); - // core.info(`📁 Working directory: ${parsedChunk.cwd}`); - // core.info(`🔑 Permission mode: ${parsedChunk.permissionMode}`); core.info( tableString([ ["model", parsedChunk.model], @@ -264,39 +206,31 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { case "assistant": if (parsedChunk.message?.content) { - // Track turns if (agent) { agent.runStats.turns++; } for (const content of parsedChunk.message.content) { if (content.type === "text") { - // Skip empty text content if (content.text.trim()) { core.info(boxString(content.text.trim(), { title: "Claude Code" })); } } else if (content.type === "tool_use") { - // Track tools used if (agent) { agent.runStats.toolsUsed++; } - // Enhanced tool usage logging const toolName = content.name; - // const toolId = content.id; core.info(`→ ${toolName}`); - // Log tool-specific details based on tool type if (content.input) { const input = content.input; - // Common tool input fields if (input.description) { core.info(` └─ ${input.description}`); } - // Tool-specific input fields if (input.command) { core.info(` └─ command: ${input.command}`); } @@ -325,7 +259,6 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { core.info(` └─ url: ${input.url}`); } - // For multi-edit or complex operations if (input.edits && Array.isArray(input.edits)) { core.info(` └─ edits: ${input.edits.length} changes`); input.edits.forEach((edit: any, index: number) => { @@ -335,19 +268,15 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { }); } - // For task operations if (input.task) { core.info(` └─ task: ${input.task}`); } - // For bash operations with specific details if (input.bash_command) { core.info(` └─ bash_command: ${input.bash_command}`); } } - // Log tool ID for debugging - // core.debug(` 🔗 Tool ID: ${toolId}`); } } } @@ -360,9 +289,6 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { if (content.is_error) { core.warning(`❌ Tool error: ${content.content}`); } else { - // Enhanced tool result logging - const _resultContent = content.content.trim(); - // do nothing for now. usually useless in headless more. } } } @@ -371,15 +297,6 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { case "result": if (parsedChunk.subtype === "success") { - // Claude already prints something almost identical to this, so skip for now - // if (parsedChunk.result) { - // core.info( - // boxString(parsedChunk.result.trim(), { - // title: "🤖 Claude Code", - // maxWidth: 70, - // }), - // ); - // } core.info( tableString([ @@ -396,7 +313,6 @@ function processJSONChunk(chunk: string, agent?: ClaudeAgent): void { break; default: - // Log unknown chunk types for debugging core.debug(`📦 Unknown chunk type: ${parsedChunk.type}`); break; } diff --git a/entry.cjs b/entry.cjs index 3b184df..4e363d2 100755 --- a/entry.cjs +++ b/entry.cjs @@ -25580,7 +25580,7 @@ async function spawn(options) { durationMs }); }); - child.on("error", (error2) => { + child.on("error", (_error) => { const durationMs = Date.now() - startTime; if (timeoutId) { clearTimeout(timeoutId); @@ -25609,8 +25609,8 @@ function tableString(rows, options) { return lineIndex === 0 || options?.title && lineIndex === 1 || lineIndex === rowCount; } } = options || {}; - if (options?.title) { - rows.unshift([options.title]); + if (title) { + rows.unshift([title]); } const tableOutput = (0, import_table.table)(rows, { drawHorizontalLine, @@ -25698,7 +25698,6 @@ var ClaudeAgent = class { turns: 0, startTime: 0 }; - // $: ExecaMethod; constructor(config) { if (!config.apiKey) { throw new Error("Claude agent requires an API key"); @@ -25927,7 +25926,6 @@ function processJSONChunk(chunk, agent) { if (content.is_error) { core.warning(`\u274C Tool error: ${content.content}`); } else { - const _resultContent = content.content.trim(); } } } diff --git a/entry.ts b/entry.ts index 9e62921..69546f7 100644 --- a/entry.ts +++ b/entry.ts @@ -11,7 +11,6 @@ import { setupGitHubInstallationToken } from "./utils/github.ts"; async function run(): Promise { try { - // Get inputs from GitHub Actions const prompt = core.getInput("prompt", { required: true }); const anthropic_api_key = core.getInput("anthropic_api_key"); @@ -19,13 +18,11 @@ async function run(): Promise { throw new Error("prompt is required"); } - // Create params object with new structure const inputs: ExecutionInputs = { prompt, anthropic_api_key, }; - // Add optional properties only if they exist const githubToken = core.getInput("github_token") || process.env.GITHUB_TOKEN; if (githubToken) { inputs.github_token = githubToken; @@ -47,7 +44,6 @@ async function run(): Promise { const result = await main(params); - // TODO: Set outputs if (!result.success) { throw new Error(result.error || "Agent execution failed"); @@ -58,7 +54,6 @@ async function run(): Promise { } } -// Run the action run().catch((error) => { console.error("Action failed:", error); process.exit(1); diff --git a/main.ts b/main.ts index e9650f3..ac79402 100644 --- a/main.ts +++ b/main.ts @@ -1,7 +1,6 @@ import * as core from "@actions/core"; import { ClaudeAgent } from "./agents/claude.ts"; -// Expected environment variables that should be passed as inputs export const EXPECTED_INPUTS: string[] = [ "ANTHROPIC_API_KEY", "GITHUB_TOKEN", @@ -29,24 +28,19 @@ export interface MainResult { export async function main(params: MainParams): Promise { try { - // Extract inputs from params const { inputs, env, cwd } = params; - // Set working directory if different from current if (cwd !== process.cwd()) { process.chdir(cwd); } - // Set environment variables Object.assign(process.env, env); core.info(`→ Starting agent run with Claude Code`); - // Create and install the Claude agent const agent = new ClaudeAgent({ apiKey: inputs.anthropic_api_key }); await agent.install(); - // Execute the agent with the prompt const result = await agent.execute(inputs.prompt); if (!result.success) { diff --git a/play.ts b/play.ts index c62ed30..e519937 100644 --- a/play.ts +++ b/play.ts @@ -8,7 +8,6 @@ import { runAct } from "./utils/act.ts"; import { generateInstallationToken } from "./utils/generate-installation-token.ts"; import { setupTestRepo } from "./utils/setup.ts"; -// Load environment variables from .env file config(); const __filename = fileURLToPath(import.meta.url); @@ -20,17 +19,14 @@ export async function run( ): Promise<{ success: boolean; output?: string | undefined; error?: string | undefined }> { try { if (options.act) { - // Use Docker/act to run the action console.log("🐳 Running with Docker/act..."); runAct(prompt); return { success: true }; } - // Setup test repository and run directly const tempDir = join(process.cwd(), ".temp"); setupTestRepo({ tempDir, forceClean: true }); - // Change to the temp directory const originalCwd = process.cwd(); process.chdir(tempDir); @@ -40,7 +36,6 @@ export async function run( console.log(prompt); console.log("─".repeat(50)); - // Set environment variables from our .env for the action to use const { EXPECTED_INPUTS } = await import("./main.ts"); EXPECTED_INPUTS.forEach((inputName) => { const value = process.env[inputName]; @@ -49,13 +44,11 @@ export async function run( } }); - // Run main with the new params structure const inputs: any = { prompt, anthropic_api_key: process.env.ANTHROPIC_API_KEY || "", }; - // Add optional properties only if they exist if (process.env.GITHUB_TOKEN) { inputs.github_token = process.env.GITHUB_TOKEN; } @@ -65,7 +58,6 @@ export async function run( inputs.github_installation_token = installationToken; console.log("✅ GitHub installation token generated successfully"); - // Set installation token in environment for Claude agent MCP configuration const envWithToken = { ...process.env, GITHUB_INSTALLATION_TOKEN: installationToken, @@ -77,7 +69,6 @@ export async function run( cwd: process.cwd(), }); - // Change back to original directory process.chdir(originalCwd); if (result.success) { @@ -97,7 +88,6 @@ export async function run( } } -// CLI execution when run directly if (import.meta.url === `file://${process.argv[1]}`) { const args = arg({ "--help": Boolean, @@ -133,15 +123,12 @@ Examples: let prompt: string; if (args["--raw"]) { - // Use raw prompt string prompt = args["--raw"]; } else { - // Load prompt from file const filePath = args._[0] || "fixtures/basic.txt"; const ext = extname(filePath).toLowerCase(); let resolvedPath: string; - // First try as fixtures path const fixturesPath = join(__dirname, "fixtures", filePath); if (existsSync(fixturesPath)) { resolvedPath = fixturesPath; @@ -153,13 +140,11 @@ Examples: switch (ext) { case ".txt": { - // Plain text - pass directly as prompt prompt = readFileSync(resolvedPath, "utf8").trim(); break; } case ".json": { - // JSON - stringify and pass as prompt const content = readFileSync(resolvedPath, "utf8"); const parsed = JSON.parse(content); prompt = JSON.stringify(parsed, null, 2); @@ -167,7 +152,6 @@ Examples: } case ".ts": { - // TypeScript - dynamic import and stringify default export const fileUrl = pathToFileURL(resolvedPath).href; const module = await import(fileUrl); @@ -175,14 +159,11 @@ Examples: throw new Error(`TypeScript file ${filePath} must have a default export`); } - // If it's a string, use it directly if (typeof module.default === "string") { prompt = module.default; } else if (typeof module.default === "object" && module.default.prompt) { - // If it's a MainParams object with a prompt field, extract the prompt prompt = module.default.prompt; } else { - // Otherwise stringify it prompt = JSON.stringify(module.default, null, 2); } break; diff --git a/utils/act.ts b/utils/act.ts index de8e479..4a8b2b6 100644 --- a/utils/act.ts +++ b/utils/act.ts @@ -12,27 +12,21 @@ const tempDir = join(__dirname, "..", ".temp"); const actionPath = join(__dirname, ".."); const envPath = join(__dirname, "..", "..", ".env"); -// Environment variables that should be passed as secrets to the workflow const ENV_VARS = ["ANTHROPIC_API_KEY", "GITHUB_INSTALLATION_TOKEN"]; export function runAct(prompt: string): void { - // Setup test repository setupTestRepo({ tempDir }); - // Load environment variables config({ path: envPath }); - // Build action bundles buildAction(actionPath); const workflowPath = join(tempDir, ".github", "workflows", "pullfrog.yml"); - // Create minimal dist for act (avoids pnpm symlink issues) const distPath = join(actionPath, ".act-dist"); console.log("📦 Creating minimal distribution for act..."); execSync(`rm -rf "${distPath}" && mkdir -p "${distPath}"`, { shell: "/bin/bash" }); - // Copy only necessary files (bundled, no node_modules needed) ["action.yml", "entry.cjs", "index.cjs", "package.json"].forEach((file) => { const src = join(actionPath, file); if (existsSync(src)) { @@ -41,8 +35,6 @@ export function runAct(prompt: string): void { }); try { - // Build the act command with input directly - // Properly escape the prompt for shell const escapedPrompt = prompt.replace(/'/g, "'\\''"); const actCommandParts = [ @@ -56,14 +48,12 @@ export function runAct(prompt: string): void { `pullfrog/action@v0=${distPath}`, // Use minimal dist without symlinks ]; - // Add environment variables as secrets that will be available to the workflow ENV_VARS.forEach((key) => { if (process.env[key]) { actCommandParts.push("-s", key); } }); - // We only need the specific ENV_VARS, no need to add other variables const actCommand = actCommandParts.join(" "); @@ -73,15 +63,12 @@ export function runAct(prompt: string): void { console.log("─".repeat(50)); console.log(""); - // Execute act execSync(actCommand, { stdio: "inherit", cwd: join(__dirname, "..", ".."), }); - // Clean up execSync(`rm -rf "${distPath}"`); } catch (error) { - // Clean up on error execSync(`rm -rf "${distPath}"`); console.error("❌ Act execution failed:", (error as Error).message); process.exit(1); diff --git a/utils/generate-installation-token.ts b/utils/generate-installation-token.ts index b6f84c7..7c30bd3 100644 --- a/utils/generate-installation-token.ts +++ b/utils/generate-installation-token.ts @@ -165,7 +165,6 @@ const findInstallationId = async (jwt: string, repoOwner: string, repoName: stri return installation.id; } } catch { - // Installation doesn't have access to repository } } diff --git a/utils/github.ts b/utils/github.ts index 743302a..59ea9a6 100644 --- a/utils/github.ts +++ b/utils/github.ts @@ -14,13 +14,11 @@ export interface InstallationToken { * Setup GitHub installation token for the action */ export async function setupGitHubInstallationToken(): Promise { - // Check if we have an installation token from inputs or environment const inputToken = core.getInput("github_installation_token"); const envToken = process.env.GITHUB_INSTALLATION_TOKEN; const existingToken = inputToken || envToken; if (existingToken) { - // Mask the existing token in logs for security core.setSecret(existingToken); core.info("Using provided GitHub installation token"); return existingToken; @@ -29,11 +27,9 @@ export async function setupGitHubInstallationToken(): Promise { core.info("Generating OIDC token..."); try { - // Generate OIDC token for our API const oidcToken = await core.getIDToken("pullfrog-api"); core.info("OIDC token generated successfully"); - // Exchange OIDC token for installation token const apiUrl = process.env.API_URL || "https://pullfrog.ai"; core.info("Exchanging OIDC token for installation token..."); @@ -52,14 +48,11 @@ export async function setupGitHubInstallationToken(): Promise { ); } - // This type is enforced by us when the response is created const tokenData = (await tokenResponse.json()) as InstallationToken; core.info(`Installation token obtained for ${tokenData.repository || "all repositories"}`); - // Mask the token in logs for security core.setSecret(tokenData.token); - // Set the token as an environment variable for this run process.env.GITHUB_INSTALLATION_TOKEN = tokenData.token; return tokenData.token; diff --git a/utils/setup.ts b/utils/setup.ts index 5f142ac..4510c9c 100644 --- a/utils/setup.ts +++ b/utils/setup.ts @@ -17,13 +17,11 @@ export function setupTestRepo(options: SetupOptions): void { forceClean = false, } = options; - // Handle existing temp directory if (existsSync(tempDir)) { if (forceClean) { console.log("🗑️ Removing existing .temp directory..."); rmSync(tempDir, { recursive: true, force: true }); - // Clone the repository console.log("📦 Cloning pullfrogai/scratch into .temp..."); execSync(`git clone ${repoUrl} ${tempDir}`, { stdio: "inherit" }); } else { diff --git a/utils/subprocess.ts b/utils/subprocess.ts index ff87e88..4432b3f 100644 --- a/utils/subprocess.ts +++ b/utils/subprocess.ts @@ -28,13 +28,11 @@ export async function spawn(options: SpawnOptions): Promise { let stderrBuffer = ""; return new Promise((resolve, reject) => { - // Spawn the child process const child = nodeSpawn(cmd, args, { env: env ? { ...process.env, ...env } : process.env, stdio: ["pipe", "pipe", "pipe"], }); - // Set up timeout if specified let timeoutId: NodeJS.Timeout | undefined; let isTimedOut = false; @@ -43,7 +41,6 @@ export async function spawn(options: SpawnOptions): Promise { isTimedOut = true; child.kill("SIGTERM"); - // If SIGTERM doesn't work, use SIGKILL after 5 seconds setTimeout(() => { if (!child.killed) { child.kill("SIGKILL"); @@ -52,7 +49,6 @@ export async function spawn(options: SpawnOptions): Promise { }, timeout); } - // Handle stdout streaming if (child.stdout) { child.stdout.on("data", (data: Buffer) => { const chunk = data.toString(); @@ -61,7 +57,6 @@ export async function spawn(options: SpawnOptions): Promise { }); } - // Handle stderr streaming if (child.stderr) { child.stderr.on("data", (data: Buffer) => { const chunk = data.toString(); @@ -70,7 +65,6 @@ export async function spawn(options: SpawnOptions): Promise { }); } - // Handle process completion child.on("close", (exitCode) => { const durationMs = Date.now() - startTime; @@ -91,15 +85,13 @@ export async function spawn(options: SpawnOptions): Promise { }); }); - // Handle process errors - child.on("error", (error) => { + child.on("error", (_error) => { const durationMs = Date.now() - startTime; if (timeoutId) { clearTimeout(timeoutId); } - // Still return buffered output even on error resolve({ stdout: stdoutBuffer, stderr: stderrBuffer, @@ -108,7 +100,6 @@ export async function spawn(options: SpawnOptions): Promise { }); }); - // Send input if provided if (input && child.stdin) { child.stdin.write(input); child.stdin.end(); diff --git a/utils/table.ts b/utils/table.ts index 209e9e1..af28642 100644 --- a/utils/table.ts +++ b/utils/table.ts @@ -22,8 +22,8 @@ export function tableString( }, } = options || {}; - if (options?.title) { - rows.unshift([options.title]); + if (title) { + rows.unshift([title]); } const tableOutput = table(rows, { @@ -140,33 +140,3 @@ export function boxString( return result; } - -// /** -// * Create a simple two-column table for displaying key-value pairs -// * @param data - Array of [key, value] pairs -// * @param title - Optional table title -// * @param indent - Optional indentation string -// */ -// export function printKeyValueTable( -// data: [string, string][], -// title?: string, -// indent?: string -// ): void { -// const rows: string[][] = [["Key", "Value"], ...data]; -// const options: Parameters[1] = {}; -// if (title !== undefined) options.title = title; -// if (indent !== undefined) options.indent = indent; -// printTable(rows, options); -// } - -// /** -// * Create a path resolution table (specific use case) -// * @param pathData - Array of [location, resolvedPath] pairs -// * @param indent - Optional indentation string -// */ -// export function printPathTable(pathData: [string, string][], indent?: string): void { -// const rows: string[][] = [["Location", "Resolved path"], ...pathData]; -// const options: Parameters[1] = {}; -// if (indent !== undefined) options.indent = indent; -// printTable(rows, options); -// }