@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.
- package/dist/commands/cancel.d.ts +1 -1
- package/dist/commands/cancel.d.ts.map +1 -1
- package/dist/commands/cancel.js +6 -7
- package/dist/commands/cancel.js.map +1 -1
- package/dist/commands/dashboard/tab-config.d.ts.map +1 -1
- package/dist/commands/dashboard/tab-config.js.map +1 -1
- package/dist/commands/dashboard/tab-logs.js.map +1 -1
- package/dist/commands/dashboard/tab-schedules.js.map +1 -1
- package/dist/commands/dashboard/tab-status.d.ts.map +1 -1
- package/dist/commands/dashboard/tab-status.js.map +1 -1
- package/dist/commands/dashboard/types.d.ts.map +1 -1
- package/dist/commands/review.d.ts +4 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +51 -19
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts +6 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +59 -21
- package/dist/commands/run.js.map +1 -1
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +185 -4
- package/dist/server/index.js.map +1 -1
- package/dist/storage/repositories/interfaces.d.ts.map +1 -1
- package/dist/storage/repositories/sqlite/execution-history-repository.d.ts.map +1 -1
- package/dist/storage/repositories/sqlite/prd-state-repository.d.ts.map +1 -1
- package/dist/storage/repositories/sqlite/project-registry-repository.d.ts.map +1 -1
- package/dist/storage/repositories/sqlite/roadmap-state-repository.d.ts.map +1 -1
- package/dist/storage/sqlite/client.js.map +1 -1
- package/dist/utils/github.d.ts +10 -0
- package/dist/utils/github.d.ts.map +1 -1
- package/dist/utils/github.js +48 -26
- package/dist/utils/github.js.map +1 -1
- package/dist/utils/script-result.d.ts +12 -0
- package/dist/utils/script-result.d.ts.map +1 -0
- package/dist/utils/script-result.js +46 -0
- package/dist/utils/script-result.js.map +1 -0
- package/dist/utils/shell.d.ts +14 -0
- package/dist/utils/shell.d.ts.map +1 -1
- package/dist/utils/shell.js +21 -1
- package/dist/utils/shell.js.map +1 -1
- package/dist/utils/status-data.d.ts +2 -0
- package/dist/utils/status-data.d.ts.map +1 -1
- package/dist/utils/status-data.js +30 -1
- package/dist/utils/status-data.js.map +1 -1
- package/package.json +4 -3
- package/scripts/night-watch-cron.sh +37 -6
- package/scripts/night-watch-helpers.sh +19 -0
- package/scripts/night-watch-pr-reviewer-cron.sh +23 -2
- package/web/dist/assets/index-BCMat9cx.js +350 -0
- package/web/dist/assets/{index-Dx_ZY5CY.css → index-BW6hS5jr.css} +1 -1
- package/web/dist/index.html +2 -2
- 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="
|
|
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
|
|
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
|
|
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
|
|
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
|