import { parse as parseShellArgs } from "shell-quote"; import type { ClaudeOptions } from "./run-claude"; import type { Options as SdkOptions } from "@anthropic-ai/claude-agent-sdk"; /** * Result of parsing ClaudeOptions for SDK usage */ export type ParsedSdkOptions = { sdkOptions: SdkOptions; showFullOutput: boolean; hasJsonSchema: boolean; }; // Flags that should accumulate multiple values instead of overwriting const ACCUMULATING_FLAGS = new Set(["allowedTools", "disallowedTools"]); /** * Parse claudeArgs string into extraArgs record for SDK pass-through * The SDK/CLI will handle --mcp-config, --json-schema, etc. * For allowedTools and disallowedTools, multiple occurrences are accumulated (comma-joined). */ function parseClaudeArgsToExtraArgs( claudeArgs?: string, ): Record { if (!claudeArgs?.trim()) return {}; const result: Record = {}; const args = parseShellArgs(claudeArgs).filter( (arg): arg is string => typeof arg === "string", ); for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg?.startsWith("--")) { const flag = arg.slice(2); const nextArg = args[i + 1]; // Check if next arg is a value (not another flag) if (nextArg && !nextArg.startsWith("--")) { // For accumulating flags, join multiple values with commas if (ACCUMULATING_FLAGS.has(flag) && result[flag]) { result[flag] = `${result[flag]},${nextArg}`; } else { result[flag] = nextArg; } i++; // Skip the value } else { result[flag] = null; // Boolean flag } } } return result; } /** * Parse ClaudeOptions into SDK-compatible options * Uses extraArgs for CLI pass-through instead of duplicating option parsing */ export function parseSdkOptions(options: ClaudeOptions): ParsedSdkOptions { // Determine output verbosity const isDebugMode = process.env.ACTIONS_STEP_DEBUG === "true"; const showFullOutput = options.showFullOutput === "true" || isDebugMode; // Parse claudeArgs into extraArgs for CLI pass-through const extraArgs = parseClaudeArgsToExtraArgs(options.claudeArgs); // Detect if --json-schema is present (for hasJsonSchema flag) const hasJsonSchema = "json-schema" in extraArgs; // Extract and merge allowedTools from both sources: // 1. From extraArgs (parsed from claudeArgs - contains tag mode's tools) // 2. From options.allowedTools (direct input - may be undefined) // This prevents duplicate flags being overwritten when claudeArgs contains --allowedTools const extraArgsAllowedTools = extraArgs["allowedTools"] ? extraArgs["allowedTools"].split(",").map((t) => t.trim()) : []; const directAllowedTools = options.allowedTools ? options.allowedTools.split(",").map((t) => t.trim()) : []; const mergedAllowedTools = [ ...new Set([...extraArgsAllowedTools, ...directAllowedTools]), ]; delete extraArgs["allowedTools"]; // Same for disallowedTools const extraArgsDisallowedTools = extraArgs["disallowedTools"] ? extraArgs["disallowedTools"].split(",").map((t) => t.trim()) : []; const directDisallowedTools = options.disallowedTools ? options.disallowedTools.split(",").map((t) => t.trim()) : []; const mergedDisallowedTools = [ ...new Set([...extraArgsDisallowedTools, ...directDisallowedTools]), ]; delete extraArgs["disallowedTools"]; // Build custom environment const env: Record = { ...process.env }; if (process.env.INPUT_ACTION_INPUTS_PRESENT) { env.GITHUB_ACTION_INPUTS = process.env.INPUT_ACTION_INPUTS_PRESENT; } // Build system prompt option - default to claude_code preset let systemPrompt: SdkOptions["systemPrompt"]; if (options.systemPrompt) { systemPrompt = options.systemPrompt; } else if (options.appendSystemPrompt) { systemPrompt = { type: "preset", preset: "claude_code", append: options.appendSystemPrompt, }; } else { // Default to claude_code preset when no custom prompt is specified systemPrompt = { type: "preset", preset: "claude_code", }; } // Build SDK options - use merged tools from both direct options and claudeArgs const sdkOptions: SdkOptions = { // Direct options from ClaudeOptions inputs model: options.model, maxTurns: options.maxTurns ? parseInt(options.maxTurns, 10) : undefined, allowedTools: mergedAllowedTools.length > 0 ? mergedAllowedTools : undefined, disallowedTools: mergedDisallowedTools.length > 0 ? mergedDisallowedTools : undefined, systemPrompt, fallbackModel: options.fallbackModel, pathToClaudeCodeExecutable: options.pathToClaudeCodeExecutable, // Pass through claudeArgs as extraArgs - CLI handles --mcp-config, --json-schema, etc. // Note: allowedTools and disallowedTools have been removed from extraArgs to prevent duplicates extraArgs, env, // Load settings from all sources to pick up CLI-installed plugins, CLAUDE.md, etc. settingSources: ["user", "project", "local"], }; return { sdkOptions, showFullOutput, hasJsonSchema, }; }