@jonit-dev/night-watch-cli 1.7.48 → 1.7.50
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 +960 -377
- package/dist/commands/cron.d.ts +8 -0
- package/dist/commands/cron.d.ts.map +1 -0
- package/dist/commands/cron.js +214 -0
- package/dist/commands/cron.js.map +1 -0
- package/dist/commands/init.js +16 -16
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/qa.d.ts.map +1 -1
- package/dist/commands/qa.js +3 -27
- package/dist/commands/qa.js.map +1 -1
- package/dist/commands/review.d.ts +20 -0
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +97 -18
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +7 -18
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/shared/env-builder.d.ts +25 -0
- package/dist/commands/shared/env-builder.d.ts.map +1 -0
- package/dist/commands/shared/env-builder.js +48 -0
- package/dist/commands/shared/env-builder.js.map +1 -0
- package/dist/commands/slice.d.ts.map +1 -1
- package/dist/commands/slice.js +3 -23
- package/dist/commands/slice.js.map +1 -1
- package/dist/scripts/night-watch-audit-cron.sh +55 -32
- package/dist/scripts/night-watch-cron.sh +108 -2
- package/dist/scripts/night-watch-helpers.sh +36 -0
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +193 -9
- package/dist/scripts/night-watch-qa-cron.sh +12 -2
- package/dist/templates/night-watch-pr-reviewer.md +147 -135
- package/dist/templates/night-watch-slicer.md +1 -1
- package/dist/templates/night-watch.md +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -46,115 +46,127 @@ import * as fs6 from "fs";
|
|
|
46
46
|
import * as fs7 from "fs";
|
|
47
47
|
import * as path6 from "path";
|
|
48
48
|
import { execSync as execSync2 } from "child_process";
|
|
49
|
-
import * as fs8 from "fs";
|
|
50
|
-
import * as path7 from "path";
|
|
51
49
|
import * as os3 from "os";
|
|
50
|
+
import * as path7 from "path";
|
|
51
|
+
import * as fs8 from "fs";
|
|
52
|
+
import * as fs9 from "fs";
|
|
52
53
|
import * as path8 from "path";
|
|
54
|
+
import * as os4 from "os";
|
|
55
|
+
import * as path9 from "path";
|
|
53
56
|
import { execFileSync } from "child_process";
|
|
57
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
58
|
+
import * as fs10 from "fs";
|
|
54
59
|
import chalk from "chalk";
|
|
55
60
|
import ora from "ora";
|
|
56
61
|
import Table from "cli-table3";
|
|
57
|
-
import
|
|
58
|
-
import * as fs10 from "fs";
|
|
59
|
-
import * as os4 from "os";
|
|
60
|
-
import * as path9 from "path";
|
|
61
|
-
import * as crypto from "crypto";
|
|
62
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
62
63
|
import * as fs11 from "fs";
|
|
63
64
|
import * as path10 from "path";
|
|
64
65
|
import * as fs12 from "fs";
|
|
65
66
|
import * as path11 from "path";
|
|
66
67
|
import * as fs13 from "fs";
|
|
68
|
+
import * as os5 from "os";
|
|
67
69
|
import * as path12 from "path";
|
|
70
|
+
import * as crypto from "crypto";
|
|
71
|
+
import * as fs14 from "fs";
|
|
72
|
+
import * as path13 from "path";
|
|
73
|
+
import * as fs15 from "fs";
|
|
74
|
+
import * as path14 from "path";
|
|
75
|
+
import * as fs16 from "fs";
|
|
76
|
+
import * as path15 from "path";
|
|
68
77
|
import { spawn } from "child_process";
|
|
69
78
|
import { createHash as createHash3 } from "crypto";
|
|
70
79
|
import { spawn as spawn2 } from "child_process";
|
|
80
|
+
import { execFileSync as execFileSync4 } from "child_process";
|
|
81
|
+
import * as fs17 from "fs";
|
|
82
|
+
import * as path16 from "path";
|
|
71
83
|
import "reflect-metadata";
|
|
72
84
|
import { Command as Command2 } from "commander";
|
|
73
|
-
import { existsSync as
|
|
85
|
+
import { existsSync as existsSync30, readFileSync as readFileSync18 } from "fs";
|
|
74
86
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
75
|
-
import { dirname as dirname8, join as
|
|
76
|
-
import
|
|
77
|
-
import
|
|
87
|
+
import { dirname as dirname8, join as join33 } from "path";
|
|
88
|
+
import fs18 from "fs";
|
|
89
|
+
import path17 from "path";
|
|
78
90
|
import { execSync as execSync3 } from "child_process";
|
|
79
91
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
80
|
-
import { dirname as dirname4, join as
|
|
92
|
+
import { dirname as dirname4, join as join16 } from "path";
|
|
81
93
|
import * as readline from "readline";
|
|
82
|
-
import * as
|
|
83
|
-
import * as path14 from "path";
|
|
84
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
85
|
-
import * as path15 from "path";
|
|
86
|
-
import * as path16 from "path";
|
|
87
|
-
import * as fs16 from "fs";
|
|
88
|
-
import * as path17 from "path";
|
|
89
|
-
import { execSync as execSync4 } from "child_process";
|
|
94
|
+
import * as fs19 from "fs";
|
|
90
95
|
import * as path18 from "path";
|
|
91
|
-
import
|
|
96
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
92
97
|
import * as path19 from "path";
|
|
93
|
-
import * as fs18 from "fs";
|
|
94
|
-
import chalk2 from "chalk";
|
|
95
|
-
import { spawn as spawn3 } from "child_process";
|
|
96
98
|
import * as path20 from "path";
|
|
97
|
-
import * as fs19 from "fs";
|
|
98
99
|
import * as fs20 from "fs";
|
|
99
100
|
import * as path21 from "path";
|
|
101
|
+
import { execSync as execSync4 } from "child_process";
|
|
102
|
+
import * as path22 from "path";
|
|
103
|
+
import * as fs21 from "fs";
|
|
104
|
+
import * as path23 from "path";
|
|
105
|
+
import * as fs22 from "fs";
|
|
106
|
+
import chalk2 from "chalk";
|
|
107
|
+
import { spawn as spawn3 } from "child_process";
|
|
108
|
+
import * as path24 from "path";
|
|
109
|
+
import * as fs23 from "fs";
|
|
110
|
+
import * as fs24 from "fs";
|
|
111
|
+
import * as path25 from "path";
|
|
100
112
|
import * as readline2 from "readline";
|
|
101
113
|
import blessed6 from "blessed";
|
|
102
114
|
import blessed from "blessed";
|
|
103
|
-
import * as
|
|
115
|
+
import * as fs25 from "fs";
|
|
104
116
|
import blessed2 from "blessed";
|
|
105
117
|
import blessed3 from "blessed";
|
|
106
118
|
import cronstrue from "cronstrue";
|
|
107
119
|
import blessed4 from "blessed";
|
|
108
120
|
import { spawn as spawn4 } from "child_process";
|
|
109
121
|
import blessed5 from "blessed";
|
|
110
|
-
import * as fs22 from "fs";
|
|
111
|
-
import * as path22 from "path";
|
|
112
|
-
import * as fs27 from "fs";
|
|
113
122
|
import * as fs26 from "fs";
|
|
114
|
-
import * as
|
|
123
|
+
import * as path26 from "path";
|
|
124
|
+
import * as fs31 from "fs";
|
|
125
|
+
import * as fs30 from "fs";
|
|
126
|
+
import * as path32 from "path";
|
|
115
127
|
import { dirname as dirname7 } from "path";
|
|
116
128
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
117
129
|
import cors from "cors";
|
|
118
130
|
import express from "express";
|
|
119
|
-
import * as
|
|
120
|
-
import * as
|
|
121
|
-
import * as
|
|
122
|
-
import * as
|
|
131
|
+
import * as fs27 from "fs";
|
|
132
|
+
import * as path27 from "path";
|
|
133
|
+
import * as fs28 from "fs";
|
|
134
|
+
import * as path28 from "path";
|
|
123
135
|
import { spawn as spawn5 } from "child_process";
|
|
124
136
|
import { Router } from "express";
|
|
125
137
|
import { Router as Router2 } from "express";
|
|
126
138
|
import { Router as Router3 } from "express";
|
|
127
139
|
import { Router as Router4 } from "express";
|
|
128
|
-
import * as
|
|
129
|
-
import * as
|
|
140
|
+
import * as fs29 from "fs";
|
|
141
|
+
import * as path29 from "path";
|
|
130
142
|
import { execSync as execSync5 } from "child_process";
|
|
131
143
|
import { Router as Router5 } from "express";
|
|
132
|
-
import * as
|
|
144
|
+
import * as path30 from "path";
|
|
133
145
|
import { Router as Router6 } from "express";
|
|
134
146
|
import { Router as Router7 } from "express";
|
|
135
|
-
import * as
|
|
147
|
+
import * as path31 from "path";
|
|
136
148
|
import { Router as Router8 } from "express";
|
|
137
149
|
import { Router as Router9 } from "express";
|
|
138
150
|
import { CronExpressionParser } from "cron-parser";
|
|
139
151
|
import { spawnSync } from "child_process";
|
|
140
|
-
import * as
|
|
141
|
-
import * as
|
|
142
|
-
import * as
|
|
143
|
-
import * as
|
|
152
|
+
import * as fs32 from "fs";
|
|
153
|
+
import * as path33 from "path";
|
|
154
|
+
import * as fs33 from "fs";
|
|
155
|
+
import * as path34 from "path";
|
|
144
156
|
import chalk3 from "chalk";
|
|
145
157
|
import chalk4 from "chalk";
|
|
146
158
|
import { execSync as execSync6 } from "child_process";
|
|
147
|
-
import * as
|
|
159
|
+
import * as fs34 from "fs";
|
|
148
160
|
import * as readline3 from "readline";
|
|
149
|
-
import * as
|
|
150
|
-
import * as
|
|
151
|
-
import * as
|
|
152
|
-
import * as
|
|
161
|
+
import * as fs35 from "fs";
|
|
162
|
+
import * as path35 from "path";
|
|
163
|
+
import * as os6 from "os";
|
|
164
|
+
import * as path36 from "path";
|
|
153
165
|
import chalk5 from "chalk";
|
|
154
166
|
import { Command } from "commander";
|
|
155
|
-
import { execFileSync as
|
|
156
|
-
import * as
|
|
157
|
-
import * as
|
|
167
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
168
|
+
import * as fs36 from "fs";
|
|
169
|
+
import * as path37 from "path";
|
|
158
170
|
import * as readline4 from "readline";
|
|
159
171
|
import chalk6 from "chalk";
|
|
160
172
|
var __defProp = Object.defineProperty;
|
|
@@ -3453,101 +3465,107 @@ function findMatchingIssue(targetTitle, issues, threshold = 0.8) {
|
|
|
3453
3465
|
}
|
|
3454
3466
|
return bestMatch;
|
|
3455
3467
|
}
|
|
3468
|
+
var HORIZON_SHORT_TERM;
|
|
3469
|
+
var HORIZON_MEDIUM_TERM;
|
|
3470
|
+
var HORIZON_LONG_TERM;
|
|
3456
3471
|
var ROADMAP_SECTION_MAPPINGS;
|
|
3457
3472
|
var init_roadmap_mapping = __esm({
|
|
3458
3473
|
"../core/dist/board/roadmap-mapping.js"() {
|
|
3459
3474
|
"use strict";
|
|
3475
|
+
HORIZON_SHORT_TERM = "short-term";
|
|
3476
|
+
HORIZON_MEDIUM_TERM = "medium-term";
|
|
3477
|
+
HORIZON_LONG_TERM = "long-term";
|
|
3460
3478
|
ROADMAP_SECTION_MAPPINGS = [
|
|
3461
3479
|
{
|
|
3462
3480
|
sectionPattern: /§1.*Reliability.*correctness/i,
|
|
3463
3481
|
category: "reliability",
|
|
3464
|
-
horizon:
|
|
3482
|
+
horizon: HORIZON_SHORT_TERM
|
|
3465
3483
|
},
|
|
3466
3484
|
{
|
|
3467
3485
|
sectionPattern: /§2.*Quality.*developer/i,
|
|
3468
3486
|
category: "quality",
|
|
3469
|
-
horizon:
|
|
3487
|
+
horizon: HORIZON_SHORT_TERM
|
|
3470
3488
|
},
|
|
3471
3489
|
{
|
|
3472
3490
|
sectionPattern: /§3.*Product.*operators/i,
|
|
3473
3491
|
category: "product",
|
|
3474
|
-
horizon:
|
|
3492
|
+
horizon: HORIZON_SHORT_TERM
|
|
3475
3493
|
},
|
|
3476
3494
|
{
|
|
3477
3495
|
sectionPattern: /§4.*Unified.*operations/i,
|
|
3478
3496
|
category: "ux",
|
|
3479
|
-
horizon:
|
|
3497
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3480
3498
|
},
|
|
3481
3499
|
{
|
|
3482
3500
|
sectionPattern: /§5.*Provider.*execution/i,
|
|
3483
3501
|
category: "provider",
|
|
3484
|
-
horizon:
|
|
3502
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3485
3503
|
},
|
|
3486
3504
|
{
|
|
3487
3505
|
sectionPattern: /§6.*Team.*multi-project/i,
|
|
3488
3506
|
category: "team",
|
|
3489
|
-
horizon:
|
|
3507
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3490
3508
|
},
|
|
3491
3509
|
{
|
|
3492
3510
|
sectionPattern: /§7.*Platformization.*enterprise/i,
|
|
3493
3511
|
category: "platform",
|
|
3494
|
-
horizon:
|
|
3512
|
+
horizon: HORIZON_LONG_TERM
|
|
3495
3513
|
},
|
|
3496
3514
|
{
|
|
3497
3515
|
sectionPattern: /§8.*Intelligence.*autonomous/i,
|
|
3498
3516
|
category: "intelligence",
|
|
3499
|
-
horizon:
|
|
3517
|
+
horizon: HORIZON_LONG_TERM
|
|
3500
3518
|
},
|
|
3501
3519
|
{
|
|
3502
3520
|
sectionPattern: /§9.*Ecosystem.*adoption/i,
|
|
3503
3521
|
category: "ecosystem",
|
|
3504
|
-
horizon:
|
|
3522
|
+
horizon: HORIZON_LONG_TERM
|
|
3505
3523
|
},
|
|
3506
3524
|
// Fallback patterns without section numbers
|
|
3507
3525
|
{
|
|
3508
3526
|
sectionPattern: /Reliability.*correctness/i,
|
|
3509
3527
|
category: "reliability",
|
|
3510
|
-
horizon:
|
|
3528
|
+
horizon: HORIZON_SHORT_TERM
|
|
3511
3529
|
},
|
|
3512
3530
|
{
|
|
3513
3531
|
sectionPattern: /Quality.*developer.*workflow/i,
|
|
3514
3532
|
category: "quality",
|
|
3515
|
-
horizon:
|
|
3533
|
+
horizon: HORIZON_SHORT_TERM
|
|
3516
3534
|
},
|
|
3517
3535
|
{
|
|
3518
3536
|
sectionPattern: /Product.*completeness/i,
|
|
3519
3537
|
category: "product",
|
|
3520
|
-
horizon:
|
|
3538
|
+
horizon: HORIZON_SHORT_TERM
|
|
3521
3539
|
},
|
|
3522
3540
|
{
|
|
3523
3541
|
sectionPattern: /Unified.*operations/i,
|
|
3524
3542
|
category: "ux",
|
|
3525
|
-
horizon:
|
|
3543
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3526
3544
|
},
|
|
3527
3545
|
{
|
|
3528
3546
|
sectionPattern: /Provider.*execution/i,
|
|
3529
3547
|
category: "provider",
|
|
3530
|
-
horizon:
|
|
3548
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3531
3549
|
},
|
|
3532
3550
|
{
|
|
3533
3551
|
sectionPattern: /Team.*multi-project/i,
|
|
3534
3552
|
category: "team",
|
|
3535
|
-
horizon:
|
|
3553
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3536
3554
|
},
|
|
3537
3555
|
{
|
|
3538
3556
|
sectionPattern: /Platformization.*enterprise/i,
|
|
3539
3557
|
category: "platform",
|
|
3540
|
-
horizon:
|
|
3558
|
+
horizon: HORIZON_LONG_TERM
|
|
3541
3559
|
},
|
|
3542
3560
|
{
|
|
3543
3561
|
sectionPattern: /Intelligence.*autonomous/i,
|
|
3544
3562
|
category: "intelligence",
|
|
3545
|
-
horizon:
|
|
3563
|
+
horizon: HORIZON_LONG_TERM
|
|
3546
3564
|
},
|
|
3547
3565
|
{
|
|
3548
3566
|
sectionPattern: /Ecosystem.*adoption/i,
|
|
3549
3567
|
category: "ecosystem",
|
|
3550
|
-
horizon:
|
|
3568
|
+
horizon: HORIZON_LONG_TERM
|
|
3551
3569
|
}
|
|
3552
3570
|
];
|
|
3553
3571
|
}
|
|
@@ -4286,6 +4304,34 @@ function checkLockFile(lockPath) {
|
|
|
4286
4304
|
return { running: false, pid: null };
|
|
4287
4305
|
}
|
|
4288
4306
|
}
|
|
4307
|
+
function acquireLock(lockPath, pid) {
|
|
4308
|
+
const effectivePid = pid ?? process.pid;
|
|
4309
|
+
if (fs5.existsSync(lockPath)) {
|
|
4310
|
+
const lockInfo = checkLockFile(lockPath);
|
|
4311
|
+
if (lockInfo.running) {
|
|
4312
|
+
return false;
|
|
4313
|
+
}
|
|
4314
|
+
try {
|
|
4315
|
+
fs5.unlinkSync(lockPath);
|
|
4316
|
+
} catch {
|
|
4317
|
+
return false;
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
try {
|
|
4321
|
+
fs5.writeFileSync(lockPath, String(effectivePid), "utf-8");
|
|
4322
|
+
return true;
|
|
4323
|
+
} catch {
|
|
4324
|
+
return false;
|
|
4325
|
+
}
|
|
4326
|
+
}
|
|
4327
|
+
function releaseLock(lockPath) {
|
|
4328
|
+
try {
|
|
4329
|
+
if (fs5.existsSync(lockPath)) {
|
|
4330
|
+
fs5.unlinkSync(lockPath);
|
|
4331
|
+
}
|
|
4332
|
+
} catch {
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4289
4335
|
function countPRDs(projectDir, prdDir, maxRuntime) {
|
|
4290
4336
|
const fullPrdPath = path5.join(projectDir, prdDir);
|
|
4291
4337
|
if (!fs5.existsSync(fullPrdPath)) {
|
|
@@ -4877,7 +4923,7 @@ function checkPrdDirectory(projectDir, prdDir) {
|
|
|
4877
4923
|
}
|
|
4878
4924
|
};
|
|
4879
4925
|
}
|
|
4880
|
-
const prds = fs7.readdirSync(prdPath).filter((f) => f.endsWith(".md")
|
|
4926
|
+
const prds = fs7.readdirSync(prdPath).filter((f) => f.endsWith(".md"));
|
|
4881
4927
|
return {
|
|
4882
4928
|
passed: true,
|
|
4883
4929
|
message: `PRD directory found: ${prdDir} (${prds.length} PRDs)`,
|
|
@@ -4950,12 +4996,90 @@ var init_checks = __esm({
|
|
|
4950
4996
|
init_constants();
|
|
4951
4997
|
}
|
|
4952
4998
|
});
|
|
4999
|
+
function claimPrd(prdDir, prdFile, pid) {
|
|
5000
|
+
const claimPath = path7.join(prdDir, prdFile + CLAIM_FILE_EXTENSION);
|
|
5001
|
+
const claimData = {
|
|
5002
|
+
timestamp: Math.floor(Date.now() / 1e3),
|
|
5003
|
+
hostname: os3.hostname(),
|
|
5004
|
+
pid: pid ?? process.pid
|
|
5005
|
+
};
|
|
5006
|
+
fs8.writeFileSync(claimPath, JSON.stringify(claimData), "utf-8");
|
|
5007
|
+
}
|
|
5008
|
+
function releaseClaim(prdDir, prdFile) {
|
|
5009
|
+
const claimPath = path7.join(prdDir, prdFile + CLAIM_FILE_EXTENSION);
|
|
5010
|
+
try {
|
|
5011
|
+
if (fs8.existsSync(claimPath)) {
|
|
5012
|
+
fs8.unlinkSync(claimPath);
|
|
5013
|
+
}
|
|
5014
|
+
} catch {
|
|
5015
|
+
}
|
|
5016
|
+
}
|
|
5017
|
+
function isClaimed(prdDir, prdFile, maxRuntime) {
|
|
5018
|
+
const claimPath = path7.join(prdDir, prdFile + CLAIM_FILE_EXTENSION);
|
|
5019
|
+
if (!fs8.existsSync(claimPath)) {
|
|
5020
|
+
return false;
|
|
5021
|
+
}
|
|
5022
|
+
try {
|
|
5023
|
+
const content = fs8.readFileSync(claimPath, "utf-8");
|
|
5024
|
+
const claimData = JSON.parse(content);
|
|
5025
|
+
if (typeof claimData.timestamp !== "number") {
|
|
5026
|
+
fs8.unlinkSync(claimPath);
|
|
5027
|
+
return false;
|
|
5028
|
+
}
|
|
5029
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
5030
|
+
const age = now - claimData.timestamp;
|
|
5031
|
+
if (age >= maxRuntime) {
|
|
5032
|
+
fs8.unlinkSync(claimPath);
|
|
5033
|
+
return false;
|
|
5034
|
+
}
|
|
5035
|
+
return true;
|
|
5036
|
+
} catch {
|
|
5037
|
+
try {
|
|
5038
|
+
fs8.unlinkSync(claimPath);
|
|
5039
|
+
} catch {
|
|
5040
|
+
}
|
|
5041
|
+
return false;
|
|
5042
|
+
}
|
|
5043
|
+
}
|
|
5044
|
+
function readClaimInfo(prdDir, prdFile, maxRuntime) {
|
|
5045
|
+
const claimPath = path7.join(prdDir, prdFile + CLAIM_FILE_EXTENSION);
|
|
5046
|
+
if (!fs8.existsSync(claimPath)) {
|
|
5047
|
+
return null;
|
|
5048
|
+
}
|
|
5049
|
+
try {
|
|
5050
|
+
const content = fs8.readFileSync(claimPath, "utf-8");
|
|
5051
|
+
const claimData = JSON.parse(content);
|
|
5052
|
+
if (typeof claimData.timestamp !== "number") {
|
|
5053
|
+
fs8.unlinkSync(claimPath);
|
|
5054
|
+
return null;
|
|
5055
|
+
}
|
|
5056
|
+
const now = Math.floor(Date.now() / 1e3);
|
|
5057
|
+
const age = now - claimData.timestamp;
|
|
5058
|
+
if (age >= maxRuntime) {
|
|
5059
|
+
fs8.unlinkSync(claimPath);
|
|
5060
|
+
return null;
|
|
5061
|
+
}
|
|
5062
|
+
return claimData;
|
|
5063
|
+
} catch {
|
|
5064
|
+
try {
|
|
5065
|
+
fs8.unlinkSync(claimPath);
|
|
5066
|
+
} catch {
|
|
5067
|
+
}
|
|
5068
|
+
return null;
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
var init_claim_manager = __esm({
|
|
5072
|
+
"../core/dist/utils/claim-manager.js"() {
|
|
5073
|
+
"use strict";
|
|
5074
|
+
init_constants();
|
|
5075
|
+
}
|
|
5076
|
+
});
|
|
4953
5077
|
function saveConfig(projectDir, changes) {
|
|
4954
|
-
const configPath =
|
|
5078
|
+
const configPath = path8.join(projectDir, CONFIG_FILE_NAME);
|
|
4955
5079
|
try {
|
|
4956
5080
|
let existing = {};
|
|
4957
|
-
if (
|
|
4958
|
-
const content =
|
|
5081
|
+
if (fs9.existsSync(configPath)) {
|
|
5082
|
+
const content = fs9.readFileSync(configPath, "utf-8");
|
|
4959
5083
|
existing = JSON.parse(content);
|
|
4960
5084
|
}
|
|
4961
5085
|
const merged = { ...existing };
|
|
@@ -4968,7 +5092,7 @@ function saveConfig(projectDir, changes) {
|
|
|
4968
5092
|
}
|
|
4969
5093
|
}
|
|
4970
5094
|
}
|
|
4971
|
-
|
|
5095
|
+
fs9.writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
4972
5096
|
return { success: true };
|
|
4973
5097
|
} catch (err) {
|
|
4974
5098
|
return {
|
|
@@ -4984,8 +5108,8 @@ var init_config_writer = __esm({
|
|
|
4984
5108
|
}
|
|
4985
5109
|
});
|
|
4986
5110
|
function getHistoryPath() {
|
|
4987
|
-
const base = process.env.NIGHT_WATCH_HOME ||
|
|
4988
|
-
return
|
|
5111
|
+
const base = process.env.NIGHT_WATCH_HOME || path9.join(os4.homedir(), GLOBAL_CONFIG_DIR);
|
|
5112
|
+
return path9.join(base, HISTORY_FILE_NAME);
|
|
4989
5113
|
}
|
|
4990
5114
|
function loadHistory() {
|
|
4991
5115
|
const { executionHistory } = getRepositories();
|
|
@@ -4996,7 +5120,7 @@ function saveHistory(history) {
|
|
|
4996
5120
|
executionHistory.replaceAll(history);
|
|
4997
5121
|
}
|
|
4998
5122
|
function recordExecution(projectDir, prdFile, outcome, exitCode, attempt = 1) {
|
|
4999
|
-
const resolved =
|
|
5123
|
+
const resolved = path9.resolve(projectDir);
|
|
5000
5124
|
const { executionHistory } = getRepositories();
|
|
5001
5125
|
const record = {
|
|
5002
5126
|
timestamp: Math.floor(Date.now() / 1e3),
|
|
@@ -5008,7 +5132,7 @@ function recordExecution(projectDir, prdFile, outcome, exitCode, attempt = 1) {
|
|
|
5008
5132
|
executionHistory.trimRecords(resolved, prdFile, MAX_HISTORY_RECORDS_PER_PRD);
|
|
5009
5133
|
}
|
|
5010
5134
|
function getLastExecution(projectDir, prdFile) {
|
|
5011
|
-
const resolved =
|
|
5135
|
+
const resolved = path9.resolve(projectDir);
|
|
5012
5136
|
const { executionHistory } = getRepositories();
|
|
5013
5137
|
const records = executionHistory.getRecords(resolved, prdFile);
|
|
5014
5138
|
return records.length > 0 ? records[0] : null;
|
|
@@ -5033,6 +5157,94 @@ var init_execution_history = __esm({
|
|
|
5033
5157
|
init_client();
|
|
5034
5158
|
}
|
|
5035
5159
|
});
|
|
5160
|
+
function getBranchTipTimestamp(projectDir, branch) {
|
|
5161
|
+
let remoteTs = null;
|
|
5162
|
+
let localTs = null;
|
|
5163
|
+
try {
|
|
5164
|
+
const output = execFileSync("git", ["-C", projectDir, "log", "-1", "--format=%ct", `refs/remotes/origin/${branch}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
5165
|
+
if (output) {
|
|
5166
|
+
remoteTs = parseInt(output, 10);
|
|
5167
|
+
}
|
|
5168
|
+
} catch {
|
|
5169
|
+
}
|
|
5170
|
+
try {
|
|
5171
|
+
const output = execFileSync("git", ["-C", projectDir, "log", "-1", "--format=%ct", `refs/heads/${branch}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
5172
|
+
if (output) {
|
|
5173
|
+
localTs = parseInt(output, 10);
|
|
5174
|
+
}
|
|
5175
|
+
} catch {
|
|
5176
|
+
}
|
|
5177
|
+
if (remoteTs !== null && localTs !== null) {
|
|
5178
|
+
return localTs > remoteTs ? localTs : remoteTs;
|
|
5179
|
+
}
|
|
5180
|
+
if (remoteTs !== null) {
|
|
5181
|
+
return remoteTs;
|
|
5182
|
+
}
|
|
5183
|
+
if (localTs !== null) {
|
|
5184
|
+
return localTs;
|
|
5185
|
+
}
|
|
5186
|
+
return null;
|
|
5187
|
+
}
|
|
5188
|
+
function detectDefaultBranch(projectDir) {
|
|
5189
|
+
const mainTs = getBranchTipTimestamp(projectDir, "main");
|
|
5190
|
+
const masterTs = getBranchTipTimestamp(projectDir, "master");
|
|
5191
|
+
if (mainTs !== null && masterTs !== null) {
|
|
5192
|
+
return mainTs >= masterTs ? "main" : "master";
|
|
5193
|
+
}
|
|
5194
|
+
if (mainTs !== null) {
|
|
5195
|
+
return "main";
|
|
5196
|
+
}
|
|
5197
|
+
if (masterTs !== null) {
|
|
5198
|
+
return "master";
|
|
5199
|
+
}
|
|
5200
|
+
try {
|
|
5201
|
+
const output = execFileSync("git", ["-C", projectDir, "symbolic-ref", "refs/remotes/origin/HEAD"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }).trim();
|
|
5202
|
+
const match = output.match(/^refs\/remotes\/origin\/(.+)$/);
|
|
5203
|
+
if (match) {
|
|
5204
|
+
return match[1];
|
|
5205
|
+
}
|
|
5206
|
+
} catch {
|
|
5207
|
+
}
|
|
5208
|
+
return "main";
|
|
5209
|
+
}
|
|
5210
|
+
function resolveWorktreeBaseRef(projectDir, defaultBranch) {
|
|
5211
|
+
try {
|
|
5212
|
+
execFileSync("git", [
|
|
5213
|
+
"-C",
|
|
5214
|
+
projectDir,
|
|
5215
|
+
"rev-parse",
|
|
5216
|
+
"--verify",
|
|
5217
|
+
"--quiet",
|
|
5218
|
+
`refs/remotes/origin/${defaultBranch}`
|
|
5219
|
+
], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5220
|
+
return `origin/${defaultBranch}`;
|
|
5221
|
+
} catch {
|
|
5222
|
+
}
|
|
5223
|
+
try {
|
|
5224
|
+
execFileSync("git", ["-C", projectDir, "rev-parse", "--verify", "--quiet", `refs/heads/${defaultBranch}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5225
|
+
return defaultBranch;
|
|
5226
|
+
} catch {
|
|
5227
|
+
}
|
|
5228
|
+
try {
|
|
5229
|
+
execFileSync("git", ["-C", projectDir, "rev-parse", "--verify", "--quiet", "refs/remotes/origin/HEAD"], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5230
|
+
return "origin/HEAD";
|
|
5231
|
+
} catch {
|
|
5232
|
+
}
|
|
5233
|
+
try {
|
|
5234
|
+
execFileSync("git", ["-C", projectDir, "rev-parse", "--verify", "--quiet", "HEAD"], {
|
|
5235
|
+
encoding: "utf-8",
|
|
5236
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
5237
|
+
});
|
|
5238
|
+
return "HEAD";
|
|
5239
|
+
} catch {
|
|
5240
|
+
}
|
|
5241
|
+
return null;
|
|
5242
|
+
}
|
|
5243
|
+
var init_git_utils = __esm({
|
|
5244
|
+
"../core/dist/utils/git-utils.js"() {
|
|
5245
|
+
"use strict";
|
|
5246
|
+
}
|
|
5247
|
+
});
|
|
5036
5248
|
function parsePrDetails(raw) {
|
|
5037
5249
|
try {
|
|
5038
5250
|
const details = JSON.parse(raw);
|
|
@@ -5054,7 +5266,7 @@ function parsePrDetails(raw) {
|
|
|
5054
5266
|
}
|
|
5055
5267
|
function fetchPrBySelector(selector, cwd) {
|
|
5056
5268
|
try {
|
|
5057
|
-
const output =
|
|
5269
|
+
const output = execFileSync2("gh", ["pr", "view", selector, "--json", "number,title,url,body,additions,deletions,changedFiles"], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5058
5270
|
return parsePrDetails(output);
|
|
5059
5271
|
} catch {
|
|
5060
5272
|
return null;
|
|
@@ -5068,7 +5280,7 @@ function fetchPrDetailsByNumber(prNumber, cwd) {
|
|
|
5068
5280
|
}
|
|
5069
5281
|
function fetchPrDetails(branchPrefix, cwd) {
|
|
5070
5282
|
try {
|
|
5071
|
-
const listOutput =
|
|
5283
|
+
const listOutput = execFileSync2("gh", ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "20"], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5072
5284
|
const prs = JSON.parse(listOutput);
|
|
5073
5285
|
const matching = prs.filter((pr) => pr.headRefName.startsWith(branchPrefix + "/"));
|
|
5074
5286
|
if (matching.length === 0) {
|
|
@@ -5082,7 +5294,7 @@ function fetchPrDetails(branchPrefix, cwd) {
|
|
|
5082
5294
|
}
|
|
5083
5295
|
function fetchReviewedPrDetails(branchPatterns, cwd) {
|
|
5084
5296
|
try {
|
|
5085
|
-
const listOutput =
|
|
5297
|
+
const listOutput = execFileSync2("gh", ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "20"], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5086
5298
|
const prs = JSON.parse(listOutput);
|
|
5087
5299
|
const matching = prs.filter((pr) => branchPatterns.some((pattern) => pr.headRefName.startsWith(pattern)));
|
|
5088
5300
|
if (matching.length === 0) {
|
|
@@ -5123,6 +5335,45 @@ var init_github = __esm({
|
|
|
5123
5335
|
"use strict";
|
|
5124
5336
|
}
|
|
5125
5337
|
});
|
|
5338
|
+
function rotateLog(logFile, maxSize = DEFAULT_MAX_LOG_SIZE) {
|
|
5339
|
+
if (!fs10.existsSync(logFile)) {
|
|
5340
|
+
return false;
|
|
5341
|
+
}
|
|
5342
|
+
try {
|
|
5343
|
+
const stats = fs10.statSync(logFile);
|
|
5344
|
+
if (stats.size > maxSize) {
|
|
5345
|
+
const oldPath = `${logFile}.old`;
|
|
5346
|
+
fs10.renameSync(logFile, oldPath);
|
|
5347
|
+
return true;
|
|
5348
|
+
}
|
|
5349
|
+
} catch {
|
|
5350
|
+
}
|
|
5351
|
+
return false;
|
|
5352
|
+
}
|
|
5353
|
+
function checkRateLimited(logFile, startLine) {
|
|
5354
|
+
if (!fs10.existsSync(logFile)) {
|
|
5355
|
+
return false;
|
|
5356
|
+
}
|
|
5357
|
+
try {
|
|
5358
|
+
const content = fs10.readFileSync(logFile, "utf-8");
|
|
5359
|
+
const lines = content.split("\n");
|
|
5360
|
+
let linesToCheck;
|
|
5361
|
+
if (startLine !== void 0 && startLine > 0) {
|
|
5362
|
+
linesToCheck = lines.slice(startLine);
|
|
5363
|
+
} else {
|
|
5364
|
+
linesToCheck = lines.slice(-20);
|
|
5365
|
+
}
|
|
5366
|
+
return linesToCheck.some((line) => line.includes("429"));
|
|
5367
|
+
} catch {
|
|
5368
|
+
return false;
|
|
5369
|
+
}
|
|
5370
|
+
}
|
|
5371
|
+
var init_log_utils = __esm({
|
|
5372
|
+
"../core/dist/utils/log-utils.js"() {
|
|
5373
|
+
"use strict";
|
|
5374
|
+
init_constants();
|
|
5375
|
+
}
|
|
5376
|
+
});
|
|
5126
5377
|
function success(msg) {
|
|
5127
5378
|
console.log(chalk.green("\u2714"), msg);
|
|
5128
5379
|
}
|
|
@@ -5282,6 +5533,11 @@ function buildDescription(ctx) {
|
|
|
5282
5533
|
if (ctx.duration !== void 0) {
|
|
5283
5534
|
lines.push(`Duration: ${ctx.duration}s`);
|
|
5284
5535
|
}
|
|
5536
|
+
if (ctx.event === "run_timeout") {
|
|
5537
|
+
lines.push("Cause: Execution hit the max runtime limit and was terminated.");
|
|
5538
|
+
lines.push("Resume: Progress is checkpointed on timeout, and the next run resumes from that branch state.");
|
|
5539
|
+
lines.push("Recommendation: Avoid huge PRDs; slice large work into smaller PRDs/phases.");
|
|
5540
|
+
}
|
|
5285
5541
|
if (ctx.event === "review_completed" && ctx.attempts !== void 0 && ctx.attempts > 1) {
|
|
5286
5542
|
const retryInfo = `Attempts: ${ctx.attempts}`;
|
|
5287
5543
|
if (ctx.finalScore !== void 0) {
|
|
@@ -5453,27 +5709,145 @@ var init_notify = __esm({
|
|
|
5453
5709
|
init_github();
|
|
5454
5710
|
}
|
|
5455
5711
|
});
|
|
5712
|
+
function getOpenBranches(projectDir) {
|
|
5713
|
+
try {
|
|
5714
|
+
const output = execFileSync3("gh", ["pr", "list", "--state", "open", "--json", "headRefName", "--jq", ".[].headRefName"], { cwd: projectDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5715
|
+
return output.trim().split("\n").filter((b) => b.length > 0);
|
|
5716
|
+
} catch {
|
|
5717
|
+
return [];
|
|
5718
|
+
}
|
|
5719
|
+
}
|
|
5720
|
+
function sortPrdsByPriority(files, priorityList) {
|
|
5721
|
+
if (!priorityList.length) {
|
|
5722
|
+
return files;
|
|
5723
|
+
}
|
|
5724
|
+
const prioritySet = new Set(priorityList);
|
|
5725
|
+
const prioritized = [];
|
|
5726
|
+
const remaining = [];
|
|
5727
|
+
for (const priorityName of priorityList) {
|
|
5728
|
+
const match = files.find((f) => f === `${priorityName}.md`);
|
|
5729
|
+
if (match) {
|
|
5730
|
+
prioritized.push(match);
|
|
5731
|
+
}
|
|
5732
|
+
}
|
|
5733
|
+
for (const file of files) {
|
|
5734
|
+
if (!prioritySet.has(file.replace(/\.md$/, ""))) {
|
|
5735
|
+
remaining.push(file);
|
|
5736
|
+
}
|
|
5737
|
+
}
|
|
5738
|
+
return [...prioritized, ...remaining];
|
|
5739
|
+
}
|
|
5740
|
+
function findEligiblePrd(options) {
|
|
5741
|
+
const { prdDir, projectDir, maxRuntime, prdPriority } = options;
|
|
5742
|
+
const doneDir = path10.join(prdDir, "done");
|
|
5743
|
+
if (!fs11.existsSync(prdDir)) {
|
|
5744
|
+
return null;
|
|
5745
|
+
}
|
|
5746
|
+
let prdFiles = fs11.readdirSync(prdDir).filter((f) => f.endsWith(".md") && fs11.statSync(path10.join(prdDir, f)).isFile()).sort();
|
|
5747
|
+
if (prdFiles.length === 0) {
|
|
5748
|
+
return null;
|
|
5749
|
+
}
|
|
5750
|
+
if (prdPriority) {
|
|
5751
|
+
const priorityList = prdPriority.split(":").filter((p) => p.length > 0);
|
|
5752
|
+
prdFiles = sortPrdsByPriority(prdFiles, priorityList);
|
|
5753
|
+
}
|
|
5754
|
+
const openBranches = getOpenBranches(projectDir);
|
|
5755
|
+
for (const prdFile of prdFiles) {
|
|
5756
|
+
const prdName = prdFile.replace(/\.md$/, "");
|
|
5757
|
+
const prdPath = path10.join(prdDir, prdFile);
|
|
5758
|
+
if (isClaimed(prdDir, prdFile, maxRuntime)) {
|
|
5759
|
+
continue;
|
|
5760
|
+
}
|
|
5761
|
+
if (isInCooldown(projectDir, prdFile, maxRuntime)) {
|
|
5762
|
+
continue;
|
|
5763
|
+
}
|
|
5764
|
+
if (openBranches.some((branch) => branch.includes(prdName))) {
|
|
5765
|
+
continue;
|
|
5766
|
+
}
|
|
5767
|
+
const dependencies = parsePrdDependencies(prdPath);
|
|
5768
|
+
let allDepsMet = true;
|
|
5769
|
+
for (const dep of dependencies) {
|
|
5770
|
+
const depFile = dep.endsWith(".md") ? dep : `${dep}.md`;
|
|
5771
|
+
const depPath = path10.join(doneDir, depFile);
|
|
5772
|
+
if (!fs11.existsSync(depPath)) {
|
|
5773
|
+
allDepsMet = false;
|
|
5774
|
+
break;
|
|
5775
|
+
}
|
|
5776
|
+
}
|
|
5777
|
+
if (!allDepsMet) {
|
|
5778
|
+
continue;
|
|
5779
|
+
}
|
|
5780
|
+
return prdFile;
|
|
5781
|
+
}
|
|
5782
|
+
return null;
|
|
5783
|
+
}
|
|
5784
|
+
function findEligibleBoardIssue(options) {
|
|
5785
|
+
const { projectDir, maxRuntime } = options;
|
|
5786
|
+
try {
|
|
5787
|
+
const output = execFileSync3("gh", ["issue", "list", "--state", "open", "--json", "number,title,body", "--jq", ".[]"], { cwd: projectDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5788
|
+
const issues = output.trim().split("\n").filter((line) => line.length > 0);
|
|
5789
|
+
for (const issueLine of issues) {
|
|
5790
|
+
try {
|
|
5791
|
+
const issue = JSON.parse(issueLine);
|
|
5792
|
+
const claimFile = `issue-${issue.number}`;
|
|
5793
|
+
if (isClaimed(projectDir, claimFile, maxRuntime)) {
|
|
5794
|
+
continue;
|
|
5795
|
+
}
|
|
5796
|
+
return {
|
|
5797
|
+
number: issue.number,
|
|
5798
|
+
title: issue.title,
|
|
5799
|
+
body: issue.body || ""
|
|
5800
|
+
};
|
|
5801
|
+
} catch {
|
|
5802
|
+
continue;
|
|
5803
|
+
}
|
|
5804
|
+
}
|
|
5805
|
+
} catch {
|
|
5806
|
+
}
|
|
5807
|
+
return null;
|
|
5808
|
+
}
|
|
5809
|
+
var init_prd_discovery = __esm({
|
|
5810
|
+
"../core/dist/utils/prd-discovery.js"() {
|
|
5811
|
+
"use strict";
|
|
5812
|
+
init_claim_manager();
|
|
5813
|
+
init_execution_history();
|
|
5814
|
+
init_status_data();
|
|
5815
|
+
}
|
|
5816
|
+
});
|
|
5456
5817
|
function slugify(name) {
|
|
5457
5818
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
5458
5819
|
}
|
|
5459
5820
|
function getNextPrdNumber(prdDir) {
|
|
5460
|
-
if (!
|
|
5821
|
+
if (!fs12.existsSync(prdDir))
|
|
5461
5822
|
return 1;
|
|
5462
|
-
const files =
|
|
5823
|
+
const files = fs12.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
|
|
5463
5824
|
const numbers = files.map((f) => {
|
|
5464
5825
|
const match = f.match(/^(\d+)-/);
|
|
5465
5826
|
return match ? parseInt(match[1], 10) : 0;
|
|
5466
5827
|
});
|
|
5467
5828
|
return Math.max(0, ...numbers) + 1;
|
|
5468
5829
|
}
|
|
5830
|
+
function markPrdDone(prdDir, prdFile) {
|
|
5831
|
+
const sourcePath = path11.join(prdDir, prdFile);
|
|
5832
|
+
if (!fs12.existsSync(sourcePath)) {
|
|
5833
|
+
return false;
|
|
5834
|
+
}
|
|
5835
|
+
const doneDir = path11.join(prdDir, "done");
|
|
5836
|
+
if (!fs12.existsSync(doneDir)) {
|
|
5837
|
+
fs12.mkdirSync(doneDir, { recursive: true });
|
|
5838
|
+
}
|
|
5839
|
+
const destPath = path11.join(doneDir, prdFile);
|
|
5840
|
+
fs12.renameSync(sourcePath, destPath);
|
|
5841
|
+
return true;
|
|
5842
|
+
}
|
|
5469
5843
|
var init_prd_utils = __esm({
|
|
5470
5844
|
"../core/dist/utils/prd-utils.js"() {
|
|
5471
5845
|
"use strict";
|
|
5472
5846
|
}
|
|
5473
5847
|
});
|
|
5474
5848
|
function getRegistryPath() {
|
|
5475
|
-
const base = process.env.NIGHT_WATCH_HOME ||
|
|
5476
|
-
return
|
|
5849
|
+
const base = process.env.NIGHT_WATCH_HOME || path12.join(os5.homedir(), GLOBAL_CONFIG_DIR);
|
|
5850
|
+
return path12.join(base, REGISTRY_FILE_NAME);
|
|
5477
5851
|
}
|
|
5478
5852
|
function loadRegistry() {
|
|
5479
5853
|
const { projectRegistry } = getRepositories();
|
|
@@ -5487,7 +5861,7 @@ function saveRegistry(entries) {
|
|
|
5487
5861
|
}
|
|
5488
5862
|
}
|
|
5489
5863
|
function registerProject(projectDir) {
|
|
5490
|
-
const resolvedPath =
|
|
5864
|
+
const resolvedPath = path12.resolve(projectDir);
|
|
5491
5865
|
const { projectRegistry } = getRepositories();
|
|
5492
5866
|
const entries = projectRegistry.getAll();
|
|
5493
5867
|
const existing = entries.find((e) => e.path === resolvedPath);
|
|
@@ -5496,13 +5870,13 @@ function registerProject(projectDir) {
|
|
|
5496
5870
|
}
|
|
5497
5871
|
const name = getProjectName(resolvedPath);
|
|
5498
5872
|
const nameExists = entries.some((e) => e.name === name);
|
|
5499
|
-
const finalName = nameExists ? `${name}-${
|
|
5873
|
+
const finalName = nameExists ? `${name}-${path12.basename(resolvedPath)}` : name;
|
|
5500
5874
|
const entry = { name: finalName, path: resolvedPath };
|
|
5501
5875
|
projectRegistry.upsert(entry);
|
|
5502
5876
|
return entry;
|
|
5503
5877
|
}
|
|
5504
5878
|
function unregisterProject(projectDir) {
|
|
5505
|
-
const resolvedPath =
|
|
5879
|
+
const resolvedPath = path12.resolve(projectDir);
|
|
5506
5880
|
const { projectRegistry } = getRepositories();
|
|
5507
5881
|
return projectRegistry.remove(resolvedPath);
|
|
5508
5882
|
}
|
|
@@ -5511,7 +5885,7 @@ function validateRegistry() {
|
|
|
5511
5885
|
const valid = [];
|
|
5512
5886
|
const invalid = [];
|
|
5513
5887
|
for (const entry of entries) {
|
|
5514
|
-
if (
|
|
5888
|
+
if (fs13.existsSync(entry.path) && fs13.existsSync(path12.join(entry.path, CONFIG_FILE_NAME))) {
|
|
5515
5889
|
valid.push(entry);
|
|
5516
5890
|
} else {
|
|
5517
5891
|
invalid.push(entry);
|
|
@@ -5704,15 +6078,15 @@ var init_roadmap_parser = __esm({
|
|
|
5704
6078
|
}
|
|
5705
6079
|
});
|
|
5706
6080
|
function getStateFilePath(prdDir) {
|
|
5707
|
-
return
|
|
6081
|
+
return path13.join(prdDir, STATE_FILE_NAME);
|
|
5708
6082
|
}
|
|
5709
6083
|
function readJsonState(prdDir) {
|
|
5710
6084
|
const statePath = getStateFilePath(prdDir);
|
|
5711
|
-
if (!
|
|
6085
|
+
if (!fs14.existsSync(statePath)) {
|
|
5712
6086
|
return null;
|
|
5713
6087
|
}
|
|
5714
6088
|
try {
|
|
5715
|
-
const content =
|
|
6089
|
+
const content = fs14.readFileSync(statePath, "utf-8");
|
|
5716
6090
|
const parsed = JSON.parse(content);
|
|
5717
6091
|
if (typeof parsed !== "object" || parsed === null) {
|
|
5718
6092
|
return null;
|
|
@@ -5750,11 +6124,11 @@ function saveRoadmapState(prdDir, state) {
|
|
|
5750
6124
|
const { roadmapState } = getRepositories();
|
|
5751
6125
|
roadmapState.save(prdDir, state);
|
|
5752
6126
|
const statePath = getStateFilePath(prdDir);
|
|
5753
|
-
const dir =
|
|
5754
|
-
if (!
|
|
5755
|
-
|
|
6127
|
+
const dir = path13.dirname(statePath);
|
|
6128
|
+
if (!fs14.existsSync(dir)) {
|
|
6129
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
5756
6130
|
}
|
|
5757
|
-
|
|
6131
|
+
fs14.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
5758
6132
|
}
|
|
5759
6133
|
function createEmptyState() {
|
|
5760
6134
|
return {
|
|
@@ -5797,9 +6171,9 @@ function loadSlicerTemplate(templateDir) {
|
|
|
5797
6171
|
if (cachedTemplate) {
|
|
5798
6172
|
return cachedTemplate;
|
|
5799
6173
|
}
|
|
5800
|
-
const templatePath = templateDir ?
|
|
6174
|
+
const templatePath = templateDir ? path14.join(templateDir, "night-watch-slicer.md") : path14.resolve(__dirname, "..", "..", "templates", "night-watch-slicer.md");
|
|
5801
6175
|
try {
|
|
5802
|
-
cachedTemplate =
|
|
6176
|
+
cachedTemplate = fs15.readFileSync(templatePath, "utf-8");
|
|
5803
6177
|
return cachedTemplate;
|
|
5804
6178
|
} catch (error2) {
|
|
5805
6179
|
console.warn(`Warning: Could not load slicer template from ${templatePath}, using default:`, error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -5824,7 +6198,7 @@ function createSlicerPromptVars(title, section, description, prdDir, prdFilename
|
|
|
5824
6198
|
title,
|
|
5825
6199
|
section,
|
|
5826
6200
|
description: description || "(No description provided)",
|
|
5827
|
-
outputFilePath:
|
|
6201
|
+
outputFilePath: path14.join(prdDir, prdFilename),
|
|
5828
6202
|
prdDir
|
|
5829
6203
|
};
|
|
5830
6204
|
}
|
|
@@ -6015,11 +6389,11 @@ function auditFindingToRoadmapItem(finding) {
|
|
|
6015
6389
|
};
|
|
6016
6390
|
}
|
|
6017
6391
|
function collectAuditPlannerItems(projectDir) {
|
|
6018
|
-
const reportPath =
|
|
6019
|
-
if (!
|
|
6392
|
+
const reportPath = path15.join(projectDir, "logs", "audit-report.md");
|
|
6393
|
+
if (!fs16.existsSync(reportPath)) {
|
|
6020
6394
|
return [];
|
|
6021
6395
|
}
|
|
6022
|
-
const reportContent =
|
|
6396
|
+
const reportContent = fs16.readFileSync(reportPath, "utf-8");
|
|
6023
6397
|
if (!reportContent.trim() || /\bNO_ISSUES_FOUND\b/.test(reportContent)) {
|
|
6024
6398
|
return [];
|
|
6025
6399
|
}
|
|
@@ -6028,9 +6402,9 @@ function collectAuditPlannerItems(projectDir) {
|
|
|
6028
6402
|
return findings.map(auditFindingToRoadmapItem);
|
|
6029
6403
|
}
|
|
6030
6404
|
function getRoadmapStatus(projectDir, config) {
|
|
6031
|
-
const roadmapPath =
|
|
6405
|
+
const roadmapPath = path15.join(projectDir, config.roadmapScanner.roadmapPath);
|
|
6032
6406
|
const scannerEnabled = config.roadmapScanner.enabled;
|
|
6033
|
-
if (!
|
|
6407
|
+
if (!fs16.existsSync(roadmapPath)) {
|
|
6034
6408
|
return {
|
|
6035
6409
|
found: false,
|
|
6036
6410
|
enabled: scannerEnabled,
|
|
@@ -6041,9 +6415,9 @@ function getRoadmapStatus(projectDir, config) {
|
|
|
6041
6415
|
items: []
|
|
6042
6416
|
};
|
|
6043
6417
|
}
|
|
6044
|
-
const content =
|
|
6418
|
+
const content = fs16.readFileSync(roadmapPath, "utf-8");
|
|
6045
6419
|
const items = parseRoadmap(content);
|
|
6046
|
-
const prdDir =
|
|
6420
|
+
const prdDir = path15.join(projectDir, config.prdDir);
|
|
6047
6421
|
const state = loadRoadmapState(prdDir);
|
|
6048
6422
|
const existingPrdSlugs = scanExistingPrdSlugs(prdDir);
|
|
6049
6423
|
const statusItems = items.map((item) => {
|
|
@@ -6080,12 +6454,12 @@ function getRoadmapStatus(projectDir, config) {
|
|
|
6080
6454
|
}
|
|
6081
6455
|
function scanExistingPrdSlugs(prdDir) {
|
|
6082
6456
|
const slugs = /* @__PURE__ */ new Set();
|
|
6083
|
-
if (!
|
|
6457
|
+
if (!fs16.existsSync(prdDir)) {
|
|
6084
6458
|
return slugs;
|
|
6085
6459
|
}
|
|
6086
|
-
const files =
|
|
6460
|
+
const files = fs16.readdirSync(prdDir);
|
|
6087
6461
|
for (const file of files) {
|
|
6088
|
-
if (!file.endsWith(".md")
|
|
6462
|
+
if (!file.endsWith(".md")) {
|
|
6089
6463
|
continue;
|
|
6090
6464
|
}
|
|
6091
6465
|
const match = file.match(/^\d+-(.+)\.md$/);
|
|
@@ -6119,19 +6493,19 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
|
|
|
6119
6493
|
const nextNum = getNextPrdNumber(prdDir);
|
|
6120
6494
|
const padded = String(nextNum).padStart(2, "0");
|
|
6121
6495
|
const filename = `${padded}-${itemSlug}.md`;
|
|
6122
|
-
const filePath =
|
|
6123
|
-
if (!
|
|
6124
|
-
|
|
6496
|
+
const filePath = path15.join(prdDir, filename);
|
|
6497
|
+
if (!fs16.existsSync(prdDir)) {
|
|
6498
|
+
fs16.mkdirSync(prdDir, { recursive: true });
|
|
6125
6499
|
}
|
|
6126
6500
|
const promptVars = createSlicerPromptVars(item.title, item.section, item.description, prdDir, filename);
|
|
6127
6501
|
const prompt2 = renderSlicerPrompt(promptVars);
|
|
6128
6502
|
const providerArgs = buildProviderArgs(config.provider, prompt2);
|
|
6129
|
-
const logDir =
|
|
6130
|
-
if (!
|
|
6131
|
-
|
|
6503
|
+
const logDir = path15.join(projectDir, "logs");
|
|
6504
|
+
if (!fs16.existsSync(logDir)) {
|
|
6505
|
+
fs16.mkdirSync(logDir, { recursive: true });
|
|
6132
6506
|
}
|
|
6133
|
-
const logFile =
|
|
6134
|
-
const logStream =
|
|
6507
|
+
const logFile = path15.join(logDir, `slicer-${itemSlug}.log`);
|
|
6508
|
+
const logStream = fs16.createWriteStream(logFile, { flags: "w" });
|
|
6135
6509
|
logStream.on("error", () => {
|
|
6136
6510
|
});
|
|
6137
6511
|
return new Promise((resolve9) => {
|
|
@@ -6168,7 +6542,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
|
|
|
6168
6542
|
});
|
|
6169
6543
|
return;
|
|
6170
6544
|
}
|
|
6171
|
-
if (!
|
|
6545
|
+
if (!fs16.existsSync(filePath)) {
|
|
6172
6546
|
resolve9({
|
|
6173
6547
|
sliced: false,
|
|
6174
6548
|
error: `Provider did not create expected file: ${filePath}`,
|
|
@@ -6191,23 +6565,23 @@ async function sliceNextItem(projectDir, config) {
|
|
|
6191
6565
|
error: "Roadmap scanner is disabled"
|
|
6192
6566
|
};
|
|
6193
6567
|
}
|
|
6194
|
-
const roadmapPath =
|
|
6568
|
+
const roadmapPath = path15.join(projectDir, config.roadmapScanner.roadmapPath);
|
|
6195
6569
|
const auditItems = collectAuditPlannerItems(projectDir);
|
|
6196
|
-
const roadmapExists =
|
|
6570
|
+
const roadmapExists = fs16.existsSync(roadmapPath);
|
|
6197
6571
|
if (!roadmapExists && auditItems.length === 0) {
|
|
6198
6572
|
return {
|
|
6199
6573
|
sliced: false,
|
|
6200
6574
|
error: "ROADMAP.md not found"
|
|
6201
6575
|
};
|
|
6202
6576
|
}
|
|
6203
|
-
const roadmapItems = roadmapExists ? parseRoadmap(
|
|
6577
|
+
const roadmapItems = roadmapExists ? parseRoadmap(fs16.readFileSync(roadmapPath, "utf-8")) : [];
|
|
6204
6578
|
if (roadmapExists && roadmapItems.length === 0 && auditItems.length === 0) {
|
|
6205
6579
|
return {
|
|
6206
6580
|
sliced: false,
|
|
6207
6581
|
error: "No items in roadmap"
|
|
6208
6582
|
};
|
|
6209
6583
|
}
|
|
6210
|
-
const prdDir =
|
|
6584
|
+
const prdDir = path15.join(projectDir, config.prdDir);
|
|
6211
6585
|
const state = loadRoadmapState(prdDir);
|
|
6212
6586
|
const existingPrdSlugs = scanExistingPrdSlugs(prdDir);
|
|
6213
6587
|
const pickEligibleItem = (items) => {
|
|
@@ -6423,6 +6797,162 @@ var init_webhook_validator = __esm({
|
|
|
6423
6797
|
"use strict";
|
|
6424
6798
|
}
|
|
6425
6799
|
});
|
|
6800
|
+
function gitExec(args, cwd, logFile) {
|
|
6801
|
+
try {
|
|
6802
|
+
const result = execFileSync4("git", args, {
|
|
6803
|
+
cwd,
|
|
6804
|
+
encoding: "utf-8",
|
|
6805
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6806
|
+
});
|
|
6807
|
+
if (logFile && result) {
|
|
6808
|
+
try {
|
|
6809
|
+
fs17.appendFileSync(logFile, result);
|
|
6810
|
+
} catch {
|
|
6811
|
+
}
|
|
6812
|
+
}
|
|
6813
|
+
return { success: true };
|
|
6814
|
+
} catch (error2) {
|
|
6815
|
+
const errorMessage = error2 instanceof Error ? error2.message : String(error2);
|
|
6816
|
+
if (logFile) {
|
|
6817
|
+
try {
|
|
6818
|
+
fs17.appendFileSync(logFile, errorMessage + "\n");
|
|
6819
|
+
} catch {
|
|
6820
|
+
}
|
|
6821
|
+
}
|
|
6822
|
+
return { success: false, error: errorMessage };
|
|
6823
|
+
}
|
|
6824
|
+
}
|
|
6825
|
+
function branchExistsLocally(projectDir, branchName) {
|
|
6826
|
+
try {
|
|
6827
|
+
execFileSync4("git", ["-C", projectDir, "rev-parse", "--verify", "--quiet", `refs/heads/${branchName}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
6828
|
+
return true;
|
|
6829
|
+
} catch {
|
|
6830
|
+
return false;
|
|
6831
|
+
}
|
|
6832
|
+
}
|
|
6833
|
+
function branchExistsRemotely(projectDir, branchName) {
|
|
6834
|
+
try {
|
|
6835
|
+
execFileSync4("git", ["-C", projectDir, "rev-parse", "--verify", "--quiet", `refs/remotes/origin/${branchName}`], { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
6836
|
+
return true;
|
|
6837
|
+
} catch {
|
|
6838
|
+
return false;
|
|
6839
|
+
}
|
|
6840
|
+
}
|
|
6841
|
+
function prepareBranchWorktree(options) {
|
|
6842
|
+
const { projectDir, worktreeDir, branchName, defaultBranch, logFile } = options;
|
|
6843
|
+
gitExec(["fetch", "origin", defaultBranch], projectDir, logFile);
|
|
6844
|
+
const baseRef = resolveWorktreeBaseRef(projectDir, defaultBranch);
|
|
6845
|
+
if (!baseRef) {
|
|
6846
|
+
return {
|
|
6847
|
+
success: false,
|
|
6848
|
+
worktreePath: worktreeDir,
|
|
6849
|
+
error: "No valid base ref found for worktree"
|
|
6850
|
+
};
|
|
6851
|
+
}
|
|
6852
|
+
if (branchExistsLocally(projectDir, branchName)) {
|
|
6853
|
+
const result2 = gitExec(["worktree", "add", worktreeDir, branchName], projectDir, logFile);
|
|
6854
|
+
return {
|
|
6855
|
+
success: result2.success,
|
|
6856
|
+
worktreePath: worktreeDir,
|
|
6857
|
+
error: result2.error
|
|
6858
|
+
};
|
|
6859
|
+
}
|
|
6860
|
+
if (branchExistsRemotely(projectDir, branchName)) {
|
|
6861
|
+
const result2 = gitExec(["worktree", "add", "-b", branchName, worktreeDir, `origin/${branchName}`], projectDir, logFile);
|
|
6862
|
+
return {
|
|
6863
|
+
success: result2.success,
|
|
6864
|
+
worktreePath: worktreeDir,
|
|
6865
|
+
error: result2.error
|
|
6866
|
+
};
|
|
6867
|
+
}
|
|
6868
|
+
const result = gitExec(["worktree", "add", "-b", branchName, worktreeDir, baseRef], projectDir, logFile);
|
|
6869
|
+
return {
|
|
6870
|
+
success: result.success,
|
|
6871
|
+
worktreePath: worktreeDir,
|
|
6872
|
+
error: result.error
|
|
6873
|
+
};
|
|
6874
|
+
}
|
|
6875
|
+
function prepareDetachedWorktree(options) {
|
|
6876
|
+
const { projectDir, worktreeDir, defaultBranch, logFile } = options;
|
|
6877
|
+
if (fs17.existsSync(worktreeDir)) {
|
|
6878
|
+
const isRegistered = isWorktreeRegistered(projectDir, worktreeDir);
|
|
6879
|
+
if (!isRegistered) {
|
|
6880
|
+
try {
|
|
6881
|
+
fs17.rmSync(worktreeDir, { recursive: true, force: true });
|
|
6882
|
+
} catch {
|
|
6883
|
+
}
|
|
6884
|
+
}
|
|
6885
|
+
}
|
|
6886
|
+
gitExec(["fetch", "origin", defaultBranch], projectDir, logFile);
|
|
6887
|
+
const baseRef = resolveWorktreeBaseRef(projectDir, defaultBranch);
|
|
6888
|
+
if (!baseRef) {
|
|
6889
|
+
return {
|
|
6890
|
+
success: false,
|
|
6891
|
+
worktreePath: worktreeDir,
|
|
6892
|
+
error: "No valid base ref found for worktree"
|
|
6893
|
+
};
|
|
6894
|
+
}
|
|
6895
|
+
const result = gitExec(["worktree", "add", "--detach", worktreeDir, baseRef], projectDir, logFile);
|
|
6896
|
+
return {
|
|
6897
|
+
success: result.success,
|
|
6898
|
+
worktreePath: worktreeDir,
|
|
6899
|
+
error: result.error
|
|
6900
|
+
};
|
|
6901
|
+
}
|
|
6902
|
+
function isWorktreeRegistered(projectDir, worktreePath) {
|
|
6903
|
+
try {
|
|
6904
|
+
const output = execFileSync4("git", ["-C", projectDir, "worktree", "list", "--porcelain"], {
|
|
6905
|
+
encoding: "utf-8",
|
|
6906
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6907
|
+
});
|
|
6908
|
+
const lines = output.split("\n");
|
|
6909
|
+
for (const line of lines) {
|
|
6910
|
+
if (line.startsWith("worktree ")) {
|
|
6911
|
+
const wtPath = line.substring("worktree ".length);
|
|
6912
|
+
if (wtPath === worktreePath) {
|
|
6913
|
+
return true;
|
|
6914
|
+
}
|
|
6915
|
+
}
|
|
6916
|
+
}
|
|
6917
|
+
return false;
|
|
6918
|
+
} catch {
|
|
6919
|
+
return false;
|
|
6920
|
+
}
|
|
6921
|
+
}
|
|
6922
|
+
function cleanupWorktrees(projectDir, scope) {
|
|
6923
|
+
const projectName = path16.basename(projectDir);
|
|
6924
|
+
const matchToken = scope ? scope : `${projectName}-nw`;
|
|
6925
|
+
const removed = [];
|
|
6926
|
+
try {
|
|
6927
|
+
const output = execFileSync4("git", ["-C", projectDir, "worktree", "list", "--porcelain"], {
|
|
6928
|
+
encoding: "utf-8",
|
|
6929
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
6930
|
+
});
|
|
6931
|
+
const lines = output.split("\n");
|
|
6932
|
+
const worktreePaths = [];
|
|
6933
|
+
for (const line of lines) {
|
|
6934
|
+
if (line.startsWith("worktree ")) {
|
|
6935
|
+
worktreePaths.push(line.substring("worktree ".length));
|
|
6936
|
+
}
|
|
6937
|
+
}
|
|
6938
|
+
for (const wtPath of worktreePaths) {
|
|
6939
|
+
if (wtPath.includes(matchToken)) {
|
|
6940
|
+
const result = gitExec(["worktree", "remove", "--force", wtPath], projectDir);
|
|
6941
|
+
if (result.success) {
|
|
6942
|
+
removed.push(wtPath);
|
|
6943
|
+
}
|
|
6944
|
+
}
|
|
6945
|
+
}
|
|
6946
|
+
} catch {
|
|
6947
|
+
}
|
|
6948
|
+
return removed;
|
|
6949
|
+
}
|
|
6950
|
+
var init_worktree_manager = __esm({
|
|
6951
|
+
"../core/dist/utils/worktree-manager.js"() {
|
|
6952
|
+
"use strict";
|
|
6953
|
+
init_git_utils();
|
|
6954
|
+
}
|
|
6955
|
+
});
|
|
6426
6956
|
function renderDependsOn(deps) {
|
|
6427
6957
|
if (deps.length === 0) {
|
|
6428
6958
|
return "";
|
|
@@ -6678,6 +7208,7 @@ __export(dist_exports, {
|
|
|
6678
7208
|
VALID_JOB_TYPES: () => VALID_JOB_TYPES,
|
|
6679
7209
|
VALID_MERGE_METHODS: () => VALID_MERGE_METHODS,
|
|
6680
7210
|
VALID_PROVIDERS: () => VALID_PROVIDERS,
|
|
7211
|
+
acquireLock: () => acquireLock,
|
|
6681
7212
|
addEntry: () => addEntry,
|
|
6682
7213
|
auditLockPath: () => auditLockPath,
|
|
6683
7214
|
buildDescription: () => buildDescription,
|
|
@@ -6692,6 +7223,9 @@ __export(dist_exports, {
|
|
|
6692
7223
|
checkNodeVersion: () => checkNodeVersion,
|
|
6693
7224
|
checkPrdDirectory: () => checkPrdDirectory,
|
|
6694
7225
|
checkProviderCli: () => checkProviderCli,
|
|
7226
|
+
checkRateLimited: () => checkRateLimited,
|
|
7227
|
+
claimPrd: () => claimPrd,
|
|
7228
|
+
cleanupWorktrees: () => cleanupWorktrees,
|
|
6695
7229
|
clearPrdState: () => clearPrdState,
|
|
6696
7230
|
clearTemplateCache: () => clearTemplateCache,
|
|
6697
7231
|
closeDb: () => closeDb,
|
|
@@ -6711,6 +7245,7 @@ __export(dist_exports, {
|
|
|
6711
7245
|
createSlicerPromptVars: () => createSlicerPromptVars,
|
|
6712
7246
|
createSpinner: () => createSpinner,
|
|
6713
7247
|
createTable: () => createTable,
|
|
7248
|
+
detectDefaultBranch: () => detectDefaultBranch,
|
|
6714
7249
|
detectProviders: () => detectProviders,
|
|
6715
7250
|
dim: () => dim,
|
|
6716
7251
|
error: () => error,
|
|
@@ -6726,6 +7261,8 @@ __export(dist_exports, {
|
|
|
6726
7261
|
fetchPrDetailsForBranch: () => fetchPrDetailsForBranch,
|
|
6727
7262
|
fetchReviewedPrDetails: () => fetchReviewedPrDetails,
|
|
6728
7263
|
fetchStatusSnapshot: () => fetchStatusSnapshot,
|
|
7264
|
+
findEligibleBoardIssue: () => findEligibleBoardIssue,
|
|
7265
|
+
findEligiblePrd: () => findEligiblePrd,
|
|
6729
7266
|
findMatchingIssue: () => findMatchingIssue,
|
|
6730
7267
|
formatDiscordPayload: () => formatDiscordPayload,
|
|
6731
7268
|
formatInstalledStatus: () => formatInstalledStatus,
|
|
@@ -6735,6 +7272,7 @@ __export(dist_exports, {
|
|
|
6735
7272
|
generateItemHash: () => generateItemHash,
|
|
6736
7273
|
generateMarker: () => generateMarker,
|
|
6737
7274
|
generatePersonaAvatar: () => generatePersonaAvatar,
|
|
7275
|
+
getBranchTipTimestamp: () => getBranchTipTimestamp,
|
|
6738
7276
|
getCrontabInfo: () => getCrontabInfo,
|
|
6739
7277
|
getDb: () => getDb,
|
|
6740
7278
|
getDbPath: () => getDbPath,
|
|
@@ -6768,6 +7306,7 @@ __export(dist_exports, {
|
|
|
6768
7306
|
header: () => header,
|
|
6769
7307
|
info: () => info,
|
|
6770
7308
|
initContainer: () => initContainer,
|
|
7309
|
+
isClaimed: () => isClaimed,
|
|
6771
7310
|
isContainerInitialized: () => isContainerInitialized,
|
|
6772
7311
|
isInCooldown: () => isInCooldown,
|
|
6773
7312
|
isItemProcessed: () => isItemProcessed,
|
|
@@ -6784,25 +7323,33 @@ __export(dist_exports, {
|
|
|
6784
7323
|
loadRoadmapState: () => loadRoadmapState,
|
|
6785
7324
|
loadSlicerTemplate: () => loadSlicerTemplate,
|
|
6786
7325
|
markItemProcessed: () => markItemProcessed,
|
|
7326
|
+
markPrdDone: () => markPrdDone,
|
|
6787
7327
|
migrateJsonToSqlite: () => migrateJsonToSqlite,
|
|
6788
7328
|
parsePrdDependencies: () => parsePrdDependencies,
|
|
6789
7329
|
parseRoadmap: () => parseRoadmap,
|
|
6790
7330
|
parseScriptResult: () => parseScriptResult,
|
|
6791
7331
|
performCancel: () => performCancel,
|
|
6792
7332
|
plannerLockPath: () => plannerLockPath,
|
|
7333
|
+
prepareBranchWorktree: () => prepareBranchWorktree,
|
|
7334
|
+
prepareDetachedWorktree: () => prepareDetachedWorktree,
|
|
6793
7335
|
projectRuntimeKey: () => projectRuntimeKey,
|
|
6794
7336
|
qaLockPath: () => qaLockPath,
|
|
7337
|
+
readClaimInfo: () => readClaimInfo,
|
|
6795
7338
|
readCrontab: () => readCrontab,
|
|
6796
7339
|
readPrdStates: () => readPrdStates,
|
|
6797
7340
|
recordExecution: () => recordExecution,
|
|
6798
7341
|
registerProject: () => registerProject,
|
|
7342
|
+
releaseClaim: () => releaseClaim,
|
|
7343
|
+
releaseLock: () => releaseLock,
|
|
6799
7344
|
removeEntries: () => removeEntries,
|
|
6800
7345
|
removeEntriesForProject: () => removeEntriesForProject,
|
|
6801
7346
|
renderPrdTemplate: () => renderPrdTemplate,
|
|
6802
7347
|
renderSlicerPrompt: () => renderSlicerPrompt,
|
|
6803
7348
|
resetRepositories: () => resetRepositories,
|
|
6804
7349
|
resolveJobProvider: () => resolveJobProvider,
|
|
7350
|
+
resolveWorktreeBaseRef: () => resolveWorktreeBaseRef,
|
|
6805
7351
|
reviewerLockPath: () => reviewerLockPath,
|
|
7352
|
+
rotateLog: () => rotateLog,
|
|
6806
7353
|
runAllChecks: () => runAllChecks,
|
|
6807
7354
|
runMigrations: () => runMigrations,
|
|
6808
7355
|
saveConfig: () => saveConfig,
|
|
@@ -6817,6 +7364,7 @@ __export(dist_exports, {
|
|
|
6817
7364
|
sliceRoadmapItem: () => sliceRoadmapItem,
|
|
6818
7365
|
slugify: () => slugify,
|
|
6819
7366
|
sortByPriority: () => sortByPriority,
|
|
7367
|
+
sortPrdsByPriority: () => sortPrdsByPriority,
|
|
6820
7368
|
step: () => step,
|
|
6821
7369
|
success: () => success,
|
|
6822
7370
|
unmarkItemProcessed: () => unmarkItemProcessed,
|
|
@@ -6850,11 +7398,15 @@ var init_dist = __esm({
|
|
|
6850
7398
|
init_logger();
|
|
6851
7399
|
init_cancel();
|
|
6852
7400
|
init_checks();
|
|
7401
|
+
init_claim_manager();
|
|
6853
7402
|
init_config_writer();
|
|
6854
7403
|
init_crontab();
|
|
6855
7404
|
init_execution_history();
|
|
7405
|
+
init_git_utils();
|
|
6856
7406
|
init_github();
|
|
7407
|
+
init_log_utils();
|
|
6857
7408
|
init_notify();
|
|
7409
|
+
init_prd_discovery();
|
|
6858
7410
|
init_prd_states();
|
|
6859
7411
|
init_prd_utils();
|
|
6860
7412
|
init_registry();
|
|
@@ -6867,6 +7419,7 @@ var init_dist = __esm({
|
|
|
6867
7419
|
init_status_data();
|
|
6868
7420
|
init_ui();
|
|
6869
7421
|
init_webhook_validator();
|
|
7422
|
+
init_worktree_manager();
|
|
6870
7423
|
init_prd_template();
|
|
6871
7424
|
init_slicer_prompt();
|
|
6872
7425
|
}
|
|
@@ -6877,22 +7430,22 @@ var __dirname2 = dirname4(__filename);
|
|
|
6877
7430
|
function findTemplatesDir(startDir) {
|
|
6878
7431
|
let d = startDir;
|
|
6879
7432
|
for (let i = 0; i < 8; i++) {
|
|
6880
|
-
const candidate =
|
|
6881
|
-
if (
|
|
7433
|
+
const candidate = join16(d, "templates");
|
|
7434
|
+
if (fs18.existsSync(candidate) && fs18.statSync(candidate).isDirectory()) {
|
|
6882
7435
|
return candidate;
|
|
6883
7436
|
}
|
|
6884
7437
|
d = dirname4(d);
|
|
6885
7438
|
}
|
|
6886
|
-
return
|
|
7439
|
+
return join16(startDir, "templates");
|
|
6887
7440
|
}
|
|
6888
7441
|
var TEMPLATES_DIR = findTemplatesDir(__dirname2);
|
|
6889
7442
|
function hasPlaywrightDependency(cwd) {
|
|
6890
|
-
const packageJsonPath =
|
|
6891
|
-
if (!
|
|
7443
|
+
const packageJsonPath = path17.join(cwd, "package.json");
|
|
7444
|
+
if (!fs18.existsSync(packageJsonPath)) {
|
|
6892
7445
|
return false;
|
|
6893
7446
|
}
|
|
6894
7447
|
try {
|
|
6895
|
-
const packageJson2 = JSON.parse(
|
|
7448
|
+
const packageJson2 = JSON.parse(fs18.readFileSync(packageJsonPath, "utf-8"));
|
|
6896
7449
|
return Boolean(packageJson2.dependencies?.["@playwright/test"] || packageJson2.dependencies?.playwright || packageJson2.devDependencies?.["@playwright/test"] || packageJson2.devDependencies?.playwright);
|
|
6897
7450
|
} catch {
|
|
6898
7451
|
return false;
|
|
@@ -6902,7 +7455,7 @@ function detectPlaywright(cwd) {
|
|
|
6902
7455
|
if (hasPlaywrightDependency(cwd)) {
|
|
6903
7456
|
return true;
|
|
6904
7457
|
}
|
|
6905
|
-
if (
|
|
7458
|
+
if (fs18.existsSync(path17.join(cwd, "node_modules", ".bin", "playwright"))) {
|
|
6906
7459
|
return true;
|
|
6907
7460
|
}
|
|
6908
7461
|
try {
|
|
@@ -6918,10 +7471,10 @@ function detectPlaywright(cwd) {
|
|
|
6918
7471
|
}
|
|
6919
7472
|
}
|
|
6920
7473
|
function resolvePlaywrightInstallCommand(cwd) {
|
|
6921
|
-
if (
|
|
7474
|
+
if (fs18.existsSync(path17.join(cwd, "pnpm-lock.yaml"))) {
|
|
6922
7475
|
return "pnpm add -D @playwright/test";
|
|
6923
7476
|
}
|
|
6924
|
-
if (
|
|
7477
|
+
if (fs18.existsSync(path17.join(cwd, "yarn.lock"))) {
|
|
6925
7478
|
return "yarn add -D @playwright/test";
|
|
6926
7479
|
}
|
|
6927
7480
|
return "npm install -D @playwright/test";
|
|
@@ -7040,36 +7593,36 @@ function promptProviderSelection(providers) {
|
|
|
7040
7593
|
});
|
|
7041
7594
|
}
|
|
7042
7595
|
function ensureDir(dirPath) {
|
|
7043
|
-
if (!
|
|
7044
|
-
|
|
7596
|
+
if (!fs18.existsSync(dirPath)) {
|
|
7597
|
+
fs18.mkdirSync(dirPath, { recursive: true });
|
|
7045
7598
|
}
|
|
7046
7599
|
}
|
|
7047
7600
|
function resolveTemplatePath(templateName, customTemplatesDir, bundledTemplatesDir) {
|
|
7048
7601
|
if (customTemplatesDir !== null) {
|
|
7049
|
-
const customPath =
|
|
7050
|
-
if (
|
|
7602
|
+
const customPath = join16(customTemplatesDir, templateName);
|
|
7603
|
+
if (fs18.existsSync(customPath)) {
|
|
7051
7604
|
return { path: customPath, source: "custom" };
|
|
7052
7605
|
}
|
|
7053
7606
|
}
|
|
7054
|
-
return { path:
|
|
7607
|
+
return { path: join16(bundledTemplatesDir, templateName), source: "bundled" };
|
|
7055
7608
|
}
|
|
7056
7609
|
function processTemplate(templateName, targetPath, replacements, force, sourcePath, source) {
|
|
7057
|
-
if (
|
|
7610
|
+
if (fs18.existsSync(targetPath) && !force) {
|
|
7058
7611
|
console.log(` Skipped (exists): ${targetPath}`);
|
|
7059
7612
|
return { created: false, source: source ?? "bundled" };
|
|
7060
7613
|
}
|
|
7061
|
-
const templatePath = sourcePath ??
|
|
7614
|
+
const templatePath = sourcePath ?? join16(TEMPLATES_DIR, templateName);
|
|
7062
7615
|
const resolvedSource = source ?? "bundled";
|
|
7063
|
-
let content =
|
|
7616
|
+
let content = fs18.readFileSync(templatePath, "utf-8");
|
|
7064
7617
|
for (const [key, value] of Object.entries(replacements)) {
|
|
7065
7618
|
content = content.replaceAll(key, value);
|
|
7066
7619
|
}
|
|
7067
|
-
|
|
7620
|
+
fs18.writeFileSync(targetPath, content);
|
|
7068
7621
|
console.log(` Created: ${targetPath} (${resolvedSource})`);
|
|
7069
7622
|
return { created: true, source: resolvedSource };
|
|
7070
7623
|
}
|
|
7071
7624
|
function addToGitignore(cwd) {
|
|
7072
|
-
const gitignorePath =
|
|
7625
|
+
const gitignorePath = path17.join(cwd, ".gitignore");
|
|
7073
7626
|
const entries = [
|
|
7074
7627
|
{
|
|
7075
7628
|
pattern: "/logs/",
|
|
@@ -7083,13 +7636,13 @@ function addToGitignore(cwd) {
|
|
|
7083
7636
|
},
|
|
7084
7637
|
{ pattern: "*.claim", label: "*.claim", check: (c) => c.includes("*.claim") }
|
|
7085
7638
|
];
|
|
7086
|
-
if (!
|
|
7639
|
+
if (!fs18.existsSync(gitignorePath)) {
|
|
7087
7640
|
const lines = ["# Night Watch", ...entries.map((e) => e.pattern), ""];
|
|
7088
|
-
|
|
7641
|
+
fs18.writeFileSync(gitignorePath, lines.join("\n"));
|
|
7089
7642
|
console.log(` Created: ${gitignorePath} (with Night Watch entries)`);
|
|
7090
7643
|
return;
|
|
7091
7644
|
}
|
|
7092
|
-
const content =
|
|
7645
|
+
const content = fs18.readFileSync(gitignorePath, "utf-8");
|
|
7093
7646
|
const missing = entries.filter((e) => !e.check(content));
|
|
7094
7647
|
if (missing.length === 0) {
|
|
7095
7648
|
console.log(` Skipped (exists): Night Watch entries in .gitignore`);
|
|
@@ -7097,30 +7650,15 @@ function addToGitignore(cwd) {
|
|
|
7097
7650
|
}
|
|
7098
7651
|
const additions = missing.map((e) => e.pattern).join("\n");
|
|
7099
7652
|
const newContent = content.trimEnd() + "\n\n# Night Watch\n" + additions + "\n";
|
|
7100
|
-
|
|
7653
|
+
fs18.writeFileSync(gitignorePath, newContent);
|
|
7101
7654
|
console.log(` Updated: ${gitignorePath} (added ${missing.map((e) => e.label).join(", ")})`);
|
|
7102
7655
|
}
|
|
7103
|
-
function createSummaryFile(summaryPath, force) {
|
|
7104
|
-
if (fs14.existsSync(summaryPath) && !force) {
|
|
7105
|
-
console.log(` Skipped (exists): ${summaryPath}`);
|
|
7106
|
-
return;
|
|
7107
|
-
}
|
|
7108
|
-
const content = `# Night Watch Summary
|
|
7109
|
-
|
|
7110
|
-
This file tracks the progress of PRDs executed by Night Watch.
|
|
7111
|
-
|
|
7112
|
-
---
|
|
7113
|
-
|
|
7114
|
-
`;
|
|
7115
|
-
fs14.writeFileSync(summaryPath, content);
|
|
7116
|
-
console.log(` Created: ${summaryPath}`);
|
|
7117
|
-
}
|
|
7118
7656
|
function initCommand(program2) {
|
|
7119
7657
|
program2.command("init").description("Initialize night-watch in the current project").option("-f, --force", "Overwrite existing configuration").option("-d, --prd-dir <path>", "Path to PRD directory").option("-p, --provider <name>", "AI provider to use (claude or codex)").option("--no-reviewer", "Disable reviewer cron job").action(async (options) => {
|
|
7120
7658
|
const cwd = process.cwd();
|
|
7121
7659
|
const force = options.force || false;
|
|
7122
7660
|
const prdDir = options.prdDir || DEFAULT_PRD_DIR;
|
|
7123
|
-
const totalSteps =
|
|
7661
|
+
const totalSteps = 11;
|
|
7124
7662
|
console.log();
|
|
7125
7663
|
header("Night Watch CLI - Initializing");
|
|
7126
7664
|
step(1, totalSteps, "Checking git repository...");
|
|
@@ -7201,57 +7739,54 @@ function initCommand(program2) {
|
|
|
7201
7739
|
"${DEFAULT_BRANCH}": defaultBranch
|
|
7202
7740
|
};
|
|
7203
7741
|
step(5, totalSteps, "Creating PRD directory structure...");
|
|
7204
|
-
const prdDirPath =
|
|
7205
|
-
const doneDirPath =
|
|
7742
|
+
const prdDirPath = path17.join(cwd, prdDir);
|
|
7743
|
+
const doneDirPath = path17.join(prdDirPath, "done");
|
|
7206
7744
|
ensureDir(doneDirPath);
|
|
7207
7745
|
success(`Created ${prdDirPath}/`);
|
|
7208
7746
|
success(`Created ${doneDirPath}/`);
|
|
7209
|
-
step(6, totalSteps, "Creating
|
|
7210
|
-
const
|
|
7211
|
-
createSummaryFile(summaryPath, force);
|
|
7212
|
-
step(7, totalSteps, "Creating logs directory...");
|
|
7213
|
-
const logsPath = path13.join(cwd, LOG_DIR);
|
|
7747
|
+
step(6, totalSteps, "Creating logs directory...");
|
|
7748
|
+
const logsPath = path17.join(cwd, LOG_DIR);
|
|
7214
7749
|
ensureDir(logsPath);
|
|
7215
7750
|
success(`Created ${logsPath}/`);
|
|
7216
7751
|
addToGitignore(cwd);
|
|
7217
|
-
step(
|
|
7218
|
-
const
|
|
7219
|
-
ensureDir(
|
|
7220
|
-
success(`Created ${
|
|
7752
|
+
step(7, totalSteps, "Creating instructions directory...");
|
|
7753
|
+
const instructionsDir = path17.join(cwd, "instructions");
|
|
7754
|
+
ensureDir(instructionsDir);
|
|
7755
|
+
success(`Created ${instructionsDir}/`);
|
|
7221
7756
|
const existingConfig = loadConfig(cwd);
|
|
7222
|
-
const customTemplatesDirPath =
|
|
7223
|
-
const customTemplatesDir =
|
|
7757
|
+
const customTemplatesDirPath = path17.join(cwd, existingConfig.templatesDir);
|
|
7758
|
+
const customTemplatesDir = fs18.existsSync(customTemplatesDirPath) ? customTemplatesDirPath : null;
|
|
7224
7759
|
const templateSources = [];
|
|
7225
7760
|
const nwResolution = resolveTemplatePath("night-watch.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7226
|
-
const nwResult = processTemplate("night-watch.md",
|
|
7761
|
+
const nwResult = processTemplate("night-watch.md", path17.join(instructionsDir, "night-watch.md"), replacements, force, nwResolution.path, nwResolution.source);
|
|
7227
7762
|
templateSources.push({ name: "night-watch.md", source: nwResult.source });
|
|
7228
7763
|
const peResolution = resolveTemplatePath("prd-executor.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7229
|
-
const peResult = processTemplate("prd-executor.md",
|
|
7764
|
+
const peResult = processTemplate("prd-executor.md", path17.join(instructionsDir, "prd-executor.md"), replacements, force, peResolution.path, peResolution.source);
|
|
7230
7765
|
templateSources.push({ name: "prd-executor.md", source: peResult.source });
|
|
7231
7766
|
const prResolution = resolveTemplatePath("night-watch-pr-reviewer.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7232
|
-
const prResult = processTemplate("night-watch-pr-reviewer.md",
|
|
7767
|
+
const prResult = processTemplate("night-watch-pr-reviewer.md", path17.join(instructionsDir, "night-watch-pr-reviewer.md"), replacements, force, prResolution.path, prResolution.source);
|
|
7233
7768
|
templateSources.push({ name: "night-watch-pr-reviewer.md", source: prResult.source });
|
|
7234
7769
|
const qaResolution = resolveTemplatePath("night-watch-qa.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7235
|
-
const qaResult = processTemplate("night-watch-qa.md",
|
|
7770
|
+
const qaResult = processTemplate("night-watch-qa.md", path17.join(instructionsDir, "night-watch-qa.md"), replacements, force, qaResolution.path, qaResolution.source);
|
|
7236
7771
|
templateSources.push({ name: "night-watch-qa.md", source: qaResult.source });
|
|
7237
7772
|
const auditResolution = resolveTemplatePath("night-watch-audit.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7238
|
-
const auditResult = processTemplate("night-watch-audit.md",
|
|
7773
|
+
const auditResult = processTemplate("night-watch-audit.md", path17.join(instructionsDir, "night-watch-audit.md"), replacements, force, auditResolution.path, auditResolution.source);
|
|
7239
7774
|
templateSources.push({ name: "night-watch-audit.md", source: auditResult.source });
|
|
7240
|
-
step(
|
|
7241
|
-
const configPath =
|
|
7242
|
-
if (
|
|
7775
|
+
step(8, totalSteps, "Creating configuration file...");
|
|
7776
|
+
const configPath = path17.join(cwd, CONFIG_FILE_NAME);
|
|
7777
|
+
if (fs18.existsSync(configPath) && !force) {
|
|
7243
7778
|
console.log(` Skipped (exists): ${configPath}`);
|
|
7244
7779
|
} else {
|
|
7245
|
-
let configContent =
|
|
7780
|
+
let configContent = fs18.readFileSync(join16(TEMPLATES_DIR, "night-watch.config.json"), "utf-8");
|
|
7246
7781
|
configContent = configContent.replace('"projectName": ""', `"projectName": "${projectName}"`);
|
|
7247
7782
|
configContent = configContent.replace('"defaultBranch": ""', `"defaultBranch": "${defaultBranch}"`);
|
|
7248
7783
|
configContent = configContent.replace(/"provider":\s*"[^"]*"/, `"provider": "${selectedProvider}"`);
|
|
7249
7784
|
configContent = configContent.replace(/"reviewerEnabled":\s*(true|false)/, `"reviewerEnabled": ${reviewerEnabled}`);
|
|
7250
|
-
|
|
7785
|
+
fs18.writeFileSync(configPath, configContent);
|
|
7251
7786
|
success(`Created ${configPath}`);
|
|
7252
7787
|
}
|
|
7253
|
-
step(
|
|
7254
|
-
const existingRaw = JSON.parse(
|
|
7788
|
+
step(9, totalSteps, "Setting up GitHub Project board...");
|
|
7789
|
+
const existingRaw = JSON.parse(fs18.readFileSync(configPath, "utf-8"));
|
|
7255
7790
|
const existingBoard = existingRaw.boardProvider;
|
|
7256
7791
|
if (existingBoard?.projectNumber && !force) {
|
|
7257
7792
|
info(`Board already configured (#${existingBoard.projectNumber}), skipping.`);
|
|
@@ -7273,13 +7808,13 @@ function initCommand(program2) {
|
|
|
7273
7808
|
const provider = createBoardProvider({ enabled: true, provider: "github" }, cwd);
|
|
7274
7809
|
const boardTitle = `${projectName} Night Watch`;
|
|
7275
7810
|
const board = await provider.setupBoard(boardTitle);
|
|
7276
|
-
const rawConfig = JSON.parse(
|
|
7811
|
+
const rawConfig = JSON.parse(fs18.readFileSync(configPath, "utf-8"));
|
|
7277
7812
|
rawConfig.boardProvider = {
|
|
7278
7813
|
enabled: true,
|
|
7279
7814
|
provider: "github",
|
|
7280
7815
|
projectNumber: board.number
|
|
7281
7816
|
};
|
|
7282
|
-
|
|
7817
|
+
fs18.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n");
|
|
7283
7818
|
success(`GitHub Project board "${boardTitle}" ready (#${board.number})`);
|
|
7284
7819
|
} catch (boardErr) {
|
|
7285
7820
|
console.warn(` Warning: Could not set up GitHub Project board: ${boardErr instanceof Error ? boardErr.message : String(boardErr)}`);
|
|
@@ -7287,7 +7822,7 @@ function initCommand(program2) {
|
|
|
7287
7822
|
}
|
|
7288
7823
|
}
|
|
7289
7824
|
}
|
|
7290
|
-
step(
|
|
7825
|
+
step(10, totalSteps, "Registering project in global registry...");
|
|
7291
7826
|
try {
|
|
7292
7827
|
const { registerProject: registerProject2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
7293
7828
|
const entry = registerProject2(cwd);
|
|
@@ -7295,23 +7830,22 @@ function initCommand(program2) {
|
|
|
7295
7830
|
} catch (regErr) {
|
|
7296
7831
|
console.warn(` Warning: Could not register in global registry: ${regErr instanceof Error ? regErr.message : String(regErr)}`);
|
|
7297
7832
|
}
|
|
7298
|
-
step(
|
|
7833
|
+
step(11, totalSteps, "Initialization complete!");
|
|
7299
7834
|
header("Initialization Complete");
|
|
7300
7835
|
const filesTable = createTable({ head: ["Created Files", ""] });
|
|
7301
7836
|
filesTable.push(["PRD Directory", `${prdDir}/done/`]);
|
|
7302
|
-
filesTable.push(["Summary File", `${prdDir}/NIGHT-WATCH-SUMMARY.md`]);
|
|
7303
7837
|
filesTable.push(["Logs Directory", `${LOG_DIR}/`]);
|
|
7304
7838
|
filesTable.push([
|
|
7305
|
-
"
|
|
7306
|
-
|
|
7839
|
+
"Instructions",
|
|
7840
|
+
`instructions/night-watch.md (${templateSources[0].source})`
|
|
7307
7841
|
]);
|
|
7308
|
-
filesTable.push(["",
|
|
7842
|
+
filesTable.push(["", `instructions/prd-executor.md (${templateSources[1].source})`]);
|
|
7309
7843
|
filesTable.push([
|
|
7310
7844
|
"",
|
|
7311
|
-
|
|
7845
|
+
`instructions/night-watch-pr-reviewer.md (${templateSources[2].source})`
|
|
7312
7846
|
]);
|
|
7313
|
-
filesTable.push(["",
|
|
7314
|
-
filesTable.push(["",
|
|
7847
|
+
filesTable.push(["", `instructions/night-watch-qa.md (${templateSources[3].source})`]);
|
|
7848
|
+
filesTable.push(["", `instructions/night-watch-audit.md (${templateSources[4].source})`]);
|
|
7315
7849
|
filesTable.push(["Config File", CONFIG_FILE_NAME]);
|
|
7316
7850
|
filesTable.push(["Global Registry", "~/.night-watch/projects.json"]);
|
|
7317
7851
|
console.log(filesTable.toString());
|
|
@@ -7352,12 +7886,12 @@ function shouldAttemptCrossProjectFallback(options, scriptStatus) {
|
|
|
7352
7886
|
return scriptStatus === "skip_no_eligible_prd";
|
|
7353
7887
|
}
|
|
7354
7888
|
function getCrossProjectFallbackCandidates(currentProjectDir) {
|
|
7355
|
-
const current =
|
|
7889
|
+
const current = path18.resolve(currentProjectDir);
|
|
7356
7890
|
const { valid, invalid } = validateRegistry();
|
|
7357
7891
|
for (const entry of invalid) {
|
|
7358
7892
|
warn(`Skipping invalid registry entry: ${entry.path}`);
|
|
7359
7893
|
}
|
|
7360
|
-
return valid.filter((entry) =>
|
|
7894
|
+
return valid.filter((entry) => path18.resolve(entry.path) !== current);
|
|
7361
7895
|
}
|
|
7362
7896
|
async function sendRunCompletionNotifications(config, projectDir, options, exitCode, scriptResult) {
|
|
7363
7897
|
if (isRateLimitFallbackTriggered(scriptResult?.data)) {
|
|
@@ -7365,7 +7899,7 @@ async function sendRunCompletionNotifications(config, projectDir, options, exitC
|
|
|
7365
7899
|
if (nonTelegramWebhooks.length > 0) {
|
|
7366
7900
|
const _rateLimitCtx = {
|
|
7367
7901
|
event: "rate_limit_fallback",
|
|
7368
|
-
projectName:
|
|
7902
|
+
projectName: path18.basename(projectDir),
|
|
7369
7903
|
exitCode,
|
|
7370
7904
|
provider: config.provider
|
|
7371
7905
|
};
|
|
@@ -7387,11 +7921,15 @@ async function sendRunCompletionNotifications(config, projectDir, options, exitC
|
|
|
7387
7921
|
}
|
|
7388
7922
|
}
|
|
7389
7923
|
if (event) {
|
|
7924
|
+
const timeoutDuration = event === "run_timeout" ? config.maxRuntime : void 0;
|
|
7390
7925
|
const _ctx = {
|
|
7391
7926
|
event,
|
|
7392
|
-
projectName:
|
|
7927
|
+
projectName: path18.basename(projectDir),
|
|
7393
7928
|
exitCode,
|
|
7394
7929
|
provider: config.provider,
|
|
7930
|
+
prdName: scriptResult?.data.prd,
|
|
7931
|
+
branchName: scriptResult?.data.branch,
|
|
7932
|
+
duration: timeoutDuration,
|
|
7395
7933
|
prUrl: prDetails?.url,
|
|
7396
7934
|
prTitle: prDetails?.title,
|
|
7397
7935
|
prBody: prDetails?.body,
|
|
@@ -7499,20 +8037,20 @@ function applyCliOverrides(config, options) {
|
|
|
7499
8037
|
return overridden;
|
|
7500
8038
|
}
|
|
7501
8039
|
function scanPrdDirectory(projectDir, prdDir, maxRuntime) {
|
|
7502
|
-
const absolutePrdDir =
|
|
7503
|
-
const doneDir =
|
|
8040
|
+
const absolutePrdDir = path18.join(projectDir, prdDir);
|
|
8041
|
+
const doneDir = path18.join(absolutePrdDir, "done");
|
|
7504
8042
|
const pending = [];
|
|
7505
8043
|
const completed = [];
|
|
7506
|
-
if (
|
|
7507
|
-
const entries =
|
|
8044
|
+
if (fs19.existsSync(absolutePrdDir)) {
|
|
8045
|
+
const entries = fs19.readdirSync(absolutePrdDir, { withFileTypes: true });
|
|
7508
8046
|
for (const entry of entries) {
|
|
7509
|
-
if (entry.isFile() && entry.name.endsWith(".md")
|
|
7510
|
-
const claimPath =
|
|
8047
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
8048
|
+
const claimPath = path18.join(absolutePrdDir, entry.name + CLAIM_FILE_EXTENSION);
|
|
7511
8049
|
let claimed = false;
|
|
7512
8050
|
let claimInfo = null;
|
|
7513
|
-
if (
|
|
8051
|
+
if (fs19.existsSync(claimPath)) {
|
|
7514
8052
|
try {
|
|
7515
|
-
const content =
|
|
8053
|
+
const content = fs19.readFileSync(claimPath, "utf-8");
|
|
7516
8054
|
const data = JSON.parse(content);
|
|
7517
8055
|
const age = Math.floor(Date.now() / 1e3) - data.timestamp;
|
|
7518
8056
|
if (age < maxRuntime) {
|
|
@@ -7526,8 +8064,8 @@ function scanPrdDirectory(projectDir, prdDir, maxRuntime) {
|
|
|
7526
8064
|
}
|
|
7527
8065
|
}
|
|
7528
8066
|
}
|
|
7529
|
-
if (
|
|
7530
|
-
const entries =
|
|
8067
|
+
if (fs19.existsSync(doneDir)) {
|
|
8068
|
+
const entries = fs19.readdirSync(doneDir, { withFileTypes: true });
|
|
7531
8069
|
for (const entry of entries) {
|
|
7532
8070
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
7533
8071
|
completed.push(entry.name);
|
|
@@ -7736,13 +8274,42 @@ function applyCliOverrides2(config, options) {
|
|
|
7736
8274
|
}
|
|
7737
8275
|
return overridden;
|
|
7738
8276
|
}
|
|
8277
|
+
function isFailingCheck(check) {
|
|
8278
|
+
const bucket = (check.bucket ?? "").toLowerCase();
|
|
8279
|
+
const state = (check.state ?? "").toLowerCase();
|
|
8280
|
+
const conclusion = (check.conclusion ?? "").toLowerCase();
|
|
8281
|
+
return bucket === "fail" || bucket === "cancel" || state === "failure" || state === "error" || state === "cancelled" || conclusion === "failure" || conclusion === "error" || conclusion === "cancelled" || conclusion === "timed_out" || conclusion === "action_required" || conclusion === "startup_failure" || conclusion === "stale";
|
|
8282
|
+
}
|
|
8283
|
+
function getPrFailingChecks(prNumber) {
|
|
8284
|
+
try {
|
|
8285
|
+
const result = execFileSync5("gh", ["pr", "checks", String(prNumber), "--json", "name,bucket,state,conclusion"], {
|
|
8286
|
+
encoding: "utf-8",
|
|
8287
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8288
|
+
});
|
|
8289
|
+
const checks = JSON.parse(result.trim() || "[]");
|
|
8290
|
+
const failing = checks.filter((check) => isFailingCheck(check)).map((check) => `${check.name ?? "unknown"} [state=${check.state ?? "unknown"}, conclusion=${check.conclusion ?? "unknown"}]`);
|
|
8291
|
+
if (failing.length > 0) {
|
|
8292
|
+
return failing;
|
|
8293
|
+
}
|
|
8294
|
+
} catch {
|
|
8295
|
+
}
|
|
8296
|
+
try {
|
|
8297
|
+
const result = execFileSync5("gh", ["pr", "checks", String(prNumber)], {
|
|
8298
|
+
encoding: "utf-8",
|
|
8299
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
8300
|
+
});
|
|
8301
|
+
return result.split("\n").map((line) => line.trim()).filter((line) => line.length > 0).filter((line) => /fail|error|cancel|timed[_ -]?out|action_required|startup_failure|stale/i.test(line));
|
|
8302
|
+
} catch {
|
|
8303
|
+
return [];
|
|
8304
|
+
}
|
|
8305
|
+
}
|
|
7739
8306
|
function getOpenPrsNeedingWork(branchPatterns) {
|
|
7740
8307
|
try {
|
|
7741
8308
|
const args = ["pr", "list", "--state", "open", "--json", "number,title,headRefName"];
|
|
7742
8309
|
for (const pattern of branchPatterns) {
|
|
7743
8310
|
args.push("--head", pattern);
|
|
7744
8311
|
}
|
|
7745
|
-
const result =
|
|
8312
|
+
const result = execFileSync5("gh", args, {
|
|
7746
8313
|
encoding: "utf-8",
|
|
7747
8314
|
stdio: ["pipe", "pipe", "pipe"]
|
|
7748
8315
|
});
|
|
@@ -7810,6 +8377,21 @@ function reviewCommand(program2) {
|
|
|
7810
8377
|
console.log();
|
|
7811
8378
|
process.exit(0);
|
|
7812
8379
|
}
|
|
8380
|
+
const preflightOpenPrs = getOpenPrsNeedingWork(config.branchPatterns);
|
|
8381
|
+
const preflightFailures = preflightOpenPrs.map((pr) => ({
|
|
8382
|
+
prNumber: pr.number,
|
|
8383
|
+
title: pr.title,
|
|
8384
|
+
failingChecks: getPrFailingChecks(pr.number)
|
|
8385
|
+
})).filter((entry) => entry.failingChecks.length > 0);
|
|
8386
|
+
if (preflightFailures.length > 0) {
|
|
8387
|
+
header("Preflight Failing Checks");
|
|
8388
|
+
for (const entry of preflightFailures) {
|
|
8389
|
+
info(`#${entry.prNumber}: ${entry.title}`);
|
|
8390
|
+
for (const check of entry.failingChecks) {
|
|
8391
|
+
dim(` ${check}`);
|
|
8392
|
+
}
|
|
8393
|
+
}
|
|
8394
|
+
}
|
|
7813
8395
|
const spinner = createSpinner("Running PR reviewer...");
|
|
7814
8396
|
spinner.start();
|
|
7815
8397
|
try {
|
|
@@ -7849,7 +8431,7 @@ ${stderr}`);
|
|
|
7849
8431
|
const finalScore = parseFinalReviewScore(scriptResult?.data.final_score);
|
|
7850
8432
|
const _reviewCtx = {
|
|
7851
8433
|
event: "review_completed",
|
|
7852
|
-
projectName:
|
|
8434
|
+
projectName: path19.basename(projectDir),
|
|
7853
8435
|
exitCode,
|
|
7854
8436
|
provider: config.provider,
|
|
7855
8437
|
prUrl: prDetails?.url,
|
|
@@ -7870,7 +8452,7 @@ ${stderr}`);
|
|
|
7870
8452
|
const autoMergedPrDetails = fetchPrDetailsByNumber(autoMergedPrNumber, projectDir);
|
|
7871
8453
|
const _mergeCtx = {
|
|
7872
8454
|
event: "pr_auto_merged",
|
|
7873
|
-
projectName:
|
|
8455
|
+
projectName: path19.basename(projectDir),
|
|
7874
8456
|
exitCode,
|
|
7875
8457
|
provider: config.provider,
|
|
7876
8458
|
prNumber: autoMergedPrDetails?.number ?? autoMergedPrNumber,
|
|
@@ -8026,7 +8608,7 @@ ${stderr}`);
|
|
|
8026
8608
|
const fallbackPrUrl = !prDetails?.url && primaryQaPr && repo ? `https://github.com/${repo}/pull/${primaryQaPr}` : void 0;
|
|
8027
8609
|
const _qaCtx = {
|
|
8028
8610
|
event: "qa_completed",
|
|
8029
|
-
projectName:
|
|
8611
|
+
projectName: path20.basename(projectDir),
|
|
8030
8612
|
exitCode,
|
|
8031
8613
|
provider: config.provider,
|
|
8032
8614
|
prNumber: prDetails?.number ?? primaryQaPr,
|
|
@@ -8105,7 +8687,7 @@ function auditCommand(program2) {
|
|
|
8105
8687
|
configTable.push(["Provider", auditProvider]);
|
|
8106
8688
|
configTable.push(["Provider CLI", PROVIDER_COMMANDS[auditProvider]]);
|
|
8107
8689
|
configTable.push(["Max Runtime", `${config.audit.maxRuntime}s`]);
|
|
8108
|
-
configTable.push(["Report File",
|
|
8690
|
+
configTable.push(["Report File", path21.join(projectDir, "logs", "audit-report.md")]);
|
|
8109
8691
|
console.log(configTable.toString());
|
|
8110
8692
|
header("Provider Invocation");
|
|
8111
8693
|
const providerCmd = PROVIDER_COMMANDS[auditProvider];
|
|
@@ -8131,8 +8713,8 @@ ${stderr}`);
|
|
|
8131
8713
|
} else if (scriptResult?.status?.startsWith("skip_")) {
|
|
8132
8714
|
spinner.succeed("Code audit skipped");
|
|
8133
8715
|
} else {
|
|
8134
|
-
const reportPath =
|
|
8135
|
-
if (!
|
|
8716
|
+
const reportPath = path21.join(projectDir, "logs", "audit-report.md");
|
|
8717
|
+
if (!fs20.existsSync(reportPath)) {
|
|
8136
8718
|
spinner.fail("Code audit finished without a report file");
|
|
8137
8719
|
process.exit(1);
|
|
8138
8720
|
}
|
|
@@ -8143,9 +8725,9 @@ ${stderr}`);
|
|
|
8143
8725
|
const providerExit = scriptResult?.data?.provider_exit;
|
|
8144
8726
|
const exitDetail = providerExit && providerExit !== String(exitCode) ? `, provider exit ${providerExit}` : "";
|
|
8145
8727
|
spinner.fail(`Code audit exited with code ${exitCode}${statusSuffix}${exitDetail}`);
|
|
8146
|
-
const logPath =
|
|
8147
|
-
if (
|
|
8148
|
-
const logLines =
|
|
8728
|
+
const logPath = path21.join(projectDir, "logs", "audit.log");
|
|
8729
|
+
if (fs20.existsSync(logPath)) {
|
|
8730
|
+
const logLines = fs20.readFileSync(logPath, "utf-8").split("\n").filter((l) => l.trim()).slice(-8);
|
|
8149
8731
|
if (logLines.length > 0) {
|
|
8150
8732
|
process.stderr.write(logLines.join("\n") + "\n");
|
|
8151
8733
|
}
|
|
@@ -8165,8 +8747,8 @@ function shellQuote(value) {
|
|
|
8165
8747
|
function getNightWatchBinPath() {
|
|
8166
8748
|
try {
|
|
8167
8749
|
const npmBin = execSync4("npm bin -g", { encoding: "utf-8" }).trim();
|
|
8168
|
-
const binPath =
|
|
8169
|
-
if (
|
|
8750
|
+
const binPath = path22.join(npmBin, "night-watch");
|
|
8751
|
+
if (fs21.existsSync(binPath)) {
|
|
8170
8752
|
return binPath;
|
|
8171
8753
|
}
|
|
8172
8754
|
} catch {
|
|
@@ -8180,13 +8762,13 @@ function getNightWatchBinPath() {
|
|
|
8180
8762
|
function getNodeBinDir() {
|
|
8181
8763
|
try {
|
|
8182
8764
|
const nodePath = execSync4("which node", { encoding: "utf-8" }).trim();
|
|
8183
|
-
return
|
|
8765
|
+
return path22.dirname(nodePath);
|
|
8184
8766
|
} catch {
|
|
8185
8767
|
return "";
|
|
8186
8768
|
}
|
|
8187
8769
|
}
|
|
8188
8770
|
function buildCronPathPrefix(nodeBinDir, nightWatchBin) {
|
|
8189
|
-
const nightWatchBinDir = nightWatchBin.includes("/") || nightWatchBin.includes("\\") ?
|
|
8771
|
+
const nightWatchBinDir = nightWatchBin.includes("/") || nightWatchBin.includes("\\") ? path22.dirname(nightWatchBin) : "";
|
|
8190
8772
|
const pathParts = Array.from(new Set([nodeBinDir, nightWatchBinDir].filter((part) => part.length > 0)));
|
|
8191
8773
|
if (pathParts.length === 0) {
|
|
8192
8774
|
return "";
|
|
@@ -8213,12 +8795,12 @@ function performInstall(projectDir, config, options) {
|
|
|
8213
8795
|
const nightWatchBin = getNightWatchBinPath();
|
|
8214
8796
|
const projectName = getProjectName(projectDir);
|
|
8215
8797
|
const marker = generateMarker(projectName);
|
|
8216
|
-
const logDir =
|
|
8217
|
-
if (!
|
|
8218
|
-
|
|
8798
|
+
const logDir = path22.join(projectDir, LOG_DIR);
|
|
8799
|
+
if (!fs21.existsSync(logDir)) {
|
|
8800
|
+
fs21.mkdirSync(logDir, { recursive: true });
|
|
8219
8801
|
}
|
|
8220
|
-
const executorLog =
|
|
8221
|
-
const reviewerLog =
|
|
8802
|
+
const executorLog = path22.join(logDir, "executor.log");
|
|
8803
|
+
const reviewerLog = path22.join(logDir, "reviewer.log");
|
|
8222
8804
|
if (!options?.force) {
|
|
8223
8805
|
const existingEntries = Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
|
|
8224
8806
|
if (existingEntries.length > 0) {
|
|
@@ -8251,7 +8833,7 @@ function performInstall(projectDir, config, options) {
|
|
|
8251
8833
|
const installSlicer = options?.noSlicer === true ? false : config.roadmapScanner.enabled;
|
|
8252
8834
|
if (installSlicer) {
|
|
8253
8835
|
const slicerSchedule = applyScheduleOffset(config.roadmapScanner.slicerSchedule, offset);
|
|
8254
|
-
const slicerLog =
|
|
8836
|
+
const slicerLog = path22.join(logDir, "slicer.log");
|
|
8255
8837
|
const slicerEntry = `${slicerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} planner >> ${shellQuote(slicerLog)} 2>&1 ${marker}`;
|
|
8256
8838
|
entries.push(slicerEntry);
|
|
8257
8839
|
}
|
|
@@ -8259,7 +8841,7 @@ function performInstall(projectDir, config, options) {
|
|
|
8259
8841
|
const installQa = disableQa ? false : config.qa.enabled;
|
|
8260
8842
|
if (installQa) {
|
|
8261
8843
|
const qaSchedule = applyScheduleOffset(config.qa.schedule, offset);
|
|
8262
|
-
const qaLog =
|
|
8844
|
+
const qaLog = path22.join(logDir, "qa.log");
|
|
8263
8845
|
const qaEntry = `${qaSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} qa >> ${shellQuote(qaLog)} 2>&1 ${marker}`;
|
|
8264
8846
|
entries.push(qaEntry);
|
|
8265
8847
|
}
|
|
@@ -8267,7 +8849,7 @@ function performInstall(projectDir, config, options) {
|
|
|
8267
8849
|
const installAudit = disableAudit ? false : config.audit.enabled;
|
|
8268
8850
|
if (installAudit) {
|
|
8269
8851
|
const auditSchedule = applyScheduleOffset(config.audit.schedule, offset);
|
|
8270
|
-
const auditLog =
|
|
8852
|
+
const auditLog = path22.join(logDir, "audit.log");
|
|
8271
8853
|
const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
|
|
8272
8854
|
entries.push(auditEntry);
|
|
8273
8855
|
}
|
|
@@ -8294,12 +8876,12 @@ function installCommand(program2) {
|
|
|
8294
8876
|
const nightWatchBin = getNightWatchBinPath();
|
|
8295
8877
|
const projectName = getProjectName(projectDir);
|
|
8296
8878
|
const marker = generateMarker(projectName);
|
|
8297
|
-
const logDir =
|
|
8298
|
-
if (!
|
|
8299
|
-
|
|
8879
|
+
const logDir = path22.join(projectDir, LOG_DIR);
|
|
8880
|
+
if (!fs21.existsSync(logDir)) {
|
|
8881
|
+
fs21.mkdirSync(logDir, { recursive: true });
|
|
8300
8882
|
}
|
|
8301
|
-
const executorLog =
|
|
8302
|
-
const reviewerLog =
|
|
8883
|
+
const executorLog = path22.join(logDir, "executor.log");
|
|
8884
|
+
const reviewerLog = path22.join(logDir, "reviewer.log");
|
|
8303
8885
|
const existingEntries = Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
|
|
8304
8886
|
if (existingEntries.length > 0) {
|
|
8305
8887
|
warn(`Night Watch is already installed for ${projectName}.`);
|
|
@@ -8332,7 +8914,7 @@ function installCommand(program2) {
|
|
|
8332
8914
|
const installSlicer = options.noSlicer === true ? false : config.roadmapScanner.enabled;
|
|
8333
8915
|
let slicerLog;
|
|
8334
8916
|
if (installSlicer) {
|
|
8335
|
-
slicerLog =
|
|
8917
|
+
slicerLog = path22.join(logDir, "slicer.log");
|
|
8336
8918
|
const slicerSchedule = applyScheduleOffset(config.roadmapScanner.slicerSchedule, offset);
|
|
8337
8919
|
const slicerEntry = `${slicerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} planner >> ${shellQuote(slicerLog)} 2>&1 ${marker}`;
|
|
8338
8920
|
entries.push(slicerEntry);
|
|
@@ -8341,7 +8923,7 @@ function installCommand(program2) {
|
|
|
8341
8923
|
const installQa = disableQa ? false : config.qa.enabled;
|
|
8342
8924
|
let qaLog;
|
|
8343
8925
|
if (installQa) {
|
|
8344
|
-
qaLog =
|
|
8926
|
+
qaLog = path22.join(logDir, "qa.log");
|
|
8345
8927
|
const qaSchedule = applyScheduleOffset(config.qa.schedule, offset);
|
|
8346
8928
|
const qaEntry = `${qaSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} qa >> ${shellQuote(qaLog)} 2>&1 ${marker}`;
|
|
8347
8929
|
entries.push(qaEntry);
|
|
@@ -8350,7 +8932,7 @@ function installCommand(program2) {
|
|
|
8350
8932
|
const installAudit = disableAudit ? false : config.audit.enabled;
|
|
8351
8933
|
let auditLog;
|
|
8352
8934
|
if (installAudit) {
|
|
8353
|
-
auditLog =
|
|
8935
|
+
auditLog = path22.join(logDir, "audit.log");
|
|
8354
8936
|
const auditSchedule = applyScheduleOffset(config.audit.schedule, offset);
|
|
8355
8937
|
const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
|
|
8356
8938
|
entries.push(auditEntry);
|
|
@@ -8399,19 +8981,19 @@ function performUninstall(projectDir, options) {
|
|
|
8399
8981
|
}
|
|
8400
8982
|
const removedCount = removeEntriesForProject(projectDir, marker);
|
|
8401
8983
|
if (!options?.keepLogs) {
|
|
8402
|
-
const logDir =
|
|
8403
|
-
if (
|
|
8984
|
+
const logDir = path23.join(projectDir, "logs");
|
|
8985
|
+
if (fs22.existsSync(logDir)) {
|
|
8404
8986
|
const logFiles = ["executor.log", "reviewer.log", "slicer.log", "audit.log"];
|
|
8405
8987
|
logFiles.forEach((logFile) => {
|
|
8406
|
-
const logPath =
|
|
8407
|
-
if (
|
|
8408
|
-
|
|
8988
|
+
const logPath = path23.join(logDir, logFile);
|
|
8989
|
+
if (fs22.existsSync(logPath)) {
|
|
8990
|
+
fs22.unlinkSync(logPath);
|
|
8409
8991
|
}
|
|
8410
8992
|
});
|
|
8411
8993
|
try {
|
|
8412
|
-
const remainingFiles =
|
|
8994
|
+
const remainingFiles = fs22.readdirSync(logDir);
|
|
8413
8995
|
if (remainingFiles.length === 0) {
|
|
8414
|
-
|
|
8996
|
+
fs22.rmdirSync(logDir);
|
|
8415
8997
|
}
|
|
8416
8998
|
} catch {
|
|
8417
8999
|
}
|
|
@@ -8442,21 +9024,21 @@ function uninstallCommand(program2) {
|
|
|
8442
9024
|
existingEntries.forEach((entry) => dim(` ${entry}`));
|
|
8443
9025
|
const removedCount = removeEntriesForProject(projectDir, marker);
|
|
8444
9026
|
if (!options.keepLogs) {
|
|
8445
|
-
const logDir =
|
|
8446
|
-
if (
|
|
9027
|
+
const logDir = path23.join(projectDir, "logs");
|
|
9028
|
+
if (fs22.existsSync(logDir)) {
|
|
8447
9029
|
const logFiles = ["executor.log", "reviewer.log", "slicer.log", "audit.log"];
|
|
8448
9030
|
let logsRemoved = 0;
|
|
8449
9031
|
logFiles.forEach((logFile) => {
|
|
8450
|
-
const logPath =
|
|
8451
|
-
if (
|
|
8452
|
-
|
|
9032
|
+
const logPath = path23.join(logDir, logFile);
|
|
9033
|
+
if (fs22.existsSync(logPath)) {
|
|
9034
|
+
fs22.unlinkSync(logPath);
|
|
8453
9035
|
logsRemoved++;
|
|
8454
9036
|
}
|
|
8455
9037
|
});
|
|
8456
9038
|
try {
|
|
8457
|
-
const remainingFiles =
|
|
9039
|
+
const remainingFiles = fs22.readdirSync(logDir);
|
|
8458
9040
|
if (remainingFiles.length === 0) {
|
|
8459
|
-
|
|
9041
|
+
fs22.rmdirSync(logDir);
|
|
8460
9042
|
}
|
|
8461
9043
|
} catch {
|
|
8462
9044
|
}
|
|
@@ -8682,11 +9264,11 @@ function statusCommand(program2) {
|
|
|
8682
9264
|
}
|
|
8683
9265
|
init_dist();
|
|
8684
9266
|
function getLastLines(filePath, lineCount) {
|
|
8685
|
-
if (!
|
|
9267
|
+
if (!fs23.existsSync(filePath)) {
|
|
8686
9268
|
return `Log file not found: ${filePath}`;
|
|
8687
9269
|
}
|
|
8688
9270
|
try {
|
|
8689
|
-
const content =
|
|
9271
|
+
const content = fs23.readFileSync(filePath, "utf-8");
|
|
8690
9272
|
const lines = content.trim().split("\n");
|
|
8691
9273
|
return lines.slice(-lineCount).join("\n");
|
|
8692
9274
|
} catch (error2) {
|
|
@@ -8694,7 +9276,7 @@ function getLastLines(filePath, lineCount) {
|
|
|
8694
9276
|
}
|
|
8695
9277
|
}
|
|
8696
9278
|
function followLog(filePath) {
|
|
8697
|
-
if (!
|
|
9279
|
+
if (!fs23.existsSync(filePath)) {
|
|
8698
9280
|
console.log(`Log file not found: ${filePath}`);
|
|
8699
9281
|
console.log("The log file will be created when the first execution runs.");
|
|
8700
9282
|
return;
|
|
@@ -8714,13 +9296,13 @@ function logsCommand(program2) {
|
|
|
8714
9296
|
program2.command("logs").description("View night-watch log output").option("-n, --lines <count>", "Number of lines to show", "50").option("-f, --follow", "Follow log output (tail -f)").option("-t, --type <type>", "Log type to view (executor|reviewer|qa|audit|planner|all)", "all").action(async (options) => {
|
|
8715
9297
|
try {
|
|
8716
9298
|
const projectDir = process.cwd();
|
|
8717
|
-
const logDir =
|
|
9299
|
+
const logDir = path24.join(projectDir, LOG_DIR);
|
|
8718
9300
|
const lineCount = parseInt(options.lines || "50", 10);
|
|
8719
|
-
const executorLog =
|
|
8720
|
-
const reviewerLog =
|
|
8721
|
-
const qaLog =
|
|
8722
|
-
const auditLog =
|
|
8723
|
-
const plannerLog =
|
|
9301
|
+
const executorLog = path24.join(logDir, EXECUTOR_LOG_FILE);
|
|
9302
|
+
const reviewerLog = path24.join(logDir, REVIEWER_LOG_FILE);
|
|
9303
|
+
const qaLog = path24.join(logDir, `${QA_LOG_NAME}.log`);
|
|
9304
|
+
const auditLog = path24.join(logDir, `${AUDIT_LOG_NAME}.log`);
|
|
9305
|
+
const plannerLog = path24.join(logDir, `${PLANNER_LOG_NAME}.log`);
|
|
8724
9306
|
const logType = options.type?.toLowerCase() || "all";
|
|
8725
9307
|
const showExecutor = logType === "all" || logType === "run" || logType === "executor";
|
|
8726
9308
|
const showReviewer = logType === "all" || logType === "review" || logType === "reviewer";
|
|
@@ -8790,9 +9372,9 @@ function slugify2(name) {
|
|
|
8790
9372
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
8791
9373
|
}
|
|
8792
9374
|
function getNextPrdNumber2(prdDir) {
|
|
8793
|
-
if (!
|
|
9375
|
+
if (!fs24.existsSync(prdDir))
|
|
8794
9376
|
return 1;
|
|
8795
|
-
const files =
|
|
9377
|
+
const files = fs24.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
|
|
8796
9378
|
const numbers = files.map((f) => {
|
|
8797
9379
|
const match = f.match(/^(\d+)-/);
|
|
8798
9380
|
return match ? parseInt(match[1], 10) : 0;
|
|
@@ -8814,10 +9396,10 @@ function parseDependencies(content) {
|
|
|
8814
9396
|
}
|
|
8815
9397
|
function isClaimActive(claimPath, maxRuntime) {
|
|
8816
9398
|
try {
|
|
8817
|
-
if (!
|
|
9399
|
+
if (!fs24.existsSync(claimPath)) {
|
|
8818
9400
|
return { active: false };
|
|
8819
9401
|
}
|
|
8820
|
-
const content =
|
|
9402
|
+
const content = fs24.readFileSync(claimPath, "utf-8");
|
|
8821
9403
|
const claim = JSON.parse(content);
|
|
8822
9404
|
const age = Math.floor(Date.now() / 1e3) - claim.timestamp;
|
|
8823
9405
|
if (age < maxRuntime) {
|
|
@@ -8833,9 +9415,9 @@ function prdCommand(program2) {
|
|
|
8833
9415
|
prd.command("create").description("Generate a new PRD markdown file from template").argument("<name>", "PRD name (used for title and filename)").option("-i, --interactive", "Prompt for complexity, dependencies, and phase count", false).option("-t, --template <path>", "Path to a custom template file").option("--deps <files>", "Comma-separated dependency filenames").option("--phases <count>", "Number of execution phases", "3").option("--no-number", "Skip auto-numbering prefix").action(async (name, options) => {
|
|
8834
9416
|
const projectDir = process.cwd();
|
|
8835
9417
|
const config = loadConfig(projectDir);
|
|
8836
|
-
const prdDir =
|
|
8837
|
-
if (!
|
|
8838
|
-
|
|
9418
|
+
const prdDir = path25.join(projectDir, config.prdDir);
|
|
9419
|
+
if (!fs24.existsSync(prdDir)) {
|
|
9420
|
+
fs24.mkdirSync(prdDir, { recursive: true });
|
|
8839
9421
|
}
|
|
8840
9422
|
let complexityScore = 5;
|
|
8841
9423
|
let dependsOn = [];
|
|
@@ -8891,20 +9473,20 @@ function prdCommand(program2) {
|
|
|
8891
9473
|
} else {
|
|
8892
9474
|
filename = `${slug}.md`;
|
|
8893
9475
|
}
|
|
8894
|
-
const filePath =
|
|
8895
|
-
if (
|
|
9476
|
+
const filePath = path25.join(prdDir, filename);
|
|
9477
|
+
if (fs24.existsSync(filePath)) {
|
|
8896
9478
|
error(`File already exists: ${filePath}`);
|
|
8897
9479
|
dim("Use a different name or remove the existing file.");
|
|
8898
9480
|
process.exit(1);
|
|
8899
9481
|
}
|
|
8900
9482
|
let customTemplate;
|
|
8901
9483
|
if (options.template) {
|
|
8902
|
-
const templatePath =
|
|
8903
|
-
if (!
|
|
9484
|
+
const templatePath = path25.resolve(options.template);
|
|
9485
|
+
if (!fs24.existsSync(templatePath)) {
|
|
8904
9486
|
error(`Template file not found: ${templatePath}`);
|
|
8905
9487
|
process.exit(1);
|
|
8906
9488
|
}
|
|
8907
|
-
customTemplate =
|
|
9489
|
+
customTemplate = fs24.readFileSync(templatePath, "utf-8");
|
|
8908
9490
|
}
|
|
8909
9491
|
const vars = {
|
|
8910
9492
|
title: name,
|
|
@@ -8915,7 +9497,7 @@ function prdCommand(program2) {
|
|
|
8915
9497
|
phaseCount
|
|
8916
9498
|
};
|
|
8917
9499
|
const content = renderPrdTemplate(vars, customTemplate);
|
|
8918
|
-
|
|
9500
|
+
fs24.writeFileSync(filePath, content, "utf-8");
|
|
8919
9501
|
header("PRD Created");
|
|
8920
9502
|
success(`Created: ${filePath}`);
|
|
8921
9503
|
info(`Title: ${name}`);
|
|
@@ -8927,15 +9509,15 @@ function prdCommand(program2) {
|
|
|
8927
9509
|
prd.command("list").description("List all PRDs with status").option("--json", "Output as JSON").action(async (options) => {
|
|
8928
9510
|
const projectDir = process.cwd();
|
|
8929
9511
|
const config = loadConfig(projectDir);
|
|
8930
|
-
const absolutePrdDir =
|
|
8931
|
-
const doneDir =
|
|
9512
|
+
const absolutePrdDir = path25.join(projectDir, config.prdDir);
|
|
9513
|
+
const doneDir = path25.join(absolutePrdDir, "done");
|
|
8932
9514
|
const pending = [];
|
|
8933
|
-
if (
|
|
8934
|
-
const files =
|
|
9515
|
+
if (fs24.existsSync(absolutePrdDir)) {
|
|
9516
|
+
const files = fs24.readdirSync(absolutePrdDir).filter((f) => f.endsWith(".md"));
|
|
8935
9517
|
for (const file of files) {
|
|
8936
|
-
const content =
|
|
9518
|
+
const content = fs24.readFileSync(path25.join(absolutePrdDir, file), "utf-8");
|
|
8937
9519
|
const deps = parseDependencies(content);
|
|
8938
|
-
const claimPath =
|
|
9520
|
+
const claimPath = path25.join(absolutePrdDir, file + CLAIM_FILE_EXTENSION);
|
|
8939
9521
|
const claimStatus = isClaimActive(claimPath, config.maxRuntime);
|
|
8940
9522
|
pending.push({
|
|
8941
9523
|
name: file,
|
|
@@ -8946,10 +9528,10 @@ function prdCommand(program2) {
|
|
|
8946
9528
|
}
|
|
8947
9529
|
}
|
|
8948
9530
|
const done = [];
|
|
8949
|
-
if (
|
|
8950
|
-
const files =
|
|
9531
|
+
if (fs24.existsSync(doneDir)) {
|
|
9532
|
+
const files = fs24.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
|
|
8951
9533
|
for (const file of files) {
|
|
8952
|
-
const content =
|
|
9534
|
+
const content = fs24.readFileSync(path25.join(doneDir, file), "utf-8");
|
|
8953
9535
|
const deps = parseDependencies(content);
|
|
8954
9536
|
done.push({ name: file, dependencies: deps });
|
|
8955
9537
|
}
|
|
@@ -8982,7 +9564,7 @@ function prdCommand(program2) {
|
|
|
8982
9564
|
}
|
|
8983
9565
|
init_dist();
|
|
8984
9566
|
init_dist();
|
|
8985
|
-
function
|
|
9567
|
+
function sortPrdsByPriority2(prds, priority) {
|
|
8986
9568
|
if (priority.length === 0)
|
|
8987
9569
|
return prds;
|
|
8988
9570
|
const priorityMap = /* @__PURE__ */ new Map();
|
|
@@ -9001,7 +9583,7 @@ function renderPrdPane(prds, priority) {
|
|
|
9001
9583
|
if (prds.length === 0) {
|
|
9002
9584
|
return "No PRD files found";
|
|
9003
9585
|
}
|
|
9004
|
-
const sorted = priority ?
|
|
9586
|
+
const sorted = priority ? sortPrdsByPriority2(prds, priority) : prds;
|
|
9005
9587
|
const lines = [];
|
|
9006
9588
|
for (const prd of sorted) {
|
|
9007
9589
|
let indicator;
|
|
@@ -9079,7 +9661,7 @@ function renderLogPane(projectDir, logs) {
|
|
|
9079
9661
|
let newestMtime = 0;
|
|
9080
9662
|
for (const log of existingLogs) {
|
|
9081
9663
|
try {
|
|
9082
|
-
const stat =
|
|
9664
|
+
const stat = fs25.statSync(log.path);
|
|
9083
9665
|
if (stat.mtimeMs > newestMtime) {
|
|
9084
9666
|
newestMtime = stat.mtimeMs;
|
|
9085
9667
|
newestLog = log;
|
|
@@ -9204,7 +9786,7 @@ function createStatusTab() {
|
|
|
9204
9786
|
ctx.showMessage("No PRDs to reorder", "info");
|
|
9205
9787
|
return;
|
|
9206
9788
|
}
|
|
9207
|
-
const sorted =
|
|
9789
|
+
const sorted = sortPrdsByPriority2(nonDone, ctx.config.prdPriority);
|
|
9208
9790
|
reorderList = sorted.map((p) => p.name);
|
|
9209
9791
|
reorderIndex = 0;
|
|
9210
9792
|
reorderMode = true;
|
|
@@ -10760,7 +11342,7 @@ function createLogsTab() {
|
|
|
10760
11342
|
let activeKeyHandlers = [];
|
|
10761
11343
|
let activeCtx = null;
|
|
10762
11344
|
function getLogPath(projectDir, logName) {
|
|
10763
|
-
return
|
|
11345
|
+
return path26.join(projectDir, "logs", `${logName}.log`);
|
|
10764
11346
|
}
|
|
10765
11347
|
function updateSelector() {
|
|
10766
11348
|
const tabs = LOG_NAMES.map((name, idx) => {
|
|
@@ -10774,7 +11356,7 @@ function createLogsTab() {
|
|
|
10774
11356
|
function loadLog(ctx) {
|
|
10775
11357
|
const logName = LOG_NAMES[selectedLogIndex];
|
|
10776
11358
|
const logPath = getLogPath(ctx.projectDir, logName);
|
|
10777
|
-
if (!
|
|
11359
|
+
if (!fs26.existsSync(logPath)) {
|
|
10778
11360
|
logContent.setContent(`{yellow-fg}No ${logName}.log file found{/yellow-fg}
|
|
10779
11361
|
|
|
10780
11362
|
Log will appear here once the ${logName} runs.`);
|
|
@@ -10782,7 +11364,7 @@ Log will appear here once the ${logName} runs.`);
|
|
|
10782
11364
|
return;
|
|
10783
11365
|
}
|
|
10784
11366
|
try {
|
|
10785
|
-
const stat =
|
|
11367
|
+
const stat = fs26.statSync(logPath);
|
|
10786
11368
|
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
10787
11369
|
logContent.setLabel(`[ ${logName}.log - ${sizeKB} KB ]`);
|
|
10788
11370
|
} catch {
|
|
@@ -11347,7 +11929,7 @@ function resolveProject(req, res, next) {
|
|
|
11347
11929
|
res.status(404).json({ error: `Project not found: ${decodedId}` });
|
|
11348
11930
|
return;
|
|
11349
11931
|
}
|
|
11350
|
-
if (!
|
|
11932
|
+
if (!fs27.existsSync(entry.path) || !fs27.existsSync(path27.join(entry.path, CONFIG_FILE_NAME))) {
|
|
11351
11933
|
res.status(404).json({ error: `Project path invalid or missing config: ${entry.path}` });
|
|
11352
11934
|
return;
|
|
11353
11935
|
}
|
|
@@ -11432,17 +12014,17 @@ function getBoardProvider(config, projectDir) {
|
|
|
11432
12014
|
function cleanOrphanedClaims(dir) {
|
|
11433
12015
|
let entries;
|
|
11434
12016
|
try {
|
|
11435
|
-
entries =
|
|
12017
|
+
entries = fs28.readdirSync(dir, { withFileTypes: true });
|
|
11436
12018
|
} catch {
|
|
11437
12019
|
return;
|
|
11438
12020
|
}
|
|
11439
12021
|
for (const entry of entries) {
|
|
11440
|
-
const fullPath =
|
|
12022
|
+
const fullPath = path28.join(dir, entry.name);
|
|
11441
12023
|
if (entry.isDirectory() && entry.name !== "done") {
|
|
11442
12024
|
cleanOrphanedClaims(fullPath);
|
|
11443
12025
|
} else if (entry.name.endsWith(CLAIM_FILE_EXTENSION)) {
|
|
11444
12026
|
try {
|
|
11445
|
-
|
|
12027
|
+
fs28.unlinkSync(fullPath);
|
|
11446
12028
|
} catch {
|
|
11447
12029
|
}
|
|
11448
12030
|
}
|
|
@@ -11490,7 +12072,7 @@ function spawnAction2(projectDir, command, req, res, onSpawned) {
|
|
|
11490
12072
|
const config = loadConfig(projectDir);
|
|
11491
12073
|
sendNotifications(config, {
|
|
11492
12074
|
event: "run_started",
|
|
11493
|
-
projectName:
|
|
12075
|
+
projectName: path28.basename(projectDir),
|
|
11494
12076
|
exitCode: 0,
|
|
11495
12077
|
provider: config.provider
|
|
11496
12078
|
}).catch(() => {
|
|
@@ -11572,19 +12154,19 @@ function createActionRouteHandlers(ctx) {
|
|
|
11572
12154
|
res.status(400).json({ error: "Invalid PRD name" });
|
|
11573
12155
|
return;
|
|
11574
12156
|
}
|
|
11575
|
-
const prdDir =
|
|
12157
|
+
const prdDir = path28.join(projectDir, config.prdDir);
|
|
11576
12158
|
const normalized = prdName.endsWith(".md") ? prdName : `${prdName}.md`;
|
|
11577
|
-
const pendingPath =
|
|
11578
|
-
const donePath =
|
|
11579
|
-
if (
|
|
12159
|
+
const pendingPath = path28.join(prdDir, normalized);
|
|
12160
|
+
const donePath = path28.join(prdDir, "done", normalized);
|
|
12161
|
+
if (fs28.existsSync(pendingPath)) {
|
|
11580
12162
|
res.json({ message: `"${normalized}" is already pending` });
|
|
11581
12163
|
return;
|
|
11582
12164
|
}
|
|
11583
|
-
if (!
|
|
12165
|
+
if (!fs28.existsSync(donePath)) {
|
|
11584
12166
|
res.status(404).json({ error: `PRD "${normalized}" not found in done/` });
|
|
11585
12167
|
return;
|
|
11586
12168
|
}
|
|
11587
|
-
|
|
12169
|
+
fs28.renameSync(donePath, pendingPath);
|
|
11588
12170
|
res.json({ message: `Moved "${normalized}" back to pending` });
|
|
11589
12171
|
} catch (error2) {
|
|
11590
12172
|
res.status(500).json({
|
|
@@ -11602,11 +12184,11 @@ function createActionRouteHandlers(ctx) {
|
|
|
11602
12184
|
res.status(409).json({ error: "Executor is actively running \u2014 use Stop instead" });
|
|
11603
12185
|
return;
|
|
11604
12186
|
}
|
|
11605
|
-
if (
|
|
11606
|
-
|
|
12187
|
+
if (fs28.existsSync(lockPath)) {
|
|
12188
|
+
fs28.unlinkSync(lockPath);
|
|
11607
12189
|
}
|
|
11608
|
-
const prdDir =
|
|
11609
|
-
if (
|
|
12190
|
+
const prdDir = path28.join(projectDir, config.prdDir);
|
|
12191
|
+
if (fs28.existsSync(prdDir)) {
|
|
11610
12192
|
cleanOrphanedClaims(prdDir);
|
|
11611
12193
|
}
|
|
11612
12194
|
broadcastSSE(ctx.getSseClients(req), "status_changed", await fetchStatusSnapshot(projectDir, config));
|
|
@@ -11743,6 +12325,7 @@ function createAgentRoutes() {
|
|
|
11743
12325
|
return router;
|
|
11744
12326
|
}
|
|
11745
12327
|
init_dist();
|
|
12328
|
+
var ERROR_BOARD_NOT_CONFIGURED = "Board not configured";
|
|
11746
12329
|
function createBoardRouteHandlers(ctx) {
|
|
11747
12330
|
const router = Router3({ mergeParams: true });
|
|
11748
12331
|
const p = ctx.pathPrefix;
|
|
@@ -11752,7 +12335,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11752
12335
|
const projectDir = ctx.getProjectDir(req);
|
|
11753
12336
|
const provider = getBoardProvider(config, projectDir);
|
|
11754
12337
|
if (!provider) {
|
|
11755
|
-
res.status(404).json({ error:
|
|
12338
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11756
12339
|
return;
|
|
11757
12340
|
}
|
|
11758
12341
|
const cached = getCachedBoardData(projectDir);
|
|
@@ -11785,7 +12368,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11785
12368
|
const projectDir = ctx.getProjectDir(req);
|
|
11786
12369
|
const provider = getBoardProvider(config, projectDir);
|
|
11787
12370
|
if (!provider) {
|
|
11788
|
-
res.status(404).json({ error:
|
|
12371
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11789
12372
|
return;
|
|
11790
12373
|
}
|
|
11791
12374
|
const issues = await provider.getAllIssues();
|
|
@@ -11800,7 +12383,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11800
12383
|
const projectDir = ctx.getProjectDir(req);
|
|
11801
12384
|
const provider = getBoardProvider(config, projectDir);
|
|
11802
12385
|
if (!provider) {
|
|
11803
|
-
res.status(404).json({ error:
|
|
12386
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11804
12387
|
return;
|
|
11805
12388
|
}
|
|
11806
12389
|
const { title, body, column } = req.body;
|
|
@@ -11831,7 +12414,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11831
12414
|
const projectDir = ctx.getProjectDir(req);
|
|
11832
12415
|
const provider = getBoardProvider(config, projectDir);
|
|
11833
12416
|
if (!provider) {
|
|
11834
|
-
res.status(404).json({ error:
|
|
12417
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11835
12418
|
return;
|
|
11836
12419
|
}
|
|
11837
12420
|
const issueNumber = parseInt(req.params.number, 10);
|
|
@@ -11861,7 +12444,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11861
12444
|
const projectDir = ctx.getProjectDir(req);
|
|
11862
12445
|
const provider = getBoardProvider(config, projectDir);
|
|
11863
12446
|
if (!provider) {
|
|
11864
|
-
res.status(404).json({ error:
|
|
12447
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11865
12448
|
return;
|
|
11866
12449
|
}
|
|
11867
12450
|
const issueNumber = parseInt(req.params.number, 10);
|
|
@@ -11889,7 +12472,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11889
12472
|
const projectDir = ctx.getProjectDir(req);
|
|
11890
12473
|
const provider = getBoardProvider(config, projectDir);
|
|
11891
12474
|
if (!provider) {
|
|
11892
|
-
res.status(404).json({ error:
|
|
12475
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11893
12476
|
return;
|
|
11894
12477
|
}
|
|
11895
12478
|
const issueNumber = parseInt(req.params.number, 10);
|
|
@@ -12177,7 +12760,7 @@ function runDoctorChecks(projectDir, config) {
|
|
|
12177
12760
|
});
|
|
12178
12761
|
}
|
|
12179
12762
|
try {
|
|
12180
|
-
const projectName =
|
|
12763
|
+
const projectName = path29.basename(projectDir);
|
|
12181
12764
|
const marker = generateMarker(projectName);
|
|
12182
12765
|
const crontabEntries = [...getEntries(marker), ...getProjectEntries(projectDir)];
|
|
12183
12766
|
if (crontabEntries.length > 0) {
|
|
@@ -12200,8 +12783,8 @@ function runDoctorChecks(projectDir, config) {
|
|
|
12200
12783
|
detail: "Failed to check crontab"
|
|
12201
12784
|
});
|
|
12202
12785
|
}
|
|
12203
|
-
const configPath =
|
|
12204
|
-
if (
|
|
12786
|
+
const configPath = path29.join(projectDir, CONFIG_FILE_NAME);
|
|
12787
|
+
if (fs29.existsSync(configPath)) {
|
|
12205
12788
|
checks.push({ name: "config", status: "pass", detail: "Config file exists" });
|
|
12206
12789
|
} else {
|
|
12207
12790
|
checks.push({
|
|
@@ -12210,9 +12793,9 @@ function runDoctorChecks(projectDir, config) {
|
|
|
12210
12793
|
detail: "Config file not found (using defaults)"
|
|
12211
12794
|
});
|
|
12212
12795
|
}
|
|
12213
|
-
const prdDir =
|
|
12214
|
-
if (
|
|
12215
|
-
const prds =
|
|
12796
|
+
const prdDir = path29.join(projectDir, config.prdDir);
|
|
12797
|
+
if (fs29.existsSync(prdDir)) {
|
|
12798
|
+
const prds = fs29.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
|
|
12216
12799
|
checks.push({
|
|
12217
12800
|
name: "prdDir",
|
|
12218
12801
|
status: "pass",
|
|
@@ -12270,7 +12853,7 @@ function createLogRoutes(deps) {
|
|
|
12270
12853
|
const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
|
|
12271
12854
|
const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
|
|
12272
12855
|
const fileName = LOG_FILE_NAMES[name] || name;
|
|
12273
|
-
const logPath =
|
|
12856
|
+
const logPath = path30.join(projectDir, LOG_DIR, `${fileName}.log`);
|
|
12274
12857
|
const logLines = getLastLogLines(logPath, linesToRead);
|
|
12275
12858
|
res.json({ name, lines: logLines });
|
|
12276
12859
|
} catch (error2) {
|
|
@@ -12296,7 +12879,7 @@ function createProjectLogRoutes() {
|
|
|
12296
12879
|
const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
|
|
12297
12880
|
const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
|
|
12298
12881
|
const fileName = LOG_FILE_NAMES[name] || name;
|
|
12299
|
-
const logPath =
|
|
12882
|
+
const logPath = path30.join(projectDir, LOG_DIR, `${fileName}.log`);
|
|
12300
12883
|
const logLines = getLastLogLines(logPath, linesToRead);
|
|
12301
12884
|
res.json({ name, lines: logLines });
|
|
12302
12885
|
} catch (error2) {
|
|
@@ -12334,7 +12917,7 @@ function createRoadmapRouteHandlers(ctx) {
|
|
|
12334
12917
|
const config = ctx.getConfig(req);
|
|
12335
12918
|
const projectDir = ctx.getProjectDir(req);
|
|
12336
12919
|
const status = getRoadmapStatus(projectDir, config);
|
|
12337
|
-
const prdDir =
|
|
12920
|
+
const prdDir = path31.join(projectDir, config.prdDir);
|
|
12338
12921
|
const state = loadRoadmapState(prdDir);
|
|
12339
12922
|
res.json({
|
|
12340
12923
|
...status,
|
|
@@ -12591,14 +13174,14 @@ data: ${JSON.stringify(snapshot)}
|
|
|
12591
13174
|
var __filename2 = fileURLToPath3(import.meta.url);
|
|
12592
13175
|
var __dirname3 = dirname7(__filename2);
|
|
12593
13176
|
function resolveWebDistPath() {
|
|
12594
|
-
const bundled =
|
|
12595
|
-
if (
|
|
13177
|
+
const bundled = path32.join(__dirname3, "web");
|
|
13178
|
+
if (fs30.existsSync(path32.join(bundled, "index.html")))
|
|
12596
13179
|
return bundled;
|
|
12597
13180
|
let d = __dirname3;
|
|
12598
13181
|
for (let i = 0; i < 8; i++) {
|
|
12599
|
-
if (
|
|
12600
|
-
const dev =
|
|
12601
|
-
if (
|
|
13182
|
+
if (fs30.existsSync(path32.join(d, "turbo.json"))) {
|
|
13183
|
+
const dev = path32.join(d, "web/dist");
|
|
13184
|
+
if (fs30.existsSync(path32.join(dev, "index.html")))
|
|
12602
13185
|
return dev;
|
|
12603
13186
|
break;
|
|
12604
13187
|
}
|
|
@@ -12608,7 +13191,7 @@ function resolveWebDistPath() {
|
|
|
12608
13191
|
}
|
|
12609
13192
|
function setupStaticFiles(app) {
|
|
12610
13193
|
const webDistPath = resolveWebDistPath();
|
|
12611
|
-
if (
|
|
13194
|
+
if (fs30.existsSync(webDistPath)) {
|
|
12612
13195
|
app.use(express.static(webDistPath));
|
|
12613
13196
|
}
|
|
12614
13197
|
app.use((req, res, next) => {
|
|
@@ -12616,8 +13199,8 @@ function setupStaticFiles(app) {
|
|
|
12616
13199
|
next();
|
|
12617
13200
|
return;
|
|
12618
13201
|
}
|
|
12619
|
-
const indexPath =
|
|
12620
|
-
if (
|
|
13202
|
+
const indexPath = path32.resolve(webDistPath, "index.html");
|
|
13203
|
+
if (fs30.existsSync(indexPath)) {
|
|
12621
13204
|
res.sendFile(indexPath, (err) => {
|
|
12622
13205
|
if (err)
|
|
12623
13206
|
next();
|
|
@@ -12727,7 +13310,7 @@ function createGlobalApp() {
|
|
|
12727
13310
|
return app;
|
|
12728
13311
|
}
|
|
12729
13312
|
function bootContainer() {
|
|
12730
|
-
initContainer(
|
|
13313
|
+
initContainer(path32.dirname(getDbPath()));
|
|
12731
13314
|
const personaRepo = container.resolve(SqliteAgentPersonaRepository);
|
|
12732
13315
|
personaRepo.seedDefaultsOnFirstRun();
|
|
12733
13316
|
personaRepo.patchDefaultAvatarUrls();
|
|
@@ -12781,9 +13364,9 @@ function isProcessRunning2(pid) {
|
|
|
12781
13364
|
}
|
|
12782
13365
|
function readPid(lockPath) {
|
|
12783
13366
|
try {
|
|
12784
|
-
if (!
|
|
13367
|
+
if (!fs31.existsSync(lockPath))
|
|
12785
13368
|
return null;
|
|
12786
|
-
const raw =
|
|
13369
|
+
const raw = fs31.readFileSync(lockPath, "utf-8").trim();
|
|
12787
13370
|
const pid = parseInt(raw, 10);
|
|
12788
13371
|
return Number.isFinite(pid) ? pid : null;
|
|
12789
13372
|
} catch {
|
|
@@ -12795,10 +13378,10 @@ function acquireServeLock(mode, port) {
|
|
|
12795
13378
|
let stalePidCleaned;
|
|
12796
13379
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
12797
13380
|
try {
|
|
12798
|
-
const fd =
|
|
12799
|
-
|
|
13381
|
+
const fd = fs31.openSync(lockPath, "wx");
|
|
13382
|
+
fs31.writeFileSync(fd, `${process.pid}
|
|
12800
13383
|
`);
|
|
12801
|
-
|
|
13384
|
+
fs31.closeSync(fd);
|
|
12802
13385
|
return { acquired: true, lockPath, stalePidCleaned };
|
|
12803
13386
|
} catch (error2) {
|
|
12804
13387
|
const err = error2;
|
|
@@ -12819,7 +13402,7 @@ function acquireServeLock(mode, port) {
|
|
|
12819
13402
|
};
|
|
12820
13403
|
}
|
|
12821
13404
|
try {
|
|
12822
|
-
|
|
13405
|
+
fs31.unlinkSync(lockPath);
|
|
12823
13406
|
if (existingPid) {
|
|
12824
13407
|
stalePidCleaned = existingPid;
|
|
12825
13408
|
}
|
|
@@ -12842,12 +13425,12 @@ function acquireServeLock(mode, port) {
|
|
|
12842
13425
|
}
|
|
12843
13426
|
function releaseServeLock(lockPath) {
|
|
12844
13427
|
try {
|
|
12845
|
-
if (!
|
|
13428
|
+
if (!fs31.existsSync(lockPath))
|
|
12846
13429
|
return;
|
|
12847
13430
|
const lockPid = readPid(lockPath);
|
|
12848
13431
|
if (lockPid !== null && lockPid !== process.pid)
|
|
12849
13432
|
return;
|
|
12850
|
-
|
|
13433
|
+
fs31.unlinkSync(lockPath);
|
|
12851
13434
|
} catch {
|
|
12852
13435
|
}
|
|
12853
13436
|
}
|
|
@@ -12933,7 +13516,7 @@ function parseProjectDirs(projects, cwd) {
|
|
|
12933
13516
|
if (!projects || projects.trim().length === 0) {
|
|
12934
13517
|
return [cwd];
|
|
12935
13518
|
}
|
|
12936
|
-
const dirs = projects.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) =>
|
|
13519
|
+
const dirs = projects.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => path33.resolve(cwd, entry));
|
|
12937
13520
|
return Array.from(new Set(dirs));
|
|
12938
13521
|
}
|
|
12939
13522
|
function runCommand2(command, args, cwd) {
|
|
@@ -12972,7 +13555,7 @@ function updateCommand(program2) {
|
|
|
12972
13555
|
}
|
|
12973
13556
|
const nightWatchBin = resolveNightWatchBin();
|
|
12974
13557
|
for (const projectDir of projectDirs) {
|
|
12975
|
-
if (!
|
|
13558
|
+
if (!fs32.existsSync(projectDir) || !fs32.statSync(projectDir).isDirectory()) {
|
|
12976
13559
|
warn(`Skipping invalid project directory: ${projectDir}`);
|
|
12977
13560
|
continue;
|
|
12978
13561
|
}
|
|
@@ -13019,26 +13602,26 @@ function normalizePrdName(name) {
|
|
|
13019
13602
|
return name;
|
|
13020
13603
|
}
|
|
13021
13604
|
function getDonePrds(doneDir) {
|
|
13022
|
-
if (!
|
|
13605
|
+
if (!fs33.existsSync(doneDir)) {
|
|
13023
13606
|
return [];
|
|
13024
13607
|
}
|
|
13025
|
-
return
|
|
13608
|
+
return fs33.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
|
|
13026
13609
|
}
|
|
13027
13610
|
function retryCommand(program2) {
|
|
13028
13611
|
program2.command("retry <prdName>").description("Move a completed PRD from done/ back to pending").action((prdName) => {
|
|
13029
13612
|
const projectDir = process.cwd();
|
|
13030
13613
|
const config = loadConfig(projectDir);
|
|
13031
|
-
const prdDir =
|
|
13032
|
-
const doneDir =
|
|
13614
|
+
const prdDir = path34.join(projectDir, config.prdDir);
|
|
13615
|
+
const doneDir = path34.join(prdDir, "done");
|
|
13033
13616
|
const normalizedPrdName = normalizePrdName(prdName);
|
|
13034
|
-
const pendingPath =
|
|
13035
|
-
if (
|
|
13617
|
+
const pendingPath = path34.join(prdDir, normalizedPrdName);
|
|
13618
|
+
if (fs33.existsSync(pendingPath)) {
|
|
13036
13619
|
info(`"${normalizedPrdName}" is already pending, nothing to retry.`);
|
|
13037
13620
|
return;
|
|
13038
13621
|
}
|
|
13039
|
-
const donePath =
|
|
13040
|
-
if (
|
|
13041
|
-
|
|
13622
|
+
const donePath = path34.join(doneDir, normalizedPrdName);
|
|
13623
|
+
if (fs33.existsSync(donePath)) {
|
|
13624
|
+
fs33.renameSync(donePath, pendingPath);
|
|
13042
13625
|
success(`Moved "${normalizedPrdName}" back to pending.`);
|
|
13043
13626
|
dim(`From: ${donePath}`);
|
|
13044
13627
|
dim(`To: ${pendingPath}`);
|
|
@@ -13212,7 +13795,7 @@ function prdsCommand(program2) {
|
|
|
13212
13795
|
const config = loadConfig(projectDir);
|
|
13213
13796
|
const prds = collectPrdInfo(projectDir, config.prdDir, config.maxRuntime);
|
|
13214
13797
|
const openPrBranches = getOpenPrBranches(projectDir);
|
|
13215
|
-
const filteredPrds = prds
|
|
13798
|
+
const filteredPrds = prds;
|
|
13216
13799
|
for (const prd of filteredPrds) {
|
|
13217
13800
|
if (prd.status !== "done") {
|
|
13218
13801
|
const matchingPr = findMatchingPr(prd.name, openPrBranches, config.branchPrefix);
|
|
@@ -13320,7 +13903,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
13320
13903
|
const pid = lockStatus.pid;
|
|
13321
13904
|
if (!lockStatus.running) {
|
|
13322
13905
|
try {
|
|
13323
|
-
|
|
13906
|
+
fs34.unlinkSync(lockPath);
|
|
13324
13907
|
return {
|
|
13325
13908
|
success: true,
|
|
13326
13909
|
message: `${processType} is not running (cleaned up stale lock file for PID ${pid})`,
|
|
@@ -13358,7 +13941,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
13358
13941
|
await sleep3(3e3);
|
|
13359
13942
|
if (!isProcessRunning3(pid)) {
|
|
13360
13943
|
try {
|
|
13361
|
-
|
|
13944
|
+
fs34.unlinkSync(lockPath);
|
|
13362
13945
|
} catch {
|
|
13363
13946
|
}
|
|
13364
13947
|
return {
|
|
@@ -13393,7 +13976,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
13393
13976
|
await sleep3(500);
|
|
13394
13977
|
if (!isProcessRunning3(pid)) {
|
|
13395
13978
|
try {
|
|
13396
|
-
|
|
13979
|
+
fs34.unlinkSync(lockPath);
|
|
13397
13980
|
} catch {
|
|
13398
13981
|
}
|
|
13399
13982
|
return {
|
|
@@ -13460,24 +14043,24 @@ function plannerLockPath2(projectDir) {
|
|
|
13460
14043
|
}
|
|
13461
14044
|
function acquirePlannerLock(projectDir) {
|
|
13462
14045
|
const lockFile = plannerLockPath2(projectDir);
|
|
13463
|
-
if (
|
|
13464
|
-
const pidRaw =
|
|
14046
|
+
if (fs35.existsSync(lockFile)) {
|
|
14047
|
+
const pidRaw = fs35.readFileSync(lockFile, "utf-8").trim();
|
|
13465
14048
|
const pid = parseInt(pidRaw, 10);
|
|
13466
14049
|
if (!Number.isNaN(pid) && isProcessRunning(pid)) {
|
|
13467
14050
|
return { acquired: false, lockFile, pid };
|
|
13468
14051
|
}
|
|
13469
14052
|
try {
|
|
13470
|
-
|
|
14053
|
+
fs35.unlinkSync(lockFile);
|
|
13471
14054
|
} catch {
|
|
13472
14055
|
}
|
|
13473
14056
|
}
|
|
13474
|
-
|
|
14057
|
+
fs35.writeFileSync(lockFile, String(process.pid));
|
|
13475
14058
|
return { acquired: true, lockFile };
|
|
13476
14059
|
}
|
|
13477
14060
|
function releasePlannerLock(lockFile) {
|
|
13478
14061
|
try {
|
|
13479
|
-
if (
|
|
13480
|
-
|
|
14062
|
+
if (fs35.existsSync(lockFile)) {
|
|
14063
|
+
fs35.unlinkSync(lockFile);
|
|
13481
14064
|
}
|
|
13482
14065
|
} catch {
|
|
13483
14066
|
}
|
|
@@ -13599,7 +14182,7 @@ function sliceCommand(program2) {
|
|
|
13599
14182
|
if (!options.dryRun) {
|
|
13600
14183
|
await sendNotifications(config, {
|
|
13601
14184
|
event: "run_started",
|
|
13602
|
-
projectName:
|
|
14185
|
+
projectName: path35.basename(projectDir),
|
|
13603
14186
|
exitCode: 0,
|
|
13604
14187
|
provider: config.provider
|
|
13605
14188
|
});
|
|
@@ -13619,7 +14202,7 @@ function sliceCommand(program2) {
|
|
|
13619
14202
|
if (!options.dryRun && result.sliced) {
|
|
13620
14203
|
await sendNotifications(config, {
|
|
13621
14204
|
event: "run_succeeded",
|
|
13622
|
-
projectName:
|
|
14205
|
+
projectName: path35.basename(projectDir),
|
|
13623
14206
|
exitCode,
|
|
13624
14207
|
provider: config.provider,
|
|
13625
14208
|
prTitle: result.item?.title
|
|
@@ -13627,7 +14210,7 @@ function sliceCommand(program2) {
|
|
|
13627
14210
|
} else if (!options.dryRun && !nothingPending) {
|
|
13628
14211
|
await sendNotifications(config, {
|
|
13629
14212
|
event: "run_failed",
|
|
13630
|
-
projectName:
|
|
14213
|
+
projectName: path35.basename(projectDir),
|
|
13631
14214
|
exitCode,
|
|
13632
14215
|
provider: config.provider
|
|
13633
14216
|
});
|
|
@@ -13645,13 +14228,13 @@ function createStateCommand() {
|
|
|
13645
14228
|
const state = new Command("state");
|
|
13646
14229
|
state.description("Manage Night Watch state");
|
|
13647
14230
|
state.command("migrate").description("Migrate legacy JSON state files to SQLite").option("--dry-run", "Show what would be migrated without making changes").action((opts) => {
|
|
13648
|
-
const nightWatchHome = process.env.NIGHT_WATCH_HOME ||
|
|
14231
|
+
const nightWatchHome = process.env.NIGHT_WATCH_HOME || path36.join(os6.homedir(), GLOBAL_CONFIG_DIR);
|
|
13649
14232
|
if (opts.dryRun) {
|
|
13650
14233
|
console.log(chalk5.cyan("Dry-run mode: no changes will be made.\n"));
|
|
13651
14234
|
console.log(`Legacy JSON files that would be migrated from: ${chalk5.bold(nightWatchHome)}`);
|
|
13652
|
-
console.log(` ${
|
|
13653
|
-
console.log(` ${
|
|
13654
|
-
console.log(` ${
|
|
14235
|
+
console.log(` ${path36.join(nightWatchHome, "projects.json")}`);
|
|
14236
|
+
console.log(` ${path36.join(nightWatchHome, "history.json")}`);
|
|
14237
|
+
console.log(` ${path36.join(nightWatchHome, "prd-states.json")}`);
|
|
13655
14238
|
console.log(` <project>/<prdDir>/.roadmap-state.json (per project)`);
|
|
13656
14239
|
console.log(chalk5.dim("\nRun without --dry-run to apply the migration."));
|
|
13657
14240
|
return;
|
|
@@ -13699,7 +14282,7 @@ function getProvider(config, cwd) {
|
|
|
13699
14282
|
return createBoardProvider(bp, cwd);
|
|
13700
14283
|
}
|
|
13701
14284
|
function defaultBoardTitle(cwd) {
|
|
13702
|
-
return `${
|
|
14285
|
+
return `${path37.basename(cwd)} Night Watch`;
|
|
13703
14286
|
}
|
|
13704
14287
|
async function ensureBoardConfigured(config, cwd, provider, options) {
|
|
13705
14288
|
if (config.boardProvider?.projectNumber) {
|
|
@@ -13739,7 +14322,7 @@ async function confirmPrompt(question) {
|
|
|
13739
14322
|
}
|
|
13740
14323
|
async function createGitHubLabel(label2, cwd) {
|
|
13741
14324
|
try {
|
|
13742
|
-
|
|
14325
|
+
execFileSync6("gh", ["label", "create", label2.name, "--description", label2.description, "--color", label2.color], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
13743
14326
|
return { created: true, skipped: false };
|
|
13744
14327
|
} catch (err) {
|
|
13745
14328
|
const output = err instanceof Error ? err.message : String(err);
|
|
@@ -13879,11 +14462,11 @@ function boardCommand(program2) {
|
|
|
13879
14462
|
let body = options.body ?? "";
|
|
13880
14463
|
if (options.bodyFile) {
|
|
13881
14464
|
const filePath = options.bodyFile;
|
|
13882
|
-
if (!
|
|
14465
|
+
if (!fs36.existsSync(filePath)) {
|
|
13883
14466
|
console.error(`File not found: ${filePath}`);
|
|
13884
14467
|
process.exit(1);
|
|
13885
14468
|
}
|
|
13886
|
-
body =
|
|
14469
|
+
body = fs36.readFileSync(filePath, "utf-8");
|
|
13887
14470
|
}
|
|
13888
14471
|
const labels = [];
|
|
13889
14472
|
if (options.label) {
|
|
@@ -14091,12 +14674,12 @@ function boardCommand(program2) {
|
|
|
14091
14674
|
const config = loadConfig(cwd);
|
|
14092
14675
|
const provider = getProvider(config, cwd);
|
|
14093
14676
|
await ensureBoardConfigured(config, cwd, provider);
|
|
14094
|
-
const roadmapPath = options.roadmap ??
|
|
14095
|
-
if (!
|
|
14677
|
+
const roadmapPath = options.roadmap ?? path37.join(cwd, "ROADMAP.md");
|
|
14678
|
+
if (!fs36.existsSync(roadmapPath)) {
|
|
14096
14679
|
console.error(`Roadmap file not found: ${roadmapPath}`);
|
|
14097
14680
|
process.exit(1);
|
|
14098
14681
|
}
|
|
14099
|
-
const roadmapContent =
|
|
14682
|
+
const roadmapContent = fs36.readFileSync(roadmapPath, "utf-8");
|
|
14100
14683
|
const items = parseRoadmap(roadmapContent);
|
|
14101
14684
|
const uncheckedItems = getUncheckedItems(items);
|
|
14102
14685
|
if (uncheckedItems.length === 0) {
|
|
@@ -14190,7 +14773,7 @@ function boardCommand(program2) {
|
|
|
14190
14773
|
try {
|
|
14191
14774
|
const labelsToAdd = [category, horizon].filter((l) => !issue.labels.includes(l));
|
|
14192
14775
|
if (labelsToAdd.length > 0) {
|
|
14193
|
-
|
|
14776
|
+
execFileSync6("gh", ["issue", "edit", String(issue.number), "--add-label", labelsToAdd.join(",")], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
14194
14777
|
}
|
|
14195
14778
|
updated++;
|
|
14196
14779
|
success(`Updated labels on #${issue.number}: ${item.title}`);
|
|
@@ -14214,14 +14797,14 @@ var __dirname4 = dirname8(__filename3);
|
|
|
14214
14797
|
function findPackageRoot(dir) {
|
|
14215
14798
|
let d = dir;
|
|
14216
14799
|
for (let i = 0; i < 5; i++) {
|
|
14217
|
-
if (
|
|
14800
|
+
if (existsSync30(join33(d, "package.json")))
|
|
14218
14801
|
return d;
|
|
14219
14802
|
d = dirname8(d);
|
|
14220
14803
|
}
|
|
14221
14804
|
return dir;
|
|
14222
14805
|
}
|
|
14223
14806
|
var packageRoot = findPackageRoot(__dirname4);
|
|
14224
|
-
var packageJson = JSON.parse(
|
|
14807
|
+
var packageJson = JSON.parse(readFileSync18(join33(packageRoot, "package.json"), "utf-8"));
|
|
14225
14808
|
var program = new Command2();
|
|
14226
14809
|
program.name("night-watch").description("Autonomous PRD execution using Claude CLI + cron").version(packageJson.version);
|
|
14227
14810
|
initCommand(program);
|