@jonit-dev/night-watch-cli 1.8.14-beta.5 → 1.8.14-beta.6

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
@@ -30,7 +30,9 @@ var init_types = __esm({
30
30
  "pr_resolver_conflict_resolved",
31
31
  "pr_resolver_failed",
32
32
  "merge_completed",
33
- "merge_failed"
33
+ "merge_failed",
34
+ "manager_blocked",
35
+ "manager_weekly_summary"
34
36
  ];
35
37
  }
36
38
  });
@@ -273,6 +275,52 @@ var init_job_registry = __esm({
273
275
  maxRuntime: 0
274
276
  }
275
277
  },
278
+ {
279
+ id: "manager",
280
+ name: "Manager",
281
+ description: "Monitors roadmap progress and creates draft PRDs for project gaps",
282
+ cliCommand: "manager",
283
+ logName: "manager",
284
+ lockSuffix: "-manager.lock",
285
+ queuePriority: 25,
286
+ envPrefix: "NW_MANAGER",
287
+ extraFields: [
288
+ {
289
+ name: "authority",
290
+ type: "enum",
291
+ enumValues: ["draft", "ready", "workflow"],
292
+ defaultValue: "draft"
293
+ },
294
+ {
295
+ name: "outputMode",
296
+ type: "enum",
297
+ enumValues: ["board-draft", "filesystem-prd", "report-only"],
298
+ defaultValue: "board-draft"
299
+ },
300
+ {
301
+ name: "targetColumn",
302
+ type: "enum",
303
+ enumValues: [...BOARD_COLUMNS],
304
+ defaultValue: "Draft"
305
+ },
306
+ { name: "memoryPath", type: "string", defaultValue: ".night-watch/manager/memory.md" },
307
+ { name: "docsDir", type: "string", defaultValue: ".night-watch/manager/docs" },
308
+ { name: "weeklySummaryEnabled", type: "boolean", defaultValue: true },
309
+ { name: "weeklySummaryDay", type: "number", defaultValue: 1 }
310
+ ],
311
+ defaultConfig: {
312
+ enabled: true,
313
+ schedule: "15 7 * * *",
314
+ maxRuntime: 0,
315
+ authority: "draft",
316
+ outputMode: "board-draft",
317
+ targetColumn: "Draft",
318
+ memoryPath: ".night-watch/manager/memory.md",
319
+ docsDir: ".night-watch/manager/docs",
320
+ weeklySummaryEnabled: true,
321
+ weeklySummaryDay: 1
322
+ }
323
+ },
276
324
  {
277
325
  id: "qa",
278
326
  name: "QA",
@@ -423,7 +471,7 @@ function resolveProviderBucketKey(provider, providerEnv) {
423
471
  return `claude-proxy:${baseUrl}`;
424
472
  }
425
473
  }
