@jonit-dev/night-watch-cli 1.7.27 → 1.7.29

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.
Files changed (72) hide show
  1. package/dist/shared/types.d.ts +1 -0
  2. package/dist/shared/types.d.ts.map +1 -1
  3. package/dist/src/cli.js +3 -0
  4. package/dist/src/cli.js.map +1 -1
  5. package/dist/src/commands/audit.d.ts +19 -0
  6. package/dist/src/commands/audit.d.ts.map +1 -0
  7. package/dist/src/commands/audit.js +98 -0
  8. package/dist/src/commands/audit.js.map +1 -0
  9. package/dist/src/commands/dashboard.js +1 -1
  10. package/dist/src/commands/dashboard.js.map +1 -1
  11. package/dist/src/commands/init.d.ts.map +1 -1
  12. package/dist/src/commands/init.js +1 -6
  13. package/dist/src/commands/init.js.map +1 -1
  14. package/dist/src/commands/install.d.ts +4 -0
  15. package/dist/src/commands/install.d.ts.map +1 -1
  16. package/dist/src/commands/install.js +25 -20
  17. package/dist/src/commands/install.js.map +1 -1
  18. package/dist/src/commands/logs.js +3 -3
  19. package/dist/src/commands/logs.js.map +1 -1
  20. package/dist/src/commands/prs.js +2 -2
  21. package/dist/src/commands/prs.js.map +1 -1
  22. package/dist/src/commands/review.d.ts.map +1 -1
  23. package/dist/src/commands/review.js +13 -5
  24. package/dist/src/commands/review.js.map +1 -1
  25. package/dist/src/commands/uninstall.d.ts.map +1 -1
  26. package/dist/src/commands/uninstall.js +3 -22
  27. package/dist/src/commands/uninstall.js.map +1 -1
  28. package/dist/src/config.d.ts.map +1 -1
  29. package/dist/src/config.js +30 -1
  30. package/dist/src/config.js.map +1 -1
  31. package/dist/src/constants.d.ts +10 -3
  32. package/dist/src/constants.d.ts.map +1 -1
  33. package/dist/src/constants.js +15 -2
  34. package/dist/src/constants.js.map +1 -1
  35. package/dist/src/server/index.d.ts.map +1 -1
  36. package/dist/src/server/index.js +50 -3
  37. package/dist/src/server/index.js.map +1 -1
  38. package/dist/src/slack/client.d.ts +3 -2
  39. package/dist/src/slack/client.d.ts.map +1 -1
  40. package/dist/src/slack/client.js +5 -6
  41. package/dist/src/slack/client.js.map +1 -1
  42. package/dist/src/slack/deliberation.d.ts +13 -1
  43. package/dist/src/slack/deliberation.d.ts.map +1 -1
  44. package/dist/src/slack/deliberation.js +585 -71
  45. package/dist/src/slack/deliberation.js.map +1 -1
  46. package/dist/src/slack/interaction-listener.d.ts +27 -9
  47. package/dist/src/slack/interaction-listener.d.ts.map +1 -1
  48. package/dist/src/slack/interaction-listener.js +357 -197
  49. package/dist/src/slack/interaction-listener.js.map +1 -1
  50. package/dist/src/storage/repositories/sqlite/agent-persona-repository.d.ts +3 -2
  51. package/dist/src/storage/repositories/sqlite/agent-persona-repository.d.ts.map +1 -1
  52. package/dist/src/storage/repositories/sqlite/agent-persona-repository.js +14 -11
  53. package/dist/src/storage/repositories/sqlite/agent-persona-repository.js.map +1 -1
  54. package/dist/src/types.d.ts +13 -0
  55. package/dist/src/types.d.ts.map +1 -1
  56. package/dist/src/utils/notify.d.ts.map +1 -1
  57. package/dist/src/utils/notify.js +5 -1
  58. package/dist/src/utils/notify.js.map +1 -1
  59. package/dist/src/utils/status-data.d.ts +2 -2
  60. package/dist/src/utils/status-data.d.ts.map +1 -1
  61. package/dist/src/utils/status-data.js +78 -123
  62. package/dist/src/utils/status-data.js.map +1 -1
  63. package/package.json +3 -1
  64. package/scripts/night-watch-audit-cron.sh +149 -0
  65. package/scripts/night-watch-cron.sh +33 -14
  66. package/scripts/night-watch-helpers.sh +10 -2
  67. package/scripts/night-watch-pr-reviewer-cron.sh +224 -18
  68. package/web/dist/assets/index-BiJf9LFT.js +458 -0
  69. package/web/dist/assets/index-OpSgvsYu.css +1 -0
  70. package/web/dist/index.html +2 -2
  71. package/web/dist/assets/index-CndIPm_F.js +0 -473
  72. package/web/dist/assets/index-w6Q6gxCS.css +0 -1
