@jonit-dev/night-watch-cli 1.1.4 → 1.1.5
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/README.md +48 -426
- package/dist/cli.js +6 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/doctor.d.ts +16 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +155 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +15 -9
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/prd.d.ts +24 -0
- package/dist/commands/prd.d.ts.map +1 -0
- package/dist/commands/prd.js +283 -0
- package/dist/commands/prd.js.map +1 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +24 -0
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts +19 -0
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +55 -6
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +27 -6
- package/dist/commands/status.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +3 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/templates/prd-template.d.ts +11 -0
- package/dist/templates/prd-template.d.ts.map +1 -0
- package/dist/templates/prd-template.js +166 -0
- package/dist/templates/prd-template.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/github.d.ts +30 -0
- package/dist/utils/github.d.ts.map +1 -0
- package/dist/utils/github.js +104 -0
- package/dist/utils/github.js.map +1 -0
- package/dist/utils/notify.d.ts +63 -0
- package/dist/utils/notify.d.ts.map +1 -0
- package/dist/utils/notify.js +237 -0
- package/dist/utils/notify.js.map +1 -0
- package/package.json +4 -4
- package/scripts/night-watch-cron.sh +8 -1
- package/scripts/night-watch-helpers.sh +51 -0
- package/scripts/test-helpers.bats +77 -0
- package/templates/prd.md +26 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jonit-dev/night-watch-cli",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.5",
|
|
4
4
|
"description": "Autonomous PRD execution using AI Provider CLIs + cron",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -40,11 +40,11 @@
|
|
|
40
40
|
"license": "MIT",
|
|
41
41
|
"repository": {
|
|
42
42
|
"type": "git",
|
|
43
|
-
"url": "https://github.com/
|
|
43
|
+
"url": "https://github.com/jonit-dev/night-watch-cli.git"
|
|
44
44
|
},
|
|
45
|
-
"homepage": "https://github.com/
|
|
45
|
+
"homepage": "https://github.com/jonit-dev/night-watch-cli#readme",
|
|
46
46
|
"bugs": {
|
|
47
|
-
"url": "https://github.com/
|
|
47
|
+
"url": "https://github.com/jonit-dev/night-watch-cli/issues"
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"chalk": "^5.6.2",
|
|
@@ -55,13 +55,19 @@ fi
|
|
|
55
55
|
|
|
56
56
|
cleanup_worktrees "${PROJECT_DIR}"
|
|
57
57
|
|
|
58
|
-
ELIGIBLE_PRD=$(find_eligible_prd "${PRD_DIR}")
|
|
58
|
+
ELIGIBLE_PRD=$(find_eligible_prd "${PRD_DIR}" "${MAX_RUNTIME}")
|
|
59
59
|
|
|
60
60
|
if [ -z "${ELIGIBLE_PRD}" ]; then
|
|
61
61
|
log "SKIP: No eligible PRDs (all done, in-progress, or blocked)"
|
|
62
62
|
exit 0
|
|
63
63
|
fi
|
|
64
64
|
|
|
65
|
+
# Claim the PRD to prevent other runs from selecting it
|
|
66
|
+
claim_prd "${PRD_DIR}" "${ELIGIBLE_PRD}"
|
|
67
|
+
|
|
68
|
+
# Update EXIT trap to also release claim
|
|
69
|
+
trap "rm -f '${LOCK_FILE}'; release_claim '${PRD_DIR}' '${ELIGIBLE_PRD}'" EXIT
|
|
70
|
+
|
|
65
71
|
PRD_NAME="${ELIGIBLE_PRD%.md}"
|
|
66
72
|
BRANCH_NAME="night-watch/${PRD_NAME}"
|
|
67
73
|
if [ -n "${NW_DEFAULT_BRANCH:-}" ]; then
|
|
@@ -146,6 +152,7 @@ esac
|
|
|
146
152
|
if [ ${EXIT_CODE} -eq 0 ]; then
|
|
147
153
|
PR_EXISTS=$(gh pr list --state open --json headRefName --jq '.[].headRefName' 2>/dev/null | grep -cF "${BRANCH_NAME}" || echo "0")
|
|
148
154
|
if [ "${PR_EXISTS}" -gt 0 ]; then
|
|
155
|
+
release_claim "${PRD_DIR}" "${ELIGIBLE_PRD}"
|
|
149
156
|
mark_prd_done "${PRD_DIR}" "${ELIGIBLE_PRD}"
|
|
150
157
|
git -C "${PROJECT_DIR}" add -A docs/PRDs/night-watch/
|
|
151
158
|
git -C "${PROJECT_DIR}" commit -m "chore: mark ${ELIGIBLE_PRD} as done (PR opened on ${BRANCH_NAME})
|
|
@@ -118,10 +118,55 @@ detect_default_branch() {
|
|
|
118
118
|
echo "main"
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
# ── Claim management ─────────────────────────────────────────────────────────
|
|
122
|
+
|
|
123
|
+
claim_prd() {
|
|
124
|
+
local prd_dir="${1:?prd_dir required}"
|
|
125
|
+
local prd_file="${2:?prd_file required}"
|
|
126
|
+
local claim_file="${prd_dir}/${prd_file}.claim"
|
|
127
|
+
|
|
128
|
+
printf '{"timestamp":%d,"hostname":"%s","pid":%d}\n' \
|
|
129
|
+
"$(date +%s)" "$(hostname)" "$$" > "${claim_file}"
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
release_claim() {
|
|
133
|
+
local prd_dir="${1:?prd_dir required}"
|
|
134
|
+
local prd_file="${2:?prd_file required}"
|
|
135
|
+
local claim_file="${prd_dir}/${prd_file}.claim"
|
|
136
|
+
|
|
137
|
+
rm -f "${claim_file}"
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
is_claimed() {
|
|
141
|
+
local prd_dir="${1:?prd_dir required}"
|
|
142
|
+
local prd_file="${2:?prd_file required}"
|
|
143
|
+
local max_runtime="${3:-7200}"
|
|
144
|
+
local claim_file="${prd_dir}/${prd_file}.claim"
|
|
145
|
+
|
|
146
|
+
if [ ! -f "${claim_file}" ]; then
|
|
147
|
+
return 1
|
|
148
|
+
fi
|
|
149
|
+
|
|
150
|
+
local claim_ts
|
|
151
|
+
claim_ts=$(grep -o '"timestamp":[0-9]*' "${claim_file}" 2>/dev/null | grep -o '[0-9]*' || echo "0")
|
|
152
|
+
local now
|
|
153
|
+
now=$(date +%s)
|
|
154
|
+
local age=$(( now - claim_ts ))
|
|
155
|
+
|
|
156
|
+
if [ "${age}" -lt "${max_runtime}" ]; then
|
|
157
|
+
return 0 # actively claimed
|
|
158
|
+
else
|
|
159
|
+
# Stale claim — remove it
|
|
160
|
+
rm -f "${claim_file}"
|
|
161
|
+
return 1
|
|
162
|
+
fi
|
|
163
|
+
}
|
|
164
|
+
|
|
121
165
|
# ── Find next eligible PRD ───────────────────────────────────────────────────
|
|
122
166
|
|
|
123
167
|
find_eligible_prd() {
|
|
124
168
|
local prd_dir="${1:?prd_dir required}"
|
|
169
|
+
local max_runtime="${2:-7200}"
|
|
125
170
|
local done_dir="${prd_dir}/done"
|
|
126
171
|
|
|
127
172
|
local prd_files
|
|
@@ -139,6 +184,12 @@ find_eligible_prd() {
|
|
|
139
184
|
prd_file=$(basename "${prd_path}")
|
|
140
185
|
local prd_name="${prd_file%.md}"
|
|
141
186
|
|
|
187
|
+
# Skip if claimed by another process
|
|
188
|
+
if is_claimed "${prd_dir}" "${prd_file}" "${max_runtime}"; then
|
|
189
|
+
log "SKIP-PRD: ${prd_file} — claimed by another process"
|
|
190
|
+
continue
|
|
191
|
+
fi
|
|
192
|
+
|
|
142
193
|
# Skip if a PR already exists for this PRD
|
|
143
194
|
if echo "${open_branches}" | grep -qF "${prd_name}"; then
|
|
144
195
|
log "SKIP-PRD: ${prd_file} — open PR already exists"
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
#!/usr/bin/env bats
|
|
2
|
+
|
|
3
|
+
# Tests for night-watch-helpers.sh claim functions
|
|
4
|
+
|
|
5
|
+
setup() {
|
|
6
|
+
# Source the helpers
|
|
7
|
+
SCRIPT_DIR="$(cd "$(dirname "${BATS_TEST_FILENAME}")" && pwd)"
|
|
8
|
+
|
|
9
|
+
# Set required globals
|
|
10
|
+
export LOG_FILE="/tmp/night-watch-test-$$.log"
|
|
11
|
+
|
|
12
|
+
source "${SCRIPT_DIR}/night-watch-helpers.sh"
|
|
13
|
+
|
|
14
|
+
# Create temp PRD directory
|
|
15
|
+
TEST_PRD_DIR=$(mktemp -d)
|
|
16
|
+
echo "# Test PRD" > "${TEST_PRD_DIR}/01-test-prd.md"
|
|
17
|
+
echo "# Test PRD 2" > "${TEST_PRD_DIR}/02-test-prd.md"
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
teardown() {
|
|
21
|
+
rm -rf "${TEST_PRD_DIR}"
|
|
22
|
+
rm -f "${LOG_FILE}"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@test "claim_prd creates .claim file with JSON" {
|
|
26
|
+
claim_prd "${TEST_PRD_DIR}" "01-test-prd.md"
|
|
27
|
+
|
|
28
|
+
[ -f "${TEST_PRD_DIR}/01-test-prd.md.claim" ]
|
|
29
|
+
|
|
30
|
+
local content
|
|
31
|
+
content=$(cat "${TEST_PRD_DIR}/01-test-prd.md.claim")
|
|
32
|
+
|
|
33
|
+
# Check JSON contains expected fields
|
|
34
|
+
echo "${content}" | grep -q '"timestamp":'
|
|
35
|
+
echo "${content}" | grep -q '"hostname":'
|
|
36
|
+
echo "${content}" | grep -q '"pid":'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
@test "is_claimed returns 0 for active claim" {
|
|
40
|
+
claim_prd "${TEST_PRD_DIR}" "01-test-prd.md"
|
|
41
|
+
|
|
42
|
+
run is_claimed "${TEST_PRD_DIR}" "01-test-prd.md" 7200
|
|
43
|
+
[ "$status" -eq 0 ]
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
@test "is_claimed returns 1 for stale claim" {
|
|
47
|
+
# Write a claim with an old timestamp (1 second)
|
|
48
|
+
printf '{"timestamp":1000000000,"hostname":"test","pid":1}\n' \
|
|
49
|
+
> "${TEST_PRD_DIR}/01-test-prd.md.claim"
|
|
50
|
+
|
|
51
|
+
run is_claimed "${TEST_PRD_DIR}" "01-test-prd.md" 7200
|
|
52
|
+
[ "$status" -eq 1 ]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
@test "is_claimed returns 1 for no claim" {
|
|
56
|
+
run is_claimed "${TEST_PRD_DIR}" "01-test-prd.md" 7200
|
|
57
|
+
[ "$status" -eq 1 ]
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@test "release_claim removes .claim file" {
|
|
61
|
+
claim_prd "${TEST_PRD_DIR}" "01-test-prd.md"
|
|
62
|
+
[ -f "${TEST_PRD_DIR}/01-test-prd.md.claim" ]
|
|
63
|
+
|
|
64
|
+
release_claim "${TEST_PRD_DIR}" "01-test-prd.md"
|
|
65
|
+
[ ! -f "${TEST_PRD_DIR}/01-test-prd.md.claim" ]
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
@test "find_eligible_prd skips claimed PRD" {
|
|
69
|
+
# Claim the first PRD
|
|
70
|
+
claim_prd "${TEST_PRD_DIR}" "01-test-prd.md"
|
|
71
|
+
|
|
72
|
+
# find_eligible_prd should skip 01 and return 02
|
|
73
|
+
local result
|
|
74
|
+
result=$(find_eligible_prd "${TEST_PRD_DIR}" 7200)
|
|
75
|
+
|
|
76
|
+
[ "${result}" = "02-test-prd.md" ]
|
|
77
|
+
}
|
package/templates/prd.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# PRD: {{TITLE}}
|
|
2
|
+
|
|
3
|
+
{{DEPENDS_ON}}
|
|
4
|
+
|
|
5
|
+
**Complexity: {{COMPLEXITY_SCORE}} → {{COMPLEXITY_LEVEL}} mode**
|
|
6
|
+
{{COMPLEXITY_BREAKDOWN}}
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Problem
|
|
11
|
+
|
|
12
|
+
<!-- What problem does this solve? Describe in 1-2 sentences. -->
|
|
13
|
+
|
|
14
|
+
## Solution
|
|
15
|
+
|
|
16
|
+
<!-- How will you solve it? 3-5 bullets explaining the approach. -->
|
|
17
|
+
|
|
18
|
+
## Phases
|
|
19
|
+
|
|
20
|
+
{{PHASES}}
|
|
21
|
+
|
|
22
|
+
## Acceptance Criteria
|
|
23
|
+
|
|
24
|
+
- [ ] All phases complete
|
|
25
|
+
- [ ] All tests pass
|
|
26
|
+
- [ ] Feature is reachable (not orphaned code)
|