mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-23 23:14:13 +08:00
Compare commits
1 Commits
boris/add-
...
feature/me
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e8e8fdc051 |
109
action.yml
109
action.yml
@@ -93,10 +93,6 @@ inputs:
|
|||||||
description: "Force tag mode with tracking comments for pull_request and issue events. Only applicable to pull_request (opened, synchronize, ready_for_review, reopened) and issue (opened, edited, labeled, assigned) events."
|
description: "Force tag mode with tracking comments for pull_request and issue events. Only applicable to pull_request (opened, synchronize, ready_for_review, reopened) and issue (opened, edited, labeled, assigned) events."
|
||||||
required: false
|
required: false
|
||||||
default: "false"
|
default: "false"
|
||||||
include_fix_links:
|
|
||||||
description: "Include 'Fix this' links in PR code review feedback that open Claude Code with context to fix the identified issue"
|
|
||||||
required: false
|
|
||||||
default: "true"
|
|
||||||
path_to_claude_code_executable:
|
path_to_claude_code_executable:
|
||||||
description: "Optional path to a custom Claude Code executable. If provided, skips automatic installation and uses this executable instead. WARNING: Using an older version may cause problems if the action begins taking advantage of new Claude Code features. This input is typically not needed unless you're debugging something specific or have unique needs in your environment."
|
description: "Optional path to a custom Claude Code executable. If provided, skips automatic installation and uses this executable instead. WARNING: Using an older version may cause problems if the action begins taking advantage of new Claude Code features. This input is typically not needed unless you're debugging something specific or have unique needs in your environment."
|
||||||
required: false
|
required: false
|
||||||
@@ -124,10 +120,10 @@ outputs:
|
|||||||
value: ${{ steps.claude-code.outputs.execution_file }}
|
value: ${{ steps.claude-code.outputs.execution_file }}
|
||||||
branch_name:
|
branch_name:
|
||||||
description: "The branch created by Claude Code for this execution"
|
description: "The branch created by Claude Code for this execution"
|
||||||
value: ${{ steps.prepare.outputs.CLAUDE_BRANCH }}
|
value: ${{ steps.claude-code.outputs.CLAUDE_BRANCH }}
|
||||||
github_token:
|
github_token:
|
||||||
description: "The GitHub token used by the action (Claude App token if available)"
|
description: "The GitHub token used by the action (Claude App token if available)"
|
||||||
value: ${{ steps.prepare.outputs.github_token }}
|
value: ${{ steps.claude-code.outputs.GITHUB_TOKEN }}
|
||||||
structured_output:
|
structured_output:
|
||||||
description: "JSON string containing all structured output fields when --json-schema is provided in claude_args. Use fromJSON() to parse: fromJSON(steps.id.outputs.structured_output).field_name"
|
description: "JSON string containing all structured output fields when --json-schema is provided in claude_args. Use fromJSON() to parse: fromJSON(steps.id.outputs.structured_output).field_name"
|
||||||
value: ${{ steps.claude-code.outputs.structured_output }}
|
value: ${{ steps.claude-code.outputs.structured_output }}
|
||||||
@@ -156,45 +152,15 @@ runs:
|
|||||||
echo "$BUN_DIR" >> "$GITHUB_PATH"
|
echo "$BUN_DIR" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
- name: Install Dependencies
|
- name: Install Dependencies
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cd ${GITHUB_ACTION_PATH}
|
|
||||||
bun install
|
|
||||||
|
|
||||||
- name: Prepare action
|
|
||||||
id: prepare
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
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 }}
|
|
||||||
BASE_BRANCH: ${{ inputs.base_branch }}
|
|
||||||
BRANCH_PREFIX: ${{ inputs.branch_prefix }}
|
|
||||||
OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }}
|
|
||||||
ALLOWED_BOTS: ${{ inputs.allowed_bots }}
|
|
||||||
ALLOWED_NON_WRITE_USERS: ${{ inputs.allowed_non_write_users }}
|
|
||||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
|
||||||
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
|
|
||||||
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
|
|
||||||
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
|
|
||||||
BOT_ID: ${{ inputs.bot_id }}
|
|
||||||
BOT_NAME: ${{ inputs.bot_name }}
|
|
||||||
TRACK_PROGRESS: ${{ inputs.track_progress }}
|
|
||||||
INCLUDE_FIX_LINKS: ${{ inputs.include_fix_links }}
|
|
||||||
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
|
|
||||||
CLAUDE_ARGS: ${{ inputs.claude_args }}
|
|
||||||
ALL_INPUTS: ${{ toJson(inputs) }}
|
|
||||||
|
|
||||||
- name: Install Base Action Dependencies
|
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true'
|
|
||||||
shell: bash
|
shell: bash
|
||||||
env:
|
env:
|
||||||
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||||
run: |
|
run: |
|
||||||
|
# Install main action dependencies
|
||||||
|
cd ${GITHUB_ACTION_PATH}
|
||||||
|
bun install
|
||||||
|
|
||||||
|
# Install base-action dependencies
|
||||||
echo "Installing base-action dependencies..."
|
echo "Installing base-action dependencies..."
|
||||||
cd ${GITHUB_ACTION_PATH}/base-action
|
cd ${GITHUB_ACTION_PATH}/base-action
|
||||||
bun install
|
bun install
|
||||||
@@ -203,7 +169,7 @@ runs:
|
|||||||
|
|
||||||
# Install Claude Code if no custom executable is provided
|
# Install Claude Code if no custom executable is provided
|
||||||
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
||||||
CLAUDE_CODE_VERSION="2.0.76"
|
CLAUDE_CODE_VERSION="2.0.74"
|
||||||
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
||||||
for attempt in 1 2 3; do
|
for attempt in 1 2 3; do
|
||||||
echo "Installation attempt $attempt..."
|
echo "Installation attempt $attempt..."
|
||||||
@@ -228,31 +194,46 @@ runs:
|
|||||||
echo "$CLAUDE_DIR" >> "$GITHUB_PATH"
|
echo "$CLAUDE_DIR" >> "$GITHUB_PATH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# Unified step: prepare + setup + run Claude
|
||||||
- name: Run Claude Code
|
- name: Run Claude Code
|
||||||
id: claude-code
|
id: claude-code
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true'
|
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
|
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/run.ts
|
||||||
# Run the base-action
|
|
||||||
bun run ${GITHUB_ACTION_PATH}/base-action/src/index.ts
|
|
||||||
env:
|
env:
|
||||||
# Base-action inputs
|
# Action configuration
|
||||||
CLAUDE_CODE_ACTION: "1"
|
CLAUDE_CODE_ACTION: "1"
|
||||||
INPUT_PROMPT_FILE: ${{ runner.temp }}/claude-prompts/claude-prompt.txt
|
MODE: ${{ inputs.mode }}
|
||||||
|
PROMPT: ${{ inputs.prompt }}
|
||||||
|
TRIGGER_PHRASE: ${{ inputs.trigger_phrase }}
|
||||||
|
ASSIGNEE_TRIGGER: ${{ inputs.assignee_trigger }}
|
||||||
|
LABEL_TRIGGER: ${{ inputs.label_trigger }}
|
||||||
|
BASE_BRANCH: ${{ inputs.base_branch }}
|
||||||
|
BRANCH_PREFIX: ${{ inputs.branch_prefix }}
|
||||||
|
OVERRIDE_GITHUB_TOKEN: ${{ inputs.github_token }}
|
||||||
|
ALLOWED_BOTS: ${{ inputs.allowed_bots }}
|
||||||
|
ALLOWED_NON_WRITE_USERS: ${{ inputs.allowed_non_write_users }}
|
||||||
|
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||||
|
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
|
||||||
|
DEFAULT_WORKFLOW_TOKEN: ${{ github.token }}
|
||||||
|
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
|
||||||
|
BOT_ID: ${{ inputs.bot_id }}
|
||||||
|
BOT_NAME: ${{ inputs.bot_name }}
|
||||||
|
TRACK_PROGRESS: ${{ inputs.track_progress }}
|
||||||
|
ADDITIONAL_PERMISSIONS: ${{ inputs.additional_permissions }}
|
||||||
|
CLAUDE_ARGS: ${{ inputs.claude_args }}
|
||||||
|
ALL_INPUTS: ${{ toJson(inputs) }}
|
||||||
|
|
||||||
|
# Base-action inputs
|
||||||
INPUT_SETTINGS: ${{ inputs.settings }}
|
INPUT_SETTINGS: ${{ inputs.settings }}
|
||||||
INPUT_CLAUDE_ARGS: ${{ steps.prepare.outputs.claude_args }}
|
INPUT_CLAUDE_ARGS: ${{ inputs.claude_args }}
|
||||||
INPUT_EXPERIMENTAL_SLASH_COMMANDS_DIR: ${{ github.action_path }}/slash-commands
|
INPUT_EXPERIMENTAL_SLASH_COMMANDS_DIR: ${{ github.action_path }}/slash-commands
|
||||||
INPUT_ACTION_INPUTS_PRESENT: ${{ steps.prepare.outputs.action_inputs_present }}
|
|
||||||
INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||||
INPUT_PATH_TO_BUN_EXECUTABLE: ${{ inputs.path_to_bun_executable }}
|
INPUT_PATH_TO_BUN_EXECUTABLE: ${{ inputs.path_to_bun_executable }}
|
||||||
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
INPUT_SHOW_FULL_OUTPUT: ${{ inputs.show_full_output }}
|
||||||
INPUT_PLUGINS: ${{ inputs.plugins }}
|
INPUT_PLUGINS: ${{ inputs.plugins }}
|
||||||
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
INPUT_PLUGIN_MARKETPLACES: ${{ inputs.plugin_marketplaces }}
|
||||||
|
|
||||||
# Model configuration
|
|
||||||
GITHUB_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
|
||||||
GH_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
|
||||||
NODE_VERSION: ${{ env.NODE_VERSION }}
|
NODE_VERSION: ${{ env.NODE_VERSION }}
|
||||||
DETAILED_PERMISSION_MESSAGES: "1"
|
DETAILED_PERMISSION_MESSAGES: "1"
|
||||||
|
|
||||||
@@ -292,33 +273,33 @@ runs:
|
|||||||
ANTHROPIC_DEFAULT_OPUS_MODEL: ${{ env.ANTHROPIC_DEFAULT_OPUS_MODEL }}
|
ANTHROPIC_DEFAULT_OPUS_MODEL: ${{ env.ANTHROPIC_DEFAULT_OPUS_MODEL }}
|
||||||
|
|
||||||
- name: Update comment with job link
|
- name: Update comment with job link
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true' && steps.prepare.outputs.claude_comment_id && always()
|
if: steps.claude-code.outputs.contains_trigger == 'true' && steps.claude-code.outputs.claude_comment_id && always()
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/update-comment-link.ts
|
bun run ${GITHUB_ACTION_PATH}/src/entrypoints/update-comment-link.ts
|
||||||
env:
|
env:
|
||||||
REPOSITORY: ${{ github.repository }}
|
REPOSITORY: ${{ github.repository }}
|
||||||
PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
PR_NUMBER: ${{ github.event.issue.number || github.event.pull_request.number }}
|
||||||
CLAUDE_COMMENT_ID: ${{ steps.prepare.outputs.claude_comment_id }}
|
CLAUDE_COMMENT_ID: ${{ steps.claude-code.outputs.claude_comment_id }}
|
||||||
GITHUB_RUN_ID: ${{ github.run_id }}
|
GITHUB_RUN_ID: ${{ github.run_id }}
|
||||||
GITHUB_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
GITHUB_TOKEN: ${{ steps.claude-code.outputs.GITHUB_TOKEN }}
|
||||||
GH_TOKEN: ${{ steps.prepare.outputs.GITHUB_TOKEN }}
|
GH_TOKEN: ${{ steps.claude-code.outputs.GITHUB_TOKEN }}
|
||||||
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
GITHUB_EVENT_NAME: ${{ github.event_name }}
|
||||||
TRIGGER_COMMENT_ID: ${{ github.event.comment.id }}
|
TRIGGER_COMMENT_ID: ${{ github.event.comment.id }}
|
||||||
CLAUDE_BRANCH: ${{ steps.prepare.outputs.CLAUDE_BRANCH }}
|
CLAUDE_BRANCH: ${{ steps.claude-code.outputs.CLAUDE_BRANCH }}
|
||||||
IS_PR: ${{ github.event.issue.pull_request != null || github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review_comment' }}
|
IS_PR: ${{ github.event.issue.pull_request != null || github.event_name == 'pull_request_target' || github.event_name == 'pull_request_review_comment' }}
|
||||||
BASE_BRANCH: ${{ steps.prepare.outputs.BASE_BRANCH }}
|
BASE_BRANCH: ${{ steps.claude-code.outputs.BASE_BRANCH }}
|
||||||
CLAUDE_SUCCESS: ${{ steps.claude-code.outputs.conclusion == 'success' }}
|
CLAUDE_SUCCESS: ${{ steps.claude-code.outputs.conclusion == 'success' }}
|
||||||
OUTPUT_FILE: ${{ steps.claude-code.outputs.execution_file || '' }}
|
OUTPUT_FILE: ${{ steps.claude-code.outputs.execution_file || '' }}
|
||||||
TRIGGER_USERNAME: ${{ github.event.comment.user.login || github.event.issue.user.login || github.event.pull_request.user.login || github.event.sender.login || github.triggering_actor || github.actor || '' }}
|
TRIGGER_USERNAME: ${{ github.event.comment.user.login || github.event.issue.user.login || github.event.pull_request.user.login || github.event.sender.login || github.triggering_actor || github.actor || '' }}
|
||||||
PREPARE_SUCCESS: ${{ steps.prepare.outcome == 'success' }}
|
PREPARE_SUCCESS: ${{ steps.claude-code.outcome == 'success' }}
|
||||||
PREPARE_ERROR: ${{ steps.prepare.outputs.prepare_error || '' }}
|
PREPARE_ERROR: ${{ steps.claude-code.outputs.prepare_error || '' }}
|
||||||
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
|
USE_STICKY_COMMENT: ${{ inputs.use_sticky_comment }}
|
||||||
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
|
USE_COMMIT_SIGNING: ${{ inputs.use_commit_signing }}
|
||||||
TRACK_PROGRESS: ${{ inputs.track_progress }}
|
TRACK_PROGRESS: ${{ inputs.track_progress }}
|
||||||
|
|
||||||
- name: Display Claude Code Report
|
- name: Display Claude Code Report
|
||||||
if: steps.prepare.outputs.contains_trigger == 'true' && steps.claude-code.outputs.execution_file != ''
|
if: steps.claude-code.outputs.contains_trigger == 'true' && steps.claude-code.outputs.execution_file != ''
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
# Try to format the turns, but if it fails, dump the raw JSON
|
# Try to format the turns, but if it fails, dump the raw JSON
|
||||||
@@ -335,12 +316,12 @@ runs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Revoke app token
|
- name: Revoke app token
|
||||||
if: always() && inputs.github_token == '' && steps.prepare.outputs.skipped_due_to_workflow_validation_mismatch != 'true'
|
if: always() && inputs.github_token == '' && steps.claude-code.outputs.skipped_due_to_workflow_validation_mismatch != 'true'
|
||||||
shell: bash
|
shell: bash
|
||||||
run: |
|
run: |
|
||||||
curl -L \
|
curl -L \
|
||||||
-X DELETE \
|
-X DELETE \
|
||||||
-H "Accept: application/vnd.github+json" \
|
-H "Accept: application/vnd.github+json" \
|
||||||
-H "Authorization: Bearer ${{ steps.prepare.outputs.GITHUB_TOKEN }}" \
|
-H "Authorization: Bearer ${{ steps.claude-code.outputs.GITHUB_TOKEN }}" \
|
||||||
-H "X-GitHub-Api-Version: 2022-11-28" \
|
-H "X-GitHub-Api-Version: 2022-11-28" \
|
||||||
${GITHUB_API_URL:-https://api.github.com}/installation/token
|
${GITHUB_API_URL:-https://api.github.com}/installation/token
|
||||||
|
|||||||
@@ -124,7 +124,7 @@ runs:
|
|||||||
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
PATH_TO_CLAUDE_CODE_EXECUTABLE: ${{ inputs.path_to_claude_code_executable }}
|
||||||
run: |
|
run: |
|
||||||
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
if [ -z "$PATH_TO_CLAUDE_CODE_EXECUTABLE" ]; then
|
||||||
CLAUDE_CODE_VERSION="2.0.76"
|
CLAUDE_CODE_VERSION="2.0.74"
|
||||||
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
echo "Installing Claude Code v${CLAUDE_CODE_VERSION}..."
|
||||||
for attempt in 1 2 3; do
|
for attempt in 1 2 3; do
|
||||||
echo "Installation attempt $attempt..."
|
echo "Installation attempt $attempt..."
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"name": "@anthropic-ai/claude-code-base-action",
|
"name": "@anthropic-ai/claude-code-base-action",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1",
|
"@actions/core": "^1.10.1",
|
||||||
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
|
"@anthropic-ai/claude-agent-sdk": "^0.1.74",
|
||||||
"shell-quote": "^1.8.3",
|
"shell-quote": "^1.8.3",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -27,7 +27,7 @@
|
|||||||
|
|
||||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||||
|
|
||||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.76", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1 || ^4.0.0" } }, "sha512-s7RvpXoFaLXLG7A1cJBAPD8ilwOhhc/12fb5mJXRuD561o4FmPtQ+WRfuy9akMmrFRfLsKv8Ornw3ClGAPL2fw=="],
|
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.74", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1 || ^4.0.0" } }, "sha512-d6H3Oo625WAG3BrBFKJsuSshi4f0amc0kTJTm83LRPPFxn9kfq58FX4Oxxt+RUD9N3QumW9sQSEDnri20/F4qQ=="],
|
||||||
|
|
||||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1",
|
"@actions/core": "^1.10.1",
|
||||||
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
|
"@anthropic-ai/claude-agent-sdk": "^0.1.74",
|
||||||
"shell-quote": "^1.8.3"
|
"shell-quote": "^1.8.3"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
13
base-action/src/lib.ts
Normal file
13
base-action/src/lib.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/**
|
||||||
|
* Library exports for the base-action.
|
||||||
|
* These functions can be imported directly by the main action
|
||||||
|
* to avoid file/output-based communication between steps.
|
||||||
|
*/
|
||||||
|
|
||||||
|
export { runClaudeWithSdk, runClaudeWithSdkFromFile } from "./run-claude-sdk";
|
||||||
|
export type { RunClaudeResult, PromptInput } from "./run-claude-sdk";
|
||||||
|
export { setupClaudeCodeSettings } from "./setup-claude-code-settings";
|
||||||
|
export { installPlugins } from "./install-plugins";
|
||||||
|
export { parseSdkOptions } from "./parse-sdk-options";
|
||||||
|
export type { ClaudeOptions } from "./run-claude";
|
||||||
|
export type { ParsedSdkOptions } from "./parse-sdk-options";
|
||||||
@@ -9,6 +9,18 @@ import type { ParsedSdkOptions } from "./parse-sdk-options";
|
|||||||
|
|
||||||
const EXECUTION_FILE = `${process.env.RUNNER_TEMP}/claude-execution-output.json`;
|
const EXECUTION_FILE = `${process.env.RUNNER_TEMP}/claude-execution-output.json`;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of running Claude via SDK
|
||||||
|
*/
|
||||||
|
export type RunClaudeResult = {
|
||||||
|
success: boolean;
|
||||||
|
executionFile: string;
|
||||||
|
conclusion: "success" | "failure";
|
||||||
|
structuredOutput?: string;
|
||||||
|
sessionId?: string;
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sanitizes SDK output to match CLI sanitization behavior
|
* Sanitizes SDK output to match CLI sanitization behavior
|
||||||
*/
|
*/
|
||||||
@@ -57,13 +69,31 @@ function sanitizeSdkOutput(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Run Claude using the Agent SDK
|
* Input for runClaudeWithSdk - either a prompt string or file path
|
||||||
|
*/
|
||||||
|
export type PromptInput =
|
||||||
|
| { type: "string"; prompt: string }
|
||||||
|
| { type: "file"; promptPath: string };
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run Claude using the Agent SDK.
|
||||||
|
*
|
||||||
|
* @param promptInput - Either a direct prompt string or path to prompt file
|
||||||
|
* @param parsedOptions - Parsed SDK options
|
||||||
|
* @param options - Additional options
|
||||||
|
* @param options.setOutputs - Whether to set GitHub Action outputs (default: true for backwards compat)
|
||||||
|
* @returns Result of the execution
|
||||||
*/
|
*/
|
||||||
export async function runClaudeWithSdk(
|
export async function runClaudeWithSdk(
|
||||||
promptPath: string,
|
promptInput: PromptInput,
|
||||||
{ sdkOptions, showFullOutput, hasJsonSchema }: ParsedSdkOptions,
|
{ sdkOptions, showFullOutput, hasJsonSchema }: ParsedSdkOptions,
|
||||||
): Promise<void> {
|
{ setOutputs = true }: { setOutputs?: boolean } = {},
|
||||||
const prompt = await readFile(promptPath, "utf-8");
|
): Promise<RunClaudeResult> {
|
||||||
|
// Get prompt from string or file
|
||||||
|
const prompt =
|
||||||
|
promptInput.type === "string"
|
||||||
|
? promptInput.prompt
|
||||||
|
: await readFile(promptInput.promptPath, "utf-8");
|
||||||
|
|
||||||
if (!showFullOutput) {
|
if (!showFullOutput) {
|
||||||
console.log(
|
console.log(
|
||||||
@@ -74,7 +104,13 @@ export async function runClaudeWithSdk(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`Running Claude with prompt from file: ${promptPath}`);
|
if (promptInput.type === "file") {
|
||||||
|
console.log(
|
||||||
|
`Running Claude with prompt from file: ${promptInput.promptPath}`,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
console.log(`Running Claude with prompt string (${prompt.length} chars)`);
|
||||||
|
}
|
||||||
// Log SDK options without env (which could contain sensitive data)
|
// Log SDK options without env (which could contain sensitive data)
|
||||||
const { env, ...optionsToLog } = sdkOptions;
|
const { env, ...optionsToLog } = sdkOptions;
|
||||||
console.log("SDK options:", JSON.stringify(optionsToLog, null, 2));
|
console.log("SDK options:", JSON.stringify(optionsToLog, null, 2));
|
||||||
@@ -97,27 +133,47 @@ export async function runClaudeWithSdk(
|
|||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("SDK execution error:", error);
|
console.error("SDK execution error:", error);
|
||||||
core.setOutput("conclusion", "failure");
|
if (setOutputs) {
|
||||||
process.exit(1);
|
core.setOutput("conclusion", "failure");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
executionFile: EXECUTION_FILE,
|
||||||
|
conclusion: "failure",
|
||||||
|
error: String(error),
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write execution file
|
// Write execution file
|
||||||
try {
|
try {
|
||||||
await writeFile(EXECUTION_FILE, JSON.stringify(messages, null, 2));
|
await writeFile(EXECUTION_FILE, JSON.stringify(messages, null, 2));
|
||||||
console.log(`Log saved to ${EXECUTION_FILE}`);
|
console.log(`Log saved to ${EXECUTION_FILE}`);
|
||||||
core.setOutput("execution_file", EXECUTION_FILE);
|
if (setOutputs) {
|
||||||
|
core.setOutput("execution_file", EXECUTION_FILE);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.warning(`Failed to write execution file: ${error}`);
|
core.warning(`Failed to write execution file: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!resultMessage) {
|
if (!resultMessage) {
|
||||||
core.setOutput("conclusion", "failure");
|
if (setOutputs) {
|
||||||
|
core.setOutput("conclusion", "failure");
|
||||||
|
}
|
||||||
core.error("No result message received from Claude");
|
core.error("No result message received from Claude");
|
||||||
process.exit(1);
|
return {
|
||||||
|
success: false,
|
||||||
|
executionFile: EXECUTION_FILE,
|
||||||
|
conclusion: "failure",
|
||||||
|
error: "No result message received from Claude",
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const isSuccess = resultMessage.subtype === "success";
|
const isSuccess = resultMessage.subtype === "success";
|
||||||
core.setOutput("conclusion", isSuccess ? "success" : "failure");
|
if (setOutputs) {
|
||||||
|
core.setOutput("conclusion", isSuccess ? "success" : "failure");
|
||||||
|
}
|
||||||
|
|
||||||
|
let structuredOutput: string | undefined;
|
||||||
|
|
||||||
// Handle structured output
|
// Handle structured output
|
||||||
if (hasJsonSchema) {
|
if (hasJsonSchema) {
|
||||||
@@ -126,26 +182,64 @@ export async function runClaudeWithSdk(
|
|||||||
"structured_output" in resultMessage &&
|
"structured_output" in resultMessage &&
|
||||||
resultMessage.structured_output
|
resultMessage.structured_output
|
||||||
) {
|
) {
|
||||||
const structuredOutputJson = JSON.stringify(
|
structuredOutput = JSON.stringify(resultMessage.structured_output);
|
||||||
resultMessage.structured_output,
|
if (setOutputs) {
|
||||||
);
|
core.setOutput("structured_output", structuredOutput);
|
||||||
core.setOutput("structured_output", structuredOutputJson);
|
}
|
||||||
core.info(
|
core.info(
|
||||||
`Set structured_output with ${Object.keys(resultMessage.structured_output as object).length} field(s)`,
|
`Set structured_output with ${Object.keys(resultMessage.structured_output as object).length} field(s)`,
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
core.setFailed(
|
const errorMsg = `--json-schema was provided but Claude did not return structured_output. Result subtype: ${resultMessage.subtype}`;
|
||||||
`--json-schema was provided but Claude did not return structured_output. Result subtype: ${resultMessage.subtype}`,
|
if (setOutputs) {
|
||||||
);
|
core.setFailed(errorMsg);
|
||||||
core.setOutput("conclusion", "failure");
|
core.setOutput("conclusion", "failure");
|
||||||
process.exit(1);
|
}
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
executionFile: EXECUTION_FILE,
|
||||||
|
conclusion: "failure",
|
||||||
|
error: errorMsg,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isSuccess) {
|
if (!isSuccess) {
|
||||||
if ("errors" in resultMessage && resultMessage.errors) {
|
const errors =
|
||||||
core.error(`Execution failed: ${resultMessage.errors.join(", ")}`);
|
"errors" in resultMessage && resultMessage.errors
|
||||||
}
|
? resultMessage.errors.join(", ")
|
||||||
|
: "Unknown error";
|
||||||
|
core.error(`Execution failed: ${errors}`);
|
||||||
|
return {
|
||||||
|
success: false,
|
||||||
|
executionFile: EXECUTION_FILE,
|
||||||
|
conclusion: "failure",
|
||||||
|
error: errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
success: true,
|
||||||
|
executionFile: EXECUTION_FILE,
|
||||||
|
conclusion: "success",
|
||||||
|
structuredOutput,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wrapper for backwards compatibility - reads prompt from file path and exits on failure
|
||||||
|
*/
|
||||||
|
export async function runClaudeWithSdkFromFile(
|
||||||
|
promptPath: string,
|
||||||
|
parsedOptions: ParsedSdkOptions,
|
||||||
|
): Promise<void> {
|
||||||
|
const result = await runClaudeWithSdk(
|
||||||
|
{ type: "file", promptPath },
|
||||||
|
parsedOptions,
|
||||||
|
{ setOutputs: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { unlink, writeFile, stat, readFile } from "fs/promises";
|
|||||||
import { createWriteStream } from "fs";
|
import { createWriteStream } from "fs";
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
import { parse as parseShellArgs } from "shell-quote";
|
import { parse as parseShellArgs } from "shell-quote";
|
||||||
import { runClaudeWithSdk } from "./run-claude-sdk";
|
import { runClaudeWithSdkFromFile } from "./run-claude-sdk";
|
||||||
import { parseSdkOptions } from "./parse-sdk-options";
|
import { parseSdkOptions } from "./parse-sdk-options";
|
||||||
|
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
@@ -205,7 +205,7 @@ export async function runClaude(promptPath: string, options: ClaudeOptions) {
|
|||||||
|
|
||||||
if (useAgentSdk) {
|
if (useAgentSdk) {
|
||||||
const parsedOptions = parseSdkOptions(options);
|
const parsedOptions = parseSdkOptions(options);
|
||||||
return runClaudeWithSdk(promptPath, parsedOptions);
|
return runClaudeWithSdkFromFile(promptPath, parsedOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
const config = prepareRunConfig(promptPath, options);
|
const config = prepareRunConfig(promptPath, options);
|
||||||
|
|||||||
4
bun.lock
4
bun.lock
@@ -7,7 +7,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1",
|
"@actions/core": "^1.10.1",
|
||||||
"@actions/github": "^6.0.1",
|
"@actions/github": "^6.0.1",
|
||||||
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
|
"@anthropic-ai/claude-agent-sdk": "^0.1.74",
|
||||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||||
"@octokit/graphql": "^8.2.2",
|
"@octokit/graphql": "^8.2.2",
|
||||||
"@octokit/rest": "^21.1.1",
|
"@octokit/rest": "^21.1.1",
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
|
|
||||||
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
"@actions/io": ["@actions/io@1.1.3", "", {}, "sha512-wi9JjgKLYS7U/z8PPbco+PvTb/nRWjeoFlJ1Qer83k/3C5PHQi28hiVdeE2kHXmIL99mQFawx8qt/JPjZilJ8Q=="],
|
||||||
|
|
||||||
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.76", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1 || ^4.0.0" } }, "sha512-s7RvpXoFaLXLG7A1cJBAPD8ilwOhhc/12fb5mJXRuD561o4FmPtQ+WRfuy9akMmrFRfLsKv8Ornw3ClGAPL2fw=="],
|
"@anthropic-ai/claude-agent-sdk": ["@anthropic-ai/claude-agent-sdk@0.1.74", "", { "optionalDependencies": { "@img/sharp-darwin-arm64": "^0.33.5", "@img/sharp-darwin-x64": "^0.33.5", "@img/sharp-linux-arm": "^0.33.5", "@img/sharp-linux-arm64": "^0.33.5", "@img/sharp-linux-x64": "^0.33.5", "@img/sharp-linuxmusl-arm64": "^0.33.5", "@img/sharp-linuxmusl-x64": "^0.33.5", "@img/sharp-win32-x64": "^0.33.5" }, "peerDependencies": { "zod": "^3.24.1 || ^4.0.0" } }, "sha512-d6H3Oo625WAG3BrBFKJsuSshi4f0amc0kTJTm83LRPPFxn9kfq58FX4Oxxt+RUD9N3QumW9sQSEDnri20/F4qQ=="],
|
||||||
|
|
||||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,6 @@ This action supports the following GitHub events ([learn more GitHub event trigg
|
|||||||
- `issues` - When issues are opened or assigned
|
- `issues` - When issues are opened or assigned
|
||||||
- `pull_request_review` - When PR reviews are submitted
|
- `pull_request_review` - When PR reviews are submitted
|
||||||
- `pull_request_review_comment` - When comments are made on PR reviews
|
- `pull_request_review_comment` - When comments are made on PR reviews
|
||||||
- `push` - When commits are pushed to a branch
|
|
||||||
- `repository_dispatch` - Custom events triggered via API
|
- `repository_dispatch` - Custom events triggered via API
|
||||||
- `workflow_dispatch` - Manual workflow triggers (coming soon)
|
- `workflow_dispatch` - Manual workflow triggers (coming soon)
|
||||||
|
|
||||||
@@ -121,42 +120,3 @@ For more control over Claude's behavior, use the `claude_args` input to pass CLI
|
|||||||
```
|
```
|
||||||
|
|
||||||
This provides full access to Claude Code CLI capabilities while maintaining the simplified action interface.
|
This provides full access to Claude Code CLI capabilities while maintaining the simplified action interface.
|
||||||
|
|
||||||
## Auto-Rebase PRs on Push
|
|
||||||
|
|
||||||
Automatically keep PRs up to date when the main branch is updated:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
name: Auto-Rebase PRs
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [main]
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write
|
|
||||||
pull-requests: write
|
|
||||||
id-token: write
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
rebase-prs:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v4
|
|
||||||
with:
|
|
||||||
fetch-depth: 0
|
|
||||||
|
|
||||||
- uses: anthropics/claude-code-action@v1
|
|
||||||
with:
|
|
||||||
prompt: |
|
|
||||||
Find all open PRs that are behind main and merge main into them.
|
|
||||||
For each PR:
|
|
||||||
1. Check out the PR branch
|
|
||||||
2. Merge main into the branch
|
|
||||||
3. Push the updated branch
|
|
||||||
|
|
||||||
Skip any PRs with merge conflicts - just report them.
|
|
||||||
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
|
|
||||||
```
|
|
||||||
|
|
||||||
This workflow triggers whenever commits are pushed to main and uses Claude to automatically merge main into any stale PR branches, keeping them up to date.
|
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ jobs:
|
|||||||
| `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\* | - |
|
||||||
| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - |
|
| `prompt` | Instructions for Claude. Can be a direct prompt or custom template for automation workflows | No | - |
|
||||||
| `track_progress` | Force tag mode with tracking comments. Only works with specific PR/issue events. Preserves GitHub context | No | `false` |
|
| `track_progress` | Force tag mode with tracking comments. Only works with specific PR/issue events. Preserves GitHub context | No | `false` |
|
||||||
| `include_fix_links` | Include 'Fix this' links in PR code review feedback that open Claude Code with context to fix the identified issue | No | `true` |
|
|
||||||
| `claude_args` | Additional [arguments to pass directly to Claude CLI](https://docs.claude.com/en/docs/claude-code/cli-reference#cli-flags) (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | No | "" |
|
| `claude_args` | Additional [arguments to pass directly to Claude CLI](https://docs.claude.com/en/docs/claude-code/cli-reference#cli-flags) (e.g., `--max-turns 10 --model claude-4-0-sonnet-20250805`) | 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 | - |
|
||||||
| `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` |
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@actions/core": "^1.10.1",
|
"@actions/core": "^1.10.1",
|
||||||
"@actions/github": "^6.0.1",
|
"@actions/github": "^6.0.1",
|
||||||
"@anthropic-ai/claude-agent-sdk": "^0.1.76",
|
"@anthropic-ai/claude-agent-sdk": "^0.1.74",
|
||||||
"@modelcontextprotocol/sdk": "^1.11.0",
|
"@modelcontextprotocol/sdk": "^1.11.0",
|
||||||
"@octokit/graphql": "^8.2.2",
|
"@octokit/graphql": "^8.2.2",
|
||||||
"@octokit/rest": "^21.1.1",
|
"@octokit/rest": "^21.1.1",
|
||||||
|
|||||||
@@ -734,13 +734,7 @@ ${eventData.eventName === "issue_comment" || eventData.eventName === "pull_reque
|
|||||||
- Reference specific code sections with file paths and line numbers${eventData.isPR ? `\n - AFTER reading files and analyzing code, you MUST call mcp__github_comment__update_claude_comment to post your review` : ""}
|
- Reference specific code sections with file paths and line numbers${eventData.isPR ? `\n - AFTER reading files and analyzing code, you MUST call mcp__github_comment__update_claude_comment to post your review` : ""}
|
||||||
- Formulate a concise, technical, and helpful response based on the context.
|
- Formulate a concise, technical, and helpful response based on the context.
|
||||||
- Reference specific code with inline formatting or code blocks.
|
- Reference specific code with inline formatting or code blocks.
|
||||||
- Include relevant file paths and line numbers when applicable.${
|
- Include relevant file paths and line numbers when applicable.
|
||||||
eventData.isPR && context.githubContext?.inputs.includeFixLinks
|
|
||||||
? `
|
|
||||||
- When identifying issues that could be fixed, include an inline link: [Fix this →](https://claude.ai/code?q=<URI_ENCODED_INSTRUCTIONS>&repo=${context.repository})
|
|
||||||
The query should be URI-encoded and include enough context for Claude Code to understand and fix the issue (file path, line numbers, branch name, what needs to change).`
|
|
||||||
: ""
|
|
||||||
}
|
|
||||||
- ${eventData.isPR ? `IMPORTANT: Submit your review feedback by updating the Claude comment using mcp__github_comment__update_claude_comment. This will be displayed as your PR review.` : `Remember that this feedback must be posted to the GitHub comment using mcp__github_comment__update_claude_comment.`}
|
- ${eventData.isPR ? `IMPORTANT: Submit your review feedback by updating the Claude comment using mcp__github_comment__update_claude_comment. This will be displayed as your PR review.` : `Remember that this feedback must be posted to the GitHub comment using mcp__github_comment__update_claude_comment.`}
|
||||||
|
|
||||||
B. For Straightforward Changes:
|
B. For Straightforward Changes:
|
||||||
@@ -847,6 +841,78 @@ f. If you are unable to complete certain steps, such as running a linter or test
|
|||||||
return promptContent;
|
return promptContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Result of generating prompt content
|
||||||
|
*/
|
||||||
|
export type PromptResult = {
|
||||||
|
promptContent: string;
|
||||||
|
allowedTools: string;
|
||||||
|
disallowedTools: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate prompt content and tool configurations.
|
||||||
|
* This function can be used directly without side effects (no file writes, no env vars).
|
||||||
|
*/
|
||||||
|
export function generatePromptContent(
|
||||||
|
mode: Mode,
|
||||||
|
modeContext: ModeContext,
|
||||||
|
githubData: FetchDataResult,
|
||||||
|
context: ParsedGitHubContext,
|
||||||
|
): PromptResult {
|
||||||
|
// Prepare the context for prompt generation
|
||||||
|
let claudeCommentId: string = "";
|
||||||
|
if (mode.name === "tag") {
|
||||||
|
if (!modeContext.commentId) {
|
||||||
|
throw new Error(
|
||||||
|
`${mode.name} mode requires a comment ID for prompt generation`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
claudeCommentId = modeContext.commentId.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
const preparedContext = prepareContext(
|
||||||
|
context,
|
||||||
|
claudeCommentId,
|
||||||
|
modeContext.baseBranch,
|
||||||
|
modeContext.claudeBranch,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Generate the prompt directly
|
||||||
|
const promptContent = generatePrompt(
|
||||||
|
preparedContext,
|
||||||
|
githubData,
|
||||||
|
context.inputs.useCommitSigning,
|
||||||
|
mode,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get mode-specific tools
|
||||||
|
const modeAllowedTools = mode.getAllowedTools();
|
||||||
|
const modeDisallowedTools = mode.getDisallowedTools();
|
||||||
|
|
||||||
|
const hasActionsReadPermission = false;
|
||||||
|
const allowedTools = buildAllowedToolsString(
|
||||||
|
modeAllowedTools,
|
||||||
|
hasActionsReadPermission,
|
||||||
|
context.inputs.useCommitSigning,
|
||||||
|
);
|
||||||
|
const disallowedTools = buildDisallowedToolsString(
|
||||||
|
modeDisallowedTools,
|
||||||
|
modeAllowedTools,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
promptContent,
|
||||||
|
allowedTools,
|
||||||
|
disallowedTools,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create prompt and write to file.
|
||||||
|
* This is the legacy function that writes files and sets environment variables.
|
||||||
|
* For the unified step, use generatePromptContent() instead.
|
||||||
|
*/
|
||||||
export async function createPrompt(
|
export async function createPrompt(
|
||||||
mode: Mode,
|
mode: Mode,
|
||||||
modeContext: ModeContext,
|
modeContext: ModeContext,
|
||||||
@@ -854,66 +920,30 @@ export async function createPrompt(
|
|||||||
context: ParsedGitHubContext,
|
context: ParsedGitHubContext,
|
||||||
) {
|
) {
|
||||||
try {
|
try {
|
||||||
// Prepare the context for prompt generation
|
const result = generatePromptContent(
|
||||||
let claudeCommentId: string = "";
|
|
||||||
if (mode.name === "tag") {
|
|
||||||
if (!modeContext.commentId) {
|
|
||||||
throw new Error(
|
|
||||||
`${mode.name} mode requires a comment ID for prompt generation`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
claudeCommentId = modeContext.commentId.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
const preparedContext = prepareContext(
|
|
||||||
context,
|
|
||||||
claudeCommentId,
|
|
||||||
modeContext.baseBranch,
|
|
||||||
modeContext.claudeBranch,
|
|
||||||
);
|
|
||||||
|
|
||||||
await mkdir(`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts`, {
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Generate the prompt directly
|
|
||||||
const promptContent = generatePrompt(
|
|
||||||
preparedContext,
|
|
||||||
githubData,
|
|
||||||
context.inputs.useCommitSigning,
|
|
||||||
mode,
|
mode,
|
||||||
|
modeContext,
|
||||||
|
githubData,
|
||||||
|
context,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Log the final prompt to console
|
// Log the final prompt to console
|
||||||
console.log("===== FINAL PROMPT =====");
|
console.log("===== FINAL PROMPT =====");
|
||||||
console.log(promptContent);
|
console.log(result.promptContent);
|
||||||
console.log("=======================");
|
console.log("=======================");
|
||||||
|
|
||||||
// Write the prompt file
|
// Write the prompt file
|
||||||
|
await mkdir(`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts`, {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
await writeFile(
|
await writeFile(
|
||||||
`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts/claude-prompt.txt`,
|
`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts/claude-prompt.txt`,
|
||||||
promptContent,
|
result.promptContent,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Set allowed tools
|
// Set environment variables
|
||||||
const hasActionsReadPermission = false;
|
core.exportVariable("ALLOWED_TOOLS", result.allowedTools);
|
||||||
|
core.exportVariable("DISALLOWED_TOOLS", result.disallowedTools);
|
||||||
// Get mode-specific tools
|
|
||||||
const modeAllowedTools = mode.getAllowedTools();
|
|
||||||
const modeDisallowedTools = mode.getDisallowedTools();
|
|
||||||
|
|
||||||
const allAllowedTools = buildAllowedToolsString(
|
|
||||||
modeAllowedTools,
|
|
||||||
hasActionsReadPermission,
|
|
||||||
context.inputs.useCommitSigning,
|
|
||||||
);
|
|
||||||
const allDisallowedTools = buildDisallowedToolsString(
|
|
||||||
modeDisallowedTools,
|
|
||||||
modeAllowedTools,
|
|
||||||
);
|
|
||||||
|
|
||||||
core.exportVariable("ALLOWED_TOOLS", allAllowedTools);
|
|
||||||
core.exportVariable("DISALLOWED_TOOLS", allDisallowedTools);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
core.setFailed(`Create prompt failed with error: ${error}`);
|
core.setFailed(`Create prompt failed with error: ${error}`);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
|||||||
186
src/entrypoints/run.ts
Normal file
186
src/entrypoints/run.ts
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
#!/usr/bin/env bun
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unified entry point for the Claude action.
|
||||||
|
*
|
||||||
|
* This combines the prepare and run phases into a single step,
|
||||||
|
* passing data directly in-memory instead of via files and outputs.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import * as core from "@actions/core";
|
||||||
|
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 } from "../modes/registry";
|
||||||
|
import { prepare } from "../prepare";
|
||||||
|
import { collectActionInputsPresence } from "./collect-inputs";
|
||||||
|
import {
|
||||||
|
runClaudeWithSdk,
|
||||||
|
setupClaudeCodeSettings,
|
||||||
|
installPlugins,
|
||||||
|
parseSdkOptions,
|
||||||
|
} from "../../base-action/src/lib";
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
try {
|
||||||
|
// ============================================
|
||||||
|
// PHASE 1: PREPARE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
collectActionInputsPresence();
|
||||||
|
|
||||||
|
// Parse GitHub context first to enable mode detection
|
||||||
|
const context = parseGitHubContext();
|
||||||
|
|
||||||
|
// Auto-detect mode based on context
|
||||||
|
const mode = getMode(context);
|
||||||
|
|
||||||
|
// Setup GitHub token
|
||||||
|
const githubToken = await setupGitHubToken();
|
||||||
|
const octokit = createOctokit(githubToken);
|
||||||
|
|
||||||
|
// Check write permissions (only for entity contexts)
|
||||||
|
if (isEntityContext(context)) {
|
||||||
|
const githubTokenProvided = !!process.env.OVERRIDE_GITHUB_TOKEN;
|
||||||
|
const hasWritePermissions = await checkWritePermissions(
|
||||||
|
octokit.rest,
|
||||||
|
context,
|
||||||
|
context.inputs.allowedNonWriteUsers,
|
||||||
|
githubTokenProvided,
|
||||||
|
);
|
||||||
|
if (!hasWritePermissions) {
|
||||||
|
throw new Error(
|
||||||
|
"Actor does not have write permissions to the repository",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check trigger conditions
|
||||||
|
const containsTrigger = mode.shouldTrigger(context);
|
||||||
|
|
||||||
|
// Debug logging
|
||||||
|
console.log(`Mode: ${mode.name}`);
|
||||||
|
console.log(`Context prompt: ${context.inputs?.prompt || "NO PROMPT"}`);
|
||||||
|
console.log(`Trigger result: ${containsTrigger}`);
|
||||||
|
|
||||||
|
// Set output for action.yml to check
|
||||||
|
core.setOutput("contains_trigger", containsTrigger.toString());
|
||||||
|
|
||||||
|
if (!containsTrigger) {
|
||||||
|
console.log("No trigger found, skipping remaining steps");
|
||||||
|
core.setOutput("GITHUB_TOKEN", githubToken);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run mode.prepare() - returns prompt, commentId, branchInfo, etc.
|
||||||
|
const prepareResult = await prepare({
|
||||||
|
context,
|
||||||
|
octokit,
|
||||||
|
mode,
|
||||||
|
githubToken,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Set outputs that may be needed by subsequent steps
|
||||||
|
core.setOutput("GITHUB_TOKEN", githubToken);
|
||||||
|
if (prepareResult.commentId) {
|
||||||
|
core.setOutput("claude_comment_id", prepareResult.commentId.toString());
|
||||||
|
}
|
||||||
|
core.setOutput(
|
||||||
|
"CLAUDE_BRANCH",
|
||||||
|
prepareResult.branchInfo.claudeBranch || "",
|
||||||
|
);
|
||||||
|
core.setOutput("BASE_BRANCH", prepareResult.branchInfo.baseBranch);
|
||||||
|
|
||||||
|
// Get system prompt from mode if available
|
||||||
|
let appendSystemPrompt: string | undefined;
|
||||||
|
if (mode.getSystemPrompt) {
|
||||||
|
const modeContext = mode.prepareContext(context, {
|
||||||
|
commentId: prepareResult.commentId,
|
||||||
|
baseBranch: prepareResult.branchInfo.baseBranch,
|
||||||
|
claudeBranch: prepareResult.branchInfo.claudeBranch,
|
||||||
|
});
|
||||||
|
appendSystemPrompt = mode.getSystemPrompt(modeContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// PHASE 2: SETUP
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Setup Claude Code settings
|
||||||
|
await setupClaudeCodeSettings(
|
||||||
|
process.env.INPUT_SETTINGS,
|
||||||
|
undefined, // homeDir
|
||||||
|
);
|
||||||
|
|
||||||
|
// Install Claude Code plugins if specified
|
||||||
|
await installPlugins(
|
||||||
|
process.env.INPUT_PLUGIN_MARKETPLACES,
|
||||||
|
process.env.INPUT_PLUGINS,
|
||||||
|
process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE,
|
||||||
|
);
|
||||||
|
|
||||||
|
// ============================================
|
||||||
|
// PHASE 3: EXECUTE
|
||||||
|
// ============================================
|
||||||
|
|
||||||
|
// Get prompt content from prepare result
|
||||||
|
const promptContent = prepareResult.promptContent;
|
||||||
|
if (!promptContent) {
|
||||||
|
throw new Error("No prompt content generated by prepare phase");
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log("===== PROMPT CONTENT =====");
|
||||||
|
console.log(`Prompt length: ${promptContent.length} chars`);
|
||||||
|
console.log("==========================");
|
||||||
|
|
||||||
|
// Build SDK options from environment and prepare result
|
||||||
|
const sdkOptions = parseSdkOptions({
|
||||||
|
claudeArgs: process.env.INPUT_CLAUDE_ARGS,
|
||||||
|
allowedTools:
|
||||||
|
prepareResult.allowedTools || process.env.INPUT_ALLOWED_TOOLS,
|
||||||
|
disallowedTools:
|
||||||
|
prepareResult.disallowedTools || process.env.INPUT_DISALLOWED_TOOLS,
|
||||||
|
maxTurns: process.env.INPUT_MAX_TURNS,
|
||||||
|
mcpConfig: process.env.INPUT_MCP_CONFIG,
|
||||||
|
systemPrompt: process.env.INPUT_SYSTEM_PROMPT,
|
||||||
|
appendSystemPrompt:
|
||||||
|
appendSystemPrompt || process.env.INPUT_APPEND_SYSTEM_PROMPT,
|
||||||
|
claudeEnv: process.env.INPUT_CLAUDE_ENV,
|
||||||
|
fallbackModel: process.env.INPUT_FALLBACK_MODEL,
|
||||||
|
model: process.env.ANTHROPIC_MODEL,
|
||||||
|
pathToClaudeCodeExecutable:
|
||||||
|
process.env.INPUT_PATH_TO_CLAUDE_CODE_EXECUTABLE,
|
||||||
|
showFullOutput: process.env.INPUT_SHOW_FULL_OUTPUT,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Run Claude with prompt string directly
|
||||||
|
const execResult = await runClaudeWithSdk(
|
||||||
|
{ type: "string", prompt: promptContent },
|
||||||
|
sdkOptions,
|
||||||
|
{ setOutputs: true },
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set additional outputs
|
||||||
|
core.setOutput("execution_file", execResult.executionFile);
|
||||||
|
core.setOutput("conclusion", execResult.conclusion);
|
||||||
|
if (execResult.structuredOutput) {
|
||||||
|
core.setOutput("structured_output", execResult.structuredOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!execResult.success) {
|
||||||
|
core.setFailed(`Claude execution failed: ${execResult.error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
||||||
|
core.setFailed(`Run step failed with error: ${errorMessage}`);
|
||||||
|
core.setOutput("prepare_error", errorMessage);
|
||||||
|
core.setOutput("conclusion", "failure");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (import.meta.main) {
|
||||||
|
run();
|
||||||
|
}
|
||||||
@@ -6,7 +6,6 @@ import type {
|
|||||||
PullRequestEvent,
|
PullRequestEvent,
|
||||||
PullRequestReviewEvent,
|
PullRequestReviewEvent,
|
||||||
PullRequestReviewCommentEvent,
|
PullRequestReviewCommentEvent,
|
||||||
PushEvent,
|
|
||||||
WorkflowRunEvent,
|
WorkflowRunEvent,
|
||||||
} from "@octokit/webhooks-types";
|
} from "@octokit/webhooks-types";
|
||||||
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "./constants";
|
import { CLAUDE_APP_BOT_ID, CLAUDE_BOT_LOGIN } from "./constants";
|
||||||
@@ -66,7 +65,6 @@ const AUTOMATION_EVENT_NAMES = [
|
|||||||
"repository_dispatch",
|
"repository_dispatch",
|
||||||
"schedule",
|
"schedule",
|
||||||
"workflow_run",
|
"workflow_run",
|
||||||
"push",
|
|
||||||
] as const;
|
] as const;
|
||||||
|
|
||||||
// Derive types from constants for better maintainability
|
// Derive types from constants for better maintainability
|
||||||
@@ -97,7 +95,6 @@ type BaseContext = {
|
|||||||
allowedBots: string;
|
allowedBots: string;
|
||||||
allowedNonWriteUsers: string;
|
allowedNonWriteUsers: string;
|
||||||
trackProgress: boolean;
|
trackProgress: boolean;
|
||||||
includeFixLinks: boolean;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,15 +111,14 @@ export type ParsedGitHubContext = BaseContext & {
|
|||||||
isPR: boolean;
|
isPR: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Context for automation events (workflow_dispatch, repository_dispatch, schedule, workflow_run, push)
|
// Context for automation events (workflow_dispatch, repository_dispatch, schedule, workflow_run)
|
||||||
export type AutomationContext = BaseContext & {
|
export type AutomationContext = BaseContext & {
|
||||||
eventName: AutomationEventName;
|
eventName: AutomationEventName;
|
||||||
payload:
|
payload:
|
||||||
| WorkflowDispatchEvent
|
| WorkflowDispatchEvent
|
||||||
| RepositoryDispatchEvent
|
| RepositoryDispatchEvent
|
||||||
| ScheduleEvent
|
| ScheduleEvent
|
||||||
| WorkflowRunEvent
|
| WorkflowRunEvent;
|
||||||
| PushEvent;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Union type for all contexts
|
// Union type for all contexts
|
||||||
@@ -154,7 +150,6 @@ export function parseGitHubContext(): GitHubContext {
|
|||||||
allowedBots: process.env.ALLOWED_BOTS ?? "",
|
allowedBots: process.env.ALLOWED_BOTS ?? "",
|
||||||
allowedNonWriteUsers: process.env.ALLOWED_NON_WRITE_USERS ?? "",
|
allowedNonWriteUsers: process.env.ALLOWED_NON_WRITE_USERS ?? "",
|
||||||
trackProgress: process.env.TRACK_PROGRESS === "true",
|
trackProgress: process.env.TRACK_PROGRESS === "true",
|
||||||
includeFixLinks: process.env.INCLUDE_FIX_LINKS === "true",
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -238,13 +233,6 @@ export function parseGitHubContext(): GitHubContext {
|
|||||||
payload: context.payload as unknown as WorkflowRunEvent,
|
payload: context.payload as unknown as WorkflowRunEvent,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
case "push": {
|
|
||||||
return {
|
|
||||||
...commonFields,
|
|
||||||
eventName: "push",
|
|
||||||
payload: context.payload as unknown as PushEvent,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unsupported event type: ${context.eventName}`);
|
throw new Error(`Unsupported event type: ${context.eventName}`);
|
||||||
}
|
}
|
||||||
@@ -286,12 +274,6 @@ export function isIssuesAssignedEvent(
|
|||||||
return isIssuesEvent(context) && context.eventAction === "assigned";
|
return isIssuesEvent(context) && context.eventAction === "assigned";
|
||||||
}
|
}
|
||||||
|
|
||||||
export function isPushEvent(
|
|
||||||
context: GitHubContext,
|
|
||||||
): context is AutomationContext & { payload: PushEvent } {
|
|
||||||
return context.eventName === "push";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Type guard to check if context is an entity context (has entityNumber and isPR)
|
// Type guard to check if context is an entity context (has entityNumber and isPR)
|
||||||
export function isEntityContext(
|
export function isEntityContext(
|
||||||
context: GitHubContext,
|
context: GitHubContext,
|
||||||
|
|||||||
@@ -95,16 +95,15 @@ export const agentMode: Mode = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create prompt directory
|
// Generate prompt content - use the user's prompt directly
|
||||||
await mkdir(`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts`, {
|
|
||||||
recursive: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Write the prompt file - use the user's prompt directly
|
|
||||||
const promptContent =
|
const promptContent =
|
||||||
context.inputs.prompt ||
|
context.inputs.prompt ||
|
||||||
`Repository: ${context.repository.owner}/${context.repository.repo}`;
|
`Repository: ${context.repository.owner}/${context.repository.repo}`;
|
||||||
|
|
||||||
|
// Also write file for backwards compatibility with current flow
|
||||||
|
await mkdir(`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts`, {
|
||||||
|
recursive: true,
|
||||||
|
});
|
||||||
await writeFile(
|
await writeFile(
|
||||||
`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts/claude-prompt.txt`,
|
`${process.env.RUNNER_TEMP || "/tmp"}/claude-prompts/claude-prompt.txt`,
|
||||||
promptContent,
|
promptContent,
|
||||||
@@ -162,6 +161,8 @@ export const agentMode: Mode = {
|
|||||||
claudeBranch: claudeBranch,
|
claudeBranch: claudeBranch,
|
||||||
},
|
},
|
||||||
mcpConfig: ourMcpConfig,
|
mcpConfig: ourMcpConfig,
|
||||||
|
promptContent,
|
||||||
|
// Agent mode doesn't use the same allowed/disallowed tools mechanism as tag mode
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,11 @@ import {
|
|||||||
fetchGitHubData,
|
fetchGitHubData,
|
||||||
extractTriggerTimestamp,
|
extractTriggerTimestamp,
|
||||||
} from "../../github/data/fetcher";
|
} from "../../github/data/fetcher";
|
||||||
import { createPrompt, generateDefaultPrompt } from "../../create-prompt";
|
import {
|
||||||
|
createPrompt,
|
||||||
|
generateDefaultPrompt,
|
||||||
|
generatePromptContent,
|
||||||
|
} from "../../create-prompt";
|
||||||
import { isEntityContext } from "../../github/context";
|
import { isEntityContext } from "../../github/context";
|
||||||
import type { PreparedContext } from "../../create-prompt/types";
|
import type { PreparedContext } from "../../create-prompt/types";
|
||||||
import type { FetchDataResult } from "../../github/data/fetcher";
|
import type { FetchDataResult } from "../../github/data/fetcher";
|
||||||
@@ -104,13 +108,22 @@ export const tagMode: Mode = {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create prompt file
|
// Create prompt
|
||||||
const modeContext = this.prepareContext(context, {
|
const modeContext = this.prepareContext(context, {
|
||||||
commentId,
|
commentId,
|
||||||
baseBranch: branchInfo.baseBranch,
|
baseBranch: branchInfo.baseBranch,
|
||||||
claudeBranch: branchInfo.claudeBranch,
|
claudeBranch: branchInfo.claudeBranch,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Generate prompt content - returns data instead of writing file
|
||||||
|
const promptResult = generatePromptContent(
|
||||||
|
tagMode,
|
||||||
|
modeContext,
|
||||||
|
githubData,
|
||||||
|
context,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Also write file for backwards compatibility with current flow
|
||||||
await createPrompt(tagMode, modeContext, githubData, context);
|
await createPrompt(tagMode, modeContext, githubData, context);
|
||||||
|
|
||||||
const userClaudeArgs = process.env.CLAUDE_ARGS || "";
|
const userClaudeArgs = process.env.CLAUDE_ARGS || "";
|
||||||
@@ -188,6 +201,9 @@ export const tagMode: Mode = {
|
|||||||
commentId,
|
commentId,
|
||||||
branchInfo,
|
branchInfo,
|
||||||
mcpConfig: ourMcpConfig,
|
mcpConfig: ourMcpConfig,
|
||||||
|
promptContent: promptResult.promptContent,
|
||||||
|
allowedTools: promptResult.allowedTools,
|
||||||
|
disallowedTools: promptResult.disallowedTools,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -97,4 +97,10 @@ export type ModeResult = {
|
|||||||
currentBranch: string;
|
currentBranch: string;
|
||||||
};
|
};
|
||||||
mcpConfig: string;
|
mcpConfig: string;
|
||||||
|
/** Generated prompt content for Claude */
|
||||||
|
promptContent?: string;
|
||||||
|
/** Comma-separated list of allowed tools */
|
||||||
|
allowedTools?: string;
|
||||||
|
/** Comma-separated list of disallowed tools */
|
||||||
|
disallowedTools?: string;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,6 +10,12 @@ export type PrepareResult = {
|
|||||||
currentBranch: string;
|
currentBranch: string;
|
||||||
};
|
};
|
||||||
mcpConfig: string;
|
mcpConfig: string;
|
||||||
|
/** Generated prompt content for Claude */
|
||||||
|
promptContent?: string;
|
||||||
|
/** Comma-separated list of allowed tools */
|
||||||
|
allowedTools?: string;
|
||||||
|
/** Comma-separated list of disallowed tools */
|
||||||
|
disallowedTools?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PrepareOptions = {
|
export type PrepareOptions = {
|
||||||
|
|||||||
@@ -37,7 +37,6 @@ describe("prepareMcpConfig", () => {
|
|||||||
allowedBots: "",
|
allowedBots: "",
|
||||||
allowedNonWriteUsers: "",
|
allowedNonWriteUsers: "",
|
||||||
trackProgress: false,
|
trackProgress: false,
|
||||||
includeFixLinks: true,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ const defaultInputs = {
|
|||||||
allowedBots: "",
|
allowedBots: "",
|
||||||
allowedNonWriteUsers: "",
|
allowedNonWriteUsers: "",
|
||||||
trackProgress: false,
|
trackProgress: false,
|
||||||
includeFixLinks: true,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultRepository = {
|
const defaultRepository = {
|
||||||
|
|||||||
@@ -177,6 +177,7 @@ describe("Agent Mode", () => {
|
|||||||
claudeBranch: undefined,
|
claudeBranch: undefined,
|
||||||
},
|
},
|
||||||
mcpConfig: expect.any(String),
|
mcpConfig: expect.any(String),
|
||||||
|
promptContent: expect.any(String),
|
||||||
});
|
});
|
||||||
|
|
||||||
// Clean up
|
// Clean up
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
import { describe, expect, it } from "bun:test";
|
import { describe, expect, it } from "bun:test";
|
||||||
import { detectMode } from "../../src/modes/detector";
|
import { detectMode } from "../../src/modes/detector";
|
||||||
import type { GitHubContext } from "../../src/github/context";
|
import type { GitHubContext } from "../../src/github/context";
|
||||||
import { isPushEvent } from "../../src/github/context";
|
|
||||||
|
|
||||||
describe("detectMode with enhanced routing", () => {
|
describe("detectMode with enhanced routing", () => {
|
||||||
const baseContext = {
|
const baseContext = {
|
||||||
@@ -26,7 +25,6 @@ describe("detectMode with enhanced routing", () => {
|
|||||||
allowedBots: "",
|
allowedBots: "",
|
||||||
allowedNonWriteUsers: "",
|
allowedNonWriteUsers: "",
|
||||||
trackProgress: false,
|
trackProgress: false,
|
||||||
includeFixLinks: true,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -258,65 +256,4 @@ describe("detectMode with enhanced routing", () => {
|
|||||||
expect(detectMode(context)).toBe("tag");
|
expect(detectMode(context)).toBe("tag");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Push Events", () => {
|
|
||||||
it("should use agent mode for push events", () => {
|
|
||||||
const context: GitHubContext = {
|
|
||||||
...baseContext,
|
|
||||||
eventName: "push",
|
|
||||||
payload: {} as any,
|
|
||||||
inputs: { ...baseContext.inputs, prompt: "Merge main into stale PRs" },
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(detectMode(context)).toBe("agent");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should throw error when track_progress is used with push event", () => {
|
|
||||||
const context: GitHubContext = {
|
|
||||||
...baseContext,
|
|
||||||
eventName: "push",
|
|
||||||
payload: {} as any,
|
|
||||||
inputs: { ...baseContext.inputs, trackProgress: true },
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(() => detectMode(context)).toThrow(
|
|
||||||
/track_progress is only supported /,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("isPushEvent type guard", () => {
|
|
||||||
it("should return true for push events", () => {
|
|
||||||
const context: GitHubContext = {
|
|
||||||
...baseContext,
|
|
||||||
eventName: "push",
|
|
||||||
payload: {} as any,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(isPushEvent(context)).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false for non-push events", () => {
|
|
||||||
const issueContext: GitHubContext = {
|
|
||||||
...baseContext,
|
|
||||||
eventName: "issues",
|
|
||||||
eventAction: "opened",
|
|
||||||
payload: { issue: { number: 1, body: "Test" } } as any,
|
|
||||||
entityNumber: 1,
|
|
||||||
isPR: false,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(isPushEvent(issueContext)).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should return false for workflow_dispatch events", () => {
|
|
||||||
const context: GitHubContext = {
|
|
||||||
...baseContext,
|
|
||||||
eventName: "workflow_dispatch",
|
|
||||||
payload: {} as any,
|
|
||||||
};
|
|
||||||
|
|
||||||
expect(isPushEvent(context)).toBe(false);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -60,15 +60,6 @@ describe("Mode Registry", () => {
|
|||||||
expect(mode.name).toBe("agent");
|
expect(mode.name).toBe("agent");
|
||||||
});
|
});
|
||||||
|
|
||||||
test("getMode auto-detects agent for push event", () => {
|
|
||||||
const pushContext = createMockAutomationContext({
|
|
||||||
eventName: "push",
|
|
||||||
});
|
|
||||||
const mode = getMode(pushContext);
|
|
||||||
expect(mode).toBe(agentMode);
|
|
||||||
expect(mode.name).toBe("agent");
|
|
||||||
});
|
|
||||||
|
|
||||||
test("getMode auto-detects agent for repository_dispatch with client_payload", () => {
|
test("getMode auto-detects agent for repository_dispatch with client_payload", () => {
|
||||||
const contextWithPayload = createMockAutomationContext({
|
const contextWithPayload = createMockAutomationContext({
|
||||||
eventName: "repository_dispatch",
|
eventName: "repository_dispatch",
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ describe("checkWritePermissions", () => {
|
|||||||
allowedBots: "",
|
allowedBots: "",
|
||||||
allowedNonWriteUsers: "",
|
allowedNonWriteUsers: "",
|
||||||
trackProgress: false,
|
trackProgress: false,
|
||||||
includeFixLinks: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user