@jonit-dev/night-watch-cli 1.7.50 → 1.7.52

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 (36) hide show
  1. package/dist/cli.js +393 -229
  2. package/dist/commands/audit.d.ts.map +1 -1
  3. package/dist/commands/audit.js +6 -24
  4. package/dist/commands/audit.js.map +1 -1
  5. package/dist/commands/init.d.ts.map +1 -1
  6. package/dist/commands/init.js +20 -23
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/qa.d.ts.map +1 -1
  9. package/dist/commands/qa.js +16 -4
  10. package/dist/commands/qa.js.map +1 -1
  11. package/dist/commands/review.d.ts.map +1 -1
  12. package/dist/commands/review.js +6 -4
  13. package/dist/commands/review.js.map +1 -1
  14. package/dist/commands/shared/env-builder.d.ts +5 -0
  15. package/dist/commands/shared/env-builder.d.ts.map +1 -1
  16. package/dist/commands/shared/env-builder.js +32 -0
  17. package/dist/commands/shared/env-builder.js.map +1 -1
  18. package/dist/commands/slice.d.ts +8 -0
  19. package/dist/commands/slice.d.ts.map +1 -1
  20. package/dist/commands/slice.js +90 -2
  21. package/dist/commands/slice.js.map +1 -1
  22. package/dist/scripts/night-watch-audit-cron.sh +17 -4
  23. package/dist/scripts/night-watch-cron.sh +19 -5
  24. package/dist/scripts/night-watch-helpers.sh +137 -0
  25. package/dist/scripts/night-watch-pr-reviewer-cron.sh +268 -5
  26. package/dist/scripts/night-watch-qa-cron.sh +427 -22
  27. package/dist/scripts/night-watch-slicer-cron.sh +14 -3
  28. package/dist/templates/audit.md +87 -0
  29. package/dist/templates/executor.md +67 -0
  30. package/dist/templates/night-watch-pr-reviewer.md +33 -0
  31. package/dist/templates/night-watch.config.json +31 -1
  32. package/dist/templates/night-watch.md +31 -0
  33. package/dist/templates/pr-reviewer.md +203 -0
  34. package/dist/templates/qa.md +157 -0
  35. package/dist/templates/slicer.md +234 -0
  36. package/package.json +1 -1
@@ -20,6 +20,7 @@ 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}"
23
+ PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
23
24
  MIN_REVIEW_SCORE="${NW_MIN_REVIEW_SCORE:-80}"
24
25
  BRANCH_PATTERNS_RAW="${NW_BRANCH_PATTERNS:-feat/,night-watch/}"
25
26
  AUTO_MERGE="${NW_AUTO_MERGE:-0}"
@@ -27,6 +28,12 @@ AUTO_MERGE_METHOD="${NW_AUTO_MERGE_METHOD:-squash}"
27
28
  TARGET_PR="${NW_TARGET_PR:-}"
28
29
  PARALLEL_ENABLED="${NW_REVIEWER_PARALLEL:-1}"
29
30
  WORKER_MODE="${NW_REVIEWER_WORKER_MODE:-0}"
