mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 06:54:13 +08:00
simplify PR by making agent mode only work with workflow dispatch and schedule events
This commit is contained in:
@@ -1,34 +1,31 @@
|
|||||||
import * as core from "@actions/core";
|
import * as core from "@actions/core";
|
||||||
import { mkdir, writeFile } from "fs/promises";
|
import { mkdir, writeFile } from "fs/promises";
|
||||||
import type { Mode, ModeOptions, ModeResult } from "../types";
|
import type { Mode, ModeOptions, ModeResult } from "../types";
|
||||||
import { prepareMcpConfig } from "../../mcp/install-mcp-server";
|
|
||||||
import { exportToolEnvironmentVariables } from "../../tools/export";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent mode implementation.
|
* Agent mode implementation.
|
||||||
*
|
*
|
||||||
* This mode is designed for automation and workflow_dispatch scenarios.
|
* This mode is specifically designed for automation events (workflow_dispatch and schedule).
|
||||||
* It always triggers (no checking), allows highly flexible configurations,
|
* It bypasses the standard trigger checking and comment tracking used by tag mode,
|
||||||
* and works well with override_prompt for custom workflows.
|
* making it ideal for scheduled tasks and manual workflow runs.
|
||||||
*
|
|
||||||
* In the future, this mode could restrict certain tools for safety in automation contexts,
|
|
||||||
* e.g., disallowing WebSearch or limiting file system operations.
|
|
||||||
*/
|
*/
|
||||||
export const agentMode: Mode = {
|
export const agentMode: Mode = {
|
||||||
name: "agent",
|
name: "agent",
|
||||||
description: "Automation mode that always runs without trigger checking",
|
description: "Automation mode for workflow_dispatch and schedule events",
|
||||||
|
|
||||||
shouldTrigger() {
|
shouldTrigger(context) {
|
||||||
return true;
|
// Only trigger for automation events
|
||||||
|
return (
|
||||||
|
context.eventName === "workflow_dispatch" ||
|
||||||
|
context.eventName === "schedule"
|
||||||
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
prepareContext(context, data) {
|
prepareContext(context) {
|
||||||
|
// Agent mode doesn't use comment tracking or branch management
|
||||||
return {
|
return {
|
||||||
mode: "agent",
|
mode: "agent",
|
||||||
githubContext: context,
|
githubContext: context,
|
||||||
commentId: data?.commentId,
|
|
||||||
baseBranch: data?.baseBranch,
|
|
||||||
claudeBranch: data?.claudeBranch,
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -44,9 +41,8 @@ export const agentMode: Mode = {
|
|||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
|
||||||
async prepare({ context, githubToken }: ModeOptions): Promise<ModeResult> {
|
async prepare({ context }: ModeOptions): Promise<ModeResult> {
|
||||||
// Agent mode is designed for automation events (workflow_dispatch, schedule)
|
// Agent mode handles automation events (workflow_dispatch, schedule) only
|
||||||
// and potentially other events where we want full automation without tracking
|
|
||||||
|
|
||||||
// Create prompt directory
|
// Create prompt directory
|
||||||
await mkdir(`${process.env.RUNNER_TEMP}/claude-prompts`, {
|
await mkdir(`${process.env.RUNNER_TEMP}/claude-prompts`, {
|
||||||
@@ -67,24 +63,48 @@ export const agentMode: Mode = {
|
|||||||
promptContent,
|
promptContent,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Export tool environment variables
|
// Export tool environment variables for agent mode
|
||||||
exportToolEnvironmentVariables(agentMode, context);
|
const baseTools = [
|
||||||
|
"Edit",
|
||||||
|
"MultiEdit",
|
||||||
|
"Glob",
|
||||||
|
"Grep",
|
||||||
|
"LS",
|
||||||
|
"Read",
|
||||||
|
"Write",
|
||||||
|
];
|
||||||
|
|
||||||
// Get MCP configuration
|
// Add user-specified tools
|
||||||
|
const allowedTools = [...baseTools, ...context.inputs.allowedTools];
|
||||||
|
const disallowedTools = [
|
||||||
|
"WebSearch",
|
||||||
|
"WebFetch",
|
||||||
|
...context.inputs.disallowedTools,
|
||||||
|
];
|
||||||
|
|
||||||
|
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
|
||||||
|
const mcpConfig: any = {
|
||||||
|
mcpServers: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
// Add user-provided additional MCP config if any
|
||||||
const additionalMcpConfig = process.env.MCP_CONFIG || "";
|
const additionalMcpConfig = process.env.MCP_CONFIG || "";
|
||||||
const mcpConfig = await prepareMcpConfig({
|
if (additionalMcpConfig.trim()) {
|
||||||
githubToken,
|
try {
|
||||||
owner: context.repository.owner,
|
const additional = JSON.parse(additionalMcpConfig);
|
||||||
repo: context.repository.repo,
|
if (additional && typeof additional === "object") {
|
||||||
branch: "", // No specific branch for agent mode
|
Object.assign(mcpConfig, additional);
|
||||||
baseBranch: "", // No base branch needed
|
}
|
||||||
additionalMcpConfig,
|
} catch (error) {
|
||||||
claudeCommentId: "",
|
core.warning(`Failed to parse additional MCP config: ${error}`);
|
||||||
allowedTools: context.inputs.allowedTools,
|
}
|
||||||
context,
|
}
|
||||||
});
|
|
||||||
|
|
||||||
core.setOutput("mcp_config", mcpConfig);
|
core.setOutput("mcp_config", JSON.stringify(mcpConfig));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
commentId: undefined,
|
commentId: undefined,
|
||||||
@@ -93,7 +113,7 @@ export const agentMode: Mode = {
|
|||||||
currentBranch: "",
|
currentBranch: "",
|
||||||
claudeBranch: undefined,
|
claudeBranch: undefined,
|
||||||
},
|
},
|
||||||
mcpConfig,
|
mcpConfig: JSON.stringify(mcpConfig),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,49 +0,0 @@
|
|||||||
/**
|
|
||||||
* Handles exporting tool-related environment variables
|
|
||||||
*/
|
|
||||||
|
|
||||||
import * as core from "@actions/core";
|
|
||||||
import type { Mode } from "../modes/types";
|
|
||||||
import type { ParsedGitHubContext } from "../github/context";
|
|
||||||
import {
|
|
||||||
buildAllowedToolsString,
|
|
||||||
buildDisallowedToolsString,
|
|
||||||
} from "../create-prompt/index";
|
|
||||||
|
|
||||||
export function exportToolEnvironmentVariables(
|
|
||||||
mode: Mode,
|
|
||||||
context: ParsedGitHubContext,
|
|
||||||
): void {
|
|
||||||
const hasActionsReadPermission =
|
|
||||||
context.inputs.additionalPermissions.get("actions") === "read" &&
|
|
||||||
context.isPR;
|
|
||||||
|
|
||||||
const modeAllowedTools = mode.getAllowedTools();
|
|
||||||
const modeDisallowedTools = mode.getDisallowedTools();
|
|
||||||
|
|
||||||
// Combine with existing allowed tools
|
|
||||||
const combinedAllowedTools = [
|
|
||||||
...context.inputs.allowedTools,
|
|
||||||
...modeAllowedTools,
|
|
||||||
];
|
|
||||||
const combinedDisallowedTools = [
|
|
||||||
...context.inputs.disallowedTools,
|
|
||||||
...modeDisallowedTools,
|
|
||||||
];
|
|
||||||
|
|
||||||
const allAllowedTools = buildAllowedToolsString(
|
|
||||||
combinedAllowedTools,
|
|
||||||
hasActionsReadPermission,
|
|
||||||
context.inputs.useCommitSigning,
|
|
||||||
);
|
|
||||||
const allDisallowedTools = buildDisallowedToolsString(
|
|
||||||
combinedDisallowedTools,
|
|
||||||
combinedAllowedTools,
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log(`Allowed tools: ${allAllowedTools}`);
|
|
||||||
console.log(`Disallowed tools: ${allDisallowedTools}`);
|
|
||||||
|
|
||||||
core.exportVariable("ALLOWED_TOOLS", allAllowedTools);
|
|
||||||
core.exportVariable("DISALLOWED_TOOLS", allDisallowedTools);
|
|
||||||
}
|
|
||||||
@@ -13,70 +13,52 @@ describe("Agent Mode", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("agent mode has correct properties and behavior", () => {
|
test("agent mode has correct properties", () => {
|
||||||
// Basic properties
|
|
||||||
expect(agentMode.name).toBe("agent");
|
expect(agentMode.name).toBe("agent");
|
||||||
expect(agentMode.description).toBe(
|
expect(agentMode.description).toBe(
|
||||||
"Automation mode that always runs without trigger checking",
|
"Automation mode for workflow_dispatch and schedule events",
|
||||||
);
|
);
|
||||||
expect(agentMode.shouldCreateTrackingComment()).toBe(false);
|
expect(agentMode.shouldCreateTrackingComment()).toBe(false);
|
||||||
|
|
||||||
// Tool methods return empty arrays
|
|
||||||
expect(agentMode.getAllowedTools()).toEqual([]);
|
expect(agentMode.getAllowedTools()).toEqual([]);
|
||||||
expect(agentMode.getDisallowedTools()).toEqual([]);
|
expect(agentMode.getDisallowedTools()).toEqual([]);
|
||||||
|
|
||||||
// Always triggers regardless of context
|
|
||||||
const contextWithoutTrigger = createMockContext({
|
|
||||||
eventName: "workflow_dispatch",
|
|
||||||
isPR: false,
|
|
||||||
inputs: {
|
|
||||||
...createMockContext().inputs,
|
|
||||||
triggerPhrase: "@claude",
|
|
||||||
},
|
|
||||||
payload: {} as any,
|
|
||||||
});
|
|
||||||
expect(agentMode.shouldTrigger(contextWithoutTrigger)).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("prepareContext includes all required data", () => {
|
test("prepareContext returns minimal data", () => {
|
||||||
const data = {
|
|
||||||
commentId: 789,
|
|
||||||
baseBranch: "develop",
|
|
||||||
claudeBranch: "claude/automated-task",
|
|
||||||
};
|
|
||||||
|
|
||||||
const context = agentMode.prepareContext(mockContext, data);
|
|
||||||
|
|
||||||
expect(context.mode).toBe("agent");
|
|
||||||
expect(context.githubContext).toBe(mockContext);
|
|
||||||
expect(context.commentId).toBe(789);
|
|
||||||
expect(context.baseBranch).toBe("develop");
|
|
||||||
expect(context.claudeBranch).toBe("claude/automated-task");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("prepareContext works without data", () => {
|
|
||||||
const context = agentMode.prepareContext(mockContext);
|
const context = agentMode.prepareContext(mockContext);
|
||||||
|
|
||||||
expect(context.mode).toBe("agent");
|
expect(context.mode).toBe("agent");
|
||||||
expect(context.githubContext).toBe(mockContext);
|
expect(context.githubContext).toBe(mockContext);
|
||||||
expect(context.commentId).toBeUndefined();
|
// Agent mode doesn't use comment tracking or branch management
|
||||||
expect(context.baseBranch).toBeUndefined();
|
expect(Object.keys(context)).toEqual(["mode", "githubContext"]);
|
||||||
expect(context.claudeBranch).toBeUndefined();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("agent mode triggers for all event types", () => {
|
test("agent mode only triggers for workflow_dispatch and schedule events", () => {
|
||||||
const events = [
|
// Should trigger for automation events
|
||||||
|
const workflowDispatchContext = createMockContext({
|
||||||
|
eventName: "workflow_dispatch",
|
||||||
|
isPR: false,
|
||||||
|
});
|
||||||
|
expect(agentMode.shouldTrigger(workflowDispatchContext)).toBe(true);
|
||||||
|
|
||||||
|
const scheduleContext = createMockContext({
|
||||||
|
eventName: "schedule",
|
||||||
|
isPR: false,
|
||||||
|
});
|
||||||
|
expect(agentMode.shouldTrigger(scheduleContext)).toBe(true);
|
||||||
|
|
||||||
|
// Should NOT trigger for other events
|
||||||
|
const otherEvents = [
|
||||||
"push",
|
"push",
|
||||||
"schedule",
|
|
||||||
"workflow_dispatch",
|
|
||||||
"repository_dispatch",
|
"repository_dispatch",
|
||||||
"issue_comment",
|
"issue_comment",
|
||||||
"pull_request",
|
"pull_request",
|
||||||
|
"pull_request_review",
|
||||||
|
"issues",
|
||||||
];
|
];
|
||||||
|
|
||||||
events.forEach((eventName) => {
|
otherEvents.forEach((eventName) => {
|
||||||
const context = createMockContext({ eventName, isPR: false });
|
const context = createMockContext({ eventName, isPR: false });
|
||||||
expect(agentMode.shouldTrigger(context)).toBe(true);
|
expect(agentMode.shouldTrigger(context)).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user