mirror of
https://github.com/anthropics/claude-code-action.git
synced 2026-01-22 22:44:13 +08:00
feat: merge prepare and run steps into unified entry point
Consolidates the two-step architecture (prepare → run) into a single step to eliminate file-based and output-based communication. Changes: - Add base-action/src/lib.ts with exports for main action import - Modify run-claude-sdk.ts to accept prompt string directly and return result - Add generatePromptContent() that returns prompt without file I/O - Update mode prepare() to return promptContent in result - Create src/entrypoints/run.ts as unified entry point - Update action.yml to use single Run Claude Code step - Update output references from steps.prepare to steps.claude-code Benefits: - No file I/O for prompt - data stays in memory - No step output parsing - direct function returns - Simpler debugging - single entry point - Faster execution - no subprocess overhead - Type safety - TypeScript across the boundary 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
102
action.yml
102
action.yml
@@ -120,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 }}
|
||||||
@@ -152,44 +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 }}
|
|
||||||
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
|
||||||
@@ -223,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"
|
||||||
|
|
||||||
@@ -287,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
|
||||||
@@ -330,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
|
||||||
|
|||||||
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);
|
||||||
|
|||||||
@@ -841,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,
|
||||||
@@ -848,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();
|
||||||
|
}
|
||||||
@@ -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 = {
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user