Compare commits

..

2 Commits

Author SHA1 Message Date
YutaSaito
f6e5597633 fix: specify baseUrl in Octokit (#107) (#108) 2025-06-01 21:07:44 -07:00
Andrew Munsell
0cd44e50dd fix: Update condition for final message in output (#106)
The last message in the Claude Code output does not have a role, but instead has a field "type" set to "result". With the current condition, the duration is never appended to the header of the comments.
2025-06-01 18:33:02 -07:00
5 changed files with 49 additions and 136 deletions

View File

@@ -361,7 +361,6 @@ export function getEventTypeAndContext(envVars: PreparedContext): {
export function generatePrompt( export function generatePrompt(
context: PreparedContext, context: PreparedContext,
githubData: FetchDataResult, githubData: FetchDataResult,
isReusedBranch?: boolean,
): string { ): string {
const { const {
contextData, contextData,
@@ -535,7 +534,7 @@ ${context.directPrompt ? ` - DIRECT INSTRUCTION: A direct instruction was prov
- Use mcp__github_file_ops__commit_files to commit files atomically in a single commit (supports single or multiple files). - Use mcp__github_file_ops__commit_files to commit files atomically in a single commit (supports single or multiple files).
- When pushing changes with this tool and TRIGGER_USERNAME is not "Unknown", include a "Co-authored-by: ${context.triggerUsername} <${context.triggerUsername}@users.noreply.github.com>" line in the commit message.` - When pushing changes with this tool and TRIGGER_USERNAME is not "Unknown", include a "Co-authored-by: ${context.triggerUsername} <${context.triggerUsername}@users.noreply.github.com>" line in the commit message.`
: ` : `
- You are already on the correct branch (${eventData.claudeBranch || "the PR branch"}). Do not create a new branch.${isReusedBranch ? `\n - NOTE: This branch (${eventData.claudeBranch}) was reused from a previous Claude invocation on this issue. It may already contain some work.` : ''} - You are already on the correct branch (${eventData.claudeBranch || "the PR branch"}). Do not create a new branch.
- Push changes directly to the current branch using mcp__github_file_ops__commit_files (works for both new and existing files) - Push changes directly to the current branch using mcp__github_file_ops__commit_files (works for both new and existing files)
- Use mcp__github_file_ops__commit_files to commit files atomically in a single commit (supports single or multiple files). - Use mcp__github_file_ops__commit_files to commit files atomically in a single commit (supports single or multiple files).
- When pushing changes and TRIGGER_USERNAME is not "Unknown", include a "Co-authored-by: ${context.triggerUsername} <${context.triggerUsername}@users.noreply.github.com>" line in the commit message. - When pushing changes and TRIGGER_USERNAME is not "Unknown", include a "Co-authored-by: ${context.triggerUsername} <${context.triggerUsername}@users.noreply.github.com>" line in the commit message.
@@ -642,7 +641,6 @@ export async function createPrompt(
claudeBranch: string | undefined, claudeBranch: string | undefined,
githubData: FetchDataResult, githubData: FetchDataResult,
context: ParsedGitHubContext, context: ParsedGitHubContext,
isReusedBranch?: boolean,
) { ) {
try { try {
const preparedContext = prepareContext( const preparedContext = prepareContext(
@@ -655,7 +653,7 @@ export async function createPrompt(
await mkdir("/tmp/claude-prompts", { recursive: true }); await mkdir("/tmp/claude-prompts", { recursive: true });
// Generate the prompt // Generate the prompt
const promptContent = generatePrompt(preparedContext, githubData, isReusedBranch); const promptContent = generatePrompt(preparedContext, githubData);
// Log the final prompt to console // Log the final prompt to console
console.log("===== FINAL PROMPT ====="); console.log("===== FINAL PROMPT =====");

View File

@@ -81,7 +81,6 @@ async function run() {
branchInfo.claudeBranch, branchInfo.claudeBranch,
githubData, githubData,
context, context,
branchInfo.isReusedBranch,
); );
// Step 11: Get MCP configuration // Step 11: Get MCP configuration

View File

@@ -87,29 +87,13 @@ async function run() {
const currentBody = comment.body ?? ""; const currentBody = comment.body ?? "";
// Check if we need to add branch link for new branches // Check if we need to add branch link for new branches
// For issues, we don't delete branches anymore to allow reuse const { shouldDeleteBranch, branchLink } = await checkAndDeleteEmptyBranch(
const skipBranchDeletion = !context.isPR && claudeBranch; octokit,
owner,
let shouldDeleteBranch = false; repo,
let branchLink = ""; claudeBranch,
baseBranch,
if (skipBranchDeletion) { );
// For issue branches, just add the branch link without checking for deletion
const branchUrl = `${GITHUB_SERVER_URL}/${owner}/${repo}/tree/${claudeBranch}`;
branchLink = `\n[View branch](${branchUrl})`;
console.log(`Keeping issue branch ${claudeBranch} for potential reuse`);
} else {
// For PR branches, use the existing cleanup logic
const result = await checkAndDeleteEmptyBranch(
octokit,
owner,
repo,
claudeBranch,
baseBranch,
);
shouldDeleteBranch = result.shouldDeleteBranch;
branchLink = result.branchLink;
}
// Check if we need to add PR URL when we have a new branch // Check if we need to add PR URL when we have a new branch
let prLink = ""; let prLink = "";
@@ -182,7 +166,7 @@ async function run() {
if (Array.isArray(outputData) && outputData.length > 0) { if (Array.isArray(outputData) && outputData.length > 0) {
const lastElement = outputData[outputData.length - 1]; const lastElement = outputData[outputData.length - 1];
if ( if (
lastElement.role === "system" && lastElement.type === "result" &&
"cost_usd" in lastElement && "cost_usd" in lastElement &&
"duration_ms" in lastElement "duration_ms" in lastElement
) { ) {

View File

@@ -9,7 +9,10 @@ export type Octokits = {
export function createOctokit(token: string): Octokits { export function createOctokit(token: string): Octokits {
return { return {
rest: new Octokit({ auth: token }), rest: new Octokit({
auth: token,
baseUrl: GITHUB_API_URL,
}),
graphql: graphql.defaults({ graphql: graphql.defaults({
baseUrl: GITHUB_API_URL, baseUrl: GITHUB_API_URL,
headers: { headers: {

View File

@@ -17,7 +17,6 @@ export type BranchInfo = {
baseBranch: string; baseBranch: string;
claudeBranch?: string; claudeBranch?: string;
currentBranch: string; currentBranch: string;
isReusedBranch?: boolean;
}; };
export async function setupBranch( export async function setupBranch(
@@ -80,127 +79,57 @@ export async function setupBranch(
// Creating a new branch for either an issue or closed/merged PR // Creating a new branch for either an issue or closed/merged PR
const entityType = isPR ? "pr" : "issue"; const entityType = isPR ? "pr" : "issue";
console.log(
// For issues, check if a Claude branch already exists `Creating new branch for ${entityType} #${entityNumber} from source branch: ${sourceBranch}...`,
let branchToUse: string | null = null; );
let isReusedBranch = false;
if (!isPR) {
// Check for existing Claude branches for this issue
try {
// Use GraphQL to efficiently search for branches with a specific prefix
const query = `
query($owner: String!, $repo: String!, $prefix: String!) {
repository(owner: $owner, name: $repo) {
refs(refPrefix: "refs/heads/", query: $prefix, first: 100) {
nodes {
name
}
}
}
}
`;
const response = await octokits.graphql<{
repository: {
refs: {
nodes: Array<{ name: string }>;
};
};
}>(query, {
owner,
repo,
prefix: `claude/issue-${entityNumber}-`,
});
const branches = response.repository.refs.nodes;
if (branches.length > 0) {
// Use the first matching branch (could be sorted by date in future)
branchToUse = branches[0].name;
isReusedBranch = true;
console.log(`Found existing Claude branch for issue #${entityNumber}: ${branchToUse}`);
}
} catch (error) {
console.error("Error checking for existing branches:", error);
// Continue with new branch creation if check fails
}
}
// If no existing branch found or this is a PR, create a new branch
if (!branchToUse) {
console.log(
`Creating new branch for ${entityType} #${entityNumber} from source branch: ${sourceBranch}...`,
);
const timestamp = new Date() const timestamp = new Date()
.toISOString() .toISOString()
.replace(/[:-]/g, "") .replace(/[:-]/g, "")
.replace(/\.\d{3}Z/, "") .replace(/\.\d{3}Z/, "")
.split("T") .split("T")
.join("_"); .join("_");
branchToUse = `claude/${entityType}-${entityNumber}-${timestamp}`; const newBranch = `claude/${entityType}-${entityNumber}-${timestamp}`;
}
try { try {
if (isReusedBranch) { // Get the SHA of the source branch
// For existing branches, just checkout const sourceBranchRef = await octokits.rest.git.getRef({
console.log(`Checking out existing branch: ${branchToUse}`); owner,
repo,
// Fetch the branch with more depth to allow for context ref: `heads/${sourceBranch}`,
await $`git fetch origin --depth=20 ${branchToUse}`; });
await $`git checkout ${branchToUse}`;
console.log(
`Successfully checked out existing branch: ${branchToUse}`,
);
console.log(
`Note: This is a reused branch from a previous Claude invocation on issue #${entityNumber}`,
);
} else {
// Get the SHA of the source branch
const sourceBranchRef = await octokits.rest.git.getRef({
owner,
repo,
ref: `heads/${sourceBranch}`,
});
const currentSHA = sourceBranchRef.data.object.sha; const currentSHA = sourceBranchRef.data.object.sha;
console.log(`Current SHA: ${currentSHA}`); console.log(`Current SHA: ${currentSHA}`);
// Create branch using GitHub API // Create branch using GitHub API
await octokits.rest.git.createRef({ await octokits.rest.git.createRef({
owner, owner,
repo, repo,
ref: `refs/heads/${branchToUse}`, ref: `refs/heads/${newBranch}`,
sha: currentSHA, sha: currentSHA,
}); });
// Checkout the new branch (shallow fetch for performance) // Checkout the new branch (shallow fetch for performance)
await $`git fetch origin --depth=1 ${branchToUse}`; await $`git fetch origin --depth=1 ${newBranch}`;
await $`git checkout ${branchToUse}`; await $`git checkout ${newBranch}`;
console.log( console.log(
`Successfully created and checked out new branch: ${branchToUse}`, `Successfully created and checked out new branch: ${newBranch}`,
); );
}
// Set outputs for GitHub Actions // Set outputs for GitHub Actions
core.setOutput("CLAUDE_BRANCH", branchToUse); core.setOutput("CLAUDE_BRANCH", newBranch);
core.setOutput("BASE_BRANCH", sourceBranch); core.setOutput("BASE_BRANCH", sourceBranch);
if (isReusedBranch) {
core.setOutput("IS_REUSED_BRANCH", "true");
}
return { return {
baseBranch: sourceBranch, baseBranch: sourceBranch,
claudeBranch: branchToUse, claudeBranch: newBranch,
currentBranch: branchToUse, currentBranch: newBranch,
isReusedBranch,
}; };
} catch (error) { } catch (error) {
console.error("Error setting up branch:", error); console.error("Error creating branch:", error);
process.exit(1); process.exit(1);
} }
} }