@@ -16,7 +16,7 @@ set -euo pipefail
16
16
  PROJECT_DIR="${1:?Usage: $0 /path/to/project}"
17
17
  PROJECT_NAME=$(basename "${PROJECT_DIR}")
18
18
  LOG_DIR="${PROJECT_DIR}/logs"
19
- LOG_FILE="${LOG_DIR}/night-watch-pr-reviewer.log"
19
+ LOG_FILE="${LOG_DIR}/reviewer.log"
20
20
  MAX_RUNTIME="${NW_REVIEWER_MAX_RUNTIME:-3600}" # 1 hour
21
21
  MAX_LOG_SIZE="524288" # 512 KB
22
22
  PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
@@ -25,6 +25,8 @@ BRANCH_PATTERNS_RAW="${NW_BRANCH_PATTERNS:-feat/,night-watch/}"
25
25
  AUTO_MERGE="${NW_AUTO_MERGE:-0}"
26
26
  AUTO_MERGE_METHOD="${NW_AUTO_MERGE_METHOD:-squash}"
27
27
  TARGET_PR="${NW_TARGET_PR:-}"
28
+ PARALLEL_ENABLED="${NW_REVIEWER_PARALLEL:-1}"
29
+ WORKER_MODE="${NW_REVIEWER_WORKER_MODE:-0}"
28
30
 
29
31
  # Ensure NVM / Node / Claude are on PATH
30
32
  export NVM_DIR="${HOME}/.nvm"
@@ -39,8 +41,13 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
39
41
  # shellcheck source=night-watch-helpers.sh
40
42
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
41
43
  PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
42
- # NOTE: Lock file path must match reviewerLockPath() in src/utils/status-data.ts
43
- LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}.lock"
44
+ GLOBAL_LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}.lock"
45
+ if [ "${WORKER_MODE}" = "1" ] && [ -n "${TARGET_PR}" ]; then
46
+ LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}-pr-${TARGET_PR}.lock"
47
+ else
48
+ # NOTE: Lock file path must match reviewerLockPath() in src/utils/status-data.ts
49
+ LOCK_FILE="${GLOBAL_LOCK_FILE}"
50
+ fi
44
51
 