426
- var DEFAULT_DEFAULT_BRANCH, DEFAULT_PRD_DIR, DEFAULT_SUMMARY_WINDOW_HOURS, 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_REVIEWER_MAX_PRS_PER_RUN, DEFAULT_FEEDBACK, 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, DEFAULT_PRIMARY_FALLBACK_MODEL, DEFAULT_SECONDARY_FALLBACK_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_VALIDATED_LABEL, DEFAULT_QA, QA_LOG_NAME, DEFAULT_AUDIT_ENABLED, DEFAULT_AUDIT_SCHEDULE, DEFAULT_AUDIT_MAX_RUNTIME, DEFAULT_AUDIT_CREATE_ISSUES, DEFAULT_AUDIT_TARGET_COLUMN, DEFAULT_AUDIT, DEFAULT_ANALYTICS_ENABLED, DEFAULT_ANALYTICS_SCHEDULE, DEFAULT_ANALYTICS_MAX_RUNTIME, DEFAULT_ANALYTICS_LOOKBACK_DAYS, DEFAULT_ANALYTICS_TARGET_COLUMN, DEFAULT_ANALYTICS_PROMPT, DEFAULT_ANALYTICS, DEFAULT_PR_RESOLVER_ENABLED, DEFAULT_PR_RESOLVER_SCHEDULE, DEFAULT_PR_RESOLVER_MAX_RUNTIME, DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN, DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT, DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION, DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION, DEFAULT_PR_RESOLVER_READY_LABEL, DEFAULT_PR_RESOLVER, DEFAULT_MERGER_ENABLED, DEFAULT_MERGER_SCHEDULE, DEFAULT_MERGER_MAX_RUNTIME, DEFAULT_MERGER_MERGE_METHOD, DEFAULT_MERGER_MIN_REVIEW_SCORE, DEFAULT_MERGER_REBASE_BEFORE_MERGE, DEFAULT_MERGER_MAX_PRS_PER_RUN, DEFAULT_MERGER_CI_POLICY, DEFAULT_MERGER_LOCAL_CHECK_COMMAND, DEFAULT_MERGER, MERGER_LOG_NAME, AUDIT_LOG_NAME, PLANNER_LOG_NAME, ANALYTICS_LOG_NAME, PR_RESOLVER_LOG_NAME, VALID_PROVIDERS, VALID_JOB_TYPES, DEFAULT_JOB_PROVIDERS, DEFAULT_PROVIDER_SCHEDULE_OVERRIDES, DEFAULT_WEBHOOK_TRIGGER_SECRET_ENV, DEFAULT_WEBHOOK_TRIGGER_MAX_SKEW_SECONDS, DEFAULT_WEBHOOK_TRIGGERS, BUILT_IN_PRESETS, BUILT_IN_PRESET_IDS, 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, GLOBAL_NOTIFICATIONS_FILE_NAME, MAX_HISTORY_RECORDS_PER_PRD, DEFAULT_QUEUE_ENABLED, DEFAULT_QUEUE_MODE, DEFAULT_QUEUE_MAX_CONCURRENCY, DEFAULT_QUEUE_MAX_WAIT_TIME, DEFAULT_QUEUE_PRIORITY, DEFAULT_QUEUE, DEFAULT_SCHEDULING_PRIORITY;
474
+ var DEFAULT_DEFAULT_BRANCH, DEFAULT_PRD_DIR, DEFAULT_SUMMARY_WINDOW_HOURS, 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_REVIEWER_MAX_PRS_PER_RUN, DEFAULT_FEEDBACK, 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, DEFAULT_PRIMARY_FALLBACK_MODEL, DEFAULT_SECONDARY_FALLBACK_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_VALIDATED_LABEL, DEFAULT_QA, QA_LOG_NAME, DEFAULT_AUDIT_ENABLED, DEFAULT_AUDIT_SCHEDULE, DEFAULT_AUDIT_MAX_RUNTIME, DEFAULT_AUDIT_CREATE_ISSUES, DEFAULT_AUDIT_TARGET_COLUMN, DEFAULT_AUDIT, DEFAULT_ANALYTICS_ENABLED, DEFAULT_ANALYTICS_SCHEDULE, DEFAULT_ANALYTICS_MAX_RUNTIME, DEFAULT_ANALYTICS_LOOKBACK_DAYS, DEFAULT_ANALYTICS_TARGET_COLUMN, DEFAULT_ANALYTICS_PROMPT, DEFAULT_ANALYTICS, DEFAULT_MANAGER_ENABLED, DEFAULT_MANAGER_SCHEDULE, DEFAULT_MANAGER_MAX_RUNTIME, DEFAULT_MANAGER_AUTHORITY, DEFAULT_MANAGER_OUTPUT_MODE, DEFAULT_MANAGER_TARGET_COLUMN, DEFAULT_MANAGER_MEMORY_PATH, DEFAULT_MANAGER_DOCS_DIR, DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED, DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY, DEFAULT_MANAGER, DEFAULT_PR_RESOLVER_ENABLED, DEFAULT_PR_RESOLVER_SCHEDULE, DEFAULT_PR_RESOLVER_MAX_RUNTIME, DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN, DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT, DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION, DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION, DEFAULT_PR_RESOLVER_READY_LABEL, DEFAULT_PR_RESOLVER, DEFAULT_MERGER_ENABLED, DEFAULT_MERGER_SCHEDULE, DEFAULT_MERGER_MAX_RUNTIME, DEFAULT_MERGER_MERGE_METHOD, DEFAULT_MERGER_MIN_REVIEW_SCORE, DEFAULT_MERGER_REBASE_BEFORE_MERGE, DEFAULT_MERGER_MAX_PRS_PER_RUN, DEFAULT_MERGER_CI_POLICY, DEFAULT_MERGER_LOCAL_CHECK_COMMAND, DEFAULT_MERGER, MERGER_LOG_NAME, AUDIT_LOG_NAME, PLANNER_LOG_NAME, ANALYTICS_LOG_NAME, PR_RESOLVER_LOG_NAME, MANAGER_LOG_NAME, VALID_PROVIDERS, VALID_JOB_TYPES, DEFAULT_JOB_PROVIDERS, DEFAULT_PROVIDER_SCHEDULE_OVERRIDES, DEFAULT_WEBHOOK_TRIGGER_SECRET_ENV, DEFAULT_WEBHOOK_TRIGGER_MAX_SKEW_SECONDS, DEFAULT_WEBHOOK_TRIGGERS, BUILT_IN_PRESETS, BUILT_IN_PRESET_IDS, 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, GLOBAL_NOTIFICATIONS_FILE_NAME, MAX_HISTORY_RECORDS_PER_PRD, DEFAULT_QUEUE_ENABLED, DEFAULT_QUEUE_MODE, DEFAULT_QUEUE_MAX_CONCURRENCY, DEFAULT_QUEUE_MAX_WAIT_TIME, DEFAULT_QUEUE_PRIORITY, DEFAULT_QUEUE, DEFAULT_SCHEDULING_PRIORITY;
427
475
  var init_constants = __esm({
428
476
  "../core/dist/constants.js"() {
429
477
  "use strict";
@@ -534,6 +582,28 @@ If no issues are warranted, output an empty array: []`;
534
582
  targetColumn: DEFAULT_ANALYTICS_TARGET_COLUMN,
535
583
  analysisPrompt: DEFAULT_ANALYTICS_PROMPT
536
584
  };
585
+ DEFAULT_MANAGER_ENABLED = true;
586
+ DEFAULT_MANAGER_SCHEDULE = "15 7 * * *";
587
+ DEFAULT_MANAGER_MAX_RUNTIME = 0;
588
+ DEFAULT_MANAGER_AUTHORITY = "draft";
589
+ DEFAULT_MANAGER_OUTPUT_MODE = "board-draft";
590
+ DEFAULT_MANAGER_TARGET_COLUMN = "Draft";
591
+ DEFAULT_MANAGER_MEMORY_PATH = ".night-watch/manager/memory.md";
592
+ DEFAULT_MANAGER_DOCS_DIR = ".night-watch/manager/docs";
593
+ DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED = true;
594
+ DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY = 1;
595
+ DEFAULT_MANAGER = {
596
+ enabled: DEFAULT_MANAGER_ENABLED,
597
+ schedule: DEFAULT_MANAGER_SCHEDULE,
598
+ maxRuntime: DEFAULT_MANAGER_MAX_RUNTIME,
599
+ authority: DEFAULT_MANAGER_AUTHORITY,
600
+ outputMode: DEFAULT_MANAGER_OUTPUT_MODE,
601
+ targetColumn: DEFAULT_MANAGER_TARGET_COLUMN,
602
+ memoryPath: DEFAULT_MANAGER_MEMORY_PATH,
603
+ docsDir: DEFAULT_MANAGER_DOCS_DIR,
604
+ weeklySummaryEnabled: DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED,
605
+ weeklySummaryDay: DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY
606
+ };
537
607
  DEFAULT_PR_RESOLVER_ENABLED = true;
538
608
  DEFAULT_PR_RESOLVER_SCHEDULE = "15 6,14,22 * * *";
539
609
  DEFAULT_PR_RESOLVER_MAX_RUNTIME = 0;
@@ -579,6 +649,7 @@ If no issues are warranted, output an empty array: []`;
579
649
  PLANNER_LOG_NAME = "slicer";
580
650
  ANALYTICS_LOG_NAME = "analytics";
581
651
  PR_RESOLVER_LOG_NAME = "pr-resolver";
652
+ MANAGER_LOG_NAME = "manager";
582
653
  VALID_PROVIDERS = ["claude", "codex"];
583
654
  VALID_JOB_TYPES = getValidJobTypes();
584
655
  DEFAULT_JOB_PROVIDERS = {};
@@ -903,7 +974,7 @@ function normalizeConfig(rawConfig) {
903
974
  if (mergeMethod && VALID_MERGE_METHODS.includes(mergeMethod)) {
904
975
  normalized.autoMergeMethod = mergeMethod;
905
976
  }
906
- for (const jobId of ["qa", "audit", "analytics", "merger"]) {
977
+ for (const jobId of ["qa", "audit", "analytics", "merger", "manager"]) {
907
978
  const jobDef = getJobDef(jobId);
908
979
  if (!jobDef)
909
980
  continue;
@@ -1254,7 +1325,7 @@ function buildEnvOverrideConfig(fileConfig) {
1254
1325
  env.claudeModel = model;
1255
1326
  }
1256
1327
  }
1257
- for (const jobId of ["qa", "audit", "analytics"]) {
1328
+ for (const jobId of ["qa", "audit", "analytics", "manager"]) {
1258
1329
  const jobDef = getJobDef(jobId);
1259
1330
  if (!jobDef)
1260
1331
  continue;
@@ -1381,6 +1452,7 @@ function getDefaultConfig() {
1381
1452
  qa: { ...DEFAULT_QA },
1382
1453
  audit: { ...DEFAULT_AUDIT },
1383
1454
  analytics: { ...DEFAULT_ANALYTICS },
1455
+ manager: { ...DEFAULT_MANAGER },
1384
1456
  feedback: { ...DEFAULT_FEEDBACK },
1385
1457
  prResolver: { ...DEFAULT_PR_RESOLVER },
1386
1458
  merger: { ...DEFAULT_MERGER },
@@ -1580,7 +1652,7 @@ function mergeConfigLayer(base, layer) {
1580
1652
  })
1581
1653
  }
1582
1654
  };
1583
- } else if (_key === "providerEnv" || _key === "boardProvider" || _key === "qa" || _key === "audit" || _key === "analytics" || _key === "feedback" || _key === "prResolver" || _key === "merger") {
1655
+ } else if (_key === "providerEnv" || _key === "boardProvider" || _key === "qa" || _key === "audit" || _key === "analytics" || _key === "manager" || _key === "feedback" || _key === "prResolver" || _key === "merger") {
1584
1656
  base[_key] = {
1585
1657
  ...base[_key],
1586
1658
  ...value
@@ -1621,6 +1693,10 @@ function mergeConfigs(base, fileConfig, envConfig) {
1621
1693
  maxActiveAugmentations: Math.max(0, Math.min(10, Math.floor(merged.feedback.maxActiveAugmentations))),
1622
1694
  successStreakToExpire: Math.max(0, Math.min(20, Math.floor(merged.feedback.successStreakToExpire)))
1623
1695
  };
1696
+ merged.manager = {
1697
+ ...merged.manager,
1698
+ weeklySummaryDay: Math.max(0, Math.min(6, Math.floor(merged.manager.weeklySummaryDay)))
1699
+ };
1624
1700
  if (merged.secondaryFallbackModel === void 0) {
1625
1701
  merged.secondaryFallbackModel = merged.primaryFallbackModel === void 0 ? DEFAULT_SECONDARY_FALLBACK_MODEL : merged.primaryFallbackModel;
1626
1702
  }
@@ -4431,7 +4507,8 @@ var init_crontab = __esm({
4431
4507
  "night-watch-qa-cron.sh",
4432
4508
  "night-watch-audit-cron.sh",
4433
4509
  "night-watch-slice-cron.sh",
4434
- "night-watch-slicer-cron.sh"
4510
+ "night-watch-slicer-cron.sh",
4511
+ "night-watch-manager-cron.sh"
4435
4512
  ];
4436
4513
  }
4437
4514
  });
@@ -4484,6 +4561,9 @@ function prResolverLockPath(projectDir) {
4484
4561
  function mergerLockPath(projectDir) {
4485
4562
  return `${LOCK_FILE_PREFIX}merger-${projectRuntimeKey(projectDir)}.lock`;
4486
4563
  }
4564
+ function managerLockPath(projectDir) {
4565
+ return `${LOCK_FILE_PREFIX}manager-${projectRuntimeKey(projectDir)}.lock`;
4566
+ }
4487
4567
  function isProcessRunning(pid) {
4488
4568
  if (pid <= 0)
4489
4569
  return false;
@@ -4874,7 +4954,8 @@ function collectLogInfo(projectDir) {
4874
4954
  { name: "planner", fileName: `${PLANNER_LOG_NAME}.log` },
4875
4955
  { name: "analytics", fileName: `${ANALYTICS_LOG_NAME}.log` },
4876
4956
  { name: "pr-resolver", fileName: `${PR_RESOLVER_LOG_NAME}.log` },
4877
- { name: "merger", fileName: `${MERGER_LOG_NAME}.log` }
4957
+ { name: "merger", fileName: `${MERGER_LOG_NAME}.log` },
4958
+ { name: "manager", fileName: `${MANAGER_LOG_NAME}.log` }
4878
4959
  ];
4879
4960
  return logEntries.map(({ name, fileName }) => {
4880
4961
  const logPath = path6.join(projectDir, LOG_DIR, fileName);
@@ -4906,6 +4987,7 @@ async function fetchStatusSnapshot(projectDir, config) {
4906
4987
  const analyticsLock = checkLockFile(analyticsLockPath(projectDir));
4907
4988
  const prResolverLock = checkLockFile(prResolverLockPath(projectDir));
4908
4989
  const mergerLock = checkLockFile(mergerLockPath(projectDir));
4990
+ const managerLock = checkLockFile(managerLockPath(projectDir));
4909
4991
  const processes = [
4910
4992
  { name: "executor", running: executorLock.running, pid: executorLock.pid },
4911
4993
  { name: "reviewer", running: reviewerLock.running, pid: reviewerLock.pid },
@@ -4914,7 +4996,8 @@ async function fetchStatusSnapshot(projectDir, config) {
4914
4996
  { name: "planner", running: plannerLock.running, pid: plannerLock.pid },
4915
4997
  { name: "analytics", running: analyticsLock.running, pid: analyticsLock.pid },
4916
4998
  { name: "pr-resolver", running: prResolverLock.running, pid: prResolverLock.pid },
4917
- { name: "merger", running: mergerLock.running, pid: mergerLock.pid }
4999
+ { name: "merger", running: mergerLock.running, pid: mergerLock.pid },
5000
+ { name: "manager", running: managerLock.running, pid: managerLock.pid }
4918
5001
  ];
4919
5002
  const prds = collectPrdInfo(projectDir, config.prdDir, config.maxRuntime);
4920
5003
  const prs = await collectPrInfo(projectDir, config.branchPatterns);
@@ -4955,7 +5038,7 @@ function getLockFilePaths(projectDir) {
4955
5038
  };
4956
5039
  }
4957
5040
  function sleep(ms) {
4958
- return new Promise((resolve9) => setTimeout(resolve9, ms));
5041
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
4959
5042
  }
4960
5043
  async function cancelProcess(processType, lockPath, force = false) {
4961
5044
  const lockStatus = checkLockFile(lockPath);
@@ -5951,6 +6034,10 @@ function getEventEmoji(event) {
5951
6034
  return "\u{1F500}";
5952
6035
  case "merge_failed":
5953
6036
  return "\u274C";
6037
+ case "manager_blocked":
6038
+ return "\u26A0\uFE0F";
6039
+ case "manager_weekly_summary":
6040
+ return "\u{1F4CA}";
5954
6041
  }
5955
6042
  }
5956
6043
  function getEventTitle(event) {
@@ -5985,6 +6072,10 @@ function getEventTitle(event) {
5985
6072
  return "PR Merged";
5986
6073
  case "merge_failed":
5987
6074
  return "Merge Failed";
6075
+ case "manager_blocked":
6076
+ return "Manager Needs Human Input";
6077
+ case "manager_weekly_summary":
6078
+ return "Manager Weekly Summary";
5988
6079
  }
5989
6080
  }
5990
6081
  function getEventColor(event) {
@@ -6019,6 +6110,10 @@ function getEventColor(event) {
6019
6110
  return 10181046;
6020
6111
  case "merge_failed":
6021
6112
  return 16711680;
6113
+ case "manager_blocked":
6114
+ return 16753920;
6115
+ case "manager_weekly_summary":
6116
+ return 3447003;
6022
6117
  }
6023
6118
  }
6024
6119
  function buildDescription(ctx) {
@@ -7273,7 +7368,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
7273
7368
  const logStream = fs18.createWriteStream(logFile, { flags: "w" });
7274
7369
  logStream.on("error", () => {
7275
7370
  });
7276
- return new Promise((resolve9) => {
7371
+ return new Promise((resolve11) => {
7277
7372
  const childEnv = {
7278
7373
  ...process.env,
7279
7374
  ...config.providerEnv,
@@ -7292,7 +7387,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
7292
7387
  });
7293
7388
  child.on("error", (error2) => {
7294
7389
  logStream.end();
7295
- resolve9({
7390
+ resolve11({
7296
7391
  sliced: false,
7297
7392
  error: `Failed to spawn provider: ${error2.message}`,
7298
7393
  item
@@ -7301,7 +7396,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
7301
7396
  child.on("close", (code) => {
7302
7397
  logStream.end();
7303
7398
  if (code !== 0) {
7304
- resolve9({
7399
+ resolve11({
7305
7400
  sliced: false,
7306
7401
  error: `Provider exited with code ${code ?? 1}`,
7307
7402
  item
@@ -7309,14 +7404,14 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
7309
7404
  return;
7310
7405
  }
7311
7406
  if (!fs18.existsSync(filePath)) {
7312
- resolve9({
7407
+ resolve11({
7313
7408
  sliced: false,
7314
7409
  error: `Provider did not create expected file: ${filePath}`,
7315
7410
  item
7316
7411
  });
7317
7412
  return;
7318
7413
  }
7319
- resolve9({
7414
+ resolve11({
7320
7415
  sliced: true,
7321
7416
  file: filename,
7322
7417
  item
@@ -7478,7 +7573,7 @@ async function executeScript(scriptPath, args = [], env = {}, options = {}) {
7478
7573
  return result.exitCode;
7479
7574
  }
7480
7575
  async function executeScriptWithOutput(scriptPath, args = [], env = {}, options = {}) {
7481
- return new Promise((resolve9, reject) => {
7576
+ return new Promise((resolve11, reject) => {
7482
7577
  const childEnv = {
7483
7578
  ...process.env,
7484
7579
  ...env
@@ -7504,7 +7599,7 @@ async function executeScriptWithOutput(scriptPath, args = [], env = {}, options
7504
7599
  reject(error2);
7505
7600
  });
7506
7601
  child.on("close", (code) => {
7507
- resolve9({
7602
+ resolve11({
7508
7603
  exitCode: code ?? 1,
7509
7604
  stdout: stdoutChunks.join(""),
7510
7605
  stderr: stderrChunks.join("")
@@ -7541,6 +7636,8 @@ function isJobTypeEnabled(config, jobType) {
7541
7636
  return config.roadmapScanner.enabled;
7542
7637
  case "analytics":
7543
7638
  return config.analytics.enabled;
7639
+ case "manager":
7640
+ return config.manager.enabled;
7544
7641
  default:
7545
7642
  return true;
7546
7643
  }
@@ -7549,6 +7646,8 @@ function getJobSchedule(config, jobType) {
7549
7646
  switch (jobType) {
7550
7647
  case "reviewer":
7551
7648
  return config.reviewerSchedule ?? "";
7649
+ case "manager":
7650
+ return config.manager.schedule ?? "";
7552
7651
  case "executor":
7553
7652
  default:
7554
7653
  return config.cronSchedule ?? "";
@@ -7909,6 +8008,8 @@ function getLockPathForJob(projectPath, jobType) {
7909
8008
  return prResolverLockPath(projectPath);
7910
8009
  case "merger":
7911
8010
  return mergerLockPath(projectPath);
8011
+ case "manager":
8012
+ return managerLockPath(projectPath);
7912
8013
  }
7913
8014
  }
7914
8015
  function isRunningEntryStale(entry) {
@@ -8929,6 +9030,562 @@ var init_audit = __esm({
8929
9030
  }
8930
9031
  });
8931
9032
 
9033
+ // ../core/dist/manager/manager-memory.js
9034
+ import * as fs23 from "fs";
9035
+ import * as path22 from "path";
9036
+ import { createHash as createHash4 } from "crypto";
9037
+ function createFindingFingerprint(parts) {
9038
+ const normalized = parts.map((part) => part.toLowerCase().trim().replace(/\s+/g, " ")).join("|");
9039
+ return createHash4("sha256").update(normalized).digest("hex").slice(0, 16);
9040
+ }
9041
+ function loadManagerMemory(memoryPath) {
9042
+ if (!fs23.existsSync(memoryPath)) {
9043
+ return { fingerprints: /* @__PURE__ */ new Set(), lastWeeklySummaryAt: null, raw: "" };
9044
+ }
9045
+ const raw = fs23.readFileSync(memoryPath, "utf-8");
9046
+ const fingerprints = /* @__PURE__ */ new Set();
9047
+ let match;
9048
+ while ((match = FINGERPRINT_PATTERN.exec(raw)) !== null) {
9049
+ fingerprints.add(match[1]);
9050
+ }
9051
+ const weeklyMatch = raw.match(WEEKLY_SUMMARY_PATTERN);
9052
+ const lastWeeklySummaryAt = weeklyMatch ? parseDate(weeklyMatch[1].trim()) : null;
9053
+ return { fingerprints, lastWeeklySummaryAt, raw };
9054
+ }
9055
+ function isKnownFinding(memory, fingerprint) {
9056
+ return memory.fingerprints.has(fingerprint);
9057
+ }
9058
+ function renderManagerMemory(result, previous) {
9059
+ const lines = [
9060
+ "# Night Watch Manager Memory",
9061
+ "",
9062
+ `Last run: ${(/* @__PURE__ */ new Date()).toISOString()}`,
9063
+ `Last weekly summary: ${getLastWeeklySummary(result, previous)}`,
9064
+ "",
9065
+ "## Latest Run",
9066
+ "",
9067
+ `- Findings: ${result.findings.length}`,
9068
+ `- Proposed drafts: ${result.proposedDrafts.length}`,
9069
+ `- Created drafts: ${result.createdDrafts.length}`,
9070
+ `- Skipped duplicates: ${result.skippedFindings.length}`,
9071
+ "",
9072
+ "## Findings",
9073
+ ""
9074
+ ];
9075
+ for (const finding of result.findings) {
9076
+ lines.push(...renderFinding(finding));
9077
+ }
9078
+ lines.push("## Created Drafts", "");
9079
+ for (const draft of result.createdDrafts) {
9080
+ lines.push(`- ${draft.title} (#${draft.issue.number}) - fingerprint: \`${draft.fingerprint}\``);
9081
+ }
9082
+ if (result.createdDrafts.length === 0) {
9083
+ lines.push("- None");
9084
+ }
9085
+ lines.push("", "## Skipped Duplicates", "");
9086
+ for (const skipped of result.skippedFindings) {
9087
+ lines.push(`- ${skipped.title} (${skipped.reason}) - fingerprint: \`${skipped.fingerprint}\``);
9088
+ }
9089
+ if (result.skippedFindings.length === 0) {
9090
+ lines.push("- None");
9091
+ }
9092
+ const previousFingerprints = [...previous.fingerprints].filter((fingerprint) => !result.findings.some((finding) => finding.fingerprint === fingerprint));
9093
+ if (previousFingerprints.length > 0) {
9094
+ lines.push("", "## Previous Fingerprints", "");
9095
+ for (const fingerprint of previousFingerprints) {
9096
+ lines.push(`- fingerprint: \`${fingerprint}\``);
9097
+ }
9098
+ }
9099
+ return `${lines.join("\n")}
9100
+ `;
9101
+ }
9102
+ function writeManagerMemory(memoryPath, result, previous) {
9103
+ fs23.mkdirSync(path22.dirname(memoryPath), { recursive: true });
9104
+ fs23.writeFileSync(memoryPath, renderManagerMemory(result, previous), "utf-8");
9105
+ }
9106
+ function renderFinding(finding) {
9107
+ return [
9108
+ `### ${finding.title}`,
9109
+ "",
9110
+ `- kind: ${finding.kind}`,
9111
+ `- severity: ${finding.severity}`,
9112
+ `- source: ${finding.source}`,
9113
+ `- fingerprint: \`${finding.fingerprint}\``,
9114
+ "",
9115
+ finding.body,
9116
+ ""
9117
+ ];
9118
+ }
9119
+ function getLastWeeklySummary(result, previous) {
9120
+ const weekly = result.notificationDecisions.find((decision) => decision.event === "manager_weekly_summary" && decision.shouldNotify);
9121
+ if (weekly) {
9122
+ return (/* @__PURE__ */ new Date()).toISOString();
9123
+ }
9124
+ return previous.lastWeeklySummaryAt?.toISOString() ?? "never";
9125
+ }
9126
+ function parseDate(value) {
9127
+ if (value === "never")
9128
+ return null;
9129
+ const date = new Date(value);
9130
+ return Number.isNaN(date.getTime()) ? null : date;
9131
+ }
9132
+ function summarizeCreatedDrafts(drafts) {
9133
+ if (drafts.length === 0)
9134
+ return "No board drafts created.";
9135
+ return `${drafts.length} board draft${drafts.length === 1 ? "" : "s"} created.`;
9136
+ }
9137
+ function summarizeSkippedFindings(skipped) {
9138
+ if (skipped.length === 0)
9139
+ return "No duplicate findings skipped.";
9140
+ return `${skipped.length} duplicate finding${skipped.length === 1 ? "" : "s"} skipped.`;
9141
+ }
9142
+ var FINGERPRINT_PATTERN, WEEKLY_SUMMARY_PATTERN;
9143
+ var init_manager_memory = __esm({
9144
+ "../core/dist/manager/manager-memory.js"() {
9145
+ "use strict";
9146
+ FINGERPRINT_PATTERN = /fingerprint:\s*`([^`]+)`/g;
9147
+ WEEKLY_SUMMARY_PATTERN = /Last weekly summary:\s*([^\n]+)/;
9148
+ }
9149
+ });
9150
+
9151
+ // ../core/dist/manager/manager-analysis.js
9152
+ import * as fs24 from "fs";
9153
+ import * as path23 from "path";
9154
+ function analyzeManagerInputs(context) {
9155
+ const roadmapPath = path23.resolve(context.projectDir, context.config.roadmapScanner?.roadmapPath || "ROADMAP.md");
9156
+ const roadmapContent = fs24.existsSync(roadmapPath) ? fs24.readFileSync(roadmapPath, "utf-8") : "";
9157
+ const roadmapItems = roadmapContent ? parseRoadmap(roadmapContent) : [];
9158
+ const prds = collectPrdFiles(path23.resolve(context.projectDir, context.config.prdDir || "docs/prds"));
9159
+ const findings = [];
9160
+ const searchableWork = buildSearchableWork(context.boardIssues.map((issue) => issue.title), prds);
9161
+ for (const item of roadmapItems.filter((roadmapItem) => !roadmapItem.checked)) {
9162
+ if (searchableWork.has(slugify(item.title))) {
9163
+ continue;
9164
+ }
9165
+ const fingerprint = createFindingFingerprint(["roadmap_gap", item.hash, item.title]);
9166
+ findings.push({
9167
+ kind: "roadmap_gap",
9168
+ severity: "warning",
9169
+ title: `Roadmap item needs an owner: ${item.title}`,
9170
+ body: item.description || `The roadmap item "${item.title}" is still unchecked and does not appear to have a matching board issue or PRD.`,
9171
+ fingerprint,
9172
+ requiresHuman: false,
9173
+ source: `roadmap:${item.section}`,
9174
+ labels: ["manager", "roadmap"]
9175
+ });
9176
+ }
9177
+ for (const prd of context.statusSnapshot?.prds ?? []) {
9178
+ if (prd.status !== "blocked")
9179
+ continue;
9180
+ const fingerprint = createFindingFingerprint(["blocked_prd", prd.name, ...prd.unmetDependencies]);
9181
+ findings.push({
9182
+ kind: "blocked_prd",
9183
+ severity: "blocker",
9184
+ title: `Blocked PRD needs human triage: ${prd.name}`,
9185
+ body: `PRD "${prd.name}" is blocked by unmet dependencies: ${prd.unmetDependencies.join(", ") || "unknown"}.`,
9186
+ fingerprint,
9187
+ requiresHuman: true,
9188
+ source: "status:prds",
9189
+ labels: ["manager", "blocked"]
9190
+ });
9191
+ }
9192
+ const oldestPendingAge = context.queueStatus?.oldestPendingAge;
9193
+ if (oldestPendingAge !== null && oldestPendingAge !== void 0 && oldestPendingAge > 6 * 60 * 60) {
9194
+ const fingerprint = createFindingFingerprint(["stale_queue", String(Math.floor(oldestPendingAge / 3600))]);
9195
+ findings.push({
9196
+ kind: "stale_queue",
9197
+ severity: "blocker",
9198
+ title: "Queue has stale pending work",
9199
+ body: `The oldest pending queue item has waited ${oldestPendingAge} seconds. This may need human capacity or credentials.`,
9200
+ fingerprint,
9201
+ requiresHuman: true,
9202
+ source: "queue",
9203
+ labels: ["manager", "queue", "blocked"]
9204
+ });
9205
+ }
9206
+ if (!fs24.existsSync(path23.join(context.managerConfig.docsDirectory, "overview.md"))) {
9207
+ const fingerprint = createFindingFingerprint(["missing_manager_doc", context.managerConfig.docsDirectory]);
9208
+ findings.push({
9209
+ kind: "missing_manager_doc",
9210
+ severity: "info",
9211
+ title: "Manager overview document is missing",
9212
+ body: "The Manager has not written its generated overview document yet.",
9213
+ fingerprint,
9214
+ requiresHuman: false,
9215
+ source: "manager-docs",
9216
+ labels: ["manager", "docs"]
9217
+ });
9218
+ }
9219
+ return { findings, roadmapItems: roadmapItems.length, prds };
9220
+ }
9221
+ function collectPrdFiles(prdDir) {
9222
+ if (!fs24.existsSync(prdDir))
9223
+ return [];
9224
+ const files = [];
9225
+ const visit = (dir) => {
9226
+ for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
9227
+ const fullPath = path23.join(dir, entry.name);
9228
+ if (entry.isDirectory()) {
9229
+ visit(fullPath);
9230
+ } else if (entry.isFile() && entry.name.endsWith(".md")) {
9231
+ files.push({
9232
+ name: entry.name.replace(/\.md$/, ""),
9233
+ path: fullPath,
9234
+ content: fs24.readFileSync(fullPath, "utf-8")
9235
+ });
9236
+ }
9237
+ }
9238
+ };
9239
+ visit(prdDir);
9240
+ return files;
9241
+ }
9242
+ function buildSearchableWork(boardTitles, prds) {
9243
+ const values = /* @__PURE__ */ new Set();
9244
+ for (const title of boardTitles) {
9245
+ values.add(slugify(stripManagerPrefix(title)));
9246
+ }
9247
+ for (const prd of prds) {
9248
+ values.add(slugify(prd.name));
9249
+ const heading = prd.content.split("\n").find((line) => line.startsWith("# "))?.slice(2).trim();
9250
+ if (heading)
9251
+ values.add(slugify(heading));
9252
+ }
9253
+ return values;
9254
+ }
9255
+ function stripManagerPrefix(title) {
9256
+ let normalized = title.trim();
9257
+ if (normalized.toLowerCase().startsWith("[manager] ")) {
9258
+ normalized = normalized.slice("[manager] ".length).trim();
9259
+ }
9260
+ const roadmapPrefix = "roadmap item needs an owner: ";
9261
+ if (normalized.toLowerCase().startsWith(roadmapPrefix)) {
9262
+ normalized = normalized.slice(roadmapPrefix.length).trim();
9263
+ }
9264
+ return normalized;
9265
+ }
9266
+ var init_manager_analysis = __esm({
9267
+ "../core/dist/manager/manager-analysis.js"() {
9268
+ "use strict";
9269
+ init_roadmap_parser();
9270
+ init_prd_utils();
9271
+ init_manager_memory();
9272
+ }
9273
+ });
9274
+
9275
+ // ../core/dist/manager/manager-prompts.js
9276
+ function buildManagerDraftTitle(finding) {
9277
+ return `[Manager] ${finding.title}`;
9278
+ }
9279
+ function buildManagerDraftBody(finding) {
9280
+ return [
9281
+ "# PRD: Manager Draft",
9282
+ "",
9283
+ "## 1. Context",
9284
+ "",
9285
+ finding.body,
9286
+ "",
9287
+ `Source: ${finding.source}`,
9288
+ `Manager fingerprint: \`${finding.fingerprint}\``,
9289
+ "",
9290
+ "## 2. Proposed Outcome",
9291
+ "",
9292
+ "Turn this finding into a reviewed, executable PRD or close it with a short rationale.",
9293
+ "",
9294
+ "## 3. Acceptance Criteria",
9295
+ "",
9296
+ "- [ ] Confirm the finding is still relevant.",
9297
+ "- [ ] Define the implementation scope and ownership.",
9298
+ "- [ ] Add or update tests appropriate for the selected implementation.",
9299
+ "- [ ] Close this draft when the work is represented by an approved PRD or issue.",
9300
+ "",
9301
+ "## 4. Manager Notes",
9302
+ "",
9303
+ `- Kind: ${finding.kind}`,
9304
+ `- Severity: ${finding.severity}`,
9305
+ `- Requires human input: ${finding.requiresHuman ? "yes" : "no"}`
9306
+ ].join("\n");
9307
+ }
9308
+ var init_manager_prompts = __esm({
9309
+ "../core/dist/manager/manager-prompts.js"() {
9310
+ "use strict";
9311
+ }
9312
+ });
9313
+
9314
+ // ../core/dist/manager/manager-board.js
9315
+ function prepareManagerDrafts(input) {
9316
+ const boardTitles = new Set(input.boardIssues.map((issue) => normalizeTitle(issue.title)));
9317
+ const drafts = [];
9318
+ const skipped = [];
9319
+ for (const finding of input.findings) {
9320
+ const title = buildManagerDraftTitle(finding);
9321
+ if (isKnownFinding(input.memory, finding.fingerprint)) {
9322
+ skipped.push({ fingerprint: finding.fingerprint, title, reason: "memory" });
9323
+ continue;
9324
+ }
9325
+ if (boardTitles.has(normalizeTitle(title)) || boardTitles.has(normalizeTitle(finding.title))) {
9326
+ skipped.push({ fingerprint: finding.fingerprint, title, reason: "board" });
9327
+ continue;
9328
+ }
9329
+ drafts.push({
9330
+ title,
9331
+ body: buildManagerDraftBody(finding),
9332
+ labels: finding.labels,
9333
+ column: input.managerConfig.targetColumn,
9334
+ fingerprint: finding.fingerprint
9335
+ });
9336
+ }
9337
+ return { drafts, skipped };
9338
+ }
9339
+ async function createManagerBoardDrafts(input) {
9340
+ if (input.dryRun || input.outputMode !== "board-draft" || !input.provider) {
9341
+ return [];
9342
+ }
9343
+ const created = [];
9344
+ for (const draft of input.drafts) {
9345
+ const issue = await input.provider.createIssue({
9346
+ title: draft.title,
9347
+ body: draft.body,
9348
+ column: draft.column,
9349
+ labels: draft.labels
9350
+ });
9351
+ created.push({ ...draft, issue });
9352
+ }
9353
+ return created;
9354
+ }
9355
+ function normalizeTitle(title) {
9356
+ return title.toLowerCase().replace(/^\[manager\]\s*/i, "").replace(/\s+/g, " ").trim();
9357
+ }
9358
+ var init_manager_board = __esm({
9359
+ "../core/dist/manager/manager-board.js"() {
9360
+ "use strict";
9361
+ init_manager_prompts();
9362
+ init_manager_memory();
9363
+ }
9364
+ });
9365
+
9366
+ // ../core/dist/manager/manager-notifications.js
9367
+ function prepareManagerNotificationDecisions(input) {
9368
+ const blockers = input.findings.filter((finding) => finding.requiresHuman || finding.severity === "blocker");
9369
+ const decisions = [
9370
+ {
9371
+ event: "manager_blocked",
9372
+ shouldNotify: blockers.length > 0,
9373
+ title: blockers.length === 1 ? "Manager found 1 blocker" : `Manager found ${blockers.length} blockers`,
9374
+ body: blockers.map((finding) => `- ${finding.title}`).join("\n") || "No blockers found.",
9375
+ findings: blockers
9376
+ }
9377
+ ];
9378
+ const weeklyDue = isWeeklySummaryDue(input.managerConfig.weeklySummaryEnabled, input.managerConfig.weeklySummaryDay, input.memory.lastWeeklySummaryAt, input.now);
9379
+ decisions.push({
9380
+ event: "manager_weekly_summary",
9381
+ shouldNotify: weeklyDue,
9382
+ title: "Manager weekly summary",
9383
+ body: [
9384
+ `Findings: ${input.findings.length}`,
9385
+ `Blockers: ${blockers.length}`,
9386
+ `Generated at: ${input.now.toISOString()}`
9387
+ ].join("\n"),
9388
+ findings: input.findings
9389
+ });
9390
+ return decisions;
9391
+ }
9392
+ function isWeeklySummaryDue(enabled, configuredDay, lastWeeklySummaryAt, now) {
9393
+ if (!enabled || now.getDay() !== configuredDay) {
9394
+ return false;
9395
+ }
9396
+ if (!lastWeeklySummaryAt) {
9397
+ return true;
9398
+ }
9399
+ const elapsedMs = now.getTime() - lastWeeklySummaryAt.getTime();
9400
+ return elapsedMs >= 6.5 * 24 * 60 * 60 * 1e3;
9401
+ }
9402
+ var init_manager_notifications = __esm({
9403
+ "../core/dist/manager/manager-notifications.js"() {
9404
+ "use strict";
9405
+ }
9406
+ });
9407
+
9408
+ // ../core/dist/manager/manager-runner.js
9409
+ import * as fs25 from "fs";
9410
+ import * as path24 from "path";
9411
+ async function runManager(projectDir, config, options = {}) {
9412
+ const dryRun = options.dryRun ?? false;
9413
+ const now = options.now ?? /* @__PURE__ */ new Date();
9414
+ const managerConfig = resolveManagerConfig(config, projectDir);
9415
+ const memory = loadManagerMemory(managerConfig.memoryFile);
9416
+ const boardProvider = await resolveBoardProvider(config, projectDir, options.boardProvider);
9417
+ const boardIssues = await readBoardIssues(boardProvider);
9418
+ const queueStatus = resolveQueueStatus(options.queueStatus);
9419
+ const statusSnapshot = await resolveStatusSnapshot(projectDir, config, options.statusSnapshot);
9420
+ const context = {
9421
+ projectDir,
9422
+ config,
9423
+ managerConfig,
9424
+ dryRun,
9425
+ now,
9426
+ boardIssues,
9427
+ statusSnapshot,
9428
+ queueStatus
9429
+ };
9430
+ const analysis = analyzeManagerInputs(context);
9431
+ const { drafts, skipped } = prepareManagerDrafts({
9432
+ findings: analysis.findings,
9433
+ memory,
9434
+ boardIssues,
9435
+ managerConfig
9436
+ });
9437
+ const createdDrafts = await createManagerBoardDrafts({
9438
+ provider: boardProvider,
9439
+ drafts,
9440
+ dryRun,
9441
+ outputMode: managerConfig.outputMode
9442
+ });
9443
+ const notificationDecisions = prepareManagerNotificationDecisions({
9444
+ findings: analysis.findings,
9445
+ memory,
9446
+ managerConfig,
9447
+ now
9448
+ });
9449
+ const result = {
9450
+ dryRun,
9451
+ projectDir,
9452
+ config: managerConfig,
9453
+ analyzed: {
9454
+ roadmapItems: analysis.roadmapItems,
9455
+ boardIssues: boardIssues.length,
9456
+ prds: analysis.prds.length
9457
+ },
9458
+ findings: analysis.findings,
9459
+ proposedDrafts: drafts,
9460
+ createdDrafts,
9461
+ skippedFindings: skipped,
9462
+ docsWritten: [],
9463
+ memoryWritten: false,
9464
+ notificationDecisions,
9465
+ summary: ""
9466
+ };
9467
+ if (!dryRun) {
9468
+ result.docsWritten = writeManagerDocs(managerConfig.docsDirectory, result, now);
9469
+ writeManagerMemory(managerConfig.memoryFile, result, memory);
9470
+ result.memoryWritten = true;
9471
+ }
9472
+ result.summary = [
9473
+ `${analysis.findings.length} finding${analysis.findings.length === 1 ? "" : "s"} found.`,
9474
+ summarizeCreatedDrafts(createdDrafts),
9475
+ summarizeSkippedFindings(skipped)
9476
+ ].join(" ");
9477
+ return result;
9478
+ }
9479
+ function resolveManagerConfig(config, projectDir) {
9480
+ const raw = config.manager ?? {};
9481
+ const merged = {
9482
+ ...DEFAULT_MANAGER,
9483
+ ...raw,
9484
+ authority: raw.authority === "draft" || raw.authority === "ready" || raw.authority === "workflow" ? raw.authority : DEFAULT_MANAGER.authority,
9485
+ outputMode: raw.outputMode === "board-draft" || raw.outputMode === "filesystem-prd" || raw.outputMode === "report-only" ? raw.outputMode : DEFAULT_MANAGER.outputMode,
9486
+ targetColumn: raw.targetColumn ?? DEFAULT_MANAGER.targetColumn,
9487
+ weeklySummaryDay: typeof raw.weeklySummaryDay === "number" && raw.weeklySummaryDay >= 0 && raw.weeklySummaryDay <= 6 ? raw.weeklySummaryDay : DEFAULT_MANAGER.weeklySummaryDay
9488
+ };
9489
+ const weeklySummaryDay = Math.floor(merged.weeklySummaryDay);
9490
+ return {
9491
+ ...merged,
9492
+ weeklySummaryDay,
9493
+ memoryFile: path24.resolve(projectDir, merged.memoryPath),
9494
+ docsDirectory: path24.resolve(projectDir, merged.docsDir)
9495
+ };
9496
+ }
9497
+ async function resolveBoardProvider(config, projectDir, injected) {
9498
+ if (injected !== void 0) {
9499
+ return injected;
9500
+ }
9501
+ if (!config.boardProvider?.enabled) {
9502
+ return null;
9503
+ }
9504
+ return createBoardProvider(config.boardProvider, projectDir);
9505
+ }
9506
+ async function readBoardIssues(provider) {
9507
+ if (!provider)
9508
+ return [];
9509
+ try {
9510
+ return await provider.getAllIssues();
9511
+ } catch {
9512
+ return [];
9513
+ }
9514
+ }
9515
+ function resolveQueueStatus(injected) {
9516
+ if (injected !== void 0) {
9517
+ return injected;
9518
+ }
9519
+ try {
9520
+ return getQueueStatus();
9521
+ } catch {
9522
+ return null;
9523
+ }
9524
+ }
9525
+ async function resolveStatusSnapshot(projectDir, config, injected) {
9526
+ if (injected !== void 0) {
9527
+ return injected;
9528
+ }
9529
+ try {
9530
+ return await fetchStatusSnapshot(projectDir, config);
9531
+ } catch {
9532
+ return null;
9533
+ }
9534
+ }
9535
+ function writeManagerDocs(docsDir, result, now) {
9536
+ const overviewPath = path24.resolve(docsDir, "overview.md");
9537
+ const relative3 = path24.relative(docsDir, overviewPath);
9538
+ if (relative3.startsWith("..") || path24.isAbsolute(relative3)) {
9539
+ throw new Error(`Refusing to write Manager docs outside docsDir: ${overviewPath}`);
9540
+ }
9541
+ fs25.mkdirSync(docsDir, { recursive: true });
9542
+ fs25.writeFileSync(overviewPath, [
9543
+ "# Manager Overview",
9544
+ "",
9545
+ `Generated: ${now.toISOString()}`,
9546
+ "",
9547
+ `Findings: ${result.findings.length}`,
9548
+ `Proposed drafts: ${result.proposedDrafts.length}`,
9549
+ `Created drafts: ${result.createdDrafts.length}`,
9550
+ ""
9551
+ ].join("\n"), "utf-8");
9552
+ return [overviewPath];
9553
+ }
9554
+ var init_manager_runner = __esm({
9555
+ "../core/dist/manager/manager-runner.js"() {
9556
+ "use strict";
9557
+ init_factory();
9558
+ init_constants();
9559
+ init_job_queue();
9560
+ init_status_data();
9561
+ init_manager_analysis();
9562
+ init_manager_board();
9563
+ init_manager_memory();
9564
+ init_manager_notifications();
9565
+ }
9566
+ });
9567
+
9568
+ // ../core/dist/manager/manager-types.js
9569
+ var init_manager_types = __esm({
9570
+ "../core/dist/manager/manager-types.js"() {
9571
+ "use strict";
9572
+ }
9573
+ });
9574
+
9575
+ // ../core/dist/manager/index.js
9576
+ var init_manager = __esm({
9577
+ "../core/dist/manager/index.js"() {
9578
+ "use strict";
9579
+ init_manager_analysis();
9580
+ init_manager_board();
9581
+ init_manager_memory();
9582
+ init_manager_notifications();
9583
+ init_manager_prompts();
9584
+ init_manager_runner();
9585
+ init_manager_types();
9586
+ }
9587
+ });
9588
+
8932
9589
  // ../core/dist/feedback/outcome-parser.js
8933
9590
  function stripAnsi2(value) {
8934
9591
  return value.replace(ANSI_PATTERN, "");
@@ -9791,6 +10448,17 @@ __export(dist_exports, {
9791
10448
  DEFAULT_FEEDBACK: () => DEFAULT_FEEDBACK,
9792
10449
  DEFAULT_JOB_PROVIDERS: () => DEFAULT_JOB_PROVIDERS,
9793
10450
  DEFAULT_LOCAL_BOARD_INFO: () => DEFAULT_LOCAL_BOARD_INFO,
10451
+ DEFAULT_MANAGER: () => DEFAULT_MANAGER,
10452
+ DEFAULT_MANAGER_AUTHORITY: () => DEFAULT_MANAGER_AUTHORITY,
10453
+ DEFAULT_MANAGER_DOCS_DIR: () => DEFAULT_MANAGER_DOCS_DIR,
10454
+ DEFAULT_MANAGER_ENABLED: () => DEFAULT_MANAGER_ENABLED,
10455
+ DEFAULT_MANAGER_MAX_RUNTIME: () => DEFAULT_MANAGER_MAX_RUNTIME,
10456
+ DEFAULT_MANAGER_MEMORY_PATH: () => DEFAULT_MANAGER_MEMORY_PATH,
10457
+ DEFAULT_MANAGER_OUTPUT_MODE: () => DEFAULT_MANAGER_OUTPUT_MODE,
10458
+ DEFAULT_MANAGER_SCHEDULE: () => DEFAULT_MANAGER_SCHEDULE,
10459
+ DEFAULT_MANAGER_TARGET_COLUMN: () => DEFAULT_MANAGER_TARGET_COLUMN,
10460
+ DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY: () => DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY,
10461
+ DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED: () => DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED,
9794
10462
  DEFAULT_MAX_LOG_SIZE: () => DEFAULT_MAX_LOG_SIZE,
9795
10463
  DEFAULT_MAX_RETRIES: () => DEFAULT_MAX_RETRIES,
9796
10464
  DEFAULT_MAX_RUNTIME: () => DEFAULT_MAX_RUNTIME,
@@ -9868,6 +10536,7 @@ __export(dist_exports, {
9868
10536
  LOG_FILE_NAMES: () => LOG_FILE_NAMES,
9869
10537
  LocalKanbanProvider: () => LocalKanbanProvider,
9870
10538
  Logger: () => Logger,
10539
+ MANAGER_LOG_NAME: () => MANAGER_LOG_NAME,
9871
10540
  MAX_HISTORY_RECORDS_PER_PRD: () => MAX_HISTORY_RECORDS_PER_PRD,
9872
10541
  MERGER_LOG_NAME: () => MERGER_LOG_NAME,
9873
10542
  NIGHT_WATCH_LABELS: () => NIGHT_WATCH_LABELS,
@@ -9896,9 +10565,12 @@ __export(dist_exports, {
9896
10565
  addEntry: () => addEntry,
9897
10566
  analyticsLockPath: () => analyticsLockPath,
9898
10567
  analyzeFeedbackOutcome: () => analyzeFeedbackOutcome,
10568
+ analyzeManagerInputs: () => analyzeManagerInputs,
9899
10569
  auditLockPath: () => auditLockPath,
9900
10570
  buildDescription: () => buildDescription,
9901
10571
  buildJobEnvOverrides: () => buildJobEnvOverrides,
10572
+ buildManagerDraftBody: () => buildManagerDraftBody,
10573
+ buildManagerDraftTitle: () => buildManagerDraftTitle,
9902
10574
  buildProjectFeedbackPromptBlock: () => buildProjectFeedbackPromptBlock,
9903
10575
  buildSessionOutcomeInput: () => buildSessionOutcomeInput,
9904
10576
  calculateStringSimilarity: () => calculateStringSimilarity,
@@ -9933,7 +10605,9 @@ __export(dist_exports, {
9933
10605
  createBoardProvider: () => createBoardProvider,
9934
10606
  createDbForDir: () => createDbForDir,
9935
10607
  createEmptyState: () => createEmptyState,
10608
+ createFindingFingerprint: () => createFindingFingerprint,
9936
10609
  createLogger: () => createLogger,
10610
+ createManagerBoardDrafts: () => createManagerBoardDrafts,
9937
10611
  createSlicerPromptVars: () => createSlicerPromptVars,
9938
10612
  createSpinner: () => createSpinner,
9939
10613
  createTable: () => createTable,
@@ -10027,20 +10701,24 @@ __export(dist_exports, {
10027
10701
  isInCooldown: () => isInCooldown,
10028
10702
  isItemProcessed: () => isItemProcessed,
10029
10703
  isJobTypeEnabled: () => isJobTypeEnabled,
10704
+ isKnownFinding: () => isKnownFinding,
10030
10705
  isProcessAliveSince: () => isProcessAliveSince,
10031
10706
  isProcessRunning: () => isProcessRunning,
10032
10707
  isValidCategory: () => isValidCategory,
10033
10708
  isValidHorizon: () => isValidHorizon,
10034
10709
  isValidPriority: () => isValidPriority,
10710
+ isWeeklySummaryDue: () => isWeeklySummaryDue,
10035
10711
  label: () => label,
10036
10712
  listPrdStatesByStatus: () => listPrdStatesByStatus,
10037
10713
  loadAuditFindings: () => loadAuditFindings,
10038
10714
  loadConfig: () => loadConfig,
10039
10715
  loadGlobalNotificationsConfig: () => loadGlobalNotificationsConfig,
10040
10716
  loadHistory: () => loadHistory,
10717
+ loadManagerMemory: () => loadManagerMemory,
10041
10718
  loadRegistry: () => loadRegistry,
10042
10719
  loadRoadmapState: () => loadRoadmapState,
10043
10720
  loadSlicerTemplate: () => loadSlicerTemplate,
10721
+ managerLockPath: () => managerLockPath,
10044
10722
  markItemProcessed: () => markItemProcessed,
10045
10723
  markJobRunning: () => markJobRunning,
10046
10724
  markPrdDone: () => markPrdDone,
@@ -10060,6 +10738,8 @@ __export(dist_exports, {
10060
10738
  prResolverLockPath: () => prResolverLockPath,
10061
10739
  prepareBranchWorktree: () => prepareBranchWorktree,
10062
10740
  prepareDetachedWorktree: () => prepareDetachedWorktree,
10741
+ prepareManagerDrafts: () => prepareManagerDrafts,
10742
+ prepareManagerNotificationDecisions: () => prepareManagerNotificationDecisions,
10063
10743
  projectRuntimeKey: () => projectRuntimeKey,
10064
10744
  pruneProjectData: () => pruneProjectData,
10065
10745
  qaLockPath: () => qaLockPath,
@@ -10074,11 +10754,13 @@ __export(dist_exports, {
10074
10754
  removeEntriesForProject: () => removeEntriesForProject,
10075
10755
  removeJob: () => removeJob,
10076
10756
  removeProject: () => removeProject,
10757
+ renderManagerMemory: () => renderManagerMemory,
10077
10758
  renderPrdTemplate: () => renderPrdTemplate,
10078
10759
  renderProjectFeedbackBlock: () => renderProjectFeedbackBlock,
10079
10760
  renderSlicerPrompt: () => renderSlicerPrompt,
10080
10761
  resetRepositories: () => resetRepositories,
10081
10762
  resolveJobProvider: () => resolveJobProvider,
10763
+ resolveManagerConfig: () => resolveManagerConfig,
10082
10764
  resolvePreset: () => resolvePreset,
10083
10765
  resolveProviderBucketKey: () => resolveProviderBucketKey,
10084
10766
  resolveWorktreeBaseRef: () => resolveWorktreeBaseRef,
@@ -10086,6 +10768,7 @@ __export(dist_exports, {
10086
10768
  rotateLog: () => rotateLog,
10087
10769
  runAllChecks: () => runAllChecks,
10088
10770
  runAnalytics: () => runAnalytics,
10771
+ runManager: () => runManager,
10089
10772
  runMigrations: () => runMigrations,
10090
10773
  saveConfig: () => saveConfig,
10091
10774
  saveGlobalNotificationsConfig: () => saveGlobalNotificationsConfig,
@@ -10104,6 +10787,8 @@ __export(dist_exports, {
10104
10787
  sortByPriority: () => sortByPriority,
10105
10788
  step: () => step,
10106
10789
  success: () => success,
10790
+ summarizeCreatedDrafts: () => summarizeCreatedDrafts,
10791
+ summarizeSkippedFindings: () => summarizeSkippedFindings,
10107
10792
  syncAuditFindingsToBoard: () => syncAuditFindingsToBoard,
10108
10793
  unmarkItemProcessed: () => unmarkItemProcessed,
10109
10794
  unregisterProject: () => unregisterProject,
@@ -10113,6 +10798,7 @@ __export(dist_exports, {
10113
10798
  validateWebhook: () => validateWebhook,
10114
10799
  warn: () => warn,
10115
10800
  writeCrontab: () => writeCrontab,
10801
+ writeManagerMemory: () => writeManagerMemory,
10116
10802
  writePrdState: () => writePrdState
10117
10803
  });
10118
10804
  var init_dist = __esm({
@@ -10163,6 +10849,7 @@ var init_dist = __esm({
10163
10849
  init_summary();
10164
10850
  init_analytics();
10165
10851
  init_audit();
10852
+ init_manager();
10166
10853
  init_outcome_parser();
10167
10854
  init_pattern_analyzer();
10168
10855
  init_prompt_augmenter();
@@ -10175,30 +10862,30 @@ var init_dist = __esm({
10175
10862
  // src/cli.ts
10176
10863
  import "reflect-metadata";
10177
10864
  import { Command as Command3 } from "commander";
10178
- import { existsSync as existsSync34, readFileSync as readFileSync21 } from "fs";
10865
+ import { existsSync as existsSync36, readFileSync as readFileSync23 } from "fs";
10179
10866
  import { fileURLToPath as fileURLToPath6 } from "url";
10180
- import { dirname as dirname12, join as join38 } from "path";
10867
+ import { dirname as dirname13, join as join39 } from "path";
10181
10868
 
10182
10869
  // src/commands/init.ts
10183
10870
  init_dist();
10184
- import fs23 from "fs";
10185
- import path22 from "path";
10871
+ import fs26 from "fs";
10872
+ import path25 from "path";
10186
10873
  import { execSync as execSync3 } from "child_process";
10187
10874
  import { fileURLToPath as fileURLToPath3 } from "url";
10188
- import { dirname as dirname6, join as join20 } from "path";
10875
+ import { dirname as dirname7, join as join21 } from "path";
10189
10876
  import * as readline from "readline";
10190
10877
  var __filename2 = fileURLToPath3(import.meta.url);
10191
- var __dirname2 = dirname6(__filename2);
10878
+ var __dirname2 = dirname7(__filename2);
10192
10879
  function findTemplatesDir(startDir) {
10193
10880
  let d = startDir;
10194
10881
  for (let i = 0; i < 8; i++) {
10195
- const candidate = join20(d, "templates");
10196
- if (fs23.existsSync(candidate) && fs23.statSync(candidate).isDirectory()) {
10882
+ const candidate = join21(d, "templates");
10883
+ if (fs26.existsSync(candidate) && fs26.statSync(candidate).isDirectory()) {
10197
10884
  return candidate;
10198
10885
  }
10199
- d = dirname6(d);
10886
+ d = dirname7(d);
10200
10887
  }
10201
- return join20(startDir, "templates");
10888
+ return join21(startDir, "templates");
10202
10889
  }
10203
10890
  var TEMPLATES_DIR = findTemplatesDir(__dirname2);
10204
10891
  var NW_SKILLS = [
@@ -10210,12 +10897,12 @@ var NW_SKILLS = [
10210
10897
  "nw-review"
10211
10898
  ];
10212
10899
  function hasPlaywrightDependency(cwd) {
10213
- const packageJsonPath = path22.join(cwd, "package.json");
10214
- if (!fs23.existsSync(packageJsonPath)) {
10900
+ const packageJsonPath = path25.join(cwd, "package.json");
10901
+ if (!fs26.existsSync(packageJsonPath)) {
10215
10902
  return false;
10216
10903
  }
10217
10904
  try {
10218
- const packageJson2 = JSON.parse(fs23.readFileSync(packageJsonPath, "utf-8"));
10905
+ const packageJson2 = JSON.parse(fs26.readFileSync(packageJsonPath, "utf-8"));
10219
10906
  return Boolean(
10220
10907
  packageJson2.dependencies?.["@playwright/test"] || packageJson2.dependencies?.playwright || packageJson2.devDependencies?.["@playwright/test"] || packageJson2.devDependencies?.playwright
10221
10908
  );
@@ -10227,7 +10914,7 @@ function detectPlaywright(cwd) {
10227
10914
  if (hasPlaywrightDependency(cwd)) {
10228
10915
  return true;
10229
10916
  }
10230
- if (fs23.existsSync(path22.join(cwd, "node_modules", ".bin", "playwright"))) {
10917
+ if (fs26.existsSync(path25.join(cwd, "node_modules", ".bin", "playwright"))) {
10231
10918
  return true;
10232
10919
  }
10233
10920
  try {
@@ -10243,10 +10930,10 @@ function detectPlaywright(cwd) {
10243
10930
  }
10244
10931
  }
10245
10932
  function resolvePlaywrightInstallCommand(cwd) {
10246
- if (fs23.existsSync(path22.join(cwd, "pnpm-lock.yaml"))) {
10933
+ if (fs26.existsSync(path25.join(cwd, "pnpm-lock.yaml"))) {
10247
10934
  return "pnpm add -D @playwright/test";
10248
10935
  }
10249
- if (fs23.existsSync(path22.join(cwd, "yarn.lock"))) {
10936
+ if (fs26.existsSync(path25.join(cwd, "yarn.lock"))) {
10250
10937
  return "yarn add -D @playwright/test";
10251
10938
  }
10252
10939
  return "npm install -D @playwright/test";
@@ -10255,7 +10942,7 @@ function promptYesNo(question, defaultNo = true) {
10255
10942
  if (!process.stdin.isTTY || !process.stdout.isTTY) {
10256
10943
  return Promise.resolve(false);
10257
10944
  }
10258
- return new Promise((resolve9) => {
10945
+ return new Promise((resolve11) => {
10259
10946
  const rl = readline.createInterface({
10260
10947
  input: process.stdin,
10261
10948
  output: process.stdout
@@ -10265,10 +10952,10 @@ function promptYesNo(question, defaultNo = true) {
10265
10952
  rl.close();
10266
10953
  const normalized = answer.trim().toLowerCase();
10267
10954
  if (normalized === "") {
10268
- resolve9(!defaultNo);
10955
+ resolve11(!defaultNo);
10269
10956
  return;
10270
10957
  }
10271
- resolve9(normalized === "y" || normalized === "yes");
10958
+ resolve11(normalized === "y" || normalized === "yes");
10272
10959
  });
10273
10960
  });
10274
10961
  }
@@ -10371,7 +11058,7 @@ function getDefaultBranch(cwd) {
10371
11058
  }
10372
11059
  }
10373
11060
  function promptProviderSelection(providers) {
10374
- return new Promise((resolve9, reject) => {
11061
+ return new Promise((resolve11, reject) => {
10375
11062
  const rl = readline.createInterface({
10376
11063
  input: process.stdin,
10377
11064
  output: process.stdout
@@ -10387,13 +11074,13 @@ function promptProviderSelection(providers) {
10387
11074
  reject(new Error("Invalid selection. Please run init again and select a valid number."));
10388
11075
  return;
10389
11076
  }
10390
- resolve9(providers[selection - 1]);
11077
+ resolve11(providers[selection - 1]);
10391
11078
  });
10392
11079
  });
10393
11080
  }
10394
11081
  function ensureDir(dirPath) {
10395
- if (!fs23.existsSync(dirPath)) {
10396
- fs23.mkdirSync(dirPath, { recursive: true });
11082
+ if (!fs26.existsSync(dirPath)) {
11083
+ fs26.mkdirSync(dirPath, { recursive: true });
10397
11084
  }
10398
11085
  }
10399
11086
  function buildInitConfig(params) {
@@ -10443,6 +11130,7 @@ function buildInitConfig(params) {
10443
11130
  },
10444
11131
  audit: { ...defaults.audit },
10445
11132
  analytics: { ...defaults.analytics },
11133
+ manager: { ...defaults.manager },
10446
11134
  feedback: { ...defaults.feedback },
10447
11135
  merger: { ...defaults.merger },
10448
11136
  prResolver: { ...defaults.prResolver },
@@ -10467,30 +11155,30 @@ function buildInitConfig(params) {
10467
11155
  }
10468
11156
  function resolveTemplatePath(templateName, customTemplatesDir, bundledTemplatesDir) {
10469
11157
  if (customTemplatesDir !== null) {
10470
- const customPath = join20(customTemplatesDir, templateName);
10471
- if (fs23.existsSync(customPath)) {
11158
+ const customPath = join21(customTemplatesDir, templateName);
11159
+ if (fs26.existsSync(customPath)) {
10472
11160
  return { path: customPath, source: "custom" };
10473
11161
  }
10474
11162
  }
10475
- return { path: join20(bundledTemplatesDir, templateName), source: "bundled" };
11163
+ return { path: join21(bundledTemplatesDir, templateName), source: "bundled" };
10476
11164
  }
10477
11165
  function processTemplate(templateName, targetPath, replacements, force, sourcePath, source) {
10478
- if (fs23.existsSync(targetPath) && !force) {
11166
+ if (fs26.existsSync(targetPath) && !force) {
10479
11167
  console.log(` Skipped (exists): ${targetPath}`);
10480
11168
  return { created: false, source: source ?? "bundled" };
10481
11169
  }
10482
- const templatePath = sourcePath ?? join20(TEMPLATES_DIR, templateName);
11170
+ const templatePath = sourcePath ?? join21(TEMPLATES_DIR, templateName);
10483
11171
  const resolvedSource = source ?? "bundled";
10484
- let content = fs23.readFileSync(templatePath, "utf-8");
11172
+ let content = fs26.readFileSync(templatePath, "utf-8");
10485
11173
  for (const [key, value] of Object.entries(replacements)) {
10486
11174
  content = content.replaceAll(key, value);
10487
11175
  }
10488
- fs23.writeFileSync(targetPath, content);
11176
+ fs26.writeFileSync(targetPath, content);
10489
11177
  console.log(` Created: ${targetPath} (${resolvedSource})`);
10490
11178
  return { created: true, source: resolvedSource };
10491
11179
  }
10492
11180
  function addToGitignore(cwd) {
10493
- const gitignorePath = path22.join(cwd, ".gitignore");
11181
+ const gitignorePath = path25.join(cwd, ".gitignore");
10494
11182
  const entries = [
10495
11183
  {
10496
11184
  pattern: "/logs/",
@@ -10504,13 +11192,13 @@ function addToGitignore(cwd) {
10504
11192
  },
10505
11193
  { pattern: "*.claim", label: "*.claim", check: (c) => c.includes("*.claim") }
10506
11194
  ];
10507
- if (!fs23.existsSync(gitignorePath)) {
11195
+ if (!fs26.existsSync(gitignorePath)) {
10508
11196
  const lines = ["# Night Watch", ...entries.map((e) => e.pattern), ""];
10509
- fs23.writeFileSync(gitignorePath, lines.join("\n"));
11197
+ fs26.writeFileSync(gitignorePath, lines.join("\n"));
10510
11198
  console.log(` Created: ${gitignorePath} (with Night Watch entries)`);
10511
11199
  return;
10512
11200
  }
10513
- const content = fs23.readFileSync(gitignorePath, "utf-8");
11201
+ const content = fs26.readFileSync(gitignorePath, "utf-8");
10514
11202
  const missing = entries.filter((e) => !e.check(content));
10515
11203
  if (missing.length === 0) {
10516
11204
  console.log(` Skipped (exists): Night Watch entries in .gitignore`);
@@ -10518,59 +11206,59 @@ function addToGitignore(cwd) {
10518
11206
  }
10519
11207
  const additions = missing.map((e) => e.pattern).join("\n");
10520
11208
  const newContent = content.trimEnd() + "\n\n# Night Watch\n" + additions + "\n";
10521
- fs23.writeFileSync(gitignorePath, newContent);
11209
+ fs26.writeFileSync(gitignorePath, newContent);
10522
11210
  console.log(` Updated: ${gitignorePath} (added ${missing.map((e) => e.label).join(", ")})`);
10523
11211
  }
10524
11212
  function installSkills(cwd, provider, force, templatesDir) {
10525
- const skillsTemplatesDir = path22.join(templatesDir, "skills");
10526
- if (!fs23.existsSync(skillsTemplatesDir)) {
11213
+ const skillsTemplatesDir = path25.join(templatesDir, "skills");
11214
+ if (!fs26.existsSync(skillsTemplatesDir)) {
10527
11215
  return { location: "", installed: 0, skipped: 0, type: "none" };
10528
11216
  }
10529
11217
  const isClaudeProvider = provider === "claude" || provider.startsWith("claude");
10530
11218
  const isCodexProvider = provider === "codex";
10531
- const claudeDir = path22.join(cwd, ".claude");
10532
- if (isClaudeProvider || fs23.existsSync(claudeDir)) {
11219
+ const claudeDir = path25.join(cwd, ".claude");
11220
+ if (isClaudeProvider || fs26.existsSync(claudeDir)) {
10533
11221
  ensureDir(claudeDir);
10534
- const skillsDir = path22.join(claudeDir, "skills");
11222
+ const skillsDir = path25.join(claudeDir, "skills");
10535
11223
  ensureDir(skillsDir);
10536
11224
  let installed = 0;
10537
11225
  let skipped = 0;
10538
11226
  for (const skillName of NW_SKILLS) {
10539
- const templateFile = path22.join(skillsTemplatesDir, `${skillName}.md`);
10540
- if (!fs23.existsSync(templateFile)) continue;
10541
- const skillDir = path22.join(skillsDir, skillName);
11227
+ const templateFile = path25.join(skillsTemplatesDir, `${skillName}.md`);
11228
+ if (!fs26.existsSync(templateFile)) continue;
11229
+ const skillDir = path25.join(skillsDir, skillName);
10542
11230
  ensureDir(skillDir);
10543
- const target = path22.join(skillDir, "SKILL.md");
10544
- if (fs23.existsSync(target) && !force) {
11231
+ const target = path25.join(skillDir, "SKILL.md");
11232
+ if (fs26.existsSync(target) && !force) {
10545
11233
  skipped++;
10546
11234
  continue;
10547
11235
  }
10548
- fs23.copyFileSync(templateFile, target);
11236
+ fs26.copyFileSync(templateFile, target);
10549
11237
  installed++;
10550
11238
  }
10551
11239
  return { location: ".claude/skills/", installed, skipped, type: "claude" };
10552
11240
  }
10553
11241
  if (isCodexProvider) {
10554
- const agentsFile = path22.join(cwd, "AGENTS.md");
10555
- const blockFile = path22.join(skillsTemplatesDir, "_codex-block.md");
10556
- if (!fs23.existsSync(blockFile)) {
11242
+ const agentsFile = path25.join(cwd, "AGENTS.md");
11243
+ const blockFile = path25.join(skillsTemplatesDir, "_codex-block.md");
11244
+ if (!fs26.existsSync(blockFile)) {
10557
11245
  return { location: "", installed: 0, skipped: 0, type: "none" };
10558
11246
  }
10559
- const block = fs23.readFileSync(blockFile, "utf-8");
11247
+ const block = fs26.readFileSync(blockFile, "utf-8");
10560
11248
  const marker = "## Night Watch Skills";
10561
- if (!fs23.existsSync(agentsFile)) {
10562
- fs23.writeFileSync(agentsFile, block);
11249
+ if (!fs26.existsSync(agentsFile)) {
11250
+ fs26.writeFileSync(agentsFile, block);
10563
11251
  return { location: "AGENTS.md", installed: NW_SKILLS.length, skipped: 0, type: "codex" };
10564
11252
  }
10565
- const existing = fs23.readFileSync(agentsFile, "utf-8");
11253
+ const existing = fs26.readFileSync(agentsFile, "utf-8");
10566
11254
  if (existing.includes(marker)) {
10567
11255
  if (!force) {
10568
11256
  return { location: "AGENTS.md", installed: 0, skipped: NW_SKILLS.length, type: "codex" };
10569
11257
  }
10570
11258
  const withoutSection = existing.replace(/\n\n## Night Watch Skills[\s\S]*$/, "");
10571
- fs23.writeFileSync(agentsFile, withoutSection + "\n\n" + block);
11259
+ fs26.writeFileSync(agentsFile, withoutSection + "\n\n" + block);
10572
11260
  } else {
10573
- fs23.appendFileSync(agentsFile, "\n\n" + block);
11261
+ fs26.appendFileSync(agentsFile, "\n\n" + block);
10574
11262
  }
10575
11263
  return { location: "AGENTS.md", installed: NW_SKILLS.length, skipped: 0, type: "codex" };
10576
11264
  }
@@ -10694,28 +11382,28 @@ function initCommand(program2) {
10694
11382
  "${DEFAULT_BRANCH}": defaultBranch
10695
11383
  };
10696
11384
  step(6, totalSteps, "Creating PRD directory structure...");
10697
- const prdDirPath = path22.join(cwd, prdDir);
10698
- const doneDirPath = path22.join(prdDirPath, "done");
11385
+ const prdDirPath = path25.join(cwd, prdDir);
11386
+ const doneDirPath = path25.join(prdDirPath, "done");
10699
11387
  ensureDir(doneDirPath);
10700
11388
  success(`Created ${prdDirPath}/`);
10701
11389
  success(`Created ${doneDirPath}/`);
10702
11390
  step(7, totalSteps, "Creating logs directory...");
10703
- const logsPath = path22.join(cwd, LOG_DIR);
11391
+ const logsPath = path25.join(cwd, LOG_DIR);
10704
11392
  ensureDir(logsPath);
10705
11393
  success(`Created ${logsPath}/`);
10706
11394
  addToGitignore(cwd);
10707
11395
  step(8, totalSteps, "Creating instructions directory...");
10708
- const instructionsDir = path22.join(cwd, "instructions");
11396
+ const instructionsDir = path25.join(cwd, "instructions");
10709
11397
  ensureDir(instructionsDir);
10710
11398
  success(`Created ${instructionsDir}/`);
10711
11399
  const existingConfig = loadConfig(cwd);
10712
- const customTemplatesDirPath = path22.join(cwd, existingConfig.templatesDir);
10713
- const customTemplatesDir = fs23.existsSync(customTemplatesDirPath) ? customTemplatesDirPath : null;
11400
+ const customTemplatesDirPath = path25.join(cwd, existingConfig.templatesDir);
11401
+ const customTemplatesDir = fs26.existsSync(customTemplatesDirPath) ? customTemplatesDirPath : null;
10714
11402
  const templateSources = [];
10715
11403
  const nwResolution = resolveTemplatePath("executor.md", customTemplatesDir, TEMPLATES_DIR);
10716
11404
  const nwResult = processTemplate(
10717
11405
  "executor.md",
10718
- path22.join(instructionsDir, "executor.md"),
11406
+ path25.join(instructionsDir, "executor.md"),
10719
11407
  replacements,
10720
11408
  force,
10721
11409
  nwResolution.path,
@@ -10729,7 +11417,7 @@ function initCommand(program2) {
10729
11417
  );
10730
11418
  const peResult = processTemplate(
10731
11419
  "prd-executor.md",
10732
- path22.join(instructionsDir, "prd-executor.md"),
11420
+ path25.join(instructionsDir, "prd-executor.md"),
10733
11421
  replacements,
10734
11422
  force,
10735
11423
  peResolution.path,
@@ -10739,7 +11427,7 @@ function initCommand(program2) {
10739
11427
  const prResolution = resolveTemplatePath("pr-reviewer.md", customTemplatesDir, TEMPLATES_DIR);
10740
11428
  const prResult = processTemplate(
10741
11429
  "pr-reviewer.md",
10742
- path22.join(instructionsDir, "pr-reviewer.md"),
11430
+ path25.join(instructionsDir, "pr-reviewer.md"),
10743
11431
  replacements,
10744
11432
  force,
10745
11433
  prResolution.path,
@@ -10749,7 +11437,7 @@ function initCommand(program2) {
10749
11437
  const qaResolution = resolveTemplatePath("qa.md", customTemplatesDir, TEMPLATES_DIR);
10750
11438
  const qaResult = processTemplate(
10751
11439
  "qa.md",
10752
- path22.join(instructionsDir, "qa.md"),
11440
+ path25.join(instructionsDir, "qa.md"),
10753
11441
  replacements,
10754
11442
  force,
10755
11443
  qaResolution.path,
@@ -10759,7 +11447,7 @@ function initCommand(program2) {
10759
11447
  const auditResolution = resolveTemplatePath("audit.md", customTemplatesDir, TEMPLATES_DIR);
10760
11448
  const auditResult = processTemplate(
10761
11449
  "audit.md",
10762
- path22.join(instructionsDir, "audit.md"),
11450
+ path25.join(instructionsDir, "audit.md"),
10763
11451
  replacements,
10764
11452
  force,
10765
11453
  auditResolution.path,
@@ -10773,7 +11461,7 @@ function initCommand(program2) {
10773
11461
  );
10774
11462
  const plannerResult = processTemplate(
10775
11463
  "prd-creator.md",
10776
- path22.join(instructionsDir, "prd-creator.md"),
11464
+ path25.join(instructionsDir, "prd-creator.md"),
10777
11465
  replacements,
10778
11466
  force,
10779
11467
  plannerResolution.path,
@@ -10781,8 +11469,8 @@ function initCommand(program2) {
10781
11469
  );
10782
11470
  templateSources.push({ name: "prd-creator.md", source: plannerResult.source });
10783
11471
  step(9, totalSteps, "Creating configuration file...");
10784
- const configPath = path22.join(cwd, CONFIG_FILE_NAME);
10785
- if (fs23.existsSync(configPath) && !force) {
11472
+ const configPath = path25.join(cwd, CONFIG_FILE_NAME);
11473
+ if (fs26.existsSync(configPath) && !force) {
10786
11474
  console.log(` Skipped (exists): ${configPath}`);
10787
11475
  } else {
10788
11476
  const config = buildInitConfig({
@@ -10792,11 +11480,11 @@ function initCommand(program2) {
10792
11480
  reviewerEnabled,
10793
11481
  prdDir
10794
11482
  });
10795
- fs23.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
11483
+ fs26.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
10796
11484
  success(`Created ${configPath}`);
10797
11485
  }
10798
11486
  step(10, totalSteps, "Setting up GitHub Project board...");
10799
- const existingRaw = JSON.parse(fs23.readFileSync(configPath, "utf-8"));
11487
+ const existingRaw = JSON.parse(fs26.readFileSync(configPath, "utf-8"));
10800
11488
  const existingBoard = existingRaw.boardProvider;
10801
11489
  let boardSetupStatus = "Skipped";
10802
11490
  if (existingBoard?.projectNumber && !force) {
@@ -10818,14 +11506,14 @@ function initCommand(program2) {
10818
11506
  const provider = createBoardProvider({ enabled: true, provider: "github" }, cwd);
10819
11507
  const boardTitle = `${projectName} Night Watch`;
10820
11508
  const board = await provider.setupBoard(boardTitle);
10821
- const rawConfig = JSON.parse(fs23.readFileSync(configPath, "utf-8"));
11509
+ const rawConfig = JSON.parse(fs26.readFileSync(configPath, "utf-8"));
10822
11510
  rawConfig.boardProvider = {
10823
11511
  enabled: true,
10824
11512
  provider: "github",
10825
11513
  projectNumber: board.number,
10826
11514
  projectTitle: board.title
10827
11515
  };
10828
- fs23.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n");
11516
+ fs26.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n");
10829
11517
  boardSetupStatus = `Created (#${board.number})`;
10830
11518
  success(`GitHub Project board "${boardTitle}" ready (#${board.number})`);
10831
11519
  } catch (boardErr) {
@@ -10987,7 +11675,7 @@ async function maybeApplyCronSchedulingDelay(config, jobType, projectDir) {
10987
11675
  return plan;
10988
11676
  }
10989
11677
  if (plan.totalDelayMinutes > 0) {
10990
- await new Promise((resolve9) => setTimeout(resolve9, plan.totalDelayMinutes * 6e4));
11678
+ await new Promise((resolve11) => setTimeout(resolve11, plan.totalDelayMinutes * 6e4));
10991
11679
  }
10992
11680
  return getSchedulingPlan(projectDir, config, jobType);
10993
11681
  }
@@ -11051,8 +11739,8 @@ function recordJobOutcome(input) {
11051
11739
  }
11052
11740
 
11053
11741
  // src/commands/run.ts
11054
- import * as fs24 from "fs";
11055
- import * as path23 from "path";
11742
+ import * as fs27 from "fs";
11743
+ import * as path26 from "path";
11056
11744
  function resolveRunNotificationEvent(exitCode, scriptStatus) {
11057
11745
  if (exitCode === 124) {
11058
11746
  return "run_timeout";
@@ -11150,7 +11838,7 @@ function buildRunNotificationContext(config, projectDir, event, exitCode, script
11150
11838
  const checkpointStatus = checkpointValue === "created" || checkpointValue === "available" || checkpointValue === "none" ? checkpointValue : void 0;
11151
11839
  return {
11152
11840
  event,
11153
- projectName: path23.basename(projectDir),
11841
+ projectName: path26.basename(projectDir),
11154
11842
  exitCode,
11155
11843
  provider: config.provider,
11156
11844
  prdName: scriptResult?.data.prd ?? extractResultValueFromOutput(rawOutput, "prd"),
@@ -11170,12 +11858,12 @@ function buildRunNotificationContext(config, projectDir, event, exitCode, script
11170
11858
  };
11171
11859
  }
11172
11860
  function getCrossProjectFallbackCandidates(currentProjectDir) {
11173
- const current = path23.resolve(currentProjectDir);
11861
+ const current = path26.resolve(currentProjectDir);
11174
11862
  const { valid, invalid } = validateRegistry();
11175
11863
  for (const entry of invalid) {
11176
11864
  warn(`Skipping invalid registry entry: ${entry.path}`);
11177
11865
  }
11178
- return valid.filter((entry) => path23.resolve(entry.path) !== current);
11866
+ return valid.filter((entry) => path26.resolve(entry.path) !== current);
11179
11867
  }
11180
11868
  async function sendRunCompletionNotifications(config, projectDir, options, exitCode, scriptResult, rawOutput) {
11181
11869
  if (isRateLimitFallbackTriggered(scriptResult?.data)) {
@@ -11185,7 +11873,7 @@ async function sendRunCompletionNotifications(config, projectDir, options, exitC
11185
11873
  if (nonTelegramWebhooks.length > 0) {
11186
11874
  const _rateLimitCtx = {
11187
11875
  event: "rate_limit_fallback",
11188
- projectName: path23.basename(projectDir),
11876
+ projectName: path26.basename(projectDir),
11189
11877
  exitCode,
11190
11878
  provider: config.provider
11191
11879
  };
@@ -11423,20 +12111,20 @@ function applyCliOverrides(config, options) {
11423
12111
  }
11424
12112
  function scanPrdDirectory(projectDir, prdDir, maxRuntime) {
11425
12113
  const claimStaleAfter = maxRuntime > 0 ? maxRuntime : 14400;
11426
- const absolutePrdDir = path23.join(projectDir, prdDir);
11427
- const doneDir = path23.join(absolutePrdDir, "done");
12114
+ const absolutePrdDir = path26.join(projectDir, prdDir);
12115
+ const doneDir = path26.join(absolutePrdDir, "done");
11428
12116
  const pending = [];
11429
12117
  const completed = [];
11430
- if (fs24.existsSync(absolutePrdDir)) {
11431
- const entries = fs24.readdirSync(absolutePrdDir, { withFileTypes: true });
12118
+ if (fs27.existsSync(absolutePrdDir)) {
12119
+ const entries = fs27.readdirSync(absolutePrdDir, { withFileTypes: true });
11432
12120
  for (const entry of entries) {
11433
12121
  if (entry.isFile() && entry.name.endsWith(".md")) {
11434
- const claimPath = path23.join(absolutePrdDir, entry.name + CLAIM_FILE_EXTENSION);
12122
+ const claimPath = path26.join(absolutePrdDir, entry.name + CLAIM_FILE_EXTENSION);
11435
12123
  let claimed = false;
11436
12124
  let claimInfo = null;
11437
- if (fs24.existsSync(claimPath)) {
12125
+ if (fs27.existsSync(claimPath)) {
11438
12126
  try {
11439
- const content = fs24.readFileSync(claimPath, "utf-8");
12127
+ const content = fs27.readFileSync(claimPath, "utf-8");
11440
12128
  const data = JSON.parse(content);
11441
12129
  const age = Math.floor(Date.now() / 1e3) - data.timestamp;
11442
12130
  if (age < claimStaleAfter) {
@@ -11450,8 +12138,8 @@ function scanPrdDirectory(projectDir, prdDir, maxRuntime) {
11450
12138
  }
11451
12139
  }
11452
12140
  }
11453
- if (fs24.existsSync(doneDir)) {
11454
- const entries = fs24.readdirSync(doneDir, { withFileTypes: true });
12141
+ if (fs27.existsSync(doneDir)) {
12142
+ const entries = fs27.readdirSync(doneDir, { withFileTypes: true });
11455
12143
  for (const entry of entries) {
11456
12144
  if (entry.isFile() && entry.name.endsWith(".md")) {
11457
12145
  completed.push(entry.name);
@@ -11636,7 +12324,7 @@ ${stderr}`
11636
12324
  // src/commands/review.ts
11637
12325
  init_dist();
11638
12326
  import { execFileSync as execFileSync5 } from "child_process";
11639
- import * as path24 from "path";
12327
+ import * as path27 from "path";
11640
12328
  function shouldSendReviewNotification(scriptStatus) {
11641
12329
  if (!scriptStatus) {
11642
12330
  return true;
@@ -11964,7 +12652,7 @@ ${stderr}`);
11964
12652
  const reviewEvent = legacyNoChangesNeeded ? "review_ready_for_human" : "review_completed";
11965
12653
  await sendNotifications(config, {
11966
12654
  event: reviewEvent,
11967
- projectName: path24.basename(projectDir),
12655
+ projectName: path27.basename(projectDir),
11968
12656
  exitCode,
11969
12657
  provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL),
11970
12658
  prUrl: fallbackPrDetails?.url,
@@ -11983,7 +12671,7 @@ ${stderr}`);
11983
12671
  const reviewEvent = target.noChangesNeeded ? "review_ready_for_human" : "review_completed";
11984
12672
  await sendNotifications(config, {
11985
12673
  event: reviewEvent,
11986
- projectName: path24.basename(projectDir),
12674
+ projectName: path27.basename(projectDir),
11987
12675
  exitCode,
11988
12676
  provider: formatProviderDisplay(
11989
12677
  envVars.NW_PROVIDER_CMD,
@@ -12008,7 +12696,7 @@ ${stderr}`);
12008
12696
  const autoMergedPrDetails = fetchPrDetailsByNumber(autoMergedPrNumber, projectDir);
12009
12697
  const _mergeCtx = {
12010
12698
  event: "pr_auto_merged",
12011
- projectName: path24.basename(projectDir),
12699
+ projectName: path27.basename(projectDir),
12012
12700
  exitCode,
12013
12701
  provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL),
12014
12702
  prNumber: autoMergedPrDetails?.number ?? autoMergedPrNumber,
@@ -12033,7 +12721,7 @@ ${stderr}`);
12033
12721
 
12034
12722
  // src/commands/qa.ts
12035
12723
  init_dist();
12036
- import * as path25 from "path";
12724
+ import * as path28 from "path";
12037
12725
  function shouldSendQaNotification(scriptStatus) {
12038
12726
  if (!scriptStatus) {
12039
12727
  return true;
@@ -12195,7 +12883,7 @@ ${stderr}`);
12195
12883
  const qaScreenshotUrls = primaryQaPr !== void 0 ? fetchQaScreenshotUrlsForPr(primaryQaPr, projectDir, repo) : [];
12196
12884
  const _qaCtx = {
12197
12885
  event: "qa_completed",
12198
- projectName: path25.basename(projectDir),
12886
+ projectName: path28.basename(projectDir),
12199
12887
  exitCode,
12200
12888
  provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL),
12201
12889
  prNumber: prDetails?.number ?? primaryQaPr,
@@ -12221,8 +12909,8 @@ ${stderr}`);
12221
12909
 
12222
12910
  // src/commands/audit.ts
12223
12911
  init_dist();
12224
- import * as fs25 from "fs";
12225
- import * as path26 from "path";
12912
+ import * as fs28 from "fs";
12913
+ import * as path29 from "path";
12226
12914
  function buildEnvVars4(config, options) {
12227
12915
  const env = buildBaseEnvVars(config, "audit", options.dryRun);
12228
12916
  env.NW_AUDIT_MAX_RUNTIME = String(config.audit.maxRuntime);
@@ -12270,7 +12958,7 @@ function auditCommand(program2) {
12270
12958
  if (config.audit.createIssues) {
12271
12959
  configTable.push(["Target Column", config.audit.targetColumn]);
12272
12960
  }
12273
- configTable.push(["Report File", path26.join(projectDir, "logs", "audit-report.md")]);
12961
+ configTable.push(["Report File", path29.join(projectDir, "logs", "audit-report.md")]);
12274
12962
  console.log(configTable.toString());
12275
12963
  header("Provider Invocation");
12276
12964
  const providerCmd = PROVIDER_COMMANDS[auditProvider];
@@ -12328,8 +13016,8 @@ ${stderr}`);
12328
13016
  } else if (scriptResult?.status?.startsWith("skip_")) {
12329
13017
  spinner.succeed("Code audit skipped");
12330
13018
  } else {
12331
- const reportPath = path26.join(projectDir, "logs", "audit-report.md");
12332
- if (!fs25.existsSync(reportPath)) {
13019
+ const reportPath = path29.join(projectDir, "logs", "audit-report.md");
13020
+ if (!fs28.existsSync(reportPath)) {
12333
13021
  spinner.fail("Code audit finished without a report file");
12334
13022
  process.exit(1);
12335
13023
  }
@@ -12346,9 +13034,9 @@ ${stderr}`);
12346
13034
  const providerExit = scriptResult?.data?.provider_exit;
12347
13035
  const exitDetail = providerExit && providerExit !== String(exitCode) ? `, provider exit ${providerExit}` : "";
12348
13036
  spinner.fail(`Code audit exited with code ${exitCode}${statusSuffix}${exitDetail}`);
12349
- const logPath = path26.join(projectDir, "logs", "audit.log");
12350
- if (fs25.existsSync(logPath)) {
12351
- const logLines = fs25.readFileSync(logPath, "utf-8").split("\n").filter((l) => l.trim()).slice(-8);
13037
+ const logPath = path29.join(projectDir, "logs", "audit.log");
13038
+ if (fs28.existsSync(logPath)) {
13039
+ const logLines = fs28.readFileSync(logPath, "utf-8").split("\n").filter((l) => l.trim()).slice(-8);
12352
13040
  if (logLines.length > 0) {
12353
13041
  process.stderr.write(logLines.join("\n") + "\n");
12354
13042
  }
@@ -12492,16 +13180,16 @@ function analyticsCommand(program2) {
12492
13180
  // src/commands/install.ts
12493
13181
  init_dist();
12494
13182
  import { execSync as execSync4 } from "child_process";
12495
- import * as path27 from "path";
12496
- import * as fs26 from "fs";
13183
+ import * as path30 from "path";
13184
+ import * as fs29 from "fs";
12497
13185
  function shellQuote(value) {
12498
13186
  return `'${value.replace(/'/g, `'"'"'`)}'`;
12499
13187
  }
12500
13188
  function getNightWatchBinPath() {
12501
13189
  try {
12502
13190
  const npmBin = execSync4("npm bin -g", { encoding: "utf-8" }).trim();
12503
- const binPath = path27.join(npmBin, "night-watch");
12504
- if (fs26.existsSync(binPath)) {
13191
+ const binPath = path30.join(npmBin, "night-watch");
13192
+ if (fs29.existsSync(binPath)) {
12505
13193
  return binPath;
12506
13194
  }
12507
13195
  } catch {
@@ -12514,17 +13202,17 @@ function getNightWatchBinPath() {
12514
13202
  }
12515
13203
  function getNodeBinDir() {
12516
13204
  if (process.execPath && process.execPath !== "node") {
12517
- return path27.dirname(process.execPath);
13205
+ return path30.dirname(process.execPath);
12518
13206
  }
12519
13207
  try {
12520
13208
  const nodePath = execSync4("which node", { encoding: "utf-8" }).trim();
12521
- return path27.dirname(nodePath);
13209
+ return path30.dirname(nodePath);
12522
13210
  } catch {
12523
13211
  return "";
12524
13212
  }
12525
13213
  }
12526
13214
  function buildCronPathPrefix(nodeBinDir, nightWatchBin) {
12527
- const nightWatchBinDir = nightWatchBin.includes("/") || nightWatchBin.includes("\\") ? path27.dirname(nightWatchBin) : "";
13215
+ const nightWatchBinDir = nightWatchBin.includes("/") || nightWatchBin.includes("\\") ? path30.dirname(nightWatchBin) : "";
12528
13216
  const pathParts = Array.from(
12529
13217
  new Set([nodeBinDir, nightWatchBinDir].filter((part) => part.length > 0))
12530
13218
  );
@@ -12540,12 +13228,12 @@ function performInstall(projectDir, config, options) {
12540
13228
  const nightWatchBin = getNightWatchBinPath();
12541
13229
  const projectName = getProjectName(projectDir);
12542
13230
  const marker = generateMarker(projectName);
12543
- const logDir = path27.join(projectDir, LOG_DIR);
12544
- if (!fs26.existsSync(logDir)) {
12545
- fs26.mkdirSync(logDir, { recursive: true });
13231
+ const logDir = path30.join(projectDir, LOG_DIR);
13232
+ if (!fs29.existsSync(logDir)) {
13233
+ fs29.mkdirSync(logDir, { recursive: true });
12546
13234
  }
12547
- const executorLog = path27.join(logDir, "executor.log");
12548
- const reviewerLog = path27.join(logDir, "reviewer.log");
13235
+ const executorLog = path30.join(logDir, "executor.log");
13236
+ const reviewerLog = path30.join(logDir, "reviewer.log");
12549
13237
  if (!options?.force) {
12550
13238
  const existingEntries2 = Array.from(
12551
13239
  /* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)])
@@ -12582,7 +13270,7 @@ function performInstall(projectDir, config, options) {
12582
13270
  const installSlicer = options?.noSlicer === true ? false : config.roadmapScanner.enabled;
12583
13271
  if (installSlicer) {
12584
13272
  const slicerSchedule = config.roadmapScanner.slicerSchedule;
12585
- const slicerLog = path27.join(logDir, "slicer.log");
13273
+ const slicerLog = path30.join(logDir, "slicer.log");
12586
13274
  const slicerEntry = `${slicerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} planner >> ${shellQuote(slicerLog)} 2>&1 ${marker}`;
12587
13275
  entries.push(slicerEntry);
12588
13276
  }
@@ -12590,7 +13278,7 @@ function performInstall(projectDir, config, options) {
12590
13278
  const installQa = disableQa ? false : config.qa.enabled;
12591
13279
  if (installQa) {
12592
13280
  const qaSchedule = config.qa.schedule;
12593
- const qaLog = path27.join(logDir, "qa.log");
13281
+ const qaLog = path30.join(logDir, "qa.log");
12594
13282
  const qaEntry = `${qaSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} qa >> ${shellQuote(qaLog)} 2>&1 ${marker}`;
12595
13283
  entries.push(qaEntry);
12596
13284
  }
@@ -12598,7 +13286,7 @@ function performInstall(projectDir, config, options) {
12598
13286
  const installAudit = disableAudit ? false : config.audit.enabled;
12599
13287
  if (installAudit) {
12600
13288
  const auditSchedule = config.audit.schedule;
12601
- const auditLog = path27.join(logDir, "audit.log");
13289
+ const auditLog = path30.join(logDir, "audit.log");
12602
13290
  const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
12603
13291
  entries.push(auditEntry);
12604
13292
  }
@@ -12606,7 +13294,7 @@ function performInstall(projectDir, config, options) {
12606
13294
  const installAnalytics = disableAnalytics ? false : config.analytics.enabled;
12607
13295
  if (installAnalytics) {
12608
13296
  const analyticsSchedule = config.analytics.schedule;
12609
- const analyticsLog = path27.join(logDir, "analytics.log");
13297
+ const analyticsLog = path30.join(logDir, "analytics.log");
12610
13298
  const analyticsEntry = `${analyticsSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} analytics >> ${shellQuote(analyticsLog)} 2>&1 ${marker}`;
12611
13299
  entries.push(analyticsEntry);
12612
13300
  }
@@ -12614,7 +13302,7 @@ function performInstall(projectDir, config, options) {
12614
13302
  const installPrResolver = disablePrResolver ? false : config.prResolver.enabled;
12615
13303
  if (installPrResolver) {
12616
13304
  const prResolverSchedule = config.prResolver.schedule;
12617
- const prResolverLog = path27.join(logDir, "pr-resolver.log");
13305
+ const prResolverLog = path30.join(logDir, "pr-resolver.log");
12618
13306
  const prResolverEntry = `${prResolverSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} resolve >> ${shellQuote(prResolverLog)} 2>&1 ${marker}`;
12619
13307
  entries.push(prResolverEntry);
12620
13308
  }
@@ -12622,10 +13310,18 @@ function performInstall(projectDir, config, options) {
12622
13310
  const installMerger = disableMerger ? false : config.merger?.enabled ?? false;
12623
13311
  if (installMerger) {
12624
13312
  const mergerSchedule = config.merger.schedule;
12625
- const mergerLog = path27.join(logDir, "merger.log");
13313
+ const mergerLog = path30.join(logDir, "merger.log");
12626
13314
  const mergerEntry = `${mergerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} merge >> ${shellQuote(mergerLog)} 2>&1 ${marker}`;
12627
13315
  entries.push(mergerEntry);
12628
13316
  }
13317
+ const disableManager = options?.noManager === true || options?.manager === false;
13318
+ const installManager = disableManager ? false : config.manager?.enabled ?? false;
13319
+ if (installManager) {
13320
+ const managerSchedule = config.manager.schedule;
13321
+ const managerLog = path30.join(logDir, `${MANAGER_LOG_NAME}.log`);
13322
+ const managerEntry = `${managerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} manager >> ${shellQuote(managerLog)} 2>&1 ${marker}`;
13323
+ entries.push(managerEntry);
13324
+ }
12629
13325
  const existingEntries = new Set(
12630
13326
  Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]))
12631
13327
  );
@@ -12644,7 +13340,7 @@ function performInstall(projectDir, config, options) {
12644
13340
  }
12645
13341
  }
12646
13342
  function installCommand(program2) {
12647
- 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("--no-analytics", "Skip installing analytics cron").option("--no-pr-resolver", "Skip installing PR resolver cron").option("--no-merger", "Skip installing merger cron").option("-f, --force", "Replace existing cron entries for this project").action(async (options) => {
13343
+ 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("--no-analytics", "Skip installing analytics cron").option("--no-pr-resolver", "Skip installing PR resolver cron").option("--no-merger", "Skip installing merger cron").option("--no-manager", "Skip installing manager cron").option("-f, --force", "Replace existing cron entries for this project").action(async (options) => {
12648
13344
  try {
12649
13345
  const projectDir = process.cwd();
12650
13346
  const config = loadConfig(projectDir);
@@ -12653,12 +13349,12 @@ function installCommand(program2) {
12653
13349
  const nightWatchBin = getNightWatchBinPath();
12654
13350
  const projectName = getProjectName(projectDir);
12655
13351
  const marker = generateMarker(projectName);
12656
- const logDir = path27.join(projectDir, LOG_DIR);
12657
- if (!fs26.existsSync(logDir)) {
12658
- fs26.mkdirSync(logDir, { recursive: true });
13352
+ const logDir = path30.join(projectDir, LOG_DIR);
13353
+ if (!fs29.existsSync(logDir)) {
13354
+ fs29.mkdirSync(logDir, { recursive: true });
12659
13355
  }
12660
- const executorLog = path27.join(logDir, "executor.log");
12661
- const reviewerLog = path27.join(logDir, "reviewer.log");
13356
+ const executorLog = path30.join(logDir, "executor.log");
13357
+ const reviewerLog = path30.join(logDir, "reviewer.log");
12662
13358
  const existingEntries = Array.from(
12663
13359
  /* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)])
12664
13360
  );
@@ -12694,7 +13390,7 @@ function installCommand(program2) {
12694
13390
  const installSlicer = options.noSlicer === true ? false : config.roadmapScanner.enabled;
12695
13391
  let slicerLog;
12696
13392
  if (installSlicer) {
12697
- slicerLog = path27.join(logDir, "slicer.log");
13393
+ slicerLog = path30.join(logDir, "slicer.log");
12698
13394
  const slicerSchedule = config.roadmapScanner.slicerSchedule;
12699
13395
  const slicerEntry = `${slicerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} planner >> ${shellQuote(slicerLog)} 2>&1 ${marker}`;
12700
13396
  entries.push(slicerEntry);
@@ -12703,7 +13399,7 @@ function installCommand(program2) {
12703
13399
  const installQa = disableQa ? false : config.qa.enabled;
12704
13400
  let qaLog;
12705
13401
  if (installQa) {
12706
- qaLog = path27.join(logDir, "qa.log");
13402
+ qaLog = path30.join(logDir, "qa.log");
12707
13403
  const qaSchedule = config.qa.schedule;
12708
13404
  const qaEntry = `${qaSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} qa >> ${shellQuote(qaLog)} 2>&1 ${marker}`;
12709
13405
  entries.push(qaEntry);
@@ -12712,7 +13408,7 @@ function installCommand(program2) {
12712
13408
  const installAudit = disableAudit ? false : config.audit.enabled;
12713
13409
  let auditLog;
12714
13410
  if (installAudit) {
12715
- auditLog = path27.join(logDir, "audit.log");
13411
+ auditLog = path30.join(logDir, "audit.log");
12716
13412
  const auditSchedule = config.audit.schedule;
12717
13413
  const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
12718
13414
  entries.push(auditEntry);
@@ -12721,7 +13417,7 @@ function installCommand(program2) {
12721
13417
  const installAnalytics = disableAnalytics ? false : config.analytics.enabled;
12722
13418
  let analyticsLog;
12723
13419
  if (installAnalytics) {
12724
- analyticsLog = path27.join(logDir, "analytics.log");
13420
+ analyticsLog = path30.join(logDir, "analytics.log");
12725
13421
  const analyticsSchedule = config.analytics.schedule;
12726
13422
  const analyticsEntry = `${analyticsSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} analytics >> ${shellQuote(analyticsLog)} 2>&1 ${marker}`;
12727
13423
  entries.push(analyticsEntry);
@@ -12730,7 +13426,7 @@ function installCommand(program2) {
12730
13426
  const installPrResolver = disablePrResolver ? false : config.prResolver.enabled;
12731
13427
  let prResolverLog;
12732
13428
  if (installPrResolver) {
12733
- prResolverLog = path27.join(logDir, "pr-resolver.log");
13429
+ prResolverLog = path30.join(logDir, "pr-resolver.log");
12734
13430
  const prResolverSchedule = config.prResolver.schedule;
12735
13431
  const prResolverEntry = `${prResolverSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} resolve >> ${shellQuote(prResolverLog)} 2>&1 ${marker}`;
12736
13432
  entries.push(prResolverEntry);
@@ -12739,11 +13435,20 @@ function installCommand(program2) {
12739
13435
  const installMerger = disableMerger ? false : config.merger?.enabled ?? false;
12740
13436
  let mergerLog;
12741
13437
  if (installMerger) {
12742
- mergerLog = path27.join(logDir, "merger.log");
13438
+ mergerLog = path30.join(logDir, "merger.log");
12743
13439
  const mergerSchedule = config.merger.schedule;
12744
13440
  const mergerEntry = `${mergerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} merge >> ${shellQuote(mergerLog)} 2>&1 ${marker}`;
12745
13441
  entries.push(mergerEntry);
12746
13442
  }
13443
+ const disableManager = options.noManager === true || options.manager === false;
13444
+ const installManager = disableManager ? false : config.manager?.enabled ?? false;
13445
+ let managerLog;
13446
+ if (installManager) {
13447
+ managerLog = path30.join(logDir, `${MANAGER_LOG_NAME}.log`);
13448
+ const managerSchedule = config.manager.schedule;
13449
+ const managerEntry = `${managerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} manager >> ${shellQuote(managerLog)} 2>&1 ${marker}`;
13450
+ entries.push(managerEntry);
13451
+ }
12747
13452
  const existingEntrySet = new Set(existingEntries);
12748
13453
  const currentCrontab = readCrontab();
12749
13454
  const baseCrontab = options.force ? currentCrontab.filter((line) => !existingEntrySet.has(line) && !line.includes(marker)) : currentCrontab;
@@ -12779,6 +13484,9 @@ function installCommand(program2) {
12779
13484
  if (installMerger && mergerLog) {
12780
13485
  dim(` Merger: ${mergerLog}`);
12781
13486
  }
13487
+ if (installManager && managerLog) {
13488
+ dim(` Manager: ${managerLog}`);
13489
+ }
12782
13490
  console.log();
12783
13491
  dim("To uninstall, run: night-watch uninstall");
12784
13492
  dim("To check status, run: night-watch status");
@@ -12793,8 +13501,8 @@ function installCommand(program2) {
12793
13501
 
12794
13502
  // src/commands/uninstall.ts
12795
13503
  init_dist();
12796
- import * as path28 from "path";
12797
- import * as fs27 from "fs";
13504
+ import * as path31 from "path";
13505
+ import * as fs30 from "fs";
12798
13506
  function performUninstall(projectDir, options) {
12799
13507
  try {
12800
13508
  const projectName = getProjectName(projectDir);
@@ -12809,25 +13517,26 @@ function performUninstall(projectDir, options) {
12809
13517
  const removedCount = removeEntriesForProject(projectDir, marker);
12810
13518
  unregisterProject(projectDir);
12811
13519
  if (!options?.keepLogs) {
12812
- const logDir = path28.join(projectDir, "logs");
12813
- if (fs27.existsSync(logDir)) {
13520
+ const logDir = path31.join(projectDir, "logs");
13521
+ if (fs30.existsSync(logDir)) {
12814
13522
  const logFiles = [
12815
13523
  "executor.log",
12816
13524
  "reviewer.log",
12817
13525
  "slicer.log",
12818
13526
  "audit.log",
12819
- "pr-resolver.log"
13527
+ "pr-resolver.log",
13528
+ "manager.log"
12820
13529
  ];
12821
13530
  logFiles.forEach((logFile) => {
12822
- const logPath = path28.join(logDir, logFile);
12823
- if (fs27.existsSync(logPath)) {
12824
- fs27.unlinkSync(logPath);
13531
+ const logPath = path31.join(logDir, logFile);
13532
+ if (fs30.existsSync(logPath)) {
13533
+ fs30.unlinkSync(logPath);
12825
13534
  }
12826
13535
  });
12827
13536
  try {
12828
- const remainingFiles = fs27.readdirSync(logDir);
13537
+ const remainingFiles = fs30.readdirSync(logDir);
12829
13538
  if (remainingFiles.length === 0) {
12830
- fs27.rmdirSync(logDir);
13539
+ fs30.rmdirSync(logDir);
12831
13540
  }
12832
13541
  } catch {
12833
13542
  }
@@ -12860,27 +13569,28 @@ function uninstallCommand(program2) {
12860
13569
  existingEntries.forEach((entry) => dim(` ${entry}`));
12861
13570
  const removedCount = removeEntriesForProject(projectDir, marker);
12862
13571
  if (!options.keepLogs) {
12863
- const logDir = path28.join(projectDir, "logs");
12864
- if (fs27.existsSync(logDir)) {
13572
+ const logDir = path31.join(projectDir, "logs");
13573
+ if (fs30.existsSync(logDir)) {
12865
13574
  const logFiles = [
12866
13575
  "executor.log",
12867
13576
  "reviewer.log",
12868
13577
  "slicer.log",
12869
13578
  "audit.log",
12870
- "pr-resolver.log"
13579
+ "pr-resolver.log",
13580
+ "manager.log"
12871
13581
  ];
12872
13582
  let logsRemoved = 0;
12873
13583
  logFiles.forEach((logFile) => {
12874
- const logPath = path28.join(logDir, logFile);
12875
- if (fs27.existsSync(logPath)) {
12876
- fs27.unlinkSync(logPath);
13584
+ const logPath = path31.join(logDir, logFile);
13585
+ if (fs30.existsSync(logPath)) {
13586
+ fs30.unlinkSync(logPath);
12877
13587
  logsRemoved++;
12878
13588
  }
12879
13589
  });
12880
13590
  try {
12881
- const remainingFiles = fs27.readdirSync(logDir);
13591
+ const remainingFiles = fs30.readdirSync(logDir);
12882
13592
  if (remainingFiles.length === 0) {
12883
- fs27.rmdirSync(logDir);
13593
+ fs30.rmdirSync(logDir);
12884
13594
  }
12885
13595
  } catch {
12886
13596
  }
@@ -13166,14 +13876,14 @@ function statusCommand(program2) {
13166
13876
  // src/commands/logs.ts
13167
13877
  init_dist();
13168
13878
  import { spawn as spawn3 } from "child_process";
13169
- import * as path29 from "path";
13170
- import * as fs28 from "fs";
13879
+ import * as path32 from "path";
13880
+ import * as fs31 from "fs";
13171
13881
  function getLastLines(filePath, lineCount) {
13172
- if (!fs28.existsSync(filePath)) {
13882
+ if (!fs31.existsSync(filePath)) {
13173
13883
  return `Log file not found: ${filePath}`;
13174
13884
  }
13175
13885
  try {
13176
- const content = fs28.readFileSync(filePath, "utf-8");
13886
+ const content = fs31.readFileSync(filePath, "utf-8");
13177
13887
  const lines = content.trim().split("\n");
13178
13888
  return lines.slice(-lineCount).join("\n");
13179
13889
  } catch (error2) {
@@ -13181,7 +13891,7 @@ function getLastLines(filePath, lineCount) {
13181
13891
  }
13182
13892
  }
13183
13893
  function followLog(filePath) {
13184
- if (!fs28.existsSync(filePath)) {
13894
+ if (!fs31.existsSync(filePath)) {
13185
13895
  console.log(`Log file not found: ${filePath}`);
13186
13896
  console.log("The log file will be created when the first execution runs.");
13187
13897
  return;
@@ -13200,20 +13910,21 @@ function followLog(filePath) {
13200
13910
  function logsCommand(program2) {
13201
13911
  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(
13202
13912
  "-t, --type <type>",
13203
- "Log type to view (executor|reviewer|qa|audit|planner|analytics|merger|all)",
13913
+ "Log type to view (executor|reviewer|qa|audit|planner|analytics|merger|manager|all)",
13204
13914
  "all"
13205
13915
  ).action(async (options) => {
13206
13916
  try {
13207
13917
  const projectDir = process.cwd();
13208
- const logDir = path29.join(projectDir, LOG_DIR);
13918
+ const logDir = path32.join(projectDir, LOG_DIR);
13209
13919
  const lineCount = parseInt(options.lines || "50", 10);
13210
- const executorLog = path29.join(logDir, EXECUTOR_LOG_FILE);
13211
- const reviewerLog = path29.join(logDir, REVIEWER_LOG_FILE);
13212
- const qaLog = path29.join(logDir, `${QA_LOG_NAME}.log`);
13213
- const auditLog = path29.join(logDir, `${AUDIT_LOG_NAME}.log`);
13214
- const plannerLog = path29.join(logDir, `${PLANNER_LOG_NAME}.log`);
13215
- const analyticsLog = path29.join(logDir, `${ANALYTICS_LOG_NAME}.log`);
13216
- const mergerLog = path29.join(logDir, `${MERGER_LOG_NAME}.log`);
13920
+ const executorLog = path32.join(logDir, EXECUTOR_LOG_FILE);
13921
+ const reviewerLog = path32.join(logDir, REVIEWER_LOG_FILE);
13922
+ const qaLog = path32.join(logDir, `${QA_LOG_NAME}.log`);
13923
+ const auditLog = path32.join(logDir, `${AUDIT_LOG_NAME}.log`);
13924
+ const plannerLog = path32.join(logDir, `${PLANNER_LOG_NAME}.log`);
13925
+ const analyticsLog = path32.join(logDir, `${ANALYTICS_LOG_NAME}.log`);
13926
+ const mergerLog = path32.join(logDir, `${MERGER_LOG_NAME}.log`);
13927
+ const managerLog = path32.join(logDir, `${MANAGER_LOG_NAME}.log`);
13217
13928
  const logType = options.type?.toLowerCase() || "all";
13218
13929
  const showExecutor = logType === "all" || logType === "run" || logType === "executor";
13219
13930
  const showReviewer = logType === "all" || logType === "review" || logType === "reviewer";
@@ -13222,10 +13933,11 @@ function logsCommand(program2) {
13222
13933
  const showPlanner = logType === "all" || logType === "planner" || logType === "slice" || logType === "slicer";
13223
13934
  const showAnalytics = logType === "all" || logType === "analytics";
13224
13935
  const showMerger = logType === "all" || logType === "merge" || logType === "merger";
13936
+ const showManager = logType === "all" || logType === "manager";
13225
13937
  if (options.follow) {
13226
13938
  if (logType === "all") {
13227
13939
  dim("Note: Following all logs is not supported. Showing executor log.");
13228
- dim("Use --type reviewer|qa|audit|planner|analytics|merger for other logs.\n");
13940
+ dim("Use --type reviewer|qa|audit|planner|analytics|merger|manager for other logs.\n");
13229
13941
  }
13230
13942
  let targetLog = executorLog;
13231
13943
  if (showReviewer) targetLog = reviewerLog;
@@ -13234,6 +13946,7 @@ function logsCommand(program2) {
13234
13946
  else if (showPlanner) targetLog = plannerLog;
13235
13947
  else if (showAnalytics) targetLog = analyticsLog;
13236
13948
  else if (showMerger) targetLog = mergerLog;
13949
+ else if (showManager) targetLog = managerLog;
13237
13950
  followLog(targetLog);
13238
13951
  return;
13239
13952
  }
@@ -13280,11 +13993,17 @@ function logsCommand(program2) {
13280
13993
  console.log();
13281
13994
  console.log(getLastLines(mergerLog, lineCount));
13282
13995
  }
13996
+ if (showManager) {
13997
+ header("Manager Log");
13998
+ dim(`File: ${managerLog}`);
13999
+ console.log();
14000
+ console.log(getLastLines(managerLog, lineCount));
14001
+ }
13283
14002
  console.log();
13284
14003
  dim("---");
13285
14004
  dim("Tip: Use -f to follow logs in real-time");
13286
14005
  dim(
13287
- " Use --type executor|reviewer|qa|audit|planner|analytics|merger to view specific logs"
14006
+ " Use --type executor|reviewer|qa|audit|planner|analytics|merger|manager to view specific logs"
13288
14007
  );
13289
14008
  } catch (err) {
13290
14009
  console.error(`Error reading logs: ${err instanceof Error ? err.message : String(err)}`);
@@ -13296,30 +14015,30 @@ function logsCommand(program2) {
13296
14015
  // src/commands/prd.ts
13297
14016
  init_dist();
13298
14017
  import { execSync as execSync5, spawn as spawn4, spawnSync } from "child_process";
13299
- import * as fs29 from "fs";
13300
- import * as path30 from "path";
14018
+ import * as fs32 from "fs";
14019
+ import * as path33 from "path";
13301
14020
  import { fileURLToPath as fileURLToPath4 } from "url";
13302
- import { dirname as dirname9 } from "path";
14021
+ import { dirname as dirname10 } from "path";
13303
14022
  var __filename3 = fileURLToPath4(import.meta.url);
13304
- var __dirname3 = dirname9(__filename3);
14023
+ var __dirname3 = dirname10(__filename3);
13305
14024
  function findTemplatesDir2(startDir) {
13306
14025
  let current = startDir;
13307
14026
  for (let i = 0; i < 8; i++) {
13308
- const candidate = path30.join(current, "templates");
13309
- if (fs29.existsSync(candidate) && fs29.statSync(candidate).isDirectory()) {
14027
+ const candidate = path33.join(current, "templates");
14028
+ if (fs32.existsSync(candidate) && fs32.statSync(candidate).isDirectory()) {
13310
14029
  return candidate;
13311
14030
  }
13312
- current = path30.dirname(current);
14031
+ current = path33.dirname(current);
13313
14032
  }
13314
- return path30.join(startDir, "templates");
14033
+ return path33.join(startDir, "templates");
13315
14034
  }
13316
14035
  var TEMPLATES_DIR2 = findTemplatesDir2(__dirname3);
13317
14036
  function slugify2(name) {
13318
14037
  return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
13319
14038
  }
13320
14039
  function getNextPrdNumber2(prdDir) {
13321
- if (!fs29.existsSync(prdDir)) return 1;
13322
- const files = fs29.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
14040
+ if (!fs32.existsSync(prdDir)) return 1;
14041
+ const files = fs32.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
13323
14042
  const numbers = files.map((f) => {
13324
14043
  const match = f.match(/^(\d+)-/);
13325
14044
  return match ? parseInt(match[1], 10) : 0;
@@ -13400,13 +14119,13 @@ function resolveGitHubBlobUrl(projectDir, relPath) {
13400
14119
  return null;
13401
14120
  }
13402
14121
  const ref = branch && branch !== "HEAD" ? branch : "main";
13403
- return `${httpsBase}/blob/${encodeURIComponent(ref).replace(/%2F/g, "/")}/${relPath.split(path30.sep).map((segment) => encodeURIComponent(segment)).join("/")}`;
14122
+ return `${httpsBase}/blob/${encodeURIComponent(ref).replace(/%2F/g, "/")}/${relPath.split(path33.sep).map((segment) => encodeURIComponent(segment)).join("/")}`;
13404
14123
  } catch {
13405
14124
  return null;
13406
14125
  }
13407
14126
  }
13408
14127
  function buildGithubIssueBody(prdPath, projectDir, prdContent) {
13409
- const relPath = path30.relative(projectDir, prdPath);
14128
+ const relPath = path33.relative(projectDir, prdPath);
13410
14129
  const blobUrl = resolveGitHubBlobUrl(projectDir, relPath);
13411
14130
  const fileLine = blobUrl ? `PRD file: [\`${relPath}\`](${blobUrl})` : `PRD file: \`${relPath}\``;
13412
14131
  return `${fileLine}
@@ -13417,17 +14136,17 @@ ${prdContent}
13417
14136
  Created via \`night-watch prd create\`.`;
13418
14137
  }
13419
14138
  async function generatePrdWithClaude(description, projectDir, model) {
13420
- const bundledTemplatePath = path30.join(TEMPLATES_DIR2, "prd-creator.md");
13421
- const installedTemplatePath = path30.join(projectDir, "instructions", "prd-creator.md");
13422
- const templatePath = fs29.existsSync(installedTemplatePath) ? installedTemplatePath : bundledTemplatePath;
13423
- if (!fs29.existsSync(templatePath)) {
14139
+ const bundledTemplatePath = path33.join(TEMPLATES_DIR2, "prd-creator.md");
14140
+ const installedTemplatePath = path33.join(projectDir, "instructions", "prd-creator.md");
14141
+ const templatePath = fs32.existsSync(installedTemplatePath) ? installedTemplatePath : bundledTemplatePath;
14142
+ if (!fs32.existsSync(templatePath)) {
13424
14143
  return null;
13425
14144
  }
13426
- const planningPrinciples = fs29.readFileSync(templatePath, "utf-8");
14145
+ const planningPrinciples = fs32.readFileSync(templatePath, "utf-8");
13427
14146
  const prompt = buildPrdPrompt(description, projectDir, planningPrinciples);
13428
14147
  const modelId = model ?? CLAUDE_MODEL_IDS.opus;
13429
14148
  const env = buildNativeClaudeEnv(process.env);
13430
- return await new Promise((resolve9) => {
14149
+ return await new Promise((resolve11) => {
13431
14150
  const child = spawn4(
13432
14151
  "claude",
13433
14152
  [
@@ -13480,9 +14199,9 @@ async function generatePrdWithClaude(description, projectDir, model) {
13480
14199
  }
13481
14200
  }
13482
14201
  process.stdout.write("\n");
13483
- resolve9(code === 0 && finalResult ? extractPrdMarkdown(finalResult) : null);
14202
+ resolve11(code === 0 && finalResult ? extractPrdMarkdown(finalResult) : null);
13484
14203
  });
13485
- child.on("error", () => resolve9(null));
14204
+ child.on("error", () => resolve11(null));
13486
14205
  });
13487
14206
  }
13488
14207
  function runGh(args, cwd) {
@@ -13491,17 +14210,17 @@ function runGh(args, cwd) {
13491
14210
  return null;
13492
14211
  }
13493
14212
  function createGithubIssue(title, prdPath, projectDir, prdContent) {
13494
- const tmpFile = path30.join(projectDir, `.prd-issue-body-${Date.now()}.tmp`);
14213
+ const tmpFile = path33.join(projectDir, `.prd-issue-body-${Date.now()}.tmp`);
13495
14214
  try {
13496
14215
  const body = buildGithubIssueBody(prdPath, projectDir, prdContent);
13497
- fs29.writeFileSync(tmpFile, body, "utf-8");
14216
+ fs32.writeFileSync(tmpFile, body, "utf-8");
13498
14217
  const baseArgs = ["issue", "create", "--title", `PRD: ${title}`, "--body-file", tmpFile];
13499
14218
  return runGh([...baseArgs, "--label", "prd"], projectDir) ?? runGh(baseArgs, projectDir);
13500
14219
  } catch {
13501
14220
  return null;
13502
14221
  } finally {
13503
14222
  try {
13504
- fs29.unlinkSync(tmpFile);
14223
+ fs32.unlinkSync(tmpFile);
13505
14224
  } catch {
13506
14225
  }
13507
14226
  }
@@ -13514,10 +14233,10 @@ function parseDependencies(content) {
13514
14233
  function isClaimActive(claimPath, maxRuntime) {
13515
14234
  const claimStaleAfter = maxRuntime > 0 ? maxRuntime : 14400;
13516
14235
  try {
13517
- if (!fs29.existsSync(claimPath)) {
14236
+ if (!fs32.existsSync(claimPath)) {
13518
14237
  return { active: false };
13519
14238
  }
13520
- const content = fs29.readFileSync(claimPath, "utf-8");
14239
+ const content = fs32.readFileSync(claimPath, "utf-8");
13521
14240
  const claim = JSON.parse(content);
13522
14241
  const age = Math.floor(Date.now() / 1e3) - claim.timestamp;
13523
14242
  if (age < claimStaleAfter) {
@@ -13532,9 +14251,9 @@ function prdCommand(program2) {
13532
14251
  const prd = program2.command("prd").description("Manage PRD files");
13533
14252
  prd.command("create").description("Generate a new PRD markdown file using Claude").argument("<name>", "PRD description").option("--number", "Add auto-numbering prefix to the filename", false).option("--model <model>", "Claude model to use (e.g. sonnet, opus, or a full model ID)").action(async (name, options) => {
13534
14253
  const projectDir = process.cwd();
13535
- const prdDir = path30.join(projectDir, resolvePrdCreateDir());
13536
- if (!fs29.existsSync(prdDir)) {
13537
- fs29.mkdirSync(prdDir, { recursive: true });
14254
+ const prdDir = path33.join(projectDir, resolvePrdCreateDir());
14255
+ if (!fs32.existsSync(prdDir)) {
14256
+ fs32.mkdirSync(prdDir, { recursive: true });
13538
14257
  }
13539
14258
  const resolvedModel = options.model ? CLAUDE_MODEL_IDS[options.model] ?? options.model : void 0;
13540
14259
  const modelLabel = resolvedModel ?? CLAUDE_MODEL_IDS.opus;
@@ -13550,13 +14269,13 @@ function prdCommand(program2) {
13550
14269
  const prdTitle = extractPrdTitle(generated) ?? name;
13551
14270
  const slug = slugify2(prdTitle);
13552
14271
  const filename = options.number ? `${String(getNextPrdNumber2(prdDir)).padStart(2, "0")}-${slug}.md` : `${slug}.md`;
13553
- const filePath = path30.join(prdDir, filename);
13554
- if (fs29.existsSync(filePath)) {
14272
+ const filePath = path33.join(prdDir, filename);
14273
+ if (fs32.existsSync(filePath)) {
13555
14274
  error(`File already exists: ${filePath}`);
13556
14275
  dim("Use a different name or remove the existing file.");
13557
14276
  process.exit(1);
13558
14277
  }
13559
- fs29.writeFileSync(filePath, generated, "utf-8");
14278
+ fs32.writeFileSync(filePath, generated, "utf-8");
13560
14279
  header("PRD Created");
13561
14280
  success(`Created: ${filePath}`);
13562
14281
  const issueUrl = createGithubIssue(prdTitle, filePath, projectDir, generated);
@@ -13569,15 +14288,15 @@ function prdCommand(program2) {
13569
14288
  prd.command("list").description("List all PRDs with status").option("--json", "Output as JSON").action(async (options) => {
13570
14289
  const projectDir = process.cwd();
13571
14290
  const config = loadConfig(projectDir);
13572
- const absolutePrdDir = path30.join(projectDir, config.prdDir);
13573
- const doneDir = path30.join(absolutePrdDir, "done");
14291
+ const absolutePrdDir = path33.join(projectDir, config.prdDir);
14292
+ const doneDir = path33.join(absolutePrdDir, "done");
13574
14293
  const pending = [];
13575
- if (fs29.existsSync(absolutePrdDir)) {
13576
- const files = fs29.readdirSync(absolutePrdDir).filter((f) => f.endsWith(".md"));
14294
+ if (fs32.existsSync(absolutePrdDir)) {
14295
+ const files = fs32.readdirSync(absolutePrdDir).filter((f) => f.endsWith(".md"));
13577
14296
  for (const file of files) {
13578
- const content = fs29.readFileSync(path30.join(absolutePrdDir, file), "utf-8");
14297
+ const content = fs32.readFileSync(path33.join(absolutePrdDir, file), "utf-8");
13579
14298
  const deps = parseDependencies(content);
13580
- const claimPath = path30.join(absolutePrdDir, file + CLAIM_FILE_EXTENSION);
14299
+ const claimPath = path33.join(absolutePrdDir, file + CLAIM_FILE_EXTENSION);
13581
14300
  const claimStatus = isClaimActive(claimPath, config.maxRuntime);
13582
14301
  pending.push({
13583
14302
  name: file,
@@ -13588,10 +14307,10 @@ function prdCommand(program2) {
13588
14307
  }
13589
14308
  }
13590
14309
  const done = [];
13591
- if (fs29.existsSync(doneDir)) {
13592
- const files = fs29.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
14310
+ if (fs32.existsSync(doneDir)) {
14311
+ const files = fs32.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
13593
14312
  for (const file of files) {
13594
- const content = fs29.readFileSync(path30.join(doneDir, file), "utf-8");
14313
+ const content = fs32.readFileSync(path33.join(doneDir, file), "utf-8");
13595
14314
  const deps = parseDependencies(content);
13596
14315
  done.push({ name: file, dependencies: deps });
13597
14316
  }
@@ -13628,7 +14347,7 @@ import blessed6 from "blessed";
13628
14347
  // src/commands/dashboard/tab-status.ts
13629
14348
  init_dist();
13630
14349
  import blessed from "blessed";
13631
- import * as fs30 from "fs";
14350
+ import * as fs33 from "fs";
13632
14351
  function sortPrdsByPriority(prds, priority) {
13633
14352
  if (priority.length === 0) return prds;
13634
14353
  const priorityMap = /* @__PURE__ */ new Map();
@@ -13724,7 +14443,7 @@ function renderLogPane(projectDir, logs) {
13724
14443
  let newestMtime = 0;
13725
14444
  for (const log of existingLogs) {
13726
14445
  try {
13727
- const stat = fs30.statSync(log.path);
14446
+ const stat = fs33.statSync(log.path);
13728
14447
  if (stat.mtimeMs > newestMtime) {
13729
14448
  newestMtime = stat.mtimeMs;
13730
14449
  newestLog = log;
@@ -14900,6 +15619,7 @@ function createSchedulesTab() {
14900
15619
  const { config } = ctx;
14901
15620
  const executorHuman = cronToHuman(config.cronSchedule);
14902
15621
  const reviewerHuman = cronToHuman(config.reviewerSchedule);
15622
+ const managerHuman = cronToHuman(config.manager.schedule);
14903
15623
  const lines = [
14904
15624
  `{bold}Executor Schedule:{/bold} ${config.cronSchedule}`,
14905
15625
  ` ${executorHuman}`,
@@ -14907,14 +15627,19 @@ function createSchedulesTab() {
14907
15627
  `{bold}Reviewer Schedule:{/bold} ${config.reviewerSchedule}`,
14908
15628
  ` ${reviewerHuman}`,
14909
15629
  "",
15630
+ `{bold}Manager Schedule:{/bold} ${config.manager.schedule}`,
15631
+ ` ${managerHuman}`,
15632
+ "",
14910
15633
  `{bold}Reviewer Enabled:{/bold} ${config.reviewerEnabled ? "{green-fg}Yes{/green-fg}" : "{red-fg}No{/red-fg}"}`,
15634
+ `{bold}Manager Enabled:{/bold} ${config.manager.enabled ? "{green-fg}Yes{/green-fg}" : "{red-fg}No{/red-fg}"}`,
14911
15635
  "",
14912
- "{#888888-fg}Keys: e:Edit Executor v:Edit Reviewer i:Install x:Uninstall R:Reinstall{/#888888-fg}"
15636
+ "{#888888-fg}Keys: e:Edit Executor v:Edit Reviewer m:Edit Manager i:Install x:Uninstall R:Reinstall{/#888888-fg}"
14913
15637
  ];
14914
15638
  scheduleBox.setContent(lines.join("\n"));
14915
15639
  }
14916
15640
  function applySchedule(ctx, field, cronExpr) {
14917
- const result = saveConfig(ctx.projectDir, { [field]: cronExpr });
15641
+ const patch = field === "manager.schedule" ? { manager: { ...ctx.config.manager, schedule: cronExpr } } : { [field]: cronExpr };
15642
+ const result = saveConfig(ctx.projectDir, patch);
14918
15643
  if (!result.success) {
14919
15644
  ctx.showMessage(`Save failed: ${result.error}`, "error");
14920
15645
  return;
@@ -14939,7 +15664,7 @@ function createSchedulesTab() {
14939
15664
  });
14940
15665
  }
14941
15666
  function showCustomCronInput(ctx, field, label2) {
14942
- const currentValue = ctx.config[field];
15667
+ const currentValue = field === "manager.schedule" ? ctx.config.manager.schedule : ctx.config[field];
14943
15668
  const inputBox = blessed3.textbox({
14944
15669
  top: "center",
14945
15670
  left: "center",
@@ -14993,7 +15718,7 @@ function createSchedulesTab() {
14993
15718
  interactive: true
14994
15719
  });
14995
15720
  selectorList.setItems(presetItems);
14996
- const currentCron = ctx.config[field];
15721
+ const currentCron = field === "manager.schedule" ? ctx.config.manager.schedule : ctx.config[field];
14997
15722
  const matchIdx = SCHEDULE_PRESETS.findIndex((p) => p.cron === currentCron);
14998
15723
  if (matchIdx >= 0) {
14999
15724
  selectorList.select(matchIdx);
@@ -15025,6 +15750,7 @@ function createSchedulesTab() {
15025
15750
  const handlers = [
15026
15751
  [["e"], () => editSchedule(ctx, "cronSchedule", "Executor Schedule")],
15027
15752
  [["v"], () => editSchedule(ctx, "reviewerSchedule", "Reviewer Schedule")],
15753
+ [["m"], () => editSchedule(ctx, "manager.schedule", "Manager Schedule")],
15028
15754
  [
15029
15755
  ["i"],
15030
15756
  () => {
@@ -15098,7 +15824,7 @@ function createSchedulesTab() {
15098
15824
  name: "Schedules",
15099
15825
  container: container2,
15100
15826
  activate(ctx) {
15101
- ctx.setFooter(" e:Executor v:Reviewer i:Install x:Uninstall R:Reinstall q:Quit");
15827
+ ctx.setFooter(" e:Executor v:Reviewer m:Manager i:Install x:Uninstall R:Reinstall q:Quit");
15102
15828
  renderCrontab(ctx);
15103
15829
  renderScheduleSettings(ctx);
15104
15830
  activeCtx = ctx;
@@ -15370,8 +16096,8 @@ function createActionsTab() {
15370
16096
  // src/commands/dashboard/tab-logs.ts
15371
16097
  init_dist();
15372
16098
  import blessed5 from "blessed";
15373
- import * as fs31 from "fs";
15374
- import * as path31 from "path";
16099
+ import * as fs34 from "fs";
16100
+ import * as path34 from "path";
15375
16101
  var LOG_NAMES = ["executor", "reviewer"];
15376
16102
  var LOG_LINES = 200;
15377
16103
  function createLogsTab() {
@@ -15412,7 +16138,7 @@ function createLogsTab() {
15412
16138
  let activeKeyHandlers = [];
15413
16139
  let activeCtx = null;
15414
16140
  function getLogPath(projectDir, logName) {
15415
- return path31.join(projectDir, "logs", `${logName}.log`);
16141
+ return path34.join(projectDir, "logs", `${logName}.log`);
15416
16142
  }
15417
16143
  function updateSelector() {
15418
16144
  const tabs = LOG_NAMES.map((name, idx) => {
@@ -15426,7 +16152,7 @@ function createLogsTab() {
15426
16152
  function loadLog(ctx) {
15427
16153
  const logName = LOG_NAMES[selectedLogIndex];
15428
16154
  const logPath = getLogPath(ctx.projectDir, logName);
15429
- if (!fs31.existsSync(logPath)) {
16155
+ if (!fs34.existsSync(logPath)) {
15430
16156
  logContent.setContent(
15431
16157
  `{yellow-fg}No ${logName}.log file found{/yellow-fg}
15432
16158
 
@@ -15436,7 +16162,7 @@ Log will appear here once the ${logName} runs.`
15436
16162
  return;
15437
16163
  }
15438
16164
  try {
15439
- const stat = fs31.statSync(logPath);
16165
+ const stat = fs34.statSync(logPath);
15440
16166
  const sizeKB = (stat.size / 1024).toFixed(1);
15441
16167
  logContent.setLabel(`[ ${logName}.log - ${sizeKB} KB ]`);
15442
16168
  } catch {
@@ -15886,13 +16612,13 @@ function doctorCommand(program2) {
15886
16612
 
15887
16613
  // src/commands/serve.ts
15888
16614
  init_dist();
15889
- import * as fs36 from "fs";
16615
+ import * as fs39 from "fs";
15890
16616
 
15891
16617
  // ../server/dist/index.js
15892
16618
  init_dist();
15893
- import * as fs35 from "fs";
15894
- import * as path37 from "path";
15895
- import { dirname as dirname11 } from "path";
16619
+ import * as fs38 from "fs";
16620
+ import * as path40 from "path";
16621
+ import { dirname as dirname12 } from "path";
15896
16622
  import { fileURLToPath as fileURLToPath5 } from "url";
15897
16623
  import cors from "cors";
15898
16624
  import express from "express";
@@ -15976,8 +16702,8 @@ function setupGracefulShutdown(server, beforeClose) {
15976
16702
 
15977
16703
  // ../server/dist/middleware/project-resolver.middleware.js
15978
16704
  init_dist();
15979
- import * as fs32 from "fs";
15980
- import * as path32 from "path";
16705
+ import * as fs35 from "fs";
16706
+ import * as path35 from "path";
15981
16707
  function resolveProject(req, res, next) {
15982
16708
  const projectId = req.params.projectId;
15983
16709
  const decodedId = decodeURIComponent(projectId).replace(/~/g, "/");
@@ -15987,7 +16713,7 @@ function resolveProject(req, res, next) {
15987
16713
  res.status(404).json({ error: `Project not found: ${decodedId}` });
15988
16714
  return;
15989
16715
  }
15990
- if (!fs32.existsSync(entry.path) || !fs32.existsSync(path32.join(entry.path, CONFIG_FILE_NAME))) {
16716
+ if (!fs35.existsSync(entry.path) || !fs35.existsSync(path35.join(entry.path, CONFIG_FILE_NAME))) {
15991
16717
  res.status(404).json({ error: `Project path invalid or missing config: ${entry.path}` });
15992
16718
  return;
15993
16719
  }
@@ -16032,8 +16758,8 @@ function startSseStatusWatcher(clients, projectDir, getConfig) {
16032
16758
 
16033
16759
  // ../server/dist/routes/action.routes.js
16034
16760
  init_dist();
16035
- import * as fs33 from "fs";
16036
- import * as path33 from "path";
16761
+ import * as fs36 from "fs";
16762
+ import * as path36 from "path";
16037
16763
  import { execSync as execSync6, spawn as spawn6 } from "child_process";
16038
16764
  import { Router } from "express";
16039
16765
 
@@ -16071,17 +16797,17 @@ function getBoardProvider(config, projectDir) {
16071
16797
  function cleanOrphanedClaims(dir) {
16072
16798
  let entries;
16073
16799
  try {
16074
- entries = fs33.readdirSync(dir, { withFileTypes: true });
16800
+ entries = fs36.readdirSync(dir, { withFileTypes: true });
16075
16801
  } catch {
16076
16802
  return;
16077
16803
  }
16078
16804
  for (const entry of entries) {
16079
- const fullPath = path33.join(dir, entry.name);
16805
+ const fullPath = path36.join(dir, entry.name);
16080
16806
  if (entry.isDirectory() && entry.name !== "done") {
16081
16807
  cleanOrphanedClaims(fullPath);
16082
16808
  } else if (entry.name.endsWith(CLAIM_FILE_EXTENSION)) {
16083
16809
  try {
16084
- fs33.unlinkSync(fullPath);
16810
+ fs36.unlinkSync(fullPath);
16085
16811
  } catch {
16086
16812
  }
16087
16813
  }
@@ -16236,19 +16962,19 @@ function createActionRouteHandlers(ctx) {
16236
16962
  res.status(400).json({ error: "Invalid PRD name" });
16237
16963
  return;
16238
16964
  }
16239
- const prdDir = path33.join(projectDir, config.prdDir);
16965
+ const prdDir = path36.join(projectDir, config.prdDir);
16240
16966
  const normalized = prdName.endsWith(".md") ? prdName : `${prdName}.md`;
16241
- const pendingPath = path33.join(prdDir, normalized);
16242
- const donePath = path33.join(prdDir, "done", normalized);
16243
- if (fs33.existsSync(pendingPath)) {
16967
+ const pendingPath = path36.join(prdDir, normalized);
16968
+ const donePath = path36.join(prdDir, "done", normalized);
16969
+ if (fs36.existsSync(pendingPath)) {
16244
16970
  res.json({ message: `"${normalized}" is already pending` });
16245
16971
  return;
16246
16972
  }
16247
- if (!fs33.existsSync(donePath)) {
16973
+ if (!fs36.existsSync(donePath)) {
16248
16974
  res.status(404).json({ error: `PRD "${normalized}" not found in done/` });
16249
16975
  return;
16250
16976
  }
16251
- fs33.renameSync(donePath, pendingPath);
16977
+ fs36.renameSync(donePath, pendingPath);
16252
16978
  res.json({ message: `Moved "${normalized}" back to pending` });
16253
16979
  } catch (error2) {
16254
16980
  res.status(500).json({
@@ -16266,11 +16992,11 @@ function createActionRouteHandlers(ctx) {
16266
16992
  res.status(409).json({ error: "Executor is actively running \u2014 use Stop instead" });
16267
16993
  return;
16268
16994
  }
16269
- if (fs33.existsSync(lockPath)) {
16270
- fs33.unlinkSync(lockPath);
16995
+ if (fs36.existsSync(lockPath)) {
16996
+ fs36.unlinkSync(lockPath);
16271
16997
  }
16272
- const prdDir = path33.join(projectDir, config.prdDir);
16273
- if (fs33.existsSync(prdDir)) {
16998
+ const prdDir = path36.join(projectDir, config.prdDir);
16999
+ if (fs36.existsSync(prdDir)) {
16274
17000
  cleanOrphanedClaims(prdDir);
16275
17001
  }
16276
17002
  broadcastSSE(ctx.getSseClients(req), "status_changed", await fetchStatusSnapshot(projectDir, config));
@@ -17038,8 +17764,8 @@ function createProjectConfigRoutes() {
17038
17764
 
17039
17765
  // ../server/dist/routes/doctor.routes.js
17040
17766
  init_dist();
17041
- import * as fs34 from "fs";
17042
- import * as path34 from "path";
17767
+ import * as fs37 from "fs";
17768
+ import * as path37 from "path";
17043
17769
  import { execSync as execSync7 } from "child_process";
17044
17770
  import { Router as Router4 } from "express";
17045
17771
  function runDoctorChecks(projectDir, config) {
@@ -17072,7 +17798,7 @@ function runDoctorChecks(projectDir, config) {
17072
17798
  });
17073
17799
  }
17074
17800
  try {
17075
- const projectName = path34.basename(projectDir);
17801
+ const projectName = path37.basename(projectDir);
17076
17802
  const marker = generateMarker(projectName);
17077
17803
  const crontabEntries = [...getEntries(marker), ...getProjectEntries(projectDir)];
17078
17804
  if (crontabEntries.length > 0) {
@@ -17095,8 +17821,8 @@ function runDoctorChecks(projectDir, config) {
17095
17821
  detail: "Failed to check crontab"
17096
17822
  });
17097
17823
  }
17098
- const configPath = path34.join(projectDir, CONFIG_FILE_NAME);
17099
- if (fs34.existsSync(configPath)) {
17824
+ const configPath = path37.join(projectDir, CONFIG_FILE_NAME);
17825
+ if (fs37.existsSync(configPath)) {
17100
17826
  checks.push({ name: "config", status: "pass", detail: "Config file exists" });
17101
17827
  } else {
17102
17828
  checks.push({
@@ -17105,9 +17831,9 @@ function runDoctorChecks(projectDir, config) {
17105
17831
  detail: "Config file not found (using defaults)"
17106
17832
  });
17107
17833
  }
17108
- const prdDir = path34.join(projectDir, config.prdDir);
17109
- if (fs34.existsSync(prdDir)) {
17110
- const prds = fs34.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
17834
+ const prdDir = path37.join(projectDir, config.prdDir);
17835
+ if (fs37.existsSync(prdDir)) {
17836
+ const prds = fs37.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
17111
17837
  checks.push({
17112
17838
  name: "prdDir",
17113
17839
  status: "pass",
@@ -17548,6 +18274,8 @@ function getLockPathForJob2(projectDir, jobId) {
17548
18274
  return prResolverLockPath(projectDir);
17549
18275
  case "merger":
17550
18276
  return mergerLockPath(projectDir);
18277
+ case "manager":
18278
+ return managerLockPath(projectDir);
17551
18279
  }
17552
18280
  }
17553
18281
  function createJobRouteHandlers(ctx) {
@@ -17674,7 +18402,7 @@ function createProjectJobRoutes() {
17674
18402
 
17675
18403
  // ../server/dist/routes/log.routes.js
17676
18404
  init_dist();
17677
- import * as path35 from "path";
18405
+ import * as path38 from "path";
17678
18406
  import { Router as Router7 } from "express";
17679
18407
  function createLogRoutes(deps) {
17680
18408
  const { projectDir } = deps;
@@ -17693,7 +18421,7 @@ function createLogRoutes(deps) {
17693
18421
  const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
17694
18422
  const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
17695
18423
  const fileName = LOG_FILE_NAMES[name] || name;
17696
- const logPath = path35.join(projectDir, LOG_DIR, `${fileName}.log`);
18424
+ const logPath = path38.join(projectDir, LOG_DIR, `${fileName}.log`);
17697
18425
  const logLines = getLastLogLines(logPath, linesToRead);
17698
18426
  res.json({ name, lines: logLines });
17699
18427
  } catch (error2) {
@@ -17719,7 +18447,7 @@ function createProjectLogRoutes() {
17719
18447
  const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
17720
18448
  const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
17721
18449
  const fileName = LOG_FILE_NAMES[name] || name;
17722
- const logPath = path35.join(projectDir, LOG_DIR, `${fileName}.log`);
18450
+ const logPath = path38.join(projectDir, LOG_DIR, `${fileName}.log`);
17723
18451
  const logLines = getLastLogLines(logPath, linesToRead);
17724
18452
  res.json({ name, lines: logLines });
17725
18453
  } catch (error2) {
@@ -17754,7 +18482,7 @@ function createProjectPrdRoutes() {
17754
18482
 
17755
18483
  // ../server/dist/routes/roadmap.routes.js
17756
18484
  init_dist();
17757
- import * as path36 from "path";
18485
+ import * as path39 from "path";
17758
18486
  import { Router as Router9 } from "express";
17759
18487
  function createRoadmapRouteHandlers(ctx) {
17760
18488
  const router = Router9({ mergeParams: true });
@@ -17764,7 +18492,7 @@ function createRoadmapRouteHandlers(ctx) {
17764
18492
  const config = ctx.getConfig(req);
17765
18493
  const projectDir = ctx.getProjectDir(req);
17766
18494
  const status = getRoadmapStatus(projectDir, config);
17767
- const prdDir = path36.join(projectDir, config.prdDir);
18495
+ const prdDir = path39.join(projectDir, config.prdDir);
17768
18496
  const state = loadRoadmapState(prdDir);
17769
18497
  res.json({
17770
18498
  ...status,
@@ -17891,6 +18619,7 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
17891
18619
  const analyticsPlan = getSchedulingPlan(projectDir, config, "analytics");
17892
18620
  const prResolverPlan = getSchedulingPlan(projectDir, config, "pr-resolver");
17893
18621
  const mergerPlan = getSchedulingPlan(projectDir, config, "merger");
18622
+ const managerPlan = getSchedulingPlan(projectDir, config, "manager");
17894
18623
  const executorInstalled = installed && config.executorEnabled !== false && hasScheduledCommand(entries, "run");
17895
18624
  const reviewerInstalled = installed && config.reviewerEnabled && hasScheduledCommand(entries, "review");
17896
18625
  const qaInstalled = installed && config.qa.enabled && hasScheduledCommand(entries, "qa");
@@ -17899,6 +18628,7 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
17899
18628
  const analyticsInstalled = installed && config.analytics.enabled && hasScheduledCommand(entries, "analytics");
17900
18629
  const prResolverInstalled = installed && (config.prResolver?.enabled ?? true) && hasScheduledCommand(entries, "resolve");
17901
18630
  const mergerInstalled = installed && (config.merger?.enabled ?? false) && hasScheduledCommand(entries, "merge");
18631
+ const managerInstalled = installed && (config.manager?.enabled ?? true) && hasScheduledCommand(entries, "manager");
17902
18632
  return {
17903
18633
  executor: {
17904
18634
  schedule: config.cronSchedule,
@@ -17964,6 +18694,14 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
17964
18694
  manualDelayMinutes: mergerPlan.manualDelayMinutes,
17965
18695
  balancedDelayMinutes: mergerPlan.balancedDelayMinutes
17966
18696
  },
18697
+ manager: {
18698
+ schedule: config.manager?.schedule ?? "15 7 * * *",
18699
+ installed: managerInstalled,
18700
+ nextRun: managerInstalled ? addDelayToIsoString(computeNextRun(config.manager?.schedule ?? "15 7 * * *"), managerPlan.totalDelayMinutes) : null,
18701
+ delayMinutes: managerPlan.totalDelayMinutes,
18702
+ manualDelayMinutes: managerPlan.manualDelayMinutes,
18703
+ balancedDelayMinutes: managerPlan.balancedDelayMinutes
18704
+ },
17967
18705
  paused: !installed,
17968
18706
  schedulingPriority: config.schedulingPriority,
17969
18707
  entries
@@ -18116,31 +18854,31 @@ function createQueueRoutes(deps) {
18116
18854
 
18117
18855
  // ../server/dist/index.js
18118
18856
  var __filename4 = fileURLToPath5(import.meta.url);
18119
- var __dirname4 = dirname11(__filename4);
18857
+ var __dirname4 = dirname12(__filename4);
18120
18858
  var JOB_RAW_BODY_LIMIT = "1mb";
18121
18859
  function setupJobRawBodyParsing(app) {
18122
18860
  app.use("/api/jobs", express.raw({ type: "*/*", limit: JOB_RAW_BODY_LIMIT }));
18123
18861
  app.use("/api/projects/:projectId/jobs", express.raw({ type: "*/*", limit: JOB_RAW_BODY_LIMIT }));
18124
18862
  }
18125
18863
  function resolveWebDistPath() {
18126
- const bundled = path37.join(__dirname4, "web");
18127
- if (fs35.existsSync(path37.join(bundled, "index.html")))
18864
+ const bundled = path40.join(__dirname4, "web");
18865
+ if (fs38.existsSync(path40.join(bundled, "index.html")))
18128
18866
  return bundled;
18129
18867
  let d = __dirname4;
18130
18868
  for (let i = 0; i < 8; i++) {
18131
- if (fs35.existsSync(path37.join(d, "turbo.json"))) {
18132
- const dev = path37.join(d, "web/dist");
18133
- if (fs35.existsSync(path37.join(dev, "index.html")))
18869
+ if (fs38.existsSync(path40.join(d, "turbo.json"))) {
18870
+ const dev = path40.join(d, "web/dist");
18871
+ if (fs38.existsSync(path40.join(dev, "index.html")))
18134
18872
  return dev;
18135
18873
  break;
18136
18874
  }
18137
- d = dirname11(d);
18875
+ d = dirname12(d);
18138
18876
  }
18139
18877
  return bundled;
18140
18878
  }
18141
18879
  function setupStaticFiles(app) {
18142
18880
  const webDistPath = resolveWebDistPath();
18143
- if (fs35.existsSync(webDistPath)) {
18881
+ if (fs38.existsSync(webDistPath)) {
18144
18882
  app.use(express.static(webDistPath));
18145
18883
  }
18146
18884
  app.use((req, res, next) => {
@@ -18148,8 +18886,8 @@ function setupStaticFiles(app) {
18148
18886
  next();
18149
18887
  return;
18150
18888
  }
18151
- const indexPath = path37.resolve(webDistPath, "index.html");
18152
- if (fs35.existsSync(indexPath)) {
18889
+ const indexPath = path40.resolve(webDistPath, "index.html");
18890
+ if (fs38.existsSync(indexPath)) {
18153
18891
  res.sendFile(indexPath, (err) => {
18154
18892
  if (err)
18155
18893
  next();
@@ -18288,7 +19026,7 @@ function createGlobalApp() {
18288
19026
  return app;
18289
19027
  }
18290
19028
  function bootContainer() {
18291
- initContainer(path37.dirname(getDbPath()));
19029
+ initContainer(path40.dirname(getDbPath()));
18292
19030
  }
18293
19031
  function startServer(projectDir, port) {
18294
19032
  bootContainer();
@@ -18341,8 +19079,8 @@ function isProcessRunning2(pid) {
18341
19079
  }
18342
19080
  function readPid(lockPath) {
18343
19081
  try {
18344
- if (!fs36.existsSync(lockPath)) return null;
18345
- const raw = fs36.readFileSync(lockPath, "utf-8").trim();
19082
+ if (!fs39.existsSync(lockPath)) return null;
19083
+ const raw = fs39.readFileSync(lockPath, "utf-8").trim();
18346
19084
  const pid = parseInt(raw, 10);
18347
19085
  return Number.isFinite(pid) ? pid : null;
18348
19086
  } catch {
@@ -18354,10 +19092,10 @@ function acquireServeLock(mode, port) {
18354
19092
  let stalePidCleaned;
18355
19093
  for (let attempt = 0; attempt < 2; attempt++) {
18356
19094
  try {
18357
- const fd = fs36.openSync(lockPath, "wx");
18358
- fs36.writeFileSync(fd, `${process.pid}
19095
+ const fd = fs39.openSync(lockPath, "wx");
19096
+ fs39.writeFileSync(fd, `${process.pid}
18359
19097
  `);
18360
- fs36.closeSync(fd);
19098
+ fs39.closeSync(fd);
18361
19099
  return { acquired: true, lockPath, stalePidCleaned };
18362
19100
  } catch (error2) {
18363
19101
  const err = error2;
@@ -18378,7 +19116,7 @@ function acquireServeLock(mode, port) {
18378
19116
  };
18379
19117
  }
18380
19118
  try {
18381
- fs36.unlinkSync(lockPath);
19119
+ fs39.unlinkSync(lockPath);
18382
19120
  if (existingPid) {
18383
19121
  stalePidCleaned = existingPid;
18384
19122
  }
@@ -18401,10 +19139,10 @@ function acquireServeLock(mode, port) {
18401
19139
  }
18402
19140
  function releaseServeLock(lockPath) {
18403
19141
  try {
18404
- if (!fs36.existsSync(lockPath)) return;
19142
+ if (!fs39.existsSync(lockPath)) return;
18405
19143
  const lockPid = readPid(lockPath);
18406
19144
  if (lockPid !== null && lockPid !== process.pid) return;
18407
- fs36.unlinkSync(lockPath);
19145
+ fs39.unlinkSync(lockPath);
18408
19146
  } catch {
18409
19147
  }
18410
19148
  }
@@ -18500,14 +19238,14 @@ function historyCommand(program2) {
18500
19238
  // src/commands/update.ts
18501
19239
  init_dist();
18502
19240
  import { spawnSync as spawnSync2 } from "child_process";
18503
- import * as fs37 from "fs";
18504
- import * as path38 from "path";
19241
+ import * as fs40 from "fs";
19242
+ import * as path41 from "path";
18505
19243
  var DEFAULT_GLOBAL_SPEC = "@jonit-dev/night-watch-cli@latest";
18506
19244
  function parseProjectDirs(projects, cwd) {
18507
19245
  if (!projects || projects.trim().length === 0) {
18508
19246
  return [cwd];
18509
19247
  }
18510
- const dirs = projects.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => path38.resolve(cwd, entry));
19248
+ const dirs = projects.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => path41.resolve(cwd, entry));
18511
19249
  return Array.from(new Set(dirs));
18512
19250
  }
18513
19251
  function shouldInstallGlobal(options) {
@@ -18549,7 +19287,7 @@ function updateCommand(program2) {
18549
19287
  }
18550
19288
  const nightWatchBin = resolveNightWatchBin();
18551
19289
  for (const projectDir of projectDirs) {
18552
- if (!fs37.existsSync(projectDir) || !fs37.statSync(projectDir).isDirectory()) {
19290
+ if (!fs40.existsSync(projectDir) || !fs40.statSync(projectDir).isDirectory()) {
18553
19291
  warn(`Skipping invalid project directory: ${projectDir}`);
18554
19292
  continue;
18555
19293
  }
@@ -18593,8 +19331,8 @@ function prdStateCommand(program2) {
18593
19331
 
18594
19332
  // src/commands/retry.ts
18595
19333
  init_dist();
18596
- import * as fs38 from "fs";
18597
- import * as path39 from "path";
19334
+ import * as fs41 from "fs";
19335
+ import * as path42 from "path";
18598
19336
  function normalizePrdName(name) {
18599
19337
  if (!name.endsWith(".md")) {
18600
19338
  return `${name}.md`;
@@ -18602,26 +19340,26 @@ function normalizePrdName(name) {
18602
19340
  return name;
18603
19341
  }
18604
19342
  function getDonePrds(doneDir) {
18605
- if (!fs38.existsSync(doneDir)) {
19343
+ if (!fs41.existsSync(doneDir)) {
18606
19344
  return [];
18607
19345
  }
18608
- return fs38.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
19346
+ return fs41.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
18609
19347
  }
18610
19348
  function retryCommand(program2) {
18611
19349
  program2.command("retry <prdName>").description("Move a completed PRD from done/ back to pending").action((prdName) => {
18612
19350
  const projectDir = process.cwd();
18613
19351
  const config = loadConfig(projectDir);
18614
- const prdDir = path39.join(projectDir, config.prdDir);
18615
- const doneDir = path39.join(prdDir, "done");
19352
+ const prdDir = path42.join(projectDir, config.prdDir);
19353
+ const doneDir = path42.join(prdDir, "done");
18616
19354
  const normalizedPrdName = normalizePrdName(prdName);
18617
- const pendingPath = path39.join(prdDir, normalizedPrdName);
18618
- if (fs38.existsSync(pendingPath)) {
19355
+ const pendingPath = path42.join(prdDir, normalizedPrdName);
19356
+ if (fs41.existsSync(pendingPath)) {
18619
19357
  info(`"${normalizedPrdName}" is already pending, nothing to retry.`);
18620
19358
  return;
18621
19359
  }
18622
- const donePath = path39.join(doneDir, normalizedPrdName);
18623
- if (fs38.existsSync(donePath)) {
18624
- fs38.renameSync(donePath, pendingPath);
19360
+ const donePath = path42.join(doneDir, normalizedPrdName);
19361
+ if (fs41.existsSync(donePath)) {
19362
+ fs41.renameSync(donePath, pendingPath);
18625
19363
  success(`Moved "${normalizedPrdName}" back to pending.`);
18626
19364
  dim(`From: ${donePath}`);
18627
19365
  dim(`To: ${pendingPath}`);
@@ -18873,7 +19611,7 @@ function prdsCommand(program2) {
18873
19611
 
18874
19612
  // src/commands/cancel.ts
18875
19613
  init_dist();
18876
- import * as fs39 from "fs";
19614
+ import * as fs42 from "fs";
18877
19615
  import * as readline2 from "readline";
18878
19616
  function getLockFilePaths2(projectDir) {
18879
19617
  const runtimeKey = projectRuntimeKey(projectDir);
@@ -18890,16 +19628,16 @@ async function promptConfirmation(prompt) {
18890
19628
  input: process.stdin,
18891
19629
  output: process.stdout
18892
19630
  });
18893
- return new Promise((resolve9) => {
19631
+ return new Promise((resolve11) => {
18894
19632
  rl.question(`${prompt} `, (answer) => {
18895
19633
  rl.close();
18896
19634
  const normalized = answer.toLowerCase().trim();
18897
- resolve9(normalized === "y" || normalized === "yes");
19635
+ resolve11(normalized === "y" || normalized === "yes");
18898
19636
  });
18899
19637
  });
18900
19638
  }
18901
19639
  function sleep2(ms) {
18902
- return new Promise((resolve9) => setTimeout(resolve9, ms));
19640
+ return new Promise((resolve11) => setTimeout(resolve11, ms));
18903
19641
  }
18904
19642
  function isProcessRunning3(pid) {
18905
19643
  try {
@@ -18920,7 +19658,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
18920
19658
  const pid = lockStatus.pid;
18921
19659
  if (!lockStatus.running) {
18922
19660
  try {
18923
- fs39.unlinkSync(lockPath);
19661
+ fs42.unlinkSync(lockPath);
18924
19662
  return {
18925
19663
  success: true,
18926
19664
  message: `${processType} is not running (cleaned up stale lock file for PID ${pid})`,
@@ -18958,7 +19696,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
18958
19696
  await sleep2(3e3);
18959
19697
  if (!isProcessRunning3(pid)) {
18960
19698
  try {
18961
- fs39.unlinkSync(lockPath);
19699
+ fs42.unlinkSync(lockPath);
18962
19700
  } catch {
18963
19701
  }
18964
19702
  return {
@@ -18993,7 +19731,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
18993
19731
  await sleep2(500);
18994
19732
  if (!isProcessRunning3(pid)) {
18995
19733
  try {
18996
- fs39.unlinkSync(lockPath);
19734
+ fs42.unlinkSync(lockPath);
18997
19735
  } catch {
18998
19736
  }
18999
19737
  return {
@@ -19054,31 +19792,31 @@ function cancelCommand(program2) {
19054
19792
 
19055
19793
  // src/commands/slice.ts
19056
19794
  init_dist();
19057
- import * as fs40 from "fs";
19058
- import * as path40 from "path";
19795
+ import * as fs43 from "fs";
19796
+ import * as path43 from "path";
19059
19797
  function plannerLockPath2(projectDir) {
19060
19798
  return `${LOCK_FILE_PREFIX}slicer-${projectRuntimeKey(projectDir)}.lock`;
19061
19799
  }
19062
19800
  function acquirePlannerLock(projectDir) {
19063
19801
  const lockFile = plannerLockPath2(projectDir);
19064
- if (fs40.existsSync(lockFile)) {
19065
- const pidRaw = fs40.readFileSync(lockFile, "utf-8").trim();
19802
+ if (fs43.existsSync(lockFile)) {
19803
+ const pidRaw = fs43.readFileSync(lockFile, "utf-8").trim();
19066
19804
  const pid = parseInt(pidRaw, 10);
19067
19805
  if (!Number.isNaN(pid) && isProcessRunning(pid)) {
19068
19806
  return { acquired: false, lockFile, pid };
19069
19807
  }
19070
19808
  try {
19071
- fs40.unlinkSync(lockFile);
19809
+ fs43.unlinkSync(lockFile);
19072
19810
  } catch {
19073
19811
  }
19074
19812
  }
19075
- fs40.writeFileSync(lockFile, String(process.pid));
19813
+ fs43.writeFileSync(lockFile, String(process.pid));
19076
19814
  return { acquired: true, lockFile };
19077
19815
  }
19078
19816
  function releasePlannerLock(lockFile) {
19079
19817
  try {
19080
- if (fs40.existsSync(lockFile)) {
19081
- fs40.unlinkSync(lockFile);
19818
+ if (fs43.existsSync(lockFile)) {
19819
+ fs43.unlinkSync(lockFile);
19082
19820
  }
19083
19821
  } catch {
19084
19822
  }
@@ -19087,12 +19825,12 @@ function resolvePlannerIssueColumn(config) {
19087
19825
  return config.roadmapScanner.issueColumn === "Draft" ? "Draft" : "Ready";
19088
19826
  }
19089
19827
  function buildPlannerIssueBody(projectDir, config, result) {
19090
- const relativePrdPath = path40.join(config.prdDir, result.file ?? "").replace(/\\/g, "/");
19091
- const absolutePrdPath = path40.join(projectDir, config.prdDir, result.file ?? "");
19828
+ const relativePrdPath = path43.join(config.prdDir, result.file ?? "").replace(/\\/g, "/");
19829
+ const absolutePrdPath = path43.join(projectDir, config.prdDir, result.file ?? "");
19092
19830
  const sourceItem = result.item;
19093
19831
  let prdContent;
19094
19832
  try {
19095
- prdContent = fs40.readFileSync(absolutePrdPath, "utf-8");
19833
+ prdContent = fs43.readFileSync(absolutePrdPath, "utf-8");
19096
19834
  } catch {
19097
19835
  prdContent = `Unable to read generated PRD file at \`${relativePrdPath}\`.`;
19098
19836
  }
@@ -19132,10 +19870,10 @@ async function createPlannerIssue(projectDir, config, result) {
19132
19870
  return { created: false, skippedReason: "board-not-configured" };
19133
19871
  }
19134
19872
  const issueTitle = `PRD: ${result.item.title}`;
19135
- const normalizeTitle = (t) => t.replace(/^PRD:\s*/i, "").trim().toLowerCase();
19873
+ const normalizeTitle2 = (t) => t.replace(/^PRD:\s*/i, "").trim().toLowerCase();
19136
19874
  const existingIssues = await provider.getAllIssues();
19137
19875
  const existing = existingIssues.find(
19138
- (issue2) => normalizeTitle(issue2.title) === normalizeTitle(result.item.title)
19876
+ (issue2) => normalizeTitle2(issue2.title) === normalizeTitle2(result.item.title)
19139
19877
  );
19140
19878
  if (existing) {
19141
19879
  return {
@@ -19322,7 +20060,7 @@ function sliceCommand(program2) {
19322
20060
  if (!options.dryRun && result.sliced) {
19323
20061
  await sendNotifications(config, {
19324
20062
  event: "run_succeeded",
19325
- projectName: path40.basename(projectDir),
20063
+ projectName: path43.basename(projectDir),
19326
20064
  exitCode,
19327
20065
  provider: config.provider,
19328
20066
  prTitle: result.item?.title
@@ -19330,7 +20068,7 @@ function sliceCommand(program2) {
19330
20068
  } else if (!options.dryRun && !nothingPending) {
19331
20069
  await sendNotifications(config, {
19332
20070
  event: "run_failed",
19333
- projectName: path40.basename(projectDir),
20071
+ projectName: path43.basename(projectDir),
19334
20072
  exitCode,
19335
20073
  provider: config.provider
19336
20074
  });
@@ -19363,20 +20101,20 @@ function sliceCommand(program2) {
19363
20101
  // src/commands/state.ts
19364
20102
  init_dist();
19365
20103
  import * as os9 from "os";
19366
- import * as path41 from "path";
20104
+ import * as path44 from "path";
19367
20105
  import chalk5 from "chalk";
19368
20106
  import { Command } from "commander";
19369
20107
  function createStateCommand() {
19370
20108
  const state = new Command("state");
19371
20109
  state.description("Manage Night Watch state");
19372
20110
  state.command("migrate").description("Migrate legacy JSON state files to SQLite").option("--dry-run", "Show what would be migrated without making changes").action((opts) => {
19373
- const nightWatchHome = process.env.NIGHT_WATCH_HOME || path41.join(os9.homedir(), GLOBAL_CONFIG_DIR);
20111
+ const nightWatchHome = process.env.NIGHT_WATCH_HOME || path44.join(os9.homedir(), GLOBAL_CONFIG_DIR);
19374
20112
  if (opts.dryRun) {
19375
20113
  console.log(chalk5.cyan("Dry-run mode: no changes will be made.\n"));
19376
20114
  console.log(`Legacy JSON files that would be migrated from: ${chalk5.bold(nightWatchHome)}`);
19377
- console.log(` ${path41.join(nightWatchHome, "projects.json")}`);
19378
- console.log(` ${path41.join(nightWatchHome, "history.json")}`);
19379
- console.log(` ${path41.join(nightWatchHome, "prd-states.json")}`);
20115
+ console.log(` ${path44.join(nightWatchHome, "projects.json")}`);
20116
+ console.log(` ${path44.join(nightWatchHome, "history.json")}`);
20117
+ console.log(` ${path44.join(nightWatchHome, "prd-states.json")}`);
19380
20118
  console.log(` <project>/<prdDir>/.roadmap-state.json (per project)`);
19381
20119
  console.log(chalk5.dim("\nRun without --dry-run to apply the migration."));
19382
20120
  return;
@@ -19414,8 +20152,8 @@ function createStateCommand() {
19414
20152
  init_dist();
19415
20153
  init_dist();
19416
20154
  import { execFileSync as execFileSync6 } from "child_process";
19417
- import * as fs41 from "fs";
19418
- import * as path42 from "path";
20155
+ import * as fs44 from "fs";
20156
+ import * as path45 from "path";
19419
20157
  import * as readline3 from "readline";
19420
20158
  import chalk6 from "chalk";
19421
20159
  async function run(fn) {
@@ -19437,7 +20175,7 @@ function getProvider(config, cwd) {
19437
20175
  return createBoardProvider(bp, cwd);
19438
20176
  }
19439
20177
  function defaultBoardTitle(cwd) {
19440
- return `${path42.basename(cwd)} Night Watch`;
20178
+ return `${path45.basename(cwd)} Night Watch`;
19441
20179
  }
19442
20180
  async function ensureBoardConfigured(config, cwd, provider, options) {
19443
20181
  if (config.boardProvider?.projectNumber) {
@@ -19469,10 +20207,10 @@ async function confirmPrompt(question) {
19469
20207
  input: process.stdin,
19470
20208
  output: process.stdout
19471
20209
  });
19472
- return new Promise((resolve9) => {
20210
+ return new Promise((resolve11) => {
19473
20211
  rl.question(question, (answer) => {
19474
20212
  rl.close();
19475
- resolve9(answer.trim().toLowerCase() === "y");
20213
+ resolve11(answer.trim().toLowerCase() === "y");
19476
20214
  });
19477
20215
  });
19478
20216
  }
@@ -19638,11 +20376,11 @@ function boardCommand(program2) {
19638
20376
  let body = options.body ?? "";
19639
20377
  if (options.bodyFile) {
19640
20378
  const filePath = options.bodyFile;
19641
- if (!fs41.existsSync(filePath)) {
20379
+ if (!fs44.existsSync(filePath)) {
19642
20380
  console.error(`File not found: ${filePath}`);
19643
20381
  process.exit(1);
19644
20382
  }
19645
- body = fs41.readFileSync(filePath, "utf-8");
20383
+ body = fs44.readFileSync(filePath, "utf-8");
19646
20384
  }
19647
20385
  const labels = [];
19648
20386
  if (options.label) {
@@ -19883,12 +20621,12 @@ function boardCommand(program2) {
19883
20621
  const config = loadConfig(cwd);
19884
20622
  const provider = getProvider(config, cwd);
19885
20623
  await ensureBoardConfigured(config, cwd, provider);
19886
- const roadmapPath = options.roadmap ?? path42.join(cwd, "ROADMAP.md");
19887
- if (!fs41.existsSync(roadmapPath)) {
20624
+ const roadmapPath = options.roadmap ?? path45.join(cwd, "ROADMAP.md");
20625
+ if (!fs44.existsSync(roadmapPath)) {
19888
20626
  console.error(`Roadmap file not found: ${roadmapPath}`);
19889
20627
  process.exit(1);
19890
20628
  }
19891
- const roadmapContent = fs41.readFileSync(roadmapPath, "utf-8");
20629
+ const roadmapContent = fs44.readFileSync(roadmapPath, "utf-8");
19892
20630
  const items = parseRoadmap(roadmapContent);
19893
20631
  const uncheckedItems = getUncheckedItems(items);
19894
20632
  if (uncheckedItems.length === 0) {
@@ -20012,7 +20750,7 @@ function boardCommand(program2) {
20012
20750
  // src/commands/queue.ts
20013
20751
  init_dist();
20014
20752
  init_dist();
20015
- import * as path43 from "path";
20753
+ import * as path46 from "path";
20016
20754
  import { spawn as spawn8 } from "child_process";
20017
20755
  import chalk7 from "chalk";
20018
20756
  import { Command as Command2 } from "commander";
@@ -20025,7 +20763,8 @@ var VALID_JOB_TYPES2 = [
20025
20763
  "slicer",
20026
20764
  "planner",
20027
20765
  "pr-resolver",
20028
- "merger"
20766
+ "merger",
20767
+ "manager"
20029
20768
  ];
20030
20769
  function formatTimestamp(unixTs) {
20031
20770
  if (unixTs === null) return "-";
@@ -20148,7 +20887,7 @@ function createQueueCommand() {
20148
20887
  process.exit(1);
20149
20888
  }
20150
20889
  }
20151
- const projectName = path43.basename(projectDir);
20890
+ const projectName = path46.basename(projectDir);
20152
20891
  const queueConfig = loadConfig(projectDir).queue;
20153
20892
  if (isJobPaused(projectDir, jobType)) {
20154
20893
  logger6.info(`Skipping enqueue for paused job: ${jobType}`);
@@ -20166,7 +20905,7 @@ function createQueueCommand() {
20166
20905
  });
20167
20906
  queue.command("resolve-key").description("Resolve the provider bucket key for a given project and job type").requiredOption("--project <dir>", "Project directory").requiredOption(
20168
20907
  "--job-type <type>",
20169
- "Job type (executor, reviewer, qa, audit, slicer, planner, pr-resolver, merger)"
20908
+ "Job type (executor, reviewer, qa, audit, slicer, planner, pr-resolver, merger, manager)"
20170
20909
  ).action((opts) => {
20171
20910
  try {
20172
20911
  const config = loadConfig(opts.project);
@@ -20249,7 +20988,7 @@ function createQueueCommand() {
20249
20988
  if (isJobPaused(projectDir, jobType)) {
20250
20989
  process.exit(2);
20251
20990
  }
20252
- const projectName = path43.basename(projectDir);
20991
+ const projectName = path46.basename(projectDir);
20253
20992
  const callerPid = opts.pid ? parseInt(opts.pid, 10) : void 0;
20254
20993
  const result = claimJobSlot(
20255
20994
  projectDir,
@@ -20316,7 +21055,8 @@ var QUEUE_MARKER_KEYS = /* @__PURE__ */ new Set([
20316
21055
  "NW_AUTO_MERGE",
20317
21056
  "NW_AUTO_MERGE_METHOD",
20318
21057
  "NW_MAX_RUNTIME",
20319
- "NW_QA_MAX_RUNTIME"
21058
+ "NW_QA_MAX_RUNTIME",
21059
+ "NW_MANAGER_MAX_RUNTIME"
20320
21060
  ]);
20321
21061
  function filterQueueMarkers(envJson) {
20322
21062
  const result = {};
@@ -20345,6 +21085,8 @@ function getScriptNameForJobType(jobType) {
20345
21085
  return "night-watch-pr-resolver-cron.sh";
20346
21086
  case "merger":
20347
21087
  return "night-watch-merger-cron.sh";
21088
+ case "manager":
21089
+ return "night-watch-manager-cron.sh";
20348
21090
  default:
20349
21091
  return null;
20350
21092
  }
@@ -20382,7 +21124,7 @@ function notifyCommand(program2) {
20382
21124
 
20383
21125
  // src/commands/summary.ts
20384
21126
  init_dist();
20385
- import path44 from "path";
21127
+ import path47 from "path";
20386
21128
  import chalk8 from "chalk";
20387
21129
  function formatDuration2(seconds) {
20388
21130
  if (seconds === null) return "-";
@@ -20412,7 +21154,7 @@ function formatJobStatus(status) {
20412
21154
  return chalk8.dim(status);
20413
21155
  }
20414
21156
  function getProjectName2(projectPath) {
20415
- return path44.basename(projectPath) || projectPath;
21157
+ return path47.basename(projectPath) || projectPath;
20416
21158
  }
20417
21159
  function formatProvider(providerKey) {
20418
21160
  return providerKey.split(":")[0] || providerKey;
@@ -20531,7 +21273,7 @@ function summaryCommand(program2) {
20531
21273
  // src/commands/resolve.ts
20532
21274
  init_dist();
20533
21275
  import { execFileSync as execFileSync7 } from "child_process";
20534
- import * as path45 from "path";
21276
+ import * as path48 from "path";
20535
21277
  function buildEnvVars6(config, options) {
20536
21278
  const env = buildBaseEnvVars(config, "pr-resolver", options.dryRun);
20537
21279
  env.NW_PR_RESOLVER_MAX_RUNTIME = String(config.prResolver.maxRuntime);
@@ -20684,7 +21426,7 @@ ${stderr}`);
20684
21426
  }
20685
21427
  await sendNotifications(config, {
20686
21428
  event: notificationEvent,
20687
- projectName: path45.basename(projectDir),
21429
+ projectName: path48.basename(projectDir),
20688
21430
  exitCode,
20689
21431
  provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL)
20690
21432
  });
@@ -20699,7 +21441,7 @@ ${stderr}`);
20699
21441
 
20700
21442
  // src/commands/merge.ts
20701
21443
  init_dist();
20702
- import * as path46 from "path";
21444
+ import * as path49 from "path";
20703
21445
  function buildEnvVars7(config, options) {
20704
21446
  const env = buildBaseEnvVars(config, "merger", options.dryRun);
20705
21447
  env.NW_MERGER_MAX_RUNTIME = String(config.merger.maxRuntime);
@@ -20835,7 +21577,7 @@ ${stderr}`);
20835
21577
  if (notificationEvent) {
20836
21578
  await sendNotifications(config, {
20837
21579
  event: notificationEvent,
20838
- projectName: path46.basename(projectDir),
21580
+ projectName: path49.basename(projectDir),
20839
21581
  exitCode,
20840
21582
  provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL)
20841
21583
  });
@@ -20849,12 +21591,208 @@ ${stderr}`);
20849
21591
  });
20850
21592
  }
20851
21593
 
21594
+ // src/commands/manager.ts
21595
+ init_dist();
21596
+ import * as path50 from "path";
21597
+ function resolveRunManager() {
21598
+ const runManager2 = runManager;
21599
+ if (typeof runManager2 !== "function") {
21600
+ throw new Error(
21601
+ "Manager runner is not available in @night-watch/core. Update core to include runManager(projectDir, config, options)."
21602
+ );
21603
+ }
21604
+ return runManager2;
21605
+ }
21606
+ function writeJson(value) {
21607
+ process.stdout.write(`${JSON.stringify(value, null, 2)}
21608
+ `);
21609
+ }
21610
+ function parseTimeout(timeout) {
21611
+ if (!timeout) return void 0;
21612
+ const parsed = parseInt(timeout, 10);
21613
+ return Number.isNaN(parsed) || parsed < 0 ? void 0 : parsed;
21614
+ }
21615
+ function buildManagerRunOptions(options) {
21616
+ const timeout = parseTimeout(options.timeout);
21617
+ return {
21618
+ dryRun: options.dryRun === true,
21619
+ ...timeout !== void 0 ? { timeout } : {},
21620
+ ...options.provider ? { provider: options.provider } : {}
21621
+ };
21622
+ }
21623
+ function applyManagerCliOverrides(config, options) {
21624
+ const timeout = parseTimeout(options.timeout);
21625
+ let overridden = config;
21626
+ if (timeout !== void 0) {
21627
+ overridden = {
21628
+ ...overridden,
21629
+ manager: {
21630
+ ...overridden.manager,
21631
+ maxRuntime: timeout
21632
+ }
21633
+ };
21634
+ }
21635
+ if (options.provider) {
21636
+ overridden = {
21637
+ ...overridden,
21638
+ _cliProviderOverride: options.provider
21639
+ };
21640
+ }
21641
+ return overridden;
21642
+ }
21643
+ function getManagerConfig(config) {
21644
+ return config.manager;
21645
+ }
21646
+ function buildJsonResult(result, options) {
21647
+ if (result && typeof result === "object" && !Array.isArray(result)) {
21648
+ return {
21649
+ ...result,
21650
+ dryRun: options.dryRun
21651
+ };
21652
+ }
21653
+ return {
21654
+ dryRun: options.dryRun,
21655
+ result
21656
+ };
21657
+ }
21658
+ function resultExitCode(result) {
21659
+ if (!result || typeof result !== "object") return 0;
21660
+ const record = result;
21661
+ if (record.ok === false || record.success === false || typeof record.error === "string") {
21662
+ return 1;
21663
+ }
21664
+ return 0;
21665
+ }
21666
+ async function sendManagerNotifications(config, projectDir, result) {
21667
+ if (!result || typeof result !== "object") return;
21668
+ const decisions = result.notificationDecisions;
21669
+ if (!Array.isArray(decisions)) return;
21670
+ for (const decision of decisions) {
21671
+ if (!decision || typeof decision !== "object") continue;
21672
+ const item = decision;
21673
+ if (!item.shouldNotify || !item.event) continue;
21674
+ await sendNotifications(config, {
21675
+ event: item.event,
21676
+ projectName: path50.basename(projectDir),
21677
+ provider: resolveJobProvider(config, "manager"),
21678
+ exitCode: 0,
21679
+ failureReason: item.title,
21680
+ failureDetail: item.body
21681
+ });
21682
+ }
21683
+ }
21684
+ function printHumanResult(result, options) {
21685
+ const payload = buildJsonResult(result, options);
21686
+ header(options.dryRun ? "Dry Run: Manager" : "Manager Result");
21687
+ const table = createTable({ head: ["Metric", "Value"] });
21688
+ for (const key of [
21689
+ "summary",
21690
+ "findings",
21691
+ "createdIssues",
21692
+ "createdDrafts",
21693
+ "skippedDuplicates",
21694
+ "blockedItems"
21695
+ ]) {
21696
+ const value = payload[key];
21697
+ if (value === void 0) continue;
21698
+ table.push([key, Array.isArray(value) ? String(value.length) : String(value)]);
21699
+ }
21700
+ console.log(table.length > 0 ? table.toString() : JSON.stringify(payload, null, 2));
21701
+ }
21702
+ function managerCommand(program2) {
21703
+ program2.command("manager").description("Run Manager to analyze roadmap, board, job status, and docs alignment").option("--dry-run", "Analyze without writing memory, docs, board issues, or notifications").option("--json", "Output structured JSON").option("--timeout <seconds>", "Override max runtime in seconds").option("--provider <string>", "AI provider to use (claude or codex)").action(async (options) => {
21704
+ const projectDir = process.cwd();
21705
+ let config = loadConfig(projectDir);
21706
+ config = applyManagerCliOverrides(config, options);
21707
+ const managerConfig = getManagerConfig(config);
21708
+ const runOptions = buildManagerRunOptions(options);
21709
+ if (!managerConfig.enabled && !runOptions.dryRun) {
21710
+ if (options.json) {
21711
+ writeJson({ dryRun: false, skipped: true, reason: "manager-disabled" });
21712
+ } else {
21713
+ info("Manager is disabled in config; skipping run.");
21714
+ }
21715
+ process.exit(0);
21716
+ }
21717
+ const startedAt = Date.now();
21718
+ let exitCode = 0;
21719
+ const run2 = async () => {
21720
+ if (!runOptions.dryRun) {
21721
+ await maybeApplyCronSchedulingDelay(config, "manager", projectDir);
21722
+ }
21723
+ const runner = resolveRunManager();
21724
+ return runner(projectDir, config, runOptions);
21725
+ };
21726
+ try {
21727
+ const spinner = options.json ? null : createSpinner("Running Manager...");
21728
+ spinner?.start();
21729
+ const result = await run2();
21730
+ exitCode = resultExitCode(result);
21731
+ if (!runOptions.dryRun) {
21732
+ await sendManagerNotifications(config, projectDir, result);
21733
+ try {
21734
+ recordJobOutcome({
21735
+ config,
21736
+ exitCode,
21737
+ finishedAt: Date.now(),
21738
+ jobType: "manager",
21739
+ metadata: buildJsonResult(result, runOptions),
21740
+ projectDir,
21741
+ providerKey: resolveJobProvider(config, "manager"),
21742
+ startedAt,
21743
+ stdout: typeof result === "string" ? result : JSON.stringify(result)
21744
+ });
21745
+ } catch {
21746
+ }
21747
+ }
21748
+ if (options.json) {
21749
+ writeJson(buildJsonResult(result, runOptions));
21750
+ } else {
21751
+ if (exitCode === 0) {
21752
+ spinner?.succeed("Manager completed successfully");
21753
+ } else {
21754
+ spinner?.fail("Manager completed with errors");
21755
+ }
21756
+ printHumanResult(result, runOptions);
21757
+ }
21758
+ } catch (err) {
21759
+ const message = err instanceof Error ? err.message : String(err);
21760
+ if (!runOptions.dryRun) {
21761
+ try {
21762
+ recordJobOutcome({
21763
+ config,
21764
+ exitCode: 1,
21765
+ finishedAt: Date.now(),
21766
+ jobType: "manager",
21767
+ metadata: { error: message },
21768
+ projectDir,
21769
+ providerKey: resolveJobProvider(config, "manager"),
21770
+ startedAt,
21771
+ stderr: message
21772
+ });
21773
+ } catch {
21774
+ }
21775
+ }
21776
+ if (options.json) {
21777
+ process.stderr.write(
21778
+ `${JSON.stringify({ dryRun: runOptions.dryRun, ok: false, error: message }, null, 2)}
21779
+ `
21780
+ );
21781
+ } else {
21782
+ error(`Manager failed: ${message}`);
21783
+ }
21784
+ process.exit(1);
21785
+ }
21786
+ process.exit(exitCode);
21787
+ });
21788
+ }
21789
+
20852
21790
  // src/commands/agent.ts
20853
21791
  init_dist();
20854
21792
  var SCHEMA_VERSION2 = 1;
20855
21793
  var JSON_OPTION = "--json";
20856
21794
  var JSON_OPTION_DESCRIPTION = "Output as JSON";
20857
- function writeJson(value) {
21795
+ function writeJson2(value) {
20858
21796
  process.stdout.write(`${JSON.stringify(value, null, 2)}
20859
21797
  `);
20860
21798
  }
@@ -21011,7 +21949,7 @@ function normalizeJobType(job) {
21011
21949
  function agentCommand(program2) {
21012
21950
  const agent = program2.command("agent").description("Machine-readable agent operations");
21013
21951
  agent.command("status").description("Print a stable machine-readable project snapshot").requiredOption(JSON_OPTION, "Output status as JSON").action(async () => {
21014
- writeJson(await buildAgentStatus(process.cwd()));
21952
+ writeJson2(await buildAgentStatus(process.cwd()));
21015
21953
  });
21016
21954
  }
21017
21955
  function configCommand(program2) {
@@ -21019,18 +21957,18 @@ function configCommand(program2) {
21019
21957
  config.command("list").description("Print resolved config").option(JSON_OPTION, JSON_OPTION_DESCRIPTION).action((options) => {
21020
21958
  const value = loadConfig(process.cwd());
21021
21959
  if (options.json) {
21022
- writeJson({ schemaVersion: SCHEMA_VERSION2, config: value });
21960
+ writeJson2({ schemaVersion: SCHEMA_VERSION2, config: value });
21023
21961
  } else {
21024
- writeJson(value);
21962
+ writeJson2(value);
21025
21963
  }
21026
21964
  });
21027
21965
  config.command("get <path>").description("Read a resolved config value by dot path").option(JSON_OPTION, JSON_OPTION_DESCRIPTION).action((dotPath, options) => {
21028
21966
  try {
21029
21967
  const result = getConfigValue(process.cwd(), dotPath);
21030
21968
  if (options.json) {
21031
- writeJson({ schemaVersion: SCHEMA_VERSION2, ...result });
21969
+ writeJson2({ schemaVersion: SCHEMA_VERSION2, ...result });
21032
21970
  } else {
21033
- writeJson(result.value);
21971
+ writeJson2(result.value);
21034
21972
  }
21035
21973
  } catch (error2) {
21036
21974
  fail(error2 instanceof Error ? error2.message : String(error2), options);
@@ -21040,7 +21978,7 @@ function configCommand(program2) {
21040
21978
  try {
21041
21979
  const result = setConfigValue(process.cwd(), dotPath, parseConfigValue(rawValue));
21042
21980
  if (options.json) {
21043
- writeJson({ schemaVersion: SCHEMA_VERSION2, ok: true, ...result });
21981
+ writeJson2({ schemaVersion: SCHEMA_VERSION2, ok: true, ...result });
21044
21982
  } else {
21045
21983
  process.stdout.write(`Updated ${result.path}
21046
21984
  `);
@@ -21056,7 +21994,7 @@ function healthCommand(program2) {
21056
21994
  const snapshot = await fetchStatusSnapshot(process.cwd(), config);
21057
21995
  const health = buildHealth(snapshot, config);
21058
21996
  if (options.json) {
21059
- writeJson(health);
21997
+ writeJson2(health);
21060
21998
  } else {
21061
21999
  for (const check of health.checks) {
21062
22000
  process.stdout.write(`${check.ok ? "ok" : "fail"} ${check.name}: ${check.message}
@@ -21075,7 +22013,7 @@ function jobCommand(program2) {
21075
22013
  const jobType = normalizeJobType(jobName);
21076
22014
  const result = setConfigValue(process.cwd(), `pausedJobs.${jobType}`, true);
21077
22015
  if (options.json) {
21078
- writeJson({
22016
+ writeJson2({
21079
22017
  schemaVersion: SCHEMA_VERSION2,
21080
22018
  ok: true,
21081
22019
  job: jobType,
@@ -21094,7 +22032,7 @@ function jobCommand(program2) {
21094
22032
  const jobType = normalizeJobType(jobName);
21095
22033
  const result = setConfigValue(process.cwd(), `pausedJobs.${jobType}`, false);
21096
22034
  if (options.json) {
21097
- writeJson({
22035
+ writeJson2({
21098
22036
  schemaVersion: SCHEMA_VERSION2,
21099
22037
  ok: true,
21100
22038
  job: jobType,
@@ -21121,17 +22059,17 @@ function jobCommand(program2) {
21121
22059
 
21122
22060
  // src/cli.ts
21123
22061
  var __filename5 = fileURLToPath6(import.meta.url);
21124
- var __dirname5 = dirname12(__filename5);
22062
+ var __dirname5 = dirname13(__filename5);
21125
22063
  function findPackageRoot(dir) {
21126
22064
  let d = dir;
21127
22065
  for (let i = 0; i < 5; i++) {
21128
- if (existsSync34(join38(d, "package.json"))) return d;
21129
- d = dirname12(d);
22066
+ if (existsSync36(join39(d, "package.json"))) return d;
22067
+ d = dirname13(d);
21130
22068
  }
21131
22069
  return dir;
21132
22070
  }
21133
22071
  var packageRoot = findPackageRoot(__dirname5);
21134
- var packageJson = JSON.parse(readFileSync21(join38(packageRoot, "package.json"), "utf-8"));
22072
+ var packageJson = JSON.parse(readFileSync23(join39(packageRoot, "package.json"), "utf-8"));
21135
22073
  var program = new Command3();
21136
22074
  program.name("night-watch").description("Autonomous PRD execution using Claude CLI + cron").version(packageJson.version);
21137
22075
  initCommand(program);
@@ -21163,6 +22101,7 @@ notifyCommand(program);
21163
22101
  summaryCommand(program);
21164
22102
  resolveCommand(program);
21165
22103
  mergeCommand(program);
22104
+ managerCommand(program);
21166
22105
  agentCommand(program);
21167
22106
  configCommand(program);
21168
22107
  healthCommand(program);