@jonit-dev/night-watch-cli 1.7.57 → 1.7.58

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
@@ -1,5 +1,173 @@
1
1
  #!/usr/bin/env node
2
2
  import 'reflect-metadata';
3
+
4
+ // dist/cli.js
5
+ import "reflect-metadata";
6
+ import * as fs from "fs";
7
+ import * as path from "path";
8
+ import { fileURLToPath } from "url";
9
+ import Database from "better-sqlite3";
10
+ import { inject, injectable } from "tsyringe";
11
+ import { createCipheriv, createDecipheriv, randomBytes, randomUUID } from "crypto";
12
+ import Database2 from "better-sqlite3";
13
+ import { inject as inject2, injectable as injectable2 } from "tsyringe";
14
+ import Database3 from "better-sqlite3";
15
+ import { inject as inject3, injectable as injectable3 } from "tsyringe";
16
+ import Database4 from "better-sqlite3";
17
+ import { inject as inject4, injectable as injectable4 } from "tsyringe";
18
+ import Database5 from "better-sqlite3";
19
+ import { inject as inject5, injectable as injectable5 } from "tsyringe";
20
+ import Database6 from "better-sqlite3";
21
+ import { inject as inject6, injectable as injectable6 } from "tsyringe";
22
+ import * as fs2 from "fs";
23
+ import * as os from "os";
24
+ import * as path2 from "path";
25
+ import Database7 from "better-sqlite3";
26
+ import "reflect-metadata";
27
+ import { container } from "tsyringe";
28
+ import { execFile } from "child_process";
29
+ import { promisify } from "util";
30
+ import { execFile as execFile2 } from "child_process";
31
+ import { promisify as promisify2 } from "util";
32
+ import * as fs3 from "fs";
33
+ import * as path3 from "path";
34
+ import { execSync } from "child_process";
35
+ import * as fs4 from "fs";
36
+ import * as os2 from "os";
37
+ import * as path4 from "path";
38
+ import { createHash } from "crypto";
39
+ import { exec } from "child_process";
40
+ import { promisify as promisify3 } from "util";
41
+ import * as fs5 from "fs";
42
+ import * as path5 from "path";
43
+ import * as fs6 from "fs";
44
+ import * as fs7 from "fs";
45
+ import * as path6 from "path";
46
+ import { execSync as execSync2 } from "child_process";
47
+ import * as os3 from "os";
48
+ import * as path7 from "path";
49
+ import * as fs8 from "fs";
50
+ import * as fs9 from "fs";
51
+ import * as path8 from "path";
52
+ import * as os4 from "os";
53
+ import * as path9 from "path";
54
+ import { execFileSync } from "child_process";
55
+ import { execFileSync as execFileSync2 } from "child_process";
56
+ import * as fs10 from "fs";
57
+ import chalk from "chalk";
58
+ import ora from "ora";
59
+ import Table from "cli-table3";
60
+ import { execFileSync as execFileSync3 } from "child_process";
61
+ import * as fs11 from "fs";
62
+ import * as path10 from "path";
63
+ import * as fs12 from "fs";
64
+ import * as path11 from "path";
65
+ import * as fs13 from "fs";
66
+ import * as os5 from "os";
67
+ import * as path12 from "path";
68
+ import * as crypto from "crypto";
69
+ import * as fs14 from "fs";
70
+ import * as path13 from "path";
71
+ import * as fs15 from "fs";
72
+ import * as path14 from "path";
73
+ import * as fs16 from "fs";
74
+ import * as path15 from "path";
75
+ import { spawn } from "child_process";
76
+ import { createHash as createHash3 } from "crypto";
77
+ import { spawn as spawn2 } from "child_process";
78
+ import { execFileSync as execFileSync4 } from "child_process";
79
+ import * as fs17 from "fs";
80
+ import * as path16 from "path";
81
+ import "reflect-metadata";
82
+ import { Command as Command2 } from "commander";
83
+ import { existsSync as existsSync30, readFileSync as readFileSync18 } from "fs";
84
+ import { fileURLToPath as fileURLToPath4 } from "url";
85
+ import { dirname as dirname8, join as join34 } from "path";
86
+ import fs18 from "fs";
87
+ import path17 from "path";
88
+ import { execSync as execSync3 } from "child_process";
89
+ import { fileURLToPath as fileURLToPath2 } from "url";
90
+ import { dirname as dirname4, join as join16 } from "path";
91
+ import * as readline from "readline";
92
+ import * as fs19 from "fs";
93
+ import * as path18 from "path";
94
+ import { execFileSync as execFileSync5 } from "child_process";
95
+ import * as path19 from "path";
96
+ import * as path20 from "path";
97
+ import * as fs20 from "fs";
98
+ import * as path21 from "path";
99
+ import { execSync as execSync4 } from "child_process";
100
+ import * as path22 from "path";
101
+ import * as fs21 from "fs";
102
+ import * as path23 from "path";
103
+ import * as fs22 from "fs";
104
+ import chalk2 from "chalk";
105
+ import { spawn as spawn3 } from "child_process";
106
+ import * as path24 from "path";
107
+ import * as fs23 from "fs";
108
+ import * as fs24 from "fs";
109
+ import * as path25 from "path";
110
+ import * as readline2 from "readline";
111
+ import blessed6 from "blessed";
112
+ import blessed from "blessed";
113
+ import * as fs25 from "fs";
114
+ import blessed2 from "blessed";
115
+ import blessed3 from "blessed";
116
+ import cronstrue from "cronstrue";
117
+ import blessed4 from "blessed";
118
+ import { spawn as spawn4 } from "child_process";
119
+ import blessed5 from "blessed";
120
+ import * as fs26 from "fs";
121
+ import * as path26 from "path";
122
+ import * as fs31 from "fs";
123
+ import * as fs30 from "fs";
124
+ import * as path32 from "path";
125
+ import { dirname as dirname7 } from "path";
126
+ import { fileURLToPath as fileURLToPath3 } from "url";
127
+ import cors from "cors";
128
+ import express from "express";
129
+ import * as fs27 from "fs";
130
+ import * as path27 from "path";
131
+ import * as fs28 from "fs";
132
+ import * as path28 from "path";
133
+ import { execSync as execSync5, spawn as spawn5 } from "child_process";
134
+ import { Router } from "express";
135
+ import { Router as Router2 } from "express";
136
+ import { Router as Router3 } from "express";
137
+ import { Router as Router4 } from "express";
138
+ import { CronExpressionParser } from "cron-parser";
139
+ import * as fs29 from "fs";
140
+ import * as path29 from "path";
141
+ import { execSync as execSync6 } from "child_process";
142
+ import { Router as Router5 } from "express";
143
+ import * as path30 from "path";
144
+ import { Router as Router6 } from "express";
145
+ import { Router as Router7 } from "express";
146
+ import * as path31 from "path";
147
+ import { Router as Router8 } from "express";
148
+ import { Router as Router9 } from "express";
149
+ import { CronExpressionParser as CronExpressionParser2 } from "cron-parser";
150
+ import { spawnSync } from "child_process";
151
+ import * as fs32 from "fs";
152
+ import * as path33 from "path";
153
+ import * as fs33 from "fs";
154
+ import * as path34 from "path";
155
+ import chalk3 from "chalk";
156
+ import chalk4 from "chalk";
157
+ import { execSync as execSync7 } from "child_process";
158
+ import * as fs34 from "fs";
159
+ import * as readline3 from "readline";
160
+ import * as fs35 from "fs";
161
+ import * as path35 from "path";
162
+ import * as os6 from "os";
163
+ import * as path36 from "path";
164
+ import chalk5 from "chalk";
165
+ import { Command } from "commander";
166
+ import { execFileSync as execFileSync6 } from "child_process";
167
+ import * as fs36 from "fs";
168
+ import * as path37 from "path";
169
+ import * as readline4 from "readline";
170
+ import chalk6 from "chalk";
3
171
  var __defProp = Object.defineProperty;
