add schedule & workflow dispatch paths. Also make prepare logic conditional

This commit is contained in:
km-anthropic
2025-07-28 11:28:06 -07:00
parent a53ce607e4
commit 999dd8a3b6
13 changed files with 479 additions and 90 deletions

View File

@@ -34,6 +34,16 @@ const BASE_ALLOWED_TOOLS = [
];
const DISALLOWED_TOOLS = ["WebSearch", "WebFetch"];
function getEntityNumberXml(eventData: EventData): string {
if (eventData.isPR && "prNumber" in eventData) {
return `<pr_number>${eventData.prNumber}</pr_number>`;
}
if ("issueNumber" in eventData) {
return `<issue_number>${eventData.issueNumber}</issue_number>`;
}
return "";
}
export function buildAllowedToolsString(
customAllowedTools?: string[],
includeActionsTools: boolean = false,
@@ -125,8 +135,10 @@ export function prepareContext(
const isPR = context.isPR;
// Get PR/Issue number from entityNumber
const prNumber = isPR ? context.entityNumber.toString() : undefined;
const issueNumber = !isPR ? context.entityNumber.toString() : undefined;
const prNumber =
isPR && context.entityNumber ? context.entityNumber.toString() : undefined;
const issueNumber =
!isPR && context.entityNumber ? context.entityNumber.toString() : undefined;
// Extract trigger username and comment data based on event type
let triggerUsername: string | undefined;
@@ -338,6 +350,24 @@ export function prepareContext(
};
break;
case "workflow_dispatch":
eventData = {
eventName: "workflow_dispatch",
isPR: false,
...(baseBranch && { baseBranch }),
...(claudeBranch && { claudeBranch }),
};
break;
case "schedule":
eventData = {
eventName: "schedule",
isPR: false,
...(baseBranch && { baseBranch }),
...(claudeBranch && { claudeBranch }),
};
break;
default:
throw new Error(`Unsupported event type: ${eventName}`);
}
@@ -400,6 +430,18 @@ export function getEventTypeAndContext(envVars: PreparedContext): {
: `pull request event`,
};
case "workflow_dispatch":
return {
eventType: "WORKFLOW_DISPATCH",
triggerContext: `workflow dispatch event`,
};
case "schedule":
return {
eventType: "SCHEDULE",
triggerContext: `scheduled automation event`,
};
default:
throw new Error(`Unexpected event type`);
}
@@ -407,11 +449,12 @@ export function getEventTypeAndContext(envVars: PreparedContext): {
function getCommitInstructions(
eventData: EventData,
githubData: FetchDataResult,
githubData: FetchDataResult | null,
context: PreparedContext,
useCommitSigning: boolean,
): string {
const coAuthorLine =
githubData &&
(githubData.triggerDisplayName ?? context.triggerUsername !== "Unknown")
? `Co-authored-by: ${githubData.triggerDisplayName ?? context.triggerUsername} <${context.triggerUsername}@users.noreply.github.com>`
: "";
@@ -466,8 +509,26 @@ function getCommitInstructions(
function substitutePromptVariables(
template: string,
context: PreparedContext,
githubData: FetchDataResult,
githubData: FetchDataResult | null,
): string {
// Handle automation events without GitHub data
if (!githubData) {
const { eventData } = context;
const variables: Record<string, string> = {
EVENT_TYPE: eventData.eventName,
REPOSITORY: context.repository,
TRIGGER_USERNAME: context.triggerUsername ?? "automation",
CURRENT_BRANCH: eventData.claudeBranch || eventData.baseBranch || "main",
BASE_BRANCH: eventData.baseBranch || "main",
};
return Object.entries(variables).reduce(
(prompt, [key, value]) =>
prompt.replace(new RegExp(`{{${key}}}`, "g"), value),
template,
);
}
const { contextData, comments, reviewData, changedFilesWithSHA } = githubData;
const { eventData } = context;
@@ -528,7 +589,7 @@ function substitutePromptVariables(
export function generatePrompt(
context: PreparedContext,
githubData: FetchDataResult,
githubData: FetchDataResult | null,
useCommitSigning: boolean,
): string {
if (context.overridePrompt) {
@@ -539,6 +600,59 @@ export function generatePrompt(
);
}
const { eventData } = context;
// Handle automation events that don't have GitHub data
if (!githubData) {
// For automation events, we have minimal context
const { eventType, triggerContext } = getEventTypeAndContext(context);
let promptContent = `You are Claude, an AI assistant designed to help with GitHub ${eventData.eventName === "workflow_dispatch" ? "workflow dispatch" : "scheduled automation"} tasks. Think carefully as you analyze the context and respond appropriately. Here's the context for your current task:
<formatted_context>
Repository: ${context.repository}
Event Type: ${eventData.eventName}
Current Branch: ${eventData.claudeBranch || eventData.baseBranch || "main"}
Base Branch: ${eventData.baseBranch || "main"}
Actor: ${context.triggerUsername ?? "automation"}
</formatted_context>
<event_type>${eventType}</event_type>
<is_pr>false</is_pr>
<trigger_context>${triggerContext}</trigger_context>
<repository>${context.repository}</repository>
<trigger_username>${context.triggerUsername ?? "automation"}</trigger_username>
${
context.directPrompt
? `<direct_prompt>
IMPORTANT: The following are direct instructions from the automation workflow:
${sanitizeContent(context.directPrompt)}
</direct_prompt>`
: ""
}
<instructions>
You have been triggered by an automated workflow. Follow these guidelines:
1. **Context**: You are running in an automated context without a specific issue or PR.
2. **Branch**: You are currently on the ${eventData.claudeBranch || eventData.baseBranch || "main"} branch.
3. **Tools**: You have access to file system and Git tools to make changes.
${
useCommitSigning
? "4. **Commits**: Use the MCP file operations server for signed commits."
: "4. **Commits**: You can commit changes directly using Git."
}
5. **Scope**: Focus on the task described${context.directPrompt ? " in the direct_prompt above" : ""}.
Please proceed with the automated task.
</instructions>
${context.customInstructions || ""}`;
return promptContent;
}
const {
contextData,
comments,
@@ -546,11 +660,10 @@ export function generatePrompt(
reviewData,
imageUrlMap,
} = githubData;
const { eventData } = context;
const { eventType, triggerContext } = getEventTypeAndContext(context);
const formattedContext = formatContext(contextData, eventData.isPR);
const formattedContext = formatContext(contextData, eventData.isPR ?? false);
const formattedComments = formatComments(comments, imageUrlMap);
const formattedReviewComments = eventData.isPR
? formatReviewComments(reviewData, imageUrlMap)
@@ -596,17 +709,13 @@ ${eventData.isPR ? formattedChangedFiles || "No files changed" : ""}
</changed_files>${imagesInfo}
<event_type>${eventType}</event_type>
<is_pr>${eventData.isPR ? "true" : "false"}</is_pr>
<is_pr>${(eventData.isPR ?? false) ? "true" : "false"}</is_pr>
<trigger_context>${triggerContext}</trigger_context>
<repository>${context.repository}</repository>
${
eventData.isPR
? `<pr_number>${eventData.prNumber}</pr_number>`
: `<issue_number>${eventData.issueNumber ?? ""}</issue_number>`
}
${getEntityNumberXml(eventData)}
<claude_comment_id>${context.claudeCommentId}</claude_comment_id>
<trigger_username>${context.triggerUsername ?? "Unknown"}</trigger_username>
<trigger_display_name>${githubData.triggerDisplayName ?? context.triggerUsername ?? "Unknown"}</trigger_display_name>
<trigger_display_name>${githubData?.triggerDisplayName ?? context.triggerUsername ?? "Unknown"}</trigger_display_name>
<trigger_phrase>${context.triggerPhrase}</trigger_phrase>
${
(eventData.eventName === "issue_comment" ||
@@ -797,7 +906,7 @@ f. If you are unable to complete certain steps, such as running a linter or test
export async function createPrompt(
mode: Mode,
modeContext: ModeContext,
githubData: FetchDataResult,
githubData: FetchDataResult | null,
context: ParsedGitHubContext,
) {
try {