@jonit-dev/night-watch-cli 1.7.78 → 1.7.80

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.
@@ -23,21 +23,13 @@ SCRIPT_TYPE="audit"
23
23
  PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
24
24
  SCRIPT_START_TIME=$(date +%s)
25
25
 
26
- # Ensure NVM / Node / Claude are on PATH
27
- export NVM_DIR="${HOME}/.nvm"
28
- [ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
29
-
30
26
  mkdir -p "${LOG_DIR}"
31
27
 
32
28
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
33
29
  # shellcheck source=night-watch-helpers.sh
34
30
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
35
- PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
36
- # NOTE: Lock file path must match auditLockPath() in src/utils/status-data.ts
37
- LOCK_FILE="/tmp/night-watch-audit-${PROJECT_RUNTIME_KEY}.lock"
38
- AUDIT_PROMPT_TEMPLATE="${SCRIPT_DIR}/../templates/audit.md"
39
- PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
40
31
 
32
+ # emit_result helper - must be defined before use
41
33
  emit_result() {
42
34
  local status="${1:?status required}"
43
35
  local details="${2:-}"
@@ -48,13 +40,25 @@ emit_result() {
48
40
  fi
49
41
  }
50
42
 
51
- # Validate provider
43
+ # Validate provider name first (must be claude or codex)
52
44
  if ! validate_provider "${PROVIDER_CMD}"; then
53
45
  echo "ERROR: Unknown provider: ${PROVIDER_CMD}" >&2
54
46
  emit_result "failure" "reason=unknown_provider"
55
47
  exit 1
56
48
  fi
57
49
 
50
+ # Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
51
+ if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
52
+ echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
53
+ emit_result "failure" "reason=provider_not_found"
54
+ exit 1
55
+ fi
56
+ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
57
+ # NOTE: Lock file path must match auditLockPath() in src/utils/status-data.ts
58
+ LOCK_FILE="/tmp/night-watch-audit-${PROJECT_RUNTIME_KEY}.lock"
59
+ AUDIT_PROMPT_TEMPLATE="${SCRIPT_DIR}/../templates/audit.md"
60
+ PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
61
+
58
62
  # Global gate: if queue is enabled and we can't acquire the global lock,
59
63
  # enqueue the job and exit. The dispatcher will run it later.
60
64
  if [ "${NW_QUEUE_ENABLED:-0}" = "1" ]; then
@@ -166,7 +170,7 @@ for AUDIT_ATTEMPT in $(seq 1 "${AUDIT_MAX_RETRIES}"); do
166
170
  cd "${AUDIT_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
167
171
  claude -p "${AUDIT_PROMPT}" \
168
172
  --dangerously-skip-permissions \
169
- >> "${LOG_FILE}" 2>&1
173
+ 2>&1 | tee -a "${LOG_FILE}"
170
174
  ); then
171
175
  EXIT_CODE=0
172
176
  else
@@ -180,7 +184,7 @@ for AUDIT_ATTEMPT in $(seq 1 "${AUDIT_MAX_RETRIES}"); do
180
184
  -C "${AUDIT_WORKTREE_DIR}" \
181
185
  --yolo \
182
186
  "${AUDIT_PROMPT}" \
183
- >> "${LOG_FILE}" 2>&1
187
+ 2>&1 | tee -a "${LOG_FILE}"
184
188
  ); then
185
189
  EXIT_CODE=0
186
190
  else
@@ -8,7 +8,7 @@ set -euo pipefail
8
8
  # NOTE: This script expects environment variables to be set by the caller.
9
9
  # The Node.js CLI will inject config values via environment variables.
10
10
  # Required env vars (with defaults shown):
11
- # NW_MAX_RUNTIME=7200 - Maximum runtime in seconds (2 hours)
11
+ # NW_MAX_RUNTIME=14400 - Maximum runtime in seconds (4 hours)
12
12
  # NW_PROVIDER_CMD=claude - AI provider CLI to use (claude, codex, etc.)
13
13
  # NW_DRY_RUN=0 - Set to 1 for dry-run mode (prints diagnostics only)
14
14
 
@@ -22,7 +22,7 @@ else
22
22
  fi
23
23
  LOG_DIR="${PROJECT_DIR}/logs"
24
24
  LOG_FILE="${LOG_DIR}/executor.log"
25
- MAX_RUNTIME="${NW_MAX_RUNTIME:-7200}" # 2 hours — used for cooldowns and eligibility
25
+ MAX_RUNTIME="${NW_MAX_RUNTIME:-14400}" # 4 hours — used for cooldowns and eligibility
26
26
  SESSION_MAX_RUNTIME="${NW_SESSION_MAX_RUNTIME:-${MAX_RUNTIME}}" # per-invocation timeout; defaults to MAX_RUNTIME
