Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/agents/setup-agentic-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,21 @@ gh secret set COPILOT_CLI_TOKEN -a actions --body "your-github-pat-here"

Say to the user:
````
You'll need an Anthropic API key.
You'll need an Anthropic API key or Claude Code OAuth token.

**Steps:**
1. Sign up for Anthropic API access at [console.anthropic.com](https://console.anthropic.com/)
2. Generate an API key from your account settings

**Documentation:** [Anthropic Claude Code Engine](https://githubnext.github.io/gh-aw/reference/engines/#anthropic-claude-code)

**Set the secret** in a separate terminal window:
**Set the secret** in a separate terminal window (choose one):

```bash
# Option 1: Using CLAUDE_CODE_OAUTH_TOKEN
gh secret set CLAUDE_CODE_OAUTH_TOKEN -a actions --body "your-claude-oauth-token-here"

# Option 2: Using ANTHROPIC_API_KEY
gh secret set ANTHROPIC_API_KEY -a actions --body "your-anthropic-api-key-here"
```
````
Expand Down
1,502 changes: 1,502 additions & 0 deletions .github/workflows/test-claude-oauth-workflow.lock.yml

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions .github/workflows/test-claude-oauth-workflow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
name: test-claude-oauth
description: Test workflow to validate CLAUDE_CODE_OAUTH_TOKEN support
on:
issues:
types: [opened]
engine: claude
---

Test the Claude OAuth token support by listing files.
10 changes: 8 additions & 2 deletions docs/src/content/docs/reference/engines.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,12 +159,18 @@ engine:

#### Required Secrets

- **`ANTHROPIC_API_KEY`**: Anthropic API key
- **`CLAUDE_CODE_OAUTH_TOKEN`** or **`ANTHROPIC_API_KEY`**: Authentication token for Claude Code. Both secrets are passed to the CLI if configured, and the CLI determines which to use (with `CLAUDE_CODE_OAUTH_TOKEN` taking precedence)
- **`GH_AW_GITHUB_TOKEN`** (optional): Required for [GitHub Tools Remote Mode](/gh-aw/reference/tools/#github-remote-mode)

Set secrets using:
Set secrets using (choose one):
```bash
# Option 1: Using CLAUDE_CODE_OAUTH_TOKEN
gh secret set CLAUDE_CODE_OAUTH_TOKEN -a actions --body "<your-claude-oauth-token>"

# Option 2: Using ANTHROPIC_API_KEY
gh secret set ANTHROPIC_API_KEY -a actions --body "<your-anthropic-api-key>"

# GitHub token (optional)
gh secret set GH_AW_GITHUB_TOKEN -a actions --body "<your-github-pat>"
```

Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/tools/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -323,7 +323,7 @@ gh aw trial ./workflow.md --use-local-secrets # Use local API keys for trial
```

**How it works:**
- Reads API keys from environment variables (`ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `COPILOT_CLI_TOKEN`, etc.)
- Reads API keys from environment variables (`CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `COPILOT_CLI_TOKEN`, etc.)
- Temporarily pushes the required secrets to the repository before workflow execution
- Automatically cleans up (deletes) the secrets after completion
- Only pushes secrets that are actually needed by the workflow's AI engine
Expand Down
8 changes: 6 additions & 2 deletions pkg/cli/templates/setup-agentic-workflows.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,17 +51,21 @@ gh secret set COPILOT_CLI_TOKEN -a actions --body "your-github-pat-here"

Say to the user:
````
You'll need an Anthropic API key.
You'll need an Anthropic API key or Claude Code OAuth token.

**Steps:**
1. Sign up for Anthropic API access at [console.anthropic.com](https://console.anthropic.com/)
2. Generate an API key from your account settings

**Documentation:** [Anthropic Claude Code Engine](https://githubnext.github.io/gh-aw/reference/engines/#anthropic-claude-code)

**Set the secret** in a separate terminal window:
**Set the secret** in a separate terminal window (choose one):

```bash
# Option 1: Using CLAUDE_CODE_OAUTH_TOKEN
gh secret set CLAUDE_CODE_OAUTH_TOKEN -a actions --body "your-claude-oauth-token-here"

# Option 2: Using ANTHROPIC_API_KEY
gh secret set ANTHROPIC_API_KEY -a actions --body "your-anthropic-api-key-here"
```
````
Expand Down
38 changes: 35 additions & 3 deletions pkg/cli/trial_command.go
Original file line number Diff line number Diff line change
Expand Up @@ -976,10 +976,38 @@ func determineAndAddEngineSecret(engineConfig *workflow.EngineConfig, hostRepoSl
// Set the appropriate secret based on engine type
switch engineType {
case "claude":
if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Setting ANTHROPIC_API_KEY secret for Claude engine"))
// Claude supports both CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY
// Try to set both if available, fail only if neither is set
var hasSecret bool

// Try CLAUDE_CODE_OAUTH_TOKEN first
if os.Getenv("CLAUDE_CODE_OAUTH_TOKEN") != "" {
if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Setting CLAUDE_CODE_OAUTH_TOKEN secret for Claude engine"))
}
if err := addEngineSecret("CLAUDE_CODE_OAUTH_TOKEN", hostRepoSlug, tracker, verbose); err == nil {
hasSecret = true
} else if verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Failed to set CLAUDE_CODE_OAUTH_TOKEN: "+err.Error()))
}
}
return addEngineSecret("ANTHROPIC_API_KEY", hostRepoSlug, tracker, verbose)

// Try ANTHROPIC_API_KEY
if os.Getenv("ANTHROPIC_API_KEY") != "" || os.Getenv("ANTHROPIC_KEY") != "" {
if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Setting ANTHROPIC_API_KEY secret for Claude engine"))
}
if err := addEngineSecret("ANTHROPIC_API_KEY", hostRepoSlug, tracker, verbose); err == nil {
hasSecret = true
} else if verbose {
fmt.Fprintln(os.Stderr, console.FormatWarningMessage("Failed to set ANTHROPIC_API_KEY: "+err.Error()))
}
}

if !hasSecret {
return fmt.Errorf("neither CLAUDE_CODE_OAUTH_TOKEN nor ANTHROPIC_API_KEY environment variable is set")
}
return nil
case "codex", "openai":
if verbose {
fmt.Fprintln(os.Stderr, console.FormatInfoMessage("Setting OPENAI_API_KEY secret for OpenAI engine"))
Expand Down Expand Up @@ -1019,7 +1047,11 @@ func addEngineSecret(secretName, hostRepoSlug string, tracker *TrialSecretTracke
// Try common alternative environment variable names
switch secretName {
case "ANTHROPIC_API_KEY":
// Try alternative name ANTHROPIC_KEY
secretValue = os.Getenv("ANTHROPIC_KEY")
case "CLAUDE_CODE_OAUTH_TOKEN":
// No alternative names for CLAUDE_CODE_OAUTH_TOKEN
// Already checked by os.Getenv(secretName) above
case "OPENAI_API_KEY":
secretValue = os.Getenv("OPENAI_KEY")
case "COPILOT_CLI_TOKEN":
Expand Down
9 changes: 5 additions & 4 deletions pkg/workflow/claude_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ func (e *ClaudeEngine) GetInstallationSteps(workflowData *WorkflowData) []GitHub

var steps []GitHubActionStep

// Add secret validation step
secretValidation := GenerateSecretValidationStep(
"ANTHROPIC_API_KEY",
// Add secret validation step - Claude supports both CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY as fallback
secretValidation := GenerateMultiSecretValidationStep(
[]string{"CLAUDE_CODE_OAUTH_TOKEN", "ANTHROPIC_API_KEY"},
"Claude Code",
"https://githubnext.github.io/gh-aw/reference/engines/#anthropic-claude-code",
)
Expand Down Expand Up @@ -207,8 +207,9 @@ func (e *ClaudeEngine) GetExecutionSteps(workflowData *WorkflowData, logFile str
// Add environment section - always include environment section for GH_AW_PROMPT
stepLines = append(stepLines, " env:")

// Add Anthropic API key
// Add both API keys - Claude Code CLI handles them separately and determines precedence
stepLines = append(stepLines, " ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}")
stepLines = append(stepLines, " CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}")

// Disable telemetry, error reporting, and bug command for privacy and security
stepLines = append(stepLines, " DISABLE_TELEMETRY: \"1\"")
Expand Down
13 changes: 10 additions & 3 deletions pkg/workflow/claude_engine_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ func TestClaudeEngine(t *testing.T) {
t.Errorf("Expected 3 installation steps for Claude (secret validation + Node.js setup + install), got %d", len(installSteps))
}

// Check for secret validation step
// Check for secret validation step (now supports both CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY)
secretValidationStep := strings.Join([]string(installSteps[0]), "\n")
if !strings.Contains(secretValidationStep, "Validate ANTHROPIC_API_KEY secret") {
t.Errorf("Expected 'Validate ANTHROPIC_API_KEY secret' in first installation step, got: %s", secretValidationStep)
if !strings.Contains(secretValidationStep, "Validate CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY secret") {
t.Errorf("Expected 'Validate CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY secret' in first installation step, got: %s", secretValidationStep)
}
if !strings.Contains(secretValidationStep, "CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}") {
t.Errorf("Expected 'CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}' in secret validation step, got: %s", secretValidationStep)
}
if !strings.Contains(secretValidationStep, "ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}") {
t.Errorf("Expected 'ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}' in secret validation step, got: %s", secretValidationStep)
Expand Down Expand Up @@ -121,6 +124,10 @@ func TestClaudeEngine(t *testing.T) {
t.Errorf("Expected ANTHROPIC_API_KEY environment variable in step: %s", stepContent)
}

if !strings.Contains(stepContent, "CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}") {
t.Errorf("Expected CLAUDE_CODE_OAUTH_TOKEN environment variable in step: %s", stepContent)
}

if !strings.Contains(stepContent, "GH_AW_PROMPT: /tmp/gh-aw/aw-prompts/prompt.txt") {
t.Errorf("Expected GH_AW_PROMPT environment variable in step: %s", stepContent)
}
Expand Down
9 changes: 6 additions & 3 deletions pkg/workflow/secret_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,10 +139,13 @@ func TestClaudeEngineHasSecretValidation(t *testing.T) {
t.Fatal("Expected at least one installation step")
}

// First step should be secret validation
// First step should be secret validation (now supports both CLAUDE_CODE_OAUTH_TOKEN and ANTHROPIC_API_KEY)
firstStep := strings.Join(steps[0], "\n")
if !strings.Contains(firstStep, "Validate ANTHROPIC_API_KEY secret") {
t.Error("First installation step should validate ANTHROPIC_API_KEY secret")
if !strings.Contains(firstStep, "Validate CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY secret") {
t.Error("First installation step should validate CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY secret")
}
if !strings.Contains(firstStep, "CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}") {
t.Error("Secret validation step should reference secrets.CLAUDE_CODE_OAUTH_TOKEN")
}
if !strings.Contains(firstStep, "ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}") {
t.Error("Secret validation step should reference secrets.ANTHROPIC_API_KEY")
Expand Down