@jonit-dev/night-watch-cli 1.7.0 → 1.7.2

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 (52) hide show
  1. package/dist/commands/cancel.d.ts +1 -1
  2. package/dist/commands/cancel.d.ts.map +1 -1
  3. package/dist/commands/cancel.js +6 -7
  4. package/dist/commands/cancel.js.map +1 -1
  5. package/dist/commands/dashboard/tab-config.d.ts.map +1 -1
  6. package/dist/commands/dashboard/tab-config.js.map +1 -1
  7. package/dist/commands/dashboard/tab-logs.js.map +1 -1
  8. package/dist/commands/dashboard/tab-schedules.js.map +1 -1
  9. package/dist/commands/dashboard/tab-status.d.ts.map +1 -1
  10. package/dist/commands/dashboard/tab-status.js.map +1 -1
  11. package/dist/commands/dashboard/types.d.ts.map +1 -1
  12. package/dist/commands/review.d.ts +4 -0
  13. package/dist/commands/review.d.ts.map +1 -1
  14. package/dist/commands/review.js +51 -19
  15. package/dist/commands/review.js.map +1 -1
  16. package/dist/commands/run.d.ts +6 -1
  17. package/dist/commands/run.d.ts.map +1 -1
  18. package/dist/commands/run.js +59 -21
  19. package/dist/commands/run.js.map +1 -1
  20. package/dist/server/index.d.ts.map +1 -1
  21. package/dist/server/index.js +185 -4
  22. package/dist/server/index.js.map +1 -1
  23. package/dist/storage/repositories/interfaces.d.ts.map +1 -1
  24. package/dist/storage/repositories/sqlite/execution-history-repository.d.ts.map +1 -1
  25. package/dist/storage/repositories/sqlite/prd-state-repository.d.ts.map +1 -1
  26. package/dist/storage/repositories/sqlite/project-registry-repository.d.ts.map +1 -1
  27. package/dist/storage/repositories/sqlite/roadmap-state-repository.d.ts.map +1 -1
  28. package/dist/storage/sqlite/client.js.map +1 -1
  29. package/dist/utils/github.d.ts +10 -0
  30. package/dist/utils/github.d.ts.map +1 -1
  31. package/dist/utils/github.js +48 -26
  32. package/dist/utils/github.js.map +1 -1
  33. package/dist/utils/script-result.d.ts +12 -0
  34. package/dist/utils/script-result.d.ts.map +1 -0
  35. package/dist/utils/script-result.js +46 -0
  36. package/dist/utils/script-result.js.map +1 -0
  37. package/dist/utils/shell.d.ts +14 -0
  38. package/dist/utils/shell.d.ts.map +1 -1
  39. package/dist/utils/shell.js +21 -1
  40. package/dist/utils/shell.js.map +1 -1
  41. package/dist/utils/status-data.d.ts +2 -0
  42. package/dist/utils/status-data.d.ts.map +1 -1
  43. package/dist/utils/status-data.js +30 -1
  44. package/dist/utils/status-data.js.map +1 -1
  45. package/package.json +4 -3
  46. package/scripts/night-watch-cron.sh +37 -6
  47. package/scripts/night-watch-helpers.sh +19 -0
  48. package/scripts/night-watch-pr-reviewer-cron.sh +23 -2
  49. package/web/dist/assets/index-BCMat9cx.js +350 -0
  50. package/web/dist/assets/{index-Dx_ZY5CY.css → index-BW6hS5jr.css} +1 -1
  51. package/web/dist/index.html +2 -2
  52. package/web/dist/assets/index-D4AfwSvr.js +0 -350
@@ -22,11 +22,10 @@ else
22
22
  fi
23
23
  LOG_DIR="${PROJECT_DIR}/logs"
24
24
  LOG_FILE="${LOG_DIR}/night-watch.log"
25
- # NOTE: Lock file path must match LOCK_FILE_PREFIX in src/constants.ts
26
- LOCK_FILE="/tmp/night-watch-${PROJECT_NAME}.lock"
27
25
  MAX_RUNTIME="${NW_MAX_RUNTIME:-7200}" # 2 hours
