@jonit-dev/night-watch-cli 1.8.14-beta.5 → 1.8.14-beta.7
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 +1439 -374
- 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/serve.d.ts +1 -0
- package/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +10 -4
- package/dist/commands/serve.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-BbiKFOgi.css +1 -0
- package/dist/web/assets/index-DatF4suf.css +1 -0
- package/dist/web/assets/index-Q3IYCcdZ.js +447 -0
- package/dist/web/assets/index-uBao8iYf.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
|
}
|
|
@@ -16030,10 +16756,128 @@ function startSseStatusWatcher(clients, projectDir, getConfig) {
|
|
|
16030
16756
|
}, 2e3);
|
|
16031
16757
|
}
|
|
16032
16758
|
|
|
16759
|
+
// ../server/dist/global-startup-summary.js
|
|
16760
|
+
init_dist();
|
|
16761
|
+
var RESET = "\x1B[0m";
|
|
16762
|
+
var DIM = "\x1B[2m";
|
|
16763
|
+
var BOLD = "\x1B[1m";
|
|
16764
|
+
var GREEN = "\x1B[32m";
|
|
16765
|
+
var YELLOW = "\x1B[33m";
|
|
16766
|
+
var RED = "\x1B[31m";
|
|
16767
|
+
var CYAN = "\x1B[36m";
|
|
16768
|
+
var FAILURE_OUTCOME_WINDOW_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
16769
|
+
function paint(enabled, code, value) {
|
|
16770
|
+
return enabled ? `${code}${value}${RESET}` : value;
|
|
16771
|
+
}
|
|
16772
|
+
function palette(color) {
|
|
16773
|
+
return {
|
|
16774
|
+
bold: (value) => paint(color, BOLD, value),
|
|
16775
|
+
dim: (value) => paint(color, DIM, value),
|
|
16776
|
+
green: (value) => paint(color, GREEN, value),
|
|
16777
|
+
yellow: (value) => paint(color, YELLOW, value),
|
|
16778
|
+
red: (value) => paint(color, RED, value),
|
|
16779
|
+
cyan: (value) => paint(color, CYAN, value)
|
|
16780
|
+
};
|
|
16781
|
+
}
|
|
16782
|
+
function shouldUseColor() {
|
|
16783
|
+
return Boolean(process.stdout.isTTY) && !process.env.NO_COLOR;
|
|
16784
|
+
}
|
|
16785
|
+
function failureRateFromSummary(summary) {
|
|
16786
|
+
if (summary.totalCount === 0)
|
|
16787
|
+
return null;
|
|
16788
|
+
const failed = summary.failureCount + summary.timeoutCount + summary.rateLimitedCount;
|
|
16789
|
+
return Math.round(failed / summary.totalCount * 100);
|
|
16790
|
+
}
|
|
16791
|
+
function getExecutorStatus(config, snapshot) {
|
|
16792
|
+
if (snapshot.processes.some((processInfo) => processInfo.running)) {
|
|
16793
|
+
return "running";
|
|
16794
|
+
}
|
|
16795
|
+
const executorActive = config.executorEnabled !== false && !config.pausedJobs?.executor;
|
|
16796
|
+
return executorActive ? "active" : "paused";
|
|
16797
|
+
}
|
|
16798
|
+
function describeError(error2) {
|
|
16799
|
+
const message = error2 instanceof Error ? error2.message : String(error2);
|
|
16800
|
+
return message.split("\n")[0]?.trim() || "unknown error";
|
|
16801
|
+
}
|
|
16802
|
+
function formatStatusMarker(status, colors) {
|
|
16803
|
+
if (status === "error")
|
|
16804
|
+
return colors.red("x");
|
|
16805
|
+
if (status === "paused")
|
|
16806
|
+
return colors.yellow("\u25CB");
|
|
16807
|
+
return colors.green("\u25CF");
|
|
16808
|
+
}
|
|
16809
|
+
function buildProjectStartupSummary(entry, config, snapshot, outcomeSummary) {
|
|
16810
|
+
return {
|
|
16811
|
+
name: entry.name,
|
|
16812
|
+
path: entry.path,
|
|
16813
|
+
status: getExecutorStatus(config, snapshot),
|
|
16814
|
+
readyPrds: snapshot.prds.filter((prd) => prd.status === "ready").length,
|
|
16815
|
+
openPrs: snapshot.prs.length,
|
|
16816
|
+
failedPrs: snapshot.prs.filter((pr) => pr.ciStatus === "fail").length,
|
|
16817
|
+
pendingPrs: snapshot.prs.filter((pr) => pr.ciStatus === "pending").length,
|
|
16818
|
+
failureRate: failureRateFromSummary(outcomeSummary),
|
|
16819
|
+
provider: String(config.provider),
|
|
16820
|
+
cronInstalled: snapshot.crontab.installed,
|
|
16821
|
+
runningProcesses: snapshot.processes.filter((processInfo) => processInfo.running).map((processInfo) => processInfo.name)
|
|
16822
|
+
};
|
|
16823
|
+
}
|
|
16824
|
+
async function collectProjectStartupSummary(entry) {
|
|
16825
|
+
try {
|
|
16826
|
+
const config = loadConfig(entry.path);
|
|
16827
|
+
const snapshot = await fetchStatusSnapshot(entry.path, config);
|
|
16828
|
+
const fromFinishedAt = Date.now() - FAILURE_OUTCOME_WINDOW_MS;
|
|
16829
|
+
const outcomeSummary = getRepositories().sessionOutcomes.querySummary({
|
|
16830
|
+
projectPath: entry.path,
|
|
16831
|
+
fromFinishedAt
|
|
16832
|
+
});
|
|
16833
|
+
return buildProjectStartupSummary(entry, config, snapshot, outcomeSummary);
|
|
16834
|
+
} catch (error2) {
|
|
16835
|
+
return {
|
|
16836
|
+
name: entry.name,
|
|
16837
|
+
path: entry.path,
|
|
16838
|
+
status: "error",
|
|
16839
|
+
readyPrds: 0,
|
|
16840
|
+
openPrs: 0,
|
|
16841
|
+
failedPrs: 0,
|
|
16842
|
+
pendingPrs: 0,
|
|
16843
|
+
failureRate: null,
|
|
16844
|
+
provider: "n/a",
|
|
16845
|
+
cronInstalled: false,
|
|
16846
|
+
runningProcesses: [],
|
|
16847
|
+
error: describeError(error2)
|
|
16848
|
+
};
|
|
16849
|
+
}
|
|
16850
|
+
}
|
|
16851
|
+
function formatProjectStartupSummaryLine(summary, options = {}) {
|
|
16852
|
+
const color = options.color ?? shouldUseColor();
|
|
16853
|
+
const c = palette(color);
|
|
16854
|
+
const statusColor = summary.status === "active" || summary.status === "running" ? c.green : c.yellow;
|
|
16855
|
+
const statusText = summary.status === "error" ? c.red("error") : statusColor(summary.status.padEnd(7, " "));
|
|
16856
|
+
const marker = formatStatusMarker(summary.status, c);
|
|
16857
|
+
if (summary.status === "error") {
|
|
16858
|
+
return ` ${marker} ${statusText} ${c.bold(summary.name)} ${c.red(summary.error ?? "unknown error")} ${c.dim(summary.path)}`;
|
|
16859
|
+
}
|
|
16860
|
+
const prStatus = summary.failedPrs > 0 || summary.pendingPrs > 0 ? ` (${summary.failedPrs} fail, ${summary.pendingPrs} pending)` : "";
|
|
16861
|
+
const failureText = summary.failureRate === null ? c.dim("fail n/a") : `fail ${c.cyan(`${summary.failureRate}%`)}`;
|
|
16862
|
+
const cronText = summary.cronInstalled ? c.green("cron on") : c.dim("cron off");
|
|
16863
|
+
const runningText = summary.runningProcesses.length > 0 ? ` ${c.dim(`run ${summary.runningProcesses.join(",")}`)}` : "";
|
|
16864
|
+
return [
|
|
16865
|
+
` ${marker}`,
|
|
16866
|
+
statusText,
|
|
16867
|
+
c.bold(summary.name),
|
|
16868
|
+
`ready ${c.cyan(String(summary.readyPrds))}`,
|
|
16869
|
+
`PRs ${c.cyan(String(summary.openPrs))}${prStatus}`,
|
|
16870
|
+
failureText,
|
|
16871
|
+
`provider ${c.cyan(summary.provider)}`,
|
|
16872
|
+
cronText,
|
|
16873
|
+
`${c.dim(summary.path)}${runningText}`
|
|
16874
|
+
].join(" ");
|
|
16875
|
+
}
|
|
16876
|
+
|
|
16033
16877
|
// ../server/dist/routes/action.routes.js
|
|
16034
16878
|
init_dist();
|
|
16035
|
-
import * as
|
|
16036
|
-
import * as
|
|
16879
|
+
import * as fs36 from "fs";
|
|
16880
|
+
import * as path36 from "path";
|
|
16037
16881
|
import { execSync as execSync6, spawn as spawn6 } from "child_process";
|
|
16038
16882
|
import { Router } from "express";
|
|
16039
16883
|
|
|
@@ -16071,17 +16915,17 @@ function getBoardProvider(config, projectDir) {
|
|
|
16071
16915
|
function cleanOrphanedClaims(dir) {
|
|
16072
16916
|
let entries;
|
|
16073
16917
|
try {
|
|
16074
|
-
entries =
|
|
16918
|
+
entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
16075
16919
|
} catch {
|
|
16076
16920
|
return;
|
|
16077
16921
|
}
|
|
16078
16922
|
for (const entry of entries) {
|
|
16079
|
-
const fullPath =
|
|
16923
|
+
const fullPath = path36.join(dir, entry.name);
|
|
16080
16924
|
if (entry.isDirectory() && entry.name !== "done") {
|
|
16081
16925
|
cleanOrphanedClaims(fullPath);
|
|
16082
16926
|
} else if (entry.name.endsWith(CLAIM_FILE_EXTENSION)) {
|
|
16083
16927
|
try {
|
|
16084
|
-
|
|
16928
|
+
fs36.unlinkSync(fullPath);
|
|
16085
16929
|
} catch {
|
|
16086
16930
|
}
|
|
16087
16931
|
}
|
|
@@ -16236,19 +17080,19 @@ function createActionRouteHandlers(ctx) {
|
|
|
16236
17080
|
res.status(400).json({ error: "Invalid PRD name" });
|
|
16237
17081
|
return;
|
|
16238
17082
|
}
|
|
16239
|
-
const prdDir =
|
|
17083
|
+
const prdDir = path36.join(projectDir, config.prdDir);
|
|
16240
17084
|
const normalized = prdName.endsWith(".md") ? prdName : `${prdName}.md`;
|
|
16241
|
-
const pendingPath =
|
|
16242
|
-
const donePath =
|
|
16243
|
-
if (
|
|
17085
|
+
const pendingPath = path36.join(prdDir, normalized);
|
|
17086
|
+
const donePath = path36.join(prdDir, "done", normalized);
|
|
17087
|
+
if (fs36.existsSync(pendingPath)) {
|
|
16244
17088
|
res.json({ message: `"${normalized}" is already pending` });
|
|
16245
17089
|
return;
|
|
16246
17090
|
}
|
|
16247
|
-
if (!
|
|
17091
|
+
if (!fs36.existsSync(donePath)) {
|
|
16248
17092
|
res.status(404).json({ error: `PRD "${normalized}" not found in done/` });
|
|
16249
17093
|
return;
|
|
16250
17094
|
}
|
|
16251
|
-
|
|
17095
|
+
fs36.renameSync(donePath, pendingPath);
|
|
16252
17096
|
res.json({ message: `Moved "${normalized}" back to pending` });
|
|
16253
17097
|
} catch (error2) {
|
|
16254
17098
|
res.status(500).json({
|
|
@@ -16266,11 +17110,11 @@ function createActionRouteHandlers(ctx) {
|
|
|
16266
17110
|
res.status(409).json({ error: "Executor is actively running \u2014 use Stop instead" });
|
|
16267
17111
|
return;
|
|
16268
17112
|
}
|
|
16269
|
-
if (
|
|
16270
|
-
|
|
17113
|
+
if (fs36.existsSync(lockPath)) {
|
|
17114
|
+
fs36.unlinkSync(lockPath);
|
|
16271
17115
|
}
|
|
16272
|
-
const prdDir =
|
|
16273
|
-
if (
|
|
17116
|
+
const prdDir = path36.join(projectDir, config.prdDir);
|
|
17117
|
+
if (fs36.existsSync(prdDir)) {
|
|
16274
17118
|
cleanOrphanedClaims(prdDir);
|
|
16275
17119
|
}
|
|
16276
17120
|
broadcastSSE(ctx.getSseClients(req), "status_changed", await fetchStatusSnapshot(projectDir, config));
|
|
@@ -17038,8 +17882,8 @@ function createProjectConfigRoutes() {
|
|
|
17038
17882
|
|
|
17039
17883
|
// ../server/dist/routes/doctor.routes.js
|
|
17040
17884
|
init_dist();
|
|
17041
|
-
import * as
|
|
17042
|
-
import * as
|
|
17885
|
+
import * as fs37 from "fs";
|
|
17886
|
+
import * as path37 from "path";
|
|
17043
17887
|
import { execSync as execSync7 } from "child_process";
|
|
17044
17888
|
import { Router as Router4 } from "express";
|
|
17045
17889
|
function runDoctorChecks(projectDir, config) {
|
|
@@ -17072,7 +17916,7 @@ function runDoctorChecks(projectDir, config) {
|
|
|
17072
17916
|
});
|
|
17073
17917
|
}
|
|
17074
17918
|
try {
|
|
17075
|
-
const projectName =
|
|
17919
|
+
const projectName = path37.basename(projectDir);
|
|
17076
17920
|
const marker = generateMarker(projectName);
|
|
17077
17921
|
const crontabEntries = [...getEntries(marker), ...getProjectEntries(projectDir)];
|
|
17078
17922
|
if (crontabEntries.length > 0) {
|
|
@@ -17095,8 +17939,8 @@ function runDoctorChecks(projectDir, config) {
|
|
|
17095
17939
|
detail: "Failed to check crontab"
|
|
17096
17940
|
});
|
|
17097
17941
|
}
|
|
17098
|
-
const configPath =
|
|
17099
|
-
if (
|
|
17942
|
+
const configPath = path37.join(projectDir, CONFIG_FILE_NAME);
|
|
17943
|
+
if (fs37.existsSync(configPath)) {
|
|
17100
17944
|
checks.push({ name: "config", status: "pass", detail: "Config file exists" });
|
|
17101
17945
|
} else {
|
|
17102
17946
|
checks.push({
|
|
@@ -17105,9 +17949,9 @@ function runDoctorChecks(projectDir, config) {
|
|
|
17105
17949
|
detail: "Config file not found (using defaults)"
|
|
17106
17950
|
});
|
|
17107
17951
|
}
|
|
17108
|
-
const prdDir =
|
|
17109
|
-
if (
|
|
17110
|
-
const prds =
|
|
17952
|
+
const prdDir = path37.join(projectDir, config.prdDir);
|
|
17953
|
+
if (fs37.existsSync(prdDir)) {
|
|
17954
|
+
const prds = fs37.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
|
|
17111
17955
|
checks.push({
|
|
17112
17956
|
name: "prdDir",
|
|
17113
17957
|
status: "pass",
|
|
@@ -17548,6 +18392,8 @@ function getLockPathForJob2(projectDir, jobId) {
|
|
|
17548
18392
|
return prResolverLockPath(projectDir);
|
|
17549
18393
|
case "merger":
|
|
17550
18394
|
return mergerLockPath(projectDir);
|
|
18395
|
+
case "manager":
|
|
18396
|
+
return managerLockPath(projectDir);
|
|
17551
18397
|
}
|
|
17552
18398
|
}
|
|
17553
18399
|
function createJobRouteHandlers(ctx) {
|
|
@@ -17674,7 +18520,7 @@ function createProjectJobRoutes() {
|
|
|
17674
18520
|
|
|
17675
18521
|
// ../server/dist/routes/log.routes.js
|
|
17676
18522
|
init_dist();
|
|
17677
|
-
import * as
|
|
18523
|
+
import * as path38 from "path";
|
|
17678
18524
|
import { Router as Router7 } from "express";
|
|
17679
18525
|
function createLogRoutes(deps) {
|
|
17680
18526
|
const { projectDir } = deps;
|
|
@@ -17693,7 +18539,7 @@ function createLogRoutes(deps) {
|
|
|
17693
18539
|
const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
|
|
17694
18540
|
const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
|
|
17695
18541
|
const fileName = LOG_FILE_NAMES[name] || name;
|
|
17696
|
-
const logPath =
|
|
18542
|
+
const logPath = path38.join(projectDir, LOG_DIR, `${fileName}.log`);
|
|
17697
18543
|
const logLines = getLastLogLines(logPath, linesToRead);
|
|
17698
18544
|
res.json({ name, lines: logLines });
|
|
17699
18545
|
} catch (error2) {
|
|
@@ -17719,7 +18565,7 @@ function createProjectLogRoutes() {
|
|
|
17719
18565
|
const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
|
|
17720
18566
|
const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
|
|
17721
18567
|
const fileName = LOG_FILE_NAMES[name] || name;
|
|
17722
|
-
const logPath =
|
|
18568
|
+
const logPath = path38.join(projectDir, LOG_DIR, `${fileName}.log`);
|
|
17723
18569
|
const logLines = getLastLogLines(logPath, linesToRead);
|
|
17724
18570
|
res.json({ name, lines: logLines });
|
|
17725
18571
|
} catch (error2) {
|
|
@@ -17754,7 +18600,7 @@ function createProjectPrdRoutes() {
|
|
|
17754
18600
|
|
|
17755
18601
|
// ../server/dist/routes/roadmap.routes.js
|
|
17756
18602
|
init_dist();
|
|
17757
|
-
import * as
|
|
18603
|
+
import * as path39 from "path";
|
|
17758
18604
|
import { Router as Router9 } from "express";
|
|
17759
18605
|
function createRoadmapRouteHandlers(ctx) {
|
|
17760
18606
|
const router = Router9({ mergeParams: true });
|
|
@@ -17764,7 +18610,7 @@ function createRoadmapRouteHandlers(ctx) {
|
|
|
17764
18610
|
const config = ctx.getConfig(req);
|
|
17765
18611
|
const projectDir = ctx.getProjectDir(req);
|
|
17766
18612
|
const status = getRoadmapStatus(projectDir, config);
|
|
17767
|
-
const prdDir =
|
|
18613
|
+
const prdDir = path39.join(projectDir, config.prdDir);
|
|
17768
18614
|
const state = loadRoadmapState(prdDir);
|
|
17769
18615
|
res.json({
|
|
17770
18616
|
...status,
|
|
@@ -17891,6 +18737,7 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
17891
18737
|
const analyticsPlan = getSchedulingPlan(projectDir, config, "analytics");
|
|
17892
18738
|
const prResolverPlan = getSchedulingPlan(projectDir, config, "pr-resolver");
|
|
17893
18739
|
const mergerPlan = getSchedulingPlan(projectDir, config, "merger");
|
|
18740
|
+
const managerPlan = getSchedulingPlan(projectDir, config, "manager");
|
|
17894
18741
|
const executorInstalled = installed && config.executorEnabled !== false && hasScheduledCommand(entries, "run");
|
|
17895
18742
|
const reviewerInstalled = installed && config.reviewerEnabled && hasScheduledCommand(entries, "review");
|
|
17896
18743
|
const qaInstalled = installed && config.qa.enabled && hasScheduledCommand(entries, "qa");
|
|
@@ -17899,6 +18746,7 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
17899
18746
|
const analyticsInstalled = installed && config.analytics.enabled && hasScheduledCommand(entries, "analytics");
|
|
17900
18747
|
const prResolverInstalled = installed && (config.prResolver?.enabled ?? true) && hasScheduledCommand(entries, "resolve");
|
|
17901
18748
|
const mergerInstalled = installed && (config.merger?.enabled ?? false) && hasScheduledCommand(entries, "merge");
|
|
18749
|
+
const managerInstalled = installed && (config.manager?.enabled ?? true) && hasScheduledCommand(entries, "manager");
|
|
17902
18750
|
return {
|
|
17903
18751
|
executor: {
|
|
17904
18752
|
schedule: config.cronSchedule,
|
|
@@ -17964,6 +18812,14 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
17964
18812
|
manualDelayMinutes: mergerPlan.manualDelayMinutes,
|
|
17965
18813
|
balancedDelayMinutes: mergerPlan.balancedDelayMinutes
|
|
17966
18814
|
},
|
|
18815
|
+
manager: {
|
|
18816
|
+
schedule: config.manager?.schedule ?? "15 7 * * *",
|
|
18817
|
+
installed: managerInstalled,
|
|
18818
|
+
nextRun: managerInstalled ? addDelayToIsoString(computeNextRun(config.manager?.schedule ?? "15 7 * * *"), managerPlan.totalDelayMinutes) : null,
|
|
18819
|
+
delayMinutes: managerPlan.totalDelayMinutes,
|
|
18820
|
+
manualDelayMinutes: managerPlan.manualDelayMinutes,
|
|
18821
|
+
balancedDelayMinutes: managerPlan.balancedDelayMinutes
|
|
18822
|
+
},
|
|
17967
18823
|
paused: !installed,
|
|
17968
18824
|
schedulingPriority: config.schedulingPriority,
|
|
17969
18825
|
entries
|
|
@@ -18116,31 +18972,31 @@ function createQueueRoutes(deps) {
|
|
|
18116
18972
|
|
|
18117
18973
|
// ../server/dist/index.js
|
|
18118
18974
|
var __filename4 = fileURLToPath5(import.meta.url);
|
|
18119
|
-
var __dirname4 =
|
|
18975
|
+
var __dirname4 = dirname12(__filename4);
|
|
18120
18976
|
var JOB_RAW_BODY_LIMIT = "1mb";
|
|
18121
18977
|
function setupJobRawBodyParsing(app) {
|
|
18122
18978
|
app.use("/api/jobs", express.raw({ type: "*/*", limit: JOB_RAW_BODY_LIMIT }));
|
|
18123
18979
|
app.use("/api/projects/:projectId/jobs", express.raw({ type: "*/*", limit: JOB_RAW_BODY_LIMIT }));
|
|
18124
18980
|
}
|
|
18125
18981
|
function resolveWebDistPath() {
|
|
18126
|
-
const bundled =
|
|
18127
|
-
if (
|
|
18982
|
+
const bundled = path40.join(__dirname4, "web");
|
|
18983
|
+
if (fs38.existsSync(path40.join(bundled, "index.html")))
|
|
18128
18984
|
return bundled;
|
|
18129
18985
|
let d = __dirname4;
|
|
18130
18986
|
for (let i = 0; i < 8; i++) {
|
|
18131
|
-
if (
|
|
18132
|
-
const dev =
|
|
18133
|
-
if (
|
|
18987
|
+
if (fs38.existsSync(path40.join(d, "turbo.json"))) {
|
|
18988
|
+
const dev = path40.join(d, "web/dist");
|
|
18989
|
+
if (fs38.existsSync(path40.join(dev, "index.html")))
|
|
18134
18990
|
return dev;
|
|
18135
18991
|
break;
|
|
18136
18992
|
}
|
|
18137
|
-
d =
|
|
18993
|
+
d = dirname12(d);
|
|
18138
18994
|
}
|
|
18139
18995
|
return bundled;
|
|
18140
18996
|
}
|
|
18141
18997
|
function setupStaticFiles(app) {
|
|
18142
18998
|
const webDistPath = resolveWebDistPath();
|
|
18143
|
-
if (
|
|
18999
|
+
if (fs38.existsSync(webDistPath)) {
|
|
18144
19000
|
app.use(express.static(webDistPath));
|
|
18145
19001
|
}
|
|
18146
19002
|
app.use((req, res, next) => {
|
|
@@ -18148,8 +19004,8 @@ function setupStaticFiles(app) {
|
|
|
18148
19004
|
next();
|
|
18149
19005
|
return;
|
|
18150
19006
|
}
|
|
18151
|
-
const indexPath =
|
|
18152
|
-
if (
|
|
19007
|
+
const indexPath = path40.resolve(webDistPath, "index.html");
|
|
19008
|
+
if (fs38.existsSync(indexPath)) {
|
|
18153
19009
|
res.sendFile(indexPath, (err) => {
|
|
18154
19010
|
if (err)
|
|
18155
19011
|
next();
|
|
@@ -18288,7 +19144,7 @@ function createGlobalApp() {
|
|
|
18288
19144
|
return app;
|
|
18289
19145
|
}
|
|
18290
19146
|
function bootContainer() {
|
|
18291
|
-
initContainer(
|
|
19147
|
+
initContainer(path40.dirname(getDbPath()));
|
|
18292
19148
|
}
|
|
18293
19149
|
function startServer(projectDir, port) {
|
|
18294
19150
|
bootContainer();
|
|
@@ -18303,7 +19159,7 @@ Night Watch UI http://localhost:${port}`);
|
|
|
18303
19159
|
});
|
|
18304
19160
|
setupGracefulShutdown(server);
|
|
18305
19161
|
}
|
|
18306
|
-
function startGlobalServer(port) {
|
|
19162
|
+
async function startGlobalServer(port) {
|
|
18307
19163
|
bootContainer();
|
|
18308
19164
|
const entries = loadRegistry();
|
|
18309
19165
|
if (entries.length === 0) {
|
|
@@ -18314,12 +19170,14 @@ function startGlobalServer(port) {
|
|
|
18314
19170
|
if (invalid.length > 0) {
|
|
18315
19171
|
console.warn(`Warning: ${invalid.length} registered project(s) have invalid paths and will be skipped.`);
|
|
18316
19172
|
}
|
|
19173
|
+
const summaries = await Promise.all(valid.map((entry) => collectProjectStartupSummary(entry)));
|
|
18317
19174
|
console.log(`
|
|
18318
19175
|
Night Watch Global UI`);
|
|
18319
19176
|
console.log(`Managing ${valid.length} project(s):`);
|
|
18320
|
-
for (const
|
|
18321
|
-
console.log(
|
|
19177
|
+
for (const summary of summaries) {
|
|
19178
|
+
console.log(formatProjectStartupSummaryLine(summary));
|
|
18322
19179
|
}
|
|
19180
|
+
console.log("");
|
|
18323
19181
|
const app = createGlobalApp();
|
|
18324
19182
|
const server = app.listen(port, () => {
|
|
18325
19183
|
console.log(`Night Watch Global UI running at http://localhost:${port}`);
|
|
@@ -18341,8 +19199,8 @@ function isProcessRunning2(pid) {
|
|
|
18341
19199
|
}
|
|
18342
19200
|
function readPid(lockPath) {
|
|
18343
19201
|
try {
|
|
18344
|
-
if (!
|
|
18345
|
-
const raw =
|
|
19202
|
+
if (!fs39.existsSync(lockPath)) return null;
|
|
19203
|
+
const raw = fs39.readFileSync(lockPath, "utf-8").trim();
|
|
18346
19204
|
const pid = parseInt(raw, 10);
|
|
18347
19205
|
return Number.isFinite(pid) ? pid : null;
|
|
18348
19206
|
} catch {
|
|
@@ -18354,10 +19212,10 @@ function acquireServeLock(mode, port) {
|
|
|
18354
19212
|
let stalePidCleaned;
|
|
18355
19213
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
18356
19214
|
try {
|
|
18357
|
-
const fd =
|
|
18358
|
-
|
|
19215
|
+
const fd = fs39.openSync(lockPath, "wx");
|
|
19216
|
+
fs39.writeFileSync(fd, `${process.pid}
|
|
18359
19217
|
`);
|
|
18360
|
-
|
|
19218
|
+
fs39.closeSync(fd);
|
|
18361
19219
|
return { acquired: true, lockPath, stalePidCleaned };
|
|
18362
19220
|
} catch (error2) {
|
|
18363
19221
|
const err = error2;
|
|
@@ -18378,7 +19236,7 @@ function acquireServeLock(mode, port) {
|
|
|
18378
19236
|
};
|
|
18379
19237
|
}
|
|
18380
19238
|
try {
|
|
18381
|
-
|
|
19239
|
+
fs39.unlinkSync(lockPath);
|
|
18382
19240
|
if (existingPid) {
|
|
18383
19241
|
stalePidCleaned = existingPid;
|
|
18384
19242
|
}
|
|
@@ -18401,15 +19259,18 @@ function acquireServeLock(mode, port) {
|
|
|
18401
19259
|
}
|
|
18402
19260
|
function releaseServeLock(lockPath) {
|
|
18403
19261
|
try {
|
|
18404
|
-
if (!
|
|
19262
|
+
if (!fs39.existsSync(lockPath)) return;
|
|
18405
19263
|
const lockPid = readPid(lockPath);
|
|
18406
19264
|
if (lockPid !== null && lockPid !== process.pid) return;
|
|
18407
|
-
|
|
19265
|
+
fs39.unlinkSync(lockPath);
|
|
18408
19266
|
} catch {
|
|
18409
19267
|
}
|
|
18410
19268
|
}
|
|
19269
|
+
function isServeDebugEnabled() {
|
|
19270
|
+
return process.env.NIGHT_WATCH_DEBUG_SERVE === "1";
|
|
19271
|
+
}
|
|
18411
19272
|
function serveCommand(program2) {
|
|
18412
|
-
program2.command("serve").description("Start the Night Watch web UI server").option("-p, --port <number>", "Port to run the server on", "7575").option("-g, --global", "Start in global mode (manage all registered projects)").action((options) => {
|
|
19273
|
+
program2.command("serve").description("Start the Night Watch web UI server").option("-p, --port <number>", "Port to run the server on", "7575").option("-g, --global", "Start in global mode (manage all registered projects)").action(async (options) => {
|
|
18413
19274
|
const port = parseInt(options.port, 10);
|
|
18414
19275
|
if (isNaN(port) || port < 1 || port > 65535) {
|
|
18415
19276
|
console.error(`Invalid port: ${options.port}. Port must be between 1 and 65535.`);
|
|
@@ -18433,11 +19294,14 @@ function serveCommand(program2) {
|
|
|
18433
19294
|
`[serve] cleaned stale lock from PID ${lock.stalePidCleaned} (${lock.lockPath})`
|
|
18434
19295
|
);
|
|
18435
19296
|
}
|
|
18436
|
-
|
|
19297
|
+
const debugServe = isServeDebugEnabled();
|
|
19298
|
+
if (debugServe) {
|
|
19299
|
+
console.log(`[serve] lock acquired ${lock.lockPath} pid=${process.pid}`);
|
|
19300
|
+
}
|
|
18437
19301
|
process.on("exit", () => {
|
|
18438
19302
|
releaseServeLock(lock.lockPath);
|
|
18439
19303
|
});
|
|
18440
|
-
if (options.global) {
|
|
19304
|
+
if (debugServe && options.global) {
|
|
18441
19305
|
const execArgv = process.execArgv.length > 0 ? process.execArgv.join(" ") : "(none)";
|
|
18442
19306
|
console.log(`[serve] mode=global port=${port} pid=${process.pid} node=${process.version}`);
|
|
18443
19307
|
console.log(`[serve] execPath=${process.execPath}`);
|
|
@@ -18445,7 +19309,7 @@ function serveCommand(program2) {
|
|
|
18445
19309
|
console.log(`[serve] argv=${process.argv.join(" ")}`);
|
|
18446
19310
|
}
|
|
18447
19311
|
if (options.global) {
|
|
18448
|
-
startGlobalServer(port);
|
|
19312
|
+
await startGlobalServer(port);
|
|
18449
19313
|
} else {
|
|
18450
19314
|
const projectDir = process.cwd();
|
|
18451
19315
|
startServer(projectDir, port);
|
|
@@ -18500,14 +19364,14 @@ function historyCommand(program2) {
|
|
|
18500
19364
|
// src/commands/update.ts
|
|
18501
19365
|
init_dist();
|
|
18502
19366
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
18503
|
-
import * as
|
|
18504
|
-
import * as
|
|
19367
|
+
import * as fs40 from "fs";
|
|
19368
|
+
import * as path41 from "path";
|
|
18505
19369
|
var DEFAULT_GLOBAL_SPEC = "@jonit-dev/night-watch-cli@latest";
|
|
18506
19370
|
function parseProjectDirs(projects, cwd) {
|
|
18507
19371
|
if (!projects || projects.trim().length === 0) {
|
|
18508
19372
|
return [cwd];
|
|
18509
19373
|
}
|
|
18510
|
-
const dirs = projects.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) =>
|
|
19374
|
+
const dirs = projects.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => path41.resolve(cwd, entry));
|
|
18511
19375
|
return Array.from(new Set(dirs));
|
|
18512
19376
|
}
|
|
18513
19377
|
function shouldInstallGlobal(options) {
|
|
@@ -18549,7 +19413,7 @@ function updateCommand(program2) {
|
|
|
18549
19413
|
}
|
|
18550
19414
|
const nightWatchBin = resolveNightWatchBin();
|
|
18551
19415
|
for (const projectDir of projectDirs) {
|
|
18552
|
-
if (!
|
|
19416
|
+
if (!fs40.existsSync(projectDir) || !fs40.statSync(projectDir).isDirectory()) {
|
|
18553
19417
|
warn(`Skipping invalid project directory: ${projectDir}`);
|
|
18554
19418
|
continue;
|
|
18555
19419
|
}
|
|
@@ -18593,8 +19457,8 @@ function prdStateCommand(program2) {
|
|
|
18593
19457
|
|
|
18594
19458
|
// src/commands/retry.ts
|
|
18595
19459
|
init_dist();
|
|
18596
|
-
import * as
|
|
18597
|
-
import * as
|
|
19460
|
+
import * as fs41 from "fs";
|
|
19461
|
+
import * as path42 from "path";
|
|
18598
19462
|
function normalizePrdName(name) {
|
|
18599
19463
|
if (!name.endsWith(".md")) {
|
|
18600
19464
|
return `${name}.md`;
|
|
@@ -18602,26 +19466,26 @@ function normalizePrdName(name) {
|
|
|
18602
19466
|
return name;
|
|
18603
19467
|
}
|
|
18604
19468
|
function getDonePrds(doneDir) {
|
|
18605
|
-
if (!
|
|
19469
|
+
if (!fs41.existsSync(doneDir)) {
|
|
18606
19470
|
return [];
|
|
18607
19471
|
}
|
|
18608
|
-
return
|
|
19472
|
+
return fs41.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
|
|
18609
19473
|
}
|
|
18610
19474
|
function retryCommand(program2) {
|
|
18611
19475
|
program2.command("retry <prdName>").description("Move a completed PRD from done/ back to pending").action((prdName) => {
|
|
18612
19476
|
const projectDir = process.cwd();
|
|
18613
19477
|
const config = loadConfig(projectDir);
|
|
18614
|
-
const prdDir =
|
|
18615
|
-
const doneDir =
|
|
19478
|
+
const prdDir = path42.join(projectDir, config.prdDir);
|
|
19479
|
+
const doneDir = path42.join(prdDir, "done");
|
|
18616
19480
|
const normalizedPrdName = normalizePrdName(prdName);
|
|
18617
|
-
const pendingPath =
|
|
18618
|
-
if (
|
|
19481
|
+
const pendingPath = path42.join(prdDir, normalizedPrdName);
|
|
19482
|
+
if (fs41.existsSync(pendingPath)) {
|
|
18619
19483
|
info(`"${normalizedPrdName}" is already pending, nothing to retry.`);
|
|
18620
19484
|
return;
|
|
18621
19485
|
}
|
|
18622
|
-
const donePath =
|
|
18623
|
-
if (
|
|
18624
|
-
|
|
19486
|
+
const donePath = path42.join(doneDir, normalizedPrdName);
|
|
19487
|
+
if (fs41.existsSync(donePath)) {
|
|
19488
|
+
fs41.renameSync(donePath, pendingPath);
|
|
18625
19489
|
success(`Moved "${normalizedPrdName}" back to pending.`);
|
|
18626
19490
|
dim(`From: ${donePath}`);
|
|
18627
19491
|
dim(`To: ${pendingPath}`);
|
|
@@ -18873,7 +19737,7 @@ function prdsCommand(program2) {
|
|
|
18873
19737
|
|
|
18874
19738
|
// src/commands/cancel.ts
|
|
18875
19739
|
init_dist();
|
|
18876
|
-
import * as
|
|
19740
|
+
import * as fs42 from "fs";
|
|
18877
19741
|
import * as readline2 from "readline";
|
|
18878
19742
|
function getLockFilePaths2(projectDir) {
|
|
18879
19743
|
const runtimeKey = projectRuntimeKey(projectDir);
|
|
@@ -18890,16 +19754,16 @@ async function promptConfirmation(prompt) {
|
|
|
18890
19754
|
input: process.stdin,
|
|
18891
19755
|
output: process.stdout
|
|
18892
19756
|
});
|
|
18893
|
-
return new Promise((
|
|
19757
|
+
return new Promise((resolve11) => {
|
|
18894
19758
|
rl.question(`${prompt} `, (answer) => {
|
|
18895
19759
|
rl.close();
|
|
18896
19760
|
const normalized = answer.toLowerCase().trim();
|
|
18897
|
-
|
|
19761
|
+
resolve11(normalized === "y" || normalized === "yes");
|
|
18898
19762
|
});
|
|
18899
19763
|
});
|
|
18900
19764
|
}
|
|
18901
19765
|
function sleep2(ms) {
|
|
18902
|
-
return new Promise((
|
|
19766
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
18903
19767
|
}
|
|
18904
19768
|
function isProcessRunning3(pid) {
|
|
18905
19769
|
try {
|
|
@@ -18920,7 +19784,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
18920
19784
|
const pid = lockStatus.pid;
|
|
18921
19785
|
if (!lockStatus.running) {
|
|
18922
19786
|
try {
|
|
18923
|
-
|
|
19787
|
+
fs42.unlinkSync(lockPath);
|
|
18924
19788
|
return {
|
|
18925
19789
|
success: true,
|
|
18926
19790
|
message: `${processType} is not running (cleaned up stale lock file for PID ${pid})`,
|
|
@@ -18958,7 +19822,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
18958
19822
|
await sleep2(3e3);
|
|
18959
19823
|
if (!isProcessRunning3(pid)) {
|
|
18960
19824
|
try {
|
|
18961
|
-
|
|
19825
|
+
fs42.unlinkSync(lockPath);
|
|
18962
19826
|
} catch {
|
|
18963
19827
|
}
|
|
18964
19828
|
return {
|
|
@@ -18993,7 +19857,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
18993
19857
|
await sleep2(500);
|
|
18994
19858
|
if (!isProcessRunning3(pid)) {
|
|
18995
19859
|
try {
|
|
18996
|
-
|
|
19860
|
+
fs42.unlinkSync(lockPath);
|
|
18997
19861
|
} catch {
|
|
18998
19862
|
}
|
|
18999
19863
|
return {
|
|
@@ -19054,31 +19918,31 @@ function cancelCommand(program2) {
|
|
|
19054
19918
|
|
|
19055
19919
|
// src/commands/slice.ts
|
|
19056
19920
|
init_dist();
|
|
19057
|
-
import * as
|
|
19058
|
-
import * as
|
|
19921
|
+
import * as fs43 from "fs";
|
|
19922
|
+
import * as path43 from "path";
|
|
19059
19923
|
function plannerLockPath2(projectDir) {
|
|
19060
19924
|
return `${LOCK_FILE_PREFIX}slicer-${projectRuntimeKey(projectDir)}.lock`;
|
|
19061
19925
|
}
|
|
19062
19926
|
function acquirePlannerLock(projectDir) {
|
|
19063
19927
|
const lockFile = plannerLockPath2(projectDir);
|
|
19064
|
-
if (
|
|
19065
|
-
const pidRaw =
|
|
19928
|
+
if (fs43.existsSync(lockFile)) {
|
|
19929
|
+
const pidRaw = fs43.readFileSync(lockFile, "utf-8").trim();
|
|
19066
19930
|
const pid = parseInt(pidRaw, 10);
|
|
19067
19931
|
if (!Number.isNaN(pid) && isProcessRunning(pid)) {
|
|
19068
19932
|
return { acquired: false, lockFile, pid };
|
|
19069
19933
|
}
|
|
19070
19934
|
try {
|
|
19071
|
-
|
|
19935
|
+
fs43.unlinkSync(lockFile);
|
|
19072
19936
|
} catch {
|
|
19073
19937
|
}
|
|
19074
19938
|
}
|
|
19075
|
-
|
|
19939
|
+
fs43.writeFileSync(lockFile, String(process.pid));
|
|
19076
19940
|
return { acquired: true, lockFile };
|
|
19077
19941
|
}
|
|
19078
19942
|
function releasePlannerLock(lockFile) {
|
|
19079
19943
|
try {
|
|
19080
|
-
if (
|
|
19081
|
-
|
|
19944
|
+
if (fs43.existsSync(lockFile)) {
|
|
19945
|
+
fs43.unlinkSync(lockFile);
|
|
19082
19946
|
}
|
|
19083
19947
|
} catch {
|
|
19084
19948
|
}
|
|
@@ -19087,12 +19951,12 @@ function resolvePlannerIssueColumn(config) {
|
|
|
19087
19951
|
return config.roadmapScanner.issueColumn === "Draft" ? "Draft" : "Ready";
|
|
19088
19952
|
}
|
|
19089
19953
|
function buildPlannerIssueBody(projectDir, config, result) {
|
|
19090
|
-
const relativePrdPath =
|
|
19091
|
-
const absolutePrdPath =
|
|
19954
|
+
const relativePrdPath = path43.join(config.prdDir, result.file ?? "").replace(/\\/g, "/");
|
|
19955
|
+
const absolutePrdPath = path43.join(projectDir, config.prdDir, result.file ?? "");
|
|
19092
19956
|
const sourceItem = result.item;
|
|
19093
19957
|
let prdContent;
|
|
19094
19958
|
try {
|
|
19095
|
-
prdContent =
|
|
19959
|
+
prdContent = fs43.readFileSync(absolutePrdPath, "utf-8");
|
|
19096
19960
|
} catch {
|
|
19097
19961
|
prdContent = `Unable to read generated PRD file at \`${relativePrdPath}\`.`;
|
|
19098
19962
|
}
|
|
@@ -19132,10 +19996,10 @@ async function createPlannerIssue(projectDir, config, result) {
|
|
|
19132
19996
|
return { created: false, skippedReason: "board-not-configured" };
|
|
19133
19997
|
}
|
|
19134
19998
|
const issueTitle = `PRD: ${result.item.title}`;
|
|
19135
|
-
const
|
|
19999
|
+
const normalizeTitle2 = (t) => t.replace(/^PRD:\s*/i, "").trim().toLowerCase();
|
|
19136
20000
|
const existingIssues = await provider.getAllIssues();
|
|
19137
20001
|
const existing = existingIssues.find(
|
|
19138
|
-
(issue2) =>
|
|
20002
|
+
(issue2) => normalizeTitle2(issue2.title) === normalizeTitle2(result.item.title)
|
|
19139
20003
|
);
|
|
19140
20004
|
if (existing) {
|
|
19141
20005
|
return {
|
|
@@ -19322,7 +20186,7 @@ function sliceCommand(program2) {
|
|
|
19322
20186
|
if (!options.dryRun && result.sliced) {
|
|
19323
20187
|
await sendNotifications(config, {
|
|
19324
20188
|
event: "run_succeeded",
|
|
19325
|
-
projectName:
|
|
20189
|
+
projectName: path43.basename(projectDir),
|
|
19326
20190
|
exitCode,
|
|
19327
20191
|
provider: config.provider,
|
|
19328
20192
|
prTitle: result.item?.title
|
|
@@ -19330,7 +20194,7 @@ function sliceCommand(program2) {
|
|
|
19330
20194
|
} else if (!options.dryRun && !nothingPending) {
|
|
19331
20195
|
await sendNotifications(config, {
|
|
19332
20196
|
event: "run_failed",
|
|
19333
|
-
projectName:
|
|
20197
|
+
projectName: path43.basename(projectDir),
|
|
19334
20198
|
exitCode,
|
|
19335
20199
|
provider: config.provider
|
|
19336
20200
|
});
|
|
@@ -19363,20 +20227,20 @@ function sliceCommand(program2) {
|
|
|
19363
20227
|
// src/commands/state.ts
|
|
19364
20228
|
init_dist();
|
|
19365
20229
|
import * as os9 from "os";
|
|
19366
|
-
import * as
|
|
20230
|
+
import * as path44 from "path";
|
|
19367
20231
|
import chalk5 from "chalk";
|
|
19368
20232
|
import { Command } from "commander";
|
|
19369
20233
|
function createStateCommand() {
|
|
19370
20234
|
const state = new Command("state");
|
|
19371
20235
|
state.description("Manage Night Watch state");
|
|
19372
20236
|
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 ||
|
|
20237
|
+
const nightWatchHome = process.env.NIGHT_WATCH_HOME || path44.join(os9.homedir(), GLOBAL_CONFIG_DIR);
|
|
19374
20238
|
if (opts.dryRun) {
|
|
19375
20239
|
console.log(chalk5.cyan("Dry-run mode: no changes will be made.\n"));
|
|
19376
20240
|
console.log(`Legacy JSON files that would be migrated from: ${chalk5.bold(nightWatchHome)}`);
|
|
19377
|
-
console.log(` ${
|
|
19378
|
-
console.log(` ${
|
|
19379
|
-
console.log(` ${
|
|
20241
|
+
console.log(` ${path44.join(nightWatchHome, "projects.json")}`);
|
|
20242
|
+
console.log(` ${path44.join(nightWatchHome, "history.json")}`);
|
|
20243
|
+
console.log(` ${path44.join(nightWatchHome, "prd-states.json")}`);
|
|
19380
20244
|
console.log(` <project>/<prdDir>/.roadmap-state.json (per project)`);
|
|
19381
20245
|
console.log(chalk5.dim("\nRun without --dry-run to apply the migration."));
|
|
19382
20246
|
return;
|
|
@@ -19414,8 +20278,8 @@ function createStateCommand() {
|
|
|
19414
20278
|
init_dist();
|
|
19415
20279
|
init_dist();
|
|
19416
20280
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
19417
|
-
import * as
|
|
19418
|
-
import * as
|
|
20281
|
+
import * as fs44 from "fs";
|
|
20282
|
+
import * as path45 from "path";
|
|
19419
20283
|
import * as readline3 from "readline";
|
|
19420
20284
|
import chalk6 from "chalk";
|
|
19421
20285
|
async function run(fn) {
|
|
@@ -19437,7 +20301,7 @@ function getProvider(config, cwd) {
|
|
|
19437
20301
|
return createBoardProvider(bp, cwd);
|
|
19438
20302
|
}
|
|
19439
20303
|
function defaultBoardTitle(cwd) {
|
|
19440
|
-
return `${
|
|
20304
|
+
return `${path45.basename(cwd)} Night Watch`;
|
|
19441
20305
|
}
|
|
19442
20306
|
async function ensureBoardConfigured(config, cwd, provider, options) {
|
|
19443
20307
|
if (config.boardProvider?.projectNumber) {
|
|
@@ -19469,10 +20333,10 @@ async function confirmPrompt(question) {
|
|
|
19469
20333
|
input: process.stdin,
|
|
19470
20334
|
output: process.stdout
|
|
19471
20335
|
});
|
|
19472
|
-
return new Promise((
|
|
20336
|
+
return new Promise((resolve11) => {
|
|
19473
20337
|
rl.question(question, (answer) => {
|
|
19474
20338
|
rl.close();
|
|
19475
|
-
|
|
20339
|
+
resolve11(answer.trim().toLowerCase() === "y");
|
|
19476
20340
|
});
|
|
19477
20341
|
});
|
|
19478
20342
|
}
|
|
@@ -19638,11 +20502,11 @@ function boardCommand(program2) {
|
|
|
19638
20502
|
let body = options.body ?? "";
|
|
19639
20503
|
if (options.bodyFile) {
|
|
19640
20504
|
const filePath = options.bodyFile;
|
|
19641
|
-
if (!
|
|
20505
|
+
if (!fs44.existsSync(filePath)) {
|
|
19642
20506
|
console.error(`File not found: ${filePath}`);
|
|
19643
20507
|
process.exit(1);
|
|
19644
20508
|
}
|
|
19645
|
-
body =
|
|
20509
|
+
body = fs44.readFileSync(filePath, "utf-8");
|
|
19646
20510
|
}
|
|
19647
20511
|
const labels = [];
|
|
19648
20512
|
if (options.label) {
|
|
@@ -19883,12 +20747,12 @@ function boardCommand(program2) {
|
|
|
19883
20747
|
const config = loadConfig(cwd);
|
|
19884
20748
|
const provider = getProvider(config, cwd);
|
|
19885
20749
|
await ensureBoardConfigured(config, cwd, provider);
|
|
19886
|
-
const roadmapPath = options.roadmap ??
|
|
19887
|
-
if (!
|
|
20750
|
+
const roadmapPath = options.roadmap ?? path45.join(cwd, "ROADMAP.md");
|
|
20751
|
+
if (!fs44.existsSync(roadmapPath)) {
|
|
19888
20752
|
console.error(`Roadmap file not found: ${roadmapPath}`);
|
|
19889
20753
|
process.exit(1);
|
|
19890
20754
|
}
|
|
19891
|
-
const roadmapContent =
|
|
20755
|
+
const roadmapContent = fs44.readFileSync(roadmapPath, "utf-8");
|
|
19892
20756
|
const items = parseRoadmap(roadmapContent);
|
|
19893
20757
|
const uncheckedItems = getUncheckedItems(items);
|
|
19894
20758
|
if (uncheckedItems.length === 0) {
|
|
@@ -20012,7 +20876,7 @@ function boardCommand(program2) {
|
|
|
20012
20876
|
// src/commands/queue.ts
|
|
20013
20877
|
init_dist();
|
|
20014
20878
|
init_dist();
|
|
20015
|
-
import * as
|
|
20879
|
+
import * as path46 from "path";
|
|
20016
20880
|
import { spawn as spawn8 } from "child_process";
|
|
20017
20881
|
import chalk7 from "chalk";
|
|
20018
20882
|
import { Command as Command2 } from "commander";
|
|
@@ -20025,7 +20889,8 @@ var VALID_JOB_TYPES2 = [
|
|
|
20025
20889
|
"slicer",
|
|
20026
20890
|
"planner",
|
|
20027
20891
|
"pr-resolver",
|
|
20028
|
-
"merger"
|
|
20892
|
+
"merger",
|
|
20893
|
+
"manager"
|
|
20029
20894
|
];
|
|
20030
20895
|
function formatTimestamp(unixTs) {
|
|
20031
20896
|
if (unixTs === null) return "-";
|
|
@@ -20148,7 +21013,7 @@ function createQueueCommand() {
|
|
|
20148
21013
|
process.exit(1);
|
|
20149
21014
|
}
|
|
20150
21015
|
}
|
|
20151
|
-
const projectName =
|
|
21016
|
+
const projectName = path46.basename(projectDir);
|
|
20152
21017
|
const queueConfig = loadConfig(projectDir).queue;
|
|
20153
21018
|
if (isJobPaused(projectDir, jobType)) {
|
|
20154
21019
|
logger6.info(`Skipping enqueue for paused job: ${jobType}`);
|
|
@@ -20166,7 +21031,7 @@ function createQueueCommand() {
|
|
|
20166
21031
|
});
|
|
20167
21032
|
queue.command("resolve-key").description("Resolve the provider bucket key for a given project and job type").requiredOption("--project <dir>", "Project directory").requiredOption(
|
|
20168
21033
|
"--job-type <type>",
|
|
20169
|
-
"Job type (executor, reviewer, qa, audit, slicer, planner, pr-resolver, merger)"
|
|
21034
|
+
"Job type (executor, reviewer, qa, audit, slicer, planner, pr-resolver, merger, manager)"
|
|
20170
21035
|
).action((opts) => {
|
|
20171
21036
|
try {
|
|
20172
21037
|
const config = loadConfig(opts.project);
|
|
@@ -20249,7 +21114,7 @@ function createQueueCommand() {
|
|
|
20249
21114
|
if (isJobPaused(projectDir, jobType)) {
|
|
20250
21115
|
process.exit(2);
|
|
20251
21116
|
}
|
|
20252
|
-
const projectName =
|
|
21117
|
+
const projectName = path46.basename(projectDir);
|
|
20253
21118
|
const callerPid = opts.pid ? parseInt(opts.pid, 10) : void 0;
|
|
20254
21119
|
const result = claimJobSlot(
|
|
20255
21120
|
projectDir,
|
|
@@ -20316,7 +21181,8 @@ var QUEUE_MARKER_KEYS = /* @__PURE__ */ new Set([
|
|
|
20316
21181
|
"NW_AUTO_MERGE",
|
|
20317
21182
|
"NW_AUTO_MERGE_METHOD",
|
|
20318
21183
|
"NW_MAX_RUNTIME",
|
|
20319
|
-
"NW_QA_MAX_RUNTIME"
|
|
21184
|
+
"NW_QA_MAX_RUNTIME",
|
|
21185
|
+
"NW_MANAGER_MAX_RUNTIME"
|
|
20320
21186
|
]);
|
|
20321
21187
|
function filterQueueMarkers(envJson) {
|
|
20322
21188
|
const result = {};
|
|
@@ -20345,6 +21211,8 @@ function getScriptNameForJobType(jobType) {
|
|
|
20345
21211
|
return "night-watch-pr-resolver-cron.sh";
|
|
20346
21212
|
case "merger":
|
|
20347
21213
|
return "night-watch-merger-cron.sh";
|
|
21214
|
+
case "manager":
|
|
21215
|
+
return "night-watch-manager-cron.sh";
|
|
20348
21216
|
default:
|
|
20349
21217
|
return null;
|
|
20350
21218
|
}
|
|
@@ -20382,7 +21250,7 @@ function notifyCommand(program2) {
|
|
|
20382
21250
|
|
|
20383
21251
|
// src/commands/summary.ts
|
|
20384
21252
|
init_dist();
|
|
20385
|
-
import
|
|
21253
|
+
import path47 from "path";
|
|
20386
21254
|
import chalk8 from "chalk";
|
|
20387
21255
|
function formatDuration2(seconds) {
|
|
20388
21256
|
if (seconds === null) return "-";
|
|
@@ -20412,7 +21280,7 @@ function formatJobStatus(status) {
|
|
|
20412
21280
|
return chalk8.dim(status);
|
|
20413
21281
|
}
|
|
20414
21282
|
function getProjectName2(projectPath) {
|
|
20415
|
-
return
|
|
21283
|
+
return path47.basename(projectPath) || projectPath;
|
|
20416
21284
|
}
|
|
20417
21285
|
function formatProvider(providerKey) {
|
|
20418
21286
|
return providerKey.split(":")[0] || providerKey;
|
|
@@ -20531,7 +21399,7 @@ function summaryCommand(program2) {
|
|
|
20531
21399
|
// src/commands/resolve.ts
|
|
20532
21400
|
init_dist();
|
|
20533
21401
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
20534
|
-
import * as
|
|
21402
|
+
import * as path48 from "path";
|
|
20535
21403
|
function buildEnvVars6(config, options) {
|
|
20536
21404
|
const env = buildBaseEnvVars(config, "pr-resolver", options.dryRun);
|
|
20537
21405
|
env.NW_PR_RESOLVER_MAX_RUNTIME = String(config.prResolver.maxRuntime);
|
|
@@ -20684,7 +21552,7 @@ ${stderr}`);
|
|
|
20684
21552
|
}
|
|
20685
21553
|
await sendNotifications(config, {
|
|
20686
21554
|
event: notificationEvent,
|
|
20687
|
-
projectName:
|
|
21555
|
+
projectName: path48.basename(projectDir),
|
|
20688
21556
|
exitCode,
|
|
20689
21557
|
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL)
|
|
20690
21558
|
});
|
|
@@ -20699,7 +21567,7 @@ ${stderr}`);
|
|
|
20699
21567
|
|
|
20700
21568
|
// src/commands/merge.ts
|
|
20701
21569
|
init_dist();
|
|
20702
|
-
import * as
|
|
21570
|
+
import * as path49 from "path";
|
|
20703
21571
|
function buildEnvVars7(config, options) {
|
|
20704
21572
|
const env = buildBaseEnvVars(config, "merger", options.dryRun);
|
|
20705
21573
|
env.NW_MERGER_MAX_RUNTIME = String(config.merger.maxRuntime);
|
|
@@ -20835,7 +21703,7 @@ ${stderr}`);
|
|
|
20835
21703
|
if (notificationEvent) {
|
|
20836
21704
|
await sendNotifications(config, {
|
|
20837
21705
|
event: notificationEvent,
|
|
20838
|
-
projectName:
|
|
21706
|
+
projectName: path49.basename(projectDir),
|
|
20839
21707
|
exitCode,
|
|
20840
21708
|
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL)
|
|
20841
21709
|
});
|
|
@@ -20849,12 +21717,208 @@ ${stderr}`);
|
|
|
20849
21717
|
});
|
|
20850
21718
|
}
|
|
20851
21719
|
|
|
21720
|
+
// src/commands/manager.ts
|
|
21721
|
+
init_dist();
|
|
21722
|
+
import * as path50 from "path";
|
|
21723
|
+
function resolveRunManager() {
|
|
21724
|
+
const runManager2 = runManager;
|
|
21725
|
+
if (typeof runManager2 !== "function") {
|
|
21726
|
+
throw new Error(
|
|
21727
|
+
"Manager runner is not available in @night-watch/core. Update core to include runManager(projectDir, config, options)."
|
|
21728
|
+
);
|
|
21729
|
+
}
|
|
21730
|
+
return runManager2;
|
|
21731
|
+
}
|
|
21732
|
+
function writeJson(value) {
|
|
21733
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
21734
|
+
`);
|
|
21735
|
+
}
|
|
21736
|
+
function parseTimeout(timeout) {
|
|
21737
|
+
if (!timeout) return void 0;
|
|
21738
|
+
const parsed = parseInt(timeout, 10);
|
|
21739
|
+
return Number.isNaN(parsed) || parsed < 0 ? void 0 : parsed;
|
|
21740
|
+
}
|
|
21741
|
+
function buildManagerRunOptions(options) {
|
|
21742
|
+
const timeout = parseTimeout(options.timeout);
|
|
21743
|
+
return {
|
|
21744
|
+
dryRun: options.dryRun === true,
|
|
21745
|
+
...timeout !== void 0 ? { timeout } : {},
|
|
21746
|
+
...options.provider ? { provider: options.provider } : {}
|
|
21747
|
+
};
|
|
21748
|
+
}
|
|
21749
|
+
function applyManagerCliOverrides(config, options) {
|
|
21750
|
+
const timeout = parseTimeout(options.timeout);
|
|
21751
|
+
let overridden = config;
|
|
21752
|
+
if (timeout !== void 0) {
|
|
21753
|
+
overridden = {
|
|
21754
|
+
...overridden,
|
|
21755
|
+
manager: {
|
|
21756
|
+
...overridden.manager,
|
|
21757
|
+
maxRuntime: timeout
|
|
21758
|
+
}
|
|
21759
|
+
};
|
|
21760
|
+
}
|
|
21761
|
+
if (options.provider) {
|
|
21762
|
+
overridden = {
|
|
21763
|
+
...overridden,
|
|
21764
|
+
_cliProviderOverride: options.provider
|
|
21765
|
+
};
|
|
21766
|
+
}
|
|
21767
|
+
return overridden;
|
|
21768
|
+
}
|
|
21769
|
+
function getManagerConfig(config) {
|
|
21770
|
+
return config.manager;
|
|
21771
|
+
}
|
|
21772
|
+
function buildJsonResult(result, options) {
|
|
21773
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
21774
|
+
return {
|
|
21775
|
+
...result,
|
|
21776
|
+
dryRun: options.dryRun
|
|
21777
|
+
};
|
|
21778
|
+
}
|
|
21779
|
+
return {
|
|
21780
|
+
dryRun: options.dryRun,
|
|
21781
|
+
result
|
|
21782
|
+
};
|
|
21783
|
+
}
|
|
21784
|
+
function resultExitCode(result) {
|
|
21785
|
+
if (!result || typeof result !== "object") return 0;
|
|
21786
|
+
const record = result;
|
|
21787
|
+
if (record.ok === false || record.success === false || typeof record.error === "string") {
|
|
21788
|
+
return 1;
|
|
21789
|
+
}
|
|
21790
|
+
return 0;
|
|
21791
|
+
}
|
|
21792
|
+
async function sendManagerNotifications(config, projectDir, result) {
|
|
21793
|
+
if (!result || typeof result !== "object") return;
|
|
21794
|
+
const decisions = result.notificationDecisions;
|
|
21795
|
+
if (!Array.isArray(decisions)) return;
|
|
21796
|
+
for (const decision of decisions) {
|
|
21797
|
+
if (!decision || typeof decision !== "object") continue;
|
|
21798
|
+
const item = decision;
|
|
21799
|
+
if (!item.shouldNotify || !item.event) continue;
|
|
21800
|
+
await sendNotifications(config, {
|
|
21801
|
+
event: item.event,
|
|
21802
|
+
projectName: path50.basename(projectDir),
|
|
21803
|
+
provider: resolveJobProvider(config, "manager"),
|
|
21804
|
+
exitCode: 0,
|
|
21805
|
+
failureReason: item.title,
|
|
21806
|
+
failureDetail: item.body
|
|
21807
|
+
});
|
|
21808
|
+
}
|
|
21809
|
+
}
|
|
21810
|
+
function printHumanResult(result, options) {
|
|
21811
|
+
const payload = buildJsonResult(result, options);
|
|
21812
|
+
header(options.dryRun ? "Dry Run: Manager" : "Manager Result");
|
|
21813
|
+
const table = createTable({ head: ["Metric", "Value"] });
|
|
21814
|
+
for (const key of [
|
|
21815
|
+
"summary",
|
|
21816
|
+
"findings",
|
|
21817
|
+
"createdIssues",
|
|
21818
|
+
"createdDrafts",
|
|
21819
|
+
"skippedDuplicates",
|
|
21820
|
+
"blockedItems"
|
|
21821
|
+
]) {
|
|
21822
|
+
const value = payload[key];
|
|
21823
|
+
if (value === void 0) continue;
|
|
21824
|
+
table.push([key, Array.isArray(value) ? String(value.length) : String(value)]);
|
|
21825
|
+
}
|
|
21826
|
+
console.log(table.length > 0 ? table.toString() : JSON.stringify(payload, null, 2));
|
|
21827
|
+
}
|
|
21828
|
+
function managerCommand(program2) {
|
|
21829
|
+
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) => {
|
|
21830
|
+
const projectDir = process.cwd();
|
|
21831
|
+
let config = loadConfig(projectDir);
|
|
21832
|
+
config = applyManagerCliOverrides(config, options);
|
|
21833
|
+
const managerConfig = getManagerConfig(config);
|
|
21834
|
+
const runOptions = buildManagerRunOptions(options);
|
|
21835
|
+
if (!managerConfig.enabled && !runOptions.dryRun) {
|
|
21836
|
+
if (options.json) {
|
|
21837
|
+
writeJson({ dryRun: false, skipped: true, reason: "manager-disabled" });
|
|
21838
|
+
} else {
|
|
21839
|
+
info("Manager is disabled in config; skipping run.");
|
|
21840
|
+
}
|
|
21841
|
+
process.exit(0);
|
|
21842
|
+
}
|
|
21843
|
+
const startedAt = Date.now();
|
|
21844
|
+
let exitCode = 0;
|
|
21845
|
+
const run2 = async () => {
|
|
21846
|
+
if (!runOptions.dryRun) {
|
|
21847
|
+
await maybeApplyCronSchedulingDelay(config, "manager", projectDir);
|
|
21848
|
+
}
|
|
21849
|
+
const runner = resolveRunManager();
|
|
21850
|
+
return runner(projectDir, config, runOptions);
|
|
21851
|
+
};
|
|
21852
|
+
try {
|
|
21853
|
+
const spinner = options.json ? null : createSpinner("Running Manager...");
|
|
21854
|
+
spinner?.start();
|
|
21855
|
+
const result = await run2();
|
|
21856
|
+
exitCode = resultExitCode(result);
|
|
21857
|
+
if (!runOptions.dryRun) {
|
|
21858
|
+
await sendManagerNotifications(config, projectDir, result);
|
|
21859
|
+
try {
|
|
21860
|
+
recordJobOutcome({
|
|
21861
|
+
config,
|
|
21862
|
+
exitCode,
|
|
21863
|
+
finishedAt: Date.now(),
|
|
21864
|
+
jobType: "manager",
|
|
21865
|
+
metadata: buildJsonResult(result, runOptions),
|
|
21866
|
+
projectDir,
|
|
21867
|
+
providerKey: resolveJobProvider(config, "manager"),
|
|
21868
|
+
startedAt,
|
|
21869
|
+
stdout: typeof result === "string" ? result : JSON.stringify(result)
|
|
21870
|
+
});
|
|
21871
|
+
} catch {
|
|
21872
|
+
}
|
|
21873
|
+
}
|
|
21874
|
+
if (options.json) {
|
|
21875
|
+
writeJson(buildJsonResult(result, runOptions));
|
|
21876
|
+
} else {
|
|
21877
|
+
if (exitCode === 0) {
|
|
21878
|
+
spinner?.succeed("Manager completed successfully");
|
|
21879
|
+
} else {
|
|
21880
|
+
spinner?.fail("Manager completed with errors");
|
|
21881
|
+
}
|
|
21882
|
+
printHumanResult(result, runOptions);
|
|
21883
|
+
}
|
|
21884
|
+
} catch (err) {
|
|
21885
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
21886
|
+
if (!runOptions.dryRun) {
|
|
21887
|
+
try {
|
|
21888
|
+
recordJobOutcome({
|
|
21889
|
+
config,
|
|
21890
|
+
exitCode: 1,
|
|
21891
|
+
finishedAt: Date.now(),
|
|
21892
|
+
jobType: "manager",
|
|
21893
|
+
metadata: { error: message },
|
|
21894
|
+
projectDir,
|
|
21895
|
+
providerKey: resolveJobProvider(config, "manager"),
|
|
21896
|
+
startedAt,
|
|
21897
|
+
stderr: message
|
|
21898
|
+
});
|
|
21899
|
+
} catch {
|
|
21900
|
+
}
|
|
21901
|
+
}
|
|
21902
|
+
if (options.json) {
|
|
21903
|
+
process.stderr.write(
|
|
21904
|
+
`${JSON.stringify({ dryRun: runOptions.dryRun, ok: false, error: message }, null, 2)}
|
|
21905
|
+
`
|
|
21906
|
+
);
|
|
21907
|
+
} else {
|
|
21908
|
+
error(`Manager failed: ${message}`);
|
|
21909
|
+
}
|
|
21910
|
+
process.exit(1);
|
|
21911
|
+
}
|
|
21912
|
+
process.exit(exitCode);
|
|
21913
|
+
});
|
|
21914
|
+
}
|
|
21915
|
+
|
|
20852
21916
|
// src/commands/agent.ts
|
|
20853
21917
|
init_dist();
|
|
20854
21918
|
var SCHEMA_VERSION2 = 1;
|
|
20855
21919
|
var JSON_OPTION = "--json";
|
|
20856
21920
|
var JSON_OPTION_DESCRIPTION = "Output as JSON";
|
|
20857
|
-
function
|
|
21921
|
+
function writeJson2(value) {
|
|
20858
21922
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
20859
21923
|
`);
|
|
20860
21924
|
}
|
|
@@ -21011,7 +22075,7 @@ function normalizeJobType(job) {
|
|
|
21011
22075
|
function agentCommand(program2) {
|
|
21012
22076
|
const agent = program2.command("agent").description("Machine-readable agent operations");
|
|
21013
22077
|
agent.command("status").description("Print a stable machine-readable project snapshot").requiredOption(JSON_OPTION, "Output status as JSON").action(async () => {
|
|
21014
|
-
|
|
22078
|
+
writeJson2(await buildAgentStatus(process.cwd()));
|
|
21015
22079
|
});
|
|
21016
22080
|
}
|
|
21017
22081
|
function configCommand(program2) {
|
|
@@ -21019,18 +22083,18 @@ function configCommand(program2) {
|
|
|
21019
22083
|
config.command("list").description("Print resolved config").option(JSON_OPTION, JSON_OPTION_DESCRIPTION).action((options) => {
|
|
21020
22084
|
const value = loadConfig(process.cwd());
|
|
21021
22085
|
if (options.json) {
|
|
21022
|
-
|
|
22086
|
+
writeJson2({ schemaVersion: SCHEMA_VERSION2, config: value });
|
|
21023
22087
|
} else {
|
|
21024
|
-
|
|
22088
|
+
writeJson2(value);
|
|
21025
22089
|
}
|
|
21026
22090
|
});
|
|
21027
22091
|
config.command("get <path>").description("Read a resolved config value by dot path").option(JSON_OPTION, JSON_OPTION_DESCRIPTION).action((dotPath, options) => {
|
|
21028
22092
|
try {
|
|
21029
22093
|
const result = getConfigValue(process.cwd(), dotPath);
|
|
21030
22094
|
if (options.json) {
|
|
21031
|
-
|
|
22095
|
+
writeJson2({ schemaVersion: SCHEMA_VERSION2, ...result });
|
|
21032
22096
|
} else {
|
|
21033
|
-
|
|
22097
|
+
writeJson2(result.value);
|
|
21034
22098
|
}
|
|
21035
22099
|
} catch (error2) {
|
|
21036
22100
|
fail(error2 instanceof Error ? error2.message : String(error2), options);
|
|
@@ -21040,7 +22104,7 @@ function configCommand(program2) {
|
|
|
21040
22104
|
try {
|
|
21041
22105
|
const result = setConfigValue(process.cwd(), dotPath, parseConfigValue(rawValue));
|
|
21042
22106
|
if (options.json) {
|
|
21043
|
-
|
|
22107
|
+
writeJson2({ schemaVersion: SCHEMA_VERSION2, ok: true, ...result });
|
|
21044
22108
|
} else {
|
|
21045
22109
|
process.stdout.write(`Updated ${result.path}
|
|
21046
22110
|
`);
|
|
@@ -21056,7 +22120,7 @@ function healthCommand(program2) {
|
|
|
21056
22120
|
const snapshot = await fetchStatusSnapshot(process.cwd(), config);
|
|
21057
22121
|
const health = buildHealth(snapshot, config);
|
|
21058
22122
|
if (options.json) {
|
|
21059
|
-
|
|
22123
|
+
writeJson2(health);
|
|
21060
22124
|
} else {
|
|
21061
22125
|
for (const check of health.checks) {
|
|
21062
22126
|
process.stdout.write(`${check.ok ? "ok" : "fail"} ${check.name}: ${check.message}
|
|
@@ -21075,7 +22139,7 @@ function jobCommand(program2) {
|
|
|
21075
22139
|
const jobType = normalizeJobType(jobName);
|
|
21076
22140
|
const result = setConfigValue(process.cwd(), `pausedJobs.${jobType}`, true);
|
|
21077
22141
|
if (options.json) {
|
|
21078
|
-
|
|
22142
|
+
writeJson2({
|
|
21079
22143
|
schemaVersion: SCHEMA_VERSION2,
|
|
21080
22144
|
ok: true,
|
|
21081
22145
|
job: jobType,
|
|
@@ -21094,7 +22158,7 @@ function jobCommand(program2) {
|
|
|
21094
22158
|
const jobType = normalizeJobType(jobName);
|
|
21095
22159
|
const result = setConfigValue(process.cwd(), `pausedJobs.${jobType}`, false);
|
|
21096
22160
|
if (options.json) {
|
|
21097
|
-
|
|
22161
|
+
writeJson2({
|
|
21098
22162
|
schemaVersion: SCHEMA_VERSION2,
|
|
21099
22163
|
ok: true,
|
|
21100
22164
|
job: jobType,
|
|
@@ -21121,17 +22185,17 @@ function jobCommand(program2) {
|
|
|
21121
22185
|
|
|
21122
22186
|
// src/cli.ts
|
|
21123
22187
|
var __filename5 = fileURLToPath6(import.meta.url);
|
|
21124
|
-
var __dirname5 =
|
|
22188
|
+
var __dirname5 = dirname13(__filename5);
|
|
21125
22189
|
function findPackageRoot(dir) {
|
|
21126
22190
|
let d = dir;
|
|
21127
22191
|
for (let i = 0; i < 5; i++) {
|
|
21128
|
-
if (
|
|
21129
|
-
d =
|
|
22192
|
+
if (existsSync36(join39(d, "package.json"))) return d;
|
|
22193
|
+
d = dirname13(d);
|
|
21130
22194
|
}
|
|
21131
22195
|
return dir;
|
|
21132
22196
|
}
|
|
21133
22197
|
var packageRoot = findPackageRoot(__dirname5);
|
|
21134
|
-
var packageJson = JSON.parse(
|
|
22198
|
+
var packageJson = JSON.parse(readFileSync23(join39(packageRoot, "package.json"), "utf-8"));
|
|
21135
22199
|
var program = new Command3();
|
|
21136
22200
|
program.name("night-watch").description("Autonomous PRD execution using Claude CLI + cron").version(packageJson.version);
|
|
21137
22201
|
initCommand(program);
|
|
@@ -21163,6 +22227,7 @@ notifyCommand(program);
|
|
|
21163
22227
|
summaryCommand(program);
|
|
21164
22228
|
resolveCommand(program);
|
|
21165
22229
|
mergeCommand(program);
|
|
22230
|
+
managerCommand(program);
|
|
21166
22231
|
agentCommand(program);
|
|
21167
22232
|
configCommand(program);
|
|
21168
22233
|
healthCommand(program);
|