31
+ PRD_DIR_REL="${NW_PRD_DIR:-docs/PRDs/night-watch}"
32
+ if [[ "${PRD_DIR_REL}" = /* ]]; then
33
+ PRD_DIR="${PRD_DIR_REL}"
34
+ else
35
+ PRD_DIR="${PROJECT_DIR}/${PRD_DIR_REL}"
36
+ fi
30
37
 
31
38
  # Retry configuration
32
39
  REVIEWER_MAX_RETRIES="${NW_REVIEWER_MAX_RETRIES:-2}"
@@ -59,6 +66,7 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
59
66
  # shellcheck source=night-watch-helpers.sh
60
67
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
61
68
  PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
69
+ PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
62
70
  GLOBAL_LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}.lock"
63
71
  if [ "${WORKER_MODE}" = "1" ] && [ -n "${TARGET_PR}" ]; then
64
72
  LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}-pr-${TARGET_PR}.lock"
@@ -85,6 +93,21 @@ emit_final_status() {
85
93
  local attempts="${5:-1}"
86
94
  local final_score="${6:-}"
87
95
  local details=""
96
+ local prs_summary=""
97
+ local auto_merged_summary=""
98
+ local auto_merge_failed_summary=""
99
+ local final_score_summary=""
100
+ local final_score_line=""
101
+
102
+ prs_summary="${prs_csv:-none}"
103
+ auto_merged_summary="${auto_merged:-none}"
104
+ auto_merge_failed_summary="${auto_merge_failed:-none}"
105
+ final_score_summary="${final_score:-n/a}"
106
+ if [ -n "${final_score}" ]; then
107
+ final_score_line="Final score: ${final_score_summary}/100"
108
+ else
109
+ final_score_line="Final score: n/a"
110
+ fi
88
111
 
89
112
  if [ "${exit_code}" -eq 0 ]; then
90
113
  details="prs=${prs_csv}|auto_merged=${auto_merged}|auto_merge_failed=${auto_merge_failed}|attempts=${attempts}"
@@ -92,6 +115,15 @@ emit_final_status() {
92
115
  details="${details}|final_score=${final_score}"
93
116
  fi
94
117
  log "DONE: PR reviewer completed successfully"
118
+ if [ "${WORKER_MODE}" != "1" ]; then
119
+ send_telegram_status_message "🔍 Night Watch Reviewer: completed" "Project: ${PROJECT_NAME}
120
+ Provider (model): ${PROVIDER_MODEL_DISPLAY}
121
+ Processed PRs: ${prs_summary}
122
+ Attempts: ${attempts}
123
+ ${final_score_line}
124
+ Auto-merged PRs: ${auto_merged_summary}
125
+ Auto-merge failed: ${auto_merge_failed_summary}"
126
+ fi
95
127
  emit_result "success_reviewed" "${details}"
96
128
  elif [ "${exit_code}" -eq 124 ]; then
97
129
  details="prs=${prs_csv}|attempts=${attempts}"
@@ -99,6 +131,14 @@ emit_final_status() {
99
131
  details="${details}|final_score=${final_score}"
100
132
  fi
101
133
  log "TIMEOUT: PR reviewer killed after ${MAX_RUNTIME}s"
134
+ if [ "${WORKER_MODE}" != "1" ]; then
135
+ send_telegram_status_message "🔍 Night Watch Reviewer: timeout" "Project: ${PROJECT_NAME}
136
+ Provider (model): ${PROVIDER_MODEL_DISPLAY}
137
+ Timeout: ${MAX_RUNTIME}s
138
+ Processed PRs: ${prs_summary}
139
+ Attempts: ${attempts}
140
+ ${final_score_line}"
141
+ fi
102
142
  emit_result "timeout" "${details}"
103
143
  else
104
144
  details="prs=${prs_csv}|attempts=${attempts}"
@@ -106,6 +146,14 @@ emit_final_status() {
106
146
  details="${details}|final_score=${final_score}"
107
147
  fi
108
148
  log "FAIL: PR reviewer exited with code ${exit_code}"
149
+ if [ "${WORKER_MODE}" != "1" ]; then
150
+ send_telegram_status_message "🔍 Night Watch Reviewer: failed" "Project: ${PROJECT_NAME}
151
+ Provider (model): ${PROVIDER_MODEL_DISPLAY}
152
+ Exit code: ${exit_code}
153
+ Processed PRs: ${prs_summary}
154
+ Attempts: ${attempts}
155
+ ${final_score_line}"
156
+ fi
109
157
  emit_result "failure" "${details}"
110
158
  fi
111
159
  }
@@ -124,6 +172,175 @@ append_csv() {
124
172
  fi
125
173
  }
126
174
 
175
+ truncate_for_prompt() {
176
+ local text="${1:-}"
177
+ local limit="${2:-7000}"
178
+ if [ "${#text}" -le "${limit}" ]; then
179
+ printf "%s" "${text}"
180
+ else
181
+ printf '%s\n\n[truncated to %s chars]' "${text:0:${limit}}" "${limit}"
182
+ fi
183
+ }
184
+
185
+ extract_linked_issue_numbers() {
186
+ local body="${1:-}"
187
+ printf '%s\n' "${body}" \
188
+ | grep -Eoi '(close[sd]?|fix(e[sd])?|resolve[sd]?)[[:space:]]*:?[[:space:]]*#[0-9]+' \
189
+ | grep -Eo '[0-9]+' \
190
+ | awk '!seen[$0]++' || true
191
+ }
192
+
193
+ find_prd_file_by_branch() {
194
+ local branch_name="${1:-}"
195
+ local branch_slug="${branch_name#*/}"
196
+ local branch_number=""
197
+ local candidate_dirs=()
198
+ local candidate=""
199
+ local base_name=""
200
+ local dir=""
201
+
202
+ if [ -z "${branch_slug}" ]; then
203
+ branch_slug="${branch_name}"
204
+ fi
205
+ [ -z "${branch_slug}" ] && return 1
206
+
207
+ if [ -d "${PRD_DIR}" ]; then
208
+ candidate_dirs+=("${PRD_DIR}")
209
+ fi
210
+ if [ -d "${PRD_DIR}/done" ]; then
211
+ candidate_dirs+=("${PRD_DIR}/done")
212
+ fi
213
+ [ "${#candidate_dirs[@]}" -eq 0 ] && return 1
214
+
215
+ for dir in "${candidate_dirs[@]}"; do
216
+ if [ -f "${dir}/${branch_slug}.md" ]; then
217
+ printf "%s" "${dir}/${branch_slug}.md"
218
+ return 0
219
+ fi
220
+ done
221
+
222
+ branch_number=$(printf '%s' "${branch_slug}" | grep -oE '^[0-9]+' || true)
223
+ for dir in "${candidate_dirs[@]}"; do
224
+ while IFS= read -r candidate; do
225
+ [ -z "${candidate}" ] && continue
226
+ base_name=$(basename "${candidate}" .md)
227
+ if [[ "${base_name}" == "${branch_slug}"* ]] || [[ "${branch_slug}" == "${base_name}"* ]]; then
228
+ printf "%s" "${candidate}"
229
+ return 0
230
+ fi
231
+ if [ -n "${branch_number}" ] && [[ "${base_name}" == "${branch_number}-"* ]]; then
232
+ printf "%s" "${candidate}"
233
+ return 0
234
+ fi
235
+ done < <(find "${dir}" -maxdepth 1 -type f -name '*.md' 2>/dev/null | sort)
236
+ done
237
+
238
+ return 1
239
+ }
240
+
241
+ build_prd_context_for_pr() {
242
+ local pr_number="${1:?PR number required}"
243
+ local pr_payload=""
244
+ local pr_title=""
245
+ local pr_branch=""
246
+ local pr_body=""
247
+ local pr_url=""
248
+ local issue_context=""
249
+ local issue_count=0
250
+ local issue_number=""
251
+ local issue_payload=""
252
+ local issue_title=""
253
+ local issue_body=""
254
+ local issue_excerpt=""
255
+ local prd_file=""
256
+ local prd_payload=""
257
+ local prd_excerpt=""
258
+ local prd_rel_path=""
259
+ local section=""
260
+
261
+ pr_payload=$(gh pr view "${pr_number}" --json title,headRefName,body,url 2>/dev/null || true)
262
+ pr_title=$(printf '%s' "${pr_payload}" | jq -r '.title // ""' 2>/dev/null || echo "")
263
+ pr_branch=$(printf '%s' "${pr_payload}" | jq -r '.headRefName // ""' 2>/dev/null || echo "")
264
+ pr_body=$(printf '%s' "${pr_payload}" | jq -r '.body // ""' 2>/dev/null || echo "")
265
+ pr_url=$(printf '%s' "${pr_payload}" | jq -r '.url // ""' 2>/dev/null || echo "")
266
+
267
+ if [ -n "${pr_body}" ]; then
268
+ while IFS= read -r issue_number; do
269
+ [ -z "${issue_number}" ] && continue
270
+ issue_count=$((issue_count + 1))
271
+ if [ "${issue_count}" -gt 2 ]; then
272
+ break
273
+ fi
274
+
275
+ issue_payload=$(gh issue view "${issue_number}" --json title,body,url 2>/dev/null || true)
276
+ issue_title=$(printf '%s' "${issue_payload}" | jq -r '.title // ""' 2>/dev/null || echo "")
277
+ issue_body=$(printf '%s' "${issue_payload}" | jq -r '.body // ""' 2>/dev/null || echo "")
278
+ [ -z "${issue_body}" ] && continue
279
+
280
+ issue_excerpt=$(truncate_for_prompt "${issue_body}" 4500)
281
+ issue_context="${issue_context}${issue_context:+$'\n\n'}Issue #${issue_number}: ${issue_title}
282
+ ${issue_excerpt}"
283
+ done < <(extract_linked_issue_numbers "${pr_body}")
284
+ fi
285
+
286
+ if [ -z "${issue_context}" ] && [ -n "${pr_branch}" ]; then
287
+ prd_file=$(find_prd_file_by_branch "${pr_branch}" || true)
288
+ if [ -n "${prd_file}" ] && [ -f "${prd_file}" ]; then
289
+ prd_payload=$(cat "${prd_file}" 2>/dev/null || true)
290
+ if [ -n "${prd_payload}" ]; then
291
+ prd_excerpt=$(truncate_for_prompt "${prd_payload}" 4500)
292
+ if [[ "${prd_file}" == "${PROJECT_DIR}/"* ]]; then
293
+ prd_rel_path="${prd_file#${PROJECT_DIR}/}"
294
+ else
295
+ prd_rel_path="${prd_file}"
296
+ fi
297
+ fi
298
+ fi
299
+ fi
300
+
301
+ section="### PR #${pr_number}"
302
+ if [ -n "${pr_title}" ]; then
303
+ section="${section} — ${pr_title}"
304
+ fi
305
+ section="${section}
306
+ - branch: ${pr_branch:-unknown}"
307
+ if [ -n "${pr_url}" ]; then
308
+ section="${section}
309
+ - url: ${pr_url}"
310
+ fi
311
+
312
+ if [ -n "${issue_context}" ]; then
313
+ section="${section}
314
+ - context source: linked GitHub issue body
315
+ ${issue_context}"
316
+ elif [ -n "${prd_excerpt}" ]; then
317
+ section="${section}
318
+ - context source: ${prd_rel_path}
319
+ ${prd_excerpt}"
320
+ else
321
+ section="${section}
322
+ - context source: not found"
323
+ fi
324
+
325
+ printf "%s" "${section}"
326
+ }
327
+
328
+ build_prd_context_prompt() {
329
+ local pr_number=""
330
+ local entry=""
331
+ local combined=""
332
+
333
+ for pr_number in "$@"; do
334
+ [ -z "${pr_number}" ] && continue
335
+ entry=$(build_prd_context_for_pr "${pr_number}")
336
+ [ -z "${entry}" ] && continue
337
+ combined="${combined}${combined:+$'\n\n'}${entry}"
338
+ done
339
+
340
+ [ -z "${combined}" ] && return 0
341
+ printf '\n\n## PRD Context\nUse this product context while reviewing and fixing PRs.\n%s\n' "${combined}"
342
+ }
343
+
127
344
  # Extract the latest review score from PR comments