27
27
  MAX_LOG_SIZE="524288" # 512 KB
28
28
  PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
@@ -34,19 +34,18 @@ EFFECTIVE_PROVIDER_LABEL="${PROVIDER_LABEL}"
34
34
  BRANCH_PREFIX="${NW_BRANCH_PREFIX:-night-watch}"
35
35
  SCRIPT_START_TIME=$(date +%s)
36
36
 
37
- # Ensure NVM / Node / Claude are on PATH
38
- export NVM_DIR="${HOME}/.nvm"
39
- [ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
40
-
41
- # NOTE: Environment variables should be set by the caller (Node.js CLI).
42
- # The .env.night-watch sourcing has been removed - config is now injected via env vars.
43
-
44
37
  mkdir -p "${LOG_DIR}"
45
38
 
46
39
  # Load shared helpers
47
40
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
48
41
  # shellcheck source=night-watch-helpers.sh
49
42
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
43
+
44
+ # Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
45
+ if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
46
+ echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
47
+ exit 127
48
+ fi
50
49
  PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
51
50
  # NOTE: Lock file path must match executorLockPath() in src/utils/status-data.ts
52
51
  LOCK_FILE="/tmp/night-watch-${PROJECT_RUNTIME_KEY}.lock"
@@ -548,7 +547,7 @@ while [ "${ATTEMPT}" -lt "${MAX_RETRIES}" ]; do
548
547
  cd "${WORKTREE_DIR}" && timeout "${SESSION_MAX_RUNTIME}" \
549
548
  claude -p "${PROMPT}" \
550
549
  --dangerously-skip-permissions \
551
- >> "${LOG_FILE}" 2>&1
550
+ 2>&1 | tee -a "${LOG_FILE}"
552
551
  ); then
553
552
  EXIT_CODE=0
554
553
  else
@@ -562,7 +561,7 @@ while [ "${ATTEMPT}" -lt "${MAX_RETRIES}" ]; do
562
561
  -C "${WORKTREE_DIR}" \
563
562
  --yolo \
564
563
  "${PROMPT}" \
565
- >> "${LOG_FILE}" 2>&1
564
+ 2>&1 | tee -a "${LOG_FILE}"
566
565
  ); then
567
566
  EXIT_CODE=0
568
567
  else
@@ -629,7 +628,7 @@ if [ "${RATE_LIMIT_FALLBACK_TRIGGERED}" = "1" ]; then
629
628
  claude -p "${PROMPT}" \
630
629
  --dangerously-skip-permissions \
631
630
  --model "${FALLBACK_MODEL}" \
632
- >> "${LOG_FILE}" 2>&1
631
+ 2>&1 | tee -a "${LOG_FILE}"
633
632
  ); then
634
633
  EXIT_CODE=0
635
634
  else
@@ -667,6 +666,7 @@ if [ ${EXIT_CODE} -eq 0 ]; then
667
666
  fi
668
667
  "${NW_CLI}" board close-issue "${ISSUE_NUMBER}" 2>>"${LOG_FILE}" || \
669
668
  "${NW_CLI}" board move-issue "${ISSUE_NUMBER}" --column "Done" 2>>"${LOG_FILE}" || true
669
+ log "SUCCESS: PR opened and ready for review — ${PR_URL}"
670
670
  emit_result "success_open_pr" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
671
671
  elif finalize_prd_done "implemented, PR opened on ${BRANCH_NAME}"; then
672
672
  # Non-board mode: post attribution comment to the PR
@@ -675,6 +675,7 @@ if [ ${EXIT_CODE} -eq 0 ]; then
675
675
  if [ -n "${NON_BOARD_PR_URL}" ]; then
676
676
  gh pr comment "${NON_BOARD_PR_URL}" --body "> 🤖 Implemented by ${EFFECTIVE_PROVIDER_LABEL}" 2>>"${LOG_FILE}" || true
677
677
  fi
678
+ log "SUCCESS: PR opened and ready for review — ${NON_BOARD_PR_URL}"
678
679
  emit_result "success_open_pr" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}"
679
680
  else
680
681
  night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" failure --exit-code 1 2>/dev/null || true
@@ -2,6 +2,78 @@
2
2
  # Night Watch helper functions — shared by cron scripts.
3
3
  # Source this file, don't execute it directly.
4
4
 
