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/src/modes/agent/index.ts b/src/modes/agent/index.ts index 9aeb8b3..f7d889c 100644 --- a/src/modes/agent/index.ts +++ b/src/modes/agent/index.ts @@ -80,9 +80,8 @@ export const agentMode: Mode = { ...context.inputs.disallowedTools, ]; - // Export as INPUT_ prefixed variables for the base action - core.exportVariable("INPUT_ALLOWED_TOOLS", allowedTools.join(",")); - core.exportVariable("INPUT_DISALLOWED_TOOLS", disallowedTools.join(",")); + core.exportVariable("ALLOWED_TOOLS", allowedTools.join(",")); + core.exportVariable("DISALLOWED_TOOLS", disallowedTools.join(",")); // Agent mode uses a minimal MCP configuration // We don't need comment servers or PR-specific tools for automation diff --git a/src/modes/review/index.ts b/src/modes/review/index.ts index eb520cc..bb1b527 100644 --- a/src/modes/review/index.ts +++ b/src/modes/review/index.ts @@ -297,9 +297,8 @@ This ensures users get value from the review even before checking individual inl ...context.inputs.disallowedTools, ]; - // Export as INPUT_ prefixed variables for the base action - core.exportVariable("INPUT_ALLOWED_TOOLS", allowedTools.join(",")); - core.exportVariable("INPUT_DISALLOWED_TOOLS", disallowedTools.join(",")); + core.exportVariable("ALLOWED_TOOLS", allowedTools.join(",")); + core.exportVariable("DISALLOWED_TOOLS", disallowedTools.join(",")); const additionalMcpConfig = process.env.MCP_CONFIG || ""; const mcpConfig = await prepareMcpConfig({ diff --git a/test/modes/agent.test.ts b/test/modes/agent.test.ts index 2daf068..4a48004 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", () => { @@ -56,4 +70,67 @@ describe("Agent Mode", () => { expect(agentMode.shouldTrigger(context)).toBe(false); }); }); + + 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)); + }); });