@jonit-dev/night-watch-cli 1.7.46 → 1.7.48

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 (48) hide show
  1. package/dist/cli.js +1097 -705
  2. package/dist/commands/audit.d.ts.map +1 -1
  3. package/dist/commands/audit.js +21 -1
  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 +14 -37
  7. package/dist/commands/init.js.map +1 -1
  8. package/dist/commands/install.d.ts.map +1 -1
  9. package/dist/commands/install.js +20 -12
  10. package/dist/commands/install.js.map +1 -1
  11. package/dist/commands/logs.d.ts.map +1 -1
  12. package/dist/commands/logs.js +37 -5
  13. package/dist/commands/logs.js.map +1 -1
  14. package/dist/commands/prd.d.ts.map +1 -1
  15. package/dist/commands/prd.js +2 -6
  16. package/dist/commands/prd.js.map +1 -1
  17. package/dist/commands/prds.d.ts.map +1 -1
  18. package/dist/commands/prds.js +1 -1
  19. package/dist/commands/prds.js.map +1 -1
  20. package/dist/commands/qa.d.ts.map +1 -1
  21. package/dist/commands/qa.js +20 -0
  22. package/dist/commands/qa.js.map +1 -1
  23. package/dist/commands/review.d.ts +10 -0
  24. package/dist/commands/review.d.ts.map +1 -1
  25. package/dist/commands/review.js +38 -0
  26. package/dist/commands/review.js.map +1 -1
  27. package/dist/commands/run.d.ts.map +1 -1
  28. package/dist/commands/run.js +7 -2
  29. package/dist/commands/run.js.map +1 -1
  30. package/dist/commands/slice.d.ts.map +1 -1
  31. package/dist/commands/slice.js +88 -17
  32. package/dist/commands/slice.js.map +1 -1
  33. package/dist/commands/status.d.ts.map +1 -1
  34. package/dist/commands/status.js +75 -0
  35. package/dist/commands/status.js.map +1 -1
  36. package/dist/scripts/night-watch-audit-cron.sh +19 -0
  37. package/dist/scripts/night-watch-cron.sh +2 -2
  38. package/dist/scripts/night-watch-helpers.sh +47 -1
  39. package/dist/scripts/night-watch-pr-reviewer-cron.sh +168 -35
  40. package/dist/scripts/night-watch-qa-cron.sh +19 -0
  41. package/dist/scripts/night-watch-slicer-cron.sh +10 -1
  42. package/dist/templates/night-watch-slicer.md +18 -3
  43. package/dist/templates/night-watch.config.json +1 -0
  44. package/dist/templates/night-watch.md +10 -37
  45. package/dist/web/assets/index-Ba-4YvTQ.js +365 -0
  46. package/dist/web/assets/index-DpVirMEe.css +1 -0
  47. package/dist/web/index.html +2 -2
  48. package/package.json +1 -1
@@ -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" "prs=${prs_csv}|auto_merged=${auto_merged}|auto_merge_failed=${auto_merge_failed}"
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" "prs=${prs_csv}"
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" "prs=${prs_csv}"
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
- case "${PROVIDER_CMD}" in
422
- claude)
423
- CLAUDE_PROMPT="/night-watch-pr-reviewer${TARGET_SCOPE_PROMPT}"
424
- if (
425
- cd "${REVIEW_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
426
- claude -p "${CLAUDE_PROMPT}" \
427
- --dangerously-skip-permissions \
428
- >> "${LOG_FILE}" 2>&1
429
- ); then
430
- EXIT_CODE=0
431
- else
432
- EXIT_CODE=$?
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
- codex)
436
- CODEX_PROMPT="$(cat "${REVIEW_WORKTREE_DIR}/.claude/commands/night-watch-pr-reviewer.md")${TARGET_SCOPE_PROMPT}"
437
- if (
438
- cd "${REVIEW_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
439
- codex --quiet \
440
- --yolo \
441
- --prompt "${CODEX_PROMPT}" \
442
- >> "${LOG_FILE}" 2>&1
443
- ); then
444
- EXIT_CODE=0
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
- EXIT_CODE=$?
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
- log "ERROR: Unknown provider: ${PROVIDER_CMD}"
451
- exit 1
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}"
@@ -68,6 +68,10 @@ fi
68
68
 