4
172
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
173
  var __esm = (fn, res) => function __init() {
@@ -9,16 +177,77 @@ var __export = (target, all) => {
9
177
  for (var name in all)
10
178
  __defProp(target, name, { get: all[name], enumerable: true });
11
179
  };
12
-
13
- // ../core/dist/types.js
14
180
  var init_types = __esm({
15
181
  "../core/dist/types.js"() {
16
182
  "use strict";
17
183
  }
18
184
  });
19
-
20
- // ../core/dist/constants.js
21
- var DEFAULT_DEFAULT_BRANCH, DEFAULT_PRD_DIR, DEFAULT_MAX_RUNTIME, DEFAULT_REVIEWER_MAX_RUNTIME, DEFAULT_CRON_SCHEDULE, DEFAULT_REVIEWER_SCHEDULE, DEFAULT_CRON_SCHEDULE_OFFSET, DEFAULT_MAX_RETRIES, DEFAULT_REVIEWER_MAX_RETRIES, DEFAULT_REVIEWER_RETRY_DELAY, DEFAULT_BRANCH_PREFIX, DEFAULT_BRANCH_PATTERNS, DEFAULT_MIN_REVIEW_SCORE, DEFAULT_MAX_LOG_SIZE, DEFAULT_PROVIDER, DEFAULT_EXECUTOR_ENABLED, DEFAULT_REVIEWER_ENABLED, DEFAULT_PROVIDER_ENV, DEFAULT_FALLBACK_ON_RATE_LIMIT, DEFAULT_CLAUDE_MODEL, VALID_CLAUDE_MODELS, CLAUDE_MODEL_IDS, DEFAULT_NOTIFICATIONS, DEFAULT_PRD_PRIORITY, DEFAULT_SLICER_SCHEDULE, DEFAULT_SLICER_MAX_RUNTIME, DEFAULT_ROADMAP_SCANNER, DEFAULT_TEMPLATES_DIR, DEFAULT_BOARD_PROVIDER, DEFAULT_LOCAL_BOARD_INFO, DEFAULT_AUTO_MERGE, DEFAULT_AUTO_MERGE_METHOD, VALID_MERGE_METHODS, DEFAULT_QA_ENABLED, DEFAULT_QA_SCHEDULE, DEFAULT_QA_MAX_RUNTIME, DEFAULT_QA_ARTIFACTS, DEFAULT_QA_SKIP_LABEL, DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT, DEFAULT_QA, QA_LOG_NAME, DEFAULT_AUDIT_ENABLED, DEFAULT_AUDIT_SCHEDULE, DEFAULT_AUDIT_MAX_RUNTIME, DEFAULT_AUDIT, AUDIT_LOG_NAME, PLANNER_LOG_NAME, VALID_PROVIDERS, VALID_JOB_TYPES, DEFAULT_JOB_PROVIDERS, PROVIDER_COMMANDS, CONFIG_FILE_NAME, LOCK_FILE_PREFIX, LOG_DIR, CLAIM_FILE_EXTENSION, EXECUTOR_LOG_NAME, REVIEWER_LOG_NAME, EXECUTOR_LOG_FILE, REVIEWER_LOG_FILE, LOG_FILE_NAMES, GLOBAL_CONFIG_DIR, REGISTRY_FILE_NAME, HISTORY_FILE_NAME, PRD_STATES_FILE_NAME, STATE_DB_FILE_NAME, MAX_HISTORY_RECORDS_PER_PRD;
185
+ var DEFAULT_DEFAULT_BRANCH;
186
+ var DEFAULT_PRD_DIR;
187
+ var DEFAULT_MAX_RUNTIME;
188
+ var DEFAULT_REVIEWER_MAX_RUNTIME;
189
+ var DEFAULT_CRON_SCHEDULE;
190
+ var DEFAULT_REVIEWER_SCHEDULE;
191
+ var DEFAULT_CRON_SCHEDULE_OFFSET;
192
+ var DEFAULT_MAX_RETRIES;
193
+ var DEFAULT_REVIEWER_MAX_RETRIES;
194
+ var DEFAULT_REVIEWER_RETRY_DELAY;
195
+ var DEFAULT_BRANCH_PREFIX;
196
+ var DEFAULT_BRANCH_PATTERNS;
197
+ var DEFAULT_MIN_REVIEW_SCORE;
198
+ var DEFAULT_MAX_LOG_SIZE;
199
+ var DEFAULT_PROVIDER;
200
+ var DEFAULT_EXECUTOR_ENABLED;
201
+ var DEFAULT_REVIEWER_ENABLED;
202
+ var DEFAULT_PROVIDER_ENV;
203
+ var DEFAULT_FALLBACK_ON_RATE_LIMIT;
204
+ var DEFAULT_CLAUDE_MODEL;
205
+ var VALID_CLAUDE_MODELS;
206
+ var CLAUDE_MODEL_IDS;
207
+ var DEFAULT_NOTIFICATIONS;
208
+ var DEFAULT_PRD_PRIORITY;
209
+ var DEFAULT_SLICER_SCHEDULE;
210
+ var DEFAULT_SLICER_MAX_RUNTIME;
211
+ var DEFAULT_ROADMAP_SCANNER;
212
+ var DEFAULT_TEMPLATES_DIR;
213
+ var DEFAULT_BOARD_PROVIDER;
214
+ var DEFAULT_LOCAL_BOARD_INFO;
215
+ var DEFAULT_AUTO_MERGE;
216
+ var DEFAULT_AUTO_MERGE_METHOD;
217
+ var VALID_MERGE_METHODS;
218
+ var DEFAULT_QA_ENABLED;
219
+ var DEFAULT_QA_SCHEDULE;
220
+ var DEFAULT_QA_MAX_RUNTIME;
221
+ var DEFAULT_QA_ARTIFACTS;
222
+ var DEFAULT_QA_SKIP_LABEL;
223
+ var DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT;
224
+ var DEFAULT_QA;
225
+ var QA_LOG_NAME;
226
+ var DEFAULT_AUDIT_ENABLED;
227
+ var DEFAULT_AUDIT_SCHEDULE;
228
+ var DEFAULT_AUDIT_MAX_RUNTIME;
229
+ var DEFAULT_AUDIT;
230
+ var AUDIT_LOG_NAME;
231
+ var PLANNER_LOG_NAME;
232
+ var VALID_PROVIDERS;
233
+ var VALID_JOB_TYPES;
234
+ var DEFAULT_JOB_PROVIDERS;
235
+ var PROVIDER_COMMANDS;
236
+ var CONFIG_FILE_NAME;
237
+ var LOCK_FILE_PREFIX;
238
+ var LOG_DIR;
239
+ var CLAIM_FILE_EXTENSION;
240
+ var EXECUTOR_LOG_NAME;
241
+ var REVIEWER_LOG_NAME;
242
+ var EXECUTOR_LOG_FILE;
243
+ var REVIEWER_LOG_FILE;
244
+ var LOG_FILE_NAMES;
245
+ var GLOBAL_CONFIG_DIR;
246
+ var REGISTRY_FILE_NAME;
247
+ var HISTORY_FILE_NAME;
248
+ var PRD_STATES_FILE_NAME;
249
+ var STATE_DB_FILE_NAME;
250
+ var MAX_HISTORY_RECORDS_PER_PRD;
22
251
  var init_constants = __esm({
23
252
  "../core/dist/constants.js"() {
24
253
  "use strict";
@@ -125,11 +354,6 @@ var init_constants = __esm({
125
354
  MAX_HISTORY_RECORDS_PER_PRD = 10;
126
355
  }
127
356
  });
128
-
129
- // ../core/dist/config.js
130
- import * as fs from "fs";
131
- import * as path from "path";
132
- import { fileURLToPath } from "url";
133
357
  function getDefaultConfig() {
134
358
  return {
135
359
  // PRD execution
@@ -144,6 +368,7 @@ function getDefaultConfig() {
144
368
  // Cron scheduling
145
369
  cronSchedule: DEFAULT_CRON_SCHEDULE,
146
370
  reviewerSchedule: DEFAULT_REVIEWER_SCHEDULE,
371
+ scheduleBundleId: null,
147
372
  cronScheduleOffset: DEFAULT_CRON_SCHEDULE_OFFSET,
148
373
  maxRetries: DEFAULT_MAX_RETRIES,
149
374
  // Reviewer retry configuration
@@ -211,6 +436,13 @@ function normalizeConfig(rawConfig) {
211
436
  normalized.maxLogSize = readNumber(rawConfig.maxLogSize) ?? readNumber(logging?.maxLogSize);
212
437
  normalized.cronSchedule = readString(rawConfig.cronSchedule) ?? readString(cron?.executorSchedule);
213
438
  normalized.reviewerSchedule = readString(rawConfig.reviewerSchedule) ?? readString(cron?.reviewerSchedule);
439
+ const rawScheduleBundleId = rawConfig.scheduleBundleId;
440
+ if (typeof rawScheduleBundleId === "string") {
441
+ const trimmed = rawScheduleBundleId.trim();
442
+ normalized.scheduleBundleId = trimmed.length > 0 ? trimmed : null;
443
+ } else if (rawScheduleBundleId === null) {
444
+ normalized.scheduleBundleId = null;
445
+ }
214
446
  normalized.cronScheduleOffset = readNumber(rawConfig.cronScheduleOffset);
215
447
  normalized.maxRetries = readNumber(rawConfig.maxRetries);
216
448
  normalized.reviewerMaxRetries = readNumber(rawConfig.reviewerMaxRetries);
@@ -725,8 +957,6 @@ var init_config = __esm({
725
957
  init_constants();
726
958
  }
727
959
  });
728
-
729
- // ../core/dist/board/types.js
730
960
  var BOARD_COLUMNS;
731
961
  var init_types2 = __esm({
732
962
  "../core/dist/board/types.js"() {
@@ -734,9 +964,9 @@ var init_types2 = __esm({
734
964
  BOARD_COLUMNS = ["Draft", "Ready", "In Progress", "Review", "Done"];
735
965
  }
736
966
  });
737
-
738
- // ../core/dist/storage/repositories/sqlite/agent-persona.defaults.js
739
- var GITHUB_RAW_BASE, DEFAULT_AVATAR_URLS, DEFAULT_PERSONAS;
967
+ var GITHUB_RAW_BASE;
968
+ var DEFAULT_AVATAR_URLS;
969
+ var DEFAULT_PERSONAS;
740
970
  var init_agent_persona_defaults = __esm({
741
971
  "../core/dist/storage/repositories/sqlite/agent-persona.defaults.js"() {
742
972
  "use strict";
@@ -1310,11 +1540,6 @@ var init_agent_persona_defaults = __esm({
1310
1540
  ];
1311
1541
  }
1312
1542
  });
1313
-
1314
- // ../core/dist/storage/repositories/sqlite/agent-persona.repository.js
1315
- import Database from "better-sqlite3";
1316
- import { inject, injectable } from "tsyringe";
1317
- import { createCipheriv, createDecipheriv, randomBytes, randomUUID } from "crypto";
1318
1543
  function defaultSoul() {
1319
1544
  return {
1320
1545
  whoIAm: "",
@@ -1392,7 +1617,12 @@ function rowToPersona(row, modelConfig) {
1392
1617
  updatedAt: row.updated_at
1393
1618
  };
1394
1619
  }
1395
- var __decorate, __metadata, __param, ENV_KEY_META_KEY, ENV_SEEDED_META_KEY, SqliteAgentPersonaRepository;
1620
+ var __decorate;
1621
+ var __metadata;
1622
+ var __param;
1623
+ var ENV_KEY_META_KEY;
1624
+ var ENV_SEEDED_META_KEY;
1625
+ var SqliteAgentPersonaRepository;
1396
1626
  var init_agent_persona_repository = __esm({
1397
1627
  "../core/dist/storage/repositories/sqlite/agent-persona.repository.js"() {
1398
1628
  "use strict";
@@ -1585,11 +1815,10 @@ var init_agent_persona_repository = __esm({
1585
1815
  ], SqliteAgentPersonaRepository);
1586
1816
  }
1587
1817
  });
1588
-
1589
- // ../core/dist/storage/repositories/sqlite/execution-history.repository.js
1590
- import Database2 from "better-sqlite3";
1591
- import { inject as inject2, injectable as injectable2 } from "tsyringe";
1592
- var __decorate2, __metadata2, __param2, SqliteExecutionHistoryRepository;
1818
+ var __decorate2;
1819
+ var __metadata2;
1820
+ var __param2;
1821
+ var SqliteExecutionHistoryRepository;
1593
1822
  var init_execution_history_repository = __esm({
1594
1823
  "../core/dist/storage/repositories/sqlite/execution-history.repository.js"() {
1595
1824
  "use strict";
@@ -1691,10 +1920,6 @@ var init_execution_history_repository = __esm({
1691
1920
  ], SqliteExecutionHistoryRepository);
1692
1921
  }
1693
1922
  });
1694
-
1695
- // ../core/dist/storage/repositories/sqlite/kanban-issue.repository.js
1696
- import Database3 from "better-sqlite3";
1697
- import { inject as inject3, injectable as injectable3 } from "tsyringe";
1698
1923
  function rowToIssue(row) {
1699
1924
  return {
1700
1925
  number: row.number,
@@ -1708,7 +1933,10 @@ function rowToIssue(row) {
1708
1933
  updatedAt: row.updated_at
1709
1934
  };
1710
1935
  }
1711
- var __decorate3, __metadata3, __param3, SqliteKanbanIssueRepository;
1936
+ var __decorate3;
1937
+ var __metadata3;
1938
+ var __param3;
1939
+ var SqliteKanbanIssueRepository;
1712
1940
  var init_kanban_issue_repository = __esm({
1713
1941
  "../core/dist/storage/repositories/sqlite/kanban-issue.repository.js"() {
1714
1942
  "use strict";
@@ -1775,11 +2003,10 @@ var init_kanban_issue_repository = __esm({
1775
2003
  ], SqliteKanbanIssueRepository);
1776
2004
  }
1777
2005
  });
1778
-
1779
- // ../core/dist/storage/repositories/sqlite/prd-state.repository.js
1780
- import Database4 from "better-sqlite3";
1781
- import { inject as inject4, injectable as injectable4 } from "tsyringe";
1782
- var __decorate4, __metadata4, __param4, SqlitePrdStateRepository;
2006
+ var __decorate4;
2007
+ var __metadata4;
2008
+ var __param4;
2009
+ var SqlitePrdStateRepository;
1783
2010
  var init_prd_state_repository = __esm({
1784
2011
  "../core/dist/storage/repositories/sqlite/prd-state.repository.js"() {
1785
2012
  "use strict";
@@ -1863,11 +2090,10 @@ var init_prd_state_repository = __esm({
1863
2090
  ], SqlitePrdStateRepository);
1864
2091
  }
1865
2092
  });
1866
-
1867
- // ../core/dist/storage/repositories/sqlite/project-registry.repository.js
1868
- import Database5 from "better-sqlite3";
1869
- import { inject as inject5, injectable as injectable5 } from "tsyringe";
1870
- var __decorate5, __metadata5, __param5, SqliteProjectRegistryRepository;
2093
+ var __decorate5;
2094
+ var __metadata5;
2095
+ var __param5;
2096
+ var SqliteProjectRegistryRepository;
1871
2097
  var init_project_registry_repository = __esm({
1872
2098
  "../core/dist/storage/repositories/sqlite/project-registry.repository.js"() {
1873
2099
  "use strict";
@@ -1918,11 +2144,10 @@ var init_project_registry_repository = __esm({
1918
2144
  ], SqliteProjectRegistryRepository);
1919
2145
  }
1920
2146
  });
1921
-
1922
- // ../core/dist/storage/repositories/sqlite/roadmap-state.repository.js
1923
- import Database6 from "better-sqlite3";
1924
- import { inject as inject6, injectable as injectable6 } from "tsyringe";
1925
- var __decorate6, __metadata6, __param6, SqliteRoadmapStateRepository;
2147
+ var __decorate6;
2148
+ var __metadata6;
2149
+ var __param6;
2150
+ var SqliteRoadmapStateRepository;
1926
2151
  var init_roadmap_state_repository = __esm({
1927
2152
  "../core/dist/storage/repositories/sqlite/roadmap-state.repository.js"() {
1928
2153
  "use strict";
@@ -1984,12 +2209,6 @@ var init_roadmap_state_repository = __esm({
1984
2209
  ], SqliteRoadmapStateRepository);
1985
2210
  }
1986
2211
  });
1987
-
1988
- // ../core/dist/storage/sqlite/client.js
1989
- import * as fs2 from "fs";
1990
- import * as os from "os";
1991
- import * as path2 from "path";
1992
- import Database7 from "better-sqlite3";
1993
2212
  function getDbPath() {
1994
2213
  const base = process.env.NIGHT_WATCH_HOME || path2.join(os.homedir(), GLOBAL_CONFIG_DIR);
1995
2214
  return path2.join(base, STATE_DB_FILE_NAME);
@@ -2028,8 +2247,6 @@ var init_client = __esm({
2028
2247
  _db = null;
2029
2248
  }
2030
2249
  });
2031
-
2032
- // ../core/dist/storage/sqlite/migrations.js
2033
2250
  function runMigrations(db) {
2034
2251
  db.exec(`
2035
2252
  CREATE TABLE IF NOT EXISTS projects (
@@ -2134,10 +2351,6 @@ var init_migrations = __esm({
2134
2351
  SCHEMA_VERSION = "1";
2135
2352
  }
2136
2353
  });
2137
-
2138
- // ../core/dist/di/container.js
2139
- import "reflect-metadata";
2140
- import { container } from "tsyringe";
2141
2354
  function initContainer(projectDir) {
2142
2355
  if (container.isRegistered(DATABASE_TOKEN)) {
2143
2356
  return;
@@ -2170,10 +2383,6 @@ var init_container = __esm({
2170
2383
  DATABASE_TOKEN = "Database";
2171
2384
  }
2172
2385
  });
2173
-
2174
- // ../core/dist/board/providers/github-graphql.js
2175
- import { execFile } from "child_process";
2176
- import { promisify } from "util";
2177
2386
  async function graphql(query, variables, cwd) {
2178
2387
  const args = ["api", "graphql", "-f", `query=${query}`];
2179
2388
  for (const [key, value] of Object.entries(variables)) {
@@ -2208,11 +2417,8 @@ var init_github_graphql = __esm({
2208
2417
  execFileAsync = promisify(execFile);
2209
2418
  }
2210
2419
  });
2211
-
2212
- // ../core/dist/board/providers/github-projects.js
2213
- import { execFile as execFile2 } from "child_process";
2214
- import { promisify as promisify2 } from "util";
2215
- var execFileAsync2, GitHubProjectsProvider;
2420
+ var execFileAsync2;
2421
+ var GitHubProjectsProvider;
2216
2422
  var init_github_projects = __esm({
2217
2423
  "../core/dist/board/providers/github-projects.js"() {
2218
2424
  "use strict";
@@ -2908,8 +3114,6 @@ var init_github_projects = __esm({
2908
3114
  };
2909
3115
  }
2910
3116
  });
2911
-
2912
- // ../core/dist/board/providers/local-kanban.js
2913
3117
  function toIBoardIssue(row) {
2914
3118
  return {
2915
3119
  id: String(row.number),
@@ -2973,8 +3177,6 @@ var init_local_kanban = __esm({
2973
3177
  };
2974
3178
  }
2975
3179
  });
2976
-
2977
- // ../core/dist/board/factory.js
2978
3180
  function createBoardProvider(config, cwd) {
2979
3181
  switch (config.provider) {
2980
3182
  case "github":
@@ -2996,8 +3198,6 @@ var init_factory = __esm({
2996
3198
  init_local_kanban();
2997
3199
  }
2998
3200
  });
2999
-
3000
- // ../core/dist/board/labels.js
3001
3201
  function isValidPriority(value) {
3002
3202
  return PRIORITY_LABELS.includes(value);
3003
3203
  }
@@ -3047,7 +3247,14 @@ function sortByPriority(issues) {
3047
3247
  return aOrder - bOrder;
3048
3248
  });
3049
3249
  }
3050
- var PRIORITY_LABELS, PRIORITY_LABEL_INFO, CATEGORY_LABELS, CATEGORY_LABEL_INFO, HORIZON_LABELS, HORIZON_LABEL_INFO, PRIORITY_COLORS, NIGHT_WATCH_LABELS;
3250
+ var PRIORITY_LABELS;
3251
+ var PRIORITY_LABEL_INFO;
3252
+ var CATEGORY_LABELS;
3253
+ var CATEGORY_LABEL_INFO;
3254
+ var HORIZON_LABELS;
3255
+ var HORIZON_LABEL_INFO;
3256
+ var PRIORITY_COLORS;
3257
+ var NIGHT_WATCH_LABELS;
3051
3258
  var init_labels = __esm({
3052
3259
  "../core/dist/board/labels.js"() {
3053
3260
  "use strict";
@@ -3148,8 +3355,6 @@ var init_labels = __esm({
3148
3355
  ];
3149
3356
  }
3150
3357
  });
3151
-
3152
- // ../core/dist/board/roadmap-mapping.js
3153
3358
  function getLabelsForSection(sectionName) {
3154
3359
  for (const mapping of ROADMAP_SECTION_MAPPINGS) {
3155
3360
  if (mapping.sectionPattern.test(sectionName)) {
@@ -3197,7 +3402,10 @@ function findMatchingIssue(targetTitle, issues, threshold = 0.8) {
3197
3402
  }
3198
3403
  return bestMatch;
3199
3404
  }
3200
- var HORIZON_SHORT_TERM, HORIZON_MEDIUM_TERM, HORIZON_LONG_TERM, ROADMAP_SECTION_MAPPINGS;
3405
+ var HORIZON_SHORT_TERM;
3406
+ var HORIZON_MEDIUM_TERM;
3407
+ var HORIZON_LONG_TERM;
3408
+ var ROADMAP_SECTION_MAPPINGS;
3201
3409
  var init_roadmap_mapping = __esm({
3202
3410
  "../core/dist/board/roadmap-mapping.js"() {
3203
3411
  "use strict";
@@ -3299,15 +3507,11 @@ var init_roadmap_mapping = __esm({
3299
3507
  ];
3300
3508
  }
3301
3509
  });
3302
-
3303
- // ../core/dist/storage/repositories/interfaces.js
3304
3510
  var init_interfaces = __esm({
3305
3511
  "../core/dist/storage/repositories/interfaces.js"() {
3306
3512
  "use strict";
3307
3513
  }
3308
3514
  });
3309
-
3310
- // ../core/dist/storage/repositories/index.js
3311
3515
  function getRepositories() {
3312
3516
  if (isContainerInitialized()) {
3313
3517
  return {
@@ -3352,10 +3556,6 @@ var init_repositories = __esm({
3352
3556
  _initialized = false;
3353
3557
  }
3354
3558
  });
3355
-
3356
- // ../core/dist/storage/json-state-migrator.js
3357
- import * as fs3 from "fs";
3358
- import * as path3 from "path";
3359
3559
  function tryReadJson(filePath) {
3360
3560
  if (!fs3.existsSync(filePath)) {
3361
3561
  return null;
@@ -3504,8 +3704,6 @@ var init_json_state_migrator = __esm({
3504
3704
  init_repositories();
3505
3705
  }
3506
3706
  });
3507
-
3508
- // ../core/dist/agents/soul-compiler.js
3509
3707
  function compileSoul(persona, memory) {
3510
3708
  if (persona.systemPromptOverride) {
3511
3709
  return persona.systemPromptOverride;
@@ -3639,7 +3837,8 @@ function compileSoul(persona, memory) {
3639
3837
  }
3640
3838
  return lines.join("\n");
3641
3839
  }
3642
- var AIISH_WORDS_TO_AVOID, CANNED_CHATBOT_PHRASES;
3840
+ var AIISH_WORDS_TO_AVOID;
3841
+ var CANNED_CHATBOT_PHRASES;
3643
3842
  var init_soul_compiler = __esm({
3644
3843
  "../core/dist/agents/soul-compiler.js"() {
3645
3844
  "use strict";
@@ -3664,8 +3863,6 @@ var init_soul_compiler = __esm({
3664
3863
  ];
3665
3864
  }
3666
3865
  });
3667
-
3668
- // ../core/dist/utils/avatar-generator.js
3669
3866
  function extractOutputUrl(output) {
3670
3867
  if (!output)
3671
3868
  return null;
@@ -3746,7 +3943,10 @@ async function generatePersonaAvatar(personaName, personaRole, apiToken) {
3746
3943
  }
3747
3944
  throw new Error("Replicate avatar generation timed out after 3 minutes");
3748
3945
  }
3749
- var REPLICATE_MODEL, POLL_INTERVAL_MS, MAX_POLLS, PERSONA_PORTRAITS;
3946
+ var REPLICATE_MODEL;
3947
+ var POLL_INTERVAL_MS;
3948
+ var MAX_POLLS;
3949
+ var PERSONA_PORTRAITS;
3750
3950
  var init_avatar_generator = __esm({
3751
3951
  "../core/dist/utils/avatar-generator.js"() {
3752
3952
  "use strict";
@@ -3781,8 +3981,6 @@ var init_avatar_generator = __esm({
3781
3981
  };
3782
3982
  }
3783
3983
  });
3784
-
3785
- // ../core/dist/utils/logger.js
3786
3984
  function colorize(color, text) {
3787
3985
  return NO_COLOR ? text : `${color}${text}${ANSI.reset}`;
3788
3986
  }
@@ -3802,7 +4000,10 @@ function formatMeta(meta) {
3802
4000
  function createLogger(context) {
3803
4001
  return new Logger(context);
3804
4002
  }
3805
- var ANSI, NO_COLOR, LEVEL_STYLES, Logger;
4003
+ var ANSI;
4004
+ var NO_COLOR;
4005
+ var LEVEL_STYLES;
4006
+ var Logger;
3806
4007
  var init_logger = __esm({
3807
4008
  "../core/dist/utils/logger.js"() {
3808
4009
  "use strict";
@@ -3859,8 +4060,6 @@ var init_logger = __esm({
3859
4060
  };
3860
4061
  }
3861
4062
  });
3862
-
3863
- // ../core/dist/utils/prd-states.js
3864
4063
  function readPrdStates() {
3865
4064
  const { prdState } = getRepositories();
3866
4065
  return prdState.readAll();
@@ -3887,12 +4086,6 @@ var init_prd_states = __esm({
3887
4086
  init_repositories();
3888
4087
  }
3889
4088
  });
3890
-
3891
- // ../core/dist/utils/crontab.js
3892
- import { execSync } from "child_process";
3893
- import * as fs4 from "fs";
3894
- import * as os2 from "os";
3895
- import * as path4 from "path";
3896
4089
  function isNightWatchEntry(line) {
3897
4090
  if (line.includes(CRONTAB_MARKER_PREFIX)) {
3898
4091
  return true;
@@ -4020,7 +4213,8 @@ function removeEntriesForProject(projectDir, marker) {
4020
4213
  }
4021
4214
  return removedCount;
4022
4215
  }
4023
- var CRONTAB_MARKER_PREFIX, LEGACY_SCRIPT_NAMES;
4216
+ var CRONTAB_MARKER_PREFIX;
4217
+ var LEGACY_SCRIPT_NAMES;
4024
4218
  var init_crontab = __esm({
4025
4219
  "../core/dist/utils/crontab.js"() {
4026
4220
  "use strict";
@@ -4035,13 +4229,6 @@ var init_crontab = __esm({
4035
4229
  ];
4036
4230
  }
4037
4231
  });
4038
-
4039
- // ../core/dist/utils/status-data.js
4040
- import { createHash } from "crypto";
4041
- import { exec } from "child_process";
4042
- import { promisify as promisify3 } from "util";
4043
- import * as fs5 from "fs";
4044
- import * as path5 from "path";
4045
4232
  function getProjectName(projectDir) {
4046
4233
  const packageJsonPath = path5.join(projectDir, "package.json");
4047
4234
  if (fs5.existsSync(packageJsonPath)) {
@@ -4497,9 +4684,6 @@ var init_status_data = __esm({
4497
4684
  execAsync = promisify3(exec);
4498
4685
  }
4499
4686
  });
4500
-
4501
- // ../core/dist/utils/cancel.js
4502
- import * as fs6 from "fs";
4503
4687
  function getLockFilePaths(projectDir) {
4504
4688
  const runtimeKey = projectRuntimeKey(projectDir);
4505
4689
  return {
@@ -4606,11 +4790,6 @@ var init_cancel = __esm({
4606
4790
  init_status_data();
4607
4791
  }
4608
4792
  });
4609
-
4610
- // ../core/dist/utils/checks.js
4611
- import * as fs7 from "fs";
4612
- import * as path6 from "path";
4613
- import { execSync as execSync2 } from "child_process";
4614
4793
  function checkGitRepo(cwd) {
4615
4794
  const isRepo = fs7.existsSync(path6.join(cwd, ".git"));
4616
4795
  return {
@@ -4801,11 +4980,6 @@ var init_checks = __esm({
4801
4980
  init_constants();
4802
4981
  }
4803
4982
  });
4804
-
4805
- // ../core/dist/utils/claim-manager.js
4806
- import * as os3 from "os";
4807
- import * as path7 from "path";
4808
- import * as fs8 from "fs";
4809
4983
  function claimPrd(prdDir, prdFile, pid) {
4810
4984
  const claimPath = path7.join(prdDir, prdFile + CLAIM_FILE_EXTENSION);
4811
4985
  const claimData = {
@@ -4884,10 +5058,9 @@ var init_claim_manager = __esm({
4884
5058
  init_constants();
4885
5059
  }
4886
5060
  });
4887
-
4888
- // ../core/dist/utils/config-writer.js
4889
- import * as fs9 from "fs";
4890
- import * as path8 from "path";
5061
+ function isPlainObject(value) {
5062
+ return value !== null && typeof value === "object" && !Array.isArray(value);
5063
+ }
4891
5064
  function saveConfig(projectDir, changes) {
4892
5065
  const configPath = path8.join(projectDir, CONFIG_FILE_NAME);
4893
5066
  try {
@@ -4899,8 +5072,8 @@ function saveConfig(projectDir, changes) {
4899
5072
  const merged = { ...existing };
4900
5073
  for (const [key, value] of Object.entries(changes)) {
4901
5074
  if (value !== void 0) {
4902
- if (key === "notifications" && existing.notifications && typeof existing.notifications === "object") {
4903
- merged.notifications = { ...existing.notifications, ...value };
5075
+ if (PARTIAL_MERGE_KEYS.has(key) && isPlainObject(existing[key]) && isPlainObject(value)) {
5076
+ merged[key] = { ...existing[key], ...value };
4904
5077
  } else {
4905
5078
  merged[key] = value;
4906
5079
  }
@@ -4915,16 +5088,14 @@ function saveConfig(projectDir, changes) {
4915
5088
  };
4916
5089
  }
4917
5090
  }
5091
+ var PARTIAL_MERGE_KEYS;
4918
5092
  var init_config_writer = __esm({
4919
5093
  "../core/dist/utils/config-writer.js"() {
4920
5094
  "use strict";
4921
5095
  init_constants();
5096
+ PARTIAL_MERGE_KEYS = /* @__PURE__ */ new Set(["notifications", "qa", "audit", "roadmapScanner"]);
4922
5097
  }
4923
5098
  });
4924
-
4925
- // ../core/dist/utils/execution-history.js
4926
- import * as os4 from "os";
4927
- import * as path9 from "path";
4928
5099
  function getHistoryPath() {
4929
5100
  const base = process.env.NIGHT_WATCH_HOME || path9.join(os4.homedir(), GLOBAL_CONFIG_DIR);
4930
5101
  return path9.join(base, HISTORY_FILE_NAME);
@@ -4975,9 +5146,6 @@ var init_execution_history = __esm({
4975
5146
  init_client();
4976
5147
  }
4977
5148
  });
4978
-
4979
- // ../core/dist/utils/git-utils.js
4980
- import { execFileSync } from "child_process";
4981
5149
  function getBranchTipTimestamp(projectDir, branch) {
4982
5150
  let remoteTs = null;
4983
5151
  let localTs = null;
@@ -5066,9 +5234,6 @@ var init_git_utils = __esm({
5066
5234
  "use strict";
5067
5235
  }
5068
5236
  });
5069
-
5070
- // ../core/dist/utils/github.js
5071
- import { execFileSync as execFileSync2 } from "child_process";
5072
5237
  function parsePrDetails(raw) {
5073
5238
  try {
5074
5239
  const details = JSON.parse(raw);
@@ -5241,7 +5406,8 @@ function extractSummary(body, maxLength = 500) {
5241
5406
  const lastSpace = truncated.lastIndexOf(" ");
5242
5407
  return (lastSpace > 0 ? truncated.slice(0, lastSpace) : truncated) + "...";
5243
5408
  }
5244
- var QA_COMMENT_MARKER, QA_SCREENSHOT_REGEX;
5409
+ var QA_COMMENT_MARKER;
5410
+ var QA_SCREENSHOT_REGEX;
5245
5411
  var init_github = __esm({
5246
5412
  "../core/dist/utils/github.js"() {
5247
5413
  "use strict";
@@ -5249,9 +5415,6 @@ var init_github = __esm({
5249
5415
  QA_SCREENSHOT_REGEX = /!\[[^\]]*]\(([^)\n]*qa-artifacts\/[^)\n]+)\)/g;
5250
5416
  }
5251
5417
  });
5252
-
5253
- // ../core/dist/utils/log-utils.js
5254
- import * as fs10 from "fs";
5255
5418
  function rotateLog(logFile, maxSize = DEFAULT_MAX_LOG_SIZE) {
5256
5419
  if (!fs10.existsSync(logFile)) {
5257
5420
  return false;
@@ -5291,11 +5454,6 @@ var init_log_utils = __esm({
5291
5454
  init_constants();
5292
5455
  }
5293
5456
  });
5294
-
5295
- // ../core/dist/utils/ui.js
5296
- import chalk from "chalk";
5297
- import ora from "ora";
5298
- import Table from "cli-table3";
5299
5457
  function success(msg) {
5300
5458
  console.log(chalk.green("\u2714"), msg);
5301
5459
  }
@@ -5378,8 +5536,6 @@ var init_ui = __esm({
5378
5536
  "use strict";
5379
5537
  }
5380
5538
  });
5381
-
5382
- // ../core/dist/utils/notify.js
5383
5539
  function getEventEmoji(event) {
5384
5540
  switch (event) {
5385
5541
  case "run_started":
@@ -5656,11 +5812,6 @@ var init_notify = __esm({
5656
5812
  MAX_QA_SCREENSHOTS_IN_NOTIFICATION = 3;
5657
5813
  }
5658
5814
  });
5659
-
5660
- // ../core/dist/utils/prd-discovery.js
5661
- import { execFileSync as execFileSync3 } from "child_process";
5662
- import * as fs11 from "fs";
5663
- import * as path10 from "path";
5664
5815
  function getOpenBranches(projectDir) {
5665
5816
  try {
5666
5817
  const output = execFileSync3("gh", ["pr", "list", "--state", "open", "--json", "headRefName", "--jq", ".[].headRefName"], { cwd: projectDir, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
@@ -5766,10 +5917,6 @@ var init_prd_discovery = __esm({
5766
5917
  init_status_data();
5767
5918
  }
5768
5919
  });
5769
-
5770
- // ../core/dist/utils/prd-utils.js
5771
- import * as fs12 from "fs";
5772
- import * as path11 from "path";
5773
5920
  function slugify(name) {
5774
5921
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
5775
5922
  }
@@ -5801,11 +5948,6 @@ var init_prd_utils = __esm({
5801
5948
  "use strict";
5802
5949
  }
5803
5950
  });
5804
-
5805
- // ../core/dist/utils/registry.js
5806
- import * as fs13 from "fs";
5807
- import * as os5 from "os";
5808
- import * as path12 from "path";
5809
5951
  function getRegistryPath() {
5810
5952
  const base = process.env.NIGHT_WATCH_HOME || path12.join(os5.homedir(), GLOBAL_CONFIG_DIR);
5811
5953
  return path12.join(base, REGISTRY_FILE_NAME);
@@ -5863,8 +6005,6 @@ var init_registry = __esm({
5863
6005
  init_status_data();
5864
6006
  }
5865
6007
  });
5866
-
5867
- // ../core/dist/utils/roadmap-context-compiler.js
5868
6008
  function isLeadRole(role) {
5869
6009
  const lower = role.toLowerCase();
5870
6010
  return LEAD_KEYWORDS.some((kw) => lower.includes(kw));
@@ -5933,7 +6073,10 @@ function buildSmartProgress(status) {
5933
6073
  }
5934
6074
  return result;
5935
6075
  }
5936
- var RAW_CONTENT_MAX, PROGRESS_MAX_FULL, PROGRESS_MAX_SUMMARY, LEAD_KEYWORDS;
6076
+ var RAW_CONTENT_MAX;
6077
+ var PROGRESS_MAX_FULL;
6078
+ var PROGRESS_MAX_SUMMARY;
6079
+ var LEAD_KEYWORDS;
5937
6080
  var init_roadmap_context_compiler = __esm({
5938
6081
  "../core/dist/utils/roadmap-context-compiler.js"() {
5939
6082
  "use strict";
@@ -5943,9 +6086,6 @@ var init_roadmap_context_compiler = __esm({
5943
6086
  LEAD_KEYWORDS = ["lead", "architect", "product", "manager", "pm", "director"];
5944
6087
  }
5945
6088
  });
5946
-
5947
- // ../core/dist/utils/roadmap-parser.js
5948
- import * as crypto from "crypto";
5949
6089
  function generateItemHash(title) {
5950
6090
  const normalizedTitle = title.toLowerCase().trim();
5951
6091
  return crypto.createHash("sha256").update(normalizedTitle).digest("hex").slice(0, 8);
@@ -6040,10 +6180,6 @@ var init_roadmap_parser = __esm({
6040
6180
  "use strict";
6041
6181
  }
6042
6182
  });
6043
-
6044
- // ../core/dist/utils/roadmap-state.js
6045
- import * as fs14 from "fs";
6046
- import * as path13 from "path";
6047
6183
  function getStateFilePath(prdDir) {
6048
6184
  return path13.join(prdDir, STATE_FILE_NAME);
6049
6185
  }
@@ -6124,7 +6260,8 @@ function getProcessedHashes(state) {
6124
6260
  function getStateItem(state, hash) {
6125
6261
  return state.items[hash];
6126
6262
  }
6127
- var STATE_VERSION, STATE_FILE_NAME;
6263
+ var STATE_VERSION;
6264
+ var STATE_FILE_NAME;
6128
6265
  var init_roadmap_state = __esm({
6129
6266
  "../core/dist/utils/roadmap-state.js"() {
6130
6267
  "use strict";
@@ -6133,10 +6270,6 @@ var init_roadmap_state = __esm({
6133
6270
  STATE_FILE_NAME = ".roadmap-state.json";
6134
6271
  }
6135
6272
  });
6136
-
6137
- // ../core/dist/templates/slicer-prompt.js
6138
- import * as fs15 from "fs";
6139
- import * as path14 from "path";
6140
6273
  function loadSlicerTemplate(templateDir) {
6141
6274
  if (cachedTemplate) {
6142
6275
  return cachedTemplate;
@@ -6172,7 +6305,8 @@ function createSlicerPromptVars(title, section, description, prdDir, prdFilename
6172
6305
  prdDir
6173
6306
  };
6174
6307
  }
6175
- var DEFAULT_SLICER_TEMPLATE, cachedTemplate;
6308
+ var DEFAULT_SLICER_TEMPLATE;
6309
+ var cachedTemplate;
6176
6310
  var init_slicer_prompt = __esm({
6177
6311
  "../core/dist/templates/slicer-prompt.js"() {
6178
6312
  "use strict";
@@ -6260,12 +6394,6 @@ DO NOT forget to write the file.
6260
6394
  cachedTemplate = null;
6261
6395
  }
6262
6396
  });
6263
-
6264
- // ../core/dist/utils/roadmap-scanner.js
6265
- import * as fs16 from "fs";
6266
- import * as path15 from "path";
6267
- import { spawn } from "child_process";
6268
- import { createHash as createHash3 } from "crypto";
6269
6397
  function normalizeAuditSeverity(raw) {
6270
6398
  const normalized = raw.trim().toLowerCase();
6271
6399
  if (normalized === "critical")
@@ -6631,8 +6759,6 @@ var init_roadmap_scanner = __esm({
6631
6759
  init_slicer_prompt();
6632
6760
  }
6633
6761
  });
6634
-
6635
- // ../core/dist/utils/script-result.js
6636
6762
  function parseScriptResult(output) {
6637
6763
  if (!output || output.trim().length === 0) {
6638
6764
  return null;
@@ -6677,9 +6803,6 @@ var init_script_result = __esm({
6677
6803
  RESULT_PREFIX = "NIGHT_WATCH_RESULT:";
6678
6804
  }
6679
6805
  });
6680
-
6681
- // ../core/dist/utils/shell.js
6682
- import { spawn as spawn2 } from "child_process";
6683
6806
  async function executeScript(scriptPath, args = [], env = {}, options = {}) {
6684
6807
  const result = await executeScriptWithOutput(scriptPath, args, env, options);
6685
6808
  return result.exitCode;
@@ -6724,8 +6847,6 @@ var init_shell = __esm({
6724
6847
  "use strict";
6725
6848
  }
6726
6849
  });
6727
-
6728
- // ../core/dist/utils/webhook-validator.js
6729
6850
  function validateWebhook(webhook) {
6730
6851
  const issues = [];
6731
6852
  if (!webhook.events || webhook.events.length === 0) {
@@ -6780,11 +6901,6 @@ var init_webhook_validator = __esm({
6780
6901
  "use strict";
6781
6902
  }
6782
6903
  });
6783
-
6784
- // ../core/dist/utils/worktree-manager.js
6785
- import { execFileSync as execFileSync4 } from "child_process";
6786
- import * as fs17 from "fs";
6787
- import * as path16 from "path";
6788
6904
  function gitExec(args, cwd, logFile) {
6789
6905
  try {
6790
6906
  const result = execFileSync4("git", args, {
@@ -6941,8 +7057,6 @@ var init_worktree_manager = __esm({
6941
7057
  init_git_utils();
6942
7058
  }
6943
7059
  });
6944
-
6945
- // ../core/dist/templates/prd-template.js
6946
7060
  function renderDependsOn(deps) {
6947
7061
  if (deps.length === 0) {
6948
7062
  return "";
@@ -7114,8 +7228,6 @@ sequenceDiagram
7114
7228
  `;
7115
7229
  }
7116
7230
  });
7117
-
7118
- // ../core/dist/index.js
7119
7231
  var dist_exports = {};
7120
7232
  __export(dist_exports, {
7121
7233
  AUDIT_LOG_NAME: () => AUDIT_LOG_NAME,
@@ -7419,22 +7531,7 @@ var init_dist = __esm({
7419
7531
  init_slicer_prompt();
7420
7532
  }
7421
7533
  });
7422
-
7423
- // dist/cli.js
7424
- import "reflect-metadata";
7425
- import { Command as Command2 } from "commander";
7426
- import { existsSync as existsSync30, readFileSync as readFileSync18 } from "fs";
7427
- import { fileURLToPath as fileURLToPath4 } from "url";
7428
- import { dirname as dirname8, join as join34 } from "path";
7429
-
7430
- // dist/commands/init.js
7431
7534
  init_dist();
7432
- import fs18 from "fs";
7433
- import path17 from "path";
7434
- import { execSync as execSync3 } from "child_process";
7435
- import { fileURLToPath as fileURLToPath2 } from "url";
7436
- import { dirname as dirname4, join as join16 } from "path";
7437
- import * as readline from "readline";
7438
7535
  var __filename = fileURLToPath2(import.meta.url);
7439
7536
  var __dirname2 = dirname4(__filename);
7440
7537
  function findTemplatesDir(startDir) {
@@ -7867,11 +7964,7 @@ function initCommand(program2) {
7867
7964
  console.log();
7868
7965
  });
7869
7966
  }
7870
-
7871
- // dist/commands/run.js
7872
7967
  init_dist();
7873
-
7874
- // dist/commands/shared/env-builder.js
7875
7968
  init_dist();
7876
7969
  function deriveProviderLabel(config, jobType) {
7877
7970
  if (config.providerLabel)
@@ -7913,10 +8006,6 @@ function formatProviderDisplay(providerCmd, providerLabel) {
7913
8006
  function getTelegramStatusWebhooks(config) {
7914
8007
  return (config.notifications?.webhooks ?? []).filter((wh) => wh.type === "telegram" && typeof wh.botToken === "string" && wh.botToken.trim().length > 0 && typeof wh.chatId === "string" && wh.chatId.trim().length > 0).map((wh) => ({ botToken: wh.botToken, chatId: wh.chatId }));
7915
8008
  }
7916
-
7917
- // dist/commands/run.js
7918
- import * as fs19 from "fs";
7919
- import * as path18 from "path";
7920
8009
  function resolveRunNotificationEvent(exitCode, scriptStatus) {
7921
8010
  if (exitCode === 124) {
7922
8011
  return "run_timeout";
@@ -8247,11 +8336,7 @@ ${stderr}`);
8247
8336
  }
8248
8337
  });
8249
8338
  }
8250
-
8251
- // dist/commands/review.js
8252
8339
  init_dist();
8253
- import { execFileSync as execFileSync5 } from "child_process";
8254
- import * as path19 from "path";
8255
8340
  function shouldSendReviewNotification(scriptStatus) {
8256
8341
  if (!scriptStatus) {
8257
8342
  return true;
@@ -8512,10 +8597,7 @@ ${stderr}`);
8512
8597
  }
8513
8598
  });
8514
8599
  }
8515
-
8516
- // dist/commands/qa.js
8517
8600
  init_dist();
8518
- import * as path20 from "path";
8519
8601
  function shouldSendQaNotification(scriptStatus) {
8520
8602
  if (!scriptStatus) {
8521
8603
  return true;
@@ -8666,11 +8748,7 @@ ${stderr}`);
8666
8748
  }
8667
8749
  });
8668
8750
  }
8669
-
8670
- // dist/commands/audit.js
8671
8751
  init_dist();
8672
- import * as fs20 from "fs";
8673
- import * as path21 from "path";
8674
8752
  function buildEnvVars4(config, options) {
8675
8753
  const env = buildBaseEnvVars(config, "audit", options.dryRun);
8676
8754
  env.NW_AUDIT_MAX_RUNTIME = String(config.audit.maxRuntime);
@@ -8766,12 +8844,7 @@ ${stderr}`);
8766
8844
  }
8767
8845
  });
8768
8846
  }
8769
-
8770
- // dist/commands/install.js
8771
8847
  init_dist();
8772
- import { execSync as execSync4 } from "child_process";
8773
- import * as path22 from "path";
8774
- import * as fs21 from "fs";
8775
8848
  function shellQuote(value) {
8776
8849
  return `'${value.replace(/'/g, `'"'"'`)}'`;
8777
8850
  }
@@ -8833,11 +8906,11 @@ function performInstall(projectDir, config, options) {
8833
8906
  const executorLog = path22.join(logDir, "executor.log");
8834
8907
  const reviewerLog = path22.join(logDir, "reviewer.log");
8835
8908
  if (!options?.force) {
8836
- const existingEntries = Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
8837
- if (existingEntries.length > 0) {
8909
+ const existingEntries2 = Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
8910
+ if (existingEntries2.length > 0) {
8838
8911
  return {
8839
8912
  success: false,
8840
- entries: existingEntries,
8913
+ entries: existingEntries2,
8841
8914
  error: "Already installed. Uninstall first or use force."
8842
8915
  };
8843
8916
  }
@@ -8884,8 +8957,10 @@ function performInstall(projectDir, config, options) {
8884
8957
  const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
8885
8958
  entries.push(auditEntry);
8886
8959
  }
8960
+ const existingEntries = new Set(Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)])));
8887
8961
  const currentCrontab = readCrontab();
8888
- const newCrontab = [...currentCrontab, ...entries];
8962
+ const baseCrontab = options?.force ? currentCrontab.filter((line) => !existingEntries.has(line) && !line.includes(marker)) : currentCrontab;
8963
+ const newCrontab = [...baseCrontab, ...entries];
8889
8964
  writeCrontab(newCrontab);
8890
8965
  return { success: true, entries };
8891
8966
  } catch (err) {
@@ -8897,7 +8972,7 @@ function performInstall(projectDir, config, options) {
8897
8972
  }
8898
8973
  }
8899
8974
  function installCommand(program2) {
8900
- program2.command("install").description("Add crontab entries for automated execution").option("-s, --schedule <cron>", "Cron schedule for PRD executor").option("--reviewer-schedule <cron>", "Cron schedule for reviewer").option("--no-reviewer", "Skip installing reviewer cron").option("--no-slicer", "Skip installing slicer cron").option("--no-qa", "Skip installing QA cron").option("--no-audit", "Skip installing audit cron").action(async (options) => {
8975
+ program2.command("install").description("Add crontab entries for automated execution").option("-s, --schedule <cron>", "Cron schedule for PRD executor").option("--reviewer-schedule <cron>", "Cron schedule for reviewer").option("--no-reviewer", "Skip installing reviewer cron").option("--no-slicer", "Skip installing slicer cron").option("--no-qa", "Skip installing QA cron").option("--no-audit", "Skip installing audit cron").option("-f, --force", "Replace existing cron entries for this project").action(async (options) => {
8901
8976
  try {
8902
8977
  const projectDir = process.cwd();
8903
8978
  const config = loadConfig(projectDir);
@@ -8914,13 +8989,13 @@ function installCommand(program2) {
8914
8989
  const executorLog = path22.join(logDir, "executor.log");
8915
8990
  const reviewerLog = path22.join(logDir, "reviewer.log");
8916
8991
  const existingEntries = Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
8917
- if (existingEntries.length > 0) {
8992
+ if (existingEntries.length > 0 && !options.force) {
8918
8993
  warn(`Night Watch is already installed for ${projectName}.`);
8919
8994
  console.log();
8920
8995
  dim("Existing crontab entries:");
8921
8996
  existingEntries.forEach((entry) => dim(` ${entry}`));
8922
8997
  console.log();
8923
- dim("Run 'night-watch uninstall' first to reinstall.");
8998
+ dim("Run 'night-watch install --force' to replace them.");
8924
8999
  return;
8925
9000
  }
8926
9001
  const entries = [];
@@ -8968,8 +9043,10 @@ function installCommand(program2) {
8968
9043
  const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
8969
9044
  entries.push(auditEntry);
8970
9045
  }
9046
+ const existingEntrySet = new Set(existingEntries);
8971
9047
  const currentCrontab = readCrontab();
8972
- const newCrontab = [...currentCrontab, ...entries];
9048
+ const baseCrontab = options.force ? currentCrontab.filter((line) => !existingEntrySet.has(line) && !line.includes(marker)) : currentCrontab;
9049
+ const newCrontab = [...baseCrontab, ...entries];
8973
9050
  writeCrontab(newCrontab);
8974
9051
  success(`Night Watch installed successfully for ${projectName}!`);
8975
9052
  console.log();
@@ -9001,11 +9078,7 @@ function installCommand(program2) {
9001
9078
  }
9002
9079
  });
9003
9080
  }
9004
-
9005
- // dist/commands/uninstall.js
9006
9081
  init_dist();
9007
- import * as path23 from "path";
9008
- import * as fs22 from "fs";
9009
9082
  function performUninstall(projectDir, options) {
9010
9083
  try {
9011
9084
  const projectName = getProjectName(projectDir);
@@ -9093,10 +9166,7 @@ function uninstallCommand(program2) {
9093
9166
  }
9094
9167
  });
9095
9168
  }
9096
-
9097
- // dist/commands/status.js
9098
9169
  init_dist();
9099
- import chalk2 from "chalk";
9100
9170
  function formatBytes(bytes) {
9101
9171
  if (bytes === 0)
9102
9172
  return "0 B";
@@ -9300,12 +9370,7 @@ function statusCommand(program2) {
9300
9370
  }
9301
9371
  });
9302
9372
  }
9303
-
9304
- // dist/commands/logs.js
9305
9373
  init_dist();
9306
- import { spawn as spawn3 } from "child_process";
9307
- import * as path24 from "path";
9308
- import * as fs23 from "fs";
9309
9374
  function getLastLines(filePath, lineCount) {
9310
9375
  if (!fs23.existsSync(filePath)) {
9311
9376
  return `Log file not found: ${filePath}`;
@@ -9410,12 +9475,7 @@ function logsCommand(program2) {
9410
9475
  }
9411
9476
  });
9412
9477
  }
9413
-
9414
- // dist/commands/prd.js
9415
9478
  init_dist();
9416
- import * as fs24 from "fs";
9417
- import * as path25 from "path";
9418
- import * as readline2 from "readline";
9419
9479
  function slugify2(name) {
9420
9480
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
9421
9481
  }
@@ -9610,15 +9670,8 @@ function prdCommand(program2) {
9610
9670
  info(`${pendingCount} pending, ${claimedCount} claimed, ${done.length} done`);
9611
9671
  });
9612
9672
  }
9613
-
9614
- // dist/commands/dashboard.js
9615
9673
  init_dist();
9616
- import blessed6 from "blessed";
9617
-
9618
- // dist/commands/dashboard/tab-status.js
9619
9674
  init_dist();
9620
- import blessed from "blessed";
9621
- import * as fs25 from "fs";
9622
9675
  function sortPrdsByPriority2(prds, priority) {
9623
9676
  if (priority.length === 0)
9624
9677
  return prds;
@@ -10004,10 +10057,7 @@ function createStatusTab() {
10004
10057
  }
10005
10058
  };
10006
10059
  }
10007
-
10008
- // dist/commands/dashboard/tab-config.js
10009
10060
  init_dist();
10010
- import blessed2 from "blessed";
10011
10061
  var SENSITIVE_PATTERNS = /TOKEN|KEY|SECRET|PASSWORD/i;
10012
10062
  function promptTextbox(screen, label2, initialValue, cb) {
10013
10063
  const input = blessed2.textbox({
@@ -10826,10 +10876,6 @@ function createConfigTab() {
10826
10876
  }
10827
10877
  };
10828
10878
  }
10829
-
10830
- // dist/commands/dashboard/tab-schedules.js
10831
- import blessed3 from "blessed";
10832
- import cronstrue from "cronstrue";
10833
10879
  init_dist();
10834
10880
  function cronToHuman(cron) {
10835
10881
  const trimmed = cron.trim();
@@ -11126,10 +11172,6 @@ function createSchedulesTab() {
11126
11172
  }
11127
11173
  };
11128
11174
  }
11129
-
11130
- // dist/commands/dashboard/tab-actions.js
11131
- import blessed4 from "blessed";
11132
- import { spawn as spawn4 } from "child_process";
11133
11175
  function spawnAction(args, ctx, outputBox, onDone) {
11134
11176
  outputBox.setContent("{cyan-fg}Starting...{/cyan-fg}\n");
11135
11177
  ctx.screen.render();
@@ -11367,12 +11409,7 @@ function createActionsTab() {
11367
11409
  }
11368
11410
  };
11369
11411
  }
11370
-
11371
- // dist/commands/dashboard/tab-logs.js
11372
11412
  init_dist();
11373
- import blessed5 from "blessed";
11374
- import * as fs26 from "fs";
11375
- import * as path26 from "path";
11376
11413
  var LOG_NAMES = ["executor", "reviewer"];
11377
11414
  var LOG_LINES = 200;
11378
11415
  function createLogsTab() {
@@ -11562,8 +11599,6 @@ Log will appear here once the ${logName} runs.`);
11562
11599
  }
11563
11600
  };
11564
11601
  }
11565
-
11566
- // dist/commands/dashboard.js
11567
11602
  function showMessage(screen, text, type, durationMs = 2e3) {
11568
11603
  const colors = { success: "green", error: "red", info: "cyan" };
11569
11604
  const msgBox = blessed6.box({
@@ -11759,8 +11794,6 @@ function dashboardCommand(program2) {
11759
11794
  screen.render();
11760
11795
  });
11761
11796
  }
11762
-
11763
- // dist/commands/doctor.js
11764
11797
  init_dist();
11765
11798
  function validateWebhook2(webhook) {
11766
11799
  const issues = [];
@@ -11919,27 +11952,12 @@ function doctorCommand(program2) {
11919
11952
  }
11920
11953
  });
11921
11954
  }
11922
-
11923
- // dist/commands/serve.js
11924
11955
  init_dist();
11925
- import * as fs31 from "fs";
11926
-
11927
- // ../server/dist/index.js
11928
11956
  init_dist();
11929
- import * as fs30 from "fs";
11930
- import * as path32 from "path";
11931
- import { dirname as dirname7 } from "path";
11932
- import { fileURLToPath as fileURLToPath3 } from "url";
11933
- import cors from "cors";
11934
- import express from "express";
11935
-
11936
- // ../server/dist/middleware/error-handler.middleware.js
11937
11957
  function errorHandler(err, _req, res, _next) {
11938
11958
  console.error("API Error:", err);
11939
11959
  res.status(500).json({ error: err.message });
11940
11960
  }
11941
-
11942
- // ../server/dist/middleware/graceful-shutdown.middleware.js
11943
11961
  var PRE_SHUTDOWN_TIMEOUT_MS = 5e3;
11944
11962
  var GRACEFUL_SHUTDOWN_TIMEOUT_MS = 12e3;
11945
11963
  function withTimeout(promise, timeoutMs, label2) {
@@ -12009,11 +12027,7 @@ function setupGracefulShutdown(server, beforeClose) {
12009
12027
  process.on("SIGTERM", () => shutdown("SIGTERM"));
12010
12028
  process.on("SIGINT", () => shutdown("SIGINT"));
12011
12029
  }
12012
-
12013
- // ../server/dist/middleware/project-resolver.middleware.js
12014
12030
  init_dist();
12015
- import * as fs27 from "fs";
12016
- import * as path27 from "path";
12017
12031
  function resolveProject(req, res, next) {
12018
12032
  const projectId = req.params.projectId;
12019
12033
  const decodedId = decodeURIComponent(projectId).replace(/~/g, "/");
@@ -12031,8 +12045,6 @@ function resolveProject(req, res, next) {
12031
12045
  req.projectConfig = loadConfig(entry.path);
12032
12046
  next();
12033
12047
  }
12034
-
12035
- // ../server/dist/middleware/sse.middleware.js
12036
12048
  init_dist();
12037
12049
  function broadcastSSE(clients, event, data) {
12038
12050
  const msg = `event: ${event}
@@ -12065,15 +12077,7 @@ function startSseStatusWatcher(clients, projectDir, getConfig) {
12065
12077
  });
12066
12078
  }, 2e3);
