@kbediako/codex-orchestrator 0.1.35 → 0.1.36
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/README.md +48 -25
- package/codex.orchestrator.json +39 -0
- package/dist/bin/codex-orchestrator.js +201 -29
- package/dist/orchestrator/src/cli/codexDefaultsSetup.js +274 -0
- package/dist/orchestrator/src/cli/doctor.js +132 -1
- package/dist/orchestrator/src/cli/doctorIssueLog.js +42 -16
- package/dist/scripts/lib/pr-watch-merge.js +170 -9
- package/dist/scripts/run-review.js +1983 -0
- package/docs/README.md +12 -10
- package/package.json +3 -1
- package/skills/agent-first-adoption-steering/SKILL.md +116 -0
- package/skills/chrome-devtools/SKILL.md +6 -0
- package/skills/collab-deliberation/SKILL.md +6 -0
- package/skills/collab-evals/SKILL.md +15 -0
- package/skills/collab-subagents-first/SKILL.md +7 -1
- package/skills/delegate-early/SKILL.md +6 -0
- package/skills/delegation-usage/DELEGATION_GUIDE.md +7 -4
- package/skills/delegation-usage/SKILL.md +14 -4
- package/skills/docs-first/SKILL.md +6 -0
- package/skills/elegance-review/SKILL.md +4 -0
- package/skills/long-poll-wait/SKILL.md +82 -0
- package/skills/release/SKILL.md +6 -2
- package/skills/standalone-review/SKILL.md +9 -3
- package/templates/README.md +5 -0
- package/templates/codex/.codex/agents/awaiter-high.toml +38 -0
- package/templates/codex/.codex/agents/explorer-fast.toml +2 -0
- package/templates/codex/.codex/agents/worker-complex.toml +2 -0
- package/templates/codex/.codex/config.toml +19 -0
- package/templates/codex/AGENTS.md +10 -4
|
@@ -13,6 +13,7 @@ const REQUIRED_BUCKET_PASS = new Set(['pass']);
|
|
|
13
13
|
const REQUIRED_BUCKET_PENDING = new Set(['pending']);
|
|
14
14
|
const REQUIRED_BUCKET_FAILED = new Set(['fail', 'cancel', 'skipping']);
|
|
15
15
|
const MERGEABLE_STATES = new Set(['CLEAN', 'HAS_HOOKS', 'UNSTABLE']);
|
|
16
|
+
const ACTION_REQUIRED_MERGE_STATES = new Set(['BEHIND', 'DIRTY']);
|
|
16
17
|
const BLOCKED_REVIEW_DECISIONS = new Set(['CHANGES_REQUESTED', 'REVIEW_REQUIRED']);
|
|
17
18
|
const DO_NOT_MERGE_LABEL = /do[\s_-]*not[\s_-]*merge/i;
|
|
18
19
|
const ACTIONABLE_BOT_LOGINS = new Set([
|
|
@@ -31,6 +32,13 @@ const BOT_MENTION_PATTERNS = {
|
|
|
31
32
|
};
|
|
32
33
|
const BOT_IN_PROGRESS_REACTION_CONTENT = new Set(['eyes']);
|
|
33
34
|
const BOT_COMPLETE_REACTION_CONTENT = new Set(['+1', 'hooray', 'heart', 'rocket', 'laugh', 'confused']);
|
|
35
|
+
class PrWatchMergeExitError extends Error {
|
|
36
|
+
constructor(message, exitCode = 1) {
|
|
37
|
+
super(message);
|
|
38
|
+
this.name = 'PrWatchMergeExitError';
|
|
39
|
+
this.exitCode = exitCode;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
34
42
|
const PR_QUERY = `
|
|
35
43
|
query($owner:String!, $repo:String!, $number:Int!) {
|
|
36
44
|
repository(owner:$owner, name:$repo) {
|
|
@@ -238,6 +246,10 @@ export function printPrWatchMergeHelp(options = {}) {
|
|
|
238
246
|
const usageCommand = typeof options.usage === 'string' && options.usage.trim().length > 0
|
|
239
247
|
? options.usage.trim()
|
|
240
248
|
: 'codex-orchestrator pr watch-merge';
|
|
249
|
+
const defaultAutoMerge = typeof options.defaultAutoMerge === 'boolean'
|
|
250
|
+
? options.defaultAutoMerge
|
|
251
|
+
: envFlagEnabled(process.env.PR_MONITOR_AUTO_MERGE, false);
|
|
252
|
+
const defaultExitOnActionRequired = Boolean(options.defaultExitOnActionRequired);
|
|
241
253
|
console.log(`Usage: ${usageCommand} [options]
|
|
242
254
|
|
|
243
255
|
Monitor PR checks/reviews with polling and optionally merge after a quiet window.
|
|
@@ -254,16 +266,21 @@ Options:
|
|
|
254
266
|
--no-auto-merge Never merge automatically (monitor only)
|
|
255
267
|
--delete-branch Delete remote branch when merging
|
|
256
268
|
--no-delete-branch Keep remote branch after merge
|
|
269
|
+
--exit-on-action-required Exit non-zero when author action is required
|
|
270
|
+
--no-exit-on-action-required Keep monitoring even when author action is required
|
|
257
271
|
--dry-run Never call gh pr merge (report only)
|
|
258
272
|
-h, --help Show this help message
|
|
259
273
|
|
|
260
274
|
Environment:
|
|
261
|
-
PR_MONITOR_AUTO_MERGE=1 Default auto-merge on
|
|
275
|
+
PR_MONITOR_AUTO_MERGE=1 Default auto-merge on (current default: ${defaultAutoMerge ? 'on' : 'off'})
|
|
262
276
|
PR_MONITOR_DELETE_BRANCH=1 Default delete branch on merge
|
|
263
277
|
PR_MONITOR_QUIET_MINUTES=<n> Override quiet window default
|
|
264
278
|
PR_MONITOR_INTERVAL_SECONDS=<n>
|
|
265
279
|
PR_MONITOR_TIMEOUT_MINUTES=<n>
|
|
266
280
|
PR_MONITOR_MERGE_METHOD=<method>`);
|
|
281
|
+
if (defaultExitOnActionRequired) {
|
|
282
|
+
console.log(' resolve-merge default: exit-on-action-required is on');
|
|
283
|
+
}
|
|
267
284
|
}
|
|
268
285
|
async function runGh(args, { allowFailure = false } = {}) {
|
|
269
286
|
return await new Promise((resolve, reject) => {
|
|
@@ -303,6 +320,39 @@ async function runGh(args, { allowFailure = false } = {}) {
|
|
|
303
320
|
});
|
|
304
321
|
});
|
|
305
322
|
}
|
|
323
|
+
async function runGit(args, { allowFailure = false } = {}) {
|
|
324
|
+
return await new Promise((resolve, reject) => {
|
|
325
|
+
const child = spawn('git', args, {
|
|
326
|
+
env: process.env,
|
|
327
|
+
stdio: ['ignore', 'pipe', 'pipe']
|
|
328
|
+
});
|
|
329
|
+
let stdout = '';
|
|
330
|
+
let stderr = '';
|
|
331
|
+
child.stdout?.on('data', (chunk) => {
|
|
332
|
+
stdout += chunk.toString();
|
|
333
|
+
});
|
|
334
|
+
child.stderr?.on('data', (chunk) => {
|
|
335
|
+
stderr += chunk.toString();
|
|
336
|
+
});
|
|
337
|
+
child.once('error', (error) => {
|
|
338
|
+
reject(new Error(`Failed to run git ${args.join(' ')}: ${error.message}`));
|
|
339
|
+
});
|
|
340
|
+
child.once('close', (code) => {
|
|
341
|
+
const exitCode = typeof code === 'number' ? code : 1;
|
|
342
|
+
const result = {
|
|
343
|
+
exitCode,
|
|
344
|
+
stdout: stdout.trim(),
|
|
345
|
+
stderr: stderr.trim()
|
|
346
|
+
};
|
|
347
|
+
if (exitCode === 0 || allowFailure) {
|
|
348
|
+
resolve(result);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const detail = result.stderr || result.stdout || `exit code ${exitCode}`;
|
|
352
|
+
reject(new Error(`git ${args.join(' ')} failed: ${detail}`));
|
|
353
|
+
});
|
|
354
|
+
});
|
|
355
|
+
}
|
|
306
356
|
async function runGhJson(args) {
|
|
307
357
|
const result = await runGh(args);
|
|
308
358
|
try {
|
|
@@ -327,6 +377,39 @@ async function ensureGhAuth() {
|
|
|
327
377
|
throw new Error('GitHub CLI is not authenticated for github.com. Run `gh auth login` and retry.');
|
|
328
378
|
}
|
|
329
379
|
}
|
|
380
|
+
export function parseGitHubRepoFromRemoteUrl(rawUrl) {
|
|
381
|
+
if (typeof rawUrl !== 'string' || rawUrl.trim().length === 0) {
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
const normalized = rawUrl.trim();
|
|
385
|
+
const patterns = [
|
|
386
|
+
/^git@github\.com:(?<owner>[^/\s]+)\/(?<repo>[^/\s]+?)(?:\.git)?$/iu,
|
|
387
|
+
/^https?:\/\/github\.com\/(?<owner>[^/\s]+)\/(?<repo>[^/\s]+?)(?:\.git)?\/?$/iu,
|
|
388
|
+
/^ssh:\/\/git@github\.com\/(?<owner>[^/\s]+)\/(?<repo>[^/\s]+?)(?:\.git)?\/?$/iu
|
|
389
|
+
];
|
|
390
|
+
for (const pattern of patterns) {
|
|
391
|
+
const match = normalized.match(pattern);
|
|
392
|
+
const owner = match?.groups?.owner?.trim();
|
|
393
|
+
const repo = match?.groups?.repo?.trim();
|
|
394
|
+
if (owner && repo) {
|
|
395
|
+
return { owner, repo };
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return null;
|
|
399
|
+
}
|
|
400
|
+
async function resolveRepoFromGitRemote() {
|
|
401
|
+
let result;
|
|
402
|
+
try {
|
|
403
|
+
result = await runGit(['remote', 'get-url', 'origin'], { allowFailure: true });
|
|
404
|
+
}
|
|
405
|
+
catch {
|
|
406
|
+
return null;
|
|
407
|
+
}
|
|
408
|
+
if (result.exitCode !== 0 || !result.stdout) {
|
|
409
|
+
return null;
|
|
410
|
+
}
|
|
411
|
+
return parseGitHubRepoFromRemoteUrl(result.stdout);
|
|
412
|
+
}
|
|
330
413
|
async function resolveRepo(ownerArg, repoArg) {
|
|
331
414
|
if (ownerArg && repoArg) {
|
|
332
415
|
return { owner: ownerArg, repo: repoArg };
|
|
@@ -334,6 +417,10 @@ async function resolveRepo(ownerArg, repoArg) {
|
|
|
334
417
|
if (ownerArg || repoArg) {
|
|
335
418
|
throw new Error('Provide both --owner and --repo, or neither.');
|
|
336
419
|
}
|
|
420
|
+
const gitRemoteRepo = await resolveRepoFromGitRemote();
|
|
421
|
+
if (gitRemoteRepo) {
|
|
422
|
+
return gitRemoteRepo;
|
|
423
|
+
}
|
|
337
424
|
const response = await runGhJson(['repo', 'view', '--json', 'nameWithOwner']);
|
|
338
425
|
const nameWithOwner = response?.nameWithOwner;
|
|
339
426
|
if (typeof nameWithOwner !== 'string' || !nameWithOwner.includes('/')) {
|
|
@@ -342,11 +429,18 @@ async function resolveRepo(ownerArg, repoArg) {
|
|
|
342
429
|
const [owner, repo] = nameWithOwner.split('/');
|
|
343
430
|
return { owner, repo };
|
|
344
431
|
}
|
|
345
|
-
|
|
432
|
+
export function buildPrNumberViewArgs(owner, repo) {
|
|
433
|
+
const args = ['pr', 'view', '--json', 'number'];
|
|
434
|
+
if (typeof owner === 'string' && owner.trim().length > 0 && typeof repo === 'string' && repo.trim().length > 0) {
|
|
435
|
+
args.push('--repo', `${owner.trim()}/${repo.trim()}`);
|
|
436
|
+
}
|
|
437
|
+
return args;
|
|
438
|
+
}
|
|
439
|
+
async function resolvePrNumber(prArg, owner, repo) {
|
|
346
440
|
if (prArg !== undefined) {
|
|
347
441
|
return parseInteger('pr', prArg, null);
|
|
348
442
|
}
|
|
349
|
-
const response = await runGhJson(
|
|
443
|
+
const response = await runGhJson(buildPrNumberViewArgs(owner, repo));
|
|
350
444
|
const number = response?.number;
|
|
351
445
|
if (!Number.isInteger(number) || number <= 0) {
|
|
352
446
|
throw new Error('Unable to infer PR number from current branch.');
|
|
@@ -580,6 +674,46 @@ export function buildStatusSnapshot(response, requiredChecks = null, inlineBotFe
|
|
|
580
674
|
headOid: pr.commits?.nodes?.[0]?.commit?.oid || null
|
|
581
675
|
};
|
|
582
676
|
}
|
|
677
|
+
export function resolveActionRequiredReasons(snapshot) {
|
|
678
|
+
if (!snapshot || typeof snapshot !== 'object') {
|
|
679
|
+
return ['snapshot=unknown'];
|
|
680
|
+
}
|
|
681
|
+
const reasons = [];
|
|
682
|
+
const reviewDecision = normalizeEnum(snapshot.reviewDecision);
|
|
683
|
+
const mergeStateStatus = normalizeEnum(snapshot.mergeStateStatus);
|
|
684
|
+
if (Boolean(snapshot.isDraft)) {
|
|
685
|
+
reasons.push('draft');
|
|
686
|
+
}
|
|
687
|
+
if (Boolean(snapshot.hasDoNotMergeLabel)) {
|
|
688
|
+
reasons.push('label:do-not-merge');
|
|
689
|
+
}
|
|
690
|
+
if (BLOCKED_REVIEW_DECISIONS.has(reviewDecision)) {
|
|
691
|
+
reasons.push(`review=${reviewDecision}`);
|
|
692
|
+
}
|
|
693
|
+
if (ACTION_REQUIRED_MERGE_STATES.has(mergeStateStatus)) {
|
|
694
|
+
reasons.push(`merge_state=${mergeStateStatus}`);
|
|
695
|
+
}
|
|
696
|
+
if (typeof snapshot.unresolvedThreadCount === 'number' && snapshot.unresolvedThreadCount > 0) {
|
|
697
|
+
reasons.push(`unresolved_threads=${snapshot.unresolvedThreadCount}`);
|
|
698
|
+
}
|
|
699
|
+
if (typeof snapshot.unacknowledgedBotFeedbackCount === 'number'
|
|
700
|
+
&& snapshot.unacknowledgedBotFeedbackCount > 0) {
|
|
701
|
+
reasons.push(`unacknowledged_bot_feedback=${snapshot.unacknowledgedBotFeedbackCount}`);
|
|
702
|
+
}
|
|
703
|
+
const requiredChecks = snapshot.requiredChecks && typeof snapshot.requiredChecks === 'object' ? snapshot.requiredChecks : null;
|
|
704
|
+
const requiredFailedCount = Array.isArray(requiredChecks?.failed) ? requiredChecks.failed.length : 0;
|
|
705
|
+
if (requiredFailedCount > 0 && snapshot.readyToMerge === false) {
|
|
706
|
+
reasons.push(`required_checks_failed=${requiredFailedCount}`);
|
|
707
|
+
}
|
|
708
|
+
else {
|
|
709
|
+
const rollupFailedCount = Array.isArray(snapshot.checks?.failed) ? snapshot.checks.failed.length : 0;
|
|
710
|
+
const rollupPendingCount = Array.isArray(snapshot.checks?.pending) ? snapshot.checks.pending.length : 0;
|
|
711
|
+
if (!requiredChecks && !MERGEABLE_STATES.has(mergeStateStatus) && rollupPendingCount === 0 && rollupFailedCount > 0) {
|
|
712
|
+
reasons.push(`checks_failed=${rollupFailedCount}`);
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
return reasons;
|
|
716
|
+
}
|
|
583
717
|
function formatStatusLine(snapshot, quietRemainingMs) {
|
|
584
718
|
const requiredChecks = snapshot.requiredChecks;
|
|
585
719
|
const failedNames = snapshot.checks.failed.map((item) => `${item.name}:${item.state}`).join(', ') || '-';
|
|
@@ -1017,15 +1151,19 @@ async function fetchSnapshot(owner, repo, prNumber, previousRequiredChecksCache
|
|
|
1017
1151
|
: null
|
|
1018
1152
|
};
|
|
1019
1153
|
}
|
|
1020
|
-
|
|
1154
|
+
export function buildPrMergeArgs({ owner, repo, prNumber, mergeMethod, deleteBranch, headOid }) {
|
|
1021
1155
|
// gh pr merge has no --yes flag; rely on non-interactive stdio + explicit merge method.
|
|
1022
|
-
const args = ['pr', 'merge', String(prNumber), `--${mergeMethod}`];
|
|
1156
|
+
const args = ['pr', 'merge', String(prNumber), `--${mergeMethod}`, '--repo', `${owner}/${repo}`];
|
|
1023
1157
|
if (deleteBranch) {
|
|
1024
1158
|
args.push('--delete-branch');
|
|
1025
1159
|
}
|
|
1026
1160
|
if (headOid) {
|
|
1027
1161
|
args.push('--match-head-commit', headOid);
|
|
1028
1162
|
}
|
|
1163
|
+
return args;
|
|
1164
|
+
}
|
|
1165
|
+
async function attemptMerge({ owner, repo, prNumber, mergeMethod, deleteBranch, headOid }) {
|
|
1166
|
+
const args = buildPrMergeArgs({ owner, repo, prNumber, mergeMethod, deleteBranch, headOid });
|
|
1029
1167
|
return await runGh(args, { allowFailure: true });
|
|
1030
1168
|
}
|
|
1031
1169
|
async function runPrWatchMergeOrThrow(argv, options) {
|
|
@@ -1046,6 +1184,8 @@ async function runPrWatchMergeOrThrow(argv, options) {
|
|
|
1046
1184
|
'no-auto-merge',
|
|
1047
1185
|
'delete-branch',
|
|
1048
1186
|
'no-delete-branch',
|
|
1187
|
+
'exit-on-action-required',
|
|
1188
|
+
'no-exit-on-action-required',
|
|
1049
1189
|
'dry-run',
|
|
1050
1190
|
'h',
|
|
1051
1191
|
'help'
|
|
@@ -1067,7 +1207,8 @@ async function runPrWatchMergeOrThrow(argv, options) {
|
|
|
1067
1207
|
const mergeMethod = parseMergeMethod(typeof args['merge-method'] === 'string'
|
|
1068
1208
|
? args['merge-method']
|
|
1069
1209
|
: process.env.PR_MONITOR_MERGE_METHOD || DEFAULT_MERGE_METHOD);
|
|
1070
|
-
const
|
|
1210
|
+
const defaultAutoMergeFallback = typeof options.defaultAutoMerge === 'boolean' ? options.defaultAutoMerge : false;
|
|
1211
|
+
const defaultAutoMerge = envFlagEnabled(process.env.PR_MONITOR_AUTO_MERGE, defaultAutoMergeFallback);
|
|
1071
1212
|
const defaultDeleteBranch = envFlagEnabled(process.env.PR_MONITOR_DELETE_BRANCH, true);
|
|
1072
1213
|
let autoMerge = defaultAutoMerge;
|
|
1073
1214
|
if (hasFlag(args, 'auto-merge')) {
|
|
@@ -1083,15 +1224,22 @@ async function runPrWatchMergeOrThrow(argv, options) {
|
|
|
1083
1224
|
if (hasFlag(args, 'no-delete-branch')) {
|
|
1084
1225
|
deleteBranch = false;
|
|
1085
1226
|
}
|
|
1227
|
+
let exitOnActionRequired = Boolean(options.defaultExitOnActionRequired);
|
|
1228
|
+
if (hasFlag(args, 'exit-on-action-required')) {
|
|
1229
|
+
exitOnActionRequired = true;
|
|
1230
|
+
}
|
|
1231
|
+
if (hasFlag(args, 'no-exit-on-action-required')) {
|
|
1232
|
+
exitOnActionRequired = false;
|
|
1233
|
+
}
|
|
1086
1234
|
const dryRun = hasFlag(args, 'dry-run');
|
|
1087
1235
|
await ensureGhAuth();
|
|
1088
1236
|
const { owner, repo } = await resolveRepo(typeof args.owner === 'string' ? args.owner : undefined, typeof args.repo === 'string' ? args.repo : undefined);
|
|
1089
|
-
const prNumber = await resolvePrNumber(args.pr);
|
|
1237
|
+
const prNumber = await resolvePrNumber(args.pr, owner, repo);
|
|
1090
1238
|
const intervalMs = Math.round(intervalSeconds * 1000);
|
|
1091
1239
|
const quietMs = Math.round(quietMinutes * 60 * 1000);
|
|
1092
1240
|
const timeoutMs = Math.round(timeoutMinutes * 60 * 1000);
|
|
1093
1241
|
const deadline = Date.now() + timeoutMs;
|
|
1094
|
-
log(`Monitoring ${owner}/${repo}#${prNumber} every ${intervalSeconds}s (quiet window ${quietMinutes}m, timeout ${timeoutMinutes}m, auto_merge=${autoMerge ? 'on' : 'off'}, dry_run=${dryRun ? 'on' : 'off'}).`);
|
|
1242
|
+
log(`Monitoring ${owner}/${repo}#${prNumber} every ${intervalSeconds}s (quiet window ${quietMinutes}m, timeout ${timeoutMinutes}m, auto_merge=${autoMerge ? 'on' : 'off'}, exit_on_action_required=${exitOnActionRequired ? 'on' : 'off'}, dry_run=${dryRun ? 'on' : 'off'}).`);
|
|
1095
1243
|
let quietWindowStartedAt = null;
|
|
1096
1244
|
let quietWindowAnchorUpdatedAt = null;
|
|
1097
1245
|
let quietWindowAnchorHeadOid = null;
|
|
@@ -1142,6 +1290,13 @@ async function runPrWatchMergeOrThrow(argv, options) {
|
|
|
1142
1290
|
const quietElapsedMs = quietWindowStartedAt ? Date.now() - quietWindowStartedAt : 0;
|
|
1143
1291
|
const quietRemainingMs = quietWindowStartedAt ? Math.max(quietMs - quietElapsedMs, 0) : quietMs;
|
|
1144
1292
|
log(formatStatusLine(snapshot, quietRemainingMs));
|
|
1293
|
+
if (exitOnActionRequired) {
|
|
1294
|
+
const actionRequiredReasons = resolveActionRequiredReasons(snapshot);
|
|
1295
|
+
if (actionRequiredReasons.length > 0) {
|
|
1296
|
+
const details = actionRequiredReasons.join(', ');
|
|
1297
|
+
throw new PrWatchMergeExitError(`Action required before merge: ${details}${snapshot.url ? ` (${snapshot.url})` : ''}`, 2);
|
|
1298
|
+
}
|
|
1299
|
+
}
|
|
1145
1300
|
if (snapshot.readyToMerge && quietWindowStartedAt !== null && quietElapsedMs >= quietMs) {
|
|
1146
1301
|
if (!autoMerge || dryRun) {
|
|
1147
1302
|
log(dryRun
|
|
@@ -1159,6 +1314,8 @@ async function runPrWatchMergeOrThrow(argv, options) {
|
|
|
1159
1314
|
lastMergeAttemptHeadOid = snapshot.headOid;
|
|
1160
1315
|
log(`Attempting merge via gh pr merge --${mergeMethod}${deleteBranch ? ' --delete-branch' : ''}.`);
|
|
1161
1316
|
const mergeResult = await attemptMerge({
|
|
1317
|
+
owner,
|
|
1318
|
+
repo,
|
|
1162
1319
|
prNumber,
|
|
1163
1320
|
mergeMethod,
|
|
1164
1321
|
deleteBranch,
|
|
@@ -1178,7 +1335,7 @@ async function runPrWatchMergeOrThrow(argv, options) {
|
|
|
1178
1335
|
}
|
|
1179
1336
|
await sleep(Math.min(intervalMs, remainingTimeMs));
|
|
1180
1337
|
}
|
|
1181
|
-
throw new
|
|
1338
|
+
throw new PrWatchMergeExitError(`Timed out after ${timeoutMinutes} minute(s) while monitoring PR #${prNumber}.`, 3);
|
|
1182
1339
|
}
|
|
1183
1340
|
export async function runPrWatchMerge(argv, options = {}) {
|
|
1184
1341
|
try {
|
|
@@ -1188,6 +1345,10 @@ export async function runPrWatchMerge(argv, options = {}) {
|
|
|
1188
1345
|
catch (error) {
|
|
1189
1346
|
const message = error instanceof Error ? error.message : String(error);
|
|
1190
1347
|
console.error(message);
|
|
1348
|
+
const exitCodeCandidate = Number(error?.exitCode);
|
|
1349
|
+
if (Number.isInteger(exitCodeCandidate) && exitCodeCandidate > 0) {
|
|
1350
|
+
return exitCodeCandidate;
|
|
1351
|
+
}
|
|
1191
1352
|
return 1;
|
|
1192
1353
|
}
|
|
1193
1354
|
}
|