28
26
  MAX_LOG_SIZE="524288" # 512 KB
29
27
  PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
28
+ BRANCH_PREFIX="${NW_BRANCH_PREFIX:-night-watch}"
30
29
 
31
30
  # Ensure NVM / Node / Claude are on PATH
32
31
  export NVM_DIR="${HOME}/.nvm"
@@ -41,6 +40,19 @@ mkdir -p "${LOG_DIR}"
41
40
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
42
41
  # shellcheck source=night-watch-helpers.sh
43
42
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
43
+ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
44
+ # NOTE: Lock file path must match executorLockPath() in src/utils/status-data.ts
45
+ LOCK_FILE="/tmp/night-watch-${PROJECT_RUNTIME_KEY}.lock"
46
+
47
+ emit_result() {
48
+ local status="${1:?status required}"
49
+ local details="${2:-}"
50
+ if [ -n "${details}" ]; then
51
+ echo "NIGHT_WATCH_RESULT:${status}|${details}"
52
+ else
53
+ echo "NIGHT_WATCH_RESULT:${status}"
54
+ fi
55
+ }
44
56
 
45
57
  # Validate provider
46
58
  if ! validate_provider "${PROVIDER_CMD}"; then
@@ -51,6 +63,7 @@ fi
51
63
  rotate_log
52
64
 
53
65
  if ! acquire_lock "${LOCK_FILE}"; then
66
+ emit_result "skip_locked"
54
67
  exit 0
55
68
  fi
56
69
 
@@ -60,6 +73,7 @@ ELIGIBLE_PRD=$(find_eligible_prd "${PRD_DIR}" "${MAX_RUNTIME}" "${PROJECT_DIR}")
60
73
 
61
74
  if [ -z "${ELIGIBLE_PRD}" ]; then
62
75
  log "SKIP: No eligible PRDs (all done, in-progress, or blocked)"
76
+ emit_result "skip_no_eligible_prd"
63
77
  exit 0
64
78
  fi
65
79
 
@@ -70,7 +84,7 @@ claim_prd "${PRD_DIR}" "${ELIGIBLE_PRD}"
70
84
  trap "rm -f '${LOCK_FILE}'; release_claim '${PRD_DIR}' '${ELIGIBLE_PRD}'" EXIT
71
85
 
72
86
  PRD_NAME="${ELIGIBLE_PRD%.md}"
73
- BRANCH_NAME="night-watch/${PRD_NAME}"
87
+ BRANCH_NAME="${BRANCH_PREFIX}/${PRD_NAME}"
74
88
  WORKTREE_DIR="$(dirname "${PROJECT_DIR}")/${PROJECT_NAME}-nw-${PRD_NAME}"
75
89
  BOOKKEEP_WORKTREE_DIR="$(dirname "${PROJECT_DIR}")/${PROJECT_NAME}-nw-bookkeeping"
76
90
  if [ -n "${NW_DEFAULT_BRANCH:-}" ]; then
@@ -126,9 +140,11 @@ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>" || true
126
140
  return 1
127
141
  }
128
142
 
143
+ PROMPT_PRD_PATH="${PRD_DIR_REL}/${ELIGIBLE_PRD}"
144
+
129
145
  log "START: Processing ${ELIGIBLE_PRD} on branch ${BRANCH_NAME} (worktree: ${WORKTREE_DIR})"
130
146
 
131
- PROMPT="Implement the PRD at docs/PRDs/night-watch/${ELIGIBLE_PRD}
147
+ PROMPT="Implement the PRD at ${PROMPT_PRD_PATH}
132
148
 
133
149
  ## Setup
134
150
  - You are already inside an isolated worktree at: ${WORKTREE_DIR}
@@ -174,9 +190,11 @@ MERGED_PR_COUNT=$(count_prs_for_branch merged "${BRANCH_NAME}")
174
190
  if [ "${MERGED_PR_COUNT}" -gt 0 ]; then
175
191
  log "INFO: Found merged PR for ${BRANCH_NAME}; skipping provider run"
