@jonit-dev/night-watch-cli 1.7.49 → 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 -379
- 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 +3 -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 +12 -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
|
@@ -5,8 +5,6 @@ import 'reflect-metadata';
|
|
|
5
5
|
import "reflect-metadata";
|
|
6
6
|
import "reflect-metadata";
|
|
7
7
|
import "reflect-metadata";
|
|
8
|
-
import "reflect-metadata";
|
|
9
|
-
import "reflect-metadata";
|
|
10
8
|
import * as fs from "fs";
|
|
11
9
|
import * as path from "path";
|
|
12
10
|
import { fileURLToPath } from "url";
|
|
@@ -48,115 +46,127 @@ import * as fs6 from "fs";
|
|
|
48
46
|
import * as fs7 from "fs";
|
|
49
47
|
import * as path6 from "path";
|
|
50
48
|
import { execSync as execSync2 } from "child_process";
|
|
51
|
-
import * as fs8 from "fs";
|
|
52
|
-
import * as path7 from "path";
|
|
53
49
|
import * as os3 from "os";
|
|
50
|
+
import * as path7 from "path";
|
|
51
|
+
import * as fs8 from "fs";
|
|
52
|
+
import * as fs9 from "fs";
|
|
54
53
|
import * as path8 from "path";
|
|
54
|
+
import * as os4 from "os";
|
|
55
|
+
import * as path9 from "path";
|
|
55
56
|
import { execFileSync } from "child_process";
|
|
57
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
58
|
+
import * as fs10 from "fs";
|
|
56
59
|
import chalk from "chalk";
|
|
57
60
|
import ora from "ora";
|
|
58
61
|
import Table from "cli-table3";
|
|
59
|
-
import
|
|
60
|
-
import * as fs10 from "fs";
|
|
61
|
-
import * as os4 from "os";
|
|
62
|
-
import * as path9 from "path";
|
|
63
|
-
import * as crypto from "crypto";
|
|
62
|
+
import { execFileSync as execFileSync3 } from "child_process";
|
|
64
63
|
import * as fs11 from "fs";
|
|
65
64
|
import * as path10 from "path";
|
|
66
65
|
import * as fs12 from "fs";
|
|
67
66
|
import * as path11 from "path";
|
|
68
67
|
import * as fs13 from "fs";
|
|
68
|
+
import * as os5 from "os";
|
|
69
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";
|
|
70
77
|
import { spawn } from "child_process";
|
|
71
78
|
import { createHash as createHash3 } from "crypto";
|
|
72
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";
|
|
73
83
|
import "reflect-metadata";
|
|
74
84
|
import { Command as Command2 } from "commander";
|
|
75
|
-
import { existsSync as
|
|
85
|
+
import { existsSync as existsSync30, readFileSync as readFileSync18 } from "fs";
|
|
76
86
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
77
|
-
import { dirname as dirname8, join as
|
|
78
|
-
import
|
|
79
|
-
import
|
|
87
|
+
import { dirname as dirname8, join as join33 } from "path";
|
|
88
|
+
import fs18 from "fs";
|
|
89
|
+
import path17 from "path";
|
|
80
90
|
import { execSync as execSync3 } from "child_process";
|
|
81
91
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
82
|
-
import { dirname as dirname4, join as
|
|
92
|
+
import { dirname as dirname4, join as join16 } from "path";
|
|
83
93
|
import * as readline from "readline";
|
|
84
|
-
import * as
|
|
85
|
-
import * as path14 from "path";
|
|
86
|
-
import { execFileSync as execFileSync2 } from "child_process";
|
|
87
|
-
import * as path15 from "path";
|
|
88
|
-
import * as path16 from "path";
|
|
89
|
-
import * as fs16 from "fs";
|
|
90
|
-
import * as path17 from "path";
|
|
91
|
-
import { execSync as execSync4 } from "child_process";
|
|
94
|
+
import * as fs19 from "fs";
|
|
92
95
|
import * as path18 from "path";
|
|
93
|
-
import
|
|
96
|
+
import { execFileSync as execFileSync5 } from "child_process";
|
|
94
97
|
import * as path19 from "path";
|
|
95
|
-
import * as fs18 from "fs";
|
|
96
|
-
import chalk2 from "chalk";
|
|
97
|
-
import { spawn as spawn3 } from "child_process";
|
|
98
98
|
import * as path20 from "path";
|
|
99
|
-
import * as fs19 from "fs";
|
|
100
99
|
import * as fs20 from "fs";
|
|
101
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";
|
|
102
112
|
import * as readline2 from "readline";
|
|
103
113
|
import blessed6 from "blessed";
|
|
104
114
|
import blessed from "blessed";
|
|
105
|
-
import * as
|
|
115
|
+
import * as fs25 from "fs";
|
|
106
116
|
import blessed2 from "blessed";
|
|
107
117
|
import blessed3 from "blessed";
|
|
108
118
|
import cronstrue from "cronstrue";
|
|
109
119
|
import blessed4 from "blessed";
|
|
110
120
|
import { spawn as spawn4 } from "child_process";
|
|
111
121
|
import blessed5 from "blessed";
|
|
112
|
-
import * as fs22 from "fs";
|
|
113
|
-
import * as path22 from "path";
|
|
114
|
-
import * as fs27 from "fs";
|
|
115
122
|
import * as fs26 from "fs";
|
|
116
|
-
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";
|
|
117
127
|
import { dirname as dirname7 } from "path";
|
|
118
128
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
119
129
|
import cors from "cors";
|
|
120
130
|
import express from "express";
|
|
121
|
-
import * as
|
|
122
|
-
import * as
|
|
123
|
-
import * as
|
|
124
|
-
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";
|
|
125
135
|
import { spawn as spawn5 } from "child_process";
|
|
126
136
|
import { Router } from "express";
|
|
127
137
|
import { Router as Router2 } from "express";
|
|
128
138
|
import { Router as Router3 } from "express";
|
|
129
139
|
import { Router as Router4 } from "express";
|
|
130
|
-
import * as
|
|
131
|
-
import * as
|
|
140
|
+
import * as fs29 from "fs";
|
|
141
|
+
import * as path29 from "path";
|
|
132
142
|
import { execSync as execSync5 } from "child_process";
|
|
133
143
|
import { Router as Router5 } from "express";
|
|
134
|
-
import * as
|
|
144
|
+
import * as path30 from "path";
|
|
135
145
|
import { Router as Router6 } from "express";
|
|
136
146
|
import { Router as Router7 } from "express";
|
|
137
|
-
import * as
|
|
147
|
+
import * as path31 from "path";
|
|
138
148
|
import { Router as Router8 } from "express";
|
|
139
149
|
import { Router as Router9 } from "express";
|
|
140
150
|
import { CronExpressionParser } from "cron-parser";
|
|
141
151
|
import { spawnSync } from "child_process";
|
|
142
|
-
import * as
|
|
143
|
-
import * as
|
|
144
|
-
import * as
|
|
145
|
-
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";
|
|
146
156
|
import chalk3 from "chalk";
|
|
147
157
|
import chalk4 from "chalk";
|
|
148
158
|
import { execSync as execSync6 } from "child_process";
|
|
149
|
-
import * as
|
|
159
|
+
import * as fs34 from "fs";
|
|
150
160
|
import * as readline3 from "readline";
|
|
151
|
-
import * as
|
|
152
|
-
import * as
|
|
153
|
-
import * as
|
|
154
|
-
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";
|
|
155
165
|
import chalk5 from "chalk";
|
|
156
166
|
import { Command } from "commander";
|
|
157
|
-
import { execFileSync as
|
|
158
|
-
import * as
|
|
159
|
-
import * as
|
|
167
|
+
import { execFileSync as execFileSync6 } from "child_process";
|
|
168
|
+
import * as fs36 from "fs";
|
|
169
|
+
import * as path37 from "path";
|
|
160
170
|
import * as readline4 from "readline";
|
|
161
171
|
import chalk6 from "chalk";
|
|
162
172
|
var __defProp = Object.defineProperty;
|
|
@@ -3455,101 +3465,107 @@ function findMatchingIssue(targetTitle, issues, threshold = 0.8) {
|
|
|
3455
3465
|
}
|
|
3456
3466
|
return bestMatch;
|
|
3457
3467
|
}
|
|
3468
|
+
var HORIZON_SHORT_TERM;
|
|
3469
|
+
var HORIZON_MEDIUM_TERM;
|
|
3470
|
+
var HORIZON_LONG_TERM;
|
|
3458
3471
|
var ROADMAP_SECTION_MAPPINGS;
|
|
3459
3472
|
var init_roadmap_mapping = __esm({
|
|
3460
3473
|
"../core/dist/board/roadmap-mapping.js"() {
|
|
3461
3474
|
"use strict";
|
|
3475
|
+
HORIZON_SHORT_TERM = "short-term";
|
|
3476
|
+
HORIZON_MEDIUM_TERM = "medium-term";
|
|
3477
|
+
HORIZON_LONG_TERM = "long-term";
|
|
3462
3478
|
ROADMAP_SECTION_MAPPINGS = [
|
|
3463
3479
|
{
|
|
3464
3480
|
sectionPattern: /§1.*Reliability.*correctness/i,
|
|
3465
3481
|
category: "reliability",
|
|
3466
|
-
horizon:
|
|
3482
|
+
horizon: HORIZON_SHORT_TERM
|
|
3467
3483
|
},
|
|
3468
3484
|
{
|
|
3469
3485
|
sectionPattern: /§2.*Quality.*developer/i,
|
|
3470
3486
|
category: "quality",
|
|
3471
|
-
horizon:
|
|
3487
|
+
horizon: HORIZON_SHORT_TERM
|
|
3472
3488
|
},
|
|
3473
3489
|
{
|
|
3474
3490
|
sectionPattern: /§3.*Product.*operators/i,
|
|
3475
3491
|
category: "product",
|
|
3476
|
-
horizon:
|
|
3492
|
+
horizon: HORIZON_SHORT_TERM
|
|
3477
3493
|
},
|
|
3478
3494
|
{
|
|
3479
3495
|
sectionPattern: /§4.*Unified.*operations/i,
|
|
3480
3496
|
category: "ux",
|
|
3481
|
-
horizon:
|
|
3497
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3482
3498
|
},
|
|
3483
3499
|
{
|
|
3484
3500
|
sectionPattern: /§5.*Provider.*execution/i,
|
|
3485
3501
|
category: "provider",
|
|
3486
|
-
horizon:
|
|
3502
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3487
3503
|
},
|
|
3488
3504
|
{
|
|
3489
3505
|
sectionPattern: /§6.*Team.*multi-project/i,
|
|
3490
3506
|
category: "team",
|
|
3491
|
-
horizon:
|
|
3507
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3492
3508
|
},
|
|
3493
3509
|
{
|
|
3494
3510
|
sectionPattern: /§7.*Platformization.*enterprise/i,
|
|
3495
3511
|
category: "platform",
|
|
3496
|
-
horizon:
|
|
3512
|
+
horizon: HORIZON_LONG_TERM
|
|
3497
3513
|
},
|
|
3498
3514
|
{
|
|
3499
3515
|
sectionPattern: /§8.*Intelligence.*autonomous/i,
|
|
3500
3516
|
category: "intelligence",
|
|
3501
|
-
horizon:
|
|
3517
|
+
horizon: HORIZON_LONG_TERM
|
|
3502
3518
|
},
|
|
3503
3519
|
{
|
|
3504
3520
|
sectionPattern: /§9.*Ecosystem.*adoption/i,
|
|
3505
3521
|
category: "ecosystem",
|
|
3506
|
-
horizon:
|
|
3522
|
+
horizon: HORIZON_LONG_TERM
|
|
3507
3523
|
},
|
|
3508
3524
|
// Fallback patterns without section numbers
|
|
3509
3525
|
{
|
|
3510
3526
|
sectionPattern: /Reliability.*correctness/i,
|
|
3511
3527
|
category: "reliability",
|
|
3512
|
-
horizon:
|
|
3528
|
+
horizon: HORIZON_SHORT_TERM
|
|
3513
3529
|
},
|
|
3514
3530
|
{
|
|
3515
3531
|
sectionPattern: /Quality.*developer.*workflow/i,
|
|
3516
3532
|
category: "quality",
|
|
3517
|
-
horizon:
|
|
3533
|
+
horizon: HORIZON_SHORT_TERM
|
|
3518
3534
|
},
|
|
3519
3535
|
{
|
|
3520
3536
|
sectionPattern: /Product.*completeness/i,
|
|
3521
3537
|
category: "product",
|
|
3522
|
-
horizon:
|
|
3538
|
+
horizon: HORIZON_SHORT_TERM
|
|
3523
3539
|
},
|
|
3524
3540
|
{
|
|
3525
3541
|
sectionPattern: /Unified.*operations/i,
|
|
3526
3542
|
category: "ux",
|
|
3527
|
-
horizon:
|
|
3543
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3528
3544
|
},
|
|
3529
3545
|
{
|
|
3530
3546
|
sectionPattern: /Provider.*execution/i,
|
|
3531
3547
|
category: "provider",
|
|
3532
|
-
horizon:
|
|
3548
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3533
3549
|
},
|
|
3534
3550
|
{
|
|
3535
3551
|
sectionPattern: /Team.*multi-project/i,
|
|
3536
3552
|
category: "team",
|
|
3537
|
-
horizon:
|
|
3553
|
+
horizon: HORIZON_MEDIUM_TERM
|
|
3538
3554
|
},
|
|
3539
3555
|
{
|
|
3540
3556
|
sectionPattern: /Platformization.*enterprise/i,
|
|
3541
3557
|
category: "platform",
|
|
3542
|
-
horizon:
|
|
3558
|
+
horizon: HORIZON_LONG_TERM
|
|
3543
3559
|
},
|
|
3544
3560
|
{
|
|
3545
3561
|
sectionPattern: /Intelligence.*autonomous/i,
|
|
3546
3562
|
category: "intelligence",
|
|
3547
|
-
horizon:
|
|
3563
|
+
horizon: HORIZON_LONG_TERM
|
|
3548
3564
|
},
|
|
3549
3565
|
{
|
|
3550
3566
|
sectionPattern: /Ecosystem.*adoption/i,
|
|
3551
3567
|
category: "ecosystem",
|
|
3552
|
-
horizon:
|
|
3568
|
+
horizon: HORIZON_LONG_TERM
|
|
3553
3569
|
}
|
|
3554
3570
|
];
|
|
3555
3571
|
}
|
|
@@ -4288,6 +4304,34 @@ function checkLockFile(lockPath) {
|
|
|
4288
4304
|
return { running: false, pid: null };
|
|
4289
4305
|
}
|
|
4290
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
|
+
}
|
|
4291
4335
|
function countPRDs(projectDir, prdDir, maxRuntime) {
|
|
4292
4336
|
const fullPrdPath = path5.join(projectDir, prdDir);
|
|
4293
4337
|
if (!fs5.existsSync(fullPrdPath)) {
|
|
@@ -4879,7 +4923,7 @@ function checkPrdDirectory(projectDir, prdDir) {
|
|
|
4879
4923
|
}
|
|
4880
4924
|
};
|
|
4881
4925
|
}
|
|
4882
|
-
const prds = fs7.readdirSync(prdPath).filter((f) => f.endsWith(".md")
|
|
4926
|
+
const prds = fs7.readdirSync(prdPath).filter((f) => f.endsWith(".md"));
|
|
4883
4927
|
return {
|
|
4884
4928
|
passed: true,
|
|
4885
4929
|
message: `PRD directory found: ${prdDir} (${prds.length} PRDs)`,
|
|
@@ -4952,12 +4996,90 @@ var init_checks = __esm({
|
|
|
4952
4996
|
init_constants();
|
|
4953
4997
|
}
|
|
4954
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
|
+
});
|
|
4955
5077
|
function saveConfig(projectDir, changes) {
|
|
4956
|
-
const configPath =
|
|
5078
|
+
const configPath = path8.join(projectDir, CONFIG_FILE_NAME);
|
|
4957
5079
|
try {
|
|
4958
5080
|
let existing = {};
|
|
4959
|
-
if (
|
|
4960
|
-
const content =
|
|
5081
|
+
if (fs9.existsSync(configPath)) {
|
|
5082
|
+
const content = fs9.readFileSync(configPath, "utf-8");
|
|
4961
5083
|
existing = JSON.parse(content);
|
|
4962
5084
|
}
|
|
4963
5085
|
const merged = { ...existing };
|
|
@@ -4970,7 +5092,7 @@ function saveConfig(projectDir, changes) {
|
|
|
4970
5092
|
}
|
|
4971
5093
|
}
|
|
4972
5094
|
}
|
|
4973
|
-
|
|
5095
|
+
fs9.writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n");
|
|
4974
5096
|
return { success: true };
|
|
4975
5097
|
} catch (err) {
|
|
4976
5098
|
return {
|
|
@@ -4986,8 +5108,8 @@ var init_config_writer = __esm({
|
|
|
4986
5108
|
}
|
|
4987
5109
|
});
|
|
4988
5110
|
function getHistoryPath() {
|
|
4989
|
-
const base = process.env.NIGHT_WATCH_HOME ||
|
|
4990
|
-
return
|
|
5111
|
+
const base = process.env.NIGHT_WATCH_HOME || path9.join(os4.homedir(), GLOBAL_CONFIG_DIR);
|
|
5112
|
+
return path9.join(base, HISTORY_FILE_NAME);
|
|
4991
5113
|
}
|
|
4992
5114
|
function loadHistory() {
|
|
4993
5115
|
const { executionHistory } = getRepositories();
|
|
@@ -4998,7 +5120,7 @@ function saveHistory(history) {
|
|
|
4998
5120
|
executionHistory.replaceAll(history);
|
|
4999
5121
|
}
|
|
5000
5122
|
function recordExecution(projectDir, prdFile, outcome, exitCode, attempt = 1) {
|
|
5001
|
-
const resolved =
|
|
5123
|
+
const resolved = path9.resolve(projectDir);
|
|
5002
5124
|
const { executionHistory } = getRepositories();
|
|
5003
5125
|
const record = {
|
|
5004
5126
|
timestamp: Math.floor(Date.now() / 1e3),
|
|
@@ -5010,7 +5132,7 @@ function recordExecution(projectDir, prdFile, outcome, exitCode, attempt = 1) {
|
|
|
5010
5132
|
executionHistory.trimRecords(resolved, prdFile, MAX_HISTORY_RECORDS_PER_PRD);
|
|
5011
5133
|
}
|
|
5012
5134
|
function getLastExecution(projectDir, prdFile) {
|
|
5013
|
-
const resolved =
|
|
5135
|
+
const resolved = path9.resolve(projectDir);
|
|
5014
5136
|
const { executionHistory } = getRepositories();
|
|
5015
5137
|
const records = executionHistory.getRecords(resolved, prdFile);
|
|
5016
5138
|
return records.length > 0 ? records[0] : null;
|
|
@@ -5035,6 +5157,94 @@ var init_execution_history = __esm({
|
|
|
5035
5157
|
init_client();
|
|
5036
5158
|
}
|
|
5037
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
|
+
});
|
|
5038
5248
|
function parsePrDetails(raw) {
|
|
5039
5249
|
try {
|
|
5040
5250
|
const details = JSON.parse(raw);
|
|
@@ -5056,7 +5266,7 @@ function parsePrDetails(raw) {
|
|
|
5056
5266
|
}
|
|
5057
5267
|
function fetchPrBySelector(selector, cwd) {
|
|
5058
5268
|
try {
|
|
5059
|
-
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"] });
|
|
5060
5270
|
return parsePrDetails(output);
|
|
5061
5271
|
} catch {
|
|
5062
5272
|
return null;
|
|
@@ -5070,7 +5280,7 @@ function fetchPrDetailsByNumber(prNumber, cwd) {
|
|
|
5070
5280
|
}
|
|
5071
5281
|
function fetchPrDetails(branchPrefix, cwd) {
|
|
5072
5282
|
try {
|
|
5073
|
-
const listOutput =
|
|
5283
|
+
const listOutput = execFileSync2("gh", ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "20"], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5074
5284
|
const prs = JSON.parse(listOutput);
|
|
5075
5285
|
const matching = prs.filter((pr) => pr.headRefName.startsWith(branchPrefix + "/"));
|
|
5076
5286
|
if (matching.length === 0) {
|
|
@@ -5084,7 +5294,7 @@ function fetchPrDetails(branchPrefix, cwd) {
|
|
|
5084
5294
|
}
|
|
5085
5295
|
function fetchReviewedPrDetails(branchPatterns, cwd) {
|
|
5086
5296
|
try {
|
|
5087
|
-
const listOutput =
|
|
5297
|
+
const listOutput = execFileSync2("gh", ["pr", "list", "--state", "open", "--json", "number,headRefName", "--limit", "20"], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
5088
5298
|
const prs = JSON.parse(listOutput);
|
|
5089
5299
|
const matching = prs.filter((pr) => branchPatterns.some((pattern) => pr.headRefName.startsWith(pattern)));
|
|
5090
5300
|
if (matching.length === 0) {
|
|
@@ -5125,6 +5335,45 @@ var init_github = __esm({
|
|
|
5125
5335
|
"use strict";
|
|
5126
5336
|
}
|
|
5127
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
|
+
});
|
|
5128
5377
|
function success(msg) {
|
|
5129
5378
|
console.log(chalk.green("\u2714"), msg);
|
|
5130
5379
|
}
|
|
@@ -5284,6 +5533,11 @@ function buildDescription(ctx) {
|
|
|
5284
5533
|
if (ctx.duration !== void 0) {
|
|
5285
5534
|
lines.push(`Duration: ${ctx.duration}s`);
|
|
5286
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
|
+
}
|
|
5287
5541
|
if (ctx.event === "review_completed" && ctx.attempts !== void 0 && ctx.attempts > 1) {
|
|
5288
5542
|
const retryInfo = `Attempts: ${ctx.attempts}`;
|
|
5289
5543
|
if (ctx.finalScore !== void 0) {
|
|
@@ -5455,27 +5709,145 @@ var init_notify = __esm({
|
|
|
5455
5709
|
init_github();
|
|
5456
5710
|
}
|
|
5457
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
|
+
});
|
|
5458
5817
|
function slugify(name) {
|
|
5459
5818
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
5460
5819
|
}
|
|
5461
5820
|
function getNextPrdNumber(prdDir) {
|
|
5462
|
-
if (!
|
|
5821
|
+
if (!fs12.existsSync(prdDir))
|
|
5463
5822
|
return 1;
|
|
5464
|
-
const files =
|
|
5823
|
+
const files = fs12.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
|
|
5465
5824
|
const numbers = files.map((f) => {
|
|
5466
5825
|
const match = f.match(/^(\d+)-/);
|
|
5467
5826
|
return match ? parseInt(match[1], 10) : 0;
|
|
5468
5827
|
});
|
|
5469
5828
|
return Math.max(0, ...numbers) + 1;
|
|
5470
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
|
+
}
|
|
5471
5843
|
var init_prd_utils = __esm({
|
|
5472
5844
|
"../core/dist/utils/prd-utils.js"() {
|
|
5473
5845
|
"use strict";
|
|
5474
5846
|
}
|
|
5475
5847
|
});
|
|
5476
5848
|
function getRegistryPath() {
|
|
5477
|
-
const base = process.env.NIGHT_WATCH_HOME ||
|
|
5478
|
-
return
|
|
5849
|
+
const base = process.env.NIGHT_WATCH_HOME || path12.join(os5.homedir(), GLOBAL_CONFIG_DIR);
|
|
5850
|
+
return path12.join(base, REGISTRY_FILE_NAME);
|
|
5479
5851
|
}
|
|
5480
5852
|
function loadRegistry() {
|
|
5481
5853
|
const { projectRegistry } = getRepositories();
|
|
@@ -5489,7 +5861,7 @@ function saveRegistry(entries) {
|
|
|
5489
5861
|
}
|
|
5490
5862
|
}
|
|
5491
5863
|
function registerProject(projectDir) {
|
|
5492
|
-
const resolvedPath =
|
|
5864
|
+
const resolvedPath = path12.resolve(projectDir);
|
|
5493
5865
|
const { projectRegistry } = getRepositories();
|
|
5494
5866
|
const entries = projectRegistry.getAll();
|
|
5495
5867
|
const existing = entries.find((e) => e.path === resolvedPath);
|
|
@@ -5498,13 +5870,13 @@ function registerProject(projectDir) {
|
|
|
5498
5870
|
}
|
|
5499
5871
|
const name = getProjectName(resolvedPath);
|
|
5500
5872
|
const nameExists = entries.some((e) => e.name === name);
|
|
5501
|
-
const finalName = nameExists ? `${name}-${
|
|
5873
|
+
const finalName = nameExists ? `${name}-${path12.basename(resolvedPath)}` : name;
|
|
5502
5874
|
const entry = { name: finalName, path: resolvedPath };
|
|
5503
5875
|
projectRegistry.upsert(entry);
|
|
5504
5876
|
return entry;
|
|
5505
5877
|
}
|
|
5506
5878
|
function unregisterProject(projectDir) {
|
|
5507
|
-
const resolvedPath =
|
|
5879
|
+
const resolvedPath = path12.resolve(projectDir);
|
|
5508
5880
|
const { projectRegistry } = getRepositories();
|
|
5509
5881
|
return projectRegistry.remove(resolvedPath);
|
|
5510
5882
|
}
|
|
@@ -5513,7 +5885,7 @@ function validateRegistry() {
|
|
|
5513
5885
|
const valid = [];
|
|
5514
5886
|
const invalid = [];
|
|
5515
5887
|
for (const entry of entries) {
|
|
5516
|
-
if (
|
|
5888
|
+
if (fs13.existsSync(entry.path) && fs13.existsSync(path12.join(entry.path, CONFIG_FILE_NAME))) {
|
|
5517
5889
|
valid.push(entry);
|
|
5518
5890
|
} else {
|
|
5519
5891
|
invalid.push(entry);
|
|
@@ -5706,15 +6078,15 @@ var init_roadmap_parser = __esm({
|
|
|
5706
6078
|
}
|
|
5707
6079
|
});
|
|
5708
6080
|
function getStateFilePath(prdDir) {
|
|
5709
|
-
return
|
|
6081
|
+
return path13.join(prdDir, STATE_FILE_NAME);
|
|
5710
6082
|
}
|
|
5711
6083
|
function readJsonState(prdDir) {
|
|
5712
6084
|
const statePath = getStateFilePath(prdDir);
|
|
5713
|
-
if (!
|
|
6085
|
+
if (!fs14.existsSync(statePath)) {
|
|
5714
6086
|
return null;
|
|
5715
6087
|
}
|
|
5716
6088
|
try {
|
|
5717
|
-
const content =
|
|
6089
|
+
const content = fs14.readFileSync(statePath, "utf-8");
|
|
5718
6090
|
const parsed = JSON.parse(content);
|
|
5719
6091
|
if (typeof parsed !== "object" || parsed === null) {
|
|
5720
6092
|
return null;
|
|
@@ -5752,11 +6124,11 @@ function saveRoadmapState(prdDir, state) {
|
|
|
5752
6124
|
const { roadmapState } = getRepositories();
|
|
5753
6125
|
roadmapState.save(prdDir, state);
|
|
5754
6126
|
const statePath = getStateFilePath(prdDir);
|
|
5755
|
-
const dir =
|
|
5756
|
-
if (!
|
|
5757
|
-
|
|
6127
|
+
const dir = path13.dirname(statePath);
|
|
6128
|
+
if (!fs14.existsSync(dir)) {
|
|
6129
|
+
fs14.mkdirSync(dir, { recursive: true });
|
|
5758
6130
|
}
|
|
5759
|
-
|
|
6131
|
+
fs14.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf-8");
|
|
5760
6132
|
}
|
|
5761
6133
|
function createEmptyState() {
|
|
5762
6134
|
return {
|
|
@@ -5799,9 +6171,9 @@ function loadSlicerTemplate(templateDir) {
|
|
|
5799
6171
|
if (cachedTemplate) {
|
|
5800
6172
|
return cachedTemplate;
|
|
5801
6173
|
}
|
|
5802
|
-
const templatePath = templateDir ?
|
|
6174
|
+
const templatePath = templateDir ? path14.join(templateDir, "night-watch-slicer.md") : path14.resolve(__dirname, "..", "..", "templates", "night-watch-slicer.md");
|
|
5803
6175
|
try {
|
|
5804
|
-
cachedTemplate =
|
|
6176
|
+
cachedTemplate = fs15.readFileSync(templatePath, "utf-8");
|
|
5805
6177
|
return cachedTemplate;
|
|
5806
6178
|
} catch (error2) {
|
|
5807
6179
|
console.warn(`Warning: Could not load slicer template from ${templatePath}, using default:`, error2 instanceof Error ? error2.message : String(error2));
|
|
@@ -5826,7 +6198,7 @@ function createSlicerPromptVars(title, section, description, prdDir, prdFilename
|
|
|
5826
6198
|
title,
|
|
5827
6199
|
section,
|
|
5828
6200
|
description: description || "(No description provided)",
|
|
5829
|
-
outputFilePath:
|
|
6201
|
+
outputFilePath: path14.join(prdDir, prdFilename),
|
|
5830
6202
|
prdDir
|
|
5831
6203
|
};
|
|
5832
6204
|
}
|
|
@@ -6017,11 +6389,11 @@ function auditFindingToRoadmapItem(finding) {
|
|
|
6017
6389
|
};
|
|
6018
6390
|
}
|
|
6019
6391
|
function collectAuditPlannerItems(projectDir) {
|
|
6020
|
-
const reportPath =
|
|
6021
|
-
if (!
|
|
6392
|
+
const reportPath = path15.join(projectDir, "logs", "audit-report.md");
|
|
6393
|
+
if (!fs16.existsSync(reportPath)) {
|
|
6022
6394
|
return [];
|
|
6023
6395
|
}
|
|
6024
|
-
const reportContent =
|
|
6396
|
+
const reportContent = fs16.readFileSync(reportPath, "utf-8");
|
|
6025
6397
|
if (!reportContent.trim() || /\bNO_ISSUES_FOUND\b/.test(reportContent)) {
|
|
6026
6398
|
return [];
|
|
6027
6399
|
}
|
|
@@ -6030,9 +6402,9 @@ function collectAuditPlannerItems(projectDir) {
|
|
|
6030
6402
|
return findings.map(auditFindingToRoadmapItem);
|
|
6031
6403
|
}
|
|
6032
6404
|
function getRoadmapStatus(projectDir, config) {
|
|
6033
|
-
const roadmapPath =
|
|
6405
|
+
const roadmapPath = path15.join(projectDir, config.roadmapScanner.roadmapPath);
|
|
6034
6406
|
const scannerEnabled = config.roadmapScanner.enabled;
|
|
6035
|
-
if (!
|
|
6407
|
+
if (!fs16.existsSync(roadmapPath)) {
|
|
6036
6408
|
return {
|
|
6037
6409
|
found: false,
|
|
6038
6410
|
enabled: scannerEnabled,
|
|
@@ -6043,9 +6415,9 @@ function getRoadmapStatus(projectDir, config) {
|
|
|
6043
6415
|
items: []
|
|
6044
6416
|
};
|
|
6045
6417
|
}
|
|
6046
|
-
const content =
|
|
6418
|
+
const content = fs16.readFileSync(roadmapPath, "utf-8");
|
|
6047
6419
|
const items = parseRoadmap(content);
|
|
6048
|
-
const prdDir =
|
|
6420
|
+
const prdDir = path15.join(projectDir, config.prdDir);
|
|
6049
6421
|
const state = loadRoadmapState(prdDir);
|
|
6050
6422
|
const existingPrdSlugs = scanExistingPrdSlugs(prdDir);
|
|
6051
6423
|
const statusItems = items.map((item) => {
|
|
@@ -6082,12 +6454,12 @@ function getRoadmapStatus(projectDir, config) {
|
|
|
6082
6454
|
}
|
|
6083
6455
|
function scanExistingPrdSlugs(prdDir) {
|
|
6084
6456
|
const slugs = /* @__PURE__ */ new Set();
|
|
6085
|
-
if (!
|
|
6457
|
+
if (!fs16.existsSync(prdDir)) {
|
|
6086
6458
|
return slugs;
|
|
6087
6459
|
}
|
|
6088
|
-
const files =
|
|
6460
|
+
const files = fs16.readdirSync(prdDir);
|
|
6089
6461
|
for (const file of files) {
|
|
6090
|
-
if (!file.endsWith(".md")
|
|
6462
|
+
if (!file.endsWith(".md")) {
|
|
6091
6463
|
continue;
|
|
6092
6464
|
}
|
|
6093
6465
|
const match = file.match(/^\d+-(.+)\.md$/);
|
|
@@ -6121,19 +6493,19 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
|
|
|
6121
6493
|
const nextNum = getNextPrdNumber(prdDir);
|
|
6122
6494
|
const padded = String(nextNum).padStart(2, "0");
|
|
6123
6495
|
const filename = `${padded}-${itemSlug}.md`;
|
|
6124
|
-
const filePath =
|
|
6125
|
-
if (!
|
|
6126
|
-
|
|
6496
|
+
const filePath = path15.join(prdDir, filename);
|
|
6497
|
+
if (!fs16.existsSync(prdDir)) {
|
|
6498
|
+
fs16.mkdirSync(prdDir, { recursive: true });
|
|
6127
6499
|
}
|
|
6128
6500
|
const promptVars = createSlicerPromptVars(item.title, item.section, item.description, prdDir, filename);
|
|
6129
6501
|
const prompt2 = renderSlicerPrompt(promptVars);
|
|
6130
6502
|
const providerArgs = buildProviderArgs(config.provider, prompt2);
|
|
6131
|
-
const logDir =
|
|
6132
|
-
if (!
|
|
6133
|
-
|
|
6503
|
+
const logDir = path15.join(projectDir, "logs");
|
|
6504
|
+
if (!fs16.existsSync(logDir)) {
|
|
6505
|
+
fs16.mkdirSync(logDir, { recursive: true });
|
|
6134
6506
|
}
|
|
6135
|
-
const logFile =
|
|
6136
|
-
const logStream =
|
|
6507
|
+
const logFile = path15.join(logDir, `slicer-${itemSlug}.log`);
|
|
6508
|
+
const logStream = fs16.createWriteStream(logFile, { flags: "w" });
|
|
6137
6509
|
logStream.on("error", () => {
|
|
6138
6510
|
});
|
|
6139
6511
|
return new Promise((resolve9) => {
|
|
@@ -6170,7 +6542,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
|
|
|
6170
6542
|
});
|
|
6171
6543
|
return;
|
|
6172
6544
|
}
|
|
6173
|
-
if (!
|
|
6545
|
+
if (!fs16.existsSync(filePath)) {
|
|
6174
6546
|
resolve9({
|
|
6175
6547
|
sliced: false,
|
|
6176
6548
|
error: `Provider did not create expected file: ${filePath}`,
|
|
@@ -6193,23 +6565,23 @@ async function sliceNextItem(projectDir, config) {
|
|
|
6193
6565
|
error: "Roadmap scanner is disabled"
|
|
6194
6566
|
};
|
|
6195
6567
|
}
|
|
6196
|
-
const roadmapPath =
|
|
6568
|
+
const roadmapPath = path15.join(projectDir, config.roadmapScanner.roadmapPath);
|
|
6197
6569
|
const auditItems = collectAuditPlannerItems(projectDir);
|
|
6198
|
-
const roadmapExists =
|
|
6570
|
+
const roadmapExists = fs16.existsSync(roadmapPath);
|
|
6199
6571
|
if (!roadmapExists && auditItems.length === 0) {
|
|
6200
6572
|
return {
|
|
6201
6573
|
sliced: false,
|
|
6202
6574
|
error: "ROADMAP.md not found"
|
|
6203
6575
|
};
|
|
6204
6576
|
}
|
|
6205
|
-
const roadmapItems = roadmapExists ? parseRoadmap(
|
|
6577
|
+
const roadmapItems = roadmapExists ? parseRoadmap(fs16.readFileSync(roadmapPath, "utf-8")) : [];
|
|
6206
6578
|
if (roadmapExists && roadmapItems.length === 0 && auditItems.length === 0) {
|
|
6207
6579
|
return {
|
|
6208
6580
|
sliced: false,
|
|
6209
6581
|
error: "No items in roadmap"
|
|
6210
6582
|
};
|
|
6211
6583
|
}
|
|
6212
|
-
const prdDir =
|
|
6584
|
+
const prdDir = path15.join(projectDir, config.prdDir);
|
|
6213
6585
|
const state = loadRoadmapState(prdDir);
|
|
6214
6586
|
const existingPrdSlugs = scanExistingPrdSlugs(prdDir);
|
|
6215
6587
|
const pickEligibleItem = (items) => {
|
|
@@ -6425,6 +6797,162 @@ var init_webhook_validator = __esm({
|
|
|
6425
6797
|
"use strict";
|
|
6426
6798
|
}
|
|
6427
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
|
+
});
|
|
6428
6956
|
function renderDependsOn(deps) {
|
|
6429
6957
|
if (deps.length === 0) {
|
|
6430
6958
|
return "";
|
|
@@ -6680,6 +7208,7 @@ __export(dist_exports, {
|
|
|
6680
7208
|
VALID_JOB_TYPES: () => VALID_JOB_TYPES,
|
|
6681
7209
|
VALID_MERGE_METHODS: () => VALID_MERGE_METHODS,
|
|
6682
7210
|
VALID_PROVIDERS: () => VALID_PROVIDERS,
|
|
7211
|
+
acquireLock: () => acquireLock,
|
|
6683
7212
|
addEntry: () => addEntry,
|
|
6684
7213
|
auditLockPath: () => auditLockPath,
|
|
6685
7214
|
buildDescription: () => buildDescription,
|
|
@@ -6694,6 +7223,9 @@ __export(dist_exports, {
|
|
|
6694
7223
|
checkNodeVersion: () => checkNodeVersion,
|
|
6695
7224
|
checkPrdDirectory: () => checkPrdDirectory,
|
|
6696
7225
|
checkProviderCli: () => checkProviderCli,
|
|
7226
|
+
checkRateLimited: () => checkRateLimited,
|
|
7227
|
+
claimPrd: () => claimPrd,
|
|
7228
|
+
cleanupWorktrees: () => cleanupWorktrees,
|
|
6697
7229
|
clearPrdState: () => clearPrdState,
|
|
6698
7230
|
clearTemplateCache: () => clearTemplateCache,
|
|
6699
7231
|
closeDb: () => closeDb,
|
|
@@ -6713,6 +7245,7 @@ __export(dist_exports, {
|
|
|
6713
7245
|
createSlicerPromptVars: () => createSlicerPromptVars,
|
|
6714
7246
|
createSpinner: () => createSpinner,
|
|
6715
7247
|
createTable: () => createTable,
|
|
7248
|
+
detectDefaultBranch: () => detectDefaultBranch,
|
|
6716
7249
|
detectProviders: () => detectProviders,
|
|
6717
7250
|
dim: () => dim,
|
|
6718
7251
|
error: () => error,
|
|
@@ -6728,6 +7261,8 @@ __export(dist_exports, {
|
|
|
6728
7261
|
fetchPrDetailsForBranch: () => fetchPrDetailsForBranch,
|
|
6729
7262
|
fetchReviewedPrDetails: () => fetchReviewedPrDetails,
|
|
6730
7263
|
fetchStatusSnapshot: () => fetchStatusSnapshot,
|
|
7264
|
+
findEligibleBoardIssue: () => findEligibleBoardIssue,
|
|
7265
|
+
findEligiblePrd: () => findEligiblePrd,
|
|
6731
7266
|
findMatchingIssue: () => findMatchingIssue,
|
|
6732
7267
|
formatDiscordPayload: () => formatDiscordPayload,
|
|
6733
7268
|
formatInstalledStatus: () => formatInstalledStatus,
|
|
@@ -6737,6 +7272,7 @@ __export(dist_exports, {
|
|
|
6737
7272
|
generateItemHash: () => generateItemHash,
|
|
6738
7273
|
generateMarker: () => generateMarker,
|
|
6739
7274
|
generatePersonaAvatar: () => generatePersonaAvatar,
|
|
7275
|
+
getBranchTipTimestamp: () => getBranchTipTimestamp,
|
|
6740
7276
|
getCrontabInfo: () => getCrontabInfo,
|
|
6741
7277
|
getDb: () => getDb,
|
|
6742
7278
|
getDbPath: () => getDbPath,
|
|
@@ -6770,6 +7306,7 @@ __export(dist_exports, {
|
|
|
6770
7306
|
header: () => header,
|
|
6771
7307
|
info: () => info,
|
|
6772
7308
|
initContainer: () => initContainer,
|
|
7309
|
+
isClaimed: () => isClaimed,
|
|
6773
7310
|
isContainerInitialized: () => isContainerInitialized,
|
|
6774
7311
|
isInCooldown: () => isInCooldown,
|
|
6775
7312
|
isItemProcessed: () => isItemProcessed,
|
|
@@ -6786,25 +7323,33 @@ __export(dist_exports, {
|
|
|
6786
7323
|
loadRoadmapState: () => loadRoadmapState,
|
|
6787
7324
|
loadSlicerTemplate: () => loadSlicerTemplate,
|
|
6788
7325
|
markItemProcessed: () => markItemProcessed,
|
|
7326
|
+
markPrdDone: () => markPrdDone,
|
|
6789
7327
|
migrateJsonToSqlite: () => migrateJsonToSqlite,
|
|
6790
7328
|
parsePrdDependencies: () => parsePrdDependencies,
|
|
6791
7329
|
parseRoadmap: () => parseRoadmap,
|
|
6792
7330
|
parseScriptResult: () => parseScriptResult,
|
|
6793
7331
|
performCancel: () => performCancel,
|
|
6794
7332
|
plannerLockPath: () => plannerLockPath,
|
|
7333
|
+
prepareBranchWorktree: () => prepareBranchWorktree,
|
|
7334
|
+
prepareDetachedWorktree: () => prepareDetachedWorktree,
|
|
6795
7335
|
projectRuntimeKey: () => projectRuntimeKey,
|
|
6796
7336
|
qaLockPath: () => qaLockPath,
|
|
7337
|
+
readClaimInfo: () => readClaimInfo,
|
|
6797
7338
|
readCrontab: () => readCrontab,
|
|
6798
7339
|
readPrdStates: () => readPrdStates,
|
|
6799
7340
|
recordExecution: () => recordExecution,
|
|
6800
7341
|
registerProject: () => registerProject,
|
|
7342
|
+
releaseClaim: () => releaseClaim,
|
|
7343
|
+
releaseLock: () => releaseLock,
|
|
6801
7344
|
removeEntries: () => removeEntries,
|
|
6802
7345
|
removeEntriesForProject: () => removeEntriesForProject,
|
|
6803
7346
|
renderPrdTemplate: () => renderPrdTemplate,
|
|
6804
7347
|
renderSlicerPrompt: () => renderSlicerPrompt,
|
|
6805
7348
|
resetRepositories: () => resetRepositories,
|
|
6806
7349
|
resolveJobProvider: () => resolveJobProvider,
|
|
7350
|
+
resolveWorktreeBaseRef: () => resolveWorktreeBaseRef,
|
|
6807
7351
|
reviewerLockPath: () => reviewerLockPath,
|
|
7352
|
+
rotateLog: () => rotateLog,
|
|
6808
7353
|
runAllChecks: () => runAllChecks,
|
|
6809
7354
|
runMigrations: () => runMigrations,
|
|
6810
7355
|
saveConfig: () => saveConfig,
|
|
@@ -6819,6 +7364,7 @@ __export(dist_exports, {
|
|
|
6819
7364
|
sliceRoadmapItem: () => sliceRoadmapItem,
|
|
6820
7365
|
slugify: () => slugify,
|
|
6821
7366
|
sortByPriority: () => sortByPriority,
|
|
7367
|
+
sortPrdsByPriority: () => sortPrdsByPriority,
|
|
6822
7368
|
step: () => step,
|
|
6823
7369
|
success: () => success,
|
|
6824
7370
|
unmarkItemProcessed: () => unmarkItemProcessed,
|
|
@@ -6852,11 +7398,15 @@ var init_dist = __esm({
|
|
|
6852
7398
|
init_logger();
|
|
6853
7399
|
init_cancel();
|
|
6854
7400
|
init_checks();
|
|
7401
|
+
init_claim_manager();
|
|
6855
7402
|
init_config_writer();
|
|
6856
7403
|
init_crontab();
|
|
6857
7404
|
init_execution_history();
|
|
7405
|
+
init_git_utils();
|
|
6858
7406
|
init_github();
|
|
7407
|
+
init_log_utils();
|
|
6859
7408
|
init_notify();
|
|
7409
|
+
init_prd_discovery();
|
|
6860
7410
|
init_prd_states();
|
|
6861
7411
|
init_prd_utils();
|
|
6862
7412
|
init_registry();
|
|
@@ -6869,6 +7419,7 @@ var init_dist = __esm({
|
|
|
6869
7419
|
init_status_data();
|
|
6870
7420
|
init_ui();
|
|
6871
7421
|
init_webhook_validator();
|
|
7422
|
+
init_worktree_manager();
|
|
6872
7423
|
init_prd_template();
|
|
6873
7424
|
init_slicer_prompt();
|
|
6874
7425
|
}
|
|
@@ -6879,22 +7430,22 @@ var __dirname2 = dirname4(__filename);
|
|
|
6879
7430
|
function findTemplatesDir(startDir) {
|
|
6880
7431
|
let d = startDir;
|
|
6881
7432
|
for (let i = 0; i < 8; i++) {
|
|
6882
|
-
const candidate =
|
|
6883
|
-
if (
|
|
7433
|
+
const candidate = join16(d, "templates");
|
|
7434
|
+
if (fs18.existsSync(candidate) && fs18.statSync(candidate).isDirectory()) {
|
|
6884
7435
|
return candidate;
|
|
6885
7436
|
}
|
|
6886
7437
|
d = dirname4(d);
|
|
6887
7438
|
}
|
|
6888
|
-
return
|
|
7439
|
+
return join16(startDir, "templates");
|
|
6889
7440
|
}
|
|
6890
7441
|
var TEMPLATES_DIR = findTemplatesDir(__dirname2);
|
|
6891
7442
|
function hasPlaywrightDependency(cwd) {
|
|
6892
|
-
const packageJsonPath =
|
|
6893
|
-
if (!
|
|
7443
|
+
const packageJsonPath = path17.join(cwd, "package.json");
|
|
7444
|
+
if (!fs18.existsSync(packageJsonPath)) {
|
|
6894
7445
|
return false;
|
|
6895
7446
|
}
|
|
6896
7447
|
try {
|
|
6897
|
-
const packageJson2 = JSON.parse(
|
|
7448
|
+
const packageJson2 = JSON.parse(fs18.readFileSync(packageJsonPath, "utf-8"));
|
|
6898
7449
|
return Boolean(packageJson2.dependencies?.["@playwright/test"] || packageJson2.dependencies?.playwright || packageJson2.devDependencies?.["@playwright/test"] || packageJson2.devDependencies?.playwright);
|
|
6899
7450
|
} catch {
|
|
6900
7451
|
return false;
|
|
@@ -6904,7 +7455,7 @@ function detectPlaywright(cwd) {
|
|
|
6904
7455
|
if (hasPlaywrightDependency(cwd)) {
|
|
6905
7456
|
return true;
|
|
6906
7457
|
}
|
|
6907
|
-
if (
|
|
7458
|
+
if (fs18.existsSync(path17.join(cwd, "node_modules", ".bin", "playwright"))) {
|
|
6908
7459
|
return true;
|
|
6909
7460
|
}
|
|
6910
7461
|
try {
|
|
@@ -6920,10 +7471,10 @@ function detectPlaywright(cwd) {
|
|
|
6920
7471
|
}
|
|
6921
7472
|
}
|
|
6922
7473
|
function resolvePlaywrightInstallCommand(cwd) {
|
|
6923
|
-
if (
|
|
7474
|
+
if (fs18.existsSync(path17.join(cwd, "pnpm-lock.yaml"))) {
|
|
6924
7475
|
return "pnpm add -D @playwright/test";
|
|
6925
7476
|
}
|
|
6926
|
-
if (
|
|
7477
|
+
if (fs18.existsSync(path17.join(cwd, "yarn.lock"))) {
|
|
6927
7478
|
return "yarn add -D @playwright/test";
|
|
6928
7479
|
}
|
|
6929
7480
|
return "npm install -D @playwright/test";
|
|
@@ -7042,36 +7593,36 @@ function promptProviderSelection(providers) {
|
|
|
7042
7593
|
});
|
|
7043
7594
|
}
|
|
7044
7595
|
function ensureDir(dirPath) {
|
|
7045
|
-
if (!
|
|
7046
|
-
|
|
7596
|
+
if (!fs18.existsSync(dirPath)) {
|
|
7597
|
+
fs18.mkdirSync(dirPath, { recursive: true });
|
|
7047
7598
|
}
|
|
7048
7599
|
}
|
|
7049
7600
|
function resolveTemplatePath(templateName, customTemplatesDir, bundledTemplatesDir) {
|
|
7050
7601
|
if (customTemplatesDir !== null) {
|
|
7051
|
-
const customPath =
|
|
7052
|
-
if (
|
|
7602
|
+
const customPath = join16(customTemplatesDir, templateName);
|
|
7603
|
+
if (fs18.existsSync(customPath)) {
|
|
7053
7604
|
return { path: customPath, source: "custom" };
|
|
7054
7605
|
}
|
|
7055
7606
|
}
|
|
7056
|
-
return { path:
|
|
7607
|
+
return { path: join16(bundledTemplatesDir, templateName), source: "bundled" };
|
|
7057
7608
|
}
|
|
7058
7609
|
function processTemplate(templateName, targetPath, replacements, force, sourcePath, source) {
|
|
7059
|
-
if (
|
|
7610
|
+
if (fs18.existsSync(targetPath) && !force) {
|
|
7060
7611
|
console.log(` Skipped (exists): ${targetPath}`);
|
|
7061
7612
|
return { created: false, source: source ?? "bundled" };
|
|
7062
7613
|
}
|
|
7063
|
-
const templatePath = sourcePath ??
|
|
7614
|
+
const templatePath = sourcePath ?? join16(TEMPLATES_DIR, templateName);
|
|
7064
7615
|
const resolvedSource = source ?? "bundled";
|
|
7065
|
-
let content =
|
|
7616
|
+
let content = fs18.readFileSync(templatePath, "utf-8");
|
|
7066
7617
|
for (const [key, value] of Object.entries(replacements)) {
|
|
7067
7618
|
content = content.replaceAll(key, value);
|
|
7068
7619
|
}
|
|
7069
|
-
|
|
7620
|
+
fs18.writeFileSync(targetPath, content);
|
|
7070
7621
|
console.log(` Created: ${targetPath} (${resolvedSource})`);
|
|
7071
7622
|
return { created: true, source: resolvedSource };
|
|
7072
7623
|
}
|
|
7073
7624
|
function addToGitignore(cwd) {
|
|
7074
|
-
const gitignorePath =
|
|
7625
|
+
const gitignorePath = path17.join(cwd, ".gitignore");
|
|
7075
7626
|
const entries = [
|
|
7076
7627
|
{
|
|
7077
7628
|
pattern: "/logs/",
|
|
@@ -7085,13 +7636,13 @@ function addToGitignore(cwd) {
|
|
|
7085
7636
|
},
|
|
7086
7637
|
{ pattern: "*.claim", label: "*.claim", check: (c) => c.includes("*.claim") }
|
|
7087
7638
|
];
|
|
7088
|
-
if (!
|
|
7639
|
+
if (!fs18.existsSync(gitignorePath)) {
|
|
7089
7640
|
const lines = ["# Night Watch", ...entries.map((e) => e.pattern), ""];
|
|
7090
|
-
|
|
7641
|
+
fs18.writeFileSync(gitignorePath, lines.join("\n"));
|
|
7091
7642
|
console.log(` Created: ${gitignorePath} (with Night Watch entries)`);
|
|
7092
7643
|
return;
|
|
7093
7644
|
}
|
|
7094
|
-
const content =
|
|
7645
|
+
const content = fs18.readFileSync(gitignorePath, "utf-8");
|
|
7095
7646
|
const missing = entries.filter((e) => !e.check(content));
|
|
7096
7647
|
if (missing.length === 0) {
|
|
7097
7648
|
console.log(` Skipped (exists): Night Watch entries in .gitignore`);
|
|
@@ -7099,30 +7650,15 @@ function addToGitignore(cwd) {
|
|
|
7099
7650
|
}
|
|
7100
7651
|
const additions = missing.map((e) => e.pattern).join("\n");
|
|
7101
7652
|
const newContent = content.trimEnd() + "\n\n# Night Watch\n" + additions + "\n";
|
|
7102
|
-
|
|
7653
|
+
fs18.writeFileSync(gitignorePath, newContent);
|
|
7103
7654
|
console.log(` Updated: ${gitignorePath} (added ${missing.map((e) => e.label).join(", ")})`);
|
|
7104
7655
|
}
|
|
7105
|
-
function createSummaryFile(summaryPath, force) {
|
|
7106
|
-
if (fs14.existsSync(summaryPath) && !force) {
|
|
7107
|
-
console.log(` Skipped (exists): ${summaryPath}`);
|
|
7108
|
-
return;
|
|
7109
|
-
}
|
|
7110
|
-
const content = `# Night Watch Summary
|
|
7111
|
-
|
|
7112
|
-
This file tracks the progress of PRDs executed by Night Watch.
|
|
7113
|
-
|
|
7114
|
-
---
|
|
7115
|
-
|
|
7116
|
-
`;
|
|
7117
|
-
fs14.writeFileSync(summaryPath, content);
|
|
7118
|
-
console.log(` Created: ${summaryPath}`);
|
|
7119
|
-
}
|
|
7120
7656
|
function initCommand(program2) {
|
|
7121
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) => {
|
|
7122
7658
|
const cwd = process.cwd();
|
|
7123
7659
|
const force = options.force || false;
|
|
7124
7660
|
const prdDir = options.prdDir || DEFAULT_PRD_DIR;
|
|
7125
|
-
const totalSteps =
|
|
7661
|
+
const totalSteps = 11;
|
|
7126
7662
|
console.log();
|
|
7127
7663
|
header("Night Watch CLI - Initializing");
|
|
7128
7664
|
step(1, totalSteps, "Checking git repository...");
|
|
@@ -7203,57 +7739,54 @@ function initCommand(program2) {
|
|
|
7203
7739
|
"${DEFAULT_BRANCH}": defaultBranch
|
|
7204
7740
|
};
|
|
7205
7741
|
step(5, totalSteps, "Creating PRD directory structure...");
|
|
7206
|
-
const prdDirPath =
|
|
7207
|
-
const doneDirPath =
|
|
7742
|
+
const prdDirPath = path17.join(cwd, prdDir);
|
|
7743
|
+
const doneDirPath = path17.join(prdDirPath, "done");
|
|
7208
7744
|
ensureDir(doneDirPath);
|
|
7209
7745
|
success(`Created ${prdDirPath}/`);
|
|
7210
7746
|
success(`Created ${doneDirPath}/`);
|
|
7211
|
-
step(6, totalSteps, "Creating
|
|
7212
|
-
const
|
|
7213
|
-
createSummaryFile(summaryPath, force);
|
|
7214
|
-
step(7, totalSteps, "Creating logs directory...");
|
|
7215
|
-
const logsPath = path13.join(cwd, LOG_DIR);
|
|
7747
|
+
step(6, totalSteps, "Creating logs directory...");
|
|
7748
|
+
const logsPath = path17.join(cwd, LOG_DIR);
|
|
7216
7749
|
ensureDir(logsPath);
|
|
7217
7750
|
success(`Created ${logsPath}/`);
|
|
7218
7751
|
addToGitignore(cwd);
|
|
7219
|
-
step(
|
|
7220
|
-
const
|
|
7221
|
-
ensureDir(
|
|
7222
|
-
success(`Created ${
|
|
7752
|
+
step(7, totalSteps, "Creating instructions directory...");
|
|
7753
|
+
const instructionsDir = path17.join(cwd, "instructions");
|
|
7754
|
+
ensureDir(instructionsDir);
|
|
7755
|
+
success(`Created ${instructionsDir}/`);
|
|
7223
7756
|
const existingConfig = loadConfig(cwd);
|
|
7224
|
-
const customTemplatesDirPath =
|
|
7225
|
-
const customTemplatesDir =
|
|
7757
|
+
const customTemplatesDirPath = path17.join(cwd, existingConfig.templatesDir);
|
|
7758
|
+
const customTemplatesDir = fs18.existsSync(customTemplatesDirPath) ? customTemplatesDirPath : null;
|
|
7226
7759
|
const templateSources = [];
|
|
7227
7760
|
const nwResolution = resolveTemplatePath("night-watch.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7228
|
-
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);
|
|
7229
7762
|
templateSources.push({ name: "night-watch.md", source: nwResult.source });
|
|
7230
7763
|
const peResolution = resolveTemplatePath("prd-executor.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7231
|
-
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);
|
|
7232
7765
|
templateSources.push({ name: "prd-executor.md", source: peResult.source });
|
|
7233
7766
|
const prResolution = resolveTemplatePath("night-watch-pr-reviewer.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7234
|
-
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);
|
|
7235
7768
|
templateSources.push({ name: "night-watch-pr-reviewer.md", source: prResult.source });
|
|
7236
7769
|
const qaResolution = resolveTemplatePath("night-watch-qa.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7237
|
-
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);
|
|
7238
7771
|
templateSources.push({ name: "night-watch-qa.md", source: qaResult.source });
|
|
7239
7772
|
const auditResolution = resolveTemplatePath("night-watch-audit.md", customTemplatesDir, TEMPLATES_DIR);
|
|
7240
|
-
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);
|
|
7241
7774
|
templateSources.push({ name: "night-watch-audit.md", source: auditResult.source });
|
|
7242
|
-
step(
|
|
7243
|
-
const configPath =
|
|
7244
|
-
if (
|
|
7775
|
+
step(8, totalSteps, "Creating configuration file...");
|
|
7776
|
+
const configPath = path17.join(cwd, CONFIG_FILE_NAME);
|
|
7777
|
+
if (fs18.existsSync(configPath) && !force) {
|
|
7245
7778
|
console.log(` Skipped (exists): ${configPath}`);
|
|
7246
7779
|
} else {
|
|
7247
|
-
let configContent =
|
|
7780
|
+
let configContent = fs18.readFileSync(join16(TEMPLATES_DIR, "night-watch.config.json"), "utf-8");
|
|
7248
7781
|
configContent = configContent.replace('"projectName": ""', `"projectName": "${projectName}"`);
|
|
7249
7782
|
configContent = configContent.replace('"defaultBranch": ""', `"defaultBranch": "${defaultBranch}"`);
|
|
7250
7783
|
configContent = configContent.replace(/"provider":\s*"[^"]*"/, `"provider": "${selectedProvider}"`);
|
|
7251
7784
|
configContent = configContent.replace(/"reviewerEnabled":\s*(true|false)/, `"reviewerEnabled": ${reviewerEnabled}`);
|
|
7252
|
-
|
|
7785
|
+
fs18.writeFileSync(configPath, configContent);
|
|
7253
7786
|
success(`Created ${configPath}`);
|
|
7254
7787
|
}
|
|
7255
|
-
step(
|
|
7256
|
-
const existingRaw = JSON.parse(
|
|
7788
|
+
step(9, totalSteps, "Setting up GitHub Project board...");
|
|
7789
|
+
const existingRaw = JSON.parse(fs18.readFileSync(configPath, "utf-8"));
|
|
7257
7790
|
const existingBoard = existingRaw.boardProvider;
|
|
7258
7791
|
if (existingBoard?.projectNumber && !force) {
|
|
7259
7792
|
info(`Board already configured (#${existingBoard.projectNumber}), skipping.`);
|
|
@@ -7275,13 +7808,13 @@ function initCommand(program2) {
|
|
|
7275
7808
|
const provider = createBoardProvider({ enabled: true, provider: "github" }, cwd);
|
|
7276
7809
|
const boardTitle = `${projectName} Night Watch`;
|
|
7277
7810
|
const board = await provider.setupBoard(boardTitle);
|
|
7278
|
-
const rawConfig = JSON.parse(
|
|
7811
|
+
const rawConfig = JSON.parse(fs18.readFileSync(configPath, "utf-8"));
|
|
7279
7812
|
rawConfig.boardProvider = {
|
|
7280
7813
|
enabled: true,
|
|
7281
7814
|
provider: "github",
|
|
7282
7815
|
projectNumber: board.number
|
|
7283
7816
|
};
|
|
7284
|
-
|
|
7817
|
+
fs18.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n");
|
|
7285
7818
|
success(`GitHub Project board "${boardTitle}" ready (#${board.number})`);
|
|
7286
7819
|
} catch (boardErr) {
|
|
7287
7820
|
console.warn(` Warning: Could not set up GitHub Project board: ${boardErr instanceof Error ? boardErr.message : String(boardErr)}`);
|
|
@@ -7289,7 +7822,7 @@ function initCommand(program2) {
|
|
|
7289
7822
|
}
|
|
7290
7823
|
}
|
|
7291
7824
|
}
|
|
7292
|
-
step(
|
|
7825
|
+
step(10, totalSteps, "Registering project in global registry...");
|
|
7293
7826
|
try {
|
|
7294
7827
|
const { registerProject: registerProject2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
7295
7828
|
const entry = registerProject2(cwd);
|
|
@@ -7297,23 +7830,22 @@ function initCommand(program2) {
|
|
|
7297
7830
|
} catch (regErr) {
|
|
7298
7831
|
console.warn(` Warning: Could not register in global registry: ${regErr instanceof Error ? regErr.message : String(regErr)}`);
|
|
7299
7832
|
}
|
|
7300
|
-
step(
|
|
7833
|
+
step(11, totalSteps, "Initialization complete!");
|
|
7301
7834
|
header("Initialization Complete");
|
|
7302
7835
|
const filesTable = createTable({ head: ["Created Files", ""] });
|
|
7303
7836
|
filesTable.push(["PRD Directory", `${prdDir}/done/`]);
|
|
7304
|
-
filesTable.push(["Summary File", `${prdDir}/NIGHT-WATCH-SUMMARY.md`]);
|
|
7305
7837
|
filesTable.push(["Logs Directory", `${LOG_DIR}/`]);
|
|
7306
7838
|
filesTable.push([
|
|
7307
|
-
"
|
|
7308
|
-
|
|
7839
|
+
"Instructions",
|
|
7840
|
+
`instructions/night-watch.md (${templateSources[0].source})`
|
|
7309
7841
|
]);
|
|
7310
|
-
filesTable.push(["",
|
|
7842
|
+
filesTable.push(["", `instructions/prd-executor.md (${templateSources[1].source})`]);
|
|
7311
7843
|
filesTable.push([
|
|
7312
7844
|
"",
|
|
7313
|
-
|
|
7845
|
+
`instructions/night-watch-pr-reviewer.md (${templateSources[2].source})`
|
|
7314
7846
|
]);
|
|
7315
|
-
filesTable.push(["",
|
|
7316
|
-
filesTable.push(["",
|
|
7847
|
+
filesTable.push(["", `instructions/night-watch-qa.md (${templateSources[3].source})`]);
|
|
7848
|
+
filesTable.push(["", `instructions/night-watch-audit.md (${templateSources[4].source})`]);
|
|
7317
7849
|
filesTable.push(["Config File", CONFIG_FILE_NAME]);
|
|
7318
7850
|
filesTable.push(["Global Registry", "~/.night-watch/projects.json"]);
|
|
7319
7851
|
console.log(filesTable.toString());
|
|
@@ -7354,12 +7886,12 @@ function shouldAttemptCrossProjectFallback(options, scriptStatus) {
|
|
|
7354
7886
|
return scriptStatus === "skip_no_eligible_prd";
|
|
7355
7887
|
}
|
|
7356
7888
|
function getCrossProjectFallbackCandidates(currentProjectDir) {
|
|
7357
|
-
const current =
|
|
7889
|
+
const current = path18.resolve(currentProjectDir);
|
|
7358
7890
|
const { valid, invalid } = validateRegistry();
|
|
7359
7891
|
for (const entry of invalid) {
|
|
7360
7892
|
warn(`Skipping invalid registry entry: ${entry.path}`);
|
|
7361
7893
|
}
|
|
7362
|
-
return valid.filter((entry) =>
|
|
7894
|
+
return valid.filter((entry) => path18.resolve(entry.path) !== current);
|
|
7363
7895
|
}
|
|
7364
7896
|
async function sendRunCompletionNotifications(config, projectDir, options, exitCode, scriptResult) {
|
|
7365
7897
|
if (isRateLimitFallbackTriggered(scriptResult?.data)) {
|
|
@@ -7367,7 +7899,7 @@ async function sendRunCompletionNotifications(config, projectDir, options, exitC
|
|
|
7367
7899
|
if (nonTelegramWebhooks.length > 0) {
|
|
7368
7900
|
const _rateLimitCtx = {
|
|
7369
7901
|
event: "rate_limit_fallback",
|
|
7370
|
-
projectName:
|
|
7902
|
+
projectName: path18.basename(projectDir),
|
|
7371
7903
|
exitCode,
|
|
7372
7904
|
provider: config.provider
|
|
7373
7905
|
};
|
|
@@ -7389,11 +7921,15 @@ async function sendRunCompletionNotifications(config, projectDir, options, exitC
|
|
|
7389
7921
|
}
|
|
7390
7922
|
}
|
|
7391
7923
|
if (event) {
|
|
7924
|
+
const timeoutDuration = event === "run_timeout" ? config.maxRuntime : void 0;
|
|
7392
7925
|
const _ctx = {
|
|
7393
7926
|
event,
|
|
7394
|
-
projectName:
|
|
7927
|
+
projectName: path18.basename(projectDir),
|
|
7395
7928
|
exitCode,
|
|
7396
7929
|
provider: config.provider,
|
|
7930
|
+
prdName: scriptResult?.data.prd,
|
|
7931
|
+
branchName: scriptResult?.data.branch,
|
|
7932
|
+
duration: timeoutDuration,
|
|
7397
7933
|
prUrl: prDetails?.url,
|
|
7398
7934
|
prTitle: prDetails?.title,
|
|
7399
7935
|
prBody: prDetails?.body,
|
|
@@ -7501,20 +8037,20 @@ function applyCliOverrides(config, options) {
|
|
|
7501
8037
|
return overridden;
|
|
7502
8038
|
}
|
|
7503
8039
|
function scanPrdDirectory(projectDir, prdDir, maxRuntime) {
|
|
7504
|
-
const absolutePrdDir =
|
|
7505
|
-
const doneDir =
|
|
8040
|
+
const absolutePrdDir = path18.join(projectDir, prdDir);
|
|
8041
|
+
const doneDir = path18.join(absolutePrdDir, "done");
|
|
7506
8042
|
const pending = [];
|
|
7507
8043
|
const completed = [];
|
|
7508
|
-
if (
|
|
7509
|
-
const entries =
|
|
8044
|
+
if (fs19.existsSync(absolutePrdDir)) {
|
|
8045
|
+
const entries = fs19.readdirSync(absolutePrdDir, { withFileTypes: true });
|
|
7510
8046
|
for (const entry of entries) {
|
|
7511
|
-
if (entry.isFile() && entry.name.endsWith(".md")
|
|
7512
|
-
const claimPath =
|
|
8047
|
+
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
8048
|
+
const claimPath = path18.join(absolutePrdDir, entry.name + CLAIM_FILE_EXTENSION);
|
|
7513
8049
|
let claimed = false;
|
|
7514
8050
|
let claimInfo = null;
|
|
7515
|
-
if (
|
|
8051
|
+
if (fs19.existsSync(claimPath)) {
|
|
7516
8052
|
try {
|
|
7517
|
-
const content =
|
|
8053
|
+
const content = fs19.readFileSync(claimPath, "utf-8");
|
|
7518
8054
|
const data = JSON.parse(content);
|
|
7519
8055
|
const age = Math.floor(Date.now() / 1e3) - data.timestamp;
|
|
7520
8056
|
if (age < maxRuntime) {
|
|
@@ -7528,8 +8064,8 @@ function scanPrdDirectory(projectDir, prdDir, maxRuntime) {
|
|
|
7528
8064
|
}
|
|
7529
8065
|
}
|
|
7530
8066
|
}
|
|
7531
|
-
if (
|
|
7532
|
-
const entries =
|
|
8067
|
+
if (fs19.existsSync(doneDir)) {
|
|
8068
|
+
const entries = fs19.readdirSync(doneDir, { withFileTypes: true });
|
|
7533
8069
|
for (const entry of entries) {
|
|
7534
8070
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
7535
8071
|
completed.push(entry.name);
|
|
@@ -7738,13 +8274,42 @@ function applyCliOverrides2(config, options) {
|
|
|
7738
8274
|
}
|
|
7739
8275
|
return overridden;
|
|
7740
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
|
+
}
|
|
7741
8306
|
function getOpenPrsNeedingWork(branchPatterns) {
|
|
7742
8307
|
try {
|
|
7743
8308
|
const args = ["pr", "list", "--state", "open", "--json", "number,title,headRefName"];
|
|
7744
8309
|
for (const pattern of branchPatterns) {
|
|
7745
8310
|
args.push("--head", pattern);
|
|
7746
8311
|
}
|
|
7747
|
-
const result =
|
|
8312
|
+
const result = execFileSync5("gh", args, {
|
|
7748
8313
|
encoding: "utf-8",
|
|
7749
8314
|
stdio: ["pipe", "pipe", "pipe"]
|
|
7750
8315
|
});
|
|
@@ -7812,6 +8377,21 @@ function reviewCommand(program2) {
|
|
|
7812
8377
|
console.log();
|
|
7813
8378
|
process.exit(0);
|
|
7814
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
|
+
}
|
|
7815
8395
|
const spinner = createSpinner("Running PR reviewer...");
|
|
7816
8396
|
spinner.start();
|
|
7817
8397
|
try {
|
|
@@ -7851,7 +8431,7 @@ ${stderr}`);
|
|
|
7851
8431
|
const finalScore = parseFinalReviewScore(scriptResult?.data.final_score);
|
|
7852
8432
|
const _reviewCtx = {
|
|
7853
8433
|
event: "review_completed",
|
|
7854
|
-
projectName:
|
|
8434
|
+
projectName: path19.basename(projectDir),
|
|
7855
8435
|
exitCode,
|
|
7856
8436
|
provider: config.provider,
|
|
7857
8437
|
prUrl: prDetails?.url,
|
|
@@ -7872,7 +8452,7 @@ ${stderr}`);
|
|
|
7872
8452
|
const autoMergedPrDetails = fetchPrDetailsByNumber(autoMergedPrNumber, projectDir);
|
|
7873
8453
|
const _mergeCtx = {
|
|
7874
8454
|
event: "pr_auto_merged",
|
|
7875
|
-
projectName:
|
|
8455
|
+
projectName: path19.basename(projectDir),
|
|
7876
8456
|
exitCode,
|
|
7877
8457
|
provider: config.provider,
|
|
7878
8458
|
prNumber: autoMergedPrDetails?.number ?? autoMergedPrNumber,
|
|
@@ -8028,7 +8608,7 @@ ${stderr}`);
|
|
|
8028
8608
|
const fallbackPrUrl = !prDetails?.url && primaryQaPr && repo ? `https://github.com/${repo}/pull/${primaryQaPr}` : void 0;
|
|
8029
8609
|
const _qaCtx = {
|
|
8030
8610
|
event: "qa_completed",
|
|
8031
|
-
projectName:
|
|
8611
|
+
projectName: path20.basename(projectDir),
|
|
8032
8612
|
exitCode,
|
|
8033
8613
|
provider: config.provider,
|
|
8034
8614
|
prNumber: prDetails?.number ?? primaryQaPr,
|
|
@@ -8107,7 +8687,7 @@ function auditCommand(program2) {
|
|
|
8107
8687
|
configTable.push(["Provider", auditProvider]);
|
|
8108
8688
|
configTable.push(["Provider CLI", PROVIDER_COMMANDS[auditProvider]]);
|
|
8109
8689
|
configTable.push(["Max Runtime", `${config.audit.maxRuntime}s`]);
|
|
8110
|
-
configTable.push(["Report File",
|
|
8690
|
+
configTable.push(["Report File", path21.join(projectDir, "logs", "audit-report.md")]);
|
|
8111
8691
|
console.log(configTable.toString());
|
|
8112
8692
|
header("Provider Invocation");
|
|
8113
8693
|
const providerCmd = PROVIDER_COMMANDS[auditProvider];
|
|
@@ -8133,8 +8713,8 @@ ${stderr}`);
|
|
|
8133
8713
|
} else if (scriptResult?.status?.startsWith("skip_")) {
|
|
8134
8714
|
spinner.succeed("Code audit skipped");
|
|
8135
8715
|
} else {
|
|
8136
|
-
const reportPath =
|
|
8137
|
-
if (!
|
|
8716
|
+
const reportPath = path21.join(projectDir, "logs", "audit-report.md");
|
|
8717
|
+
if (!fs20.existsSync(reportPath)) {
|
|
8138
8718
|
spinner.fail("Code audit finished without a report file");
|
|
8139
8719
|
process.exit(1);
|
|
8140
8720
|
}
|
|
@@ -8145,9 +8725,9 @@ ${stderr}`);
|
|
|
8145
8725
|
const providerExit = scriptResult?.data?.provider_exit;
|
|
8146
8726
|
const exitDetail = providerExit && providerExit !== String(exitCode) ? `, provider exit ${providerExit}` : "";
|
|
8147
8727
|
spinner.fail(`Code audit exited with code ${exitCode}${statusSuffix}${exitDetail}`);
|
|
8148
|
-
const logPath =
|
|
8149
|
-
if (
|
|
8150
|
-
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);
|
|
8151
8731
|
if (logLines.length > 0) {
|
|
8152
8732
|
process.stderr.write(logLines.join("\n") + "\n");
|
|
8153
8733
|
}
|
|
@@ -8167,8 +8747,8 @@ function shellQuote(value) {
|
|
|
8167
8747
|
function getNightWatchBinPath() {
|
|
8168
8748
|
try {
|
|
8169
8749
|
const npmBin = execSync4("npm bin -g", { encoding: "utf-8" }).trim();
|
|
8170
|
-
const binPath =
|
|
8171
|
-
if (
|
|
8750
|
+
const binPath = path22.join(npmBin, "night-watch");
|
|
8751
|
+
if (fs21.existsSync(binPath)) {
|
|
8172
8752
|
return binPath;
|
|
8173
8753
|
}
|
|
8174
8754
|
} catch {
|
|
@@ -8182,13 +8762,13 @@ function getNightWatchBinPath() {
|
|
|
8182
8762
|
function getNodeBinDir() {
|
|
8183
8763
|
try {
|
|
8184
8764
|
const nodePath = execSync4("which node", { encoding: "utf-8" }).trim();
|
|
8185
|
-
return
|
|
8765
|
+
return path22.dirname(nodePath);
|
|
8186
8766
|
} catch {
|
|
8187
8767
|
return "";
|
|
8188
8768
|
}
|
|
8189
8769
|
}
|
|
8190
8770
|
function buildCronPathPrefix(nodeBinDir, nightWatchBin) {
|
|
8191
|
-
const nightWatchBinDir = nightWatchBin.includes("/") || nightWatchBin.includes("\\") ?
|
|
8771
|
+
const nightWatchBinDir = nightWatchBin.includes("/") || nightWatchBin.includes("\\") ? path22.dirname(nightWatchBin) : "";
|
|
8192
8772
|
const pathParts = Array.from(new Set([nodeBinDir, nightWatchBinDir].filter((part) => part.length > 0)));
|
|
8193
8773
|
if (pathParts.length === 0) {
|
|
8194
8774
|
return "";
|
|
@@ -8215,12 +8795,12 @@ function performInstall(projectDir, config, options) {
|
|
|
8215
8795
|
const nightWatchBin = getNightWatchBinPath();
|
|
8216
8796
|
const projectName = getProjectName(projectDir);
|
|
8217
8797
|
const marker = generateMarker(projectName);
|
|
8218
|
-
const logDir =
|
|
8219
|
-
if (!
|
|
8220
|
-
|
|
8798
|
+
const logDir = path22.join(projectDir, LOG_DIR);
|
|
8799
|
+
if (!fs21.existsSync(logDir)) {
|
|
8800
|
+
fs21.mkdirSync(logDir, { recursive: true });
|
|
8221
8801
|
}
|
|
8222
|
-
const executorLog =
|
|
8223
|
-
const reviewerLog =
|
|
8802
|
+
const executorLog = path22.join(logDir, "executor.log");
|
|
8803
|
+
const reviewerLog = path22.join(logDir, "reviewer.log");
|
|
8224
8804
|
if (!options?.force) {
|
|
8225
8805
|
const existingEntries = Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
|
|
8226
8806
|
if (existingEntries.length > 0) {
|
|
@@ -8253,7 +8833,7 @@ function performInstall(projectDir, config, options) {
|
|
|
8253
8833
|
const installSlicer = options?.noSlicer === true ? false : config.roadmapScanner.enabled;
|
|
8254
8834
|
if (installSlicer) {
|
|
8255
8835
|
const slicerSchedule = applyScheduleOffset(config.roadmapScanner.slicerSchedule, offset);
|
|
8256
|
-
const slicerLog =
|
|
8836
|
+
const slicerLog = path22.join(logDir, "slicer.log");
|
|
8257
8837
|
const slicerEntry = `${slicerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} planner >> ${shellQuote(slicerLog)} 2>&1 ${marker}`;
|
|
8258
8838
|
entries.push(slicerEntry);
|
|
8259
8839
|
}
|
|
@@ -8261,7 +8841,7 @@ function performInstall(projectDir, config, options) {
|
|
|
8261
8841
|
const installQa = disableQa ? false : config.qa.enabled;
|
|
8262
8842
|
if (installQa) {
|
|
8263
8843
|
const qaSchedule = applyScheduleOffset(config.qa.schedule, offset);
|
|
8264
|
-
const qaLog =
|
|
8844
|
+
const qaLog = path22.join(logDir, "qa.log");
|
|
8265
8845
|
const qaEntry = `${qaSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} qa >> ${shellQuote(qaLog)} 2>&1 ${marker}`;
|
|
8266
8846
|
entries.push(qaEntry);
|
|
8267
8847
|
}
|
|
@@ -8269,7 +8849,7 @@ function performInstall(projectDir, config, options) {
|
|
|
8269
8849
|
const installAudit = disableAudit ? false : config.audit.enabled;
|
|
8270
8850
|
if (installAudit) {
|
|
8271
8851
|
const auditSchedule = applyScheduleOffset(config.audit.schedule, offset);
|
|
8272
|
-
const auditLog =
|
|
8852
|
+
const auditLog = path22.join(logDir, "audit.log");
|
|
8273
8853
|
const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
|
|
8274
8854
|
entries.push(auditEntry);
|
|
8275
8855
|
}
|
|
@@ -8296,12 +8876,12 @@ function installCommand(program2) {
|
|
|
8296
8876
|
const nightWatchBin = getNightWatchBinPath();
|
|
8297
8877
|
const projectName = getProjectName(projectDir);
|
|
8298
8878
|
const marker = generateMarker(projectName);
|
|
8299
|
-
const logDir =
|
|
8300
|
-
if (!
|
|
8301
|
-
|
|
8879
|
+
const logDir = path22.join(projectDir, LOG_DIR);
|
|
8880
|
+
if (!fs21.existsSync(logDir)) {
|
|
8881
|
+
fs21.mkdirSync(logDir, { recursive: true });
|
|
8302
8882
|
}
|
|
8303
|
-
const executorLog =
|
|
8304
|
-
const reviewerLog =
|
|
8883
|
+
const executorLog = path22.join(logDir, "executor.log");
|
|
8884
|
+
const reviewerLog = path22.join(logDir, "reviewer.log");
|
|
8305
8885
|
const existingEntries = Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
|
|
8306
8886
|
if (existingEntries.length > 0) {
|
|
8307
8887
|
warn(`Night Watch is already installed for ${projectName}.`);
|
|
@@ -8334,7 +8914,7 @@ function installCommand(program2) {
|
|
|
8334
8914
|
const installSlicer = options.noSlicer === true ? false : config.roadmapScanner.enabled;
|
|
8335
8915
|
let slicerLog;
|
|
8336
8916
|
if (installSlicer) {
|
|
8337
|
-
slicerLog =
|
|
8917
|
+
slicerLog = path22.join(logDir, "slicer.log");
|
|
8338
8918
|
const slicerSchedule = applyScheduleOffset(config.roadmapScanner.slicerSchedule, offset);
|
|
8339
8919
|
const slicerEntry = `${slicerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} planner >> ${shellQuote(slicerLog)} 2>&1 ${marker}`;
|
|
8340
8920
|
entries.push(slicerEntry);
|
|
@@ -8343,7 +8923,7 @@ function installCommand(program2) {
|
|
|
8343
8923
|
const installQa = disableQa ? false : config.qa.enabled;
|
|
8344
8924
|
let qaLog;
|
|
8345
8925
|
if (installQa) {
|
|
8346
|
-
qaLog =
|
|
8926
|
+
qaLog = path22.join(logDir, "qa.log");
|
|
8347
8927
|
const qaSchedule = applyScheduleOffset(config.qa.schedule, offset);
|
|
8348
8928
|
const qaEntry = `${qaSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} qa >> ${shellQuote(qaLog)} 2>&1 ${marker}`;
|
|
8349
8929
|
entries.push(qaEntry);
|
|
@@ -8352,7 +8932,7 @@ function installCommand(program2) {
|
|
|
8352
8932
|
const installAudit = disableAudit ? false : config.audit.enabled;
|
|
8353
8933
|
let auditLog;
|
|
8354
8934
|
if (installAudit) {
|
|
8355
|
-
auditLog =
|
|
8935
|
+
auditLog = path22.join(logDir, "audit.log");
|
|
8356
8936
|
const auditSchedule = applyScheduleOffset(config.audit.schedule, offset);
|
|
8357
8937
|
const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
|
|
8358
8938
|
entries.push(auditEntry);
|
|
@@ -8401,19 +8981,19 @@ function performUninstall(projectDir, options) {
|
|
|
8401
8981
|
}
|
|
8402
8982
|
const removedCount = removeEntriesForProject(projectDir, marker);
|
|
8403
8983
|
if (!options?.keepLogs) {
|
|
8404
|
-
const logDir =
|
|
8405
|
-
if (
|
|
8984
|
+
const logDir = path23.join(projectDir, "logs");
|
|
8985
|
+
if (fs22.existsSync(logDir)) {
|
|
8406
8986
|
const logFiles = ["executor.log", "reviewer.log", "slicer.log", "audit.log"];
|
|
8407
8987
|
logFiles.forEach((logFile) => {
|
|
8408
|
-
const logPath =
|
|
8409
|
-
if (
|
|
8410
|
-
|
|
8988
|
+
const logPath = path23.join(logDir, logFile);
|
|
8989
|
+
if (fs22.existsSync(logPath)) {
|
|
8990
|
+
fs22.unlinkSync(logPath);
|
|
8411
8991
|
}
|
|
8412
8992
|
});
|
|
8413
8993
|
try {
|
|
8414
|
-
const remainingFiles =
|
|
8994
|
+
const remainingFiles = fs22.readdirSync(logDir);
|
|
8415
8995
|
if (remainingFiles.length === 0) {
|
|
8416
|
-
|
|
8996
|
+
fs22.rmdirSync(logDir);
|
|
8417
8997
|
}
|
|
8418
8998
|
} catch {
|
|
8419
8999
|
}
|
|
@@ -8444,21 +9024,21 @@ function uninstallCommand(program2) {
|
|
|
8444
9024
|
existingEntries.forEach((entry) => dim(` ${entry}`));
|
|
8445
9025
|
const removedCount = removeEntriesForProject(projectDir, marker);
|
|
8446
9026
|
if (!options.keepLogs) {
|
|
8447
|
-
const logDir =
|
|
8448
|
-
if (
|
|
9027
|
+
const logDir = path23.join(projectDir, "logs");
|
|
9028
|
+
if (fs22.existsSync(logDir)) {
|
|
8449
9029
|
const logFiles = ["executor.log", "reviewer.log", "slicer.log", "audit.log"];
|
|
8450
9030
|
let logsRemoved = 0;
|
|
8451
9031
|
logFiles.forEach((logFile) => {
|
|
8452
|
-
const logPath =
|
|
8453
|
-
if (
|
|
8454
|
-
|
|
9032
|
+
const logPath = path23.join(logDir, logFile);
|
|
9033
|
+
if (fs22.existsSync(logPath)) {
|
|
9034
|
+
fs22.unlinkSync(logPath);
|
|
8455
9035
|
logsRemoved++;
|
|
8456
9036
|
}
|
|
8457
9037
|
});
|
|
8458
9038
|
try {
|
|
8459
|
-
const remainingFiles =
|
|
9039
|
+
const remainingFiles = fs22.readdirSync(logDir);
|
|
8460
9040
|
if (remainingFiles.length === 0) {
|
|
8461
|
-
|
|
9041
|
+
fs22.rmdirSync(logDir);
|
|
8462
9042
|
}
|
|
8463
9043
|
} catch {
|
|
8464
9044
|
}
|
|
@@ -8684,11 +9264,11 @@ function statusCommand(program2) {
|
|
|
8684
9264
|
}
|
|
8685
9265
|
init_dist();
|
|
8686
9266
|
function getLastLines(filePath, lineCount) {
|
|
8687
|
-
if (!
|
|
9267
|
+
if (!fs23.existsSync(filePath)) {
|
|
8688
9268
|
return `Log file not found: ${filePath}`;
|
|
8689
9269
|
}
|
|
8690
9270
|
try {
|
|
8691
|
-
const content =
|
|
9271
|
+
const content = fs23.readFileSync(filePath, "utf-8");
|
|
8692
9272
|
const lines = content.trim().split("\n");
|
|
8693
9273
|
return lines.slice(-lineCount).join("\n");
|
|
8694
9274
|
} catch (error2) {
|
|
@@ -8696,7 +9276,7 @@ function getLastLines(filePath, lineCount) {
|
|
|
8696
9276
|
}
|
|
8697
9277
|
}
|
|
8698
9278
|
function followLog(filePath) {
|
|
8699
|
-
if (!
|
|
9279
|
+
if (!fs23.existsSync(filePath)) {
|
|
8700
9280
|
console.log(`Log file not found: ${filePath}`);
|
|
8701
9281
|
console.log("The log file will be created when the first execution runs.");
|
|
8702
9282
|
return;
|
|
@@ -8716,13 +9296,13 @@ function logsCommand(program2) {
|
|
|
8716
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) => {
|
|
8717
9297
|
try {
|
|
8718
9298
|
const projectDir = process.cwd();
|
|
8719
|
-
const logDir =
|
|
9299
|
+
const logDir = path24.join(projectDir, LOG_DIR);
|
|
8720
9300
|
const lineCount = parseInt(options.lines || "50", 10);
|
|
8721
|
-
const executorLog =
|
|
8722
|
-
const reviewerLog =
|
|
8723
|
-
const qaLog =
|
|
8724
|
-
const auditLog =
|
|
8725
|
-
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`);
|
|
8726
9306
|
const logType = options.type?.toLowerCase() || "all";
|
|
8727
9307
|
const showExecutor = logType === "all" || logType === "run" || logType === "executor";
|
|
8728
9308
|
const showReviewer = logType === "all" || logType === "review" || logType === "reviewer";
|
|
@@ -8792,9 +9372,9 @@ function slugify2(name) {
|
|
|
8792
9372
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
8793
9373
|
}
|
|
8794
9374
|
function getNextPrdNumber2(prdDir) {
|
|
8795
|
-
if (!
|
|
9375
|
+
if (!fs24.existsSync(prdDir))
|
|
8796
9376
|
return 1;
|
|
8797
|
-
const files =
|
|
9377
|
+
const files = fs24.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
|
|
8798
9378
|
const numbers = files.map((f) => {
|
|
8799
9379
|
const match = f.match(/^(\d+)-/);
|
|
8800
9380
|
return match ? parseInt(match[1], 10) : 0;
|
|
@@ -8816,10 +9396,10 @@ function parseDependencies(content) {
|
|
|
8816
9396
|
}
|
|
8817
9397
|
function isClaimActive(claimPath, maxRuntime) {
|
|
8818
9398
|
try {
|
|
8819
|
-
if (!
|
|
9399
|
+
if (!fs24.existsSync(claimPath)) {
|
|
8820
9400
|
return { active: false };
|
|
8821
9401
|
}
|
|
8822
|
-
const content =
|
|
9402
|
+
const content = fs24.readFileSync(claimPath, "utf-8");
|
|
8823
9403
|
const claim = JSON.parse(content);
|
|
8824
9404
|
const age = Math.floor(Date.now() / 1e3) - claim.timestamp;
|
|
8825
9405
|
if (age < maxRuntime) {
|
|
@@ -8835,9 +9415,9 @@ function prdCommand(program2) {
|
|
|
8835
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) => {
|
|
8836
9416
|
const projectDir = process.cwd();
|
|
8837
9417
|
const config = loadConfig(projectDir);
|
|
8838
|
-
const prdDir =
|
|
8839
|
-
if (!
|
|
8840
|
-
|
|
9418
|
+
const prdDir = path25.join(projectDir, config.prdDir);
|
|
9419
|
+
if (!fs24.existsSync(prdDir)) {
|
|
9420
|
+
fs24.mkdirSync(prdDir, { recursive: true });
|
|
8841
9421
|
}
|
|
8842
9422
|
let complexityScore = 5;
|
|
8843
9423
|
let dependsOn = [];
|
|
@@ -8893,20 +9473,20 @@ function prdCommand(program2) {
|
|
|
8893
9473
|
} else {
|
|
8894
9474
|
filename = `${slug}.md`;
|
|
8895
9475
|
}
|
|
8896
|
-
const filePath =
|
|
8897
|
-
if (
|
|
9476
|
+
const filePath = path25.join(prdDir, filename);
|
|
9477
|
+
if (fs24.existsSync(filePath)) {
|
|
8898
9478
|
error(`File already exists: ${filePath}`);
|
|
8899
9479
|
dim("Use a different name or remove the existing file.");
|
|
8900
9480
|
process.exit(1);
|
|
8901
9481
|
}
|
|
8902
9482
|
let customTemplate;
|
|
8903
9483
|
if (options.template) {
|
|
8904
|
-
const templatePath =
|
|
8905
|
-
if (!
|
|
9484
|
+
const templatePath = path25.resolve(options.template);
|
|
9485
|
+
if (!fs24.existsSync(templatePath)) {
|
|
8906
9486
|
error(`Template file not found: ${templatePath}`);
|
|
8907
9487
|
process.exit(1);
|
|
8908
9488
|
}
|
|
8909
|
-
customTemplate =
|
|
9489
|
+
customTemplate = fs24.readFileSync(templatePath, "utf-8");
|
|
8910
9490
|
}
|
|
8911
9491
|
const vars = {
|
|
8912
9492
|
title: name,
|
|
@@ -8917,7 +9497,7 @@ function prdCommand(program2) {
|
|
|
8917
9497
|
phaseCount
|
|
8918
9498
|
};
|
|
8919
9499
|
const content = renderPrdTemplate(vars, customTemplate);
|
|
8920
|
-
|
|
9500
|
+
fs24.writeFileSync(filePath, content, "utf-8");
|
|
8921
9501
|
header("PRD Created");
|
|
8922
9502
|
success(`Created: ${filePath}`);
|
|
8923
9503
|
info(`Title: ${name}`);
|
|
@@ -8929,15 +9509,15 @@ function prdCommand(program2) {
|
|
|
8929
9509
|
prd.command("list").description("List all PRDs with status").option("--json", "Output as JSON").action(async (options) => {
|
|
8930
9510
|
const projectDir = process.cwd();
|
|
8931
9511
|
const config = loadConfig(projectDir);
|
|
8932
|
-
const absolutePrdDir =
|
|
8933
|
-
const doneDir =
|
|
9512
|
+
const absolutePrdDir = path25.join(projectDir, config.prdDir);
|
|
9513
|
+
const doneDir = path25.join(absolutePrdDir, "done");
|
|
8934
9514
|
const pending = [];
|
|
8935
|
-
if (
|
|
8936
|
-
const files =
|
|
9515
|
+
if (fs24.existsSync(absolutePrdDir)) {
|
|
9516
|
+
const files = fs24.readdirSync(absolutePrdDir).filter((f) => f.endsWith(".md"));
|
|
8937
9517
|
for (const file of files) {
|
|
8938
|
-
const content =
|
|
9518
|
+
const content = fs24.readFileSync(path25.join(absolutePrdDir, file), "utf-8");
|
|
8939
9519
|
const deps = parseDependencies(content);
|
|
8940
|
-
const claimPath =
|
|
9520
|
+
const claimPath = path25.join(absolutePrdDir, file + CLAIM_FILE_EXTENSION);
|
|
8941
9521
|
const claimStatus = isClaimActive(claimPath, config.maxRuntime);
|
|
8942
9522
|
pending.push({
|
|
8943
9523
|
name: file,
|
|
@@ -8948,10 +9528,10 @@ function prdCommand(program2) {
|
|
|
8948
9528
|
}
|
|
8949
9529
|
}
|
|
8950
9530
|
const done = [];
|
|
8951
|
-
if (
|
|
8952
|
-
const files =
|
|
9531
|
+
if (fs24.existsSync(doneDir)) {
|
|
9532
|
+
const files = fs24.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
|
|
8953
9533
|
for (const file of files) {
|
|
8954
|
-
const content =
|
|
9534
|
+
const content = fs24.readFileSync(path25.join(doneDir, file), "utf-8");
|
|
8955
9535
|
const deps = parseDependencies(content);
|
|
8956
9536
|
done.push({ name: file, dependencies: deps });
|
|
8957
9537
|
}
|
|
@@ -8984,7 +9564,7 @@ function prdCommand(program2) {
|
|
|
8984
9564
|
}
|
|
8985
9565
|
init_dist();
|
|
8986
9566
|
init_dist();
|
|
8987
|
-
function
|
|
9567
|
+
function sortPrdsByPriority2(prds, priority) {
|
|
8988
9568
|
if (priority.length === 0)
|
|
8989
9569
|
return prds;
|
|
8990
9570
|
const priorityMap = /* @__PURE__ */ new Map();
|
|
@@ -9003,7 +9583,7 @@ function renderPrdPane(prds, priority) {
|
|
|
9003
9583
|
if (prds.length === 0) {
|
|
9004
9584
|
return "No PRD files found";
|
|
9005
9585
|
}
|
|
9006
|
-
const sorted = priority ?
|
|
9586
|
+
const sorted = priority ? sortPrdsByPriority2(prds, priority) : prds;
|
|
9007
9587
|
const lines = [];
|
|
9008
9588
|
for (const prd of sorted) {
|
|
9009
9589
|
let indicator;
|
|
@@ -9081,7 +9661,7 @@ function renderLogPane(projectDir, logs) {
|
|
|
9081
9661
|
let newestMtime = 0;
|
|
9082
9662
|
for (const log of existingLogs) {
|
|
9083
9663
|
try {
|
|
9084
|
-
const stat =
|
|
9664
|
+
const stat = fs25.statSync(log.path);
|
|
9085
9665
|
if (stat.mtimeMs > newestMtime) {
|
|
9086
9666
|
newestMtime = stat.mtimeMs;
|
|
9087
9667
|
newestLog = log;
|
|
@@ -9206,7 +9786,7 @@ function createStatusTab() {
|
|
|
9206
9786
|
ctx.showMessage("No PRDs to reorder", "info");
|
|
9207
9787
|
return;
|
|
9208
9788
|
}
|
|
9209
|
-
const sorted =
|
|
9789
|
+
const sorted = sortPrdsByPriority2(nonDone, ctx.config.prdPriority);
|
|
9210
9790
|
reorderList = sorted.map((p) => p.name);
|
|
9211
9791
|
reorderIndex = 0;
|
|
9212
9792
|
reorderMode = true;
|
|
@@ -10762,7 +11342,7 @@ function createLogsTab() {
|
|
|
10762
11342
|
let activeKeyHandlers = [];
|
|
10763
11343
|
let activeCtx = null;
|
|
10764
11344
|
function getLogPath(projectDir, logName) {
|
|
10765
|
-
return
|
|
11345
|
+
return path26.join(projectDir, "logs", `${logName}.log`);
|
|
10766
11346
|
}
|
|
10767
11347
|
function updateSelector() {
|
|
10768
11348
|
const tabs = LOG_NAMES.map((name, idx) => {
|
|
@@ -10776,7 +11356,7 @@ function createLogsTab() {
|
|
|
10776
11356
|
function loadLog(ctx) {
|
|
10777
11357
|
const logName = LOG_NAMES[selectedLogIndex];
|
|
10778
11358
|
const logPath = getLogPath(ctx.projectDir, logName);
|
|
10779
|
-
if (!
|
|
11359
|
+
if (!fs26.existsSync(logPath)) {
|
|
10780
11360
|
logContent.setContent(`{yellow-fg}No ${logName}.log file found{/yellow-fg}
|
|
10781
11361
|
|
|
10782
11362
|
Log will appear here once the ${logName} runs.`);
|
|
@@ -10784,7 +11364,7 @@ Log will appear here once the ${logName} runs.`);
|
|
|
10784
11364
|
return;
|
|
10785
11365
|
}
|
|
10786
11366
|
try {
|
|
10787
|
-
const stat =
|
|
11367
|
+
const stat = fs26.statSync(logPath);
|
|
10788
11368
|
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
10789
11369
|
logContent.setLabel(`[ ${logName}.log - ${sizeKB} KB ]`);
|
|
10790
11370
|
} catch {
|
|
@@ -11349,7 +11929,7 @@ function resolveProject(req, res, next) {
|
|
|
11349
11929
|
res.status(404).json({ error: `Project not found: ${decodedId}` });
|
|
11350
11930
|
return;
|
|
11351
11931
|
}
|
|
11352
|
-
if (!
|
|
11932
|
+
if (!fs27.existsSync(entry.path) || !fs27.existsSync(path27.join(entry.path, CONFIG_FILE_NAME))) {
|
|
11353
11933
|
res.status(404).json({ error: `Project path invalid or missing config: ${entry.path}` });
|
|
11354
11934
|
return;
|
|
11355
11935
|
}
|
|
@@ -11434,17 +12014,17 @@ function getBoardProvider(config, projectDir) {
|
|
|
11434
12014
|
function cleanOrphanedClaims(dir) {
|
|
11435
12015
|
let entries;
|
|
11436
12016
|
try {
|
|
11437
|
-
entries =
|
|
12017
|
+
entries = fs28.readdirSync(dir, { withFileTypes: true });
|
|
11438
12018
|
} catch {
|
|
11439
12019
|
return;
|
|
11440
12020
|
}
|
|
11441
12021
|
for (const entry of entries) {
|
|
11442
|
-
const fullPath =
|
|
12022
|
+
const fullPath = path28.join(dir, entry.name);
|
|
11443
12023
|
if (entry.isDirectory() && entry.name !== "done") {
|
|
11444
12024
|
cleanOrphanedClaims(fullPath);
|
|
11445
12025
|
} else if (entry.name.endsWith(CLAIM_FILE_EXTENSION)) {
|
|
11446
12026
|
try {
|
|
11447
|
-
|
|
12027
|
+
fs28.unlinkSync(fullPath);
|
|
11448
12028
|
} catch {
|
|
11449
12029
|
}
|
|
11450
12030
|
}
|
|
@@ -11492,7 +12072,7 @@ function spawnAction2(projectDir, command, req, res, onSpawned) {
|
|
|
11492
12072
|
const config = loadConfig(projectDir);
|
|
11493
12073
|
sendNotifications(config, {
|
|
11494
12074
|
event: "run_started",
|
|
11495
|
-
projectName:
|
|
12075
|
+
projectName: path28.basename(projectDir),
|
|
11496
12076
|
exitCode: 0,
|
|
11497
12077
|
provider: config.provider
|
|
11498
12078
|
}).catch(() => {
|
|
@@ -11574,19 +12154,19 @@ function createActionRouteHandlers(ctx) {
|
|
|
11574
12154
|
res.status(400).json({ error: "Invalid PRD name" });
|
|
11575
12155
|
return;
|
|
11576
12156
|
}
|
|
11577
|
-
const prdDir =
|
|
12157
|
+
const prdDir = path28.join(projectDir, config.prdDir);
|
|
11578
12158
|
const normalized = prdName.endsWith(".md") ? prdName : `${prdName}.md`;
|
|
11579
|
-
const pendingPath =
|
|
11580
|
-
const donePath =
|
|
11581
|
-
if (
|
|
12159
|
+
const pendingPath = path28.join(prdDir, normalized);
|
|
12160
|
+
const donePath = path28.join(prdDir, "done", normalized);
|
|
12161
|
+
if (fs28.existsSync(pendingPath)) {
|
|
11582
12162
|
res.json({ message: `"${normalized}" is already pending` });
|
|
11583
12163
|
return;
|
|
11584
12164
|
}
|
|
11585
|
-
if (!
|
|
12165
|
+
if (!fs28.existsSync(donePath)) {
|
|
11586
12166
|
res.status(404).json({ error: `PRD "${normalized}" not found in done/` });
|
|
11587
12167
|
return;
|
|
11588
12168
|
}
|
|
11589
|
-
|
|
12169
|
+
fs28.renameSync(donePath, pendingPath);
|
|
11590
12170
|
res.json({ message: `Moved "${normalized}" back to pending` });
|
|
11591
12171
|
} catch (error2) {
|
|
11592
12172
|
res.status(500).json({
|
|
@@ -11604,11 +12184,11 @@ function createActionRouteHandlers(ctx) {
|
|
|
11604
12184
|
res.status(409).json({ error: "Executor is actively running \u2014 use Stop instead" });
|
|
11605
12185
|
return;
|
|
11606
12186
|
}
|
|
11607
|
-
if (
|
|
11608
|
-
|
|
12187
|
+
if (fs28.existsSync(lockPath)) {
|
|
12188
|
+
fs28.unlinkSync(lockPath);
|
|
11609
12189
|
}
|
|
11610
|
-
const prdDir =
|
|
11611
|
-
if (
|
|
12190
|
+
const prdDir = path28.join(projectDir, config.prdDir);
|
|
12191
|
+
if (fs28.existsSync(prdDir)) {
|
|
11612
12192
|
cleanOrphanedClaims(prdDir);
|
|
11613
12193
|
}
|
|
11614
12194
|
broadcastSSE(ctx.getSseClients(req), "status_changed", await fetchStatusSnapshot(projectDir, config));
|
|
@@ -11745,6 +12325,7 @@ function createAgentRoutes() {
|
|
|
11745
12325
|
return router;
|
|
11746
12326
|
}
|
|
11747
12327
|
init_dist();
|
|
12328
|
+
var ERROR_BOARD_NOT_CONFIGURED = "Board not configured";
|
|
11748
12329
|
function createBoardRouteHandlers(ctx) {
|
|
11749
12330
|
const router = Router3({ mergeParams: true });
|
|
11750
12331
|
const p = ctx.pathPrefix;
|
|
@@ -11754,7 +12335,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11754
12335
|
const projectDir = ctx.getProjectDir(req);
|
|
11755
12336
|
const provider = getBoardProvider(config, projectDir);
|
|
11756
12337
|
if (!provider) {
|
|
11757
|
-
res.status(404).json({ error:
|
|
12338
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11758
12339
|
return;
|
|
11759
12340
|
}
|
|
11760
12341
|
const cached = getCachedBoardData(projectDir);
|
|
@@ -11787,7 +12368,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11787
12368
|
const projectDir = ctx.getProjectDir(req);
|
|
11788
12369
|
const provider = getBoardProvider(config, projectDir);
|
|
11789
12370
|
if (!provider) {
|
|
11790
|
-
res.status(404).json({ error:
|
|
12371
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11791
12372
|
return;
|
|
11792
12373
|
}
|
|
11793
12374
|
const issues = await provider.getAllIssues();
|
|
@@ -11802,7 +12383,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11802
12383
|
const projectDir = ctx.getProjectDir(req);
|
|
11803
12384
|
const provider = getBoardProvider(config, projectDir);
|
|
11804
12385
|
if (!provider) {
|
|
11805
|
-
res.status(404).json({ error:
|
|
12386
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11806
12387
|
return;
|
|
11807
12388
|
}
|
|
11808
12389
|
const { title, body, column } = req.body;
|
|
@@ -11833,7 +12414,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11833
12414
|
const projectDir = ctx.getProjectDir(req);
|
|
11834
12415
|
const provider = getBoardProvider(config, projectDir);
|
|
11835
12416
|
if (!provider) {
|
|
11836
|
-
res.status(404).json({ error:
|
|
12417
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11837
12418
|
return;
|
|
11838
12419
|
}
|
|
11839
12420
|
const issueNumber = parseInt(req.params.number, 10);
|
|
@@ -11863,7 +12444,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11863
12444
|
const projectDir = ctx.getProjectDir(req);
|
|
11864
12445
|
const provider = getBoardProvider(config, projectDir);
|
|
11865
12446
|
if (!provider) {
|
|
11866
|
-
res.status(404).json({ error:
|
|
12447
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11867
12448
|
return;
|
|
11868
12449
|
}
|
|
11869
12450
|
const issueNumber = parseInt(req.params.number, 10);
|
|
@@ -11891,7 +12472,7 @@ function createBoardRouteHandlers(ctx) {
|
|
|
11891
12472
|
const projectDir = ctx.getProjectDir(req);
|
|
11892
12473
|
const provider = getBoardProvider(config, projectDir);
|
|
11893
12474
|
if (!provider) {
|
|
11894
|
-
res.status(404).json({ error:
|
|
12475
|
+
res.status(404).json({ error: ERROR_BOARD_NOT_CONFIGURED });
|
|
11895
12476
|
return;
|
|
11896
12477
|
}
|
|
11897
12478
|
const issueNumber = parseInt(req.params.number, 10);
|
|
@@ -12179,7 +12760,7 @@ function runDoctorChecks(projectDir, config) {
|
|
|
12179
12760
|
});
|
|
12180
12761
|
}
|
|
12181
12762
|
try {
|
|
12182
|
-
const projectName =
|
|
12763
|
+
const projectName = path29.basename(projectDir);
|
|
12183
12764
|
const marker = generateMarker(projectName);
|
|
12184
12765
|
const crontabEntries = [...getEntries(marker), ...getProjectEntries(projectDir)];
|
|
12185
12766
|
if (crontabEntries.length > 0) {
|
|
@@ -12202,8 +12783,8 @@ function runDoctorChecks(projectDir, config) {
|
|
|
12202
12783
|
detail: "Failed to check crontab"
|
|
12203
12784
|
});
|
|
12204
12785
|
}
|
|
12205
|
-
const configPath =
|
|
12206
|
-
if (
|
|
12786
|
+
const configPath = path29.join(projectDir, CONFIG_FILE_NAME);
|
|
12787
|
+
if (fs29.existsSync(configPath)) {
|
|
12207
12788
|
checks.push({ name: "config", status: "pass", detail: "Config file exists" });
|
|
12208
12789
|
} else {
|
|
12209
12790
|
checks.push({
|
|
@@ -12212,9 +12793,9 @@ function runDoctorChecks(projectDir, config) {
|
|
|
12212
12793
|
detail: "Config file not found (using defaults)"
|
|
12213
12794
|
});
|
|
12214
12795
|
}
|
|
12215
|
-
const prdDir =
|
|
12216
|
-
if (
|
|
12217
|
-
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"));
|
|
12218
12799
|
checks.push({
|
|
12219
12800
|
name: "prdDir",
|
|
12220
12801
|
status: "pass",
|
|
@@ -12272,7 +12853,7 @@ function createLogRoutes(deps) {
|
|
|
12272
12853
|
const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
|
|
12273
12854
|
const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
|
|
12274
12855
|
const fileName = LOG_FILE_NAMES[name] || name;
|
|
12275
|
-
const logPath =
|
|
12856
|
+
const logPath = path30.join(projectDir, LOG_DIR, `${fileName}.log`);
|
|
12276
12857
|
const logLines = getLastLogLines(logPath, linesToRead);
|
|
12277
12858
|
res.json({ name, lines: logLines });
|
|
12278
12859
|
} catch (error2) {
|
|
@@ -12298,7 +12879,7 @@ function createProjectLogRoutes() {
|
|
|
12298
12879
|
const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
|
|
12299
12880
|
const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
|
|
12300
12881
|
const fileName = LOG_FILE_NAMES[name] || name;
|
|
12301
|
-
const logPath =
|
|
12882
|
+
const logPath = path30.join(projectDir, LOG_DIR, `${fileName}.log`);
|
|
12302
12883
|
const logLines = getLastLogLines(logPath, linesToRead);
|
|
12303
12884
|
res.json({ name, lines: logLines });
|
|
12304
12885
|
} catch (error2) {
|
|
@@ -12336,7 +12917,7 @@ function createRoadmapRouteHandlers(ctx) {
|
|
|
12336
12917
|
const config = ctx.getConfig(req);
|
|
12337
12918
|
const projectDir = ctx.getProjectDir(req);
|
|
12338
12919
|
const status = getRoadmapStatus(projectDir, config);
|
|
12339
|
-
const prdDir =
|
|
12920
|
+
const prdDir = path31.join(projectDir, config.prdDir);
|
|
12340
12921
|
const state = loadRoadmapState(prdDir);
|
|
12341
12922
|
res.json({
|
|
12342
12923
|
...status,
|
|
@@ -12593,14 +13174,14 @@ data: ${JSON.stringify(snapshot)}
|
|
|
12593
13174
|
var __filename2 = fileURLToPath3(import.meta.url);
|
|
12594
13175
|
var __dirname3 = dirname7(__filename2);
|
|
12595
13176
|
function resolveWebDistPath() {
|
|
12596
|
-
const bundled =
|
|
12597
|
-
if (
|
|
13177
|
+
const bundled = path32.join(__dirname3, "web");
|
|
13178
|
+
if (fs30.existsSync(path32.join(bundled, "index.html")))
|
|
12598
13179
|
return bundled;
|
|
12599
13180
|
let d = __dirname3;
|
|
12600
13181
|
for (let i = 0; i < 8; i++) {
|
|
12601
|
-
if (
|
|
12602
|
-
const dev =
|
|
12603
|
-
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")))
|
|
12604
13185
|
return dev;
|
|
12605
13186
|
break;
|
|
12606
13187
|
}
|
|
@@ -12610,7 +13191,7 @@ function resolveWebDistPath() {
|
|
|
12610
13191
|
}
|
|
12611
13192
|
function setupStaticFiles(app) {
|
|
12612
13193
|
const webDistPath = resolveWebDistPath();
|
|
12613
|
-
if (
|
|
13194
|
+
if (fs30.existsSync(webDistPath)) {
|
|
12614
13195
|
app.use(express.static(webDistPath));
|
|
12615
13196
|
}
|
|
12616
13197
|
app.use((req, res, next) => {
|
|
@@ -12618,8 +13199,8 @@ function setupStaticFiles(app) {
|
|
|
12618
13199
|
next();
|
|
12619
13200
|
return;
|
|
12620
13201
|
}
|
|
12621
|
-
const indexPath =
|
|
12622
|
-
if (
|
|
13202
|
+
const indexPath = path32.resolve(webDistPath, "index.html");
|
|
13203
|
+
if (fs30.existsSync(indexPath)) {
|
|
12623
13204
|
res.sendFile(indexPath, (err) => {
|
|
12624
13205
|
if (err)
|
|
12625
13206
|
next();
|
|
@@ -12729,7 +13310,7 @@ function createGlobalApp() {
|
|
|
12729
13310
|
return app;
|
|
12730
13311
|
}
|
|
12731
13312
|
function bootContainer() {
|
|
12732
|
-
initContainer(
|
|
13313
|
+
initContainer(path32.dirname(getDbPath()));
|
|
12733
13314
|
const personaRepo = container.resolve(SqliteAgentPersonaRepository);
|
|
12734
13315
|
personaRepo.seedDefaultsOnFirstRun();
|
|
12735
13316
|
personaRepo.patchDefaultAvatarUrls();
|
|
@@ -12783,9 +13364,9 @@ function isProcessRunning2(pid) {
|
|
|
12783
13364
|
}
|
|
12784
13365
|
function readPid(lockPath) {
|
|
12785
13366
|
try {
|
|
12786
|
-
if (!
|
|
13367
|
+
if (!fs31.existsSync(lockPath))
|
|
12787
13368
|
return null;
|
|
12788
|
-
const raw =
|
|
13369
|
+
const raw = fs31.readFileSync(lockPath, "utf-8").trim();
|
|
12789
13370
|
const pid = parseInt(raw, 10);
|
|
12790
13371
|
return Number.isFinite(pid) ? pid : null;
|
|
12791
13372
|
} catch {
|
|
@@ -12797,10 +13378,10 @@ function acquireServeLock(mode, port) {
|
|
|
12797
13378
|
let stalePidCleaned;
|
|
12798
13379
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
12799
13380
|
try {
|
|
12800
|
-
const fd =
|
|
12801
|
-
|
|
13381
|
+
const fd = fs31.openSync(lockPath, "wx");
|
|
13382
|
+
fs31.writeFileSync(fd, `${process.pid}
|
|
12802
13383
|
`);
|
|
12803
|
-
|
|
13384
|
+
fs31.closeSync(fd);
|
|
12804
13385
|
return { acquired: true, lockPath, stalePidCleaned };
|
|
12805
13386
|
} catch (error2) {
|
|
12806
13387
|
const err = error2;
|
|
@@ -12821,7 +13402,7 @@ function acquireServeLock(mode, port) {
|
|
|
12821
13402
|
};
|
|
12822
13403
|
}
|
|
12823
13404
|
try {
|
|
12824
|
-
|
|
13405
|
+
fs31.unlinkSync(lockPath);
|
|
12825
13406
|
if (existingPid) {
|
|
12826
13407
|
stalePidCleaned = existingPid;
|
|
12827
13408
|
}
|
|
@@ -12844,12 +13425,12 @@ function acquireServeLock(mode, port) {
|
|
|
12844
13425
|
}
|
|
12845
13426
|
function releaseServeLock(lockPath) {
|
|
12846
13427
|
try {
|
|
12847
|
-
if (!
|
|
13428
|
+
if (!fs31.existsSync(lockPath))
|
|
12848
13429
|
return;
|
|
12849
13430
|
const lockPid = readPid(lockPath);
|
|
12850
13431
|
if (lockPid !== null && lockPid !== process.pid)
|
|
12851
13432
|
return;
|
|
12852
|
-
|
|
13433
|
+
fs31.unlinkSync(lockPath);
|
|
12853
13434
|
} catch {
|
|
12854
13435
|
}
|
|
12855
13436
|
}
|
|
@@ -12935,7 +13516,7 @@ function parseProjectDirs(projects, cwd) {
|
|
|
12935
13516
|
if (!projects || projects.trim().length === 0) {
|
|
12936
13517
|
return [cwd];
|
|
12937
13518
|
}
|
|
12938
|
-
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));
|
|
12939
13520
|
return Array.from(new Set(dirs));
|
|
12940
13521
|
}
|
|
12941
13522
|
function runCommand2(command, args, cwd) {
|
|
@@ -12974,7 +13555,7 @@ function updateCommand(program2) {
|
|
|
12974
13555
|
}
|
|
12975
13556
|
const nightWatchBin = resolveNightWatchBin();
|
|
12976
13557
|
for (const projectDir of projectDirs) {
|
|
12977
|
-
if (!
|
|
13558
|
+
if (!fs32.existsSync(projectDir) || !fs32.statSync(projectDir).isDirectory()) {
|
|
12978
13559
|
warn(`Skipping invalid project directory: ${projectDir}`);
|
|
12979
13560
|
continue;
|
|
12980
13561
|
}
|
|
@@ -13021,26 +13602,26 @@ function normalizePrdName(name) {
|
|
|
13021
13602
|
return name;
|
|
13022
13603
|
}
|
|
13023
13604
|
function getDonePrds(doneDir) {
|
|
13024
|
-
if (!
|
|
13605
|
+
if (!fs33.existsSync(doneDir)) {
|
|
13025
13606
|
return [];
|
|
13026
13607
|
}
|
|
13027
|
-
return
|
|
13608
|
+
return fs33.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
|
|
13028
13609
|
}
|
|
13029
13610
|
function retryCommand(program2) {
|
|
13030
13611
|
program2.command("retry <prdName>").description("Move a completed PRD from done/ back to pending").action((prdName) => {
|
|
13031
13612
|
const projectDir = process.cwd();
|
|
13032
13613
|
const config = loadConfig(projectDir);
|
|
13033
|
-
const prdDir =
|
|
13034
|
-
const doneDir =
|
|
13614
|
+
const prdDir = path34.join(projectDir, config.prdDir);
|
|
13615
|
+
const doneDir = path34.join(prdDir, "done");
|
|
13035
13616
|
const normalizedPrdName = normalizePrdName(prdName);
|
|
13036
|
-
const pendingPath =
|
|
13037
|
-
if (
|
|
13617
|
+
const pendingPath = path34.join(prdDir, normalizedPrdName);
|
|
13618
|
+
if (fs33.existsSync(pendingPath)) {
|
|
13038
13619
|
info(`"${normalizedPrdName}" is already pending, nothing to retry.`);
|
|
13039
13620
|
return;
|
|
13040
13621
|
}
|
|
13041
|
-
const donePath =
|
|
13042
|
-
if (
|
|
13043
|
-
|
|
13622
|
+
const donePath = path34.join(doneDir, normalizedPrdName);
|
|
13623
|
+
if (fs33.existsSync(donePath)) {
|
|
13624
|
+
fs33.renameSync(donePath, pendingPath);
|
|
13044
13625
|
success(`Moved "${normalizedPrdName}" back to pending.`);
|
|
13045
13626
|
dim(`From: ${donePath}`);
|
|
13046
13627
|
dim(`To: ${pendingPath}`);
|
|
@@ -13214,7 +13795,7 @@ function prdsCommand(program2) {
|
|
|
13214
13795
|
const config = loadConfig(projectDir);
|
|
13215
13796
|
const prds = collectPrdInfo(projectDir, config.prdDir, config.maxRuntime);
|
|
13216
13797
|
const openPrBranches = getOpenPrBranches(projectDir);
|
|
13217
|
-
const filteredPrds = prds
|
|
13798
|
+
const filteredPrds = prds;
|
|
13218
13799
|
for (const prd of filteredPrds) {
|
|
13219
13800
|
if (prd.status !== "done") {
|
|
13220
13801
|
const matchingPr = findMatchingPr(prd.name, openPrBranches, config.branchPrefix);
|
|
@@ -13322,7 +13903,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
13322
13903
|
const pid = lockStatus.pid;
|
|
13323
13904
|
if (!lockStatus.running) {
|
|
13324
13905
|
try {
|
|
13325
|
-
|
|
13906
|
+
fs34.unlinkSync(lockPath);
|
|
13326
13907
|
return {
|
|
13327
13908
|
success: true,
|
|
13328
13909
|
message: `${processType} is not running (cleaned up stale lock file for PID ${pid})`,
|
|
@@ -13360,7 +13941,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
13360
13941
|
await sleep3(3e3);
|
|
13361
13942
|
if (!isProcessRunning3(pid)) {
|
|
13362
13943
|
try {
|
|
13363
|
-
|
|
13944
|
+
fs34.unlinkSync(lockPath);
|
|
13364
13945
|
} catch {
|
|
13365
13946
|
}
|
|
13366
13947
|
return {
|
|
@@ -13395,7 +13976,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
13395
13976
|
await sleep3(500);
|
|
13396
13977
|
if (!isProcessRunning3(pid)) {
|
|
13397
13978
|
try {
|
|
13398
|
-
|
|
13979
|
+
fs34.unlinkSync(lockPath);
|
|
13399
13980
|
} catch {
|
|
13400
13981
|
}
|
|
13401
13982
|
return {
|
|
@@ -13462,24 +14043,24 @@ function plannerLockPath2(projectDir) {
|
|
|
13462
14043
|
}
|
|
13463
14044
|
function acquirePlannerLock(projectDir) {
|
|
13464
14045
|
const lockFile = plannerLockPath2(projectDir);
|
|
13465
|
-
if (
|
|
13466
|
-
const pidRaw =
|
|
14046
|
+
if (fs35.existsSync(lockFile)) {
|
|
14047
|
+
const pidRaw = fs35.readFileSync(lockFile, "utf-8").trim();
|
|
13467
14048
|
const pid = parseInt(pidRaw, 10);
|
|
13468
14049
|
if (!Number.isNaN(pid) && isProcessRunning(pid)) {
|
|
13469
14050
|
return { acquired: false, lockFile, pid };
|
|
13470
14051
|
}
|
|
13471
14052
|
try {
|
|
13472
|
-
|
|
14053
|
+
fs35.unlinkSync(lockFile);
|
|
13473
14054
|
} catch {
|
|
13474
14055
|
}
|
|
13475
14056
|
}
|
|
13476
|
-
|
|
14057
|
+
fs35.writeFileSync(lockFile, String(process.pid));
|
|
13477
14058
|
return { acquired: true, lockFile };
|
|
13478
14059
|
}
|
|
13479
14060
|
function releasePlannerLock(lockFile) {
|
|
13480
14061
|
try {
|
|
13481
|
-
if (
|
|
13482
|
-
|
|
14062
|
+
if (fs35.existsSync(lockFile)) {
|
|
14063
|
+
fs35.unlinkSync(lockFile);
|
|
13483
14064
|
}
|
|
13484
14065
|
} catch {
|
|
13485
14066
|
}
|
|
@@ -13601,7 +14182,7 @@ function sliceCommand(program2) {
|
|
|
13601
14182
|
if (!options.dryRun) {
|
|
13602
14183
|
await sendNotifications(config, {
|
|
13603
14184
|
event: "run_started",
|
|
13604
|
-
projectName:
|
|
14185
|
+
projectName: path35.basename(projectDir),
|
|
13605
14186
|
exitCode: 0,
|
|
13606
14187
|
provider: config.provider
|
|
13607
14188
|
});
|
|
@@ -13621,7 +14202,7 @@ function sliceCommand(program2) {
|
|
|
13621
14202
|
if (!options.dryRun && result.sliced) {
|
|
13622
14203
|
await sendNotifications(config, {
|
|
13623
14204
|
event: "run_succeeded",
|
|
13624
|
-
projectName:
|
|
14205
|
+
projectName: path35.basename(projectDir),
|
|
13625
14206
|
exitCode,
|
|
13626
14207
|
provider: config.provider,
|
|
13627
14208
|
prTitle: result.item?.title
|
|
@@ -13629,7 +14210,7 @@ function sliceCommand(program2) {
|
|
|
13629
14210
|
} else if (!options.dryRun && !nothingPending) {
|
|
13630
14211
|
await sendNotifications(config, {
|
|
13631
14212
|
event: "run_failed",
|
|
13632
|
-
projectName:
|
|
14213
|
+
projectName: path35.basename(projectDir),
|
|
13633
14214
|
exitCode,
|
|
13634
14215
|
provider: config.provider
|
|
13635
14216
|
});
|
|
@@ -13647,13 +14228,13 @@ function createStateCommand() {
|
|
|
13647
14228
|
const state = new Command("state");
|
|
13648
14229
|
state.description("Manage Night Watch state");
|
|
13649
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) => {
|
|
13650
|
-
const nightWatchHome = process.env.NIGHT_WATCH_HOME ||
|
|
14231
|
+
const nightWatchHome = process.env.NIGHT_WATCH_HOME || path36.join(os6.homedir(), GLOBAL_CONFIG_DIR);
|
|
13651
14232
|
if (opts.dryRun) {
|
|
13652
14233
|
console.log(chalk5.cyan("Dry-run mode: no changes will be made.\n"));
|
|
13653
14234
|
console.log(`Legacy JSON files that would be migrated from: ${chalk5.bold(nightWatchHome)}`);
|
|
13654
|
-
console.log(` ${
|
|
13655
|
-
console.log(` ${
|
|
13656
|
-
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")}`);
|
|
13657
14238
|
console.log(` <project>/<prdDir>/.roadmap-state.json (per project)`);
|
|
13658
14239
|
console.log(chalk5.dim("\nRun without --dry-run to apply the migration."));
|
|
13659
14240
|
return;
|
|
@@ -13701,7 +14282,7 @@ function getProvider(config, cwd) {
|
|
|
13701
14282
|
return createBoardProvider(bp, cwd);
|
|
13702
14283
|
}
|
|
13703
14284
|
function defaultBoardTitle(cwd) {
|
|
13704
|
-
return `${
|
|
14285
|
+
return `${path37.basename(cwd)} Night Watch`;
|
|
13705
14286
|
}
|
|
13706
14287
|
async function ensureBoardConfigured(config, cwd, provider, options) {
|
|
13707
14288
|
if (config.boardProvider?.projectNumber) {
|
|
@@ -13741,7 +14322,7 @@ async function confirmPrompt(question) {
|
|
|
13741
14322
|
}
|
|
13742
14323
|
async function createGitHubLabel(label2, cwd) {
|
|
13743
14324
|
try {
|
|
13744
|
-
|
|
14325
|
+
execFileSync6("gh", ["label", "create", label2.name, "--description", label2.description, "--color", label2.color], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
13745
14326
|
return { created: true, skipped: false };
|
|
13746
14327
|
} catch (err) {
|
|
13747
14328
|
const output = err instanceof Error ? err.message : String(err);
|
|
@@ -13881,11 +14462,11 @@ function boardCommand(program2) {
|
|
|
13881
14462
|
let body = options.body ?? "";
|
|
13882
14463
|
if (options.bodyFile) {
|
|
13883
14464
|
const filePath = options.bodyFile;
|
|
13884
|
-
if (!
|
|
14465
|
+
if (!fs36.existsSync(filePath)) {
|
|
13885
14466
|
console.error(`File not found: ${filePath}`);
|
|
13886
14467
|
process.exit(1);
|
|
13887
14468
|
}
|
|
13888
|
-
body =
|
|
14469
|
+
body = fs36.readFileSync(filePath, "utf-8");
|
|
13889
14470
|
}
|
|
13890
14471
|
const labels = [];
|
|
13891
14472
|
if (options.label) {
|
|
@@ -14093,12 +14674,12 @@ function boardCommand(program2) {
|
|
|
14093
14674
|
const config = loadConfig(cwd);
|
|
14094
14675
|
const provider = getProvider(config, cwd);
|
|
14095
14676
|
await ensureBoardConfigured(config, cwd, provider);
|
|
14096
|
-
const roadmapPath = options.roadmap ??
|
|
14097
|
-
if (!
|
|
14677
|
+
const roadmapPath = options.roadmap ?? path37.join(cwd, "ROADMAP.md");
|
|
14678
|
+
if (!fs36.existsSync(roadmapPath)) {
|
|
14098
14679
|
console.error(`Roadmap file not found: ${roadmapPath}`);
|
|
14099
14680
|
process.exit(1);
|
|
14100
14681
|
}
|
|
14101
|
-
const roadmapContent =
|
|
14682
|
+
const roadmapContent = fs36.readFileSync(roadmapPath, "utf-8");
|
|
14102
14683
|
const items = parseRoadmap(roadmapContent);
|
|
14103
14684
|
const uncheckedItems = getUncheckedItems(items);
|
|
14104
14685
|
if (uncheckedItems.length === 0) {
|
|
@@ -14192,7 +14773,7 @@ function boardCommand(program2) {
|
|
|
14192
14773
|
try {
|
|
14193
14774
|
const labelsToAdd = [category, horizon].filter((l) => !issue.labels.includes(l));
|
|
14194
14775
|
if (labelsToAdd.length > 0) {
|
|
14195
|
-
|
|
14776
|
+
execFileSync6("gh", ["issue", "edit", String(issue.number), "--add-label", labelsToAdd.join(",")], { cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
14196
14777
|
}
|
|
14197
14778
|
updated++;
|
|
14198
14779
|
success(`Updated labels on #${issue.number}: ${item.title}`);
|
|
@@ -14216,14 +14797,14 @@ var __dirname4 = dirname8(__filename3);
|
|
|
14216
14797
|
function findPackageRoot(dir) {
|
|
14217
14798
|
let d = dir;
|
|
14218
14799
|
for (let i = 0; i < 5; i++) {
|
|
14219
|
-
if (
|
|
14800
|
+
if (existsSync30(join33(d, "package.json")))
|
|
14220
14801
|
return d;
|
|
14221
14802
|
d = dirname8(d);
|
|
14222
14803
|
}
|
|
14223
14804
|
return dir;
|
|
14224
14805
|
}
|
|
14225
14806
|
var packageRoot = findPackageRoot(__dirname4);
|
|
14226
|
-
var packageJson = JSON.parse(
|
|
14807
|
+
var packageJson = JSON.parse(readFileSync18(join33(packageRoot, "package.json"), "utf-8"));
|
|
14227
14808
|
var program = new Command2();
|
|
14228
14809
|
program.name("night-watch").description("Autonomous PRD execution using Claude CLI + cron").version(packageJson.version);
|
|
14229
14810
|
initCommand(program);
|