128
345
  # Returns empty string if no score found
129
346
  get_pr_score() {
@@ -287,6 +504,14 @@ fi
287
504
 
288
505
  cd "${PROJECT_DIR}"
289
506
 
507
+ if [ "${WORKER_MODE}" != "1" ]; then
508
+ send_telegram_status_message "🔍 Night Watch Reviewer: started" "Project: ${PROJECT_NAME}
509
+ Provider (model): ${PROVIDER_MODEL_DISPLAY}
510
+ Branch patterns: ${BRANCH_PATTERNS_RAW}
511
+ Target PR: ${TARGET_PR:-all matching}
512
+ Action: scanning open PRs for failing checks or low review scores."
513
+ fi
514
+
290
515
  # Convert comma-separated branch prefixes into a regex that matches branch starts.
291
516
  BRANCH_REGEX=""
292
517
  IFS=',' read -r -a BRANCH_PATTERNS <<< "${BRANCH_PATTERNS_RAW}"
@@ -320,6 +545,13 @@ fi
320
545
 
321
546
  if [ "${OPEN_PRS}" -eq 0 ]; then
322
547
  log "SKIP: No open PRs matching branch patterns (${BRANCH_PATTERNS_RAW})"
548
+ if [ "${WORKER_MODE}" != "1" ]; then
549
+ send_telegram_status_message "🔍 Night Watch Reviewer: no matching PRs" "Project: ${PROJECT_NAME}
550
+ Provider (model): ${PROVIDER_MODEL_DISPLAY}
551
+ Branch patterns: ${BRANCH_PATTERNS_RAW}
552
+ Target PR: ${TARGET_PR:-all matching}
553
+ Result: 0 open PRs matched."
554
+ fi
323
555
  emit_result "skip_no_open_prs"
324
556
  exit 0
325
557
  fi
@@ -443,6 +675,11 @@ if [ "${NEEDS_WORK}" -eq 0 ]; then
443
675
  fi
444
676
  fi
445
677
 
678
+ if [ "${WORKER_MODE}" != "1" ]; then
679
+ send_telegram_status_message "🔍 Night Watch Reviewer: nothing to do" "Project: ${PROJECT_NAME}
680
+ Provider (model): ${PROVIDER_MODEL_DISPLAY}
681
+ Result: all ${OPEN_PRS} matching PRs already pass CI and review threshold (${MIN_REVIEW_SCORE})."
682
+ fi
446
683
  emit_result "skip_all_passing"
447
684
  exit 0
448
685
  fi
@@ -473,7 +710,7 @@ if [ -z "${TARGET_PR}" ] && [ "${WORKER_MODE}" != "1" ] && [ "${PARALLEL_ENABLED
473
710
  # Dry-run mode: print diagnostics and exit
474
711
  if [ "${NW_DRY_RUN:-0}" = "1" ]; then
475
712
  echo "=== Dry Run: PR Reviewer ==="
476
- echo "Provider: ${PROVIDER_CMD}"
713
+ echo "Provider (model): ${PROVIDER_MODEL_DISPLAY}"
477
714
  echo "Branch Patterns: ${BRANCH_PATTERNS_RAW}"
478
715
  echo "Min Review Score: ${MIN_REVIEW_SCORE}"
479
716
  echo "Auto-merge: ${AUTO_MERGE}"
@@ -604,7 +841,7 @@ cleanup_reviewer_worktrees "${REVIEW_WORKTREE_BASENAME}"
604
841
  # Dry-run mode: print diagnostics and exit
605
842
  if [ "${NW_DRY_RUN:-0}" = "1" ]; then
606
843
  echo "=== Dry Run: PR Reviewer ==="
607
- echo "Provider: ${PROVIDER_CMD}"
844
+ echo "Provider (model): ${PROVIDER_MODEL_DISPLAY}"
608
845
  echo "Branch Patterns: ${BRANCH_PATTERNS_RAW}"
609
846
  echo "Min Review Score: ${MIN_REVIEW_SCORE}"
610
847
  echo "Auto-merge: ${AUTO_MERGE}"
@@ -629,16 +866,26 @@ if ! prepare_detached_worktree "${PROJECT_DIR}" "${REVIEW_WORKTREE_DIR}" "${DEFA
629
866
  exit 1
630
867
  fi
631
868
 
632
- REVIEWER_PROMPT_PATH=$(resolve_instruction_path "${REVIEW_WORKTREE_DIR}" "night-watch-pr-reviewer.md" || true)
869
+ REVIEWER_PROMPT_PATH=$(resolve_instruction_path_with_fallback "${REVIEW_WORKTREE_DIR}" "pr-reviewer.md" "night-watch-pr-reviewer.md" || true)
633
870
  if [ -z "${REVIEWER_PROMPT_PATH}" ]; then
634
- log "FAIL: Missing reviewer prompt file. Checked instructions/, .claude/commands/, and bundled templates/"
871
+ log "FAIL: Missing reviewer prompt file. Checked pr-reviewer.md/night-watch-pr-reviewer.md in instructions/, .claude/commands/, and bundled templates/"
635
872
  emit_result "failure" "reason=missing_reviewer_prompt"
636
873
  exit 1
637
874
  fi
875
+ REVIEWER_PROMPT_BUNDLED_NAME="pr-reviewer.md"
876
+ if [[ "${REVIEWER_PROMPT_PATH}" == */night-watch-pr-reviewer.md ]]; then
877
+ REVIEWER_PROMPT_BUNDLED_NAME="night-watch-pr-reviewer.md"
878
+ fi
879
+ REVIEWER_PROMPT_PATH=$(prefer_bundled_prompt_if_legacy_command "${REVIEW_WORKTREE_DIR}" "${REVIEWER_PROMPT_PATH}" "${REVIEWER_PROMPT_BUNDLED_NAME}")
638
880
  REVIEWER_PROMPT_BASE=$(cat "${REVIEWER_PROMPT_PATH}")
639
881
  REVIEWER_PROMPT_REF=$(instruction_ref_for_prompt "${REVIEW_WORKTREE_DIR}" "${REVIEWER_PROMPT_PATH}")
640
882
  log "INFO: Using reviewer prompt from ${REVIEWER_PROMPT_REF}"
641
883
 
884
+ # Inject provider attribution requirement into the reviewer prompt.
885
+ # The AI must add a footer to every review comment it posts.
886
+ REVIEWER_PROVIDER_LABEL="${NW_PROVIDER_LABEL:-${PROVIDER_CMD}}"
887
+ REVIEWER_PROMPT_BASE="${REVIEWER_PROMPT_BASE}"$'\n\n'"## Reviewer Attribution (Required)"$'\n'"At the very end of each review comment you post, add this footer on its own line:"$'\n'"> 🔍 Reviewed by ${REVIEWER_PROVIDER_LABEL}"
888
+
642
889
  EXIT_CODE=0
643
890
  ATTEMPTS_MADE=1
644
891
  FINAL_SCORE=""
@@ -663,6 +910,22 @@ if [ -n "${TARGET_PR}" ]; then
663
910
  fi
664
911
  fi
665
912
 
913
+ PRD_CONTEXT_PROMPT=""
914
+ if [ -n "${TARGET_PR}" ]; then
915
+ PRD_CONTEXT_PROMPT=$(build_prd_context_prompt "${TARGET_PR}")
916
+ elif [ "${#PR_NUMBER_ARRAY[@]}" -gt 0 ]; then
917
+ PRD_CONTEXT_PROMPT=$(build_prd_context_prompt "${PR_NUMBER_ARRAY[@]}")
918
+ fi
919
+ if [ -n "${PRD_CONTEXT_PROMPT}" ]; then
920
+ if [ -n "${TARGET_PR}" ]; then
921
+ log "INFO: Added PRD context for PR #${TARGET_PR}"
922
+ else
923
+ log "INFO: Added PRD context for ${#PR_NUMBER_ARRAY[@]} PR(s)"
924
+ fi
925
+ else
926
+ log "WARN: No PRD context found for current reviewer scope"
927
+ fi
928
+
666
929
  # ── Retry Loop for Targeted PR Review ──────────────────────────────────────────
667
930
  # Only retry when targeting a specific PR. Non-targeted mode handles all PRs in one shot.
668
931
  TOTAL_ATTEMPTS=1
@@ -695,7 +958,7 @@ for ATTEMPT in $(seq 1 "${TOTAL_ATTEMPTS}"); do
695
958
 
696
959
  log "RETRY: Starting attempt ${ATTEMPT}/${TOTAL_ATTEMPTS} (timeout: ${ATTEMPT_TIMEOUT}s)"
697
960
  LOG_LINE_BEFORE=$(wc -l < "${LOG_FILE}" 2>/dev/null || echo 0)
698
- REVIEWER_PROMPT="${REVIEWER_PROMPT_BASE}${TARGET_SCOPE_PROMPT}"
961
+ REVIEWER_PROMPT="${REVIEWER_PROMPT_BASE}${TARGET_SCOPE_PROMPT}${PRD_CONTEXT_PROMPT}"
699
962
 
700
963
  case "${PROVIDER_CMD}" in
701
964
  claude)