@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 +3 -3
- package/dist/scripts/night-watch-audit-cron.sh +11 -0
- package/dist/scripts/night-watch-cron.sh +13 -0
- package/dist/scripts/night-watch-helpers.sh +38 -0
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +12 -0
- package/dist/scripts/night-watch-qa-cron.sh +10 -0
- package/dist/scripts/publish.sh +5 -3
- package/package.json +1 -1
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
|
package/dist/scripts/publish.sh
CHANGED
|
@@ -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 (
|
|
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
|
|
47
|
-
sleep
|
|
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
|