mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 06:54:13 +08:00
Simplify agent mode and re-add additional_permissions input
- Agent mode now only triggers when explicit prompt is provided - Removed automatic triggering for workflow_dispatch/schedule without prompt - Re-added additional_permissions input for requesting GitHub permissions - Fixed TypeScript types for mock context helpers to properly handle partial inputs - Updated documentation to reflect simplified mode behavior
This commit is contained in:
@@ -53,7 +53,7 @@ Execution steps:
|
|||||||
#### Mode System (`src/modes/`)
|
#### Mode System (`src/modes/`)
|
||||||
|
|
||||||
- **Tag Mode** (`tag/`): Responds to `@claude` mentions and issue assignments
|
- **Tag Mode** (`tag/`): Responds to `@claude` mentions and issue assignments
|
||||||
- **Agent Mode** (`agent/`): Automated execution for workflow_dispatch and schedule events only
|
- **Agent Mode** (`agent/`): Direct execution when explicit prompt is provided
|
||||||
- Extensible registry pattern in `modes/registry.ts`
|
- Extensible registry pattern in `modes/registry.ts`
|
||||||
|
|
||||||
#### GitHub Integration (`src/github/`)
|
#### GitHub Integration (`src/github/`)
|
||||||
@@ -118,7 +118,7 @@ src/
|
|||||||
|
|
||||||
- Modes implement `Mode` interface with `shouldTrigger()` and `prepare()` methods
|
- Modes implement `Mode` interface with `shouldTrigger()` and `prepare()` methods
|
||||||
- Registry validates mode compatibility with GitHub event types
|
- Registry validates mode compatibility with GitHub event types
|
||||||
- Agent mode only works with workflow_dispatch and schedule events
|
- Agent mode triggers when explicit prompt is provided
|
||||||
|
|
||||||
### Comment Threading
|
### Comment Threading
|
||||||
|
|
||||||
|
|||||||
@@ -65,6 +65,10 @@ inputs:
|
|||||||
description: "Additional arguments to pass directly to Claude CLI"
|
description: "Additional arguments to pass directly to Claude CLI"
|
||||||
required: false
|
required: false
|
||||||
default: ""
|
default: ""
|
||||||
|
additional_permissions:
|
||||||
|
description: "Additional GitHub permissions to request (e.g., 'actions: read')"
|
||||||
|
required: false
|
||||||
|
default: ""
|
||||||
use_sticky_comment:
|
use_sticky_comment:
|
||||||
description: "Use just one comment to deliver issue/PR comments"
|
description: "Use just one comment to deliver issue/PR comments"
|
||||||
required: false
|
required: false
|
||||||
@@ -119,6 +123,7 @@ runs:
|
|||||||
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
|
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
|
||||||
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
|
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
|
||||||
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
|
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
|
||||||
|
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
|
||||||
|
|
||||||
- name: Install Base Action Dependencies
|
- name: Install Base Action Dependencies
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true'
|
if: steps.prepare.outputs.contains_trigger == 'true'
|
||||||
|
|||||||
@@ -25,19 +25,19 @@ The traditional implementation mode that responds to @claude mentions, issue ass
|
|||||||
|
|
||||||
**Note: Agent mode is currently in active development and may undergo breaking changes.**
|
**Note: Agent mode is currently in active development and may undergo breaking changes.**
|
||||||
|
|
||||||
For automation with workflow_dispatch and scheduled events only.
|
For direct automation when an explicit prompt is provided.
|
||||||
|
|
||||||
- **Triggers**: Only works with `workflow_dispatch` and `schedule` events - does NOT work with PR/issue events
|
- **Triggers**: Works with any event when `prompt` input is provided
|
||||||
- **Features**: Perfect for scheduled tasks, works with `override_prompt`
|
- **Features**: Direct execution without @claude mentions, no tracking comments
|
||||||
- **Use case**: Maintenance tasks, automated reporting, scheduled checks
|
- **Use case**: Automated PR reviews, scheduled tasks, workflow automation
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
- uses: anthropics/claude-code-action@beta
|
- uses: anthropics/claude-code-action@beta
|
||||||
with:
|
with:
|
||||||
mode: agent
|
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
override_prompt: |
|
prompt: |
|
||||||
Check for outdated dependencies and create an issue if any are found.
|
Check for outdated dependencies and create an issue if any are found.
|
||||||
|
# Mode is auto-detected when prompt is provided
|
||||||
```
|
```
|
||||||
|
|
||||||
### Experimental Review Mode
|
### Experimental Review Mode
|
||||||
|
|||||||
@@ -1,23 +1,22 @@
|
|||||||
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 { isAutomationContext } from "../../github/context";
|
|
||||||
import type { PreparedContext } from "../../create-prompt/types";
|
import type { PreparedContext } from "../../create-prompt/types";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Agent mode implementation.
|
* Agent mode implementation.
|
||||||
*
|
*
|
||||||
* This mode is specifically designed for automation events (workflow_dispatch and schedule).
|
* This mode runs whenever an explicit prompt is provided in the workflow configuration.
|
||||||
* It bypasses the standard trigger checking and comment tracking used by tag mode,
|
* It bypasses the standard @claude mention checking and comment tracking used by tag mode,
|
||||||
* making it ideal for scheduled tasks and manual workflow runs.
|
* providing direct access to Claude Code for automation workflows.
|
||||||
*/
|
*/
|
||||||
export const agentMode: Mode = {
|
export const agentMode: Mode = {
|
||||||
name: "agent",
|
name: "agent",
|
||||||
description: "Automation mode for workflow_dispatch and schedule events",
|
description: "Direct automation mode for explicit prompts",
|
||||||
|
|
||||||
shouldTrigger(context) {
|
shouldTrigger(context) {
|
||||||
// Only trigger for automation events
|
// Only trigger when an explicit prompt is provided
|
||||||
return isAutomationContext(context);
|
return !!context.inputs?.prompt;
|
||||||
},
|
},
|
||||||
|
|
||||||
prepareContext(context) {
|
prepareContext(context) {
|
||||||
@@ -41,7 +40,7 @@ export const agentMode: Mode = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
async prepare({ context }: ModeOptions): Promise<ModeResult> {
|
async prepare({ context }: ModeOptions): Promise<ModeResult> {
|
||||||
// Agent mode handles automation events (workflow_dispatch, schedule) only
|
// Agent mode handles automation events and any event with explicit prompts
|
||||||
|
|
||||||
// TODO: handle by createPrompt (similar to tag and review modes)
|
// TODO: handle by createPrompt (similar to tag and review modes)
|
||||||
// Create prompt directory
|
// Create prompt directory
|
||||||
|
|||||||
@@ -9,13 +9,7 @@ import { checkContainsTrigger } from "../github/validation/trigger";
|
|||||||
export type AutoDetectedMode = "tag" | "agent";
|
export type AutoDetectedMode = "tag" | "agent";
|
||||||
|
|
||||||
export function detectMode(context: GitHubContext): AutoDetectedMode {
|
export function detectMode(context: GitHubContext): AutoDetectedMode {
|
||||||
// If prompt is provided, always use agent mode
|
// If prompt is provided, use agent mode for direct execution
|
||||||
// Reasoning: When users provide explicit instructions via the prompt parameter,
|
|
||||||
// they want Claude to execute those instructions immediately without waiting for
|
|
||||||
// @claude mentions or other triggers. This aligns with the v1.0 philosophy where
|
|
||||||
// Claude Code handles everything - the GitHub Action is just a thin wrapper that
|
|
||||||
// passes through prompts directly to Claude Code for native handling (including
|
|
||||||
// slash commands). This provides the most direct and flexible interaction model.
|
|
||||||
if (context.inputs?.prompt) {
|
if (context.inputs?.prompt) {
|
||||||
return "agent";
|
return "agent";
|
||||||
}
|
}
|
||||||
@@ -38,7 +32,7 @@ export function detectMode(context: GitHubContext): AutoDetectedMode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Default to agent mode for everything else
|
// Default to agent mode (which won't trigger without a prompt)
|
||||||
return "agent";
|
return "agent";
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +41,7 @@ export function getModeDescription(mode: AutoDetectedMode): string {
|
|||||||
case "tag":
|
case "tag":
|
||||||
return "Interactive mode triggered by @claude mentions";
|
return "Interactive mode triggered by @claude mentions";
|
||||||
case "agent":
|
case "agent":
|
||||||
return "General automation mode for all events";
|
return "Direct automation mode for explicit prompts";
|
||||||
default:
|
default:
|
||||||
return "Unknown mode";
|
return "Unknown mode";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ export type ModeData = {
|
|||||||
*
|
*
|
||||||
* Current modes include:
|
* Current modes include:
|
||||||
* - 'tag': Interactive mode triggered by @claude mentions
|
* - 'tag': Interactive mode triggered by @claude mentions
|
||||||
* - 'agent': General automation mode for all events (default)
|
* - 'agent': Direct automation mode triggered by explicit prompts
|
||||||
*/
|
*/
|
||||||
export type Mode = {
|
export type Mode = {
|
||||||
name: ModeName;
|
name: ModeName;
|
||||||
|
|||||||
@@ -27,8 +27,12 @@ const defaultRepository = {
|
|||||||
full_name: "test-owner/test-repo",
|
full_name: "test-owner/test-repo",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type MockContextOverrides = Omit<Partial<ParsedGitHubContext>, 'inputs'> & {
|
||||||
|
inputs?: Partial<ParsedGitHubContext['inputs']>;
|
||||||
|
};
|
||||||
|
|
||||||
export const createMockContext = (
|
export const createMockContext = (
|
||||||
overrides: Partial<ParsedGitHubContext> = {},
|
overrides: MockContextOverrides = {},
|
||||||
): ParsedGitHubContext => {
|
): ParsedGitHubContext => {
|
||||||
const baseContext: ParsedGitHubContext = {
|
const baseContext: ParsedGitHubContext = {
|
||||||
runId: "1234567890",
|
runId: "1234567890",
|
||||||
@@ -42,15 +46,19 @@ export const createMockContext = (
|
|||||||
inputs: defaultInputs,
|
inputs: defaultInputs,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (overrides.inputs) {
|
const mergedInputs = overrides.inputs
|
||||||
overrides.inputs = { ...defaultInputs, ...overrides.inputs };
|
? { ...defaultInputs, ...overrides.inputs }
|
||||||
}
|
: defaultInputs;
|
||||||
|
|
||||||
return { ...baseContext, ...overrides };
|
return { ...baseContext, ...overrides, inputs: mergedInputs };
|
||||||
|
};
|
||||||
|
|
||||||
|
type MockAutomationOverrides = Omit<Partial<AutomationContext>, 'inputs'> & {
|
||||||
|
inputs?: Partial<AutomationContext['inputs']>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const createMockAutomationContext = (
|
export const createMockAutomationContext = (
|
||||||
overrides: Partial<AutomationContext> = {},
|
overrides: MockAutomationOverrides = {},
|
||||||
): AutomationContext => {
|
): AutomationContext => {
|
||||||
const baseContext: AutomationContext = {
|
const baseContext: AutomationContext = {
|
||||||
runId: "1234567890",
|
runId: "1234567890",
|
||||||
@@ -62,7 +70,11 @@ export const createMockAutomationContext = (
|
|||||||
inputs: defaultInputs,
|
inputs: defaultInputs,
|
||||||
};
|
};
|
||||||
|
|
||||||
return { ...baseContext, ...overrides };
|
const mergedInputs = overrides.inputs
|
||||||
|
? { ...defaultInputs, ...overrides.inputs }
|
||||||
|
: defaultInputs;
|
||||||
|
|
||||||
|
return { ...baseContext, ...overrides, inputs: mergedInputs };
|
||||||
};
|
};
|
||||||
|
|
||||||
export const mockIssueOpenedContext: ParsedGitHubContext = {
|
export const mockIssueOpenedContext: ParsedGitHubContext = {
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ describe("Agent Mode", () => {
|
|||||||
test("agent mode has correct properties", () => {
|
test("agent mode has correct properties", () => {
|
||||||
expect(agentMode.name).toBe("agent");
|
expect(agentMode.name).toBe("agent");
|
||||||
expect(agentMode.description).toBe(
|
expect(agentMode.description).toBe(
|
||||||
"Automation mode for workflow_dispatch and schedule events",
|
"Direct automation mode for explicit prompts",
|
||||||
);
|
);
|
||||||
expect(agentMode.shouldCreateTrackingComment()).toBe(false);
|
expect(agentMode.shouldCreateTrackingComment()).toBe(false);
|
||||||
expect(agentMode.getAllowedTools()).toEqual([]);
|
expect(agentMode.getAllowedTools()).toEqual([]);
|
||||||
@@ -31,19 +31,19 @@ describe("Agent Mode", () => {
|
|||||||
expect(Object.keys(context)).toEqual(["mode", "githubContext"]);
|
expect(Object.keys(context)).toEqual(["mode", "githubContext"]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("agent mode only triggers for workflow_dispatch and schedule events", () => {
|
test("agent mode only triggers when prompt is provided", () => {
|
||||||
// Should trigger for automation events
|
// Should NOT trigger for automation events without prompt
|
||||||
const workflowDispatchContext = createMockAutomationContext({
|
const workflowDispatchContext = createMockAutomationContext({
|
||||||
eventName: "workflow_dispatch",
|
eventName: "workflow_dispatch",
|
||||||
});
|
});
|
||||||
expect(agentMode.shouldTrigger(workflowDispatchContext)).toBe(true);
|
expect(agentMode.shouldTrigger(workflowDispatchContext)).toBe(false);
|
||||||
|
|
||||||
const scheduleContext = createMockAutomationContext({
|
const scheduleContext = createMockAutomationContext({
|
||||||
eventName: "schedule",
|
eventName: "schedule",
|
||||||
});
|
});
|
||||||
expect(agentMode.shouldTrigger(scheduleContext)).toBe(true);
|
expect(agentMode.shouldTrigger(scheduleContext)).toBe(false);
|
||||||
|
|
||||||
// Should NOT trigger for entity events
|
// Should NOT trigger for entity events without prompt
|
||||||
const entityEvents = [
|
const entityEvents = [
|
||||||
"issue_comment",
|
"issue_comment",
|
||||||
"pull_request",
|
"pull_request",
|
||||||
@@ -52,8 +52,32 @@ describe("Agent Mode", () => {
|
|||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
entityEvents.forEach((eventName) => {
|
entityEvents.forEach((eventName) => {
|
||||||
const context = createMockContext({ eventName });
|
const contextNoPrompt = createMockContext({ eventName });
|
||||||
expect(agentMode.shouldTrigger(context)).toBe(false);
|
expect(agentMode.shouldTrigger(contextNoPrompt)).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Should trigger for ANY event when prompt is provided
|
||||||
|
const allEvents = [
|
||||||
|
"workflow_dispatch",
|
||||||
|
"schedule",
|
||||||
|
"issue_comment",
|
||||||
|
"pull_request",
|
||||||
|
"pull_request_review",
|
||||||
|
"issues",
|
||||||
|
] as const;
|
||||||
|
|
||||||
|
allEvents.forEach((eventName) => {
|
||||||
|
const contextWithPrompt =
|
||||||
|
eventName === "workflow_dispatch" || eventName === "schedule"
|
||||||
|
? createMockAutomationContext({
|
||||||
|
eventName,
|
||||||
|
inputs: { prompt: "Do something" },
|
||||||
|
})
|
||||||
|
: createMockContext({
|
||||||
|
eventName,
|
||||||
|
inputs: { prompt: "Do something" },
|
||||||
|
});
|
||||||
|
expect(agentMode.shouldTrigger(contextWithPrompt)).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user