diff --git a/README.md b/README.md index 3597680..ce976ef 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,19 @@ A general-purpose [Claude Code](https://claude.ai/code) action for GitHub PRs an - 📋 **Progress Tracking**: Visual progress indicators with checkboxes that dynamically update as Claude completes tasks - 🏃 **Runs on Your Infrastructure**: The action executes entirely on your own GitHub runner (Anthropic API calls go to your chosen provider) +## ⚠️ **BREAKING CHANGES COMING IN v1.0** ⚠️ + +**We're planning a major update that will significantly change how this action works.** The new version will: + +- ✨ Automatically select the appropriate mode (no more `mode` input) +- 🔧 Simplify configuration with unified `prompt` and `claude_args` +- 🚀 Align more closely with the Claude Code SDK capabilities +- 💥 Remove multiple inputs like `direct_prompt`, `custom_instructions`, and others + +**[→ Read the full v1.0 roadmap and provide feedback](https://github.com/anthropics/claude-code-action/discussions/428)** + +--- + ## Quickstart The easiest way to set up this action is through [Claude Code](https://claude.ai/code) in the terminal. Just open `claude` and run `/install-github-app`. diff --git a/docs/configuration.md b/docs/configuration.md index ec0f317..33dfff5 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -207,15 +207,8 @@ Claude does **not** have access to execute arbitrary Bash commands by default. I ```yaml - uses: anthropics/claude-code-action@beta with: - allowed_tools: | - Bash(npm install) - Bash(npm run test) - Edit - Replace - NotebookEditCell - disallowed_tools: | - TaskOutput - KillTask + allowed_tools: "Bash(npm install),Bash(npm run test),Edit,Replace,NotebookEditCell" + disallowed_tools: "TaskOutput,KillTask" # ... other inputs ``` diff --git a/test/modes/agent.test.ts b/test/modes/agent.test.ts index 090c894..2e0b789 100644 --- a/test/modes/agent.test.ts +++ b/test/modes/agent.test.ts @@ -1,15 +1,29 @@ -import { describe, test, expect, beforeEach } from "bun:test"; +import { describe, test, expect, beforeEach, afterEach, spyOn } from "bun:test"; import { agentMode } from "../../src/modes/agent"; import type { GitHubContext } from "../../src/github/context"; import { createMockContext, createMockAutomationContext } from "../mockContext"; +import * as core from "@actions/core"; describe("Agent Mode", () => { let mockContext: GitHubContext; + let exportVariableSpy: any; + let setOutputSpy: any; beforeEach(() => { mockContext = createMockAutomationContext({ eventName: "workflow_dispatch", }); + exportVariableSpy = spyOn(core, "exportVariable").mockImplementation( + () => {}, + ); + setOutputSpy = spyOn(core, "setOutput").mockImplementation(() => {}); + }); + + afterEach(() => { + exportVariableSpy?.mockClear(); + setOutputSpy?.mockClear(); + exportVariableSpy?.mockRestore(); + setOutputSpy?.mockRestore(); }); test("agent mode has correct properties", () => { @@ -80,4 +94,67 @@ describe("Agent Mode", () => { expect(agentMode.shouldTrigger(contextWithPrompt)).toBe(true); }); }); + + test("prepare method sets up tools environment variables correctly", async () => { + // Clear any previous calls before this test + exportVariableSpy.mockClear(); + setOutputSpy.mockClear(); + + const contextWithCustomTools = createMockAutomationContext({ + eventName: "workflow_dispatch", + }); + contextWithCustomTools.inputs.allowedTools = ["CustomTool1", "CustomTool2"]; + contextWithCustomTools.inputs.disallowedTools = ["BadTool"]; + + const mockOctokit = {} as any; + const result = await agentMode.prepare({ + context: contextWithCustomTools, + octokit: mockOctokit, + githubToken: "test-token", + }); + + // Verify that both ALLOWED_TOOLS and DISALLOWED_TOOLS are set + expect(exportVariableSpy).toHaveBeenCalledWith( + "ALLOWED_TOOLS", + "Edit,MultiEdit,Glob,Grep,LS,Read,Write,CustomTool1,CustomTool2", + ); + expect(exportVariableSpy).toHaveBeenCalledWith( + "DISALLOWED_TOOLS", + "WebSearch,WebFetch,BadTool", + ); + + // Verify MCP config is set + expect(setOutputSpy).toHaveBeenCalledWith("mcp_config", expect.any(String)); + + // Verify return structure + expect(result).toEqual({ + commentId: undefined, + branchInfo: { + baseBranch: "", + currentBranch: "", + claudeBranch: undefined, + }, + mcpConfig: expect.any(String), + }); + }); + + test("prepare method creates prompt file with correct content", async () => { + const contextWithPrompts = createMockAutomationContext({ + eventName: "workflow_dispatch", + }); + contextWithPrompts.inputs.overridePrompt = "Custom override prompt"; + contextWithPrompts.inputs.directPrompt = + "Direct prompt (should be ignored)"; + + const mockOctokit = {} as any; + await agentMode.prepare({ + context: contextWithPrompts, + octokit: mockOctokit, + githubToken: "test-token", + }); + + // Note: We can't easily test file creation in this unit test, + // but we can verify the method completes without errors + expect(setOutputSpy).toHaveBeenCalledWith("mcp_config", expect.any(String)); + }); });