GetExecutionSteps returns the GitHub Actions steps for executing Claude
(workflowData *WorkflowData, logFile string)
| 154 | |
| 155 | // GetExecutionSteps returns the GitHub Actions steps for executing Claude |
| 156 | func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile string) []GitHubActionStep { |
| 157 | claudeLog.Printf("Generating execution steps for Claude engine: workflow=%s, firewall=%v", workflowData.Name, isFirewallEnabled(workflowData)) |
| 158 | |
| 159 | var steps []GitHubActionStep |
| 160 | |
| 161 | // Build claude CLI arguments based on configuration |
| 162 | var claudeArgs []string |
| 163 | toolsWithMountedCLIs := withMountedCLIShellCommandsInRestrictedBash(workflowData) |
| 164 | |
| 165 | // Add print flag for non-interactive mode |
| 166 | claudeArgs = append(claudeArgs, "--print") |
| 167 | |
| 168 | // Disable Chrome integration for security and deterministic execution |
| 169 | claudeArgs = append(claudeArgs, "--no-chrome") |
| 170 | |
| 171 | // Model is always passed via the native ANTHROPIC_MODEL environment variable when configured. |
| 172 | // This avoids embedding the value directly in the shell command (which fails template injection |
| 173 | // validation for GitHub Actions expressions like ${{ inputs.model }}). |
| 174 | // Fallback for unconfigured model uses GH_AW_MODEL_AGENT_CLAUDE with shell expansion. |
| 175 | modelConfigured := workflowData.EngineConfig != nil && workflowData.EngineConfig.Model != "" |
| 176 | |
| 177 | // Add max_turns if specified (in CLI it's max-turns) |
| 178 | if workflowData.EngineConfig != nil && workflowData.EngineConfig.MaxTurns != "" { |
| 179 | claudeLog.Printf("Setting max turns: %s", workflowData.EngineConfig.MaxTurns) |
| 180 | claudeArgs = append(claudeArgs, "--max-turns", workflowData.EngineConfig.MaxTurns) |
| 181 | } |
| 182 | |
| 183 | // Add MCP configuration only if there are MCP servers. |
| 184 | // Keep this argument outside shellJoinArgs so ${RUNNER_TEMP} expands at runtime. |
| 185 | mcpConfigArg := "" |
| 186 | if HasMCPServers(workflowData) { |
| 187 | claudeLog.Print("Adding MCP configuration") |
| 188 | mcpConfigArg = ` --mcp-config "${RUNNER_TEMP}/gh-aw/mcp-config/mcp-servers.json"` |
| 189 | } |
| 190 | |
| 191 | // Add allowed tools configuration |
| 192 | // Note: Claude Code CLI v2.0.31 introduced a simpler --tools flag, but we continue to use |
| 193 | // --allowed-tools because it provides fine-grained control needed by gh-aw: |
| 194 | // - Specific bash commands: Bash(git:*), Bash(ls) |
| 195 | // - MCP tool prefixes: mcp__github__issue_read |
| 196 | // - Path-specific tools: Read(/tmp/gh-aw/cache-memory/*) |
| 197 | // The --tools flag only supports basic tool names (e.g., "Bash,Edit,Read") without patterns. |
| 198 | allowedTools := e.computeAllowedClaudeToolsString(toolsWithMountedCLIs, workflowData.SafeOutputs, workflowData.CacheMemoryConfig, workflowData.MCPScripts, workflowData.SandboxConfig) |
| 199 | if allowedTools != "" { |
| 200 | claudeArgs = append(claudeArgs, "--allowed-tools", allowedTools) |
| 201 | } |
| 202 | |
| 203 | // Add debug-file flag to write debug logs directly to file |
| 204 | // This implicitly enables debug mode and provides cleaner, more reliable log capture |
| 205 | // than shell redirection with 2>&1 | tee |
| 206 | claudeArgs = append(claudeArgs, "--debug-file", logFile) |
| 207 | |
| 208 | // Always add verbose flag for enhanced debugging output |
| 209 | claudeArgs = append(claudeArgs, "--verbose") |
| 210 | |
| 211 | // Add permission mode for non-interactive execution. |
| 212 | // |
| 213 | // Default to "acceptEdits" so Claude Code honours --allowed-tools as the effective |