From 424454409378a7a3a7c5314f90826432a3843f1e Mon Sep 17 00:00:00 2001 From: atsushi-ishibashi Date: Wed, 27 Aug 2025 14:33:37 +0900 Subject: [PATCH 1/4] feat: expose file download feature outside of tag mode --- action.yml | 5 ++ examples/agent-with-assets.yml | 58 ++++++++++++++ src/modes/agent/index.ts | 32 ++++++++ test/modes/agent.test.ts | 140 +++++++++++++++++++++++++++++++++ 4 files changed, 235 insertions(+) create mode 100644 examples/agent-with-assets.yml diff --git a/action.yml b/action.yml index aded2f368..231ec0773 100644 --- a/action.yml +++ b/action.yml @@ -27,6 +27,10 @@ inputs: description: "Comma-separated list of allowed bot usernames, or '*' to allow all bots. Empty string (default) allows no bots." required: false default: "" + download_github_assets: + description: "Download GitHub assets (images, attachments) for access via environment variables" + required: false + default: "false" # Claude Code configuration prompt: @@ -143,6 +147,7 @@ runs: ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }} CLAUDE_ARGS: ${{ inputs.claude_args }} ALL_INPUTS: ${{ toJson(inputs) }} + DOWNLOAD_GITHUB_ASSETS: ${{ inputs.download_github_assets }} - name: Install Base Action Dependencies if: steps.prepare.outputs.contains_trigger == 'true' diff --git a/examples/agent-with-assets.yml b/examples/agent-with-assets.yml new file mode 100644 index 000000000..73ddef9b3 --- /dev/null +++ b/examples/agent-with-assets.yml @@ -0,0 +1,58 @@ +name: Agent Mode Issue Screenshot Analysis +on: + issue_comment: + types: [created] + +jobs: + analyze: + if: contains(github.event.comment.body, '@claude') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + # First step: Download assets and prepare file list + - name: Download GitHub assets + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + download_github_assets: true + prompt: "Downloading GitHub assets..." + + # Second step: Expand CLAUDE_ASSET_FILES and create file list + - name: Prepare asset file list + shell: bash + run: | + if [ -n "$CLAUDE_ASSET_FILES" ]; then + echo "ASSET_FILE_LIST<> $GITHUB_ENV + echo "$CLAUDE_ASSET_FILES" | tr ',' '\n' | while IFS= read -r file; do + [ -n "$file" ] && echo "- $file" + done >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + + echo "ASSET_COUNT<> $GITHUB_ENV + echo "$CLAUDE_ASSET_FILES" | tr ',' '\n' | grep -c . >> $GITHUB_ENV + echo "EOF" >> $GITHUB_ENV + else + echo "ASSET_FILE_LIST=No images found in this issue/PR" >> $GITHUB_ENV + echo "ASSET_COUNT=0" >> $GITHUB_ENV + fi + + # Third step: Analyze images with expanded file list in prompt + - name: Analyze images + uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + prompt: | + ## Image Analysis Task + + Please analyze the images that were downloaded from this GitHub issue/PR context. + + Downloaded files (${{ env.ASSET_COUNT }} total): + ${{ env.ASSET_FILE_LIST }} + + For each file listed above: + 1. Use the Read() tool to access the image file + 2. Analyze the content and describe what you see + 3. Provide insights about the image (UI elements, code snippets, diagrams, etc.) + + Please provide a comprehensive analysis of all available images. diff --git a/src/modes/agent/index.ts b/src/modes/agent/index.ts index 25191f150..fb6edf118 100644 --- a/src/modes/agent/index.ts +++ b/src/modes/agent/index.ts @@ -5,6 +5,8 @@ import type { PreparedContext } from "../../create-prompt/types"; import { prepareMcpConfig } from "../../mcp/install-mcp-server"; import { parseAllowedTools } from "./parse-tools"; import { configureGitAuth } from "../../github/operations/git-config"; +import { fetchGitHubData } from "../../github/data/fetcher"; +import { isEntityContext } from "../../github/context"; /** * Agent mode implementation. @@ -122,6 +124,36 @@ export const agentMode: Mode = { // Append user's claude_args (which may have more --mcp-config flags) claudeArgs = `${claudeArgs} ${userClaudeArgs}`.trim(); + // Download GitHub assets if requested + const shouldDownload = process.env.DOWNLOAD_GITHUB_ASSETS === "true"; + + if (shouldDownload && isEntityContext(context)) { + console.log("Downloading GitHub assets for agent mode..."); + + try { + // Fetch GitHub data (reuse existing logic) + const githubData = await fetchGitHubData({ + octokits: octokit, + repository: `${context.repository.owner}/${context.repository.repo}`, + prNumber: context.entityNumber.toString(), + isPR: context.isPR, + triggerUsername: context.actor, + }); + + // Set simple environment variable with comma-separated paths + const downloadedPaths = Array.from(githubData.imageUrlMap.values()); + if (downloadedPaths.length > 0) { + process.env.CLAUDE_ASSET_FILES = downloadedPaths.join(","); + console.log( + `Exposed ${downloadedPaths.length} assets: ${process.env.CLAUDE_ASSET_FILES}`, + ); + } + } catch (error) { + console.error("Failed to download GitHub assets:", error); + // Continue execution - don't fail the entire action + } + } + core.setOutput("claude_args", claudeArgs); return { diff --git a/test/modes/agent.test.ts b/test/modes/agent.test.ts index 6bf7d0016..055fab760 100644 --- a/test/modes/agent.test.ts +++ b/test/modes/agent.test.ts @@ -3,6 +3,7 @@ import { agentMode } from "../../src/modes/agent"; import type { GitHubContext } from "../../src/github/context"; import { createMockContext, createMockAutomationContext } from "../mockContext"; import * as core from "@actions/core"; +import * as fetcher from "../../src/github/data/fetcher"; describe("Agent Mode", () => { let mockContext: GitHubContext; @@ -166,4 +167,143 @@ describe("Agent Mode", () => { expect(callArgs[0]).toBe("claude_args"); expect(callArgs[1]).toContain("--mcp-config"); }); + + test("downloads GitHub assets when enabled for entity context", async () => { + // Mock the fetchGitHubData function + const mockImageMap = new Map([ + [ + "https://github.com/user-attachments/assets/image1", + "/tmp/github-images/image-123.png", + ], + [ + "https://github.com/user-attachments/assets/image2", + "/tmp/github-images/image-456.jpg", + ], + ]); + const fetchSpy = spyOn(fetcher, "fetchGitHubData").mockResolvedValue({ + contextData: {} as any, + comments: [], + changedFiles: [], + changedFilesWithSHA: [], + reviewData: null, + imageUrlMap: mockImageMap, + triggerDisplayName: null, + }); + + // Create an entity context (issue comment) + const entityContext = createMockContext({ + eventName: "issue_comment", + inputs: { prompt: "Analyze images" }, + }); + + // Set download assets environment variable + const originalDownload = process.env.DOWNLOAD_GITHUB_ASSETS; + process.env.DOWNLOAD_GITHUB_ASSETS = "true"; + + const mockOctokit = {} as any; + + await agentMode.prepare({ + context: entityContext, + octokit: mockOctokit, + githubToken: "test-token", + }); + + // Verify fetchGitHubData was called + expect(fetchSpy).toHaveBeenCalledTimes(1); + expect(fetchSpy).toHaveBeenCalledWith({ + octokits: mockOctokit, + repository: `${entityContext.repository.owner}/${entityContext.repository.repo}`, + prNumber: entityContext.entityNumber.toString(), + isPR: entityContext.isPR, + triggerUsername: entityContext.actor, + }); + + // Verify CLAUDE_ASSET_FILES environment variable was set + expect(process.env.CLAUDE_ASSET_FILES).toBe( + "/tmp/github-images/image-123.png,/tmp/github-images/image-456.jpg", + ); + + // Clean up + delete process.env.CLAUDE_ASSET_FILES; + if (originalDownload !== undefined) { + process.env.DOWNLOAD_GITHUB_ASSETS = originalDownload; + } else { + delete process.env.DOWNLOAD_GITHUB_ASSETS; + } + fetchSpy.mockRestore(); + }); + + test("skips asset download when disabled", async () => { + const fetchSpy = spyOn(fetcher, "fetchGitHubData"); + + const entityContext = createMockContext({ + eventName: "issue_comment", + inputs: { prompt: "Analyze images" }, + }); + + // Ensure download assets is disabled (default) + const originalDownload = process.env.DOWNLOAD_GITHUB_ASSETS; + process.env.DOWNLOAD_GITHUB_ASSETS = "false"; + + const mockOctokit = {} as any; + + await agentMode.prepare({ + context: entityContext, + octokit: mockOctokit, + githubToken: "test-token", + }); + + // Verify fetchGitHubData was NOT called + expect(fetchSpy).toHaveBeenCalledTimes(0); + expect(process.env.CLAUDE_ASSET_FILES).toBeUndefined(); + + // Clean up + if (originalDownload !== undefined) { + process.env.DOWNLOAD_GITHUB_ASSETS = originalDownload; + } else { + delete process.env.DOWNLOAD_GITHUB_ASSETS; + } + fetchSpy.mockRestore(); + }); + + test("handles asset download errors gracefully", async () => { + const consoleSpy = spyOn(console, "error").mockImplementation(() => {}); + const fetchSpy = spyOn(fetcher, "fetchGitHubData").mockRejectedValue( + new Error("Network error"), + ); + + const entityContext = createMockContext({ + eventName: "issue_comment", + inputs: { prompt: "Analyze images" }, + }); + + // Set download assets environment variable + const originalDownload = process.env.DOWNLOAD_GITHUB_ASSETS; + process.env.DOWNLOAD_GITHUB_ASSETS = "true"; + + const mockOctokit = {} as any; + + // This should not throw despite the error + await agentMode.prepare({ + context: entityContext, + octokit: mockOctokit, + githubToken: "test-token", + }); + + // Verify error was logged but execution continued + expect(consoleSpy).toHaveBeenCalledWith( + "Failed to download GitHub assets:", + expect.any(Error), + ); + expect(process.env.CLAUDE_ASSET_FILES).toBeUndefined(); + + // Clean up + if (originalDownload !== undefined) { + process.env.DOWNLOAD_GITHUB_ASSETS = originalDownload; + } else { + delete process.env.DOWNLOAD_GITHUB_ASSETS; + } + fetchSpy.mockRestore(); + consoleSpy.mockRestore(); + }); }); From da668e2b6bb6efcd8b167e79d73e40dae47a05cc Mon Sep 17 00:00:00 2001 From: atsushi-ishibashi Date: Wed, 27 Aug 2025 18:51:32 +0900 Subject: [PATCH 2/4] fix subprocess env --- src/modes/agent/index.ts | 6 ++++-- test/modes/agent.test.ts | 6 ++++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/src/modes/agent/index.ts b/src/modes/agent/index.ts index fb6edf118..36ce38c44 100644 --- a/src/modes/agent/index.ts +++ b/src/modes/agent/index.ts @@ -143,9 +143,11 @@ export const agentMode: Mode = { // Set simple environment variable with comma-separated paths const downloadedPaths = Array.from(githubData.imageUrlMap.values()); if (downloadedPaths.length > 0) { - process.env.CLAUDE_ASSET_FILES = downloadedPaths.join(","); + const concatDownloadedPaths = downloadedPaths.join(","); + process.env.CLAUDE_ASSET_FILES = concatDownloadedPaths; + core.exportVariable("CLAUDE_ASSET_FILES", concatDownloadedPaths); console.log( - `Exposed ${downloadedPaths.length} assets: ${process.env.CLAUDE_ASSET_FILES}`, + `Exposed ${downloadedPaths.length} assets: ${concatDownloadedPaths}`, ); } } catch (error) { diff --git a/test/modes/agent.test.ts b/test/modes/agent.test.ts index 055fab760..8186c9bad 100644 --- a/test/modes/agent.test.ts +++ b/test/modes/agent.test.ts @@ -223,6 +223,12 @@ describe("Agent Mode", () => { "/tmp/github-images/image-123.png,/tmp/github-images/image-456.jpg", ); + // Verify core.exportVariable was called with correct arguments + expect(exportVariableSpy).toHaveBeenCalledWith( + "CLAUDE_ASSET_FILES", + "/tmp/github-images/image-123.png,/tmp/github-images/image-456.jpg", + ); + // Clean up delete process.env.CLAUDE_ASSET_FILES; if (originalDownload !== undefined) { From 59842bd51fa2ab676a82b7128d0f011d1b9926bb Mon Sep 17 00:00:00 2001 From: atsushi-ishibashi Date: Wed, 27 Aug 2025 21:51:46 +0900 Subject: [PATCH 3/4] update docs and example --- docs/usage.md | 60 ++++++++++++++++++++++------------ examples/agent-with-assets.yml | 50 +++++----------------------- 2 files changed, 47 insertions(+), 63 deletions(-) diff --git a/docs/usage.md b/docs/usage.md index 84f0f8558..96aab5260 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -47,27 +47,28 @@ jobs: ## Inputs -| Input | Description | Required | Default | -| ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -------- | --------- | -| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - | -| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - | -| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - | -| `claude_args` | Additional arguments to pass directly to Claude CLI (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" | -| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | -| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` | -| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - | -| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` | -| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` | -| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" | -| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - | -| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - | -| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` | -| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` | -| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" | -| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | -| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" | -| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` | -| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" | +| Input | Description | Required | Default | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | +| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - | +| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - | +| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - | +| `claude_args` | Additional arguments to pass directly to Claude CLI (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" | +| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | +| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` | +| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - | +| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` | +| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` | +| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" | +| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - | +| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - | +| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` | +| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` | +| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" | +| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | +| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" | +| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` | +| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" | +| `download_github_assets` | Enable GitHub asset downloading in Agent Mode (Tag Mode downloads assets by default). Downloaded file paths available via `CLAUDE_ASSET_FILES` environment variable | No | `false` | ### Deprecated Inputs @@ -211,3 +212,20 @@ Upload a screenshot of a bug and ask Claude to fix it: ``` Claude can see and analyze images, making it easy to fix visual bugs or UI issues. + +### Analyze Images with GitHub Assets + +For automation workflows that need to analyze images from GitHub issues or PRs, you can enable asset downloading: + +```yaml +- uses: anthropics/claude-code-action@v1 + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + download_github_assets: true + prompt: | + Analyze any images attached to this issue/PR. + Downloaded files are available via CLAUDE_ASSET_FILES environment variable. + Use the Read tool to access and analyze each image file. +``` + +This automatically downloads images from the GitHub context and makes them available to Claude for analysis. diff --git a/examples/agent-with-assets.yml b/examples/agent-with-assets.yml index 73ddef9b3..305a1df82 100644 --- a/examples/agent-with-assets.yml +++ b/examples/agent-with-assets.yml @@ -1,4 +1,4 @@ -name: Agent Mode Issue Screenshot Analysis +name: Agent Mode Asset Analysis on: issue_comment: types: [created] @@ -8,51 +8,17 @@ jobs: if: contains(github.event.comment.body, '@claude') runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - # First step: Download assets and prepare file list - - name: Download GitHub assets - uses: anthropics/claude-code-action@v1 + - uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} download_github_assets: true - prompt: "Downloading GitHub assets..." - - # Second step: Expand CLAUDE_ASSET_FILES and create file list - - name: Prepare asset file list - shell: bash - run: | - if [ -n "$CLAUDE_ASSET_FILES" ]; then - echo "ASSET_FILE_LIST<> $GITHUB_ENV - echo "$CLAUDE_ASSET_FILES" | tr ',' '\n' | while IFS= read -r file; do - [ -n "$file" ] && echo "- $file" - done >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - - echo "ASSET_COUNT<> $GITHUB_ENV - echo "$CLAUDE_ASSET_FILES" | tr ',' '\n' | grep -c . >> $GITHUB_ENV - echo "EOF" >> $GITHUB_ENV - else - echo "ASSET_FILE_LIST=No images found in this issue/PR" >> $GITHUB_ENV - echo "ASSET_COUNT=0" >> $GITHUB_ENV - fi - - # Third step: Analyze images with expanded file list in prompt - - name: Analyze images - uses: anthropics/claude-code-action@v1 - with: - anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} prompt: | - ## Image Analysis Task - - Please analyze the images that were downloaded from this GitHub issue/PR context. + Analyze the images from this GitHub issue/PR. - Downloaded files (${{ env.ASSET_COUNT }} total): - ${{ env.ASSET_FILE_LIST }} + The downloaded file paths are available in the CLAUDE_ASSET_FILES environment variable. + You can use bash commands to process this environment variable and access the files. - For each file listed above: - 1. Use the Read() tool to access the image file - 2. Analyze the content and describe what you see - 3. Provide insights about the image (UI elements, code snippets, diagrams, etc.) + For example, list the files with: + `echo $CLAUDE_ASSET_FILES | tr ',' '\n'` - Please provide a comprehensive analysis of all available images. + Then use the Read tool to analyze each file. From 89070581608fa683c807212065c267e63d446c4e Mon Sep 17 00:00:00 2001 From: atsushi-ishibashi Date: Thu, 28 Aug 2025 00:55:41 +0900 Subject: [PATCH 4/4] rm download_github_assets input --- action.yml | 5 ---- docs/usage.md | 51 +++++++++++++++++----------------- examples/agent-with-assets.yml | 4 +-- src/modes/agent/index.ts | 6 ++-- test/modes/agent.test.ts | 44 ++++++----------------------- 5 files changed, 37 insertions(+), 73 deletions(-) diff --git a/action.yml b/action.yml index 231ec0773..aded2f368 100644 --- a/action.yml +++ b/action.yml @@ -27,10 +27,6 @@ inputs: description: "Comma-separated list of allowed bot usernames, or '*' to allow all bots. Empty string (default) allows no bots." required: false default: "" - download_github_assets: - description: "Download GitHub assets (images, attachments) for access via environment variables" - required: false - default: "false" # Claude Code configuration prompt: @@ -147,7 +143,6 @@ runs: ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }} CLAUDE_ARGS: ${{ inputs.claude_args }} ALL_INPUTS: ${{ toJson(inputs) }} - DOWNLOAD_GITHUB_ASSETS: ${{ inputs.download_github_assets }} - name: Install Base Action Dependencies if: steps.prepare.outputs.contains_trigger == 'true' diff --git a/docs/usage.md b/docs/usage.md index 96aab5260..49a8aaefc 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -47,28 +47,27 @@ jobs: ## Inputs -| Input | Description | Required | Default | -| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- | -| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - | -| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - | -| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - | -| `claude_args` | Additional arguments to pass directly to Claude CLI (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" | -| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | -| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` | -| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - | -| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` | -| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` | -| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" | -| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - | -| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - | -| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` | -| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` | -| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" | -| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | -| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" | -| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` | -| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" | -| `download_github_assets` | Enable GitHub asset downloading in Agent Mode (Tag Mode downloads assets by default). Downloaded file paths available via `CLAUDE_ASSET_FILES` environment variable | No | `false` | +| Input | Description | Required | Default | +| ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -------- | --------- | +| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - | +| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - | +| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - | +| `claude_args` | Additional arguments to pass directly to Claude CLI (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" | +| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - | +| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` | +| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - | +| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` | +| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` | +| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" | +| `assignee_trigger` | The assignee username that triggers the action (e.g. @claude). Only used for issue assignment | No | - | +| `label_trigger` | The label name that triggers the action when applied to an issue (e.g. "claude") | No | - | +| `trigger_phrase` | The trigger phrase to look for in comments, issue/PR bodies, and issue titles | No | `@claude` | +| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` | +| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" | +| `additional_permissions` | Additional permissions to enable. Currently supports 'actions: read' for viewing workflow results | No | "" | +| `experimental_allowed_domains` | Restrict network access to these domains only (newline-separated). | No | "" | +| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` | +| `allowed_bots` | Comma-separated list of allowed bot usernames, or '\*' to allow all bots. Empty string (default) allows no bots | No | "" | ### Deprecated Inputs @@ -215,17 +214,17 @@ Claude can see and analyze images, making it easy to fix visual bugs or UI issue ### Analyze Images with GitHub Assets -For automation workflows that need to analyze images from GitHub issues or PRs, you can enable asset downloading: +For automation workflows that need to analyze images from GitHub issues or PRs, GitHub assets (images, attachments) are automatically downloaded when running in entity contexts (issue comments, pull requests, etc.): ```yaml - uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - download_github_assets: true prompt: | Analyze any images attached to this issue/PR. - Downloaded files are available via CLAUDE_ASSET_FILES environment variable. + + GitHub assets are automatically downloaded and available via CLAUDE_ASSET_FILES environment variable. Use the Read tool to access and analyze each image file. ``` -This automatically downloads images from the GitHub context and makes them available to Claude for analysis. +Images from GitHub issues and PRs are automatically downloaded and made available to Claude through the `CLAUDE_ASSET_FILES` environment variable when running in entity contexts. diff --git a/examples/agent-with-assets.yml b/examples/agent-with-assets.yml index 305a1df82..48af1b7f8 100644 --- a/examples/agent-with-assets.yml +++ b/examples/agent-with-assets.yml @@ -11,11 +11,11 @@ jobs: - uses: anthropics/claude-code-action@v1 with: anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} - download_github_assets: true prompt: | Analyze the images from this GitHub issue/PR. - The downloaded file paths are available in the CLAUDE_ASSET_FILES environment variable. + GitHub assets (images, attachments) are automatically downloaded and available + in the CLAUDE_ASSET_FILES environment variable. You can use bash commands to process this environment variable and access the files. For example, list the files with: diff --git a/src/modes/agent/index.ts b/src/modes/agent/index.ts index 36ce38c44..4cf618baf 100644 --- a/src/modes/agent/index.ts +++ b/src/modes/agent/index.ts @@ -124,10 +124,8 @@ export const agentMode: Mode = { // Append user's claude_args (which may have more --mcp-config flags) claudeArgs = `${claudeArgs} ${userClaudeArgs}`.trim(); - // Download GitHub assets if requested - const shouldDownload = process.env.DOWNLOAD_GITHUB_ASSETS === "true"; - - if (shouldDownload && isEntityContext(context)) { + // Download GitHub assets automatically for entity contexts + if (isEntityContext(context)) { console.log("Downloading GitHub assets for agent mode..."); try { diff --git a/test/modes/agent.test.ts b/test/modes/agent.test.ts index 8186c9bad..c8b1efe55 100644 --- a/test/modes/agent.test.ts +++ b/test/modes/agent.test.ts @@ -168,7 +168,7 @@ describe("Agent Mode", () => { expect(callArgs[1]).toContain("--mcp-config"); }); - test("downloads GitHub assets when enabled for entity context", async () => { + test("automatically downloads GitHub assets for entity context", async () => { // Mock the fetchGitHubData function const mockImageMap = new Map([ [ @@ -196,10 +196,6 @@ describe("Agent Mode", () => { inputs: { prompt: "Analyze images" }, }); - // Set download assets environment variable - const originalDownload = process.env.DOWNLOAD_GITHUB_ASSETS; - process.env.DOWNLOAD_GITHUB_ASSETS = "true"; - const mockOctokit = {} as any; await agentMode.prepare({ @@ -231,44 +227,30 @@ describe("Agent Mode", () => { // Clean up delete process.env.CLAUDE_ASSET_FILES; - if (originalDownload !== undefined) { - process.env.DOWNLOAD_GITHUB_ASSETS = originalDownload; - } else { - delete process.env.DOWNLOAD_GITHUB_ASSETS; - } fetchSpy.mockRestore(); }); - test("skips asset download when disabled", async () => { + test("skips asset download for non-entity contexts", async () => { const fetchSpy = spyOn(fetcher, "fetchGitHubData"); - const entityContext = createMockContext({ - eventName: "issue_comment", - inputs: { prompt: "Analyze images" }, + // Create an automation context (workflow_dispatch) + const automationContext = createMockAutomationContext({ + eventName: "workflow_dispatch", + inputs: { prompt: "Analyze something" }, }); - // Ensure download assets is disabled (default) - const originalDownload = process.env.DOWNLOAD_GITHUB_ASSETS; - process.env.DOWNLOAD_GITHUB_ASSETS = "false"; - const mockOctokit = {} as any; await agentMode.prepare({ - context: entityContext, + context: automationContext, octokit: mockOctokit, githubToken: "test-token", }); - // Verify fetchGitHubData was NOT called + // Verify fetchGitHubData was NOT called for automation contexts expect(fetchSpy).toHaveBeenCalledTimes(0); expect(process.env.CLAUDE_ASSET_FILES).toBeUndefined(); - // Clean up - if (originalDownload !== undefined) { - process.env.DOWNLOAD_GITHUB_ASSETS = originalDownload; - } else { - delete process.env.DOWNLOAD_GITHUB_ASSETS; - } fetchSpy.mockRestore(); }); @@ -283,10 +265,6 @@ describe("Agent Mode", () => { inputs: { prompt: "Analyze images" }, }); - // Set download assets environment variable - const originalDownload = process.env.DOWNLOAD_GITHUB_ASSETS; - process.env.DOWNLOAD_GITHUB_ASSETS = "true"; - const mockOctokit = {} as any; // This should not throw despite the error @@ -303,12 +281,6 @@ describe("Agent Mode", () => { ); expect(process.env.CLAUDE_ASSET_FILES).toBeUndefined(); - // Clean up - if (originalDownload !== undefined) { - process.env.DOWNLOAD_GITHUB_ASSETS = originalDownload; - } else { - delete process.env.DOWNLOAD_GITHUB_ASSETS; - } fetchSpy.mockRestore(); consoleSpy.mockRestore(); });