Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
9a66562
feat: implement Claude Code GitHub Action v1.0 with auto-detection an…
km-anthropic Aug 6, 2025
da182b6
test + formatting fixes
km-anthropic Aug 7, 2025
acbef8d
feat: simplify to two modes (tag and agent) for v1.0
km-anthropic Aug 7, 2025
65896ab
fix: address PR review comments for v1.0 simplification
km-anthropic Aug 7, 2025
1846b19
Merge branch 'main' into v1-dev
km-anthropic Aug 7, 2025
18bb011
chore: remove unused js-yaml dependencies
km-anthropic Aug 7, 2025
cc07dbf
fix: remove experimental-review mode reference from MCP config
km-anthropic Aug 7, 2025
36c720c
prettify
km-anthropic Aug 7, 2025
dfcaac8
feat: add claudeArgs input for direct CLI argument passing
km-anthropic Aug 7, 2025
b6238ad
refactor: use industry-standard shell-quote for argument parsing
km-anthropic Aug 7, 2025
e2bdca6
bun format
km-anthropic Aug 7, 2025
a7759cf
feat: add claudeArgs input for direct CLI argument passing
km-anthropic Aug 7, 2025
f2775d6
format
km-anthropic Aug 7, 2025
f592586
refactor: complete v1.0 simplification by removing all legacy inputs
km-anthropic Aug 8, 2025
f407f21
fix: update MCP server tests after removing additionalPermissions
km-anthropic Aug 8, 2025
ed42f1a
model version update
km-anthropic Aug 8, 2025
90461a9
Merge branch 'main' of https://github.com/anthropics/claude-code-acti…
km-anthropic Aug 8, 2025
3d480aa
Merge branch 'main' of https://github.com/anthropics/claude-code-acti…
km-anthropic Aug 8, 2025
450e1a8
Update package json
km-anthropic Aug 8, 2025
e2aee89
remove deprecated workflow file (tests features we no longer support)
km-anthropic Aug 8, 2025
1b4fc38
Simplify agent mode and re-add additional_permissions input
km-anthropic Aug 8, 2025
5bdb1e4
Fix MCP config not being passed to Claude CLI
km-anthropic Aug 8, 2025
d5fbc80
Fix MCP tool availability and shell escaping in tag mode
km-anthropic Aug 11, 2025
c93188b
Merge branch 'main' into v1-dev
km-anthropic Aug 11, 2025
c7801e9
bun format
km-anthropic Aug 11, 2025
65d9b31
tests, typecheck, format
km-anthropic Aug 11, 2025
0e90e18
registry test update
km-anthropic Aug 11, 2025
d7a5b00
Update agent mode to have github server as a default
km-anthropic Aug 11, 2025
c03f13d
Fix agent mode to include GitHub MCP server with proper token
km-anthropic Aug 11, 2025
8084086
Simplify review workflow - prevent multiple submissions
km-anthropic Aug 11, 2025
55e9436
Add GitHub MCP server and context prefix to agent mode
km-anthropic Aug 12, 2025
86e2835
Delete .github/workflows/claude-auto-review-test.yml
km-anthropic Aug 12, 2025
24433f3
Remove github_comment and inline_comment servers from agent mode defa…
km-anthropic Aug 12, 2025
30fb4ed
Remove all default MCP servers from agent mode
km-anthropic Aug 12, 2025
73948c3
Remove GitHub context prefixing and clean up agent mode
km-anthropic Aug 12, 2025
eb146ef
Merge branch 'main' of https://github.com/anthropics/claude-code-acti…
km-anthropic Aug 12, 2025
e3b5697
Add GitHub MCP support to agent mode
km-anthropic Aug 13, 2025
d24561d
Format code with prettier
km-anthropic Aug 13, 2025
8c230f7
Fix agent mode test to expect branch values
km-anthropic Aug 13, 2025
0ac14b0
Fix agent test to handle dynamic branch names from environment
km-anthropic Aug 13, 2025
c13c2af
Better fix: Control environment variables in agent test for predictab…
km-anthropic Aug 13, 2025
ab7f1d6
minor formatting
km-anthropic Aug 13, 2025
d91030d
Simplify MCP configuration to use multiple --mcp-config flags
km-anthropic Aug 13, 2025
632f04b
feat: Copy project subagents to Claude runtime environment
km-anthropic Aug 14, 2025
30530c9
formatting
km-anthropic Aug 14, 2025
9613b21
Merge branch 'main' of https://github.com/anthropics/claude-code-acti…
km-anthropic Aug 19, 2025
4ec65ed
Add auto-fix CI workflows with slash command and inline approaches
km-anthropic Aug 19, 2025
8d32355
Add workflow_run event support and auto-fix CI workflows
km-anthropic Aug 19, 2025
91034c2
Use proper WorkflowRunEvent type instead of any
km-anthropic Aug 19, 2025
52736c6
bun formatting
km-anthropic Aug 19, 2025
bf04905
Remove auto-fix workflows and commands from v1-dev
km-anthropic Aug 19, 2025
c72a45a
feat: Expose GitHub token as action output for external use
katchu11 Aug 20, 2025
130874e
Debug: Add logging and always output github_token in prepare step
katchu11 Aug 20, 2025
b12deea
Fix: Add git authentication to agent mode
katchu11 Aug 20, 2025
2ad58b1
minor bun format
katchu11 Aug 20, 2025
b69e4f8
Merge branch 'main' of https://github.com/anthropics/claude-code-acti…
katchu11 Aug 20, 2025
ccac9ce
Merge branch 'v1-dev' of https://github.com/anthropics/claude-code-ac…
katchu11 Aug 20, 2025
604ec53
remove unnecessary file
katchu11 Aug 20, 2025
b0ce9a2
fix: Add branch environment variable support to agent mode for signed…
km-anthropic Aug 21, 2025
7929f4a
feat: Add auto-fix CI workflow examples
km-anthropic Aug 21, 2025
ca512bc
fix: Fix TypeScript error in agent mode git config
km-anthropic Aug 21, 2025
5d72c06
fix: Align agent mode git config with existing patterns
km-anthropic Aug 21, 2025
f5c5d2d
refactor: Use shared configureGitAuth function in agent mode
km-anthropic Aug 21, 2025
31085bc
Merge branch 'main' of https://github.com/anthropics/claude-code-acti…
km-anthropic Aug 21, 2025
f7111a5
feat: Improve error message for 403 permission errors when committing
km-anthropic Aug 21, 2025
b903a6e
docs: Update documentation for v1.0 release (#476)
km-anthropic Aug 21, 2025
10cf63a
feat: Add comprehensive examples for hero use cases
km-anthropic Aug 21, 2025
3c6f220
Merge branch into v1-dev - resolved conflicts in base-action files
km-anthropic Aug 25, 2025
986e40a
refactor: Remove timeout_minutes parameter from action (#482)
km-anthropic Aug 25, 2025
f8c3629
refactor: Remove unused slash commands and agents copying logic
km-anthropic Aug 25, 2025
554fbba
docs: Remove references to timeout_minutes parameter
km-anthropic Aug 25, 2025
31f17eb
fix: Resolve TypeScript errors after removing slash commands
km-anthropic Aug 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: "Claude Code Action Official"
description: "General-purpose Claude agent for GitHub PRs and issues. Can answer questions and implement code changes."
name: "Claude Code Action v1.0"
description: "Flexible GitHub automation platform with Claude. Auto-detects mode based on event type: PR reviews, @claude mentions, or custom automation."
branding:
icon: "at-sign"
color: "orange"
Expand All @@ -24,11 +24,11 @@ inputs:
required: false
default: "claude/"

# Mode configuration
# Mode configuration (v1.0: auto-detected, kept for backward compatibility)
mode:
description: "Execution mode for the action. Valid modes: 'tag' (default - triggered by mentions/assignments), 'agent' (for automation with no trigger checking), 'experimental-review' (experimental mode for code reviews with inline comments and suggestions)"
description: "DEPRECATED in v1.0: Mode is now auto-detected. Review mode for PRs, Tag mode for @claude mentions, Agent mode for automation."
required: false
default: "tag"
default: ""

# Claude Code configuration
model:
Expand All @@ -52,12 +52,16 @@ inputs:
description: "Additional custom instructions to include in the prompt for Claude"
required: false
default: ""
direct_prompt:
description: "Direct instruction for Claude (bypasses normal trigger detection)"
prompt:
description: "Instructions for Claude. Can be a direct prompt, slash command (e.g. /review), or custom template. Replaces override_prompt and direct_prompt from v0.x"
required: false
default: ""
override_prompt:
description: "Complete replacement of Claude's prompt with custom template (supports variable substitution)"
description: "DEPRECATED: Use 'prompt' instead. Kept for backward compatibility."
required: false
default: ""
direct_prompt:
description: "DEPRECATED: Use 'prompt' instead. Kept for backward compatibility."
required: false
default: ""
mcp_config:
Expand Down Expand Up @@ -144,6 +148,7 @@ runs:
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/prepare.ts
env:
MODE: ${{ inputs.mode }}
PROMPT: ${{ inputs.prompt }}
TRIGGER_PHRASE: ${{ inputs.trigger_phrase }}
ASSIGNEE_TRIGGER: ${{ inputs.assignee_trigger }}
LABEL_TRIGGER: ${{ inputs.label_trigger }}
Expand Down
8 changes: 8 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
"@octokit/graphql": "^8.2.2",
"@octokit/rest": "^21.1.1",
"@octokit/webhooks-types": "^7.6.1",
"@types/js-yaml": "^4.0.9",
"js-yaml": "^4.1.0",
"node-fetch": "^3.3.2",
"zod": "^3.24.4"
},
Expand Down
66 changes: 55 additions & 11 deletions src/create-prompt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import * as core from "@actions/core";
import { writeFile, mkdir } from "fs/promises";
import type { FetchDataResult } from "../github/data/fetcher";
import { resolveSlashCommand } from "../slash-commands/loader";
import {
formatContext,
formatBody,
Expand Down Expand Up @@ -118,6 +119,7 @@ export function prepareContext(
const customInstructions = context.inputs.customInstructions;
const allowedTools = context.inputs.allowedTools;
const disallowedTools = context.inputs.disallowedTools;
const prompt = context.inputs.prompt; // v1.0: Unified prompt field
const directPrompt = context.inputs.directPrompt;
const overridePrompt = context.inputs.overridePrompt;
const isPR = context.isPR;
Expand Down Expand Up @@ -157,6 +159,7 @@ export function prepareContext(
...(disallowedTools.length > 0 && {
disallowedTools: disallowedTools.join(","),
}),
...(prompt && { prompt }),
...(directPrompt && { directPrompt }),
...(overridePrompt && { overridePrompt }),
...(claudeBranch && { claudeBranch }),
Expand Down Expand Up @@ -524,21 +527,62 @@ function substitutePromptVariables(
return result;
}

export function generatePrompt(
export async function generatePrompt(
context: PreparedContext,
githubData: FetchDataResult,
useCommitSigning: boolean,
mode: Mode,
): string {
if (context.overridePrompt) {
return substitutePromptVariables(
context.overridePrompt,
context,
githubData,
);
): Promise<string> {
// Check for unified prompt field first (v1.0)
let prompt =
context.prompt || context.overridePrompt || context.directPrompt || "";

// Handle slash commands
if (prompt.startsWith("/")) {
const variables = {
repository: context.repository,
pr_number:
context.eventData.isPR && "prNumber" in context.eventData
? context.eventData.prNumber
: "",
issue_number:
!context.eventData.isPR && "issueNumber" in context.eventData
? context.eventData.issueNumber
: "",
branch: context.eventData.claudeBranch || "",
base_branch: context.eventData.baseBranch || "",
trigger_user: context.triggerUsername,
};

const resolved = await resolveSlashCommand(prompt, variables);

// Apply any tools from the slash command
if (resolved.tools && resolved.tools.length > 0) {
const currentAllowedTools = process.env.ALLOWED_TOOLS || "";
const newTools = resolved.tools.join(",");
const combinedTools = currentAllowedTools
? `${currentAllowedTools},${newTools}`
: newTools;
core.exportVariable("ALLOWED_TOOLS", combinedTools);
}

// Apply any settings from the slash command
if (resolved.settings) {
core.exportVariable(
"SLASH_COMMAND_SETTINGS",
JSON.stringify(resolved.settings),
);
}

prompt = resolved.expandedPrompt;
}

// If we have a prompt, use it (with variable substitution)
if (prompt) {
return substitutePromptVariables(prompt, context, githubData);
}

// Use the mode's prompt generator
// Otherwise use the mode's default prompt generator
return mode.generatePrompt(context, githubData, useCommitSigning);
}

Expand Down Expand Up @@ -840,8 +884,8 @@ export async function createPrompt(
recursive: true,
});

// Generate the prompt directly
const promptContent = generatePrompt(
// Generate the prompt directly (now async due to slash commands)
const promptContent = await generatePrompt(
preparedContext,
githubData,
context.inputs.useCommitSigning,
Expand Down
5 changes: 3 additions & 2 deletions src/create-prompt/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ export type CommonFields = {
customInstructions?: string;
allowedTools?: string;
disallowedTools?: string;
directPrompt?: string;
overridePrompt?: string;
prompt?: string; // v1.0: Unified prompt field
directPrompt?: string; // Deprecated
overridePrompt?: string; // Deprecated
};

type PullRequestReviewCommentEvent = {
Expand Down
32 changes: 12 additions & 20 deletions src/entrypoints/prepare.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,26 @@ import { setupGitHubToken } from "../github/token";
import { checkWritePermissions } from "../github/validation/permissions";
import { createOctokit } from "../github/api/client";
import { parseGitHubContext, isEntityContext } from "../github/context";
import { getMode, isValidMode, DEFAULT_MODE } from "../modes/registry";
import type { ModeName } from "../modes/types";
import { getMode } from "../modes/registry";
import { prepare } from "../prepare";

async function run() {
try {
// Step 1: Get mode first to determine authentication method
const modeInput = process.env.MODE || DEFAULT_MODE;
// Parse GitHub context first to enable mode detection
const context = parseGitHubContext();

// Validate mode input
if (!isValidMode(modeInput)) {
throw new Error(`Invalid mode: ${modeInput}`);
}
const validatedMode: ModeName = modeInput;
// Auto-detect mode based on context, with optional override
const modeOverride = process.env.MODE;
const mode = getMode(context, modeOverride);
const modeName = mode.name;

// Step 2: Setup GitHub token based on mode
// Setup GitHub token based on mode
let githubToken: string;
if (validatedMode === "experimental-review") {
// For experimental-review mode, use the default GitHub Action token
if (modeName === "review" || modeName === "experimental-review") {
// For review mode, use the default GitHub Action token
githubToken = process.env.DEFAULT_WORKFLOW_TOKEN || "";
if (!githubToken) {
throw new Error(
"DEFAULT_WORKFLOW_TOKEN not found for experimental-review mode",
);
throw new Error("DEFAULT_WORKFLOW_TOKEN not found for review mode");
}
console.log("Using default GitHub Action token for review mode");
core.setOutput("GITHUB_TOKEN", githubToken);
Expand All @@ -43,9 +39,6 @@ async function run() {
}
const octokit = createOctokit(githubToken);

// Step 2: Parse GitHub context (once for all operations)
const context = parseGitHubContext();

// Step 3: Check write permissions (only for entity contexts)
if (isEntityContext(context)) {
const hasWritePermissions = await checkWritePermissions(
Expand All @@ -59,8 +52,7 @@ async function run() {
}
}

// Step 4: Get mode and check trigger conditions
const mode = getMode(validatedMode, context);
// Check trigger conditions
const containsTrigger = mode.shouldTrigger(context);

// Set output for action.yml to check
Expand Down
28 changes: 17 additions & 11 deletions src/github/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ export type ScheduleEvent = {
};
};
import type { ModeName } from "../modes/types";
import { DEFAULT_MODE, isValidMode } from "../modes/registry";

// Event name constants for better maintainability
const ENTITY_EVENT_NAMES = [
Expand Down Expand Up @@ -63,15 +62,16 @@ type BaseContext = {
};
actor: string;
inputs: {
mode: ModeName;
mode?: ModeName; // Optional for v1.0 backward compatibility
prompt: string; // New unified prompt field
triggerPhrase: string;
assigneeTrigger: string;
labelTrigger: string;
allowedTools: string[];
disallowedTools: string[];
customInstructions: string;
directPrompt: string;
overridePrompt: string;
directPrompt: string; // Deprecated, kept for compatibility
overridePrompt: string; // Deprecated, kept for compatibility
baseBranch?: string;
branchPrefix: string;
useStickyComment: boolean;
Expand Down Expand Up @@ -105,10 +105,10 @@ export type GitHubContext = ParsedGitHubContext | AutomationContext;
export function parseGitHubContext(): GitHubContext {
const context = github.context;

const modeInput = process.env.MODE ?? DEFAULT_MODE;
if (!isValidMode(modeInput)) {
throw new Error(`Invalid mode: ${modeInput}.`);
}
// Mode is optional in v1.0 (auto-detected)
const modeInput = process.env.MODE
? (process.env.MODE as ModeName)
: undefined;

const commonFields = {
runId: process.env.GITHUB_RUN_ID!,
Expand All @@ -120,15 +120,21 @@ export function parseGitHubContext(): GitHubContext {
},
actor: context.actor,
inputs: {
mode: modeInput as ModeName,
mode: modeInput,
// v1.0: Unified prompt field with fallback to legacy fields
prompt:
process.env.PROMPT ||
process.env.OVERRIDE_PROMPT ||
process.env.DIRECT_PROMPT ||
"",
triggerPhrase: process.env.TRIGGER_PHRASE ?? "@claude",
assigneeTrigger: process.env.ASSIGNEE_TRIGGER ?? "",
labelTrigger: process.env.LABEL_TRIGGER ?? "",
allowedTools: parseMultilineInput(process.env.ALLOWED_TOOLS ?? ""),
disallowedTools: parseMultilineInput(process.env.DISALLOWED_TOOLS ?? ""),
customInstructions: process.env.CUSTOM_INSTRUCTIONS ?? "",
directPrompt: process.env.DIRECT_PROMPT ?? "",
overridePrompt: process.env.OVERRIDE_PROMPT ?? "",
directPrompt: process.env.DIRECT_PROMPT ?? "", // Deprecated
overridePrompt: process.env.OVERRIDE_PROMPT ?? "", // Deprecated
baseBranch: process.env.BASE_BRANCH,
branchPrefix: process.env.BRANCH_PREFIX ?? "claude/",
useStickyComment: process.env.USE_STICKY_COMMENT === "true",
Expand Down
Loading