Skip to content

Commit 30cca8b

Browse files
committed
Use Claude to create proper commits for CI fixes
After Claude fixes CI failures, launch another Claude session to review changes and create proper commits with logical groupings and no AI attribution. Then push and continue the --green cycle automatically.
1 parent e218dde commit 30cca8b

File tree

1 file changed

+143
-29
lines changed

1 file changed

+143
-29
lines changed

scripts/claude.mjs

Lines changed: 143 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3351,7 +3351,7 @@ Let's work through this together to get CI passing.`
33513351

33523352
// Keep logs under 2000 chars to avoid context issues
33533353
const truncatedLogs = filteredLogs.length > 2000
3354-
? filteredLogs.substring(0, 2000) + '\n... (truncated)'
3354+
? `${filteredLogs.substring(0, 2000)}\n... (truncated)`
33553355
: filteredLogs
33563356

33573357
const fixPrompt = `Fix CI failures for commit ${currentSha.substring(0, 7)} in ${owner}/${repo}.
@@ -3485,28 +3485,86 @@ Fix all issues by making necessary file changes. Be direct, don't ask questions.
34853485
.join(', ')
34863486
log.substep(`Changed files: ${changedFiles}`)
34873487

3488-
await runCommand('git', ['add', '.'], { cwd: rootPath })
3488+
// Use Claude to create proper commits (logical groupings, no AI attribution)
3489+
const commitPrompt = `Review the changes and create commits with logical groupings.
34893490
3490-
// Commit with descriptive message (no AI attribution per CLAUDE.md)
3491-
const ciFixMessage = `Fix CI failures from run ${lastRunId}`
3492-
const ciCommitArgs = ['commit', '-m', ciFixMessage]
3493-
if (useNoVerify) {
3494-
ciCommitArgs.push('--no-verify')
3491+
Changed files: ${changedFiles}
3492+
3493+
Requirements:
3494+
- Create one or more commits as needed for logical grouping
3495+
- No AI attribution in commit messages
3496+
- Follow conventional commit style from the repo
3497+
- Be concise and descriptive
3498+
3499+
Commit the changes now.`
3500+
3501+
const commitTmpFile = path.join(rootPath, `.claude-commit-${Date.now()}.txt`)
3502+
await fs.writeFile(commitTmpFile, commitPrompt, 'utf8')
3503+
3504+
const commitArgs = prepareClaudeArgs([], opts)
3505+
const commitArgsStr = commitArgs.join(' ')
3506+
const commitCommand = commitArgsStr
3507+
? `${claudeCmd} ${commitArgsStr}`
3508+
: claudeCmd
3509+
3510+
let commitScriptCmd
3511+
if (WIN32) {
3512+
const winptyCheck = await runCommandWithOutput('where', ['winpty'])
3513+
if (winptyCheck.exitCode === 0) {
3514+
commitScriptCmd = `winpty ${commitCommand} < "${commitTmpFile}"`
3515+
} else {
3516+
commitScriptCmd = `${commitCommand} < "${commitTmpFile}"`
3517+
}
3518+
} else {
3519+
commitScriptCmd = `script -q /dev/null sh -c '${commitCommand} < "${commitTmpFile}"'`
34953520
}
3496-
await runCommand('git', ciCommitArgs, { cwd: rootPath })
3497-
await runCommand('git', ['push'], { cwd: rootPath })
3498-
log.done(`Pushed fix commit: ${ciFixMessage}`)
3499-
3500-
// Update SHA and push time for next check
3501-
const newShaResult = await runCommandWithOutput(
3502-
'git',
3503-
['rev-parse', 'HEAD'],
3504-
{
3521+
3522+
const commitExitCode = await new Promise((resolve, _reject) => {
3523+
const child = spawn(commitScriptCmd, [], {
3524+
stdio: 'inherit',
35053525
cwd: rootPath,
3506-
},
3507-
)
3508-
currentSha = newShaResult.stdout.trim()
3509-
pushTime = Date.now()
3526+
shell: true,
3527+
})
3528+
3529+
const sigintHandler = () => {
3530+
child.kill('SIGINT')
3531+
resolve(130)
3532+
}
3533+
process.on('SIGINT', sigintHandler)
3534+
3535+
child.on('exit', code => {
3536+
process.off('SIGINT', sigintHandler)
3537+
resolve(code || 0)
3538+
})
3539+
3540+
child.on('error', () => {
3541+
process.off('SIGINT', sigintHandler)
3542+
resolve(1)
3543+
})
3544+
})
3545+
3546+
try {
3547+
await fs.unlink(commitTmpFile)
3548+
} catch {}
3549+
3550+
if (commitExitCode === 0) {
3551+
// Push the commits
3552+
await runCommand('git', ['push'], { cwd: rootPath })
3553+
log.done('Pushed fix commits')
3554+
3555+
// Update SHA and push time for next check
3556+
const newShaResult = await runCommandWithOutput(
3557+
'git',
3558+
['rev-parse', 'HEAD'],
3559+
{
3560+
cwd: rootPath,
3561+
},
3562+
)
3563+
currentSha = newShaResult.stdout.trim()
3564+
pushTime = Date.now()
3565+
} else {
3566+
log.warn(`Claude commit failed with exit code ${commitExitCode}`)
3567+
}
35103568
}
35113569

35123570
retryCount++
@@ -3733,18 +3791,74 @@ Fix the issue by making necessary file changes. Be direct, don't ask questions.`
37333791
.join(', ')
37343792
log.substep(`Changed files: ${changedFiles}`)
37353793

3736-
await runCommand('git', ['add', '.'], { cwd: rootPath })
3794+
// Use Claude to create proper commits (logical groupings, no AI attribution)
3795+
const commitPrompt = `Review the changes and create commits with logical groupings.
3796+
3797+
Changed files: ${changedFiles}
3798+
3799+
Requirements:
3800+
- Create one or more commits as needed for logical grouping
3801+
- No AI attribution in commit messages
3802+
- Follow conventional commit style from the repo
3803+
- Be concise and descriptive
37373804
3738-
// Commit with descriptive message (no AI attribution per CLAUDE.md)
3739-
const ciFixMessage = `Fix CI failure in ${job.name} (run ${lastRunId})`
3740-
const ciCommitArgs = ['commit', '-m', ciFixMessage]
3741-
if (useNoVerify) {
3742-
ciCommitArgs.push('--no-verify')
3805+
Commit the changes now.`
3806+
3807+
const commitTmpFile = path.join(rootPath, `.claude-commit-${Date.now()}.txt`)
3808+
await fs.writeFile(commitTmpFile, commitPrompt, 'utf8')
3809+
3810+
const commitArgs = prepareClaudeArgs([], opts)
3811+
const commitArgsStr = commitArgs.join(' ')
3812+
const commitCommand = commitArgsStr
3813+
? `${claudeCmd} ${commitArgsStr}`
3814+
: claudeCmd
3815+
3816+
let commitScriptCmd
3817+
if (WIN32) {
3818+
const winptyCheck = await runCommandWithOutput('where', ['winpty'])
3819+
if (winptyCheck.exitCode === 0) {
3820+
commitScriptCmd = `winpty ${commitCommand} < "${commitTmpFile}"`
3821+
} else {
3822+
commitScriptCmd = `${commitCommand} < "${commitTmpFile}"`
3823+
}
3824+
} else {
3825+
commitScriptCmd = `script -q /dev/null sh -c '${commitCommand} < "${commitTmpFile}"'`
37433826
}
3744-
await runCommand('git', ciCommitArgs, { cwd: rootPath })
3745-
log.done(`Committed fix for ${job.name}`)
37463827

3747-
hasPendingCommits = true
3828+
const commitExitCode = await new Promise((resolve, _reject) => {
3829+
const child = spawn(commitScriptCmd, [], {
3830+
stdio: 'inherit',
3831+
cwd: rootPath,
3832+
shell: true,
3833+
})
3834+
3835+
const sigintHandler = () => {
3836+
child.kill('SIGINT')
3837+
resolve(130)
3838+
}
3839+
process.on('SIGINT', sigintHandler)
3840+
3841+
child.on('exit', code => {
3842+
process.off('SIGINT', sigintHandler)
3843+
resolve(code || 0)
3844+
})
3845+
3846+
child.on('error', () => {
3847+
process.off('SIGINT', sigintHandler)
3848+
resolve(1)
3849+
})
3850+
})
3851+
3852+
try {
3853+
await fs.unlink(commitTmpFile)
3854+
} catch {}
3855+
3856+
if (commitExitCode === 0) {
3857+
log.done(`Committed fix for ${job.name}`)
3858+
hasPendingCommits = true
3859+
} else {
3860+
log.warn(`Claude commit failed with exit code ${commitExitCode}`)
3861+
}
37483862
} else {
37493863
log.substep(`No changes to commit for ${job.name}`)
37503864
}

0 commit comments

Comments
 (0)