@jonit-dev/night-watch-cli 1.7.76 → 1.7.77

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.
package/dist/cli.js CHANGED
@@ -5399,9 +5399,9 @@ function scanExistingPrdSlugs(prdDir) {
5399
5399
  }
5400
5400
  return slugs;
5401
5401
  }
5402
- function buildProviderArgs(provider, prompt2) {
5402
+ function buildProviderArgs(provider, prompt2, workingDir) {
5403
5403
  if (provider === "codex") {
5404
- return ["exec", "--yolo", prompt2];
5404
+ return ["exec", "-C", workingDir, "--yolo", prompt2];
5405
5405
  }
5406
5406
  return ["-p", prompt2, "--dangerously-skip-permissions"];
5407
5407
  }
@@ -5425,7 +5425,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
5425
5425
  const promptVars = createSlicerPromptVars(item.title, item.section, item.description, prdDir, filename);
5426
5426
  const prompt2 = renderSlicerPrompt(promptVars);
5427
5427
  const provider = resolveJobProvider(config, "slicer");
5428
- const providerArgs = buildProviderArgs(provider, prompt2);
5428
+ const providerArgs = buildProviderArgs(provider, prompt2, projectDir);
5429
5429
  const logDir = path14.join(projectDir, "logs");
5430
5430
  if (!fs15.existsSync(logDir)) {
5431
5431
  fs15.mkdirSync(logDir, { recursive: true });
@@ -135,6 +135,16 @@ Failed to create audit worktree."
135
135
  exit 1
136
136
  fi
137
137
 
138
+ if ! assert_isolated_worktree "${PROJECT_DIR}" "${AUDIT_WORKTREE_DIR}" "audit"; then
139
+ log "FAIL: Audit worktree guard rejected ${AUDIT_WORKTREE_DIR}"
140
+ send_telegram_status_message "🔎 Night Watch Auditor: failed" "Project: ${PROJECT_NAME}
141
+ Provider (model): ${PROVIDER_MODEL_DISPLAY}
142
+ Failure reason: worktree_guard_failed
143
+ Audit run refused to execute in the primary checkout."
144
+ emit_result "failure" "reason=worktree_guard_failed"
145
+ exit 1
146
+ fi
147
+
138
148
  # Ensure the logs dir exists inside the worktree so the provider can write the report
139
149
  mkdir -p "${AUDIT_WORKTREE_DIR}/logs"
140
150
 
@@ -167,6 +177,7 @@ for AUDIT_ATTEMPT in $(seq 1 "${AUDIT_MAX_RETRIES}"); do
167
177
  if (
168
178
  cd "${AUDIT_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
169
179
  codex exec \
180
+ -C "${AUDIT_WORKTREE_DIR}" \
170
181
  --yolo \
171
182
  "${AUDIT_PROMPT}" \
172
183
  >> "${LOG_FILE}" 2>&1
@@ -333,6 +333,10 @@ finalize_prd_done() {
333
333
  # NOTE: PRDs are moved to done/ immediately when a PR is opened (or already merged)
334
334
  # rather than waiting for reviewer/merge loops.
335
335
  if prepare_detached_worktree "${PROJECT_DIR}" "${BOOKKEEP_WORKTREE_DIR}" "${DEFAULT_BRANCH}" "${LOG_FILE}"; then
336
+ if ! assert_isolated_worktree "${PROJECT_DIR}" "${BOOKKEEP_WORKTREE_DIR}" "executor-bookkeeping"; then
337
+ log "WARN: Bookkeeping worktree guard rejected ${BOOKKEEP_WORKTREE_DIR}"
338
+ return 1
339
+ fi
336
340
  if mark_prd_done "${BOOKKEEP_PRD_DIR}" "${ELIGIBLE_PRD}"; then
337
341
  night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" success --exit-code 0 2>/dev/null || true
338
342
  if [[ "${PRD_DIR_REL}" = /* ]]; then
@@ -480,6 +484,14 @@ if ! prepare_branch_worktree "${PROJECT_DIR}" "${WORKTREE_DIR}" "${BRANCH_NAME}"
480
484
  exit 1
481
485
  fi
482
486
 
487
+ if ! assert_isolated_worktree "${PROJECT_DIR}" "${WORKTREE_DIR}" "executor"; then
488
+ log "FAIL: Executor worktree guard rejected ${WORKTREE_DIR}"
489
+ restore_issue_to_ready "Failed worktree isolation guard for branch ${BRANCH_NAME}. Moved back to Ready for retry."
490
+ night_watch_history record "${PROJECT_DIR}" "${ELIGIBLE_PRD}" failure --exit-code 1 2>/dev/null || true
491
+ emit_result "failure" "prd=${ELIGIBLE_PRD}|branch=${BRANCH_NAME}|reason=worktree_guard_failed|detail=$(latest_failure_detail "${LOG_FILE}")"
492
+ exit 1
493
+ fi
494
+
483
495
  # Sandbox: prevent the agent from modifying crontab during execution
484
496
  export NW_EXECUTION_CONTEXT=agent
485
497
 
@@ -521,6 +533,7 @@ while [ "${ATTEMPT}" -lt "${MAX_RETRIES}" ]; do
521
533
  if (
522
534
  cd "${WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
523
535
  codex exec \
536
+ -C "${WORKTREE_DIR}" \
524
537
  --yolo \
525
538
  "${PROMPT}" \
526
539
  >> "${LOG_FILE}" 2>&1
@@ -550,6 +550,44 @@ prepare_detached_worktree() {
550
550
  git -C "${project_dir}" worktree add --detach "${worktree_dir}" "${base_ref}" >> "${log_file}" 2>&1
551
551
  }
552
552
 
553
+ assert_isolated_worktree() {
554
+ local project_dir="${1:?project_dir required}"
555
+ local worktree_dir="${2:?worktree_dir required}"
556
+ local context_label="${3:-worktree}"
557
+ local project_real=""
558
+ local worktree_real=""
559
+
560
+ if ! project_real=$(cd "${project_dir}" && pwd -P); then
561
+ log "FAIL: ${context_label} guard could not resolve project directory ${project_dir}"
562
+ return 1
563
+ fi
564
+
565
+ if ! worktree_real=$(cd "${worktree_dir}" && pwd -P); then
566
+ log "FAIL: ${context_label} guard could not resolve worktree directory ${worktree_dir}"
567
+ return 1
568
+ fi
569
+
570
+ if [ "${project_real}" = "${worktree_real}" ]; then
571
+ log "FAIL: ${context_label} guard refused to run in the primary checkout ${project_real}"
572
+ return 1
573
+ fi
574
+
575
+ if [ ! -f "${worktree_dir}/.git" ]; then
576
+ log "FAIL: ${context_label} guard expected linked worktree metadata at ${worktree_dir}/.git"
577
+ return 1
578
+ fi
579
+
580
+ if ! git -C "${worktree_dir}" rev-parse --is-inside-work-tree >/dev/null 2>&1; then
581
+ log "FAIL: ${context_label} guard found invalid git worktree at ${worktree_real}"
582
+ return 1
583
+ fi
584
+
585
+ if ! git -C "${project_dir}" worktree list --porcelain 2>/dev/null | grep -qF "worktree ${worktree_real}"; then
586
+ log "FAIL: ${context_label} guard could not find registered worktree ${worktree_real}"
587
+ return 1
588
+ fi
589
+ }
590
+
553
591
  # ── Mark PRD as done ─────────────────────────────────────────────────────────
554
592
 
555
593
  mark_prd_done() {
@@ -910,6 +910,12 @@ if ! prepare_detached_worktree "${PROJECT_DIR}" "${REVIEW_WORKTREE_DIR}" "${DEFA
910
910
  exit 1
911
911
  fi
912
912
 
913
+ if ! assert_isolated_worktree "${PROJECT_DIR}" "${REVIEW_WORKTREE_DIR}" "reviewer"; then
914
+ log "FAIL: Reviewer worktree guard rejected ${REVIEW_WORKTREE_DIR}"
915
+ emit_result "failure" "reason=worktree_guard_failed"
916
+ exit 1
917
+ fi
918
+
913
919
  REVIEWER_PROMPT_PATH=$(resolve_instruction_path_with_fallback "${REVIEW_WORKTREE_DIR}" "pr-reviewer.md" "night-watch-pr-reviewer.md" || true)
914
920
  if [ -z "${REVIEWER_PROMPT_PATH}" ]; then
915
921
  log "FAIL: Missing reviewer prompt file. Checked pr-reviewer.md/night-watch-pr-reviewer.md in instructions/, .claude/commands/, and bundled templates/"
@@ -1054,6 +1060,11 @@ for ATTEMPT in $(seq 1 "${TOTAL_ATTEMPTS}"); do
1054
1060
  log "RETRY: Unable to recreate reviewer worktree; aborting"
1055
1061
  break
1056
1062
  fi
1063
+ if ! assert_isolated_worktree "${PROJECT_DIR}" "${REVIEW_WORKTREE_DIR}" "reviewer"; then
1064
+ EXIT_CODE=1
1065
+ log "RETRY: Reviewer worktree guard rejected recreated directory; aborting"
1066
+ break
1067
+ fi
1057
1068
  fi
1058
1069
 
1059
1070
  log "RETRY: Starting attempt ${ATTEMPT}/${TOTAL_ATTEMPTS} (timeout: ${ATTEMPT_TIMEOUT}s) pr=${TARGET_PR:-all}"
@@ -1078,6 +1089,7 @@ for ATTEMPT in $(seq 1 "${TOTAL_ATTEMPTS}"); do
1078
1089
  if (
1079
1090
  cd "${REVIEW_WORKTREE_DIR}" && timeout "${ATTEMPT_TIMEOUT}" \
1080
1091
  codex exec \
1092
+ -C "${REVIEW_WORKTREE_DIR}" \
1081
1093
  --yolo \
1082
1094
  "${REVIEWER_PROMPT}" \
1083
1095
  >> "${LOG_FILE}" 2>&1
@@ -541,6 +541,15 @@ Action: generating QA tests and evidence."
541
541
  break
542
542
  fi
543
543
 
544
+ if ! assert_isolated_worktree "${PROJECT_DIR}" "${QA_WORKTREE_DIR}" "qa"; then
545
+ log "FAIL: QA worktree guard rejected ${QA_WORKTREE_DIR} for PR #${pr_num}"
546
+ FAILED_AUTOMATION_PRS_CSV=$(append_csv "${FAILED_AUTOMATION_PRS_CSV}" "#${pr_num}")
547
+ FAILED_PR="#${pr_num}"
548
+ FAILED_REASON="worktree_guard_failed"
549
+ EXIT_CODE=1
550
+ break
551
+ fi
552
+
544
553
  log "QA: Checking out PR #${pr_num} in worktree"
545
554
  # Prefer detached checkout to avoid "branch already used by worktree" failures
546
555
  # when the same branch is already checked out in another local worktree.
@@ -613,6 +622,7 @@ Action: generating QA tests and evidence."
613
622
  if (
614
623
  cd "${QA_WORKTREE_DIR}" && timeout "${MAX_RUNTIME}" \
615
624
  codex exec \
625
+ -C "${QA_WORKTREE_DIR}" \
616
626
  --yolo \
617
627
  "${QA_PROMPT}" \
618
628
  >> "${LOG_FILE}" 2>&1
@@ -37,14 +37,16 @@ echo "==> Publishing to npm..."
37
37
  cd "$CLI_PKG"
38
38
  npm publish --access public
39
39
 
40
- echo "==> Installing globally (with retry for registry propagation)..."
40
+ echo "==> Installing globally (waiting for registry propagation)..."
41
+ echo " Waiting 60s for npm registry to propagate..."
42
+ sleep 60
41
43
  for attempt in 1 2 3 4 5; do
42
44
  if npm i -g "@jonit-dev/night-watch-cli@$NEW_VERSION"; then
43
45
  break
44
46
  fi
45
47
  if [[ $attempt -lt 5 ]]; then
46
- echo " Attempt $attempt failed, retrying in 30s..."
47
- sleep 30
48
+ echo " Attempt $attempt failed, retrying in 60s..."
49
+ sleep 60
48
50
  else
49
51
  echo " All attempts failed. Install manually: npm i -g @jonit-dev/night-watch-cli@$NEW_VERSION"
50
52
  exit 1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jonit-dev/night-watch-cli",
3
- "version": "1.7.76",
3
+ "version": "1.7.77",
4
4
  "description": "Autonomous PRD execution using AI Provider CLIs + cron",
5
5
  "type": "module",
6
6
  "bin": {