@jonit-dev/night-watch-cli 1.7.48 → 1.7.50

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