create-claude-workspace 2.3.13 → 2.3.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/scheduler/loop.mjs +65 -35
- package/dist/scheduler/ui/tui.mjs +2 -2
- package/package.json +1 -1
package/dist/scheduler/loop.mjs
CHANGED
|
@@ -905,40 +905,82 @@ const PR_MAX_POLL_TIME = 30 * 60_000;
|
|
|
905
905
|
async function pollAndMergePR(task, pipeline, branch, platform, projectDir, worktreePath, agents, deps) {
|
|
906
906
|
const { pool, logger, state, onMessage, onSpawnStart, onSpawnEnd } = deps;
|
|
907
907
|
const startTime = Date.now();
|
|
908
|
+
let lastSeenCIStatus = 'none'; // Track CI transitions to detect stale status
|
|
909
|
+
// Initial wait — give CI time to pick up the new push
|
|
910
|
+
logger.info(`[${task.id}] Waiting for CI pipeline to start...`);
|
|
911
|
+
await sleep(PR_POLL_INTERVAL);
|
|
908
912
|
while (Date.now() - startTime < PR_MAX_POLL_TIME) {
|
|
909
913
|
const prStatus = getPRStatus(projectDir, platform, branch);
|
|
910
914
|
if (prStatus.status === 'merged')
|
|
911
915
|
return true;
|
|
912
916
|
if (prStatus.status === 'closed')
|
|
913
917
|
return false;
|
|
914
|
-
//
|
|
915
|
-
if (prStatus.
|
|
918
|
+
// Ready to merge — CI passed and mergeable
|
|
919
|
+
if (prStatus.mergeable) {
|
|
920
|
+
logger.info(`[${task.id}] PR mergeable — merging`);
|
|
921
|
+
const merged = mergePR(projectDir, platform, prStatus.number);
|
|
922
|
+
return merged;
|
|
923
|
+
}
|
|
924
|
+
// CI still running or not started — wait
|
|
925
|
+
if (prStatus.ciStatus === 'pending' || prStatus.ciStatus === 'not-found') {
|
|
926
|
+
if (prStatus.ciStatus !== lastSeenCIStatus) {
|
|
927
|
+
logger.info(`[${task.id}] CI: ${prStatus.ciStatus} — waiting...`);
|
|
928
|
+
lastSeenCIStatus = prStatus.ciStatus;
|
|
929
|
+
}
|
|
930
|
+
await sleep(PR_POLL_INTERVAL);
|
|
931
|
+
continue;
|
|
932
|
+
}
|
|
933
|
+
// CI passed but not mergeable (needs approval, merge checks, etc.)
|
|
934
|
+
if (prStatus.ciStatus === 'passed' && !prStatus.mergeable) {
|
|
935
|
+
if (lastSeenCIStatus !== 'passed-not-mergeable') {
|
|
936
|
+
logger.info(`[${task.id}] CI passed but not mergeable — waiting for approval or checks`);
|
|
937
|
+
lastSeenCIStatus = 'passed-not-mergeable';
|
|
938
|
+
}
|
|
939
|
+
await sleep(PR_POLL_INTERVAL);
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
// CI failed — delegate to implementing agent to fix
|
|
943
|
+
if (prStatus.ciStatus === 'failed') {
|
|
944
|
+
// Avoid re-fixing if we just pushed and the old pipeline result is stale
|
|
945
|
+
if (lastSeenCIStatus === 'fix-pushed') {
|
|
946
|
+
// We just pushed a fix — wait for new pipeline to start
|
|
947
|
+
logger.info(`[${task.id}] Waiting for new CI pipeline after fix push...`);
|
|
948
|
+
lastSeenCIStatus = 'pending-after-fix';
|
|
949
|
+
await sleep(PR_POLL_INTERVAL * 2); // Extra wait for new pipeline
|
|
950
|
+
continue;
|
|
951
|
+
}
|
|
952
|
+
if (pipeline.ciFixes >= MAX_CI_FIXES) {
|
|
953
|
+
logger.error(`[${task.id}] CI fix limit (${MAX_CI_FIXES}) reached — giving up`);
|
|
954
|
+
return false;
|
|
955
|
+
}
|
|
916
956
|
pipeline.ciFixes++;
|
|
917
|
-
logger.warn(`[${task.id}] CI failed — fix attempt ${pipeline.ciFixes}/${MAX_CI_FIXES}`);
|
|
957
|
+
logger.warn(`[${task.id}] CI failed — delegating fix to agent (attempt ${pipeline.ciFixes}/${MAX_CI_FIXES})`);
|
|
918
958
|
const logs = fetchFailureLogs(branch, platform === 'github' ? 'github' : 'gitlab', projectDir);
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
959
|
+
const fixResult = await spawnAgent(pool, pipeline.workerId, {
|
|
960
|
+
agent: pipeline.assignedAgent ?? undefined,
|
|
961
|
+
cwd: worktreePath,
|
|
962
|
+
prompt: buildCIFixPrompt({ task, worktreePath, projectDir }, logs ?? 'No logs available — check the CI pipeline manually'),
|
|
963
|
+
model: getAgentModel(pipeline.assignedAgent, agents, task),
|
|
964
|
+
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
|
|
965
|
+
if (fixResult.success) {
|
|
966
|
+
const sha = commitInWorktree(worktreePath, `fix: CI failure for ${task.title}`);
|
|
967
|
+
if (sha) {
|
|
928
968
|
forcePushWorktree(worktreePath);
|
|
969
|
+
lastSeenCIStatus = 'fix-pushed';
|
|
970
|
+
logger.info(`[${task.id}] Fix pushed — waiting for new CI pipeline`);
|
|
971
|
+
await sleep(PR_POLL_INTERVAL * 2); // Wait for new pipeline to start
|
|
929
972
|
continue;
|
|
930
973
|
}
|
|
931
974
|
}
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
}
|
|
975
|
+
// Fix didn't produce changes or failed — wait and retry
|
|
976
|
+
await sleep(PR_POLL_INTERVAL);
|
|
977
|
+
continue;
|
|
936
978
|
}
|
|
937
979
|
// Unresolved PR comments — spawn agent to address
|
|
938
980
|
const comments = getPRComments(projectDir, platform, prStatus.number);
|
|
939
981
|
const unresolved = comments.filter(c => !c.resolved);
|
|
940
982
|
if (unresolved.length > 0) {
|
|
941
|
-
logger.info(`[${task.id}] ${unresolved.length} unresolved PR comments —
|
|
983
|
+
logger.info(`[${task.id}] ${unresolved.length} unresolved PR comments — delegating to agent`);
|
|
942
984
|
const commentText = unresolved
|
|
943
985
|
.map(c => `${c.path ?? 'general'}${c.line ? `:${c.line}` : ''} — ${c.author}: ${c.body}`)
|
|
944
986
|
.join('\n\n');
|
|
@@ -949,25 +991,13 @@ async function pollAndMergePR(task, pipeline, branch, platform, projectDir, work
|
|
|
949
991
|
model: getAgentModel(pipeline.assignedAgent, agents, task),
|
|
950
992
|
}, state, task.id, logger, onMessage, onSpawnStart, onSpawnEnd);
|
|
951
993
|
if (commentResult.success) {
|
|
952
|
-
commitInWorktree(worktreePath,
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
994
|
+
const sha = commitInWorktree(worktreePath, 'fix: address PR review comments');
|
|
995
|
+
if (sha) {
|
|
996
|
+
forcePushWorktree(worktreePath);
|
|
997
|
+
lastSeenCIStatus = 'fix-pushed';
|
|
998
|
+
appendEvent(projectDir, createEvent('pr_comment_resolved', { taskId: task.id, detail: `${unresolved.length} comments` }));
|
|
999
|
+
}
|
|
956
1000
|
}
|
|
957
|
-
}
|
|
958
|
-
// Ready to merge
|
|
959
|
-
if (prStatus.mergeable) {
|
|
960
|
-
const merged = mergePR(projectDir, platform, prStatus.number);
|
|
961
|
-
return merged;
|
|
962
|
-
}
|
|
963
|
-
// CI pending or waiting for approval — keep polling
|
|
964
|
-
if (prStatus.ciStatus === 'pending' || prStatus.ciStatus === 'not-found') {
|
|
965
|
-
await sleep(PR_POLL_INTERVAL);
|
|
966
|
-
continue;
|
|
967
|
-
}
|
|
968
|
-
// CI passed but not mergeable (needs approval?) — wait
|
|
969
|
-
if (prStatus.ciStatus === 'passed' && !prStatus.mergeable) {
|
|
970
|
-
logger.info(`[${task.id}] CI passed but not mergeable (waiting for approval?)`);
|
|
971
1001
|
await sleep(PR_POLL_INTERVAL);
|
|
972
1002
|
continue;
|
|
973
1003
|
}
|
|
@@ -290,8 +290,8 @@ export class TUI {
|
|
|
290
290
|
return '';
|
|
291
291
|
const cur = this.state.agents[this.state.agents.length - 1];
|
|
292
292
|
const col = ANSI_COLORS[agentColor(cur)] || '';
|
|
293
|
-
const name = cur.length >
|
|
294
|
-
return `${col}${name.padEnd(
|
|
293
|
+
const name = cur.length > 24 ? cur.slice(0, 24) : cur;
|
|
294
|
+
return `${col}${name.padEnd(25)}${RESET}`;
|
|
295
295
|
}
|
|
296
296
|
// ─── Public API ───
|
|
297
297
|
banner() {
|