5
+ # ── Provider PATH resolution ─────────────────────────────────────────────────
6
+
7
+ # Ensure AI provider CLI (claude, codex, etc.) and Node.js tooling are
8
+ # discoverable on PATH. Sources common Node version managers and probes
9
+ # well-known bin directories so the script works regardless of how the
10
+ # provider was installed (nvm, fnm, volta, npm global, Homebrew, etc.).
11
+ # Returns 0 if the provider is found, 1 otherwise.
12
+ ensure_provider_on_path() {
13
+ local provider="${1:-claude}"
14
+
15
+ # Already available — nothing to do
16
+ if command -v "${provider}" >/dev/null 2>&1; then
17
+ return 0
18
+ fi
19
+
20
+ # ── Node version managers ──────────────────────────────────────────────
21
+ # nvm
22
+ export NVM_DIR="${NVM_DIR:-${HOME}/.nvm}"
23
+ if [ -s "${NVM_DIR}/nvm.sh" ]; then
24
+ # shellcheck source=/dev/null
25
+ . "${NVM_DIR}/nvm.sh"
26
+ if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
27
+ fi
28
+
29
+ # fnm (Fast Node Manager)
30
+ if command -v fnm >/dev/null 2>&1; then
31
+ eval "$(fnm env 2>/dev/null)" || true
32
+ elif [ -x "${HOME}/.local/share/fnm/fnm" ]; then
33
+ export PATH="${HOME}/.local/share/fnm:${PATH}"
34
+ eval "$(fnm env 2>/dev/null)" || true
35
+ fi
36
+ if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
37
+
38
+ # volta
39
+ if [ -d "${HOME}/.volta/bin" ]; then
40
+ export PATH="${HOME}/.volta/bin:${PATH}"
41
+ if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
42
+ fi
43
+
44
+ # mise / asdf
45
+ if [ -d "${HOME}/.local/share/mise/shims" ]; then
46
+ export PATH="${HOME}/.local/share/mise/shims:${PATH}"
47
+ if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
48
+ fi
49
+ if [ -d "${HOME}/.asdf/shims" ]; then
50
+ export PATH="${HOME}/.asdf/shims:${PATH}"
51
+ if command -v "${provider}" >/dev/null 2>&1; then return 0; fi
52
+ fi
53
+
54
+ # ── Well-known bin directories ─────────────────────────────────────────
55
+ local candidate_dirs=(
56
+ "${HOME}/.npm-global/bin"
57
+ "${HOME}/.local/bin"
58
+ "${HOME}/.claude/bin"
59
+ "/usr/local/bin"
60
+ "${HOME}/.yarn/bin"
61
+ "${HOME}/.bun/bin"
62
+ "${HOME}/.local/share/pnpm"
63
+ "/home/linuxbrew/.linuxbrew/bin"
64
+ "/opt/homebrew/bin"
65
+ )
66
+
67
+ for dir in "${candidate_dirs[@]}"; do
68
+ if [ -x "${dir}/${provider}" ]; then
69
+ export PATH="${dir}:${PATH}"
70
+ return 0
71
+ fi
72
+ done
73
+
74
+ return 1
75
+ }
76
+
5
77
  # ── Provider validation ───────────────────────────────────────────────────────
6
78
 
7
79
  # Validates that the provider command is supported.
@@ -168,7 +240,9 @@ log() {
168
240
  _esec=$(( _elapsed % 60 ))
169
241
  elapsed_str=" [+${_emin}m${_esec}s]"
170
242
  fi
171
- echo "[$(date '+%Y-%m-%d %H:%M:%S')] [PID:$$]${elapsed_str} $*" >> "${log_file}"
243
+ local msg="[$(date '+%Y-%m-%d %H:%M:%S')] [PID:$$]${elapsed_str} $*"
244
+ echo "${msg}" >> "${log_file}"
245
+ echo "${msg}" >&2
172
246
  }
173
247
 
174
248
  # Write a visual separator line to the log to delimit separate runs.
@@ -54,18 +54,17 @@ if [ "${REVIEWER_RETRY_DELAY}" -gt 300 ]; then
54
54
  REVIEWER_RETRY_DELAY="300"
55
55
  fi
56
56
 
57
- # Ensure NVM / Node / Claude are on PATH
58
- export NVM_DIR="${HOME}/.nvm"
59
- [ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
60
-
61
- # NOTE: Environment variables should be set by the caller (Node.js CLI).
62
- # The .env.night-watch sourcing has been removed - config is now injected via env vars.
63
-
64
57
  mkdir -p "${LOG_DIR}"
65
58
 
66
59
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
67
60
  # shellcheck source=night-watch-helpers.sh
68
61
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
62
+
63
+ # Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
64
+ if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
65
+ echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
66
+ exit 127
67
+ fi
69
68
  PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
70
69
  PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
71
70
  GLOBAL_LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}.lock"