176
192
  if finalize_prd_done "already merged on ${BRANCH_NAME}"; then
193
+ emit_result "success_already_merged" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
177
194
  exit 0
178
195
  fi
179
196
  night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" failure --exit-code 1 2>/dev/null || true
197
+ emit_result "failure_finalize" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
180
198
  exit 1
181
199
  fi
182
200
 
@@ -258,26 +276,39 @@ done
258
276
  if [ ${EXIT_CODE} -eq 0 ]; then
259
277
  OPEN_PR_COUNT=$(count_prs_for_branch open "${BRANCH_NAME}")
260
278
  if [ "${OPEN_PR_COUNT}" -gt 0 ]; then
261
- if ! finalize_prd_done "implemented, PR opened on ${BRANCH_NAME}"; then
279
+ if finalize_prd_done "implemented, PR opened on ${BRANCH_NAME}"; then
280
+ emit_result "success_open_pr" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
281
+ else
262
282
  night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" failure --exit-code 1 2>/dev/null || true
283
+ emit_result "failure_finalize" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
284
+ EXIT_CODE=1
263
285
  fi
264
286
  else
265
287
  MERGED_PR_COUNT=$(count_prs_for_branch merged "${BRANCH_NAME}")
266
288
  if [ "${MERGED_PR_COUNT}" -gt 0 ]; then
267
- if ! finalize_prd_done "already merged on ${BRANCH_NAME}"; then
289
+ if finalize_prd_done "already merged on ${BRANCH_NAME}"; then
290
+ emit_result "success_already_merged" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
291
+ else
268
292
  night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" failure --exit-code 1 2>/dev/null || true
293
+ emit_result "failure_finalize" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
294
+ EXIT_CODE=1
269
295
  fi
270
296
  else
271
297
  log "WARN: ${PROVIDER_CMD} exited 0 but no open/merged PR found on ${BRANCH_NAME} — recording cooldown to avoid repeated stuck runs"
272
298
  night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" failure --exit-code 1 2>/dev/null || true
299
+ emit_result "failure_no_pr_after_success" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
300
+ EXIT_CODE=1
273
301
  fi
274
302
  fi
275
303
  elif [ ${EXIT_CODE} -eq 124 ]; then
276
304
  log "TIMEOUT: Night watch killed after ${MAX_RUNTIME}s while processing ${ELIGIBLE_PRD}"
277
305
  night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" timeout --exit-code 124 2>/dev/null || true
306
+ emit_result "timeout" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
278
307
  else
279
308
  log "FAIL: Night watch exited with code ${EXIT_CODE} while processing ${ELIGIBLE_PRD}"
280
309
  night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" failure --exit-code "${EXIT_CODE}" 2>/dev/null || true
310
+ emit_result "failure" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
281
311
  fi
282
312
 
283
313
  cleanup_worktrees "${PROJECT_DIR}"
