@jonit-dev/night-watch-cli 1.7.47 → 1.7.49
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/cli.js +1098 -708
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +13 -36
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/prd.d.ts.map +1 -1
- package/dist/commands/prd.js +2 -6
- package/dist/commands/prd.js.map +1 -1
- package/dist/commands/prds.d.ts.map +1 -1
- package/dist/commands/prds.js +1 -1
- package/dist/commands/prds.js.map +1 -1
- package/dist/commands/review.d.ts +10 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +34 -0
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +5 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/scripts/night-watch-cron.sh +96 -0
- package/dist/scripts/night-watch-helpers.sh +1 -1
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +168 -35
- package/dist/web/assets/index-Ba-4YvTQ.js +365 -0
- package/dist/web/assets/index-DpVirMEe.css +1 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
|
@@ -184,6 +184,95 @@ count_prs_for_branch() {
|
|
|
184
184
|
echo "${count:-0}"
|
|
185
185
|
}
|
|
186
186
|
|
|
187
|
+
checkpoint_timeout_progress() {
|
|
188
|
+
local worktree_dir="${1:?worktree_dir required}"
|
|
189
|
+
local branch_name="${2:?branch_name required}"
|
|
190
|
+
local prd_file="${3:?prd_file required}"
|
|
191
|
+
|
|
192
|
+
if [ ! -d "${worktree_dir}" ]; then
|
|
193
|
+
return 0
|
|
194
|
+
fi
|
|
195
|
+
|
|
196
|
+
if ! git -C "${worktree_dir}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
|
197
|
+
return 0
|
|
198
|
+
fi
|
|
199
|
+
|
|
200
|
+
if [ -z "$(git -C "${worktree_dir}" status --porcelain 2>/dev/null)" ]; then
|
|
201
|
+
log "TIMEOUT: No local changes to checkpoint for ${prd_file}"
|
|
202
|
+
return 0
|
|
203
|
+
fi
|
|
204
|
+
|
|
205
|
+
log "TIMEOUT: Checkpointing local progress for ${prd_file} on ${branch_name}"
|
|
206
|
+
if git -C "${worktree_dir}" add -A >/dev/null 2>&1; then
|
|
207
|
+
if ! git -C "${worktree_dir}" diff --cached --quiet >/dev/null 2>&1; then
|
|
208
|
+
git -C "${worktree_dir}" commit --no-verify \
|
|
209
|
+
-m "chore: checkpoint timed-out progress for ${prd_file}" \
|
|
210
|
+
>> "${LOG_FILE}" 2>&1 || true
|
|
211
|
+
fi
|
|
212
|
+
fi
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
extract_timeout_phase_titles() {
|
|
216
|
+
local issue_body="${1:-}"
|
|
217
|
+
if [ -z "${issue_body}" ]; then
|
|
218
|
+
return 0
|
|
219
|
+
fi
|
|
220
|
+
|
|
221
|
+
printf '%s\n' "${issue_body}" \
|
|
222
|
+
| tr -d '\r' \
|
|
223
|
+
| awk '
|
|
224
|
+
BEGIN { count = 0 }
|
|
225
|
+
/^[[:space:]]*#{2,4}[[:space:]]*Phase[[:space:]]+[0-9]+[[:space:]]*:/ {
|
|
226
|
+
line = $0
|
|
227
|
+
sub(/^[[:space:]]*#{2,4}[[:space:]]*/, "", line)
|
|
228
|
+
gsub(/[[:space:]]+$/, "", line)
|
|
229
|
+
if (count < 3) {
|
|
230
|
+
count++
|
|
231
|
+
print line
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
'
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
build_timeout_followup_comment() {
|
|
238
|
+
local max_runtime="${1:?max_runtime required}"
|
|
239
|
+
local prd_label="${2:?prd_label required}"
|
|
240
|
+
local branch_name="${3:?branch_name required}"
|
|
241
|
+
local issue_body="${4:-}"
|
|
242
|
+
local phase_titles=""
|
|
243
|
+
local comment=""
|
|
244
|
+
|
|
245
|
+
phase_titles=$(extract_timeout_phase_titles "${issue_body}" || true)
|
|
246
|
+
|
|
247
|
+
comment="Timeout follow-up:
|
|
248
|
+
|
|
249
|
+
Execution hit the ${max_runtime}s runtime limit while processing ${prd_label}.
|
|
250
|
+
Progress was checkpointed on branch ${branch_name}, so the next run will resume from the latest checkpoint.
|
|
251
|
+
|
|
252
|
+
Suggested slices for the next runs:"
|
|
253
|
+
|
|
254
|
+
if [ -n "${phase_titles}" ]; then
|
|
255
|
+
local idx=1
|
|
256
|
+
while IFS= read -r phase_title; do
|
|
257
|
+
[ -z "${phase_title}" ] && continue
|
|
258
|
+
comment="${comment}
|
|
259
|
+
${idx}. ${phase_title}"
|
|
260
|
+
idx=$((idx + 1))
|
|
261
|
+
done <<< "${phase_titles}"
|
|
262
|
+
else
|
|
263
|
+
comment="${comment}
|
|
264
|
+
1. Phase 1: Setup and interfaces
|
|
265
|
+
2. Phase 2: Core implementation and tests
|
|
266
|
+
3. Phase 3: Integration and verification"
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
comment="${comment}
|
|
270
|
+
|
|
271
|
+
Recommendation: avoid huge PRDs. Slice large work into smaller PRDs/phases so each run can finish within the runtime window."
|
|
272
|
+
|
|
273
|
+
printf '%s' "${comment}"
|
|
274
|
+
}
|
|
275
|
+
|
|
187
276
|
finalize_prd_done() {
|
|
188
277
|
local reason="${1:?reason required}"
|
|
189
278
|
|
|
@@ -472,10 +561,17 @@ if [ ${EXIT_CODE} -eq 0 ]; then
|
|
|
472
561
|
fi
|
|
473
562
|
elif [ ${EXIT_CODE} -eq 124 ]; then
|
|
474
563
|
log "TIMEOUT: Night watch killed after ${MAX_RUNTIME}s while processing ${ELIGIBLE_PRD}"
|
|
564
|
+
checkpoint_timeout_progress "${WORKTREE_DIR}" "${BRANCH_NAME}" "${ELIGIBLE_PRD}"
|
|
475
565
|
if [ -n "${ISSUE_NUMBER}" ]; then
|
|
476
566
|
"${NW_CLI}" board move-issue "${ISSUE_NUMBER}" --column "Ready" 2>>"${LOG_FILE}" || true
|
|
477
567
|
"${NW_CLI}" board comment "${ISSUE_NUMBER}" \
|
|
478
568
|
--body "Execution timed out after ${MAX_RUNTIME}s. Moved back to Ready for retry." 2>>"${LOG_FILE}" || true
|
|
569
|
+
TIMEOUT_FOLLOWUP_COMMENT=$(build_timeout_followup_comment \
|
|
570
|
+
"${MAX_RUNTIME}" \
|
|
571
|
+
"${ELIGIBLE_PRD}" \
|
|
572
|
+
"${BRANCH_NAME}" \
|
|
573
|
+
"${ISSUE_BODY}")
|
|
574
|
+
"${NW_CLI}" board comment "${ISSUE_NUMBER}" --body "${TIMEOUT_FOLLOWUP_COMMENT}" 2>>"${LOG_FILE}" || true
|
|
479
575
|
fi
|
|
480
576
|
night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" timeout --exit-code 124 2>/dev/null || true
|
|
481
577
|
emit_result "timeout" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
|
|
@@ -228,7 +228,7 @@ find_eligible_prd() {
|
|
|
228
228
|
local done_dir="${prd_dir}/done"
|
|
229
229
|
|
|
230
230
|
local prd_files
|
|
231
|
-
prd_files=$(find "${prd_dir}" -maxdepth 1 -name '*.md'
|
|
231
|
+
prd_files=$(find "${prd_dir}" -maxdepth 1 -name '*.md' -type f 2>/dev/null | sort)
|
|
232
232
|
|
|
233
233
|
if [ -z "${prd_files}" ]; then
|
|
234
234
|
return 0
|
|
@@ -28,6 +28,24 @@ TARGET_PR="${NW_TARGET_PR:-}"
|
|
|
28
28
|
PARALLEL_ENABLED="${NW_REVIEWER_PARALLEL:-1}"
|
|
29
29
|
WORKER_MODE="${NW_REVIEWER_WORKER_MODE:-0}"
|
|
30
30
|
|
|
31
|
+
# Retry configuration
|
|
32
|
+
REVIEWER_MAX_RETRIES="${NW_REVIEWER_MAX_RETRIES:-2}"
|
|
33
|
+
REVIEWER_RETRY_DELAY="${NW_REVIEWER_RETRY_DELAY:-30}"
|
|
34
|
+
|
|
35
|
+
# Normalize retry settings to safe numeric ranges
|
|
36
|
+
if ! [[ "${REVIEWER_MAX_RETRIES}" =~ ^[0-9]+$ ]]; then
|
|
37
|
+
REVIEWER_MAX_RETRIES="2"
|
|
38
|
+
fi
|
|
39
|
+
if ! [[ "${REVIEWER_RETRY_DELAY}" =~ ^[0-9]+$ ]]; then
|
|
40
|
+
REVIEWER_RETRY_DELAY="30"
|
|
41
|
+
fi
|
|
42
|
+
if [ "${REVIEWER_MAX_RETRIES}" -gt 10 ]; then
|
|
43
|
+
REVIEWER_MAX_RETRIES="10"
|
|
44
|
+
fi
|
|
45
|
+
if [ "${REVIEWER_RETRY_DELAY}" -gt 300 ]; then
|
|
46
|
+
REVIEWER_RETRY_DELAY="300"
|
|
47
|
+
fi
|
|
48
|
+
|
|
31
49
|
# Ensure NVM / Node / Claude are on PATH
|
|
32
50
|
export NVM_DIR="${HOME}/.nvm"
|
|
33
51
|
[ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
|
|
@@ -64,16 +82,31 @@ emit_final_status() {
|
|
|
64
82
|
local prs_csv="${2:-}"
|
|
65
83
|
local auto_merged="${3:-}"
|
|
66
84
|
local auto_merge_failed="${4:-}"
|
|
85
|
+
local attempts="${5:-1}"
|
|
86
|
+
local final_score="${6:-}"
|
|
87
|
+
local details=""
|
|
67
88
|
|
|
68
89
|
if [ "${exit_code}" -eq 0 ]; then
|
|
90
|
+
details="prs=${prs_csv}|auto_merged=${auto_merged}|auto_merge_failed=${auto_merge_failed}|attempts=${attempts}"
|
|
91
|
+
if [ -n "${final_score}" ]; then
|
|
92
|
+
details="${details}|final_score=${final_score}"
|
|
93
|
+
fi
|
|
69
94
|
log "DONE: PR reviewer completed successfully"
|
|
70
|
-
emit_result "success_reviewed" "
|
|
95
|
+
emit_result "success_reviewed" "${details}"
|
|
71
96
|
elif [ "${exit_code}" -eq 124 ]; then
|
|
97
|
+
details="prs=${prs_csv}|attempts=${attempts}"
|
|
98
|
+
if [ -n "${final_score}" ]; then
|
|
99
|
+
details="${details}|final_score=${final_score}"
|
|
100
|
+
fi
|
|
72
101
|
log "TIMEOUT: PR reviewer killed after ${MAX_RUNTIME}s"
|
|
73
|
-
emit_result "timeout" "
|
|
102
|
+
emit_result "timeout" "${details}"
|
|
74
103
|
else
|
|
104
|
+
details="prs=${prs_csv}|attempts=${attempts}"
|
|
105
|
+
if [ -n "${final_score}" ]; then
|
|
106
|
+
details="${details}|final_score=${final_score}"
|
|
107
|
+
fi
|
|
75
108
|
log "FAIL: PR reviewer exited with code ${exit_code}"
|
|
76
|
-
emit_result "failure" "
|
|
109
|
+
emit_result "failure" "${details}"
|
|
77
110
|
fi
|
|
78
111
|
}
|
|
79
112
|
|
|
@@ -91,6 +124,25 @@ append_csv() {
|
|
|
91
124
|
fi
|
|
92
125
|
}
|
|
93
126
|
|
|
127
|
+
# Extract the latest review score from PR comments
|
|
128
|
+
# Returns empty string if no score found
|
|
129
|
+
get_pr_score() {
|
|
130
|
+
local pr_number="${1:?PR number required}"
|
|
131
|
+
local all_comments
|
|
132
|
+
all_comments=$(
|
|
133
|
+
{
|
|
134
|
+
gh pr view "${pr_number}" --json comments --jq '.comments[].body' 2>/dev/null || true
|
|
135
|
+
if [ -n "${REPO:-}" ]; then
|
|
136
|
+
gh api "repos/${REPO}/issues/${pr_number}/comments" --jq '.[].body' 2>/dev/null || true
|
|
137
|
+
fi
|
|
138
|
+
} | sort -u
|
|
139
|
+
)
|
|
140
|
+
echo "${all_comments}" \
|
|
141
|
+
| grep -oP 'Overall Score:\*?\*?\s*(\d+)/100' \
|
|
142
|
+
| tail -1 \
|
|
143
|
+
| grep -oP '\d+(?=/100)' || echo ""
|
|
144
|
+
}
|
|
145
|
+
|
|
94
146
|
# Validate provider
|
|
95
147
|
if ! validate_provider "${PROVIDER_CMD}"; then
|
|
96
148
|
echo "ERROR: Unknown provider: ${PROVIDER_CMD}" >&2
|
|
@@ -323,6 +375,8 @@ if [ -z "${TARGET_PR}" ] && [ "${WORKER_MODE}" != "1" ] && [ "${PARALLEL_ENABLED
|
|
|
323
375
|
EXIT_CODE=0
|
|
324
376
|
AUTO_MERGED_PRS=""
|
|
325
377
|
AUTO_MERGE_FAILED_PRS=""
|
|
378
|
+
MAX_WORKER_ATTEMPTS=1
|
|
379
|
+
MAX_WORKER_FINAL_SCORE=""
|
|
326
380
|
|
|
327
381
|
for idx in "${!WORKER_PIDS[@]}"; do
|
|
328
382
|
worker_pid="${WORKER_PIDS[$idx]}"
|
|
@@ -344,10 +398,21 @@ if [ -z "${TARGET_PR}" ] && [ "${WORKER_MODE}" != "1" ] && [ "${PARALLEL_ENABLED
|
|
|
344
398
|
worker_status=$(printf '%s' "${worker_result}" | sed -n 's/^NIGHT_WATCH_RESULT:\([^|]*\).*$/\1/p')
|
|
345
399
|
worker_auto_merged=$(printf '%s' "${worker_result}" | grep -oP '(?<=auto_merged=)[^|]+' || true)
|
|
346
400
|
worker_auto_merge_failed=$(printf '%s' "${worker_result}" | grep -oP '(?<=auto_merge_failed=)[^|]+' || true)
|
|
401
|
+
worker_attempts=$(printf '%s' "${worker_result}" | grep -oP '(?<=attempts=)[^|]+' || true)
|
|
402
|
+
worker_final_score=$(printf '%s' "${worker_result}" | grep -oP '(?<=final_score=)[^|]+' || true)
|
|
347
403
|
|
|
348
404
|
AUTO_MERGED_PRS=$(append_csv "${AUTO_MERGED_PRS}" "${worker_auto_merged}")
|
|
349
405
|
AUTO_MERGE_FAILED_PRS=$(append_csv "${AUTO_MERGE_FAILED_PRS}" "${worker_auto_merge_failed}")
|
|
350
406
|
|
|
407
|
+
if [[ "${worker_attempts}" =~ ^[0-9]+$ ]] && [ "${worker_attempts}" -gt "${MAX_WORKER_ATTEMPTS}" ]; then
|
|
408
|
+
MAX_WORKER_ATTEMPTS="${worker_attempts}"
|
|
409
|
+
fi
|
|
410
|
+
if [[ "${worker_final_score}" =~ ^[0-9]+$ ]]; then
|
|
411
|
+
if [ -z "${MAX_WORKER_FINAL_SCORE}" ] || [ "${worker_final_score}" -gt "${MAX_WORKER_FINAL_SCORE}" ]; then
|
|
412
|
+
MAX_WORKER_FINAL_SCORE="${worker_final_score}"
|
|
413
|
+
fi
|
|
414
|
+
fi
|
|
415
|
+
|
|
351
416
|
rm -f "${worker_output}"
|
|
352
417
|
|
|
353
418
|
if [ "${worker_status}" = "failure" ] || { [ -n "${worker_status}" ] && [ "${worker_status}" != "success_reviewed" ] && [ "${worker_status}" != "timeout" ] && [ "${worker_status#skip_}" = "${worker_status}" ]; }; then
|
|
@@ -374,7 +439,7 @@ if [ -z "${TARGET_PR}" ] && [ "${WORKER_MODE}" != "1" ] && [ "${PARALLEL_ENABLED
|
|
|
374
439
|
fi
|
|
375
440
|
done
|
|
376
441
|
|
|
377
|
-
emit_final_status "${EXIT_CODE}" "${PRS_NEEDING_WORK_CSV}" "${AUTO_MERGED_PRS}" "${AUTO_MERGE_FAILED_PRS}"
|
|
442
|
+
emit_final_status "${EXIT_CODE}" "${PRS_NEEDING_WORK_CSV}" "${AUTO_MERGED_PRS}" "${AUTO_MERGE_FAILED_PRS}" "${MAX_WORKER_ATTEMPTS}" "${MAX_WORKER_FINAL_SCORE}"
|
|
378
443
|
exit 0
|
|
379
444
|
fi
|
|
380
445
|
|
|
@@ -396,6 +461,8 @@ if [ "${NW_DRY_RUN:-0}" = "1" ]; then
|
|
|
396
461
|
if [ "${AUTO_MERGE}" = "1" ]; then
|
|
397
462
|
echo "Auto-merge Method: ${AUTO_MERGE_METHOD}"
|
|
398
463
|
fi
|
|
464
|
+
echo "Max Retries: ${REVIEWER_MAX_RETRIES}"
|
|
465
|
+
echo "Retry Delay: ${REVIEWER_RETRY_DELAY}s"
|
|
399
466
|
echo "Open PRs needing work:${PRS_NEEDING_WORK}"
|
|
400
467
|
echo "Default Branch: ${DEFAULT_BRANCH}"
|
|
401
468
|
echo "Review Worktree: ${REVIEW_WORKTREE_DIR}"
|
|
@@ -413,44 +480,110 @@ if ! prepare_detached_worktree "${PROJECT_DIR}" "${REVIEW_WORKTREE_DIR}" "${DEFA
|
|
|
413
480
|
fi
|
|
414
481
|
|
|
415
482
|
EXIT_CODE=0
|
|
483
|
+
ATTEMPTS_MADE=1
|
|
484
|
+
FINAL_SCORE=""
|
|
416
485
|
TARGET_SCOPE_PROMPT=""
|
|
417
486
|
if [ -n "${TARGET_PR}" ]; then
|
|
418
487
|
TARGET_SCOPE_PROMPT=$'\n\n## Target Scope\n- Only process PR #'"${TARGET_PR}"$'.\n- Ignore all other PRs.\n- If this PR no longer needs work, stop immediately.\n'
|
|
419
488
|
fi
|
|
420
489
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
490
|
+
# ── Retry Loop for Targeted PR Review ──────────────────────────────────────────
|
|
491
|
+
# Only retry when targeting a specific PR. Non-targeted mode handles all PRs in one shot.
|
|
492
|
+
TOTAL_ATTEMPTS=1
|
|
493
|
+
if [ -n "${TARGET_PR}" ]; then
|
|
494
|
+
TOTAL_ATTEMPTS=$((REVIEWER_MAX_RETRIES + 1))
|
|
495
|
+
fi
|
|
496
|
+
RUN_STARTED_AT=$(date +%s)
|
|
497
|
+
|
|
498
|
+
for ATTEMPT in $(seq 1 "${TOTAL_ATTEMPTS}"); do
|
|
499
|
+
ATTEMPTS_MADE="${ATTEMPT}"
|
|
500
|
+
|
|
501
|
+
ATTEMPT_TIMEOUT="${MAX_RUNTIME}"
|
|
502
|
+
if [ -n "${TARGET_PR}" ]; then
|
|
503
|
+
# Calculate timeout from remaining runtime budget.
|
|
504
|
+
NOW_TS=$(date +%s)
|
|
505
|
+
ELAPSED=$((NOW_TS - RUN_STARTED_AT))
|
|
506
|
+
REMAINING_BUDGET=$((MAX_RUNTIME - ELAPSED))
|
|
507
|
+
if [ "${REMAINING_BUDGET}" -le 0 ]; then
|
|
508
|
+
EXIT_CODE=124
|
|
509
|
+
log "RETRY: Runtime budget exhausted before attempt ${ATTEMPT}"
|
|
510
|
+
break
|
|
433
511
|
fi
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
if
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
512
|
+
|
|
513
|
+
REMAINING_ATTEMPTS=$((TOTAL_ATTEMPTS - ATTEMPT + 1))
|
|
514
|
+
ATTEMPT_TIMEOUT=$((REMAINING_BUDGET / REMAINING_ATTEMPTS))
|
|
515
|
+
if [ "${ATTEMPT_TIMEOUT}" -lt 1 ]; then
|
|
516
|
+
ATTEMPT_TIMEOUT=1
|
|
517
|
+
fi
|
|
518
|
+
fi
|
|
519
|
+
|
|
520
|
+
log "RETRY: Starting attempt ${ATTEMPT}/${TOTAL_ATTEMPTS} (timeout: ${ATTEMPT_TIMEOUT}s)"
|
|
521
|
+
|
|
522
|
+
case "${PROVIDER_CMD}" in
|
|
523
|
+
claude)
|
|
524
|
+
CLAUDE_PROMPT="/night-watch-pr-reviewer${TARGET_SCOPE_PROMPT}"
|
|
525
|
+
if (
|
|
526
|
+
cd "${REVIEW_WORKTREE_DIR}" && timeout "${ATTEMPT_TIMEOUT}" \
|
|
527
|
+
claude -p "${CLAUDE_PROMPT}" \
|
|
528
|
+
--dangerously-skip-permissions \
|
|
529
|
+
>> "${LOG_FILE}" 2>&1
|
|
530
|
+
); then
|
|
531
|
+
EXIT_CODE=0
|
|
532
|
+
else
|
|
533
|
+
EXIT_CODE=$?
|
|
534
|
+
fi
|
|
535
|
+
;;
|
|
536
|
+
codex)
|
|
537
|
+
CODEX_PROMPT="$(cat "${REVIEW_WORKTREE_DIR}/.claude/commands/night-watch-pr-reviewer.md")${TARGET_SCOPE_PROMPT}"
|
|
538
|
+
if (
|
|
539
|
+
cd "${REVIEW_WORKTREE_DIR}" && timeout "${ATTEMPT_TIMEOUT}" \
|
|
540
|
+
codex --quiet \
|
|
541
|
+
--yolo \
|
|
542
|
+
--prompt "${CODEX_PROMPT}" \
|
|
543
|
+
>> "${LOG_FILE}" 2>&1
|
|
544
|
+
); then
|
|
545
|
+
EXIT_CODE=0
|
|
546
|
+
else
|
|
547
|
+
EXIT_CODE=$?
|
|
548
|
+
fi
|
|
549
|
+
;;
|
|
550
|
+
*)
|
|
551
|
+
log "ERROR: Unknown provider: ${PROVIDER_CMD}"
|
|
552
|
+
exit 1
|
|
553
|
+
;;
|
|
554
|
+
esac
|
|
555
|
+
|
|
556
|
+
# If provider failed (non-zero exit), don't retry
|
|
557
|
+
if [ "${EXIT_CODE}" -ne 0 ]; then
|
|
558
|
+
log "RETRY: Provider exited with code ${EXIT_CODE}, not retrying"
|
|
559
|
+
break
|
|
560
|
+
fi
|
|
561
|
+
|
|
562
|
+
# Re-check score for the target PR (only in targeted mode)
|
|
563
|
+
if [ -n "${TARGET_PR}" ]; then
|
|
564
|
+
CURRENT_SCORE=$(get_pr_score "${TARGET_PR}")
|
|
565
|
+
if [ -z "${CURRENT_SCORE}" ]; then
|
|
566
|
+
log "RETRY: No review score found for PR #${TARGET_PR} after attempt ${ATTEMPT}; not retrying"
|
|
567
|
+
break
|
|
568
|
+
fi
|
|
569
|
+
|
|
570
|
+
FINAL_SCORE="${CURRENT_SCORE}"
|
|
571
|
+
if [ "${CURRENT_SCORE}" -ge "${MIN_REVIEW_SCORE}" ]; then
|
|
572
|
+
log "RETRY: PR #${TARGET_PR} now scores ${CURRENT_SCORE}/100 (>= ${MIN_REVIEW_SCORE}) after attempt ${ATTEMPT}"
|
|
573
|
+
break
|
|
574
|
+
fi
|
|
575
|
+
if [ "${ATTEMPT}" -lt "${TOTAL_ATTEMPTS}" ]; then
|
|
576
|
+
log "RETRY: PR #${TARGET_PR} scores ${CURRENT_SCORE:-unknown}/100 after attempt ${ATTEMPT}/${TOTAL_ATTEMPTS}, retrying in ${REVIEWER_RETRY_DELAY}s..."
|
|
577
|
+
sleep "${REVIEWER_RETRY_DELAY}"
|
|
445
578
|
else
|
|
446
|
-
|
|
579
|
+
log "RETRY: PR #${TARGET_PR} still at ${CURRENT_SCORE:-unknown}/100 after ${TOTAL_ATTEMPTS} attempts - giving up"
|
|
580
|
+
gh pr edit "${TARGET_PR}" --add-label "needs-human-review" 2>/dev/null || true
|
|
447
581
|
fi
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
esac
|
|
582
|
+
else
|
|
583
|
+
# Non-targeted mode: no retry (reviewer handles all PRs in one shot)
|
|
584
|
+
break
|
|
585
|
+
fi
|
|
586
|
+
done
|
|
454
587
|
|
|
455
588
|
cleanup_worktrees "${PROJECT_DIR}" "${REVIEW_WORKTREE_BASENAME}"
|
|
456
589
|
|
|
@@ -529,4 +662,4 @@ if [ "${AUTO_MERGE}" = "1" ] && [ ${EXIT_CODE} -eq 0 ]; then
|
|
|
529
662
|
done < <(gh pr list --state open --json number,headRefName --jq '.[] | [.number, .headRefName] | @tsv' 2>/dev/null || true)
|
|
530
663
|
fi
|
|
531
664
|
|
|
532
|
-
emit_final_status "${EXIT_CODE}" "${PRS_NEEDING_WORK_CSV}" "${AUTO_MERGED_PRS}" "${AUTO_MERGE_FAILED_PRS}"
|
|
665
|
+
emit_final_status "${EXIT_CODE}" "${PRS_NEEDING_WORK_CSV}" "${AUTO_MERGED_PRS}" "${AUTO_MERGE_FAILED_PRS}" "${ATTEMPTS_MADE}" "${FINAL_SCORE}"
|