@@ -1078,7 +1077,7 @@ for ATTEMPT in $(seq 1 "${TOTAL_ATTEMPTS}"); do
1078
1077
  cd "${REVIEW_WORKTREE_DIR}" && timeout "${ATTEMPT_TIMEOUT}" \
1079
1078
  claude -p "${REVIEWER_PROMPT}" \
1080
1079
  --dangerously-skip-permissions \
1081
- >> "${LOG_FILE}" 2>&1
1080
+ 2>&1 | tee -a "${LOG_FILE}"
1082
1081
  ); then
1083
1082
  EXIT_CODE=0
1084
1083
  else
@@ -1092,7 +1091,7 @@ for ATTEMPT in $(seq 1 "${TOTAL_ATTEMPTS}"); do
1092
1091
  -C "${REVIEW_WORKTREE_DIR}" \
1093
1092
  --yolo \
1094
1093
  "${REVIEWER_PROMPT}" \
1095
- >> "${LOG_FILE}" 2>&1
1094
+ 2>&1 | tee -a "${LOG_FILE}"
1096
1095
  ); then
1097
1096
  EXIT_CODE=0
1098
1097
  else
@@ -29,18 +29,17 @@ QA_ARTIFACTS="${NW_QA_ARTIFACTS:-both}"
29
29
  QA_AUTO_INSTALL_PLAYWRIGHT="${NW_QA_AUTO_INSTALL_PLAYWRIGHT:-1}"
30
30
  SCRIPT_START_TIME=$(date +%s)
31
31
 
32
- # Ensure NVM / Node / Claude are on PATH
33
- export NVM_DIR="${HOME}/.nvm"
34
- [ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
35
-
36
- # NOTE: Environment variables should be set by the caller (Node.js CLI).
37
- # The .env.night-watch sourcing has been removed - config is now injected via env vars.
38
-
39
32
  mkdir -p "${LOG_DIR}"
40
33
 
41
34
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
42
35
  # shellcheck source=night-watch-helpers.sh
43
36
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
37
+
38
+ # Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
39
+ if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
40
+ echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
41
+ exit 127
42
+ fi
44
43
  PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
45
44
  # NOTE: Lock file path must match qaLockPath() in src/utils/status-data.ts
46
45
  LOCK_FILE="/tmp/night-watch-qa-${PROJECT_RUNTIME_KEY}.lock"
@@ -598,7 +597,7 @@ Action: generating QA tests and evidence."
598
597
  cd "${QA_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
599
598
  claude -p "${QA_PROMPT}" \
600
599
  --dangerously-skip-permissions \
601
- >> "${LOG_FILE}" 2>&1
600
+ 2>&1 | tee -a "${LOG_FILE}"
602
601
  ); then
603
602
  PROVIDER_OK=1
604
603
  else
@@ -625,7 +624,7 @@ Action: generating QA tests and evidence."
625
624
  -C "${QA_WORKTREE_DIR}" \
626
625
  --yolo \
627
626
  "${QA_PROMPT}" \
628
- >> "${LOG_FILE}" 2>&1
627
+ 2>&1 | tee -a "${LOG_FILE}"
629
628
  ); then
630
629
  PROVIDER_OK=1
631
630
  else
@@ -25,15 +25,14 @@ PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
25
25
  PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
26
26
  SCRIPT_START_TIME=$(date +%s)
27
27
 
28
- # Ensure NVM / Node / Night Watch CLI are on PATH
29
- export NVM_DIR="${HOME}/.nvm"
30
- [ -s "${NVM_DIR}/nvm.sh" ] && . "${NVM_DIR}/nvm.sh"
31
-
32
28
  mkdir -p "${LOG_DIR}"
33
29
 
34
30
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
35
31
  # shellcheck source=night-watch-helpers.sh
36
32
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
33
+
34
+ # Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
35
+ ensure_provider_on_path "${PROVIDER_CMD}" || true
37
36
  PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
38
37
  LOCK_FILE="/tmp/night-watch-slicer-${PROJECT_RUNTIME_KEY}.lock"
39
38
  PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonit-dev/night-watch-cli",
3
- "version": "1.7.78",
3
+ "version": "1.7.80",
4
4
  "description": "Autonomous PRD execution using AI Provider CLIs + cron",
5
5
  "type": "module",
6
6
  "bin": {