mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 22:44:13 +08:00
feat: add agent mode for automation scenarios (#337)
* feat: add agent mode for automation scenarios - Add agent mode that always triggers without checking for mentions - Implement Mode interface with support for mode-specific tool configuration - Add getAllowedTools() and getDisallowedTools() methods to Mode interface - Simplify tests by combining related test cases - Update documentation and examples to include agent mode - Fix TypeScript imports to prevent circular dependencies Agent mode is designed for automation and workflow_dispatch scenarios where Claude should always run without requiring trigger phrases. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> * Minor update to readme (from @main to @beta) * Since workflow_dispatch isn't in the base action, update the examples accordingly * minor formatting issue * Update to say beta instead of main * Fix missed tracking comment to be false --------- Co-authored-by: km-anthropic <km-anthropic@users.noreply.github.com> Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
98
README.md
98
README.md
@@ -167,41 +167,79 @@ jobs:
|
|||||||
|
|
||||||
## Inputs
|
## Inputs
|
||||||
|
|
||||||
| Input | Description | Required | Default |
|
| Input | Description | Required | Default |
|
||||||
| ------------------------------ | -------------------------------------------------------------------------------------------------------------------- | -------- | --------- |
|
| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------- | -------- | --------- |
|
||||||
| `mode` | Execution mode for the action. Currently supports 'tag' (default). Future modes: 'review', 'freeform' | No | `tag` |
|
| `mode` | Execution mode: 'tag' (default - triggered by mentions/assignments), 'agent' (for automation with no trigger checking) | No | `tag` |
|
||||||
| `anthropic_api_key` | Anthropic API key (required for direct API, not needed for Bedrock/Vertex) | No\* | - |
|
| `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\* | - |
|
| `claude_code_oauth_token` | Claude Code OAuth token (alternative to anthropic_api_key) | No\* | - |
|
||||||
| `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - |
|
| `direct_prompt` | Direct prompt for Claude to execute automatically without needing a trigger (for automated workflows) | No | - |
|
||||||
| `override_prompt` | Complete replacement of Claude's prompt with custom template (supports variable substitution) | No | - |
|
| `override_prompt` | Complete replacement of Claude's prompt with custom template (supports variable substitution) | No | - |
|
||||||
| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - |
|
| `base_branch` | The base branch to use for creating new branches (e.g., 'main', 'develop') | No | - |
|
||||||
| `max_turns` | Maximum number of conversation turns Claude can take (limits back-and-forth exchanges) | No | - |
|
| `max_turns` | Maximum number of conversation turns Claude can take (limits back-and-forth exchanges) | No | - |
|
||||||
| `timeout_minutes` | Timeout in minutes for execution | No | `30` |
|
| `timeout_minutes` | Timeout in minutes for execution | No | `30` |
|
||||||
| `use_sticky_comment` | Use just one comment to deliver PR comments (only applies for pull_request event workflows) | No | `false` |
|
| `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 | - |
|
| `github_token` | GitHub token for Claude to operate with. **Only include this if you're connecting a custom GitHub app of your own!** | No | - |
|
||||||
| `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | - |
|
| `model` | Model to use (provider-specific format required for Bedrock/Vertex) | No | - |
|
||||||
| `fallback_model` | Enable automatic fallback to specified model when primary model is unavailable | No | - |
|
| `fallback_model` | Enable automatic fallback to specified model when primary model is unavailable | No | - |
|
||||||
| `anthropic_model` | **DEPRECATED**: Use `model` instead. Kept for backward compatibility. | No | - |
|
| `anthropic_model` | **DEPRECATED**: Use `model` instead. Kept for backward compatibility. | No | - |
|
||||||
| `use_bedrock` | Use Amazon Bedrock with OIDC authentication instead of direct Anthropic API | No | `false` |
|
| `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` |
|
| `use_vertex` | Use Google Vertex AI with OIDC authentication instead of direct Anthropic API | No | `false` |
|
||||||
| `allowed_tools` | Additional tools for Claude to use (the base GitHub tools will always be included) | No | "" |
|
| `allowed_tools` | Additional tools for Claude to use (the base GitHub tools will always be included) | No | "" |
|
||||||
| `disallowed_tools` | Tools that Claude should never use | No | "" |
|
| `disallowed_tools` | Tools that Claude should never use | No | "" |
|
||||||
| `custom_instructions` | Additional custom instructions to include in the prompt for Claude | No | "" |
|
| `custom_instructions` | Additional custom instructions to include in the prompt for Claude | No | "" |
|
||||||
| `mcp_config` | Additional MCP configuration (JSON string) that merges with the built-in GitHub MCP servers | No | "" |
|
| `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 | - |
|
| `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 | - |
|
| `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` |
|
| `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/` |
|
| `branch_prefix` | The prefix to use for Claude branches (defaults to 'claude/', use 'claude-' for dash format) | No | `claude/` |
|
||||||
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" |
|
| `claude_env` | Custom environment variables to pass to Claude Code execution (YAML format) | No | "" |
|
||||||
| `settings` | Claude Code settings as JSON string or path to settings JSON file | No | "" |
|
| `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 | "" |
|
| `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 | "" |
|
| `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` |
|
| `use_commit_signing` | Enable commit signing using GitHub's commit signature verification. When false, Claude uses standard git commands | No | `false` |
|
||||||
|
|
||||||
\*Required when using direct Anthropic API (default and when not using Bedrock or Vertex)
|
\*Required when using direct Anthropic API (default and when not using Bedrock or Vertex)
|
||||||
|
|
||||||
> **Note**: This action is currently in beta. Features and APIs may change as we continue to improve the integration.
|
> **Note**: This action is currently in beta. Features and APIs may change as we continue to improve the integration.
|
||||||
|
|
||||||
|
## Execution Modes
|
||||||
|
|
||||||
|
The action supports two execution modes, each optimized for different use cases:
|
||||||
|
|
||||||
|
### Tag Mode (Default)
|
||||||
|
|
||||||
|
The traditional implementation mode that responds to @claude mentions, issue assignments, or labels.
|
||||||
|
|
||||||
|
- **Triggers**: `@claude` mentions, issue assignment, label application
|
||||||
|
- **Features**: Creates tracking comments with progress checkboxes, full implementation capabilities
|
||||||
|
- **Use case**: General-purpose code implementation and Q&A
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: anthropics/claude-code-action@beta
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
# mode: tag is the default
|
||||||
|
```
|
||||||
|
|
||||||
|
### Agent Mode
|
||||||
|
|
||||||
|
For automation and scheduled tasks without trigger checking.
|
||||||
|
|
||||||
|
- **Triggers**: Always runs (no trigger checking)
|
||||||
|
- **Features**: Perfect for scheduled tasks, works with `override_prompt`
|
||||||
|
- **Use case**: Maintenance tasks, automated reporting, scheduled checks
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
- uses: anthropics/claude-code-action@beta
|
||||||
|
with:
|
||||||
|
mode: agent
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
override_prompt: |
|
||||||
|
Check for outdated dependencies and create an issue if any are found.
|
||||||
|
```
|
||||||
|
|
||||||
|
See [`examples/claude-modes.yml`](./examples/claude-modes.yml) for complete examples of each mode.
|
||||||
|
|
||||||
### Using Custom MCP Configuration
|
### Using Custom MCP Configuration
|
||||||
|
|
||||||
The `mcp_config` input allows you to add custom MCP (Model Context Protocol) servers to extend Claude's capabilities. These servers merge with the built-in GitHub MCP servers.
|
The `mcp_config` input allows you to add custom MCP (Model Context Protocol) servers to extend Claude's capabilities. These servers merge with the built-in GitHub MCP servers.
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ inputs:
|
|||||||
|
|
||||||
# Mode configuration
|
# Mode configuration
|
||||||
mode:
|
mode:
|
||||||
description: "Execution mode for the action. Currently only 'tag' mode is supported (traditional implementation triggered by mentions/assignments)"
|
description: "Execution mode for the action. Valid modes: 'tag' (default - triggered by mentions/assignments), 'agent' (for automation with no trigger checking)"
|
||||||
required: false
|
required: false
|
||||||
default: "tag"
|
default: "tag"
|
||||||
|
|
||||||
|
|||||||
56
examples/claude-modes.yml
Normal file
56
examples/claude-modes.yml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
name: Claude Mode Examples
|
||||||
|
|
||||||
|
on:
|
||||||
|
# Common events for both modes
|
||||||
|
issue_comment:
|
||||||
|
types: [created]
|
||||||
|
issues:
|
||||||
|
types: [opened, labeled]
|
||||||
|
pull_request:
|
||||||
|
types: [opened]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
# Tag Mode (Default) - Traditional implementation
|
||||||
|
tag-mode-example:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: anthropics/claude-code-action@beta
|
||||||
|
with:
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
# Tag mode (default) behavior:
|
||||||
|
# - Scans for @claude mentions in comments, issues, and PRs
|
||||||
|
# - Only acts when trigger phrase is found
|
||||||
|
# - Creates tracking comments with progress checkboxes
|
||||||
|
# - Perfect for: Interactive Q&A, on-demand code changes
|
||||||
|
|
||||||
|
# Agent Mode - Automation without triggers
|
||||||
|
agent-mode-auto-review:
|
||||||
|
# Automatically review every new PR
|
||||||
|
if: github.event_name == 'pull_request' && github.event.action == 'opened'
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
id-token: write
|
||||||
|
steps:
|
||||||
|
- uses: anthropics/claude-code-action@beta
|
||||||
|
with:
|
||||||
|
mode: agent
|
||||||
|
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
||||||
|
override_prompt: |
|
||||||
|
Review this PR for code quality. Focus on:
|
||||||
|
- Potential bugs or logic errors
|
||||||
|
- Security concerns
|
||||||
|
- Performance issues
|
||||||
|
|
||||||
|
Provide specific, actionable feedback.
|
||||||
|
# Agent mode behavior:
|
||||||
|
# - NO @claude mention needed - runs immediately
|
||||||
|
# - Enables true automation (impossible with tag mode)
|
||||||
|
# - Perfect for: CI/CD integration, automatic reviews, label-based workflows
|
||||||
@@ -840,14 +840,29 @@ export async function createPrompt(
|
|||||||
const hasActionsReadPermission =
|
const hasActionsReadPermission =
|
||||||
context.inputs.additionalPermissions.get("actions") === "read" &&
|
context.inputs.additionalPermissions.get("actions") === "read" &&
|
||||||
context.isPR;
|
context.isPR;
|
||||||
|
|
||||||
|
// Get mode-specific tools
|
||||||
|
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(
|
const allAllowedTools = buildAllowedToolsString(
|
||||||
context.inputs.allowedTools,
|
combinedAllowedTools,
|
||||||
hasActionsReadPermission,
|
hasActionsReadPermission,
|
||||||
context.inputs.useCommitSigning,
|
context.inputs.useCommitSigning,
|
||||||
);
|
);
|
||||||
const allDisallowedTools = buildDisallowedToolsString(
|
const allDisallowedTools = buildDisallowedToolsString(
|
||||||
context.inputs.disallowedTools,
|
combinedDisallowedTools,
|
||||||
context.inputs.allowedTools,
|
combinedAllowedTools,
|
||||||
);
|
);
|
||||||
|
|
||||||
core.exportVariable("ALLOWED_TOOLS", allAllowedTools);
|
core.exportVariable("ALLOWED_TOOLS", allAllowedTools);
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ async function run() {
|
|||||||
await checkHumanActor(octokit.rest, context);
|
await checkHumanActor(octokit.rest, context);
|
||||||
|
|
||||||
// Step 6: Create initial tracking comment (mode-aware)
|
// Step 6: Create initial tracking comment (mode-aware)
|
||||||
// Some modes (e.g., future review/freeform modes) may not need tracking comments
|
// Some modes (e.g., agent mode) may not need tracking comments
|
||||||
let commentId: number | undefined;
|
let commentId: number | undefined;
|
||||||
let commentData:
|
let commentData:
|
||||||
| Awaited<ReturnType<typeof createInitialComment>>
|
| Awaited<ReturnType<typeof createInitialComment>>
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ import type {
|
|||||||
PullRequestReviewEvent,
|
PullRequestReviewEvent,
|
||||||
PullRequestReviewCommentEvent,
|
PullRequestReviewCommentEvent,
|
||||||
} from "@octokit/webhooks-types";
|
} from "@octokit/webhooks-types";
|
||||||
import type { ModeName } from "../modes/registry";
|
import type { ModeName } from "../modes/types";
|
||||||
import { DEFAULT_MODE } from "../modes/registry";
|
import { DEFAULT_MODE, isValidMode } from "../modes/registry";
|
||||||
import { isValidMode } from "../modes/registry";
|
|
||||||
|
|
||||||
export type ParsedGitHubContext = {
|
export type ParsedGitHubContext = {
|
||||||
runId: string;
|
runId: string;
|
||||||
|
|||||||
42
src/modes/agent/index.ts
Normal file
42
src/modes/agent/index.ts
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
import type { Mode } from "../types";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Agent mode implementation.
|
||||||
|
*
|
||||||
|
* This mode is designed for automation and workflow_dispatch scenarios.
|
||||||
|
* It always triggers (no checking), allows highly flexible configurations,
|
||||||
|
* and works well with override_prompt for custom workflows.
|
||||||
|
*
|
||||||
|
* 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 = {
|
||||||
|
name: "agent",
|
||||||
|
description: "Automation mode that always runs without trigger checking",
|
||||||
|
|
||||||
|
shouldTrigger() {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
prepareContext(context, data) {
|
||||||
|
return {
|
||||||
|
mode: "agent",
|
||||||
|
githubContext: context,
|
||||||
|
commentId: data?.commentId,
|
||||||
|
baseBranch: data?.baseBranch,
|
||||||
|
claudeBranch: data?.claudeBranch,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
|
||||||
|
getAllowedTools() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
|
getDisallowedTools() {
|
||||||
|
return [];
|
||||||
|
},
|
||||||
|
|
||||||
|
shouldCreateTrackingComment() {
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -5,17 +5,17 @@
|
|||||||
*
|
*
|
||||||
* To add a new mode:
|
* To add a new mode:
|
||||||
* 1. Add the mode name to VALID_MODES below
|
* 1. Add the mode name to VALID_MODES below
|
||||||
* 2. Create the mode implementation in a new directory (e.g., src/modes/review/)
|
* 2. Create the mode implementation in a new directory (e.g., src/modes/new-mode/)
|
||||||
* 3. Import and add it to the modes object below
|
* 3. Import and add it to the modes object below
|
||||||
* 4. Update action.yml description to mention the new mode
|
* 4. Update action.yml description to mention the new mode
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Mode } from "./types";
|
import type { Mode, ModeName } from "./types";
|
||||||
import { tagMode } from "./tag/index";
|
import { tagMode } from "./tag";
|
||||||
|
import { agentMode } from "./agent";
|
||||||
|
|
||||||
export const DEFAULT_MODE = "tag" as const;
|
export const DEFAULT_MODE = "tag" as const;
|
||||||
export const VALID_MODES = ["tag"] as const;
|
export const VALID_MODES = ["tag", "agent"] as const;
|
||||||
export type ModeName = (typeof VALID_MODES)[number];
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* All available modes.
|
* All available modes.
|
||||||
@@ -23,6 +23,7 @@ export type ModeName = (typeof VALID_MODES)[number];
|
|||||||
*/
|
*/
|
||||||
const modes = {
|
const modes = {
|
||||||
tag: tagMode,
|
tag: tagMode,
|
||||||
|
agent: agentMode,
|
||||||
} as const satisfies Record<ModeName, Mode>;
|
} as const satisfies Record<ModeName, Mode>;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { ParsedGitHubContext } from "../github/context";
|
import type { ParsedGitHubContext } from "../github/context";
|
||||||
import type { ModeName } from "./registry";
|
|
||||||
|
export type ModeName = "tag" | "agent";
|
||||||
|
|
||||||
export type ModeContext = {
|
export type ModeContext = {
|
||||||
mode: ModeName;
|
mode: ModeName;
|
||||||
@@ -20,9 +21,9 @@ export type ModeData = {
|
|||||||
* Each mode defines its own behavior for trigger detection, prompt generation,
|
* Each mode defines its own behavior for trigger detection, prompt generation,
|
||||||
* and tracking comment creation.
|
* and tracking comment creation.
|
||||||
*
|
*
|
||||||
* Future modes might include:
|
* Current modes include:
|
||||||
* - 'review': Optimized for code reviews without tracking comments
|
* - 'tag': Traditional implementation triggered by mentions/assignments
|
||||||
* - 'freeform': For automation with no trigger checking
|
* - 'agent': For automation with no trigger checking
|
||||||
*/
|
*/
|
||||||
export type Mode = {
|
export type Mode = {
|
||||||
name: ModeName;
|
name: ModeName;
|
||||||
@@ -39,13 +40,12 @@ export type Mode = {
|
|||||||
prepareContext(context: ParsedGitHubContext, data?: ModeData): ModeContext;
|
prepareContext(context: ParsedGitHubContext, data?: ModeData): ModeContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns additional tools that should be allowed for this mode
|
* Returns the list of tools that should be allowed for this mode
|
||||||
* (base GitHub tools are always included)
|
|
||||||
*/
|
*/
|
||||||
getAllowedTools(): string[];
|
getAllowedTools(): string[];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns tools that should be disallowed for this mode
|
* Returns the list of tools that should be disallowed for this mode
|
||||||
*/
|
*/
|
||||||
getDisallowedTools(): string[];
|
getDisallowedTools(): string[];
|
||||||
|
|
||||||
|
|||||||
82
test/modes/agent.test.ts
Normal file
82
test/modes/agent.test.ts
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
import { describe, test, expect, beforeEach } from "bun:test";
|
||||||
|
import { agentMode } from "../../src/modes/agent";
|
||||||
|
import type { ParsedGitHubContext } from "../../src/github/context";
|
||||||
|
import { createMockContext } from "../mockContext";
|
||||||
|
|
||||||
|
describe("Agent Mode", () => {
|
||||||
|
let mockContext: ParsedGitHubContext;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
mockContext = createMockContext({
|
||||||
|
eventName: "workflow_dispatch",
|
||||||
|
isPR: false,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("agent mode has correct properties and behavior", () => {
|
||||||
|
// Basic properties
|
||||||
|
expect(agentMode.name).toBe("agent");
|
||||||
|
expect(agentMode.description).toBe(
|
||||||
|
"Automation mode that always runs without trigger checking",
|
||||||
|
);
|
||||||
|
expect(agentMode.shouldCreateTrackingComment()).toBe(false);
|
||||||
|
|
||||||
|
// Tool methods return empty arrays
|
||||||
|
expect(agentMode.getAllowedTools()).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", () => {
|
||||||
|
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);
|
||||||
|
|
||||||
|
expect(context.mode).toBe("agent");
|
||||||
|
expect(context.githubContext).toBe(mockContext);
|
||||||
|
expect(context.commentId).toBeUndefined();
|
||||||
|
expect(context.baseBranch).toBeUndefined();
|
||||||
|
expect(context.claudeBranch).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("agent mode triggers for all event types", () => {
|
||||||
|
const events = [
|
||||||
|
"push",
|
||||||
|
"schedule",
|
||||||
|
"workflow_dispatch",
|
||||||
|
"repository_dispatch",
|
||||||
|
"issue_comment",
|
||||||
|
"pull_request",
|
||||||
|
];
|
||||||
|
|
||||||
|
events.forEach((eventName) => {
|
||||||
|
const context = createMockContext({ eventName, isPR: false });
|
||||||
|
expect(agentMode.shouldTrigger(context)).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
import { describe, test, expect } from "bun:test";
|
import { describe, test, expect } from "bun:test";
|
||||||
import { getMode, isValidMode, type ModeName } from "../../src/modes/registry";
|
import { getMode, isValidMode } from "../../src/modes/registry";
|
||||||
|
import type { ModeName } from "../../src/modes/types";
|
||||||
import { tagMode } from "../../src/modes/tag";
|
import { tagMode } from "../../src/modes/tag";
|
||||||
|
import { agentMode } from "../../src/modes/agent";
|
||||||
|
|
||||||
describe("Mode Registry", () => {
|
describe("Mode Registry", () => {
|
||||||
test("getMode returns tag mode by default", () => {
|
test("getMode returns tag mode by default", () => {
|
||||||
@@ -9,20 +11,26 @@ describe("Mode Registry", () => {
|
|||||||
expect(mode.name).toBe("tag");
|
expect(mode.name).toBe("tag");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("getMode returns agent mode", () => {
|
||||||
|
const mode = getMode("agent");
|
||||||
|
expect(mode).toBe(agentMode);
|
||||||
|
expect(mode.name).toBe("agent");
|
||||||
|
});
|
||||||
|
|
||||||
test("getMode throws error for invalid mode", () => {
|
test("getMode throws error for invalid mode", () => {
|
||||||
const invalidMode = "invalid" as unknown as ModeName;
|
const invalidMode = "invalid" as unknown as ModeName;
|
||||||
expect(() => getMode(invalidMode)).toThrow(
|
expect(() => getMode(invalidMode)).toThrow(
|
||||||
"Invalid mode 'invalid'. Valid modes are: 'tag'. Please check your workflow configuration.",
|
"Invalid mode 'invalid'. Valid modes are: 'tag', 'agent'. Please check your workflow configuration.",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("isValidMode returns true for tag mode", () => {
|
test("isValidMode returns true for all valid modes", () => {
|
||||||
expect(isValidMode("tag")).toBe(true);
|
expect(isValidMode("tag")).toBe(true);
|
||||||
|
expect(isValidMode("agent")).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("isValidMode returns false for invalid mode", () => {
|
test("isValidMode returns false for invalid mode", () => {
|
||||||
expect(isValidMode("invalid")).toBe(false);
|
expect(isValidMode("invalid")).toBe(false);
|
||||||
expect(isValidMode("review")).toBe(false);
|
expect(isValidMode("review")).toBe(false);
|
||||||
expect(isValidMode("freeform")).toBe(false);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user