12067
12079
  }
12068
-
12069
- // ../server/dist/routes/action.routes.js
12070
12080
  init_dist();
12071
- import * as fs28 from "fs";
12072
- import * as path28 from "path";
12073
- import { execSync as execSync5, spawn as spawn5 } from "child_process";
12074
- import { Router } from "express";
12075
-
12076
- // ../server/dist/helpers.js
12077
12081
  init_dist();
12078
12082
  function validatePrdName(name) {
12079
12083
  return /^[a-zA-Z0-9_-]+(\.md)?$/.test(name) && !name.includes("..");
@@ -12115,8 +12119,6 @@ function getBoardProvider(config, projectDir) {
12115
12119
  }
12116
12120
  return createBoardProvider(config.boardProvider, projectDir);
12117
12121
  }
12118
-
12119
- // ../server/dist/routes/action.routes.js
12120
12122
  function cleanOrphanedClaims(dir) {
12121
12123
  let entries;
12122
12124
  try {
@@ -12239,8 +12241,7 @@ function createActionRouteHandlers(ctx) {
12239
12241
  router.post(`/${p}install-cron`, (req, res) => {
12240
12242
  const projectDir = ctx.getProjectDir(req);
12241
12243
  try {
12242
- runCliCommand(projectDir, ["uninstall", "--keep-logs"]);
12243
- runCliCommand(projectDir, ["install"]);
12244
+ runCliCommand(projectDir, ["install", "--force"]);
12244
12245
  res.json({ started: true });
12245
12246
  } catch (error2) {
12246
12247
  res.status(500).json({ error: formatCommandError(error2) });
@@ -12361,10 +12362,7 @@ function createProjectActionRoutes(deps) {
12361
12362
  pathPrefix: "actions/"
12362
12363
  });
12363
12364
  }
12364
-
12365
- // ../server/dist/routes/agent.routes.js
12366
12365
  init_dist();
12367
- import { Router as Router2 } from "express";
12368
12366
  function createAgentRoutes() {
12369
12367
  const router = Router2();
12370
12368
  router.post("/seed-defaults", (_req, res) => {
@@ -12464,10 +12462,7 @@ function createAgentRoutes() {
12464
12462
  });
12465
12463
  return router;
12466
12464
  }
12467
-
12468
- // ../server/dist/routes/board.routes.js
12469
12465
  init_dist();
12470
- import { Router as Router3 } from "express";
12471
12466
  var ERROR_BOARD_NOT_CONFIGURED = "Board not configured";
12472
12467
  function createBoardRouteHandlers(ctx) {
12473
12468
  const router = Router3({ mergeParams: true });
@@ -12648,10 +12643,27 @@ function createProjectBoardRoutes() {
12648
12643
  pathPrefix: "board/"
12649
12644
  });
12650
12645
  }
12651
-
12652
- // ../server/dist/routes/config.routes.js
12653
12646
  init_dist();
12654
- import { Router as Router4 } from "express";
12647
+ function isValidCronExpression(value) {
12648
+ try {
12649
+ CronExpressionParser.parse(value.trim());
12650
+ return true;
12651
+ } catch {
12652
+ return false;
12653
+ }
12654
+ }
12655
+ function validateCronField(fieldName, value) {
12656
+ if (value === void 0) {
12657
+ return null;
12658
+ }
12659
+ if (typeof value !== "string" || value.trim().length === 0) {
12660
+ return `${fieldName} must be a non-empty string`;
12661
+ }
12662
+ if (!isValidCronExpression(value)) {
12663
+ return `${fieldName} must be a valid cron expression`;
12664
+ }
12665
+ return null;
12666
+ }
12655
12667
  function validateConfigChanges(changes) {
12656
12668
  if (typeof changes !== "object" || changes === null) {
12657
12669
  return "Invalid request body";
@@ -12704,11 +12716,18 @@ function validateConfigChanges(changes) {
12704
12716
  if (changes.prdPriority !== void 0 && (!Array.isArray(changes.prdPriority) || !changes.prdPriority.every((p) => typeof p === "string"))) {
12705
12717
  return "prdPriority must be an array of strings";
12706
12718
  }
12707
- if (changes.cronSchedule !== void 0 && (typeof changes.cronSchedule !== "string" || changes.cronSchedule.trim().length === 0)) {
12708
- return "cronSchedule must be a non-empty string";
12719
+ const rootCronFields = [
12720
+ ["cronSchedule", changes.cronSchedule],
12721
+ ["reviewerSchedule", changes.reviewerSchedule]
12722
+ ];
12723
+ for (const [fieldName, value] of rootCronFields) {
12724
+ const cronError = validateCronField(fieldName, value);
12725
+ if (cronError) {
12726
+ return cronError;
12727
+ }
12709
12728
  }
12710
- if (changes.reviewerSchedule !== void 0 && (typeof changes.reviewerSchedule !== "string" || changes.reviewerSchedule.trim().length === 0)) {
12711
- return "reviewerSchedule must be a non-empty string";
12729
+ if (changes.scheduleBundleId !== void 0 && changes.scheduleBundleId !== null && (typeof changes.scheduleBundleId !== "string" || changes.scheduleBundleId.trim().length === 0)) {
12730
+ return "scheduleBundleId must be a non-empty string or null";
12712
12731
  }
12713
12732
  if (changes.notifications?.webhooks !== void 0) {
12714
12733
  if (!Array.isArray(changes.notifications.webhooks)) {
@@ -12735,6 +12754,19 @@ function validateConfigChanges(changes) {
12735
12754
  if (rs.autoScanInterval !== void 0 && (typeof rs.autoScanInterval !== "number" || rs.autoScanInterval < 30)) {
12736
12755
  return "roadmapScanner.autoScanInterval must be a number >= 30";
12737
12756
  }
12757
+ const slicerScheduleError = validateCronField("roadmapScanner.slicerSchedule", rs.slicerSchedule);
12758
+ if (slicerScheduleError) {
12759
+ return slicerScheduleError;
12760
+ }
12761
+ if (rs.slicerMaxRuntime !== void 0 && (typeof rs.slicerMaxRuntime !== "number" || rs.slicerMaxRuntime < 60)) {
12762
+ return "roadmapScanner.slicerMaxRuntime must be a number >= 60";
12763
+ }
12764
+ if (rs.priorityMode !== void 0 && rs.priorityMode !== "roadmap-first" && rs.priorityMode !== "audit-first") {
12765
+ return "roadmapScanner.priorityMode must be one of: roadmap-first, audit-first";
12766
+ }
12767
+ if (rs.issueColumn !== void 0 && rs.issueColumn !== "Draft" && rs.issueColumn !== "Ready") {
12768
+ return "roadmapScanner.issueColumn must be one of: Draft, Ready";
12769
+ }
12738
12770
  }
12739
12771
  if (changes.providerEnv !== void 0) {
12740
12772
  if (typeof changes.providerEnv !== "object" || changes.providerEnv === null) {
@@ -12794,8 +12826,9 @@ function validateConfigChanges(changes) {
12794
12826
  if (qa.enabled !== void 0 && typeof qa.enabled !== "boolean") {
12795
12827
  return "qa.enabled must be a boolean";
12796
12828
  }
12797
- if (qa.schedule !== void 0 && (typeof qa.schedule !== "string" || qa.schedule.trim().length === 0)) {
12798
- return "qa.schedule must be a non-empty string";
12829
+ const qaScheduleError = validateCronField("qa.schedule", qa.schedule);
12830
+ if (qaScheduleError) {
12831
+ return qaScheduleError;
12799
12832
  }
12800
12833
  if (qa.maxRuntime !== void 0 && (typeof qa.maxRuntime !== "number" || qa.maxRuntime < 60)) {
12801
12834
  return "qa.maxRuntime must be a number >= 60";
@@ -12826,28 +12859,14 @@ function validateConfigChanges(changes) {
12826
12859
  if (audit.enabled !== void 0 && typeof audit.enabled !== "boolean") {
12827
12860
  return "audit.enabled must be a boolean";
12828
12861
  }
12829
- if (audit.schedule !== void 0 && (typeof audit.schedule !== "string" || audit.schedule.trim().length === 0)) {
12830
- return "audit.schedule must be a non-empty string";
12862
+ const auditScheduleError = validateCronField("audit.schedule", audit.schedule);
12863
+ if (auditScheduleError) {
12864
+ return auditScheduleError;
12831
12865
  }
12832
12866
  if (audit.maxRuntime !== void 0 && (typeof audit.maxRuntime !== "number" || audit.maxRuntime < 60)) {
12833
12867
  return "audit.maxRuntime must be a number >= 60";
12834
12868
  }
12835
12869
  }
12836
- if (changes.roadmapScanner !== void 0) {
12837
- const rs = changes.roadmapScanner;
12838
- if (rs.slicerSchedule !== void 0 && (typeof rs.slicerSchedule !== "string" || rs.slicerSchedule.trim().length === 0)) {
12839
- return "roadmapScanner.slicerSchedule must be a non-empty string";
12840
- }
12841
- if (rs.slicerMaxRuntime !== void 0 && (typeof rs.slicerMaxRuntime !== "number" || rs.slicerMaxRuntime < 60)) {
12842
- return "roadmapScanner.slicerMaxRuntime must be a number >= 60";
12843
- }
12844
- if (rs.priorityMode !== void 0 && rs.priorityMode !== "roadmap-first" && rs.priorityMode !== "audit-first") {
12845
- return "roadmapScanner.priorityMode must be one of: roadmap-first, audit-first";
12846
- }
12847
- if (rs.issueColumn !== void 0 && rs.issueColumn !== "Draft" && rs.issueColumn !== "Ready") {
12848
- return "roadmapScanner.issueColumn must be one of: Draft, Ready";
12849
- }
12850
- }
12851
12870
  if (changes.boardProvider !== void 0) {
12852
12871
  if (typeof changes.boardProvider !== "object" || changes.boardProvider === null) {
12853
12872
  return "boardProvider must be an object";
@@ -12928,13 +12947,7 @@ function createProjectConfigRoutes() {
12928
12947
  });
12929
12948
  return router;
12930
12949
  }
12931
-
12932
- // ../server/dist/routes/doctor.routes.js
12933
12950
  init_dist();
12934
- import * as fs29 from "fs";
12935
- import * as path29 from "path";
12936
- import { execSync as execSync6 } from "child_process";
12937
- import { Router as Router5 } from "express";
12938
12951
  function runDoctorChecks(projectDir, config) {
12939
12952
  const checks = [];
12940
12953
  try {
@@ -13036,11 +13049,7 @@ function createProjectDoctorRoutes() {
13036
13049
  });
13037
13050
  return router;
13038
13051
  }
13039
-
13040
- // ../server/dist/routes/log.routes.js
13041
13052
  init_dist();
13042
- import * as path30 from "path";
13043
- import { Router as Router6 } from "express";
13044
13053
  function createLogRoutes(deps) {
13045
13054
  const { projectDir } = deps;
13046
13055
  const router = Router6();
@@ -13093,9 +13102,6 @@ function createProjectLogRoutes() {
13093
13102
  });
13094
13103
  return router;
13095
13104
  }
13096
-
13097
- // ../server/dist/routes/prd.routes.js
13098
- import { Router as Router7 } from "express";
13099
13105
  function createPrdRoutes(_deps) {
13100
13106
  const router = Router7();
13101
13107
  router.get("/", (_req, res) => {
@@ -13116,11 +13122,7 @@ function createProjectPrdRoutes() {
13116
13122
  });
13117
13123
  return router;
13118
13124
  }
13119
-
13120
- // ../server/dist/routes/roadmap.routes.js
13121
13125
  init_dist();
13122
- import * as path31 from "path";
13123
- import { Router as Router8 } from "express";
13124
13126
  function createRoadmapRouteHandlers(ctx) {
13125
13127
  const router = Router8({ mergeParams: true });
13126
13128
  const p = ctx.pathPrefix;
@@ -13200,11 +13202,7 @@ function createProjectRoadmapRoutes() {
13200
13202
  pathPrefix: "roadmap/"
13201
13203
  });
13202
13204
  }
13203
-
13204
- // ../server/dist/routes/status.routes.js
13205
13205
  init_dist();
13206
- import { Router as Router9 } from "express";
13207
- import { CronExpressionParser } from "cron-parser";
13208
13206
  function createStatusRoutes(deps) {
13209
13207
  const { projectDir, getConfig, sseClients } = deps;
13210
13208
  const router = Router9();
@@ -13235,9 +13233,20 @@ data: ${JSON.stringify(snapshot)}
13235
13233
  });
13236
13234
  return router;
13237
13235
  }
13236
+ function applyScheduleOffset2(schedule, offset) {
13237
+ if (offset === 0) {
13238
+ return schedule;
13239
+ }
13240
+ const parts = schedule.trim().split(/\s+/);
13241
+ if (parts.length < 5 || !/^\d+$/.test(parts[0])) {
13242
+ return schedule.trim();
13243
+ }
13244
+ parts[0] = String(offset);
13245
+ return parts.join(" ");
13246
+ }
13238
13247
  function computeNextRun(cronExpr) {
13239
13248
  try {
13240
- const interval = CronExpressionParser.parse(cronExpr);
13249
+ const interval = CronExpressionParser2.parse(cronExpr);
13241
13250
  return interval.next().toISOString();
13242
13251
  } catch {
13243
13252
  return null;
@@ -13247,6 +13256,48 @@ function hasScheduledCommand(entries, command) {
13247
13256
  const commandPattern = new RegExp(`\\s${command}\\s+>>`);
13248
13257
  return entries.some((entry) => commandPattern.test(entry));
13249
13258
  }
13259
+ function buildScheduleInfoResponse(config, entries, installed) {
13260
+ const offset = config.cronScheduleOffset ?? 0;
13261
+ const executorSchedule = applyScheduleOffset2(config.cronSchedule, offset);
13262
+ const reviewerSchedule = applyScheduleOffset2(config.reviewerSchedule, offset);
13263
+ const qaSchedule = applyScheduleOffset2(config.qa.schedule, offset);
13264
+ const auditSchedule = applyScheduleOffset2(config.audit.schedule, offset);
13265
+ const plannerSchedule = applyScheduleOffset2(config.roadmapScanner.slicerSchedule, offset);
13266
+ const executorInstalled = installed && config.executorEnabled !== false && hasScheduledCommand(entries, "run");
13267
+ const reviewerInstalled = installed && config.reviewerEnabled && hasScheduledCommand(entries, "review");
13268
+ const qaInstalled = installed && config.qa.enabled && hasScheduledCommand(entries, "qa");
13269
+ const auditInstalled = installed && config.audit.enabled && hasScheduledCommand(entries, "audit");
13270
+ const plannerInstalled = installed && config.roadmapScanner.enabled && (hasScheduledCommand(entries, "planner") || hasScheduledCommand(entries, "slice"));
13271
+ return {
13272
+ executor: {
13273
+ schedule: executorSchedule,
13274
+ installed: executorInstalled,
13275
+ nextRun: executorInstalled ? computeNextRun(executorSchedule) : null
13276
+ },
13277
+ reviewer: {
13278
+ schedule: reviewerSchedule,
13279
+ installed: reviewerInstalled,
13280
+ nextRun: reviewerInstalled ? computeNextRun(reviewerSchedule) : null
13281
+ },
13282
+ qa: {
13283
+ schedule: qaSchedule,
13284
+ installed: qaInstalled,
13285
+ nextRun: qaInstalled ? computeNextRun(qaSchedule) : null
13286
+ },
13287
+ audit: {
13288
+ schedule: auditSchedule,
13289
+ installed: auditInstalled,
13290
+ nextRun: auditInstalled ? computeNextRun(auditSchedule) : null
13291
+ },
13292
+ planner: {
13293
+ schedule: plannerSchedule,
13294
+ installed: plannerInstalled,
13295
+ nextRun: plannerInstalled ? computeNextRun(plannerSchedule) : null
13296
+ },
13297
+ paused: !installed,
13298
+ entries
13299
+ };
13300
+ }
13250
13301
  function createScheduleInfoRoutes(deps) {
13251
13302
  const { projectDir, getConfig } = deps;
13252
13303
  const router = Router9();
@@ -13254,42 +13305,7 @@ function createScheduleInfoRoutes(deps) {
13254
13305
  try {
13255
13306
  const config = getConfig();
13256
13307
  const snapshot = await fetchStatusSnapshot(projectDir, config);
13257
- const installed = snapshot.crontab.installed;
13258
- const entries = snapshot.crontab.entries;
13259
- const executorInstalled = installed && config.executorEnabled !== false && hasScheduledCommand(entries, "run");
13260
- const reviewerInstalled = installed && config.reviewerEnabled && hasScheduledCommand(entries, "review");
13261
- const qaInstalled = installed && config.qa.enabled && hasScheduledCommand(entries, "qa");
13262
- const auditInstalled = installed && config.audit.enabled && hasScheduledCommand(entries, "audit");
13263
- const plannerInstalled = installed && config.roadmapScanner.enabled && (hasScheduledCommand(entries, "planner") || hasScheduledCommand(entries, "slice"));
13264
- res.json({
13265
- executor: {
13266
- schedule: config.cronSchedule,
13267
- installed: executorInstalled,
13268
- nextRun: executorInstalled ? computeNextRun(config.cronSchedule) : null
13269
- },
13270
- reviewer: {
13271
- schedule: config.reviewerSchedule,
13272
- installed: reviewerInstalled,
13273
- nextRun: reviewerInstalled ? computeNextRun(config.reviewerSchedule) : null
13274
- },
13275
- qa: {
13276
- schedule: config.qa.schedule,
13277
- installed: qaInstalled,
13278
- nextRun: qaInstalled ? computeNextRun(config.qa.schedule) : null
13279
- },
13280
- audit: {
13281
- schedule: config.audit.schedule,
13282
- installed: auditInstalled,
13283
- nextRun: auditInstalled ? computeNextRun(config.audit.schedule) : null
13284
- },
13285
- planner: {
13286
- schedule: config.roadmapScanner.slicerSchedule,
13287
- installed: plannerInstalled,
13288
- nextRun: plannerInstalled ? computeNextRun(config.roadmapScanner.slicerSchedule) : null
13289
- },
13290
- paused: !installed,
13291
- entries
13292
- });
13308
+ res.json(buildScheduleInfoResponse(config, snapshot.crontab.entries, snapshot.crontab.installed));
13293
13309
  } catch (error2) {
13294
13310
  res.status(500).json({ error: error2 instanceof Error ? error2.message : String(error2) });
13295
13311
  }
@@ -13345,50 +13361,13 @@ data: ${JSON.stringify(snapshot)}
13345
13361
  const config = req.projectConfig;
13346
13362
  const projectDir = req.projectDir;
13347
13363
  const snapshot = await fetchStatusSnapshot(projectDir, config);
13348
- const installed = snapshot.crontab.installed;
13349
- const entries = snapshot.crontab.entries;
13350
- const executorInstalled = installed && config.executorEnabled !== false && hasScheduledCommand(entries, "run");
13351
- const reviewerInstalled = installed && config.reviewerEnabled && hasScheduledCommand(entries, "review");
13352
- const qaInstalled = installed && config.qa.enabled && hasScheduledCommand(entries, "qa");
13353
- const auditInstalled = installed && config.audit.enabled && hasScheduledCommand(entries, "audit");
13354
- const plannerInstalled = installed && config.roadmapScanner.enabled && (hasScheduledCommand(entries, "planner") || hasScheduledCommand(entries, "slice"));
13355
- res.json({
13356
- executor: {
13357
- schedule: config.cronSchedule,
13358
- installed: executorInstalled,
13359
- nextRun: executorInstalled ? computeNextRun(config.cronSchedule) : null
13360
- },
13361
- reviewer: {
13362
- schedule: config.reviewerSchedule,
13363
- installed: reviewerInstalled,
13364
- nextRun: reviewerInstalled ? computeNextRun(config.reviewerSchedule) : null
13365
- },
13366
- qa: {
13367
- schedule: config.qa.schedule,
13368
- installed: qaInstalled,
13369
- nextRun: qaInstalled ? computeNextRun(config.qa.schedule) : null
13370
- },
13371
- audit: {
13372
- schedule: config.audit.schedule,
13373
- installed: auditInstalled,
13374
- nextRun: auditInstalled ? computeNextRun(config.audit.schedule) : null
13375
- },
13376
- planner: {
13377
- schedule: config.roadmapScanner.slicerSchedule,
13378
- installed: plannerInstalled,
13379
- nextRun: plannerInstalled ? computeNextRun(config.roadmapScanner.slicerSchedule) : null
13380
- },
13381
- paused: !installed,
13382
- entries
13383
- });
13364
+ res.json(buildScheduleInfoResponse(config, snapshot.crontab.entries, snapshot.crontab.installed));
13384
13365
  } catch (error2) {
13385
13366
  res.status(500).json({ error: error2 instanceof Error ? error2.message : String(error2) });
13386
13367
  }
13387
13368
  });
13388
13369
  return router;
13389
13370
  }
13390
-
13391
- // ../server/dist/index.js
13392
13371
  var __filename2 = fileURLToPath3(import.meta.url);
13393
13372
  var __dirname3 = dirname7(__filename2);
13394
13373
  function resolveWebDistPath() {
@@ -13569,8 +13548,6 @@ Night Watch Global UI`);
13569
13548
  });
13570
13549
  setupGracefulShutdown(server);
13571
13550
  }
13572
-
13573
- // dist/commands/serve.js
13574
13551
  function getServeLockPath(mode, port) {
13575
13552
  return `${LOCK_FILE_PREFIX}serve-${mode}-${port}.lock`;
13576
13553
  }
@@ -13692,8 +13669,6 @@ function serveCommand(program2) {
13692
13669
  }
13693
13670
  });
13694
13671
  }
13695
-
13696
- // dist/commands/history.js
13697
13672
  init_dist();
13698
13673
  var VALID_OUTCOMES = ["success", "failure", "timeout", "rate_limited"];
13699
13674
  function historyCommand(program2) {
@@ -13732,12 +13707,7 @@ function historyCommand(program2) {
13732
13707
  }
13733
13708
  });
13734
13709
  }
13735
-
13736
- // dist/commands/update.js
13737
13710
  init_dist();
13738
- import { spawnSync } from "child_process";
13739
- import * as fs32 from "fs";
13740
- import * as path33 from "path";
13741
13711
  var DEFAULT_GLOBAL_SPEC = "@jonit-dev/night-watch-cli@latest";
13742
13712
  function parseProjectDirs(projects, cwd) {
13743
13713
  if (!projects || projects.trim().length === 0) {
@@ -13801,8 +13771,6 @@ function updateCommand(program2) {
13801
13771
  }
13802
13772
  });
13803
13773
  }
13804
-
13805
- // dist/commands/prd-state.js
13806
13774
  init_dist();
13807
13775
  function prdStateCommand(program2) {
13808
13776
  const prdState = program2.command("prd-state").description("Manage PRD state entries in ~/.night-watch/prd-states.json");
@@ -13826,11 +13794,7 @@ function prdStateCommand(program2) {
13826
13794
  }
13827
13795
  });
13828
13796
  }
13829
-
13830
- // dist/commands/retry.js
13831
13797
  init_dist();
13832
- import * as fs33 from "fs";
13833
- import * as path34 from "path";
13834
13798
  function normalizePrdName(name) {
13835
13799
  if (!name.endsWith(".md")) {
13836
13800
  return `${name}.md`;
@@ -13876,10 +13840,7 @@ function retryCommand(program2) {
13876
13840
  process.exit(1);
13877
13841
  });
13878
13842
  }
13879
-
13880
- // dist/commands/prs.js
13881
13843
  init_dist();
13882
- import chalk3 from "chalk";
13883
13844
  function formatCiStatus(status) {
13884
13845
  switch (status) {
13885
13846
  case "pass":
@@ -13956,11 +13917,7 @@ function prsCommand(program2) {
13956
13917
  }
13957
13918
  });
13958
13919
  }
13959
-
13960
- // dist/commands/prds.js
13961
13920
  init_dist();
13962
- import chalk4 from "chalk";
13963
- import { execSync as execSync7 } from "child_process";
13964
13921
  function getOpenPrBranches(projectDir) {
13965
13922
  try {
13966
13923
  execSync7("git rev-parse --git-dir", {
@@ -14100,11 +14057,7 @@ function prdsCommand(program2) {
14100
14057
  }
14101
14058
  });
14102
14059
  }
14103
-
14104
- // dist/commands/cancel.js
14105
14060
  init_dist();
14106
- import * as fs34 from "fs";
14107
- import * as readline3 from "readline";
14108
14061
  function getLockFilePaths2(projectDir) {
14109
14062
  const runtimeKey = projectRuntimeKey(projectDir);
14110
14063
  return {
@@ -14281,11 +14234,7 @@ function cancelCommand(program2) {
14281
14234
  }
14282
14235
  });
14283
14236
  }
14284
-
14285
- // dist/commands/slice.js
14286
14237
  init_dist();
14287
- import * as fs35 from "fs";
14288
- import * as path35 from "path";
14289
14238
  function plannerLockPath2(projectDir) {
14290
14239
  return `${LOCK_FILE_PREFIX}slicer-${projectRuntimeKey(projectDir)}.lock`;
14291
14240
  }
@@ -14545,13 +14494,7 @@ function sliceCommand(program2) {
14545
14494
  }
14546
14495
  });
14547
14496
  }
14548
-
14549
- // dist/commands/state.js
14550
14497
  init_dist();
14551
- import * as os6 from "os";
14552
- import * as path36 from "path";
14553
- import chalk5 from "chalk";
14554
- import { Command } from "commander";
14555
14498
  function createStateCommand() {
14556
14499
  const state = new Command("state");
14557
14500
  state.description("Manage Night Watch state");
@@ -14591,15 +14534,8 @@ function createStateCommand() {
14591
14534
  });
14592
14535
  return state;
14593
14536
  }
14594
-
14595
- // dist/commands/board.js
14596
14537
  init_dist();
14597
14538
  init_dist();
14598
- import { execFileSync as execFileSync6 } from "child_process";
14599
- import * as fs36 from "fs";
14600
- import * as path37 from "path";
14601
- import * as readline4 from "readline";
14602
- import chalk6 from "chalk";
14603
14539
  async function run(fn) {
14604
14540
  try {
14605
14541
  await fn();
@@ -15127,8 +15063,6 @@ function boardCommand(program2) {
15127
15063
  }
15128
15064
  }));
15129
15065
  }
15130
-
15131
- // dist/cli.js
15132
15066
  var __filename3 = fileURLToPath4(import.meta.url);
15133
15067
  var __dirname4 = dirname8(__filename3);
15134
15068
  function findPackageRoot(dir) {