@jonit-dev/night-watch-cli 1.8.8-beta.1 → 1.8.8-beta.11
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 +981 -78
- package/dist/cli.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +39 -6
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +8 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +50 -0
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/merge.d.ts +26 -0
- package/dist/commands/merge.d.ts.map +1 -0
- package/dist/commands/merge.js +159 -0
- package/dist/commands/merge.js.map +1 -0
- package/dist/commands/qa.d.ts.map +1 -1
- package/dist/commands/qa.js +2 -0
- package/dist/commands/qa.js.map +1 -1
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +27 -4
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/resolve.d.ts +26 -0
- package/dist/commands/resolve.d.ts.map +1 -0
- package/dist/commands/resolve.js +186 -0
- package/dist/commands/resolve.js.map +1 -0
- package/dist/commands/review.d.ts +5 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +18 -18
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/slice.d.ts +1 -0
- package/dist/commands/slice.d.ts.map +1 -1
- package/dist/commands/slice.js +19 -19
- package/dist/commands/slice.js.map +1 -1
- package/dist/commands/summary.d.ts +14 -0
- package/dist/commands/summary.d.ts.map +1 -0
- package/dist/commands/summary.js +193 -0
- package/dist/commands/summary.js.map +1 -0
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +14 -2
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/scripts/night-watch-helpers.sh +10 -1
- package/dist/scripts/night-watch-merger-cron.sh +321 -0
- package/dist/scripts/night-watch-pr-resolver-cron.sh +402 -0
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +23 -142
- package/dist/scripts/night-watch-qa-cron.sh +30 -4
- package/dist/scripts/test-helpers.bats +45 -0
- package/dist/templates/night-watch-pr-reviewer.md +2 -1
- package/dist/templates/pr-reviewer.md +2 -1
- package/dist/templates/slicer.md +54 -64
- package/dist/web/assets/index-CPQbZ1BL.css +1 -0
- package/dist/web/assets/index-CiRJZI4z.js +386 -0
- package/dist/web/assets/index-ZE5lOeJp.js +386 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -201,6 +201,35 @@ var init_job_registry = __esm({
|
|
|
201
201
|
maxRuntime: 3600
|
|
202
202
|
}
|
|
203
203
|
},
|
|
204
|
+
{
|
|
205
|
+
id: "pr-resolver",
|
|
206
|
+
name: "PR Conflict Solver",
|
|
207
|
+
description: "Resolves merge conflicts via AI rebase; optionally addresses review comments and labels PRs ready-to-merge",
|
|
208
|
+
cliCommand: "resolve",
|
|
209
|
+
logName: "pr-resolver",
|
|
210
|
+
lockSuffix: "-pr-resolver.lock",
|
|
211
|
+
queuePriority: 35,
|
|
212
|
+
envPrefix: "NW_PR_RESOLVER",
|
|
213
|
+
extraFields: [
|
|
214
|
+
{ name: "branchPatterns", type: "string[]", defaultValue: [] },
|
|
215
|
+
{ name: "maxPrsPerRun", type: "number", defaultValue: 0 },
|
|
216
|
+
{ name: "perPrTimeout", type: "number", defaultValue: 600 },
|
|
217
|
+
{ name: "aiConflictResolution", type: "boolean", defaultValue: true },
|
|
218
|
+
{ name: "aiReviewResolution", type: "boolean", defaultValue: false },
|
|
219
|
+
{ name: "readyLabel", type: "string", defaultValue: "ready-to-merge" }
|
|
220
|
+
],
|
|
221
|
+
defaultConfig: {
|
|
222
|
+
enabled: true,
|
|
223
|
+
schedule: "15 6,14,22 * * *",
|
|
224
|
+
maxRuntime: 3600,
|
|
225
|
+
branchPatterns: [],
|
|
226
|
+
maxPrsPerRun: 0,
|
|
227
|
+
perPrTimeout: 600,
|
|
228
|
+
aiConflictResolution: true,
|
|
229
|
+
aiReviewResolution: false,
|
|
230
|
+
readyLabel: "ready-to-merge"
|
|
231
|
+
}
|
|
232
|
+
},
|
|
204
233
|
{
|
|
205
234
|
id: "slicer",
|
|
206
235
|
name: "Slicer",
|
|
@@ -234,7 +263,8 @@ var init_job_registry = __esm({
|
|
|
234
263
|
defaultValue: "both"
|
|
235
264
|
},
|
|
236
265
|
{ name: "skipLabel", type: "string", defaultValue: "skip-qa" },
|
|
237
|
-
{ name: "autoInstallPlaywright", type: "boolean", defaultValue: true }
|
|
266
|
+
{ name: "autoInstallPlaywright", type: "boolean", defaultValue: true },
|
|
267
|
+
{ name: "validatedLabel", type: "string", defaultValue: "e2e-validated" }
|
|
238
268
|
],
|
|
239
269
|
defaultConfig: {
|
|
240
270
|
enabled: true,
|
|
@@ -243,7 +273,8 @@ var init_job_registry = __esm({
|
|
|
243
273
|
branchPatterns: [],
|
|
244
274
|
artifacts: "both",
|
|
245
275
|
skipLabel: "skip-qa",
|
|
246
|
-
autoInstallPlaywright: true
|
|
276
|
+
autoInstallPlaywright: true,
|
|
277
|
+
validatedLabel: "e2e-validated"
|
|
247
278
|
}
|
|
248
279
|
},
|
|
249
280
|
{
|
|
@@ -288,6 +319,38 @@ var init_job_registry = __esm({
|
|
|
288
319
|
targetColumn: "Draft",
|
|
289
320
|
analysisPrompt: ""
|
|
290
321
|
}
|
|
322
|
+
},
|
|
323
|
+
{
|
|
324
|
+
id: "merger",
|
|
325
|
+
name: "Merge Orchestrator",
|
|
326
|
+
description: "Repo-wide PR merge coordinator \u2014 scans, rebases, and merges in FIFO order",
|
|
327
|
+
cliCommand: "merge",
|
|
328
|
+
logName: "merger",
|
|
329
|
+
lockSuffix: "-merger.lock",
|
|
330
|
+
queuePriority: 45,
|
|
331
|
+
envPrefix: "NW_MERGER",
|
|
332
|
+
extraFields: [
|
|
333
|
+
{
|
|
334
|
+
name: "mergeMethod",
|
|
335
|
+
type: "enum",
|
|
336
|
+
enumValues: ["squash", "merge", "rebase"],
|
|
337
|
+
defaultValue: "squash"
|
|
338
|
+
},
|
|
339
|
+
{ name: "minReviewScore", type: "number", defaultValue: 80 },
|
|
340
|
+
{ name: "branchPatterns", type: "string[]", defaultValue: [] },
|
|
341
|
+
{ name: "rebaseBeforeMerge", type: "boolean", defaultValue: true },
|
|
342
|
+
{ name: "maxPrsPerRun", type: "number", defaultValue: 0 }
|
|
343
|
+
],
|
|
344
|
+
defaultConfig: {
|
|
345
|
+
enabled: false,
|
|
346
|
+
schedule: "55 */4 * * *",
|
|
347
|
+
maxRuntime: 1800,
|
|
348
|
+
mergeMethod: "squash",
|
|
349
|
+
minReviewScore: 80,
|
|
350
|
+
branchPatterns: [],
|
|
351
|
+
rebaseBeforeMerge: true,
|
|
352
|
+
maxPrsPerRun: 0
|
|
353
|
+
}
|
|
291
354
|
}
|
|
292
355
|
];
|
|
293
356
|
JOB_MAP = new Map(JOB_REGISTRY.map((job) => [job.id, job]));
|
|
@@ -308,13 +371,14 @@ function resolveProviderBucketKey(provider, providerEnv) {
|
|
|
308
371
|
return `claude-proxy:${baseUrl}`;
|
|
309
372
|
}
|
|
310
373
|
}
|
|
311
|
-
var DEFAULT_DEFAULT_BRANCH, DEFAULT_PRD_DIR, DEFAULT_MAX_RUNTIME, DEFAULT_REVIEWER_MAX_RUNTIME, DEFAULT_CRON_SCHEDULE, DEFAULT_REVIEWER_SCHEDULE, DEFAULT_CRON_SCHEDULE_OFFSET, DEFAULT_MAX_RETRIES, DEFAULT_REVIEWER_MAX_RETRIES, DEFAULT_REVIEWER_RETRY_DELAY, DEFAULT_REVIEWER_MAX_PRS_PER_RUN, 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, QA_LOG_NAME, DEFAULT_AUDIT_ENABLED, DEFAULT_AUDIT_SCHEDULE, DEFAULT_AUDIT_MAX_RUNTIME, 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, AUDIT_LOG_NAME, PLANNER_LOG_NAME, ANALYTICS_LOG_NAME, VALID_PROVIDERS, VALID_JOB_TYPES, DEFAULT_JOB_PROVIDERS, DEFAULT_PROVIDER_SCHEDULE_OVERRIDES, 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;
|
|
374
|
+
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_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, 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, 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, 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;
|
|
312
375
|
var init_constants = __esm({
|
|
313
376
|
"../core/dist/constants.js"() {
|
|
314
377
|
"use strict";
|
|
315
378
|
init_job_registry();
|
|
316
379
|
DEFAULT_DEFAULT_BRANCH = "";
|
|
317
380
|
DEFAULT_PRD_DIR = "docs/prds";
|
|
381
|
+
DEFAULT_SUMMARY_WINDOW_HOURS = 12;
|
|
318
382
|
DEFAULT_MAX_RUNTIME = 7200;
|
|
319
383
|
DEFAULT_REVIEWER_MAX_RUNTIME = 3600;
|
|
320
384
|
DEFAULT_CRON_SCHEDULE = "5 * * * *";
|
|
@@ -352,7 +416,7 @@ var init_constants = __esm({
|
|
|
352
416
|
slicerSchedule: DEFAULT_SLICER_SCHEDULE,
|
|
353
417
|
slicerMaxRuntime: DEFAULT_SLICER_MAX_RUNTIME,
|
|
354
418
|
priorityMode: "roadmap-first",
|
|
355
|
-
issueColumn: "
|
|
419
|
+
issueColumn: "Ready"
|
|
356
420
|
};
|
|
357
421
|
DEFAULT_TEMPLATES_DIR = ".night-watch/templates";
|
|
358
422
|
DEFAULT_BOARD_PROVIDER = {
|
|
@@ -369,6 +433,7 @@ var init_constants = __esm({
|
|
|
369
433
|
DEFAULT_QA_ARTIFACTS = "both";
|
|
370
434
|
DEFAULT_QA_SKIP_LABEL = "skip-qa";
|
|
371
435
|
DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT = true;
|
|
436
|
+
DEFAULT_QA_VALIDATED_LABEL = "e2e-validated";
|
|
372
437
|
DEFAULT_QA = {
|
|
373
438
|
enabled: DEFAULT_QA_ENABLED,
|
|
374
439
|
schedule: DEFAULT_QA_SCHEDULE,
|
|
@@ -376,7 +441,8 @@ var init_constants = __esm({
|
|
|
376
441
|
branchPatterns: [],
|
|
377
442
|
artifacts: DEFAULT_QA_ARTIFACTS,
|
|
378
443
|
skipLabel: DEFAULT_QA_SKIP_LABEL,
|
|
379
|
-
autoInstallPlaywright: DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT
|
|
444
|
+
autoInstallPlaywright: DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT,
|
|
445
|
+
validatedLabel: DEFAULT_QA_VALIDATED_LABEL
|
|
380
446
|
};
|
|
381
447
|
QA_LOG_NAME = "night-watch-qa";
|
|
382
448
|
DEFAULT_AUDIT_ENABLED = true;
|
|
@@ -405,9 +471,47 @@ If no issues are warranted, output an empty array: []`;
|
|
|
405
471
|
targetColumn: DEFAULT_ANALYTICS_TARGET_COLUMN,
|
|
406
472
|
analysisPrompt: DEFAULT_ANALYTICS_PROMPT
|
|
407
473
|
};
|
|
474
|
+
DEFAULT_PR_RESOLVER_ENABLED = true;
|
|
475
|
+
DEFAULT_PR_RESOLVER_SCHEDULE = "15 6,14,22 * * *";
|
|
476
|
+
DEFAULT_PR_RESOLVER_MAX_RUNTIME = 3600;
|
|
477
|
+
DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN = 0;
|
|
478
|
+
DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT = 600;
|
|
479
|
+
DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION = true;
|
|
480
|
+
DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION = false;
|
|
481
|
+
DEFAULT_PR_RESOLVER_READY_LABEL = "ready-to-merge";
|
|
482
|
+
DEFAULT_PR_RESOLVER = {
|
|
483
|
+
enabled: DEFAULT_PR_RESOLVER_ENABLED,
|
|
484
|
+
schedule: DEFAULT_PR_RESOLVER_SCHEDULE,
|
|
485
|
+
maxRuntime: DEFAULT_PR_RESOLVER_MAX_RUNTIME,
|
|
486
|
+
branchPatterns: [],
|
|
487
|
+
maxPrsPerRun: DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN,
|
|
488
|
+
perPrTimeout: DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT,
|
|
489
|
+
aiConflictResolution: DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION,
|
|
490
|
+
aiReviewResolution: DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION,
|
|
491
|
+
readyLabel: DEFAULT_PR_RESOLVER_READY_LABEL
|
|
492
|
+
};
|
|
493
|
+
DEFAULT_MERGER_ENABLED = false;
|
|
494
|
+
DEFAULT_MERGER_SCHEDULE = "55 */4 * * *";
|
|
495
|
+
DEFAULT_MERGER_MAX_RUNTIME = 1800;
|
|
496
|
+
DEFAULT_MERGER_MERGE_METHOD = "squash";
|
|
497
|
+
DEFAULT_MERGER_MIN_REVIEW_SCORE = 80;
|
|
498
|
+
DEFAULT_MERGER_REBASE_BEFORE_MERGE = true;
|
|
499
|
+
DEFAULT_MERGER_MAX_PRS_PER_RUN = 0;
|
|
500
|
+
DEFAULT_MERGER = {
|
|
501
|
+
enabled: DEFAULT_MERGER_ENABLED,
|
|
502
|
+
schedule: DEFAULT_MERGER_SCHEDULE,
|
|
503
|
+
maxRuntime: DEFAULT_MERGER_MAX_RUNTIME,
|
|
504
|
+
mergeMethod: DEFAULT_MERGER_MERGE_METHOD,
|
|
505
|
+
minReviewScore: DEFAULT_MERGER_MIN_REVIEW_SCORE,
|
|
506
|
+
branchPatterns: [],
|
|
507
|
+
rebaseBeforeMerge: DEFAULT_MERGER_REBASE_BEFORE_MERGE,
|
|
508
|
+
maxPrsPerRun: DEFAULT_MERGER_MAX_PRS_PER_RUN
|
|
509
|
+
};
|
|
510
|
+
MERGER_LOG_NAME = "merger";
|
|
408
511
|
AUDIT_LOG_NAME = "audit";
|
|
409
512
|
PLANNER_LOG_NAME = "slicer";
|
|
410
513
|
ANALYTICS_LOG_NAME = "analytics";
|
|
514
|
+
PR_RESOLVER_LOG_NAME = "pr-resolver";
|
|
411
515
|
VALID_PROVIDERS = ["claude", "codex"];
|
|
412
516
|
VALID_JOB_TYPES = getValidJobTypes();
|
|
413
517
|
DEFAULT_JOB_PROVIDERS = {};
|
|
@@ -680,7 +784,7 @@ function normalizeConfig(rawConfig) {
|
|
|
680
784
|
if (mergeMethod && VALID_MERGE_METHODS.includes(mergeMethod)) {
|
|
681
785
|
normalized.autoMergeMethod = mergeMethod;
|
|
682
786
|
}
|
|
683
|
-
for (const jobId of ["qa", "audit", "analytics"]) {
|
|
787
|
+
for (const jobId of ["qa", "audit", "analytics", "merger"]) {
|
|
684
788
|
const jobDef = getJobDef(jobId);
|
|
685
789
|
if (!jobDef)
|
|
686
790
|
continue;
|
|
@@ -689,6 +793,13 @@ function normalizeConfig(rawConfig) {
|
|
|
689
793
|
normalized[jobId] = normalizeJobConfig(rawJob, jobDef);
|
|
690
794
|
}
|
|
691
795
|
}
|
|
796
|
+
const prResolverDef = getJobDef("pr-resolver");
|
|
797
|
+
if (prResolverDef) {
|
|
798
|
+
const rawJob = readObject(rawConfig.prResolver);
|
|
799
|
+
if (rawJob) {
|
|
800
|
+
normalized.prResolver = normalizeJobConfig(rawJob, prResolverDef);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
692
803
|
const rawJobProviders = readObject(rawConfig.jobProviders);
|
|
693
804
|
if (rawJobProviders) {
|
|
694
805
|
const jobProviders = {};
|
|
@@ -996,6 +1107,28 @@ function buildEnvOverrideConfig(fileConfig) {
|
|
|
996
1107
|
env[jobId] = overrides;
|
|
997
1108
|
}
|
|
998
1109
|
}
|
|
1110
|
+
const prResolverDef = getJobDef("pr-resolver");
|
|
1111
|
+
if (prResolverDef) {
|
|
1112
|
+
const currentBase = (
|
|
1113
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1114
|
+
env.prResolver ?? fileConfig?.prResolver ?? prResolverDef.defaultConfig
|
|
1115
|
+
);
|
|
1116
|
+
const overrides = buildJobEnvOverrides(prResolverDef.envPrefix, currentBase, prResolverDef.extraFields);
|
|
1117
|
+
if (overrides) {
|
|
1118
|
+
env.prResolver = overrides;
|
|
1119
|
+
}
|
|
1120
|
+
}
|
|
1121
|
+
const mergerDef = getJobDef("merger");
|
|
1122
|
+
if (mergerDef) {
|
|
1123
|
+
const currentBase = (
|
|
1124
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
1125
|
+
env.merger ?? fileConfig?.merger ?? mergerDef.defaultConfig
|
|
1126
|
+
);
|
|
1127
|
+
const overrides = buildJobEnvOverrides(mergerDef.envPrefix, currentBase, mergerDef.extraFields);
|
|
1128
|
+
if (overrides) {
|
|
1129
|
+
env.merger = overrides;
|
|
1130
|
+
}
|
|
1131
|
+
}
|
|
999
1132
|
const jobProvidersEnv = {};
|
|
1000
1133
|
for (const jobType of VALID_JOB_TYPES) {
|
|
1001
1134
|
const val = process.env[`NW_JOB_PROVIDER_${jobType.toUpperCase()}`];
|
|
@@ -1090,6 +1223,8 @@ function getDefaultConfig() {
|
|
|
1090
1223
|
qa: { ...DEFAULT_QA },
|
|
1091
1224
|
audit: { ...DEFAULT_AUDIT },
|
|
1092
1225
|
analytics: { ...DEFAULT_ANALYTICS },
|
|
1226
|
+
prResolver: { ...DEFAULT_PR_RESOLVER },
|
|
1227
|
+
merger: { ...DEFAULT_MERGER },
|
|
1093
1228
|
jobProviders: { ...DEFAULT_JOB_PROVIDERS },
|
|
1094
1229
|
providerScheduleOverrides: [...DEFAULT_PROVIDER_SCHEDULE_OVERRIDES],
|
|
1095
1230
|
queue: { ...DEFAULT_QUEUE }
|
|
@@ -1157,7 +1292,7 @@ function mergeConfigLayer(base, layer) {
|
|
|
1157
1292
|
...layerQueue,
|
|
1158
1293
|
providerBuckets: { ...baseQueue.providerBuckets, ...layerQueue.providerBuckets }
|
|
1159
1294
|
};
|
|
1160
|
-
} else if (_key === "providerEnv" || _key === "boardProvider" || _key === "qa" || _key === "audit" || _key === "analytics") {
|
|
1295
|
+
} else if (_key === "providerEnv" || _key === "boardProvider" || _key === "qa" || _key === "audit" || _key === "analytics" || _key === "prResolver" || _key === "merger") {
|
|
1161
1296
|
base[_key] = {
|
|
1162
1297
|
...base[_key],
|
|
1163
1298
|
...value
|
|
@@ -1180,6 +1315,13 @@ function mergeConfigs(base, fileConfig, envConfig) {
|
|
|
1180
1315
|
if (fileConfig)
|
|
1181
1316
|
mergeConfigLayer(merged, fileConfig);
|
|
1182
1317
|
mergeConfigLayer(merged, envConfig);
|
|
1318
|
+
if (merged.autoMerge === true && !fileConfig?.merger) {
|
|
1319
|
+
merged.merger = {
|
|
1320
|
+
...merged.merger,
|
|
1321
|
+
enabled: true,
|
|
1322
|
+
mergeMethod: merged.autoMergeMethod ?? "squash"
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1183
1325
|
merged.maxRetries = sanitizeMaxRetries(merged.maxRetries, DEFAULT_MAX_RETRIES);
|
|
1184
1326
|
merged.reviewerMaxRetries = sanitizeReviewerMaxRetries(merged.reviewerMaxRetries, DEFAULT_REVIEWER_MAX_RETRIES);
|
|
1185
1327
|
merged.reviewerRetryDelay = sanitizeReviewerRetryDelay(merged.reviewerRetryDelay, DEFAULT_REVIEWER_RETRY_DELAY);
|
|
@@ -2936,6 +3078,11 @@ var init_labels = __esm({
|
|
|
2936
3078
|
name: "analytics",
|
|
2937
3079
|
description: "Created by the analytics job for Amplitude findings",
|
|
2938
3080
|
color: "1d76db"
|
|
3081
|
+
},
|
|
3082
|
+
{
|
|
3083
|
+
name: "e2e-validated",
|
|
3084
|
+
description: "PR acceptance requirements validated by e2e/integration tests",
|
|
3085
|
+
color: "0e8a16"
|
|
2939
3086
|
}
|
|
2940
3087
|
];
|
|
2941
3088
|
}
|
|
@@ -3509,6 +3656,12 @@ function plannerLockPath(projectDir) {
|
|
|
3509
3656
|
function analyticsLockPath(projectDir) {
|
|
3510
3657
|
return `${LOCK_FILE_PREFIX}analytics-${projectRuntimeKey(projectDir)}.lock`;
|
|
3511
3658
|
}
|
|
3659
|
+
function prResolverLockPath(projectDir) {
|
|
3660
|
+
return `${LOCK_FILE_PREFIX}pr-resolver-${projectRuntimeKey(projectDir)}.lock`;
|
|
3661
|
+
}
|
|
3662
|
+
function mergerLockPath(projectDir) {
|
|
3663
|
+
return `${LOCK_FILE_PREFIX}merger-${projectRuntimeKey(projectDir)}.lock`;
|
|
3664
|
+
}
|
|
3512
3665
|
function isProcessRunning(pid) {
|
|
3513
3666
|
try {
|
|
3514
3667
|
process.kill(pid, 0);
|
|
@@ -3809,7 +3962,7 @@ async function collectPrInfo(projectDir, branchPatterns) {
|
|
|
3809
3962
|
} catch {
|
|
3810
3963
|
return [];
|
|
3811
3964
|
}
|
|
3812
|
-
const { stdout: output } = await execAsync("gh pr list --state open --json headRefName,number,title,url,statusCheckRollup,reviewDecision", {
|
|
3965
|
+
const { stdout: output } = await execAsync("gh pr list --state open --json headRefName,number,title,url,statusCheckRollup,reviewDecision,labels", {
|
|
3813
3966
|
cwd: projectDir,
|
|
3814
3967
|
encoding: "utf-8"
|
|
3815
3968
|
});
|
|
@@ -3833,7 +3986,8 @@ async function collectPrInfo(projectDir, branchPatterns) {
|
|
|
3833
3986
|
branch: pr.headRefName,
|
|
3834
3987
|
url: pr.url,
|
|
3835
3988
|
ciStatus: deriveCiStatus(pr.statusCheckRollup),
|
|
3836
|
-
reviewScore: deriveReviewScore(pr.reviewDecision)
|
|
3989
|
+
reviewScore: deriveReviewScore(pr.reviewDecision),
|
|
3990
|
+
labels: (pr.labels ?? []).map((l) => l.name)
|
|
3837
3991
|
};
|
|
3838
3992
|
});
|
|
3839
3993
|
} catch {
|
|
@@ -4812,6 +4966,16 @@ function getEventEmoji(event) {
|
|
|
4812
4966
|
return "\u{1F500}";
|
|
4813
4967
|
case "qa_completed":
|
|
4814
4968
|
return "\u{1F9EA}";
|
|
4969
|
+
case "pr_resolver_completed":
|
|
4970
|
+
return "\u{1F527}";
|
|
4971
|
+
case "pr_resolver_conflict_resolved":
|
|
4972
|
+
return "\u2705";
|
|
4973
|
+
case "pr_resolver_failed":
|
|
4974
|
+
return "\u274C";
|
|
4975
|
+
case "merge_completed":
|
|
4976
|
+
return "\u{1F500}";
|
|
4977
|
+
case "merge_failed":
|
|
4978
|
+
return "\u274C";
|
|
4815
4979
|
}
|
|
4816
4980
|
}
|
|
4817
4981
|
function getEventTitle(event) {
|
|
@@ -4836,6 +5000,16 @@ function getEventTitle(event) {
|
|
|
4836
5000
|
return "PR Auto-Merged";
|
|
4837
5001
|
case "qa_completed":
|
|
4838
5002
|
return "QA Completed";
|
|
5003
|
+
case "pr_resolver_completed":
|
|
5004
|
+
return "PR Resolver Completed";
|
|
5005
|
+
case "pr_resolver_conflict_resolved":
|
|
5006
|
+
return "PR Conflict Resolved";
|
|
5007
|
+
case "pr_resolver_failed":
|
|
5008
|
+
return "PR Resolver Failed";
|
|
5009
|
+
case "merge_completed":
|
|
5010
|
+
return "PR Merged";
|
|
5011
|
+
case "merge_failed":
|
|
5012
|
+
return "Merge Failed";
|
|
4839
5013
|
}
|
|
4840
5014
|
}
|
|
4841
5015
|
function getEventColor(event) {
|
|
@@ -4860,6 +5034,16 @@ function getEventColor(event) {
|
|
|
4860
5034
|
return 10181046;
|
|
4861
5035
|
case "qa_completed":
|
|
4862
5036
|
return 3066993;
|
|
5037
|
+
case "pr_resolver_completed":
|
|
5038
|
+
return 51283;
|
|
5039
|
+
case "pr_resolver_conflict_resolved":
|
|
5040
|
+
return 65280;
|
|
5041
|
+
case "pr_resolver_failed":
|
|
5042
|
+
return 16711680;
|
|
5043
|
+
case "merge_completed":
|
|
5044
|
+
return 10181046;
|
|
5045
|
+
case "merge_failed":
|
|
5046
|
+
return 16711680;
|
|
4863
5047
|
}
|
|
4864
5048
|
}
|
|
4865
5049
|
function buildDescription(ctx) {
|
|
@@ -5600,9 +5784,9 @@ var init_slicer_prompt = __esm({
|
|
|
5600
5784
|
"use strict";
|
|
5601
5785
|
__filename = fileURLToPath2(import.meta.url);
|
|
5602
5786
|
__dirname = path14.dirname(__filename);
|
|
5603
|
-
DEFAULT_SLICER_TEMPLATE = `You are a **
|
|
5787
|
+
DEFAULT_SLICER_TEMPLATE = `You are a **Principal Software Architect**. Your job: analyze the codebase and write a complete Product Requirements Document (PRD) for a feature. The PRD will be used directly as a GitHub issue body, so it must be self-contained and immediately actionable by an engineer.
|
|
5604
5788
|
|
|
5605
|
-
When this activates: \`
|
|
5789
|
+
When this activates: \`Planning Mode: Principal Architect\`
|
|
5606
5790
|
|
|
5607
5791
|
---
|
|
5608
5792
|
|
|
@@ -5623,22 +5807,16 @@ The PRD directory is: \`{{PRD_DIR}}\`
|
|
|
5623
5807
|
|
|
5624
5808
|
## Your Task
|
|
5625
5809
|
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
2. **Assess Complexity** - Score the complexity using the rubric and determine whether this is LOW, MEDIUM, or HIGH complexity.
|
|
5631
|
-
|
|
5632
|
-
3. **Write a Complete PRD** - Create a full PRD following the prd-creator template structure with Context, Solution, Phases, Tests, and Acceptance Criteria.
|
|
5633
|
-
|
|
5634
|
-
4. **Write the PRD File** - Use the Write tool to create the PRD file at the exact path specified in \`{{OUTPUT_FILE_PATH}}\`.
|
|
5810
|
+
1. **Explore the Codebase** \u2014 Read relevant existing files to understand structure, patterns, and conventions.
|
|
5811
|
+
2. **Assess Complexity** \u2014 Score using the rubric below and determine LOW / MEDIUM / HIGH.
|
|
5812
|
+
3. **Write a Complete PRD** \u2014 Follow the exact template structure below. Every section must be filled with concrete information.
|
|
5813
|
+
4. **Write the PRD File** \u2014 Use the Write tool to create the PRD file at \`{{OUTPUT_FILE_PATH}}\`.
|
|
5635
5814
|
|
|
5636
5815
|
---
|
|
5637
5816
|
|
|
5638
5817
|
## Complexity Scoring
|
|
5639
5818
|
|
|
5640
5819
|
\`\`\`
|
|
5641
|
-
COMPLEXITY SCORE (sum all that apply):
|
|
5642
5820
|
+1 Touches 1-5 files
|
|
5643
5821
|
+2 Touches 6-10 files
|
|
5644
5822
|
+3 Touches 10+ files
|
|
@@ -5650,7 +5828,7 @@ COMPLEXITY SCORE (sum all that apply):
|
|
|
5650
5828
|
|
|
5651
5829
|
| Score | Level | Template Mode |
|
|
5652
5830
|
| ----- | ------ | ----------------------------------------------- |
|
|
5653
|
-
| 1-3 | LOW | Minimal (skip sections marked
|
|
5831
|
+
| 1-3 | LOW | Minimal (skip sections marked MEDIUM/HIGH) |
|
|
5654
5832
|
| 4-6 | MEDIUM | Standard (all sections) |
|
|
5655
5833
|
| 7+ | HIGH | Full + mandatory checkpoints every phase |
|
|
5656
5834
|
\`\`\`
|
|
@@ -5659,25 +5837,77 @@ COMPLEXITY SCORE (sum all that apply):
|
|
|
5659
5837
|
|
|
5660
5838
|
## PRD Template Structure
|
|
5661
5839
|
|
|
5662
|
-
Your PRD MUST
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5840
|
+
Your PRD MUST use this structure. Replace every [bracketed placeholder] with real content.
|
|
5841
|
+
|
|
5842
|
+
# PRD: [Title]
|
|
5843
|
+
|
|
5844
|
+
**Complexity: [SCORE] \u2192 [LEVEL] mode**
|
|
5845
|
+
|
|
5846
|
+
## 1. Context
|
|
5847
|
+
|
|
5848
|
+
**Problem:** [1-2 sentences]
|
|
5849
|
+
|
|
5850
|
+
**Files Analyzed:**
|
|
5851
|
+
- \`path/to/file.ts\` \u2014 [what you found]
|
|
5852
|
+
|
|
5853
|
+
**Current Behavior:**
|
|
5854
|
+
- [3-5 bullets]
|
|
5855
|
+
|
|
5856
|
+
### Integration Points
|
|
5857
|
+
- Entry point: [cron / CLI / event / route]
|
|
5858
|
+
- Caller file: [file invoking new code]
|
|
5859
|
+
- User flow: User does X \u2192 triggers Y \u2192 result Z
|
|
5860
|
+
|
|
5861
|
+
## 2. Solution
|
|
5862
|
+
|
|
5863
|
+
**Approach:**
|
|
5864
|
+
- [3-5 bullets]
|
|
5865
|
+
|
|
5866
|
+
**Key Decisions:** [library choices, error handling, reused utilities]
|
|
5867
|
+
|
|
5868
|
+
**Data Changes:** [schema changes, or "None"]
|
|
5869
|
+
|
|
5870
|
+
## 3. Sequence Flow (MEDIUM/HIGH only)
|
|
5871
|
+
|
|
5872
|
+
[mermaid sequenceDiagram]
|
|
5873
|
+
|
|
5874
|
+
## 4. Execution Phases
|
|
5875
|
+
|
|
5876
|
+
### Phase N: [Name] \u2014 [User-visible outcome]
|
|
5877
|
+
|
|
5878
|
+
**Files (max 5):**
|
|
5879
|
+
- \`src/path/file.ts\` \u2014 [what changes]
|
|
5880
|
+
|
|
5881
|
+
**Implementation:**
|
|
5882
|
+
- [ ] Step 1
|
|
5883
|
+
|
|
5884
|
+
**Tests Required:**
|
|
5885
|
+
| Test File | Test Name | Assertion |
|
|
5886
|
+
|-----------|-----------|-----------|
|
|
5887
|
+
| \`src/__tests__/feature.test.ts\` | \`should X when Y\` | \`expect(r).toBe(Z)\` |
|
|
5888
|
+
|
|
5889
|
+
**Checkpoint:** Run \`yarn verify\` and related tests after this phase.
|
|
5890
|
+
|
|
5891
|
+
## 5. Acceptance Criteria
|
|
5892
|
+
|
|
5893
|
+
- [ ] All phases complete
|
|
5894
|
+
- [ ] All tests pass
|
|
5895
|
+
- [ ] \`yarn verify\` passes
|
|
5896
|
+
- [ ] Feature is reachable (not orphaned code)
|
|
5668
5897
|
|
|
5669
5898
|
---
|
|
5670
5899
|
|
|
5671
5900
|
## Critical Instructions
|
|
5672
5901
|
|
|
5673
|
-
1.
|
|
5674
|
-
2.
|
|
5675
|
-
3.
|
|
5676
|
-
4.
|
|
5677
|
-
5.
|
|
5678
|
-
6.
|
|
5902
|
+
1. Read all relevant files BEFORE writing the PRD
|
|
5903
|
+
2. Follow existing patterns \u2014 use \`@/*\` path aliases, match naming conventions
|
|
5904
|
+
3. Include concrete file paths and implementation steps
|
|
5905
|
+
4. Include specific test names and assertions
|
|
5906
|
+
5. Use the Write tool to create the file at \`{{OUTPUT_FILE_PATH}}\`
|
|
5907
|
+
6. No placeholder text in the final PRD
|
|
5908
|
+
7. The PRD is the GitHub issue body \u2014 make it self-contained
|
|
5679
5909
|
|
|
5680
|
-
DO NOT leave placeholder text
|
|
5910
|
+
DO NOT leave [bracketed placeholder] text in the output.
|
|
5681
5911
|
DO NOT skip any sections.
|
|
5682
5912
|
DO NOT forget to write the file.
|
|
5683
5913
|
`;
|
|
@@ -6178,6 +6408,15 @@ function isJobTypeEnabled(config, jobType) {
|
|
|
6178
6408
|
return true;
|
|
6179
6409
|
}
|
|
6180
6410
|
}
|
|
6411
|
+
function getJobSchedule(config, jobType) {
|
|
6412
|
+
switch (jobType) {
|
|
6413
|
+
case "reviewer":
|
|
6414
|
+
return config.reviewerSchedule ?? "";
|
|
6415
|
+
case "executor":
|
|
6416
|
+
default:
|
|
6417
|
+
return config.cronSchedule ?? "";
|
|
6418
|
+
}
|
|
6419
|
+
}
|
|
6181
6420
|
function loadPeerConfig(projectPath) {
|
|
6182
6421
|
if (!fs17.existsSync(projectPath) || !fs17.existsSync(path16.join(projectPath, CONFIG_FILE_NAME))) {
|
|
6183
6422
|
return null;
|
|
@@ -6191,11 +6430,15 @@ function loadPeerConfig(projectPath) {
|
|
|
6191
6430
|
function collectSchedulingPeers(currentProjectDir, currentConfig, jobType) {
|
|
6192
6431
|
const peers = /* @__PURE__ */ new Map();
|
|
6193
6432
|
const currentPath = path16.resolve(currentProjectDir);
|
|
6433
|
+
const currentSchedule = getJobSchedule(currentConfig, jobType);
|
|
6194
6434
|
const addPeer = (projectPath, config) => {
|
|
6195
6435
|
const resolvedPath = path16.resolve(projectPath);
|
|
6196
6436
|
if (!isJobTypeEnabled(config, jobType)) {
|
|
6197
6437
|
return;
|
|
6198
6438
|
}
|
|
6439
|
+
if (getJobSchedule(config, jobType) !== currentSchedule) {
|
|
6440
|
+
return;
|
|
6441
|
+
}
|
|
6199
6442
|
peers.set(resolvedPath, {
|
|
6200
6443
|
path: resolvedPath,
|
|
6201
6444
|
config,
|
|
@@ -6532,6 +6775,10 @@ function getLockPathForJob(projectPath, jobType) {
|
|
|
6532
6775
|
return plannerLockPath(projectPath);
|
|
6533
6776
|
case "analytics":
|
|
6534
6777
|
return analyticsLockPath(projectPath);
|
|
6778
|
+
case "pr-resolver":
|
|
6779
|
+
return prResolverLockPath(projectPath);
|
|
6780
|
+
case "merger":
|
|
6781
|
+
return mergerLockPath(projectPath);
|
|
6535
6782
|
}
|
|
6536
6783
|
}
|
|
6537
6784
|
function reconcileStaleRunningJobs(db) {
|
|
@@ -6621,6 +6868,15 @@ function getJobPriority(jobType, config) {
|
|
|
6621
6868
|
function enqueueJob(projectPath, projectName, jobType, envVars, config, providerKey) {
|
|
6622
6869
|
const db = openDb();
|
|
6623
6870
|
try {
|
|
6871
|
+
const existing = db.prepare(`SELECT id FROM job_queue WHERE project_path = ? AND job_type = ? AND status IN ('pending', 'dispatched', 'running')`).get(projectPath, jobType);
|
|
6872
|
+
if (existing) {
|
|
6873
|
+
logger2.info("Skipping duplicate enqueue \u2014 active entry already exists", {
|
|
6874
|
+
id: existing.id,
|
|
6875
|
+
jobType,
|
|
6876
|
+
project: projectName
|
|
6877
|
+
});
|
|
6878
|
+
return existing.id;
|
|
6879
|
+
}
|
|
6624
6880
|
const priority = getJobPriority(jobType, config);
|
|
6625
6881
|
const now = Math.floor(Date.now() / 1e3);
|
|
6626
6882
|
const envJson = JSON.stringify(envVars);
|
|
@@ -7086,6 +7342,88 @@ var init_job_queue = __esm({
|
|
|
7086
7342
|
}
|
|
7087
7343
|
});
|
|
7088
7344
|
|
|
7345
|
+
// ../core/dist/utils/summary.js
|
|
7346
|
+
function computeCounts(runs) {
|
|
7347
|
+
const counts = {
|
|
7348
|
+
total: runs.length,
|
|
7349
|
+
succeeded: 0,
|
|
7350
|
+
failed: 0,
|
|
7351
|
+
timedOut: 0,
|
|
7352
|
+
rateLimited: 0,
|
|
7353
|
+
skipped: 0
|
|
7354
|
+
};
|
|
7355
|
+
for (const run2 of runs) {
|
|
7356
|
+
switch (run2.status) {
|
|
7357
|
+
case "success":
|
|
7358
|
+
counts.succeeded++;
|
|
7359
|
+
break;
|
|
7360
|
+
case "failure":
|
|
7361
|
+
counts.failed++;
|
|
7362
|
+
break;
|
|
7363
|
+
case "timeout":
|
|
7364
|
+
counts.timedOut++;
|
|
7365
|
+
break;
|
|
7366
|
+
case "rate_limited":
|
|
7367
|
+
counts.rateLimited++;
|
|
7368
|
+
break;
|
|
7369
|
+
case "skipped":
|
|
7370
|
+
counts.skipped++;
|
|
7371
|
+
break;
|
|
7372
|
+
}
|
|
7373
|
+
}
|
|
7374
|
+
return counts;
|
|
7375
|
+
}
|
|
7376
|
+
function buildActionItems(counts, prs, pendingItems) {
|
|
7377
|
+
const items = [];
|
|
7378
|
+
if (counts.failed > 0) {
|
|
7379
|
+
items.push(`${counts.failed} failed job${counts.failed > 1 ? "s" : ""} \u2014 run \`night-watch logs\` to investigate`);
|
|
7380
|
+
}
|
|
7381
|
+
if (counts.timedOut > 0) {
|
|
7382
|
+
items.push(`${counts.timedOut} timed out job${counts.timedOut > 1 ? "s" : ""} \u2014 check logs for details`);
|
|
7383
|
+
}
|
|
7384
|
+
if (counts.rateLimited > 0) {
|
|
7385
|
+
items.push(`${counts.rateLimited} rate-limited job${counts.rateLimited > 1 ? "s" : ""} \u2014 consider adjusting schedule`);
|
|
7386
|
+
}
|
|
7387
|
+
const failingCiPrs = prs.filter((pr) => pr.ciStatus === "fail");
|
|
7388
|
+
for (const pr of failingCiPrs) {
|
|
7389
|
+
items.push(`PR #${pr.number} has failing CI \u2014 check ${pr.url}`);
|
|
7390
|
+
}
|
|
7391
|
+
const readyToMergePrs = prs.filter((pr) => pr.labels.includes("ready-to-merge"));
|
|
7392
|
+
if (readyToMergePrs.length > 0) {
|
|
7393
|
+
items.push(`${readyToMergePrs.length} PR${readyToMergePrs.length > 1 ? "s" : ""} marked ready-to-merge \u2014 review and merge`);
|
|
7394
|
+
}
|
|
7395
|
+
if (pendingItems.length > 0) {
|
|
7396
|
+
const jobTypes = [...new Set(pendingItems.map((item) => item.jobType))];
|
|
7397
|
+
items.push(`${pendingItems.length} job${pendingItems.length > 1 ? "s" : ""} pending in queue (${jobTypes.join(", ")})`);
|
|
7398
|
+
}
|
|
7399
|
+
return items;
|
|
7400
|
+
}
|
|
7401
|
+
async function getSummaryData(projectDir, windowHours = DEFAULT_SUMMARY_WINDOW_HOURS, branchPatterns = []) {
|
|
7402
|
+
const analytics = getJobRunsAnalytics(windowHours);
|
|
7403
|
+
const jobRuns = analytics.recentRuns;
|
|
7404
|
+
const counts = computeCounts(jobRuns);
|
|
7405
|
+
const openPrs = await collectPrInfo(projectDir, branchPatterns);
|
|
7406
|
+
const queueStatus = getQueueStatus();
|
|
7407
|
+
const pendingQueueItems = queueStatus.items.filter((item) => item.status === "pending");
|
|
7408
|
+
const actionItems = buildActionItems(counts, openPrs, pendingQueueItems);
|
|
7409
|
+
return {
|
|
7410
|
+
windowHours,
|
|
7411
|
+
jobRuns,
|
|
7412
|
+
counts,
|
|
7413
|
+
openPrs,
|
|
7414
|
+
pendingQueueItems,
|
|
7415
|
+
actionItems
|
|
7416
|
+
};
|
|
7417
|
+
}
|
|
7418
|
+
var init_summary = __esm({
|
|
7419
|
+
"../core/dist/utils/summary.js"() {
|
|
7420
|
+
"use strict";
|
|
7421
|
+
init_constants();
|
|
7422
|
+
init_status_data();
|
|
7423
|
+
init_job_queue();
|
|
7424
|
+
}
|
|
7425
|
+
});
|
|
7426
|
+
|
|
7089
7427
|
// ../core/dist/analytics/amplitude-client.js
|
|
7090
7428
|
function buildAuthHeader(apiKey, secretKey) {
|
|
7091
7429
|
return `Basic ${Buffer.from(`${apiKey}:${secretKey}`).toString("base64")}`;
|
|
@@ -7508,6 +7846,14 @@ __export(dist_exports, {
|
|
|
7508
7846
|
DEFAULT_MAX_LOG_SIZE: () => DEFAULT_MAX_LOG_SIZE,
|
|
7509
7847
|
DEFAULT_MAX_RETRIES: () => DEFAULT_MAX_RETRIES,
|
|
7510
7848
|
DEFAULT_MAX_RUNTIME: () => DEFAULT_MAX_RUNTIME,
|
|
7849
|
+
DEFAULT_MERGER: () => DEFAULT_MERGER,
|
|
7850
|
+
DEFAULT_MERGER_ENABLED: () => DEFAULT_MERGER_ENABLED,
|
|
7851
|
+
DEFAULT_MERGER_MAX_PRS_PER_RUN: () => DEFAULT_MERGER_MAX_PRS_PER_RUN,
|
|
7852
|
+
DEFAULT_MERGER_MAX_RUNTIME: () => DEFAULT_MERGER_MAX_RUNTIME,
|
|
7853
|
+
DEFAULT_MERGER_MERGE_METHOD: () => DEFAULT_MERGER_MERGE_METHOD,
|
|
7854
|
+
DEFAULT_MERGER_MIN_REVIEW_SCORE: () => DEFAULT_MERGER_MIN_REVIEW_SCORE,
|
|
7855
|
+
DEFAULT_MERGER_REBASE_BEFORE_MERGE: () => DEFAULT_MERGER_REBASE_BEFORE_MERGE,
|
|
7856
|
+
DEFAULT_MERGER_SCHEDULE: () => DEFAULT_MERGER_SCHEDULE,
|
|
7511
7857
|
DEFAULT_MIN_REVIEW_SCORE: () => DEFAULT_MIN_REVIEW_SCORE,
|
|
7512
7858
|
DEFAULT_NOTIFICATIONS: () => DEFAULT_NOTIFICATIONS,
|
|
7513
7859
|
DEFAULT_PRD_DIR: () => DEFAULT_PRD_DIR,
|
|
@@ -7516,6 +7862,15 @@ __export(dist_exports, {
|
|
|
7516
7862
|
DEFAULT_PROVIDER: () => DEFAULT_PROVIDER,
|
|
7517
7863
|
DEFAULT_PROVIDER_ENV: () => DEFAULT_PROVIDER_ENV,
|
|
7518
7864
|
DEFAULT_PROVIDER_SCHEDULE_OVERRIDES: () => DEFAULT_PROVIDER_SCHEDULE_OVERRIDES,
|
|
7865
|
+
DEFAULT_PR_RESOLVER: () => DEFAULT_PR_RESOLVER,
|
|
7866
|
+
DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION: () => DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION,
|
|
7867
|
+
DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION: () => DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION,
|
|
7868
|
+
DEFAULT_PR_RESOLVER_ENABLED: () => DEFAULT_PR_RESOLVER_ENABLED,
|
|
7869
|
+
DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN: () => DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN,
|
|
7870
|
+
DEFAULT_PR_RESOLVER_MAX_RUNTIME: () => DEFAULT_PR_RESOLVER_MAX_RUNTIME,
|
|
7871
|
+
DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT: () => DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT,
|
|
7872
|
+
DEFAULT_PR_RESOLVER_READY_LABEL: () => DEFAULT_PR_RESOLVER_READY_LABEL,
|
|
7873
|
+
DEFAULT_PR_RESOLVER_SCHEDULE: () => DEFAULT_PR_RESOLVER_SCHEDULE,
|
|
7519
7874
|
DEFAULT_QA: () => DEFAULT_QA,
|
|
7520
7875
|
DEFAULT_QA_ARTIFACTS: () => DEFAULT_QA_ARTIFACTS,
|
|
7521
7876
|
DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT: () => DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT,
|
|
@@ -7523,6 +7878,7 @@ __export(dist_exports, {
|
|
|
7523
7878
|
DEFAULT_QA_MAX_RUNTIME: () => DEFAULT_QA_MAX_RUNTIME,
|
|
7524
7879
|
DEFAULT_QA_SCHEDULE: () => DEFAULT_QA_SCHEDULE,
|
|
7525
7880
|
DEFAULT_QA_SKIP_LABEL: () => DEFAULT_QA_SKIP_LABEL,
|
|
7881
|
+
DEFAULT_QA_VALIDATED_LABEL: () => DEFAULT_QA_VALIDATED_LABEL,
|
|
7526
7882
|
DEFAULT_QUEUE: () => DEFAULT_QUEUE,
|
|
7527
7883
|
DEFAULT_QUEUE_ENABLED: () => DEFAULT_QUEUE_ENABLED,
|
|
7528
7884
|
DEFAULT_QUEUE_MAX_CONCURRENCY: () => DEFAULT_QUEUE_MAX_CONCURRENCY,
|
|
@@ -7540,6 +7896,7 @@ __export(dist_exports, {
|
|
|
7540
7896
|
DEFAULT_SECONDARY_FALLBACK_MODEL: () => DEFAULT_SECONDARY_FALLBACK_MODEL,
|
|
7541
7897
|
DEFAULT_SLICER_MAX_RUNTIME: () => DEFAULT_SLICER_MAX_RUNTIME,
|
|
7542
7898
|
DEFAULT_SLICER_SCHEDULE: () => DEFAULT_SLICER_SCHEDULE,
|
|
7899
|
+
DEFAULT_SUMMARY_WINDOW_HOURS: () => DEFAULT_SUMMARY_WINDOW_HOURS,
|
|
7543
7900
|
DEFAULT_TEMPLATES_DIR: () => DEFAULT_TEMPLATES_DIR,
|
|
7544
7901
|
EXECUTOR_LOG_FILE: () => EXECUTOR_LOG_FILE,
|
|
7545
7902
|
EXECUTOR_LOG_NAME: () => EXECUTOR_LOG_NAME,
|
|
@@ -7555,6 +7912,7 @@ __export(dist_exports, {
|
|
|
7555
7912
|
LocalKanbanProvider: () => LocalKanbanProvider,
|
|
7556
7913
|
Logger: () => Logger,
|
|
7557
7914
|
MAX_HISTORY_RECORDS_PER_PRD: () => MAX_HISTORY_RECORDS_PER_PRD,
|
|
7915
|
+
MERGER_LOG_NAME: () => MERGER_LOG_NAME,
|
|
7558
7916
|
NIGHT_WATCH_LABELS: () => NIGHT_WATCH_LABELS,
|
|
7559
7917
|
PLANNER_LOG_NAME: () => PLANNER_LOG_NAME,
|
|
7560
7918
|
PRD_STATES_FILE_NAME: () => PRD_STATES_FILE_NAME,
|
|
@@ -7562,6 +7920,7 @@ __export(dist_exports, {
|
|
|
7562
7920
|
PRIORITY_LABELS: () => PRIORITY_LABELS,
|
|
7563
7921
|
PRIORITY_LABEL_INFO: () => PRIORITY_LABEL_INFO,
|
|
7564
7922
|
PROVIDER_COMMANDS: () => PROVIDER_COMMANDS,
|
|
7923
|
+
PR_RESOLVER_LOG_NAME: () => PR_RESOLVER_LOG_NAME,
|
|
7565
7924
|
QA_LOG_NAME: () => QA_LOG_NAME,
|
|
7566
7925
|
REGISTRY_FILE_NAME: () => REGISTRY_FILE_NAME,
|
|
7567
7926
|
REVIEWER_LOG_FILE: () => REVIEWER_LOG_FILE,
|
|
@@ -7690,6 +8049,7 @@ __export(dist_exports, {
|
|
|
7690
8049
|
getScriptPath: () => getScriptPath,
|
|
7691
8050
|
getStateFilePath: () => getStateFilePath,
|
|
7692
8051
|
getStateItem: () => getStateItem,
|
|
8052
|
+
getSummaryData: () => getSummaryData,
|
|
7693
8053
|
getUncheckedItems: () => getUncheckedItems,
|
|
7694
8054
|
getValidJobTypes: () => getValidJobTypes,
|
|
7695
8055
|
groupBySection: () => groupBySection2,
|
|
@@ -7717,6 +8077,7 @@ __export(dist_exports, {
|
|
|
7717
8077
|
markItemProcessed: () => markItemProcessed,
|
|
7718
8078
|
markJobRunning: () => markJobRunning,
|
|
7719
8079
|
markPrdDone: () => markPrdDone,
|
|
8080
|
+
mergerLockPath: () => mergerLockPath,
|
|
7720
8081
|
migrateJsonToSqlite: () => migrateJsonToSqlite,
|
|
7721
8082
|
normalizeJobConfig: () => normalizeJobConfig,
|
|
7722
8083
|
normalizeSchedulingPriority: () => normalizeSchedulingPriority,
|
|
@@ -7726,6 +8087,7 @@ __export(dist_exports, {
|
|
|
7726
8087
|
parseTimeToMinutes: () => parseTimeToMinutes,
|
|
7727
8088
|
performCancel: () => performCancel,
|
|
7728
8089
|
plannerLockPath: () => plannerLockPath,
|
|
8090
|
+
prResolverLockPath: () => prResolverLockPath,
|
|
7729
8091
|
prepareBranchWorktree: () => prepareBranchWorktree,
|
|
7730
8092
|
prepareDetachedWorktree: () => prepareDetachedWorktree,
|
|
7731
8093
|
projectRuntimeKey: () => projectRuntimeKey,
|
|
@@ -7822,6 +8184,7 @@ var init_dist = __esm({
|
|
|
7822
8184
|
init_webhook_validator();
|
|
7823
8185
|
init_worktree_manager();
|
|
7824
8186
|
init_job_queue();
|
|
8187
|
+
init_summary();
|
|
7825
8188
|
init_analytics();
|
|
7826
8189
|
init_prd_template();
|
|
7827
8190
|
init_slicer_prompt();
|
|
@@ -8098,6 +8461,8 @@ function buildInitConfig(params) {
|
|
|
8098
8461
|
},
|
|
8099
8462
|
audit: { ...defaults.audit },
|
|
8100
8463
|
analytics: { ...defaults.analytics },
|
|
8464
|
+
merger: { ...defaults.merger },
|
|
8465
|
+
prResolver: { ...defaults.prResolver },
|
|
8101
8466
|
jobProviders: { ...defaults.jobProviders },
|
|
8102
8467
|
queue: {
|
|
8103
8468
|
...defaults.queue,
|
|
@@ -8221,7 +8586,7 @@ function initCommand(program2) {
|
|
|
8221
8586
|
const cwd = process.cwd();
|
|
8222
8587
|
const force = options.force || false;
|
|
8223
8588
|
const prdDir = options.prdDir || DEFAULT_PRD_DIR;
|
|
8224
|
-
const totalSteps =
|
|
8589
|
+
const totalSteps = 14;
|
|
8225
8590
|
const interactive = isInteractiveInitSession();
|
|
8226
8591
|
console.log();
|
|
8227
8592
|
header("Night Watch CLI - Initializing");
|
|
@@ -8476,7 +8841,35 @@ function initCommand(program2) {
|
|
|
8476
8841
|
}
|
|
8477
8842
|
}
|
|
8478
8843
|
}
|
|
8479
|
-
step(11, totalSteps, "
|
|
8844
|
+
step(11, totalSteps, "Syncing Night Watch labels to GitHub...");
|
|
8845
|
+
let labelSyncStatus = "Skipped";
|
|
8846
|
+
if (!remoteStatus.hasGitHubRemote || !ghAuthenticated) {
|
|
8847
|
+
labelSyncStatus = !remoteStatus.hasGitHubRemote ? "Skipped (no GitHub remote)" : "Skipped (gh auth required)";
|
|
8848
|
+
info("Skipping label sync (no GitHub remote or gh not authenticated).");
|
|
8849
|
+
} else {
|
|
8850
|
+
try {
|
|
8851
|
+
const { NIGHT_WATCH_LABELS: NIGHT_WATCH_LABELS2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
8852
|
+
let created = 0;
|
|
8853
|
+
for (const label2 of NIGHT_WATCH_LABELS2) {
|
|
8854
|
+
try {
|
|
8855
|
+
execSync3(
|
|
8856
|
+
`gh label create "${label2.name}" --description "${label2.description}" --color "${label2.color}" --force`,
|
|
8857
|
+
{ cwd, encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] }
|
|
8858
|
+
);
|
|
8859
|
+
created++;
|
|
8860
|
+
} catch {
|
|
8861
|
+
}
|
|
8862
|
+
}
|
|
8863
|
+
labelSyncStatus = `Synced ${created}/${NIGHT_WATCH_LABELS2.length} labels`;
|
|
8864
|
+
success(`Synced ${created}/${NIGHT_WATCH_LABELS2.length} labels to GitHub`);
|
|
8865
|
+
} catch (labelErr) {
|
|
8866
|
+
labelSyncStatus = "Failed";
|
|
8867
|
+
warn(
|
|
8868
|
+
`Could not sync labels: ${labelErr instanceof Error ? labelErr.message : String(labelErr)}`
|
|
8869
|
+
);
|
|
8870
|
+
}
|
|
8871
|
+
}
|
|
8872
|
+
step(12, totalSteps, "Registering project in global registry...");
|
|
8480
8873
|
try {
|
|
8481
8874
|
const { registerProject: registerProject2 } = await Promise.resolve().then(() => (init_dist(), dist_exports));
|
|
8482
8875
|
const entry = registerProject2(cwd);
|
|
@@ -8486,7 +8879,7 @@ function initCommand(program2) {
|
|
|
8486
8879
|
` Warning: Could not register in global registry: ${regErr instanceof Error ? regErr.message : String(regErr)}`
|
|
8487
8880
|
);
|
|
8488
8881
|
}
|
|
8489
|
-
step(
|
|
8882
|
+
step(13, totalSteps, "Installing Night Watch skills...");
|
|
8490
8883
|
const skillsResult = installSkills(cwd, selectedProvider, force, TEMPLATES_DIR);
|
|
8491
8884
|
if (skillsResult.installed > 0) {
|
|
8492
8885
|
success(`Installed ${skillsResult.installed} skills to ${skillsResult.location}`);
|
|
@@ -8498,7 +8891,7 @@ function initCommand(program2) {
|
|
|
8498
8891
|
} else if (skillsResult.type === "none") {
|
|
8499
8892
|
info("No compatible AI skills directory detected \u2014 skipping.");
|
|
8500
8893
|
}
|
|
8501
|
-
step(
|
|
8894
|
+
step(14, totalSteps, "Initialization complete!");
|
|
8502
8895
|
header("Initialization Complete");
|
|
8503
8896
|
const filesTable = createTable({ head: ["Created Files", ""] });
|
|
8504
8897
|
filesTable.push(["PRD Directory", `${prdDir}/done/`]);
|
|
@@ -8511,6 +8904,7 @@ function initCommand(program2) {
|
|
|
8511
8904
|
filesTable.push(["", `instructions/prd-creator.md (${templateSources[5].source})`]);
|
|
8512
8905
|
filesTable.push(["Config File", CONFIG_FILE_NAME]);
|
|
8513
8906
|
filesTable.push(["Board Setup", boardSetupStatus]);
|
|
8907
|
+
filesTable.push(["Label Sync", labelSyncStatus]);
|
|
8514
8908
|
filesTable.push(["Global Registry", "~/.night-watch/projects.json"]);
|
|
8515
8909
|
let skillsSummary;
|
|
8516
8910
|
if (skillsResult.installed > 0) {
|
|
@@ -9058,6 +9452,15 @@ function shouldSendReviewNotification(scriptStatus) {
|
|
|
9058
9452
|
}
|
|
9059
9453
|
return !scriptStatus.startsWith("skip_");
|
|
9060
9454
|
}
|
|
9455
|
+
function shouldSendReviewCompletionNotification(exitCode, scriptStatus) {
|
|
9456
|
+
if (exitCode !== 0) {
|
|
9457
|
+
return false;
|
|
9458
|
+
}
|
|
9459
|
+
if (scriptStatus === "failure" || scriptStatus === "timeout") {
|
|
9460
|
+
return false;
|
|
9461
|
+
}
|
|
9462
|
+
return shouldSendReviewNotification(scriptStatus);
|
|
9463
|
+
}
|
|
9061
9464
|
function parseAutoMergedPrNumbers(raw) {
|
|
9062
9465
|
if (!raw || raw.trim().length === 0) {
|
|
9063
9466
|
return [];
|
|
@@ -9139,10 +9542,6 @@ function buildEnvVars2(config, options) {
|
|
|
9139
9542
|
env.NW_BRANCH_PATTERNS = config.branchPatterns.join(",");
|
|
9140
9543
|
env.NW_PRD_DIR = config.prdDir;
|
|
9141
9544
|
env.NW_CLAUDE_MODEL_ID = CLAUDE_MODEL_IDS[config.primaryFallbackModel ?? config.claudeModel ?? "sonnet"];
|
|
9142
|
-
if (config.autoMerge) {
|
|
9143
|
-
env.NW_AUTO_MERGE = "1";
|
|
9144
|
-
}
|
|
9145
|
-
env.NW_AUTO_MERGE_METHOD = config.autoMergeMethod;
|
|
9146
9545
|
return env;
|
|
9147
9546
|
}
|
|
9148
9547
|
function applyCliOverrides2(config, options) {
|
|
@@ -9156,9 +9555,6 @@ function applyCliOverrides2(config, options) {
|
|
|
9156
9555
|
if (options.provider) {
|
|
9157
9556
|
overridden._cliProviderOverride = options.provider;
|
|
9158
9557
|
}
|
|
9159
|
-
if (options.autoMerge !== void 0) {
|
|
9160
|
-
overridden.autoMerge = options.autoMerge;
|
|
9161
|
-
}
|
|
9162
9558
|
return overridden;
|
|
9163
9559
|
}
|
|
9164
9560
|
function isFailingCheck(check) {
|
|
@@ -9219,7 +9615,7 @@ function getOpenPrsNeedingWork(branchPatterns) {
|
|
|
9219
9615
|
}
|
|
9220
9616
|
}
|
|
9221
9617
|
function reviewCommand(program2) {
|
|
9222
|
-
program2.command("review").description("Run PR reviewer now").option("--dry-run", "Show what would be executed without running").option("--timeout <seconds>", "Override max runtime in seconds for reviewer").option("--provider <string>", "AI provider to use (claude or codex)").
|
|
9618
|
+
program2.command("review").description("Run PR reviewer now").option("--dry-run", "Show what would be executed without running").option("--timeout <seconds>", "Override max runtime in seconds for reviewer").option("--provider <string>", "AI provider to use (claude or codex)").action(async (options) => {
|
|
9223
9619
|
const projectDir = process.cwd();
|
|
9224
9620
|
let config = loadConfig(projectDir);
|
|
9225
9621
|
config = applyCliOverrides2(config, options);
|
|
@@ -9242,10 +9638,6 @@ function reviewCommand(program2) {
|
|
|
9242
9638
|
]);
|
|
9243
9639
|
configTable.push(["Min Review Score", `${config.minReviewScore}/100`]);
|
|
9244
9640
|
configTable.push(["Branch Patterns", config.branchPatterns.join(", ")]);
|
|
9245
|
-
configTable.push([
|
|
9246
|
-
"Auto-merge",
|
|
9247
|
-
config.autoMerge ? `Enabled (${config.autoMergeMethod})` : "Disabled"
|
|
9248
|
-
]);
|
|
9249
9641
|
configTable.push(["Max Retry Attempts", String(config.reviewerMaxRetries)]);
|
|
9250
9642
|
configTable.push(["Retry Delay", `${config.reviewerRetryDelay}s`]);
|
|
9251
9643
|
configTable.push([
|
|
@@ -9316,12 +9708,15 @@ ${stderr}`);
|
|
|
9316
9708
|
spinner.fail(`PR reviewer exited with code ${exitCode}`);
|
|
9317
9709
|
}
|
|
9318
9710
|
if (!options.dryRun) {
|
|
9319
|
-
const
|
|
9320
|
-
|
|
9321
|
-
|
|
9711
|
+
const shouldNotifyCompletion = shouldSendReviewCompletionNotification(
|
|
9712
|
+
exitCode,
|
|
9713
|
+
scriptResult?.status
|
|
9714
|
+
);
|
|
9715
|
+
if (!shouldNotifyCompletion) {
|
|
9716
|
+
info("Skipping review completion notification (review did not complete successfully)");
|
|
9322
9717
|
}
|
|
9323
9718
|
let fallbackPrDetails = null;
|
|
9324
|
-
if (
|
|
9719
|
+
if (shouldNotifyCompletion) {
|
|
9325
9720
|
const reviewedPrNumbers = parseReviewedPrNumbers(scriptResult?.data.prs);
|
|
9326
9721
|
const firstReviewedPrNumber = reviewedPrNumbers[0];
|
|
9327
9722
|
if (firstReviewedPrNumber !== void 0) {
|
|
@@ -9331,7 +9726,7 @@ ${stderr}`);
|
|
|
9331
9726
|
fallbackPrDetails = fetchReviewedPrDetails(config.branchPatterns, projectDir);
|
|
9332
9727
|
}
|
|
9333
9728
|
}
|
|
9334
|
-
if (
|
|
9729
|
+
if (shouldNotifyCompletion) {
|
|
9335
9730
|
const attempts = parseRetryAttempts(scriptResult?.data.attempts);
|
|
9336
9731
|
const finalScore = parseFinalReviewScore(scriptResult?.data.final_score);
|
|
9337
9732
|
const legacyNoChangesNeeded = scriptResult?.data.no_changes_needed === "1";
|
|
@@ -9453,6 +9848,7 @@ function buildEnvVars3(config, options) {
|
|
|
9453
9848
|
const branchPatterns = config.qa.branchPatterns.length > 0 ? config.qa.branchPatterns : config.branchPatterns;
|
|
9454
9849
|
env.NW_BRANCH_PATTERNS = branchPatterns.join(",");
|
|
9455
9850
|
env.NW_QA_SKIP_LABEL = config.qa.skipLabel;
|
|
9851
|
+
env.NW_QA_VALIDATED_LABEL = config.qa.validatedLabel;
|
|
9456
9852
|
env.NW_QA_ARTIFACTS = config.qa.artifacts;
|
|
9457
9853
|
env.NW_QA_AUTO_INSTALL_PLAYWRIGHT = config.qa.autoInstallPlaywright ? "1" : "0";
|
|
9458
9854
|
env.NW_CLAUDE_MODEL_ID = CLAUDE_MODEL_IDS[config.primaryFallbackModel ?? config.claudeModel ?? "sonnet"];
|
|
@@ -9502,6 +9898,7 @@ function qaCommand(program2) {
|
|
|
9502
9898
|
const branchPatterns = config.qa.branchPatterns.length > 0 ? config.qa.branchPatterns : config.branchPatterns;
|
|
9503
9899
|
configTable.push(["Branch Patterns", branchPatterns.join(", ")]);
|
|
9504
9900
|
configTable.push(["Skip Label", config.qa.skipLabel]);
|
|
9901
|
+
configTable.push(["Validated Label", config.qa.validatedLabel]);
|
|
9505
9902
|
configTable.push(["Artifacts", config.qa.artifacts]);
|
|
9506
9903
|
configTable.push([
|
|
9507
9904
|
"Auto-install Playwright",
|
|
@@ -9866,6 +10263,22 @@ function performInstall(projectDir, config, options) {
|
|
|
9866
10263
|
const analyticsEntry = `${analyticsSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} analytics >> ${shellQuote(analyticsLog)} 2>&1 ${marker}`;
|
|
9867
10264
|
entries.push(analyticsEntry);
|
|
9868
10265
|
}
|
|
10266
|
+
const disablePrResolver = options?.noPrResolver === true || options?.prResolver === false;
|
|
10267
|
+
const installPrResolver = disablePrResolver ? false : config.prResolver.enabled;
|
|
10268
|
+
if (installPrResolver) {
|
|
10269
|
+
const prResolverSchedule = config.prResolver.schedule;
|
|
10270
|
+
const prResolverLog = path25.join(logDir, "pr-resolver.log");
|
|
10271
|
+
const prResolverEntry = `${prResolverSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} resolve >> ${shellQuote(prResolverLog)} 2>&1 ${marker}`;
|
|
10272
|
+
entries.push(prResolverEntry);
|
|
10273
|
+
}
|
|
10274
|
+
const disableMerger = options?.noMerger === true || options?.merger === false;
|
|
10275
|
+
const installMerger = disableMerger ? false : config.merger?.enabled ?? false;
|
|
10276
|
+
if (installMerger) {
|
|
10277
|
+
const mergerSchedule = config.merger.schedule;
|
|
10278
|
+
const mergerLog = path25.join(logDir, "merger.log");
|
|
10279
|
+
const mergerEntry = `${mergerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} merge >> ${shellQuote(mergerLog)} 2>&1 ${marker}`;
|
|
10280
|
+
entries.push(mergerEntry);
|
|
10281
|
+
}
|
|
9869
10282
|
const existingEntries = new Set(
|
|
9870
10283
|
Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]))
|
|
9871
10284
|
);
|
|
@@ -9884,7 +10297,7 @@ function performInstall(projectDir, config, options) {
|
|
|
9884
10297
|
}
|
|
9885
10298
|
}
|
|
9886
10299
|
function installCommand(program2) {
|
|
9887
|
-
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("-f, --force", "Replace existing cron entries for this project").action(async (options) => {
|
|
10300
|
+
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) => {
|
|
9888
10301
|
try {
|
|
9889
10302
|
const projectDir = process.cwd();
|
|
9890
10303
|
const config = loadConfig(projectDir);
|
|
@@ -9966,6 +10379,24 @@ function installCommand(program2) {
|
|
|
9966
10379
|
const analyticsEntry = `${analyticsSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} analytics >> ${shellQuote(analyticsLog)} 2>&1 ${marker}`;
|
|
9967
10380
|
entries.push(analyticsEntry);
|
|
9968
10381
|
}
|
|
10382
|
+
const disablePrResolver = options.noPrResolver === true || options.prResolver === false;
|
|
10383
|
+
const installPrResolver = disablePrResolver ? false : config.prResolver.enabled;
|
|
10384
|
+
let prResolverLog;
|
|
10385
|
+
if (installPrResolver) {
|
|
10386
|
+
prResolverLog = path25.join(logDir, "pr-resolver.log");
|
|
10387
|
+
const prResolverSchedule = config.prResolver.schedule;
|
|
10388
|
+
const prResolverEntry = `${prResolverSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} resolve >> ${shellQuote(prResolverLog)} 2>&1 ${marker}`;
|
|
10389
|
+
entries.push(prResolverEntry);
|
|
10390
|
+
}
|
|
10391
|
+
const disableMerger = options.noMerger === true || options.merger === false;
|
|
10392
|
+
const installMerger = disableMerger ? false : config.merger?.enabled ?? false;
|
|
10393
|
+
let mergerLog;
|
|
10394
|
+
if (installMerger) {
|
|
10395
|
+
mergerLog = path25.join(logDir, "merger.log");
|
|
10396
|
+
const mergerSchedule = config.merger.schedule;
|
|
10397
|
+
const mergerEntry = `${mergerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} merge >> ${shellQuote(mergerLog)} 2>&1 ${marker}`;
|
|
10398
|
+
entries.push(mergerEntry);
|
|
10399
|
+
}
|
|
9969
10400
|
const existingEntrySet = new Set(existingEntries);
|
|
9970
10401
|
const currentCrontab = readCrontab();
|
|
9971
10402
|
const baseCrontab = options.force ? currentCrontab.filter((line) => !existingEntrySet.has(line) && !line.includes(marker)) : currentCrontab;
|
|
@@ -9995,6 +10426,12 @@ function installCommand(program2) {
|
|
|
9995
10426
|
if (installAnalytics && analyticsLog) {
|
|
9996
10427
|
dim(` Analytics: ${analyticsLog}`);
|
|
9997
10428
|
}
|
|
10429
|
+
if (installPrResolver && prResolverLog) {
|
|
10430
|
+
dim(` PR Resolver: ${prResolverLog}`);
|
|
10431
|
+
}
|
|
10432
|
+
if (installMerger && mergerLog) {
|
|
10433
|
+
dim(` Merger: ${mergerLog}`);
|
|
10434
|
+
}
|
|
9998
10435
|
console.log();
|
|
9999
10436
|
dim("To uninstall, run: night-watch uninstall");
|
|
10000
10437
|
dim("To check status, run: night-watch status");
|
|
@@ -10027,7 +10464,13 @@ function performUninstall(projectDir, options) {
|
|
|
10027
10464
|
if (!options?.keepLogs) {
|
|
10028
10465
|
const logDir = path26.join(projectDir, "logs");
|
|
10029
10466
|
if (fs24.existsSync(logDir)) {
|
|
10030
|
-
const logFiles = [
|
|
10467
|
+
const logFiles = [
|
|
10468
|
+
"executor.log",
|
|
10469
|
+
"reviewer.log",
|
|
10470
|
+
"slicer.log",
|
|
10471
|
+
"audit.log",
|
|
10472
|
+
"pr-resolver.log"
|
|
10473
|
+
];
|
|
10031
10474
|
logFiles.forEach((logFile) => {
|
|
10032
10475
|
const logPath = path26.join(logDir, logFile);
|
|
10033
10476
|
if (fs24.existsSync(logPath)) {
|
|
@@ -10072,7 +10515,13 @@ function uninstallCommand(program2) {
|
|
|
10072
10515
|
if (!options.keepLogs) {
|
|
10073
10516
|
const logDir = path26.join(projectDir, "logs");
|
|
10074
10517
|
if (fs24.existsSync(logDir)) {
|
|
10075
|
-
const logFiles = [
|
|
10518
|
+
const logFiles = [
|
|
10519
|
+
"executor.log",
|
|
10520
|
+
"reviewer.log",
|
|
10521
|
+
"slicer.log",
|
|
10522
|
+
"audit.log",
|
|
10523
|
+
"pr-resolver.log"
|
|
10524
|
+
];
|
|
10076
10525
|
let logsRemoved = 0;
|
|
10077
10526
|
logFiles.forEach((logFile) => {
|
|
10078
10527
|
const logPath = path26.join(logDir, logFile);
|
|
@@ -14546,12 +14995,14 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
14546
14995
|
const auditPlan = getSchedulingPlan(projectDir, config, "audit");
|
|
14547
14996
|
const plannerPlan = getSchedulingPlan(projectDir, config, "slicer");
|
|
14548
14997
|
const analyticsPlan = getSchedulingPlan(projectDir, config, "analytics");
|
|
14998
|
+
const mergerPlan = getSchedulingPlan(projectDir, config, "merger");
|
|
14549
14999
|
const executorInstalled = installed && config.executorEnabled !== false && hasScheduledCommand(entries, "run");
|
|
14550
15000
|
const reviewerInstalled = installed && config.reviewerEnabled && hasScheduledCommand(entries, "review");
|
|
14551
15001
|
const qaInstalled = installed && config.qa.enabled && hasScheduledCommand(entries, "qa");
|
|
14552
15002
|
const auditInstalled = installed && config.audit.enabled && hasScheduledCommand(entries, "audit");
|
|
14553
15003
|
const plannerInstalled = installed && config.roadmapScanner.enabled && (hasScheduledCommand(entries, "planner") || hasScheduledCommand(entries, "slice"));
|
|
14554
15004
|
const analyticsInstalled = installed && config.analytics.enabled && hasScheduledCommand(entries, "analytics");
|
|
15005
|
+
const mergerInstalled = installed && (config.merger?.enabled ?? false) && hasScheduledCommand(entries, "merge");
|
|
14555
15006
|
return {
|
|
14556
15007
|
executor: {
|
|
14557
15008
|
schedule: config.cronSchedule,
|
|
@@ -14601,6 +15052,14 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
14601
15052
|
manualDelayMinutes: analyticsPlan.manualDelayMinutes,
|
|
14602
15053
|
balancedDelayMinutes: analyticsPlan.balancedDelayMinutes
|
|
14603
15054
|
},
|
|
15055
|
+
merger: {
|
|
15056
|
+
schedule: config.merger?.schedule ?? "55 */4 * * *",
|
|
15057
|
+
installed: mergerInstalled,
|
|
15058
|
+
nextRun: mergerInstalled ? addDelayToIsoString(computeNextRun(config.merger?.schedule ?? "55 */4 * * *"), mergerPlan.totalDelayMinutes) : null,
|
|
15059
|
+
delayMinutes: mergerPlan.totalDelayMinutes,
|
|
15060
|
+
manualDelayMinutes: mergerPlan.manualDelayMinutes,
|
|
15061
|
+
balancedDelayMinutes: mergerPlan.balancedDelayMinutes
|
|
15062
|
+
},
|
|
14604
15063
|
paused: !installed,
|
|
14605
15064
|
schedulingPriority: config.schedulingPriority,
|
|
14606
15065
|
entries
|
|
@@ -15710,7 +16169,7 @@ function releasePlannerLock(lockFile) {
|
|
|
15710
16169
|
}
|
|
15711
16170
|
}
|
|
15712
16171
|
function resolvePlannerIssueColumn(config) {
|
|
15713
|
-
return config.roadmapScanner.issueColumn === "
|
|
16172
|
+
return config.roadmapScanner.issueColumn === "Draft" ? "Draft" : "Ready";
|
|
15714
16173
|
}
|
|
15715
16174
|
function buildPlannerIssueBody(projectDir, config, result) {
|
|
15716
16175
|
const relativePrdPath = path38.join(config.prdDir, result.file ?? "").replace(/\\/g, "/");
|
|
@@ -15724,23 +16183,25 @@ function buildPlannerIssueBody(projectDir, config, result) {
|
|
|
15724
16183
|
}
|
|
15725
16184
|
const maxBodyChars = 6e4;
|
|
15726
16185
|
const truncated = prdContent.length > maxBodyChars;
|
|
15727
|
-
const
|
|
16186
|
+
const prdBody = truncated ? `${prdContent.slice(0, maxBodyChars)}
|
|
15728
16187
|
|
|
15729
16188
|
...[truncated]` : prdContent;
|
|
15730
|
-
const
|
|
15731
|
-
|
|
15732
|
-
`- Source
|
|
15733
|
-
sourceItem.description
|
|
15734
|
-
|
|
16189
|
+
const metaLines = [`- PRD file: \`${relativePrdPath}\``];
|
|
16190
|
+
if (sourceItem) {
|
|
16191
|
+
metaLines.push(`- Source section: ${sourceItem.section}`);
|
|
16192
|
+
if (sourceItem.description) {
|
|
16193
|
+
metaLines.push(`- Source summary: ${sourceItem.description}`);
|
|
16194
|
+
}
|
|
16195
|
+
}
|
|
15735
16196
|
return [
|
|
15736
|
-
|
|
16197
|
+
prdBody,
|
|
15737
16198
|
"",
|
|
15738
|
-
|
|
15739
|
-
|
|
16199
|
+
"<details>",
|
|
16200
|
+
"<summary>Source metadata</summary>",
|
|
15740
16201
|
"",
|
|
15741
|
-
|
|
16202
|
+
...metaLines,
|
|
15742
16203
|
"",
|
|
15743
|
-
|
|
16204
|
+
"</details>"
|
|
15744
16205
|
].join("\n");
|
|
15745
16206
|
}
|
|
15746
16207
|
async function createPlannerIssue(projectDir, config, result) {
|
|
@@ -15755,9 +16216,11 @@ async function createPlannerIssue(projectDir, config, result) {
|
|
|
15755
16216
|
if (!board) {
|
|
15756
16217
|
return { created: false, skippedReason: "board-not-configured" };
|
|
15757
16218
|
}
|
|
16219
|
+
const issueTitle = `PRD: ${result.item.title}`;
|
|
16220
|
+
const normalizeTitle = (t) => t.replace(/^PRD:\s*/i, "").trim().toLowerCase();
|
|
15758
16221
|
const existingIssues = await provider.getAllIssues();
|
|
15759
16222
|
const existing = existingIssues.find(
|
|
15760
|
-
(issue2) => issue2.title
|
|
16223
|
+
(issue2) => normalizeTitle(issue2.title) === normalizeTitle(result.item.title)
|
|
15761
16224
|
);
|
|
15762
16225
|
if (existing) {
|
|
15763
16226
|
return {
|
|
@@ -15768,7 +16231,7 @@ async function createPlannerIssue(projectDir, config, result) {
|
|
|
15768
16231
|
};
|
|
15769
16232
|
}
|
|
15770
16233
|
const issue = await provider.createIssue({
|
|
15771
|
-
title:
|
|
16234
|
+
title: issueTitle,
|
|
15772
16235
|
body: buildPlannerIssueBody(projectDir, config, result),
|
|
15773
16236
|
column: resolvePlannerIssueColumn(config)
|
|
15774
16237
|
});
|
|
@@ -16742,8 +17205,9 @@ function createQueueCommand() {
|
|
|
16742
17205
|
}
|
|
16743
17206
|
process.exit(0);
|
|
16744
17207
|
});
|
|
16745
|
-
queue.command("dispatch").description("Dispatch the next pending job (used by cron scripts)").option("--log <file>", "Log file to write dispatch output").action((_opts) => {
|
|
16746
|
-
const
|
|
17208
|
+
queue.command("dispatch").description("Dispatch the next pending job (used by cron scripts)").option("--log <file>", "Log file to write dispatch output").option("--project-dir <dir>", "Project directory to load queue config from (defaults to cwd)").action((_opts) => {
|
|
17209
|
+
const configDir = _opts.projectDir ?? process.cwd();
|
|
17210
|
+
const entry = dispatchNextJob(loadConfig(configDir).queue);
|
|
16747
17211
|
if (!entry) {
|
|
16748
17212
|
logger5.info("No pending jobs to dispatch");
|
|
16749
17213
|
return;
|
|
@@ -16821,8 +17285,9 @@ function createQueueCommand() {
|
|
|
16821
17285
|
}
|
|
16822
17286
|
removeJob(queueId);
|
|
16823
17287
|
});
|
|
16824
|
-
queue.command("can-start").description("Return a zero exit status when the global queue has an available slot").action(() => {
|
|
16825
|
-
const
|
|
17288
|
+
queue.command("can-start").description("Return a zero exit status when the global queue has an available slot").option("--project-dir <dir>", "Project directory to load queue config from (defaults to cwd)").action((opts) => {
|
|
17289
|
+
const configDir = opts.projectDir ?? process.cwd();
|
|
17290
|
+
const queueConfig = loadConfig(configDir).queue;
|
|
16826
17291
|
process.exit(canStartJob(queueConfig) ? 0 : 1);
|
|
16827
17292
|
});
|
|
16828
17293
|
queue.command("expire").description("Expire stale queued jobs").option(
|
|
@@ -16844,7 +17309,26 @@ function createQueueCommand() {
|
|
|
16844
17309
|
});
|
|
16845
17310
|
return queue;
|
|
16846
17311
|
}
|
|
16847
|
-
var QUEUE_MARKER_KEYS = /* @__PURE__ */ new Set([
|
|
17312
|
+
var QUEUE_MARKER_KEYS = /* @__PURE__ */ new Set([
|
|
17313
|
+
"NW_DRY_RUN",
|
|
17314
|
+
"NW_CRON_TRIGGER",
|
|
17315
|
+
"NW_DEFAULT_BRANCH",
|
|
17316
|
+
"NW_TARGET_PR",
|
|
17317
|
+
"NW_REVIEWER_WORKER_MODE",
|
|
17318
|
+
"NW_REVIEWER_PARALLEL",
|
|
17319
|
+
"NW_REVIEWER_WORKER_STAGGER",
|
|
17320
|
+
"NW_REVIEWER_MAX_RUNTIME",
|
|
17321
|
+
"NW_REVIEWER_MAX_RETRIES",
|
|
17322
|
+
"NW_REVIEWER_RETRY_DELAY",
|
|
17323
|
+
"NW_REVIEWER_MAX_PRS_PER_RUN",
|
|
17324
|
+
"NW_MIN_REVIEW_SCORE",
|
|
17325
|
+
"NW_BRANCH_PATTERNS",
|
|
17326
|
+
"NW_PRD_DIR",
|
|
17327
|
+
"NW_AUTO_MERGE",
|
|
17328
|
+
"NW_AUTO_MERGE_METHOD",
|
|
17329
|
+
"NW_MAX_RUNTIME",
|
|
17330
|
+
"NW_QA_MAX_RUNTIME"
|
|
17331
|
+
]);
|
|
16848
17332
|
function filterQueueMarkers(envJson) {
|
|
16849
17333
|
const result = {};
|
|
16850
17334
|
for (const [key, value] of Object.entries(envJson)) {
|
|
@@ -16913,6 +17397,422 @@ function notifyCommand(program2) {
|
|
|
16913
17397
|
);
|
|
16914
17398
|
}
|
|
16915
17399
|
|
|
17400
|
+
// src/commands/summary.ts
|
|
17401
|
+
init_dist();
|
|
17402
|
+
import path42 from "path";
|
|
17403
|
+
import chalk8 from "chalk";
|
|
17404
|
+
function formatDuration2(seconds) {
|
|
17405
|
+
if (seconds === null) return "-";
|
|
17406
|
+
const mins = Math.floor(seconds / 60);
|
|
17407
|
+
const secs = seconds % 60;
|
|
17408
|
+
if (mins === 0) return `${secs}s`;
|
|
17409
|
+
return `${mins}m ${secs}s`;
|
|
17410
|
+
}
|
|
17411
|
+
function formatCiStatus2(status) {
|
|
17412
|
+
if (status === "pass") return chalk8.green("pass");
|
|
17413
|
+
if (status === "fail") return chalk8.red("fail");
|
|
17414
|
+
if (status === "pending") return chalk8.yellow("pending");
|
|
17415
|
+
return chalk8.dim("unknown");
|
|
17416
|
+
}
|
|
17417
|
+
function formatReviewScore2(score) {
|
|
17418
|
+
if (score === null) return chalk8.dim("-");
|
|
17419
|
+
if (score >= 80) return chalk8.green(String(score));
|
|
17420
|
+
if (score >= 60) return chalk8.yellow(String(score));
|
|
17421
|
+
return chalk8.red(String(score));
|
|
17422
|
+
}
|
|
17423
|
+
function formatJobStatus(status) {
|
|
17424
|
+
if (status === "success") return chalk8.green("success");
|
|
17425
|
+
if (status === "failure") return chalk8.red("failure");
|
|
17426
|
+
if (status === "timeout") return chalk8.yellow("timeout");
|
|
17427
|
+
if (status === "rate_limited") return chalk8.magenta("rate_limited");
|
|
17428
|
+
if (status === "skipped") return chalk8.dim("skipped");
|
|
17429
|
+
return chalk8.dim(status);
|
|
17430
|
+
}
|
|
17431
|
+
function getProjectName2(projectPath) {
|
|
17432
|
+
return path42.basename(projectPath) || projectPath;
|
|
17433
|
+
}
|
|
17434
|
+
function formatProvider(providerKey) {
|
|
17435
|
+
return providerKey.split(":")[0] || providerKey;
|
|
17436
|
+
}
|
|
17437
|
+
function summaryCommand(program2) {
|
|
17438
|
+
program2.command("summary").description("Show a summary of recent Night Watch activity").option(
|
|
17439
|
+
"--hours <n>",
|
|
17440
|
+
"Time window in hours (default: 12)",
|
|
17441
|
+
String(DEFAULT_SUMMARY_WINDOW_HOURS)
|
|
17442
|
+
).option("--json", "Output summary as JSON").action(async (options) => {
|
|
17443
|
+
try {
|
|
17444
|
+
const projectDir = process.cwd();
|
|
17445
|
+
const config = loadConfig(projectDir);
|
|
17446
|
+
const hours = parseInt(options.hours || String(DEFAULT_SUMMARY_WINDOW_HOURS), 10);
|
|
17447
|
+
if (isNaN(hours) || hours <= 0) {
|
|
17448
|
+
console.error("Error: --hours must be a positive integer");
|
|
17449
|
+
process.exit(1);
|
|
17450
|
+
}
|
|
17451
|
+
const data = await getSummaryData(projectDir, hours, config.branchPatterns);
|
|
17452
|
+
if (options.json) {
|
|
17453
|
+
console.log(JSON.stringify(data, null, 2));
|
|
17454
|
+
return;
|
|
17455
|
+
}
|
|
17456
|
+
console.log();
|
|
17457
|
+
console.log(chalk8.bold.cyan(`Night Watch Summary (last ${data.windowHours}h)`));
|
|
17458
|
+
console.log(chalk8.dim("\u2500".repeat(40)));
|
|
17459
|
+
console.log();
|
|
17460
|
+
if (data.jobRuns.length === 0) {
|
|
17461
|
+
info("No recent activity in this time window.");
|
|
17462
|
+
console.log();
|
|
17463
|
+
} else {
|
|
17464
|
+
const countParts = [];
|
|
17465
|
+
if (data.counts.succeeded > 0) {
|
|
17466
|
+
countParts.push(chalk8.green(`\u2713 ${data.counts.succeeded} succeeded`));
|
|
17467
|
+
}
|
|
17468
|
+
if (data.counts.failed > 0) {
|
|
17469
|
+
countParts.push(chalk8.red(`\u2717 ${data.counts.failed} failed`));
|
|
17470
|
+
}
|
|
17471
|
+
if (data.counts.timedOut > 0) {
|
|
17472
|
+
countParts.push(chalk8.yellow(`\u23F1 ${data.counts.timedOut} timed out`));
|
|
17473
|
+
}
|
|
17474
|
+
if (data.counts.rateLimited > 0) {
|
|
17475
|
+
countParts.push(chalk8.magenta(`\u23F3 ${data.counts.rateLimited} rate limited`));
|
|
17476
|
+
}
|
|
17477
|
+
if (data.counts.skipped > 0) {
|
|
17478
|
+
countParts.push(chalk8.dim(`${data.counts.skipped} skipped`));
|
|
17479
|
+
}
|
|
17480
|
+
console.log(`Jobs Executed: ${data.counts.total}`);
|
|
17481
|
+
if (countParts.length > 0) {
|
|
17482
|
+
console.log(` ${countParts.join(" ")}`);
|
|
17483
|
+
}
|
|
17484
|
+
console.log();
|
|
17485
|
+
const table = createTable({
|
|
17486
|
+
head: ["Job", "Status", "Project", "Provider", "Duration"],
|
|
17487
|
+
colWidths: [12, 12, 20, 12, 12]
|
|
17488
|
+
});
|
|
17489
|
+
for (const run2 of data.jobRuns.slice(0, 10)) {
|
|
17490
|
+
table.push([
|
|
17491
|
+
run2.jobType,
|
|
17492
|
+
formatJobStatus(run2.status),
|
|
17493
|
+
getProjectName2(run2.projectPath),
|
|
17494
|
+
formatProvider(run2.providerKey),
|
|
17495
|
+
formatDuration2(run2.durationSeconds)
|
|
17496
|
+
]);
|
|
17497
|
+
}
|
|
17498
|
+
console.log(table.toString());
|
|
17499
|
+
if (data.jobRuns.length > 10) {
|
|
17500
|
+
dim(` ... and ${data.jobRuns.length - 10} more`);
|
|
17501
|
+
}
|
|
17502
|
+
console.log();
|
|
17503
|
+
}
|
|
17504
|
+
if (data.openPrs.length > 0) {
|
|
17505
|
+
header(`Open PRs (${data.openPrs.length})`);
|
|
17506
|
+
const prTable = createTable({
|
|
17507
|
+
head: ["#", "Title", "CI", "Score"],
|
|
17508
|
+
colWidths: [6, 40, 10, 8]
|
|
17509
|
+
});
|
|
17510
|
+
for (const pr of data.openPrs) {
|
|
17511
|
+
const title = pr.title.length > 37 ? pr.title.substring(0, 34) + "..." : pr.title;
|
|
17512
|
+
prTable.push([
|
|
17513
|
+
String(pr.number),
|
|
17514
|
+
title,
|
|
17515
|
+
formatCiStatus2(pr.ciStatus),
|
|
17516
|
+
formatReviewScore2(pr.reviewScore)
|
|
17517
|
+
]);
|
|
17518
|
+
}
|
|
17519
|
+
console.log(prTable.toString());
|
|
17520
|
+
console.log();
|
|
17521
|
+
}
|
|
17522
|
+
if (data.pendingQueueItems.length > 0) {
|
|
17523
|
+
const jobTypes = [...new Set(data.pendingQueueItems.map((item) => item.jobType))];
|
|
17524
|
+
const projectNames = [...new Set(data.pendingQueueItems.map((item) => item.projectName))];
|
|
17525
|
+
dim(
|
|
17526
|
+
`Queue: ${data.pendingQueueItems.length} pending (${jobTypes.join(", ")}) for ${projectNames.join(", ")}`
|
|
17527
|
+
);
|
|
17528
|
+
console.log();
|
|
17529
|
+
}
|
|
17530
|
+
if (data.actionItems.length > 0) {
|
|
17531
|
+
console.log(chalk8.yellow("\u26A0 Action needed:"));
|
|
17532
|
+
for (const item of data.actionItems) {
|
|
17533
|
+
console.log(` \u2022 ${item}`);
|
|
17534
|
+
}
|
|
17535
|
+
} else {
|
|
17536
|
+
console.log(chalk8.green("\u2713 No action needed \u2014 all jobs healthy."));
|
|
17537
|
+
}
|
|
17538
|
+
console.log();
|
|
17539
|
+
} catch (error2) {
|
|
17540
|
+
console.error(
|
|
17541
|
+
`Error getting summary: ${error2 instanceof Error ? error2.message : String(error2)}`
|
|
17542
|
+
);
|
|
17543
|
+
process.exit(1);
|
|
17544
|
+
}
|
|
17545
|
+
});
|
|
17546
|
+
}
|
|
17547
|
+
|
|
17548
|
+
// src/commands/resolve.ts
|
|
17549
|
+
init_dist();
|
|
17550
|
+
import { execFileSync as execFileSync7 } from "child_process";
|
|
17551
|
+
import * as path43 from "path";
|
|
17552
|
+
function buildEnvVars6(config, options) {
|
|
17553
|
+
const env = buildBaseEnvVars(config, "pr-resolver", options.dryRun);
|
|
17554
|
+
env.NW_PR_RESOLVER_MAX_RUNTIME = String(config.prResolver.maxRuntime);
|
|
17555
|
+
env.NW_PR_RESOLVER_MAX_PRS_PER_RUN = String(config.prResolver.maxPrsPerRun);
|
|
17556
|
+
env.NW_PR_RESOLVER_PER_PR_TIMEOUT = String(config.prResolver.perPrTimeout);
|
|
17557
|
+
env.NW_PR_RESOLVER_AI_CONFLICT_RESOLUTION = config.prResolver.aiConflictResolution ? "1" : "0";
|
|
17558
|
+
env.NW_PR_RESOLVER_AI_REVIEW_RESOLUTION = config.prResolver.aiReviewResolution ? "1" : "0";
|
|
17559
|
+
env.NW_PR_RESOLVER_READY_LABEL = config.prResolver.readyLabel;
|
|
17560
|
+
env.NW_PR_RESOLVER_BRANCH_PATTERNS = config.prResolver.branchPatterns.join(",");
|
|
17561
|
+
return env;
|
|
17562
|
+
}
|
|
17563
|
+
function applyCliOverrides5(config, options) {
|
|
17564
|
+
const overridden = { ...config, prResolver: { ...config.prResolver } };
|
|
17565
|
+
if (options.timeout) {
|
|
17566
|
+
const timeout = parseInt(options.timeout, 10);
|
|
17567
|
+
if (!isNaN(timeout)) {
|
|
17568
|
+
overridden.prResolver.maxRuntime = timeout;
|
|
17569
|
+
}
|
|
17570
|
+
}
|
|
17571
|
+
if (options.provider) {
|
|
17572
|
+
overridden._cliProviderOverride = options.provider;
|
|
17573
|
+
}
|
|
17574
|
+
return overridden;
|
|
17575
|
+
}
|
|
17576
|
+
function getOpenPrs() {
|
|
17577
|
+
try {
|
|
17578
|
+
const args = ["pr", "list", "--state", "open", "--json", "number,title,headRefName,mergeable"];
|
|
17579
|
+
const result = execFileSync7("gh", args, {
|
|
17580
|
+
encoding: "utf-8",
|
|
17581
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
17582
|
+
});
|
|
17583
|
+
const prs = JSON.parse(result.trim() || "[]");
|
|
17584
|
+
return prs.map(
|
|
17585
|
+
(pr) => ({
|
|
17586
|
+
number: pr.number,
|
|
17587
|
+
title: pr.title,
|
|
17588
|
+
branch: pr.headRefName,
|
|
17589
|
+
mergeable: pr.mergeable
|
|
17590
|
+
})
|
|
17591
|
+
);
|
|
17592
|
+
} catch {
|
|
17593
|
+
return [];
|
|
17594
|
+
}
|
|
17595
|
+
}
|
|
17596
|
+
function resolveCommand(program2) {
|
|
17597
|
+
program2.command("resolve").description("Run PR conflict resolver now").option("--dry-run", "Show what would be executed without running").option("--timeout <seconds>", "Override max runtime").option("--provider <string>", "AI provider to use").action(async (options) => {
|
|
17598
|
+
const projectDir = process.cwd();
|
|
17599
|
+
let config = loadConfig(projectDir);
|
|
17600
|
+
config = applyCliOverrides5(config, options);
|
|
17601
|
+
if (!config.prResolver.enabled && !options.dryRun) {
|
|
17602
|
+
info("PR resolver is disabled in config; skipping.");
|
|
17603
|
+
process.exit(0);
|
|
17604
|
+
}
|
|
17605
|
+
const envVars = buildEnvVars6(config, options);
|
|
17606
|
+
const scriptPath = getScriptPath("night-watch-pr-resolver-cron.sh");
|
|
17607
|
+
if (options.dryRun) {
|
|
17608
|
+
header("Dry Run: PR Resolver");
|
|
17609
|
+
const resolverProvider = resolveJobProvider(config, "pr-resolver");
|
|
17610
|
+
header("Configuration");
|
|
17611
|
+
const configTable = createTable({ head: ["Setting", "Value"] });
|
|
17612
|
+
configTable.push(["Provider", resolverProvider]);
|
|
17613
|
+
configTable.push([
|
|
17614
|
+
"Max Runtime",
|
|
17615
|
+
`${config.prResolver.maxRuntime}s (${Math.floor(config.prResolver.maxRuntime / 60)}min)`
|
|
17616
|
+
]);
|
|
17617
|
+
configTable.push([
|
|
17618
|
+
"Max PRs Per Run",
|
|
17619
|
+
config.prResolver.maxPrsPerRun === 0 ? "Unlimited" : String(config.prResolver.maxPrsPerRun)
|
|
17620
|
+
]);
|
|
17621
|
+
configTable.push(["Per-PR Timeout", `${config.prResolver.perPrTimeout}s`]);
|
|
17622
|
+
configTable.push([
|
|
17623
|
+
"AI Conflict Resolution",
|
|
17624
|
+
config.prResolver.aiConflictResolution ? "Enabled" : "Disabled"
|
|
17625
|
+
]);
|
|
17626
|
+
configTable.push([
|
|
17627
|
+
"AI Review Resolution",
|
|
17628
|
+
config.prResolver.aiReviewResolution ? "Enabled" : "Disabled"
|
|
17629
|
+
]);
|
|
17630
|
+
configTable.push(["Ready Label", config.prResolver.readyLabel]);
|
|
17631
|
+
configTable.push([
|
|
17632
|
+
"Branch Patterns",
|
|
17633
|
+
config.prResolver.branchPatterns.length > 0 ? config.prResolver.branchPatterns.join(", ") : "(all)"
|
|
17634
|
+
]);
|
|
17635
|
+
console.log(configTable.toString());
|
|
17636
|
+
header("Open PRs");
|
|
17637
|
+
const openPrs = getOpenPrs();
|
|
17638
|
+
if (openPrs.length === 0) {
|
|
17639
|
+
dim(" (no open PRs found)");
|
|
17640
|
+
} else {
|
|
17641
|
+
for (const pr of openPrs) {
|
|
17642
|
+
const conflictStatus = pr.mergeable === "CONFLICTING" ? " [CONFLICT]" : "";
|
|
17643
|
+
info(`#${pr.number}: ${pr.title}${conflictStatus}`);
|
|
17644
|
+
dim(` Branch: ${pr.branch}`);
|
|
17645
|
+
}
|
|
17646
|
+
}
|
|
17647
|
+
header("Environment Variables");
|
|
17648
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
17649
|
+
dim(` ${key}=${value}`);
|
|
17650
|
+
}
|
|
17651
|
+
header("Command");
|
|
17652
|
+
dim(` bash ${scriptPath} ${projectDir}`);
|
|
17653
|
+
console.log();
|
|
17654
|
+
process.exit(0);
|
|
17655
|
+
}
|
|
17656
|
+
const spinner = createSpinner("Running PR resolver...");
|
|
17657
|
+
spinner.start();
|
|
17658
|
+
try {
|
|
17659
|
+
await maybeApplyCronSchedulingDelay(config, "pr-resolver", projectDir);
|
|
17660
|
+
const { exitCode, stdout, stderr } = await executeScriptWithOutput(
|
|
17661
|
+
scriptPath,
|
|
17662
|
+
[projectDir],
|
|
17663
|
+
envVars
|
|
17664
|
+
);
|
|
17665
|
+
const scriptResult = parseScriptResult(`${stdout}
|
|
17666
|
+
${stderr}`);
|
|
17667
|
+
if (exitCode === 0) {
|
|
17668
|
+
if (scriptResult?.status === "queued") {
|
|
17669
|
+
spinner.succeed("PR resolver queued \u2014 another job is currently running");
|
|
17670
|
+
} else if (scriptResult?.status?.startsWith("skip_")) {
|
|
17671
|
+
spinner.succeed("PR resolver completed (no PRs needed resolution)");
|
|
17672
|
+
} else {
|
|
17673
|
+
spinner.succeed("PR resolver completed successfully");
|
|
17674
|
+
}
|
|
17675
|
+
} else {
|
|
17676
|
+
spinner.fail(`PR resolver exited with code ${exitCode}`);
|
|
17677
|
+
}
|
|
17678
|
+
const notificationEvent = exitCode === 0 ? "pr_resolver_completed" : "pr_resolver_failed";
|
|
17679
|
+
await sendNotifications(config, {
|
|
17680
|
+
event: notificationEvent,
|
|
17681
|
+
projectName: path43.basename(projectDir),
|
|
17682
|
+
exitCode,
|
|
17683
|
+
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL)
|
|
17684
|
+
});
|
|
17685
|
+
process.exit(exitCode);
|
|
17686
|
+
} catch (err) {
|
|
17687
|
+
spinner.fail("Failed to execute resolve command");
|
|
17688
|
+
error(`${err instanceof Error ? err.message : String(err)}`);
|
|
17689
|
+
process.exit(1);
|
|
17690
|
+
}
|
|
17691
|
+
});
|
|
17692
|
+
}
|
|
17693
|
+
|
|
17694
|
+
// src/commands/merge.ts
|
|
17695
|
+
init_dist();
|
|
17696
|
+
import * as path44 from "path";
|
|
17697
|
+
function buildEnvVars7(config, options) {
|
|
17698
|
+
const env = buildBaseEnvVars(config, "merger", options.dryRun);
|
|
17699
|
+
env.NW_MERGER_MAX_RUNTIME = String(config.merger.maxRuntime);
|
|
17700
|
+
env.NW_MERGER_MERGE_METHOD = config.merger.mergeMethod;
|
|
17701
|
+
env.NW_MERGER_MIN_REVIEW_SCORE = String(config.merger.minReviewScore);
|
|
17702
|
+
env.NW_MERGER_BRANCH_PATTERNS = (config.merger.branchPatterns.length > 0 ? config.merger.branchPatterns : config.branchPatterns).join(",");
|
|
17703
|
+
env.NW_MERGER_REBASE_BEFORE_MERGE = config.merger.rebaseBeforeMerge ? "1" : "0";
|
|
17704
|
+
env.NW_MERGER_MAX_PRS_PER_RUN = String(config.merger.maxPrsPerRun);
|
|
17705
|
+
return env;
|
|
17706
|
+
}
|
|
17707
|
+
function applyCliOverrides6(config, options) {
|
|
17708
|
+
const overridden = { ...config, merger: { ...config.merger } };
|
|
17709
|
+
if (options.timeout) {
|
|
17710
|
+
const timeout = parseInt(options.timeout, 10);
|
|
17711
|
+
if (!isNaN(timeout)) {
|
|
17712
|
+
overridden.merger.maxRuntime = timeout;
|
|
17713
|
+
}
|
|
17714
|
+
}
|
|
17715
|
+
if (options.provider) {
|
|
17716
|
+
overridden._cliProviderOverride = options.provider;
|
|
17717
|
+
}
|
|
17718
|
+
return overridden;
|
|
17719
|
+
}
|
|
17720
|
+
function resolveMergeNotificationEvent(exitCode, mergedCount, failedCount) {
|
|
17721
|
+
if (exitCode === 0 && mergedCount > 0) {
|
|
17722
|
+
return "merge_completed";
|
|
17723
|
+
}
|
|
17724
|
+
if (exitCode !== 0 || failedCount > 0) {
|
|
17725
|
+
return "merge_failed";
|
|
17726
|
+
}
|
|
17727
|
+
return null;
|
|
17728
|
+
}
|
|
17729
|
+
function printDryRun(config, envVars, scriptPath, projectDir) {
|
|
17730
|
+
header("Dry Run: Merge Orchestrator");
|
|
17731
|
+
const mergerProvider = resolveJobProvider(config, "merger");
|
|
17732
|
+
header("Configuration");
|
|
17733
|
+
const configTable = createTable({ head: ["Setting", "Value"] });
|
|
17734
|
+
configTable.push(["Provider", mergerProvider]);
|
|
17735
|
+
configTable.push([
|
|
17736
|
+
"Max Runtime",
|
|
17737
|
+
`${config.merger.maxRuntime}s (${Math.floor(config.merger.maxRuntime / 60)}min)`
|
|
17738
|
+
]);
|
|
17739
|
+
configTable.push(["Merge Method", config.merger.mergeMethod]);
|
|
17740
|
+
configTable.push(["Min Review Score", `${config.merger.minReviewScore}/100`]);
|
|
17741
|
+
configTable.push([
|
|
17742
|
+
"Branch Patterns",
|
|
17743
|
+
config.merger.branchPatterns.length > 0 ? config.merger.branchPatterns.join(", ") : "(top-level)"
|
|
17744
|
+
]);
|
|
17745
|
+
configTable.push(["Rebase Before Merge", config.merger.rebaseBeforeMerge ? "Yes" : "No"]);
|
|
17746
|
+
configTable.push([
|
|
17747
|
+
"Max PRs Per Run",
|
|
17748
|
+
config.merger.maxPrsPerRun === 0 ? "Unlimited" : String(config.merger.maxPrsPerRun)
|
|
17749
|
+
]);
|
|
17750
|
+
console.log(configTable.toString());
|
|
17751
|
+
header("Environment Variables");
|
|
17752
|
+
for (const [key, value] of Object.entries(envVars)) {
|
|
17753
|
+
dim(` ${key}=${value}`);
|
|
17754
|
+
}
|
|
17755
|
+
header("Command");
|
|
17756
|
+
dim(` bash ${scriptPath} ${projectDir}`);
|
|
17757
|
+
console.log();
|
|
17758
|
+
}
|
|
17759
|
+
function mergeCommand(program2) {
|
|
17760
|
+
program2.command("merge").description("Merge eligible PRs in FIFO order").option("--dry-run", "Show what would be executed without running").option("--timeout <seconds>", "Override max runtime").option("--provider <string>", "AI provider to use").action(async (options) => {
|
|
17761
|
+
const projectDir = process.cwd();
|
|
17762
|
+
let config = loadConfig(projectDir);
|
|
17763
|
+
config = applyCliOverrides6(config, options);
|
|
17764
|
+
if (!config.merger.enabled && !options.dryRun) {
|
|
17765
|
+
info("Merge orchestrator is disabled in config; skipping.");
|
|
17766
|
+
process.exit(0);
|
|
17767
|
+
}
|
|
17768
|
+
const envVars = buildEnvVars7(config, options);
|
|
17769
|
+
const scriptPath = getScriptPath("night-watch-merger-cron.sh");
|
|
17770
|
+
if (options.dryRun) {
|
|
17771
|
+
printDryRun(config, envVars, scriptPath, projectDir);
|
|
17772
|
+
process.exit(0);
|
|
17773
|
+
}
|
|
17774
|
+
const spinner = createSpinner("Running merge orchestrator...");
|
|
17775
|
+
spinner.start();
|
|
17776
|
+
try {
|
|
17777
|
+
await maybeApplyCronSchedulingDelay(config, "merger", projectDir);
|
|
17778
|
+
const { exitCode, stdout, stderr } = await executeScriptWithOutput(
|
|
17779
|
+
scriptPath,
|
|
17780
|
+
[projectDir],
|
|
17781
|
+
envVars
|
|
17782
|
+
);
|
|
17783
|
+
const scriptResult = parseScriptResult(`${stdout}
|
|
17784
|
+
${stderr}`);
|
|
17785
|
+
if (exitCode === 0) {
|
|
17786
|
+
if (scriptResult?.status === "queued") {
|
|
17787
|
+
spinner.succeed("Merge orchestrator queued \u2014 another job is currently running");
|
|
17788
|
+
} else if (scriptResult?.status?.startsWith("skip_")) {
|
|
17789
|
+
spinner.succeed("Merge orchestrator completed (no eligible PRs)");
|
|
17790
|
+
} else {
|
|
17791
|
+
spinner.succeed("Merge orchestrator completed successfully");
|
|
17792
|
+
}
|
|
17793
|
+
} else {
|
|
17794
|
+
spinner.fail(`Merge orchestrator exited with code ${exitCode}`);
|
|
17795
|
+
}
|
|
17796
|
+
const mergedCount = parseInt(scriptResult?.data?.merged ?? "0", 10);
|
|
17797
|
+
const failedCount = parseInt(scriptResult?.data?.failed ?? "0", 10);
|
|
17798
|
+
const notificationEvent = resolveMergeNotificationEvent(exitCode, mergedCount, failedCount);
|
|
17799
|
+
if (notificationEvent) {
|
|
17800
|
+
await sendNotifications(config, {
|
|
17801
|
+
event: notificationEvent,
|
|
17802
|
+
projectName: path44.basename(projectDir),
|
|
17803
|
+
exitCode,
|
|
17804
|
+
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL)
|
|
17805
|
+
});
|
|
17806
|
+
}
|
|
17807
|
+
process.exit(exitCode);
|
|
17808
|
+
} catch (err) {
|
|
17809
|
+
spinner.fail("Failed to execute merge command");
|
|
17810
|
+
error(`${err instanceof Error ? err.message : String(err)}`);
|
|
17811
|
+
process.exit(1);
|
|
17812
|
+
}
|
|
17813
|
+
});
|
|
17814
|
+
}
|
|
17815
|
+
|
|
16916
17816
|
// src/cli.ts
|
|
16917
17817
|
var __filename5 = fileURLToPath6(import.meta.url);
|
|
16918
17818
|
var __dirname5 = dirname12(__filename5);
|
|
@@ -16954,4 +17854,7 @@ program.addCommand(createStateCommand());
|
|
|
16954
17854
|
boardCommand(program);
|
|
16955
17855
|
queueCommand(program);
|
|
16956
17856
|
notifyCommand(program);
|
|
17857
|
+
summaryCommand(program);
|
|
17858
|
+
resolveCommand(program);
|
|
17859
|
+
mergeCommand(program);
|
|
16957
17860
|
program.parse();
|