314
+ exit "${EXIT_CODE}"
@@ -77,6 +77,25 @@ rotate_log() {
77
77
 
78
78
  # ── Lock management ──────────────────────────────────────────────────────────
79
79
 
80
+ project_runtime_key() {
81
+ local project_dir="${1:?project_dir required}"
82
+ local project_name
83
+ local project_hash=""
84
+ project_name=$(basename "${project_dir}")
85
+
86
+ if command -v sha1sum >/dev/null 2>&1; then
87
+ project_hash=$(printf '%s' "${project_dir}" | sha1sum | awk '{print $1}')
88
+ elif command -v shasum >/dev/null 2>&1; then
89
+ project_hash=$(printf '%s' "${project_dir}" | shasum -a 1 | awk '{print $1}')
90
+ elif command -v openssl >/dev/null 2>&1; then
91
+ project_hash=$(printf '%s' "${project_dir}" | openssl sha1 | awk '{print $NF}')
92
+ else
93
+ return 1
94
+ fi
95
+
96
+ printf '%s-%s' "${project_name}" "${project_hash:0:12}"
97
+ }
98
+
80
99
  acquire_lock() {
81
100
  local lock_file="${1:?lock_file required}"
82
101
 
@@ -15,8 +15,6 @@ PROJECT_DIR="${1:?Usage: $0 /path/to/project}"
15
15
  PROJECT_NAME=$(basename "${PROJECT_DIR}")
16
16
  LOG_DIR="${PROJECT_DIR}/logs"
17
17
  LOG_FILE="${LOG_DIR}/night-watch-pr-reviewer.log"
18
- # NOTE: Lock file path must match LOCK_FILE_PREFIX in src/constants.ts (reviewer: prefix + "pr-reviewer-" + name)
19
- LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_NAME}.lock"
20
18
  MAX_RUNTIME="${NW_REVIEWER_MAX_RUNTIME:-3600}" # 1 hour
21
19
  MAX_LOG_SIZE="524288" # 512 KB
22
20
  PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
@@ -35,6 +33,19 @@ mkdir -p "${LOG_DIR}"
35
33
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
36
34
  # shellcheck source=night-watch-helpers.sh
37
35
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
36
+ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
37
+ # NOTE: Lock file path must match reviewerLockPath() in src/utils/status-data.ts
38
+ LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}.lock"
39
+
40
+ emit_result() {
41
+ local status="${1:?status required}"
42
+ local details="${2:-}"
43
+ if [ -n "${details}" ]; then
44
+ echo "NIGHT_WATCH_RESULT:${status}|${details}"
45
+ else
46
+ echo "NIGHT_WATCH_RESULT:${status}"
47
+ fi
48
+ }
38
49
 
39
50
  # Validate provider
40
51
  if ! validate_provider "${PROVIDER_CMD}"; then
@@ -45,6 +56,7 @@ fi
45
56
  rotate_log
46
57
 
47
58
  if ! acquire_lock "${LOCK_FILE}"; then
59
+ emit_result "skip_locked"
48
60
  exit 0
49
61
  fi
50
62
 
@@ -73,6 +85,7 @@ OPEN_PRS=$(
73
85
 
74
86
  if [ "${OPEN_PRS}" -eq 0 ]; then
75
87
  log "SKIP: No open PRs matching branch patterns (${BRANCH_PATTERNS_RAW})"
88
+ emit_result "skip_no_open_prs"
76
89
  exit 0
77
90
  fi
78
91
 
@@ -118,9 +131,14 @@ done < <(gh pr list --state open --json number,headRefName --jq '.[] | [.number,
118
131
 
119
132
  if [ "${NEEDS_WORK}" -eq 0 ]; then
120
133
  log "SKIP: All ${OPEN_PRS} open PR(s) have passing CI and review score >= ${MIN_REVIEW_SCORE} (or no score yet)"
134
+ emit_result "skip_all_passing"
121
135
  exit 0
122
136
  fi
123
137
 
138
+ PRS_NEEDING_WORK=$(echo "${PRS_NEEDING_WORK}" \
139
+ | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]][[:space:]]*/ /g' -e 's/[[:space:]]*$//')
140
+ PRS_NEEDING_WORK_CSV="${PRS_NEEDING_WORK// /,}"
141
+
124
142
  if [ -n "${NW_DEFAULT_BRANCH:-}" ]; then
125
143
  DEFAULT_BRANCH="${NW_DEFAULT_BRANCH}"
126
144
  else
@@ -188,8 +206,11 @@ cleanup_worktrees "${PROJECT_DIR}"
188
206
 
189
207
  if [ ${EXIT_CODE} -eq 0 ]; then
190
208
  log "DONE: PR reviewer completed successfully"
209
+ emit_result "success_reviewed" "prs=${PRS_NEEDING_WORK_CSV}"
191
210
  elif [ ${EXIT_CODE} -eq 124 ]; then
192
211
  log "TIMEOUT: PR reviewer killed after ${MAX_RUNTIME}s"
212
+ emit_result "timeout" "prs=${PRS_NEEDING_WORK_CSV}"
193
213
  else
194
214
  log "FAIL: PR reviewer exited with code ${EXIT_CODE}"
215
+ emit_result "failure" "prs=${PRS_NEEDING_WORK_CSV}"
195
216
  fi