69
69
  cd "${PROJECT_DIR}"
70
70
 
71
+ send_telegram_status_message "🧪 Night Watch QA: started" "Project: ${PROJECT_NAME}
72
+ Provider: ${PROVIDER_CMD}
73
+ Scanning open PRs for QA candidates."
74
+
71
75
  # Convert comma-separated branch prefixes into a regex that matches branch starts.
72
76
  BRANCH_REGEX=""
73
77
  IFS=',' read -r -a BRANCH_PATTERNS <<< "${BRANCH_PATTERNS_RAW}"
@@ -96,6 +100,8 @@ OPEN_PRS=$(
96
100
 
97
101
  if [ "${OPEN_PRS}" -eq 0 ]; then
98
102
  log "SKIP: No open PRs matching branch patterns (${BRANCH_PATTERNS_RAW})"
103
+ send_telegram_status_message "🧪 Night Watch QA: no matching PRs" "Project: ${PROJECT_NAME}
104
+ Branch patterns: ${BRANCH_PATTERNS_RAW}"
99
105
  emit_result "skip_no_open_prs"
100
106
  exit 0
101
107
  fi
@@ -151,6 +157,8 @@ done < <(
151
157
 
152
158
  if [ "${QA_NEEDED}" -eq 0 ]; then
153
159
  log "SKIP: All ${OPEN_PRS} open PR(s) matching patterns already have QA comments"
160
+ send_telegram_status_message "🧪 Night Watch QA: nothing to do" "Project: ${PROJECT_NAME}
161
+ All matching PRs already have QA results."
154
162
  emit_result "skip_all_qa_done"
155
163
  exit 0
156
164
  fi
@@ -190,6 +198,9 @@ EXIT_CODE=0
190
198
  # Process each PR that needs QA
191
199
  for pr_ref in ${PRS_NEEDING_QA}; do
192
200
  pr_num="${pr_ref#\#}"
201
+ send_telegram_status_message "🧪 Night Watch QA: processing PR #${pr_num}" "Project: ${PROJECT_NAME}
202
+ Provider: ${PROVIDER_CMD}
203
+ Artifacts: ${QA_ARTIFACTS}"
193
204
 
194
205
  cleanup_worktrees "${PROJECT_DIR}"
195
206
  if ! prepare_detached_worktree "${PROJECT_DIR}" "${QA_WORKTREE_DIR}" "${DEFAULT_BRANCH}" "${LOG_FILE}"; then
@@ -257,6 +268,8 @@ cleanup_worktrees "${PROJECT_DIR}"
257
268
 
258
269
  if [ ${EXIT_CODE} -eq 0 ]; then
259
270
  log "DONE: QA runner completed successfully"
271
+ send_telegram_status_message "🧪 Night Watch QA: completed" "Project: ${PROJECT_NAME}
272
+ Processed PRs: ${PRS_NEEDING_QA_CSV}"
260
273
  if [ -n "${REPO}" ]; then
261
274
  emit_result "success_qa" "prs=${PRS_NEEDING_QA_CSV}|repo=${REPO}"
262
275
  else
@@ -264,6 +277,9 @@ if [ ${EXIT_CODE} -eq 0 ]; then
264
277
  fi
265
278
  elif [ ${EXIT_CODE} -eq 124 ]; then
266
279
  log "TIMEOUT: QA runner killed after ${MAX_RUNTIME}s"
280
+ send_telegram_status_message "🧪 Night Watch QA: timeout" "Project: ${PROJECT_NAME}
281
+ Timeout: ${MAX_RUNTIME}s
282
+ Processed PRs: ${PRS_NEEDING_QA_CSV}"
267
283
  if [ -n "${REPO}" ]; then
268
284
  emit_result "timeout" "prs=${PRS_NEEDING_QA_CSV}|repo=${REPO}"
269
285
  else
@@ -271,6 +287,9 @@ elif [ ${EXIT_CODE} -eq 124 ]; then
271
287
  fi
272
288
  else
273
289
  log "FAIL: QA runner exited with code ${EXIT_CODE}"
290
+ send_telegram_status_message "🧪 Night Watch QA: failed" "Project: ${PROJECT_NAME}
291
+ Exit code: ${EXIT_CODE}
292
+ Processed PRs: ${PRS_NEEDING_QA_CSV}"
274
293
  if [ -n "${REPO}" ]; then
275
294
  emit_result "failure" "prs=${PRS_NEEDING_QA_CSV}|repo=${REPO}"
276
295
  else
@@ -17,7 +17,7 @@ set -euo pipefail
17
17
  PROJECT_DIR="${1:?Usage: $0 /path/to/project}"
18
18
  PROJECT_NAME=$(basename "${PROJECT_DIR}")
19
19
  LOG_DIR="${PROJECT_DIR}/logs"
20
- LOG_FILE="${LOG_DIR}/night-watch-slicer.log"
20
+ LOG_FILE="${LOG_DIR}/slicer.log"
21
21
  LOCK_FILE=""
22
22
  MAX_RUNTIME="${NW_SLICER_MAX_RUNTIME:-600}" # 10 minutes
23
23
  MAX_LOG_SIZE="524288" # 512 KB
@@ -54,6 +54,9 @@ cleanup_on_exit() {
54
54
  trap cleanup_on_exit EXIT
55
55
 
56
56
  log "START: Running roadmap slicer for ${PROJECT_DIR}"
57
+ send_telegram_status_message "📋 Night Watch Planner: started" "Project: ${PROJECT_NAME}
58
+ Provider: ${PROVIDER_CMD}
59
+ Planning next roadmap item into a PRD."
57
60
 
58
61
  # Dry-run mode: print diagnostics and exit
59
62
  if [ "${NW_DRY_RUN:-0}" = "1" ]; then
@@ -81,10 +84,16 @@ fi
81
84
 
82
85
  if [ ${EXIT_CODE} -eq 0 ]; then
83
86
  log "DONE: Slicer completed successfully"
87
+ send_telegram_status_message "📋 Night Watch Planner: completed" "Project: ${PROJECT_NAME}
88
+ PRD planning run finished successfully."
84
89
  elif [ ${EXIT_CODE} -eq 124 ]; then
85
90
  log "TIMEOUT: Slicer killed after ${MAX_RUNTIME}s"
91
+ send_telegram_status_message "📋 Night Watch Planner: timeout" "Project: ${PROJECT_NAME}
92
+ Timeout: ${MAX_RUNTIME}s"
86
93
  else
87
94
  log "FAIL: Slicer exited with code ${EXIT_CODE}"
95
+ send_telegram_status_message "📋 Night Watch Planner: failed" "Project: ${PROJECT_NAME}
96
+ Exit code: ${EXIT_CODE}"
88
97
  fi
89
98
 
90
99
  exit ${EXIT_CODE}
@@ -21,6 +21,8 @@ The PRD directory is: `{{PRD_DIR}}`
21
21
 
22
22
  ## Your Task
23
23
 
24
+ 0. **Load Planner Skill** - Read and apply `.claude/skills/prd-creator/SKILL.md` before writing the PRD. If unavailable, continue with the instructions in this template.
25
+
24
26
  1. **Explore the Codebase** - Read relevant existing files to understand the project structure, patterns, and conventions. Look for:
25
27
  - CLAUDE.md or similar AI assistant documentation files
26
28
  - Existing code patterns in the area you'll be modifying
@@ -61,7 +63,7 @@ COMPLEXITY SCORE (sum all that apply):
61
63
 
62
64
  Your PRD MUST follow this exact structure:
63
65
 
64
- ```markdown
66
+ ````markdown
65
67
  # PRD: [Title from roadmap item]
66
68
 
67
69
  **Depends on:** [List any prerequisite PRDs/files, or omit if none]
@@ -77,24 +79,29 @@ Your PRD MUST follow this exact structure:
77
79
  **Problem:** [1-2 sentences describing the issue being solved]
78
80
 
79
81
  **Files Analyzed:**
82
+
80
83
  - `path/to/file.ts` — [what the file does]
81
84
  - [List all files you inspected before planning]
82
85
 
83
86
  **Current Behavior:**
87
+
84
88
  - [3-5 bullets describing current state]
85
89
 
86
90
  ### Integration Points Checklist
87
91
 
88
92
  **How will this feature be reached?**
93
+
89
94
  - [ ] Entry point identified: [e.g., route, event, cron, CLI command]
90
95
  - [ ] Caller file identified: [file that will invoke this new code]
91
96
  - [ ] Registration/wiring needed: [e.g., add route to router, register handler, add menu item]
92
97
 
93
98
  **Is this user-facing?**
99
+
94
100
  - [ ] YES → UI components required (list them)
95
101
  - [ ] NO → Internal/background feature (explain how it's triggered)
96
102
 
97
103
  **Full user flow:**
104
+
98
105
  1. User does: [action]
99
106
  2. Triggers: [what code path]
100
107
  3. Reaches new feature via: [specific connection point]
@@ -105,6 +112,7 @@ Your PRD MUST follow this exact structure:
105
112
  ## 2. Solution
106
113
 
107
114
  **Approach:**
115
+
108
116
  - [3-5 bullets explaining the chosen solution]
109
117
 
110
118
  **Architecture Diagram** <!-- (MEDIUM/HIGH complexity) -->:
@@ -113,8 +121,10 @@ Your PRD MUST follow this exact structure:
113
121
  flowchart LR
114
122
  A[Component A] --> B[Component B] --> C[Component C]
115
123
  ```
124
+ ````
116
125
 
117
126
  **Key Decisions:**
127
+
118
128
  - [Library/framework choices, error-handling strategy, reused utilities]
119
129
 
120
130
  **Data Changes:** [New schemas/migrations, or "None"]
@@ -140,6 +150,7 @@ sequenceDiagram
140
150
  ## 4. Execution Phases
141
151
 
142
152
  **CRITICAL RULES:**
153
+
143
154
  1. Each phase = ONE user-testable vertical slice
144
155
  2. Max 5 files per phase (split if larger)
145
156
  3. Each phase MUST include concrete tests
@@ -148,9 +159,11 @@ sequenceDiagram
148
159
  ### Phase 1: [Name] — [User-visible outcome in 1 sentence]
149
160
 
150
161
  **Files (max 5):**
162
+
151
163
  - `src/path/file.ts` — [what changes]
152
164
 
153
165
  **Implementation:**
166
+
154
167
  - [ ] Step 1
155
168
  - [ ] Step 2
156
169
 
@@ -160,6 +173,7 @@ sequenceDiagram
160
173
  | `src/__tests__/feature.test.ts` | `should do X when Y` | `expect(result).toBe(Z)` |
161
174
 
162
175
  **Verification Plan:**
176
+
163
177
  1. **Unit Tests:** File and test names
164
178
  2. **Integration Test:** (if applicable)
165
179
  3. **User Verification:**
@@ -183,7 +197,8 @@ sequenceDiagram
183
197
  - [ ] Feature is reachable (entry point connected, not orphaned code)
184
198
  - [ ] [additional criterion specific to this feature]
185
199
  - [ ] [additional criterion specific to this feature]
186
- ```
200
+
201
+ ````
187
202
 
188
203
  ---
189
204
 
@@ -216,4 +231,4 @@ After writing the PRD file, report:
216
231
 
217
232
  ### Summary
218
233
  [1-2 sentences summarizing the PRD content]
219
- ```
234
+ ````
@@ -3,6 +3,7 @@
3
3
  "projectName": "",
4
4
  "defaultBranch": "",
5
5
  "provider": "claude",
6
+ "executorEnabled": true,
6
7
  "reviewerEnabled": true,
7
8
  "prdDir": "docs/PRDs/night-watch",
8
9
  "templatesDir": ".night-watch/templates",
@@ -2,9 +2,9 @@ You are the Night Watch agent. Your job is to autonomously pick up PRD tickets a
2
2
 
3
3
  ## Instructions
4
4
 
5
- 1. **Scan for PRDs**: List files in `docs/PRDs/night-watch/` (exclude `NIGHT-WATCH-SUMMARY.md` and the `done/` directory). Each `.md` file is a ticket.
5
+ 1. **Scan for PRDs**: Use `night-watch prd list --json` to get available PRDs. Each PRD is a ticket.
6
6
 
7
- 2. **Check dependencies**: Read each PRD. If it says "Depends on:" another PRD, check if that dependency is already in `docs/PRDs/night-watch/done/`. Skip PRDs with unmet dependencies.
7
+ 2. **Check dependencies**: For each PRD, verify its dependencies are satisfied (depended-on PRD is marked as done). Skip PRDs with unmet dependencies.
8
8
 
9
9
  3. **Check for already-in-progress PRDs**: Before processing any PRD, check if a PR already exists for it:
10
10
 
@@ -30,11 +30,11 @@ You are the Night Watch agent. Your job is to autonomously pick up PRD tickets a
30
30
  d. `cd` into the worktree and run package install (npm install, yarn install, or pnpm install as appropriate). Keep all implementation steps inside this worktree.
31
31
 
32
32
  e. **Implement the PRD using the PRD Executor workflow**:
33
- - Read `.claude/commands/prd-executor.md` and follow its full execution pipeline.
34
- - This means: parse the PRD phases, build a dependency graph, create a task list, and execute phases in parallel waves using agent swarms.
35
- - Maximize parallelism — launch all independent phases concurrently.
36
- - Run the project's verify/test command between waves to catch issues early.
37
- - Follow all project conventions from AI assistant documentation files (e.g., CLAUDE.md, AGENTS.md, or similar).
33
+ - Read `.claude/skills/prd-executor/SKILL.md` (preferred) or `.claude/commands/prd-executor.md` (fallback), and follow the full execution pipeline.
34
+ - This means: parse the PRD phases, build a dependency graph, create a task list, and execute phases in parallel waves using agent swarms.
35
+ - Maximize parallelism — launch all independent phases concurrently.
36
+ - Run the project's verify/test command between waves to catch issues early.
37
+ - Follow all project conventions from AI assistant documentation files (e.g., CLAUDE.md, AGENTS.md, or similar).
38
38
 
39
39
  f. **Write tests** as specified in each PRD phase (the prd-executor agents handle this per-phase).
40
40
 
@@ -58,37 +58,10 @@ You are the Night Watch agent. Your job is to autonomously pick up PRD tickets a
58
58
  gh pr create --title "feat: <short title>" --body "<summary with PRD reference>"
59
59
  ```
60
60
 
61
- j. **Move PRD to done** (back in main repo on ${DEFAULT_BRANCH}):
61
+ j. **Mark PRD as done**: `night-watch prd done <filename>`
62
62
 
63
- ```
64
- cd ${PROJECT_DIR}
65
- git checkout ${DEFAULT_BRANCH}
66
- mkdir -p docs/PRDs/night-watch/done
67
- mv docs/PRDs/night-watch/<file>.md docs/PRDs/night-watch/done/
68
- ```
69
-
70
- k. **Update summary**: Append to `docs/PRDs/night-watch/NIGHT-WATCH-SUMMARY.md`:
71
-
72
- ```
73
- ## <Title>
74
- - **PRD**: <filename>
75
- - **Branch**: night-watch/<name>
76
- - **PR**: <url>
77
- - **Date**: <YYYY-MM-DD>
78
- - **Status**: PR Opened
79
- ### What was done
80
- <bullet points>
81
- ### Files changed
82
- <list>
83
- ---
84
- ```
85
-
86
- l. **Commit** the move + summary update, push ${DEFAULT_BRANCH}.
87
-
88
- m. **Clean up worktree**: `git worktree remove ../${PROJECT_NAME}-nw-<prd-name>`
89
-
90
- n. **STOP after this PRD**. Do NOT continue to the next PRD. One PRD per run prevents timeouts and reduces risk. The next cron trigger will pick up the next PRD.
63
+ k. **STOP after this PRD**. Do NOT continue to the next PRD. One PRD per run prevents timeouts and reduces risk. The next cron trigger will pick up the next PRD.
91
64
 
92
- 5. **On failure**: Do NOT move the PRD to done. Log the failure in NIGHT-WATCH-SUMMARY.md with status "Failed" and the reason. Clean up worktree and **stop** -- do not attempt the next PRD.
65
+ 5. **On failure**: Do NOT mark the PRD as done. Log the failure and clean up worktree. **Stop** -- do not attempt the next PRD.
93
66
 
94
67
  Start now. Scan for available PRDs and process the first eligible one.