45
52
  emit_result() {
46
53
  local status="${1:?status required}"
@@ -52,6 +59,38 @@ emit_result() {
52
59
  fi
53
60
  }
54
61
 
62
+ emit_final_status() {
63
+ local exit_code="${1:?exit code required}"
64
+ local prs_csv="${2:-}"
65
+ local auto_merged="${3:-}"
66
+ local auto_merge_failed="${4:-}"
67
+
68
+ if [ "${exit_code}" -eq 0 ]; then
69
+ log "DONE: PR reviewer completed successfully"
70
+ emit_result "success_reviewed" "prs=${prs_csv}|auto_merged=${auto_merged}|auto_merge_failed=${auto_merge_failed}"
71
+ elif [ "${exit_code}" -eq 124 ]; then
72
+ log "TIMEOUT: PR reviewer killed after ${MAX_RUNTIME}s"
73
+ emit_result "timeout" "prs=${prs_csv}"
74
+ else
75
+ log "FAIL: PR reviewer exited with code ${exit_code}"
76
+ emit_result "failure" "prs=${prs_csv}"
77
+ fi
78
+ }
79
+
80
+ append_csv() {
81
+ local current="${1:-}"
82
+ local incoming="${2:-}"
83
+ if [ -z "${incoming}" ]; then
84
+ printf "%s" "${current}"
85
+ return 0
86
+ fi
87
+ if [ -z "${current}" ]; then
88
+ printf "%s" "${incoming}"
89
+ else
90
+ printf "%s,%s" "${current}" "${incoming}"
91
+ fi
92
+ }
93
+
55
94
  # Validate provider
56
95
  if ! validate_provider "${PROVIDER_CMD}"; then
57
96
  echo "ERROR: Unknown provider: ${PROVIDER_CMD}" >&2
@@ -159,6 +198,62 @@ done < <(gh pr list --state open --json number,headRefName --jq '.[] | [.number,
159
198
 
160
199
  if [ "${NEEDS_WORK}" -eq 0 ]; then
161
200
  log "SKIP: All ${OPEN_PRS} open PR(s) have passing CI and review score >= ${MIN_REVIEW_SCORE} (or no score yet)"
201
+
202
+ # ── Auto-merge eligible PRs ───────────────────────────────
203
+ if [ "${NW_AUTO_MERGE:-0}" = "1" ]; then
204
+ AUTO_MERGE_METHOD="${NW_AUTO_MERGE_METHOD:-squash}"
205
+ AUTO_MERGED_COUNT=0
206
+
207
+ log "AUTO-MERGE: Checking for merge-ready PRs (method: ${AUTO_MERGE_METHOD})"
208
+
209
+ while IFS=$'\t' read -r pr_number pr_branch; do
210
+ [ -z "${pr_number}" ] || [ -z "${pr_branch}" ] && continue
211
+ printf '%s\n' "${pr_branch}" | grep -Eq "${BRANCH_REGEX}" || continue
212
+
213
+ # Check CI status
214
+ FAILED_CHECKS=$(gh pr checks "${pr_number}" 2>/dev/null | grep -ci 'fail' || true)
215
+ [ "${FAILED_CHECKS}" -gt 0 ] && continue
216
+
217
+ # Check review score
218
+ PR_COMMENTS=$(
219
+ {
220
+ gh pr view "${pr_number}" --json comments --jq '.comments[].body' 2>/dev/null || true
221
+ if [ -n "${REPO}" ]; then
222
+ gh api "repos/${REPO}/issues/${pr_number}/comments" --jq '.[].body' 2>/dev/null || true
223
+ fi
224
+ } | sort -u
225
+ )
226
+ PR_SCORE=$(echo "${PR_COMMENTS}" \
227
+ | grep -oP 'Overall Score:\*?\*?\s*(\d+)/100' \
228
+ | tail -1 \
229
+ | grep -oP '\d+(?=/100)' || echo "")
230
+
231
+ # Skip PRs without a score or with score below threshold
232
+ [ -z "${PR_SCORE}" ] && continue
233
+ [ "${PR_SCORE}" -lt "${MIN_REVIEW_SCORE}" ] && continue
234
+
235
+ # PR is merge-ready
236
+ log "AUTO-MERGE: PR #${pr_number} (${pr_branch}) — score ${PR_SCORE}/100, CI passing"
237
+
238
+ # Dry-run mode: show what would be merged
239
+ if [ "${NW_DRY_RUN:-0}" = "1" ]; then
240
+ log "AUTO-MERGE (dry-run): Would queue merge for PR #${pr_number} using ${AUTO_MERGE_METHOD}"
241
+ continue
242
+ fi
243
+
244
+ if gh pr merge "${pr_number}" --"${AUTO_MERGE_METHOD}" --auto --delete-branch 2>>"${LOG_FILE}"; then
245
+ log "AUTO-MERGE: Successfully queued merge for PR #${pr_number}"
246
+ AUTO_MERGED_COUNT=$((AUTO_MERGED_COUNT + 1))
247
+ else
248
+ log "WARN: Auto-merge failed for PR #${pr_number}"
249
+ fi
250
+ done < <(gh pr list --state open --json number,headRefName --jq '.[] | [.number, .headRefName] | @tsv' 2>/dev/null || true)
251
+
252
+ if [ "${AUTO_MERGED_COUNT}" -gt 0 ]; then
253
+ log "AUTO-MERGE: Queued ${AUTO_MERGED_COUNT} PR(s) for merge"
254
+ fi
255
+ fi
256
+
162
257
  emit_result "skip_all_passing"
163
258
  exit 0
164
259
  fi
@@ -172,11 +267,121 @@ if [ -n "${NW_DEFAULT_BRANCH:-}" ]; then
172
267
  else
173
268
  DEFAULT_BRANCH=$(detect_default_branch "${PROJECT_DIR}")
174
269
  fi
175
- REVIEW_WORKTREE_DIR="$(dirname "${PROJECT_DIR}")/${PROJECT_NAME}-nw-review-runner"
176
270
 
177
271
  log "START: Found PR(s) needing work:${PRS_NEEDING_WORK}"
178
272
 
179
- cleanup_worktrees "${PROJECT_DIR}"
273
+ # Convert "#12 #34" into ["12", "34"] for worker fan-out.
274
+ PR_NUMBER_ARRAY=()
275
+ for pr_token in ${PRS_NEEDING_WORK}; do
276
+ PR_NUMBER_ARRAY+=("${pr_token#\#}")
277
+ done
278
+
279
+ if [ -z "${TARGET_PR}" ] && [ "${WORKER_MODE}" != "1" ] && [ "${PARALLEL_ENABLED}" = "1" ] && [ "${#PR_NUMBER_ARRAY[@]}" -gt 1 ]; then
280
+ # Dry-run mode: print diagnostics and exit
281
+ if [ "${NW_DRY_RUN:-0}" = "1" ]; then
282
+ echo "=== Dry Run: PR Reviewer ==="
283
+ echo "Provider: ${PROVIDER_CMD}"
284
+ echo "Branch Patterns: ${BRANCH_PATTERNS_RAW}"
285
+ echo "Min Review Score: ${MIN_REVIEW_SCORE}"
286
+ echo "Auto-merge: ${AUTO_MERGE}"
287
+ if [ "${AUTO_MERGE}" = "1" ]; then
288
+ echo "Auto-merge Method: ${AUTO_MERGE_METHOD}"
289
+ fi
290
+ echo "Open PRs needing work:${PRS_NEEDING_WORK}"
291
+ echo "Default Branch: ${DEFAULT_BRANCH}"
292
+ echo "Parallel Workers: ${#PR_NUMBER_ARRAY[@]}"
293
+ echo "Timeout: ${MAX_RUNTIME}s"
294
+ exit 0
295
+ fi
296
+
297
+ log "PARALLEL: Launching ${#PR_NUMBER_ARRAY[@]} reviewer worker(s)"
298
+
299
+ declare -a WORKER_PIDS=()
300
+ declare -a WORKER_PRS=()
301
+ declare -a WORKER_OUTPUTS=()
302
+
303
+ for pr_number in "${PR_NUMBER_ARRAY[@]}"; do
304
+ worker_output=$(mktemp "/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}-pr-${pr_number}.XXXXXX")
305
+ WORKER_OUTPUTS+=("${worker_output}")
306
+ WORKER_PRS+=("${pr_number}")
307
+
308
+ (
309
+ NW_TARGET_PR="${pr_number}" \
310
+ NW_REVIEWER_WORKER_MODE="1" \
311
+ NW_REVIEWER_PARALLEL="0" \
312
+ bash "${SCRIPT_DIR}/night-watch-pr-reviewer-cron.sh" "${PROJECT_DIR}" > "${worker_output}" 2>&1
313
+ ) &
314
+
315
+ worker_pid=$!
316
+ WORKER_PIDS+=("${worker_pid}")
317
+ log "PARALLEL: Worker PID ${worker_pid} started for PR #${pr_number}"
318
+ done
319
+
320
+ EXIT_CODE=0
321
+ AUTO_MERGED_PRS=""
322
+ AUTO_MERGE_FAILED_PRS=""
323
+
324
+ for idx in "${!WORKER_PIDS[@]}"; do
325
+ worker_pid="${WORKER_PIDS[$idx]}"
326
+ worker_pr="${WORKER_PRS[$idx]}"
327
+ worker_output="${WORKER_OUTPUTS[$idx]}"
328
+
329
+ worker_exit_code=0
330
+ if wait "${worker_pid}"; then
331
+ worker_exit_code=0
332
+ else
333
+ worker_exit_code=$?
334
+ fi
335
+
336
+ if [ -f "${worker_output}" ] && [ -s "${worker_output}" ]; then
337
+ cat "${worker_output}" >> "${LOG_FILE}"
338
+ fi
339
+
340
+ worker_result=$(grep -o 'NIGHT_WATCH_RESULT:.*' "${worker_output}" 2>/dev/null | tail -1 || true)
341
+ worker_status=$(printf '%s' "${worker_result}" | sed -n 's/^NIGHT_WATCH_RESULT:\([^|]*\).*$/\1/p')
342
+ worker_auto_merged=$(printf '%s' "${worker_result}" | grep -oP '(?<=auto_merged=)[^|]+' || true)
343
+ worker_auto_merge_failed=$(printf '%s' "${worker_result}" | grep -oP '(?<=auto_merge_failed=)[^|]+' || true)
344
+
345
+ AUTO_MERGED_PRS=$(append_csv "${AUTO_MERGED_PRS}" "${worker_auto_merged}")
346
+ AUTO_MERGE_FAILED_PRS=$(append_csv "${AUTO_MERGE_FAILED_PRS}" "${worker_auto_merge_failed}")
347
+
348
+ rm -f "${worker_output}"
349
+
350
+ if [ "${worker_status}" = "failure" ] || { [ -n "${worker_status}" ] && [ "${worker_status}" != "success_reviewed" ] && [ "${worker_status}" != "timeout" ] && [ "${worker_status#skip_}" = "${worker_status}" ]; }; then
351
+ if [ "${EXIT_CODE}" -eq 0 ] || [ "${EXIT_CODE}" -eq 124 ]; then
352
+ EXIT_CODE=1
353
+ fi
354
+ log "PARALLEL: Worker for PR #${worker_pr} reported status '${worker_status:-unknown}'"
355
+ elif [ "${worker_status}" = "timeout" ]; then
356
+ if [ "${EXIT_CODE}" -eq 0 ]; then
357
+ EXIT_CODE=124
358
+ fi
359
+ log "PARALLEL: Worker for PR #${worker_pr} timed out"
360
+ elif [ "${worker_exit_code}" -ne 0 ]; then
361
+ if [ "${worker_exit_code}" -eq 124 ]; then
362
+ if [ "${EXIT_CODE}" -eq 0 ]; then
363
+ EXIT_CODE=124
364
+ fi
365
+ elif [ "${EXIT_CODE}" -eq 0 ] || [ "${EXIT_CODE}" -eq 124 ]; then
366
+ EXIT_CODE="${worker_exit_code}"
367
+ fi
368
+ log "PARALLEL: Worker for PR #${worker_pr} exited with code ${worker_exit_code}"
369
+ else
370
+ log "PARALLEL: Worker for PR #${worker_pr} completed"
371
+ fi
372
+ done
373
+
374
+ emit_final_status "${EXIT_CODE}" "${PRS_NEEDING_WORK_CSV}" "${AUTO_MERGED_PRS}" "${AUTO_MERGE_FAILED_PRS}"
375
+ exit 0
376
+ fi
377
+
378
+ REVIEW_WORKTREE_BASENAME="${PROJECT_NAME}-nw-review-runner"
379
+ if [ -n "${TARGET_PR}" ]; then
380
+ REVIEW_WORKTREE_BASENAME="${REVIEW_WORKTREE_BASENAME}-pr-${TARGET_PR}"
381
+ fi
382
+ REVIEW_WORKTREE_DIR="$(dirname "${PROJECT_DIR}")/${REVIEW_WORKTREE_BASENAME}"
383
+
384
+ cleanup_worktrees "${PROJECT_DIR}" "${REVIEW_WORKTREE_BASENAME}"
180
385
 
181
386
  # Dry-run mode: print diagnostics and exit
182
387
  if [ "${NW_DRY_RUN:-0}" = "1" ]; then
@@ -191,6 +396,10 @@ if [ "${NW_DRY_RUN:-0}" = "1" ]; then
191
396
  echo "Open PRs needing work:${PRS_NEEDING_WORK}"
192
397
  echo "Default Branch: ${DEFAULT_BRANCH}"
193
398
  echo "Review Worktree: ${REVIEW_WORKTREE_DIR}"
399
+ echo "Target PR: ${TARGET_PR:-all}"
400
+ if [ -n "${TARGET_PR}" ]; then
401
+ echo "Worker Mode: ${WORKER_MODE}"
402
+ fi
194
403
  echo "Timeout: ${MAX_RUNTIME}s"
195
404
  exit 0
196
405
  fi
@@ -201,12 +410,17 @@ if ! prepare_detached_worktree "${PROJECT_DIR}" "${REVIEW_WORKTREE_DIR}" "${DEFA
201
410
  fi
202
411
 
203
412
  EXIT_CODE=0
413
+ TARGET_SCOPE_PROMPT=""
414
+ if [ -n "${TARGET_PR}" ]; then
415
+ 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'
416
+ fi
204
417
 
205
418
  case "${PROVIDER_CMD}" in
206
419
  claude)
420
+ CLAUDE_PROMPT="/night-watch-pr-reviewer${TARGET_SCOPE_PROMPT}"
207
421
  if (
208
422
  cd "${REVIEW_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
209
- claude -p "/night-watch-pr-reviewer" \
423
+ claude -p "${CLAUDE_PROMPT}" \
210
424
  --dangerously-skip-permissions \
211
425
  >> "${LOG_FILE}" 2>&1
212
426
  ); then
@@ -216,11 +430,12 @@ case "${PROVIDER_CMD}" in
216
430
  fi
217
431
  ;;
218
432
  codex)
