@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 +1306 -367
- package/dist/cli.js.map +1 -1
- package/dist/commands/dashboard/tab-schedules.d.ts +1 -1
- package/dist/commands/dashboard/tab-schedules.d.ts.map +1 -1
- package/dist/commands/dashboard/tab-schedules.js +15 -6
- package/dist/commands/dashboard/tab-schedules.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +26 -1
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +14 -4
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/manager.d.ts +23 -0
- package/dist/commands/manager.d.ts.map +1 -0
- package/dist/commands/manager.js +220 -0
- package/dist/commands/manager.js.map +1 -0
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +5 -1
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +2 -0
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/scripts/night-watch-manager-cron.sh +61 -0
- package/dist/web/assets/index-DatF4suf.css +1 -0
- package/dist/web/assets/index-Q3IYCcdZ.js +447 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
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((
|
|
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((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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((
|
|
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
|
-
|
|
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
|
|
10865
|
+
import { existsSync as existsSync36, readFileSync as readFileSync23 } from "fs";
|
|
10179
10866
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
10180
|
-
import { dirname as
|
|
10867
|
+
import { dirname as dirname13, join as join39 } from "path";
|
|
10181
10868
|
|
|
10182
10869
|
// src/commands/init.ts
|
|
10183
10870
|
init_dist();
|
|
10184
|
-
import
|
|
10185
|
-
import
|
|
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
|
|
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 =
|
|
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 =
|
|
10196
|
-
if (
|
|
10882
|
+
const candidate = join21(d, "templates");
|
|
10883
|
+
if (fs26.existsSync(candidate) && fs26.statSync(candidate).isDirectory()) {
|
|
10197
10884
|
return candidate;
|
|
10198
10885
|
}
|
|
10199
|
-
d =
|
|
10886
|
+
d = dirname7(d);
|
|
10200
10887
|
}
|
|
10201
|
-
return
|
|
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 =
|
|
10214
|
-
if (!
|
|
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(
|
|
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 (
|
|
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 (
|
|
10933
|
+
if (fs26.existsSync(path25.join(cwd, "pnpm-lock.yaml"))) {
|
|
10247
10934
|
return "pnpm add -D @playwright/test";
|
|
10248
10935
|
}
|
|
10249
|
-
if (
|
|
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((
|
|
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
|
-
|
|
10955
|
+
resolve11(!defaultNo);
|
|
10269
10956
|
return;
|
|
10270
10957
|
}
|
|
10271
|
-
|
|
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((
|
|
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
|
-
|
|
11077
|
+
resolve11(providers[selection - 1]);
|
|
10391
11078
|
});
|
|
10392
11079
|
});
|
|
10393
11080
|
}
|
|
10394
11081
|
function ensureDir(dirPath) {
|
|
10395
|
-
if (!
|
|
10396
|
-
|
|
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 =
|
|
10471
|
-
if (
|
|
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:
|
|
11163
|
+
return { path: join21(bundledTemplatesDir, templateName), source: "bundled" };
|
|
10476
11164
|
}
|
|
10477
11165
|
function processTemplate(templateName, targetPath, replacements, force, sourcePath, source) {
|
|
10478
|
-
if (
|
|
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 ??
|
|
11170
|
+
const templatePath = sourcePath ?? join21(TEMPLATES_DIR, templateName);
|
|
10483
11171
|
const resolvedSource = source ?? "bundled";
|
|
10484
|
-
let content =
|
|
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
|
-
|
|
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 =
|
|
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 (!
|
|
11195
|
+
if (!fs26.existsSync(gitignorePath)) {
|
|
10508
11196
|
const lines = ["# Night Watch", ...entries.map((e) => e.pattern), ""];
|
|
10509
|
-
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
10526
|
-
if (!
|
|
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 =
|
|
10532
|
-
if (isClaudeProvider ||
|
|
11219
|
+
const claudeDir = path25.join(cwd, ".claude");
|
|
11220
|
+
if (isClaudeProvider || fs26.existsSync(claudeDir)) {
|
|
10533
11221
|
ensureDir(claudeDir);
|
|
10534
|
-
const skillsDir =
|
|
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 =
|
|
10540
|
-
if (!
|
|
10541
|
-
const skillDir =
|
|
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 =
|
|
10544
|
-
if (
|
|
11231
|
+
const target = path25.join(skillDir, "SKILL.md");
|
|
11232
|
+
if (fs26.existsSync(target) && !force) {
|
|
10545
11233
|
skipped++;
|
|
10546
11234
|
continue;
|
|
10547
11235
|
}
|
|
10548
|
-
|
|
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 =
|
|
10555
|
-
const blockFile =
|
|
10556
|
-
if (!
|
|
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 =
|
|
11247
|
+
const block = fs26.readFileSync(blockFile, "utf-8");
|
|
10560
11248
|
const marker = "## Night Watch Skills";
|
|
10561
|
-
if (!
|
|
10562
|
-
|
|
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 =
|
|
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
|
-
|
|
11259
|
+
fs26.writeFileSync(agentsFile, withoutSection + "\n\n" + block);
|
|
10572
11260
|
} else {
|
|
10573
|
-
|
|
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 =
|
|
10698
|
-
const doneDirPath =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
10713
|
-
const customTemplatesDir =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
10785
|
-
if (
|
|
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
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
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((
|
|
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
|
|
11055
|
-
import * as
|
|
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:
|
|
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 =
|
|
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) =>
|
|
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:
|
|
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 =
|
|
11427
|
-
const doneDir =
|
|
12114
|
+
const absolutePrdDir = path26.join(projectDir, prdDir);
|
|
12115
|
+
const doneDir = path26.join(absolutePrdDir, "done");
|
|
11428
12116
|
const pending = [];
|
|
11429
12117
|
const completed = [];
|
|
11430
|
-
if (
|
|
11431
|
-
const entries =
|
|
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 =
|
|
12122
|
+
const claimPath = path26.join(absolutePrdDir, entry.name + CLAIM_FILE_EXTENSION);
|
|
11435
12123
|
let claimed = false;
|
|
11436
12124
|
let claimInfo = null;
|
|
11437
|
-
if (
|
|
12125
|
+
if (fs27.existsSync(claimPath)) {
|
|
11438
12126
|
try {
|
|
11439
|
-
const content =
|
|
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 (
|
|
11454
|
-
const entries =
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
12225
|
-
import * as
|
|
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",
|
|
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 =
|
|
12332
|
-
if (!
|
|
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 =
|
|
12350
|
-
if (
|
|
12351
|
-
const logLines =
|
|
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
|
|
12496
|
-
import * as
|
|
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 =
|
|
12504
|
-
if (
|
|
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
|
|
13205
|
+
return path30.dirname(process.execPath);
|
|
12518
13206
|
}
|
|
12519
13207
|
try {
|
|
12520
13208
|
const nodePath = execSync4("which node", { encoding: "utf-8" }).trim();
|
|
12521
|
-
return
|
|
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("\\") ?
|
|
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 =
|
|
12544
|
-
if (!
|
|
12545
|
-
|
|
13231
|
+
const logDir = path30.join(projectDir, LOG_DIR);
|
|
13232
|
+
if (!fs29.existsSync(logDir)) {
|
|
13233
|
+
fs29.mkdirSync(logDir, { recursive: true });
|
|
12546
13234
|
}
|
|
12547
|
-
const executorLog =
|
|
12548
|
-
const reviewerLog =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
12657
|
-
if (!
|
|
12658
|
-
|
|
13352
|
+
const logDir = path30.join(projectDir, LOG_DIR);
|
|
13353
|
+
if (!fs29.existsSync(logDir)) {
|
|
13354
|
+
fs29.mkdirSync(logDir, { recursive: true });
|
|
12659
13355
|
}
|
|
12660
|
-
const executorLog =
|
|
12661
|
-
const reviewerLog =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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 =
|
|
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
|
|
12797
|
-
import * as
|
|
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 =
|
|
12813
|
-
if (
|
|
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 =
|
|
12823
|
-
if (
|
|
12824
|
-
|
|
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 =
|
|
13537
|
+
const remainingFiles = fs30.readdirSync(logDir);
|
|
12829
13538
|
if (remainingFiles.length === 0) {
|
|
12830
|
-
|
|
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 =
|
|
12864
|
-
if (
|
|
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 =
|
|
12875
|
-
if (
|
|
12876
|
-
|
|
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 =
|
|
13591
|
+
const remainingFiles = fs30.readdirSync(logDir);
|
|
12882
13592
|
if (remainingFiles.length === 0) {
|
|
12883
|
-
|
|
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
|
|
13170
|
-
import * as
|
|
13879
|
+
import * as path32 from "path";
|
|
13880
|
+
import * as fs31 from "fs";
|
|
13171
13881
|
function getLastLines(filePath, lineCount) {
|
|
13172
|
-
if (!
|
|
13882
|
+
if (!fs31.existsSync(filePath)) {
|
|
13173
13883
|
return `Log file not found: ${filePath}`;
|
|
13174
13884
|
}
|
|
13175
13885
|
try {
|
|
13176
|
-
const content =
|
|
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 (!
|
|
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 =
|
|
13918
|
+
const logDir = path32.join(projectDir, LOG_DIR);
|
|
13209
13919
|
const lineCount = parseInt(options.lines || "50", 10);
|
|
13210
|
-
const executorLog =
|
|
13211
|
-
const reviewerLog =
|
|
13212
|
-
const qaLog =
|
|
13213
|
-
const auditLog =
|
|
13214
|
-
const plannerLog =
|
|
13215
|
-
const analyticsLog =
|
|
13216
|
-
const mergerLog =
|
|
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
|
|
13300
|
-
import * as
|
|
14018
|
+
import * as fs32 from "fs";
|
|
14019
|
+
import * as path33 from "path";
|
|
13301
14020
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13302
|
-
import { dirname as
|
|
14021
|
+
import { dirname as dirname10 } from "path";
|
|
13303
14022
|
var __filename3 = fileURLToPath4(import.meta.url);
|
|
13304
|
-
var __dirname3 =
|
|
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 =
|
|
13309
|
-
if (
|
|
14027
|
+
const candidate = path33.join(current, "templates");
|
|
14028
|
+
if (fs32.existsSync(candidate) && fs32.statSync(candidate).isDirectory()) {
|
|
13310
14029
|
return candidate;
|
|
13311
14030
|
}
|
|
13312
|
-
current =
|
|
14031
|
+
current = path33.dirname(current);
|
|
13313
14032
|
}
|
|
13314
|
-
return
|
|
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 (!
|
|
13322
|
-
const files =
|
|
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(
|
|
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 =
|
|
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 =
|
|
13421
|
-
const installedTemplatePath =
|
|
13422
|
-
const templatePath =
|
|
13423
|
-
if (!
|
|
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 =
|
|
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((
|
|
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
|
-
|
|
14202
|
+
resolve11(code === 0 && finalResult ? extractPrdMarkdown(finalResult) : null);
|
|
13484
14203
|
});
|
|
13485
|
-
child.on("error", () =>
|
|
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 =
|
|
14213
|
+
const tmpFile = path33.join(projectDir, `.prd-issue-body-${Date.now()}.tmp`);
|
|
13495
14214
|
try {
|
|
13496
14215
|
const body = buildGithubIssueBody(prdPath, projectDir, prdContent);
|
|
13497
|
-
|
|
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
|
-
|
|
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 (!
|
|
14236
|
+
if (!fs32.existsSync(claimPath)) {
|
|
13518
14237
|
return { active: false };
|
|
13519
14238
|
}
|
|
13520
|
-
const content =
|
|
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 =
|
|
13536
|
-
if (!
|
|
13537
|
-
|
|
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 =
|
|
13554
|
-
if (
|
|
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
|
-
|
|
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 =
|
|
13573
|
-
const doneDir =
|
|
14291
|
+
const absolutePrdDir = path33.join(projectDir, config.prdDir);
|
|
14292
|
+
const doneDir = path33.join(absolutePrdDir, "done");
|
|
13574
14293
|
const pending = [];
|
|
13575
|
-
if (
|
|
13576
|
-
const files =
|
|
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 =
|
|
14297
|
+
const content = fs32.readFileSync(path33.join(absolutePrdDir, file), "utf-8");
|
|
13579
14298
|
const deps = parseDependencies(content);
|
|
13580
|
-
const claimPath =
|
|
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 (
|
|
13592
|
-
const files =
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
15374
|
-
import * as
|
|
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
|
|
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 (!
|
|
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 =
|
|
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
|
|
16615
|
+
import * as fs39 from "fs";
|
|
15890
16616
|
|
|
15891
16617
|
// ../server/dist/index.js
|
|
15892
16618
|
init_dist();
|
|
15893
|
-
import * as
|
|
15894
|
-
import * as
|
|
15895
|
-
import { dirname as
|
|
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
|
|
15980
|
-
import * as
|
|
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 (!
|
|
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
|
|
16036
|
-
import * as
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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 =
|
|
16965
|
+
const prdDir = path36.join(projectDir, config.prdDir);
|
|
16240
16966
|
const normalized = prdName.endsWith(".md") ? prdName : `${prdName}.md`;
|
|
16241
|
-
const pendingPath =
|
|
16242
|
-
const donePath =
|
|
16243
|
-
if (
|
|
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 (!
|
|
16973
|
+
if (!fs36.existsSync(donePath)) {
|
|
16248
16974
|
res.status(404).json({ error: `PRD "${normalized}" not found in done/` });
|
|
16249
16975
|
return;
|
|
16250
16976
|
}
|
|
16251
|
-
|
|
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 (
|
|
16270
|
-
|
|
16995
|
+
if (fs36.existsSync(lockPath)) {
|
|
16996
|
+
fs36.unlinkSync(lockPath);
|
|
16271
16997
|
}
|
|
16272
|
-
const prdDir =
|
|
16273
|
-
if (
|
|
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
|
|
17042
|
-
import * as
|
|
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 =
|
|
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 =
|
|
17099
|
-
if (
|
|
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 =
|
|
17109
|
-
if (
|
|
17110
|
-
const prds =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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 =
|
|
18127
|
-
if (
|
|
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 (
|
|
18132
|
-
const dev =
|
|
18133
|
-
if (
|
|
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 =
|
|
18875
|
+
d = dirname12(d);
|
|
18138
18876
|
}
|
|
18139
18877
|
return bundled;
|
|
18140
18878
|
}
|
|
18141
18879
|
function setupStaticFiles(app) {
|
|
18142
18880
|
const webDistPath = resolveWebDistPath();
|
|
18143
|
-
if (
|
|
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 =
|
|
18152
|
-
if (
|
|
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(
|
|
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 (!
|
|
18345
|
-
const raw =
|
|
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 =
|
|
18358
|
-
|
|
19095
|
+
const fd = fs39.openSync(lockPath, "wx");
|
|
19096
|
+
fs39.writeFileSync(fd, `${process.pid}
|
|
18359
19097
|
`);
|
|
18360
|
-
|
|
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
|
-
|
|
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 (!
|
|
19142
|
+
if (!fs39.existsSync(lockPath)) return;
|
|
18405
19143
|
const lockPid = readPid(lockPath);
|
|
18406
19144
|
if (lockPid !== null && lockPid !== process.pid) return;
|
|
18407
|
-
|
|
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
|
|
18504
|
-
import * as
|
|
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) =>
|
|
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 (!
|
|
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
|
|
18597
|
-
import * as
|
|
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 (!
|
|
19343
|
+
if (!fs41.existsSync(doneDir)) {
|
|
18606
19344
|
return [];
|
|
18607
19345
|
}
|
|
18608
|
-
return
|
|
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 =
|
|
18615
|
-
const doneDir =
|
|
19352
|
+
const prdDir = path42.join(projectDir, config.prdDir);
|
|
19353
|
+
const doneDir = path42.join(prdDir, "done");
|
|
18616
19354
|
const normalizedPrdName = normalizePrdName(prdName);
|
|
18617
|
-
const pendingPath =
|
|
18618
|
-
if (
|
|
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 =
|
|
18623
|
-
if (
|
|
18624
|
-
|
|
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
|
|
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((
|
|
19631
|
+
return new Promise((resolve11) => {
|
|
18894
19632
|
rl.question(`${prompt} `, (answer) => {
|
|
18895
19633
|
rl.close();
|
|
18896
19634
|
const normalized = answer.toLowerCase().trim();
|
|
18897
|
-
|
|
19635
|
+
resolve11(normalized === "y" || normalized === "yes");
|
|
18898
19636
|
});
|
|
18899
19637
|
});
|
|
18900
19638
|
}
|
|
18901
19639
|
function sleep2(ms) {
|
|
18902
|
-
return new Promise((
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
19058
|
-
import * as
|
|
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 (
|
|
19065
|
-
const pidRaw =
|
|
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
|
-
|
|
19809
|
+
fs43.unlinkSync(lockFile);
|
|
19072
19810
|
} catch {
|
|
19073
19811
|
}
|
|
19074
19812
|
}
|
|
19075
|
-
|
|
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 (
|
|
19081
|
-
|
|
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 =
|
|
19091
|
-
const absolutePrdPath =
|
|
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 =
|
|
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
|
|
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) =>
|
|
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:
|
|
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:
|
|
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
|
|
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 ||
|
|
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(` ${
|
|
19378
|
-
console.log(` ${
|
|
19379
|
-
console.log(` ${
|
|
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
|
|
19418
|
-
import * as
|
|
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 `${
|
|
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((
|
|
20210
|
+
return new Promise((resolve11) => {
|
|
19473
20211
|
rl.question(question, (answer) => {
|
|
19474
20212
|
rl.close();
|
|
19475
|
-
|
|
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 (!
|
|
20379
|
+
if (!fs44.existsSync(filePath)) {
|
|
19642
20380
|
console.error(`File not found: ${filePath}`);
|
|
19643
20381
|
process.exit(1);
|
|
19644
20382
|
}
|
|
19645
|
-
body =
|
|
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 ??
|
|
19887
|
-
if (!
|
|
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 =
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
21960
|
+
writeJson2({ schemaVersion: SCHEMA_VERSION2, config: value });
|
|
21023
21961
|
} else {
|
|
21024
|
-
|
|
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
|
-
|
|
21969
|
+
writeJson2({ schemaVersion: SCHEMA_VERSION2, ...result });
|
|
21032
21970
|
} else {
|
|
21033
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 =
|
|
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 (
|
|
21129
|
-
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(
|
|
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);
|