433
+ CODEX_PROMPT="$(cat "${REVIEW_WORKTREE_DIR}/.claude/commands/night-watch-pr-reviewer.md")${TARGET_SCOPE_PROMPT}"
219
434
  if (
220
435
  cd "${REVIEW_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
221
436
  codex --quiet \
222
437
  --yolo \
223
- --prompt "$(cat "${REVIEW_WORKTREE_DIR}/.claude/commands/night-watch-pr-reviewer.md")" \
438
+ --prompt "${CODEX_PROMPT}" \
224
439
  >> "${LOG_FILE}" 2>&1
225
440
  ); then
226
441
  EXIT_CODE=0
@@ -234,7 +449,7 @@ case "${PROVIDER_CMD}" in
234
449
  ;;
235
450
  esac
236
451
 
237
- cleanup_worktrees "${PROJECT_DIR}"
452
+ cleanup_worktrees "${PROJECT_DIR}" "${REVIEW_WORKTREE_BASENAME}"
238
453
 
239
454
  # ── Auto-merge eligible PRs ─────────────────────────────────────────────────────
240
455
  # After the reviewer completes, check for PRs that are merge-ready and queue them
@@ -310,13 +525,4 @@ if [ "${AUTO_MERGE}" = "1" ] && [ ${EXIT_CODE} -eq 0 ]; then
310
525
  done < <(gh pr list --state open --json number,headRefName --jq '.[] | [.number, .headRefName] | @tsv' 2>/dev/null || true)
311
526
  fi
312
527
 
313
- if [ ${EXIT_CODE} -eq 0 ]; then
314
- log "DONE: PR reviewer completed successfully"
315
- emit_result "success_reviewed" "prs=${PRS_NEEDING_WORK_CSV}|auto_merged=${AUTO_MERGED_PRS}|auto_merge_failed=${AUTO_MERGE_FAILED_PRS}"
316
- elif [ ${EXIT_CODE} -eq 124 ]; then
317
- log "TIMEOUT: PR reviewer killed after ${MAX_RUNTIME}s"
318
- emit_result "timeout" "prs=${PRS_NEEDING_WORK_CSV}"
319
- else
320
- log "FAIL: PR reviewer exited with code ${EXIT_CODE}"
321
- emit_result "failure" "prs=${PRS_NEEDING_WORK_CSV}"
322
- fi
528
+ emit_final_status "${EXIT_CODE}" "${PRS_NEEDING_WORK_CSV}" "${AUTO_MERGED_PRS}" "${AUTO_MERGE_FAILED_PRS}"