@jonit-dev/night-watch-cli 1.8.8-beta.10 → 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 +346 -57
- package/dist/cli.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 +25 -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/review.d.ts +0 -1
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +0 -13
- 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/scripts/night-watch-merger-cron.sh +321 -0
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +1 -137
- 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
|
@@ -319,6 +319,38 @@ var init_job_registry = __esm({
|
|
|
319
319
|
targetColumn: "Draft",
|
|
320
320
|
analysisPrompt: ""
|
|
321
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
|
+
}
|
|
322
354
|
}
|
|
323
355
|
];
|
|
324
356
|
JOB_MAP = new Map(JOB_REGISTRY.map((job) => [job.id, job]));
|
|
@@ -339,7 +371,7 @@ function resolveProviderBucketKey(provider, providerEnv) {
|
|
|
339
371
|
return `claude-proxy:${baseUrl}`;
|
|
340
372
|
}
|
|
341
373
|
}
|
|
342
|
-
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, 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;
|
|
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;
|
|
343
375
|
var init_constants = __esm({
|
|
344
376
|
"../core/dist/constants.js"() {
|
|
345
377
|
"use strict";
|
|
@@ -384,7 +416,7 @@ var init_constants = __esm({
|
|
|
384
416
|
slicerSchedule: DEFAULT_SLICER_SCHEDULE,
|
|
385
417
|
slicerMaxRuntime: DEFAULT_SLICER_MAX_RUNTIME,
|
|
386
418
|
priorityMode: "roadmap-first",
|
|
387
|
-
issueColumn: "
|
|
419
|
+
issueColumn: "Ready"
|
|
388
420
|
};
|
|
389
421
|
DEFAULT_TEMPLATES_DIR = ".night-watch/templates";
|
|
390
422
|
DEFAULT_BOARD_PROVIDER = {
|
|
@@ -458,6 +490,24 @@ If no issues are warranted, output an empty array: []`;
|
|
|
458
490
|
aiReviewResolution: DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION,
|
|
459
491
|
readyLabel: DEFAULT_PR_RESOLVER_READY_LABEL
|
|
460
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";
|
|
461
511
|
AUDIT_LOG_NAME = "audit";
|
|
462
512
|
PLANNER_LOG_NAME = "slicer";
|
|
463
513
|
ANALYTICS_LOG_NAME = "analytics";
|
|
@@ -734,7 +784,7 @@ function normalizeConfig(rawConfig) {
|
|
|
734
784
|
if (mergeMethod && VALID_MERGE_METHODS.includes(mergeMethod)) {
|
|
735
785
|
normalized.autoMergeMethod = mergeMethod;
|
|
736
786
|
}
|
|
737
|
-
for (const jobId of ["qa", "audit", "analytics"]) {
|
|
787
|
+
for (const jobId of ["qa", "audit", "analytics", "merger"]) {
|
|
738
788
|
const jobDef = getJobDef(jobId);
|
|
739
789
|
if (!jobDef)
|
|
740
790
|
continue;
|
|
@@ -1068,6 +1118,17 @@ function buildEnvOverrideConfig(fileConfig) {
|
|
|
1068
1118
|
env.prResolver = overrides;
|
|
1069
1119
|
}
|
|
1070
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
|
+
}
|
|
1071
1132
|
const jobProvidersEnv = {};
|
|
1072
1133
|
for (const jobType of VALID_JOB_TYPES) {
|
|
1073
1134
|
const val = process.env[`NW_JOB_PROVIDER_${jobType.toUpperCase()}`];
|
|
@@ -1163,6 +1224,7 @@ function getDefaultConfig() {
|
|
|
1163
1224
|
audit: { ...DEFAULT_AUDIT },
|
|
1164
1225
|
analytics: { ...DEFAULT_ANALYTICS },
|
|
1165
1226
|
prResolver: { ...DEFAULT_PR_RESOLVER },
|
|
1227
|
+
merger: { ...DEFAULT_MERGER },
|
|
1166
1228
|
jobProviders: { ...DEFAULT_JOB_PROVIDERS },
|
|
1167
1229
|
providerScheduleOverrides: [...DEFAULT_PROVIDER_SCHEDULE_OVERRIDES],
|
|
1168
1230
|
queue: { ...DEFAULT_QUEUE }
|
|
@@ -1230,7 +1292,7 @@ function mergeConfigLayer(base, layer) {
|
|
|
1230
1292
|
...layerQueue,
|
|
1231
1293
|
providerBuckets: { ...baseQueue.providerBuckets, ...layerQueue.providerBuckets }
|
|
1232
1294
|
};
|
|
1233
|
-
} else if (_key === "providerEnv" || _key === "boardProvider" || _key === "qa" || _key === "audit" || _key === "analytics" || _key === "prResolver") {
|
|
1295
|
+
} else if (_key === "providerEnv" || _key === "boardProvider" || _key === "qa" || _key === "audit" || _key === "analytics" || _key === "prResolver" || _key === "merger") {
|
|
1234
1296
|
base[_key] = {
|
|
1235
1297
|
...base[_key],
|
|
1236
1298
|
...value
|
|
@@ -1253,6 +1315,13 @@ function mergeConfigs(base, fileConfig, envConfig) {
|
|
|
1253
1315
|
if (fileConfig)
|
|
1254
1316
|
mergeConfigLayer(merged, fileConfig);
|
|
1255
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
|
+
}
|
|
1256
1325
|
merged.maxRetries = sanitizeMaxRetries(merged.maxRetries, DEFAULT_MAX_RETRIES);
|
|
1257
1326
|
merged.reviewerMaxRetries = sanitizeReviewerMaxRetries(merged.reviewerMaxRetries, DEFAULT_REVIEWER_MAX_RETRIES);
|
|
1258
1327
|
merged.reviewerRetryDelay = sanitizeReviewerRetryDelay(merged.reviewerRetryDelay, DEFAULT_REVIEWER_RETRY_DELAY);
|
|
@@ -3590,6 +3659,9 @@ function analyticsLockPath(projectDir) {
|
|
|
3590
3659
|
function prResolverLockPath(projectDir) {
|
|
3591
3660
|
return `${LOCK_FILE_PREFIX}pr-resolver-${projectRuntimeKey(projectDir)}.lock`;
|
|
3592
3661
|
}
|
|
3662
|
+
function mergerLockPath(projectDir) {
|
|
3663
|
+
return `${LOCK_FILE_PREFIX}merger-${projectRuntimeKey(projectDir)}.lock`;
|
|
3664
|
+
}
|
|
3593
3665
|
function isProcessRunning(pid) {
|
|
3594
3666
|
try {
|
|
3595
3667
|
process.kill(pid, 0);
|
|
@@ -4900,6 +4972,10 @@ function getEventEmoji(event) {
|
|
|
4900
4972
|
return "\u2705";
|
|
4901
4973
|
case "pr_resolver_failed":
|
|
4902
4974
|
return "\u274C";
|
|
4975
|
+
case "merge_completed":
|
|
4976
|
+
return "\u{1F500}";
|
|
4977
|
+
case "merge_failed":
|
|
4978
|
+
return "\u274C";
|
|
4903
4979
|
}
|
|
4904
4980
|
}
|
|
4905
4981
|
function getEventTitle(event) {
|
|
@@ -4930,6 +5006,10 @@ function getEventTitle(event) {
|
|
|
4930
5006
|
return "PR Conflict Resolved";
|
|
4931
5007
|
case "pr_resolver_failed":
|
|
4932
5008
|
return "PR Resolver Failed";
|
|
5009
|
+
case "merge_completed":
|
|
5010
|
+
return "PR Merged";
|
|
5011
|
+
case "merge_failed":
|
|
5012
|
+
return "Merge Failed";
|
|
4933
5013
|
}
|
|
4934
5014
|
}
|
|
4935
5015
|
function getEventColor(event) {
|
|
@@ -4960,6 +5040,10 @@ function getEventColor(event) {
|
|
|
4960
5040
|
return 65280;
|
|
4961
5041
|
case "pr_resolver_failed":
|
|
4962
5042
|
return 16711680;
|
|
5043
|
+
case "merge_completed":
|
|
5044
|
+
return 10181046;
|
|
5045
|
+
case "merge_failed":
|
|
5046
|
+
return 16711680;
|
|
4963
5047
|
}
|
|
4964
5048
|
}
|
|
4965
5049
|
function buildDescription(ctx) {
|
|
@@ -5700,9 +5784,9 @@ var init_slicer_prompt = __esm({
|
|
|
5700
5784
|
"use strict";
|
|
5701
5785
|
__filename = fileURLToPath2(import.meta.url);
|
|
5702
5786
|
__dirname = path14.dirname(__filename);
|
|
5703
|
-
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.
|
|
5704
5788
|
|
|
5705
|
-
When this activates: \`
|
|
5789
|
+
When this activates: \`Planning Mode: Principal Architect\`
|
|
5706
5790
|
|
|
5707
5791
|
---
|
|
5708
5792
|
|
|
@@ -5723,22 +5807,16 @@ The PRD directory is: \`{{PRD_DIR}}\`
|
|
|
5723
5807
|
|
|
5724
5808
|
## Your Task
|
|
5725
5809
|
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
2. **Assess Complexity** - Score the complexity using the rubric and determine whether this is LOW, MEDIUM, or HIGH complexity.
|
|
5731
|
-
|
|
5732
|
-
3. **Write a Complete PRD** - Create a full PRD following the prd-creator template structure with Context, Solution, Phases, Tests, and Acceptance Criteria.
|
|
5733
|
-
|
|
5734
|
-
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}}\`.
|
|
5735
5814
|
|
|
5736
5815
|
---
|
|
5737
5816
|
|
|
5738
5817
|
## Complexity Scoring
|
|
5739
5818
|
|
|
5740
5819
|
\`\`\`
|
|
5741
|
-
COMPLEXITY SCORE (sum all that apply):
|
|
5742
5820
|
+1 Touches 1-5 files
|
|
5743
5821
|
+2 Touches 6-10 files
|
|
5744
5822
|
+3 Touches 10+ files
|
|
@@ -5750,7 +5828,7 @@ COMPLEXITY SCORE (sum all that apply):
|
|
|
5750
5828
|
|
|
5751
5829
|
| Score | Level | Template Mode |
|
|
5752
5830
|
| ----- | ------ | ----------------------------------------------- |
|
|
5753
|
-
| 1-3 | LOW | Minimal (skip sections marked
|
|
5831
|
+
| 1-3 | LOW | Minimal (skip sections marked MEDIUM/HIGH) |
|
|
5754
5832
|
| 4-6 | MEDIUM | Standard (all sections) |
|
|
5755
5833
|
| 7+ | HIGH | Full + mandatory checkpoints every phase |
|
|
5756
5834
|
\`\`\`
|
|
@@ -5759,25 +5837,77 @@ COMPLEXITY SCORE (sum all that apply):
|
|
|
5759
5837
|
|
|
5760
5838
|
## PRD Template Structure
|
|
5761
5839
|
|
|
5762
|
-
Your PRD MUST
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
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)
|
|
5768
5897
|
|
|
5769
5898
|
---
|
|
5770
5899
|
|
|
5771
5900
|
## Critical Instructions
|
|
5772
5901
|
|
|
5773
|
-
1.
|
|
5774
|
-
2.
|
|
5775
|
-
3.
|
|
5776
|
-
4.
|
|
5777
|
-
5.
|
|
5778
|
-
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
|
|
5779
5909
|
|
|
5780
|
-
DO NOT leave placeholder text
|
|
5910
|
+
DO NOT leave [bracketed placeholder] text in the output.
|
|
5781
5911
|
DO NOT skip any sections.
|
|
5782
5912
|
DO NOT forget to write the file.
|
|
5783
5913
|
`;
|
|
@@ -6647,6 +6777,8 @@ function getLockPathForJob(projectPath, jobType) {
|
|
|
6647
6777
|
return analyticsLockPath(projectPath);
|
|
6648
6778
|
case "pr-resolver":
|
|
6649
6779
|
return prResolverLockPath(projectPath);
|
|
6780
|
+
case "merger":
|
|
6781
|
+
return mergerLockPath(projectPath);
|
|
6650
6782
|
}
|
|
6651
6783
|
}
|
|
6652
6784
|
function reconcileStaleRunningJobs(db) {
|
|
@@ -7714,6 +7846,14 @@ __export(dist_exports, {
|
|
|
7714
7846
|
DEFAULT_MAX_LOG_SIZE: () => DEFAULT_MAX_LOG_SIZE,
|
|
7715
7847
|
DEFAULT_MAX_RETRIES: () => DEFAULT_MAX_RETRIES,
|
|
7716
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,
|
|
7717
7857
|
DEFAULT_MIN_REVIEW_SCORE: () => DEFAULT_MIN_REVIEW_SCORE,
|
|
7718
7858
|
DEFAULT_NOTIFICATIONS: () => DEFAULT_NOTIFICATIONS,
|
|
7719
7859
|
DEFAULT_PRD_DIR: () => DEFAULT_PRD_DIR,
|
|
@@ -7772,6 +7912,7 @@ __export(dist_exports, {
|
|
|
7772
7912
|
LocalKanbanProvider: () => LocalKanbanProvider,
|
|
7773
7913
|
Logger: () => Logger,
|
|
7774
7914
|
MAX_HISTORY_RECORDS_PER_PRD: () => MAX_HISTORY_RECORDS_PER_PRD,
|
|
7915
|
+
MERGER_LOG_NAME: () => MERGER_LOG_NAME,
|
|
7775
7916
|
NIGHT_WATCH_LABELS: () => NIGHT_WATCH_LABELS,
|
|
7776
7917
|
PLANNER_LOG_NAME: () => PLANNER_LOG_NAME,
|
|
7777
7918
|
PRD_STATES_FILE_NAME: () => PRD_STATES_FILE_NAME,
|
|
@@ -7936,6 +8077,7 @@ __export(dist_exports, {
|
|
|
7936
8077
|
markItemProcessed: () => markItemProcessed,
|
|
7937
8078
|
markJobRunning: () => markJobRunning,
|
|
7938
8079
|
markPrdDone: () => markPrdDone,
|
|
8080
|
+
mergerLockPath: () => mergerLockPath,
|
|
7939
8081
|
migrateJsonToSqlite: () => migrateJsonToSqlite,
|
|
7940
8082
|
normalizeJobConfig: () => normalizeJobConfig,
|
|
7941
8083
|
normalizeSchedulingPriority: () => normalizeSchedulingPriority,
|
|
@@ -8319,6 +8461,7 @@ function buildInitConfig(params) {
|
|
|
8319
8461
|
},
|
|
8320
8462
|
audit: { ...defaults.audit },
|
|
8321
8463
|
analytics: { ...defaults.analytics },
|
|
8464
|
+
merger: { ...defaults.merger },
|
|
8322
8465
|
prResolver: { ...defaults.prResolver },
|
|
8323
8466
|
jobProviders: { ...defaults.jobProviders },
|
|
8324
8467
|
queue: {
|
|
@@ -9399,10 +9542,6 @@ function buildEnvVars2(config, options) {
|
|
|
9399
9542
|
env.NW_BRANCH_PATTERNS = config.branchPatterns.join(",");
|
|
9400
9543
|
env.NW_PRD_DIR = config.prdDir;
|
|
9401
9544
|
env.NW_CLAUDE_MODEL_ID = CLAUDE_MODEL_IDS[config.primaryFallbackModel ?? config.claudeModel ?? "sonnet"];
|
|
9402
|
-
if (config.autoMerge) {
|
|
9403
|
-
env.NW_AUTO_MERGE = "1";
|
|
9404
|
-
}
|
|
9405
|
-
env.NW_AUTO_MERGE_METHOD = config.autoMergeMethod;
|
|
9406
9545
|
return env;
|
|
9407
9546
|
}
|
|
9408
9547
|
function applyCliOverrides2(config, options) {
|
|
@@ -9416,9 +9555,6 @@ function applyCliOverrides2(config, options) {
|
|
|
9416
9555
|
if (options.provider) {
|
|
9417
9556
|
overridden._cliProviderOverride = options.provider;
|
|
9418
9557
|
}
|
|
9419
|
-
if (options.autoMerge !== void 0) {
|
|
9420
|
-
overridden.autoMerge = options.autoMerge;
|
|
9421
|
-
}
|
|
9422
9558
|
return overridden;
|
|
9423
9559
|
}
|
|
9424
9560
|
function isFailingCheck(check) {
|
|
@@ -9479,7 +9615,7 @@ function getOpenPrsNeedingWork(branchPatterns) {
|
|
|
9479
9615
|
}
|
|
9480
9616
|
}
|
|
9481
9617
|
function reviewCommand(program2) {
|
|
9482
|
-
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) => {
|
|
9483
9619
|
const projectDir = process.cwd();
|
|
9484
9620
|
let config = loadConfig(projectDir);
|
|
9485
9621
|
config = applyCliOverrides2(config, options);
|
|
@@ -9502,10 +9638,6 @@ function reviewCommand(program2) {
|
|
|
9502
9638
|
]);
|
|
9503
9639
|
configTable.push(["Min Review Score", `${config.minReviewScore}/100`]);
|
|
9504
9640
|
configTable.push(["Branch Patterns", config.branchPatterns.join(", ")]);
|
|
9505
|
-
configTable.push([
|
|
9506
|
-
"Auto-merge",
|
|
9507
|
-
config.autoMerge ? `Enabled (${config.autoMergeMethod})` : "Disabled"
|
|
9508
|
-
]);
|
|
9509
9641
|
configTable.push(["Max Retry Attempts", String(config.reviewerMaxRetries)]);
|
|
9510
9642
|
configTable.push(["Retry Delay", `${config.reviewerRetryDelay}s`]);
|
|
9511
9643
|
configTable.push([
|
|
@@ -10139,6 +10271,14 @@ function performInstall(projectDir, config, options) {
|
|
|
10139
10271
|
const prResolverEntry = `${prResolverSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} resolve >> ${shellQuote(prResolverLog)} 2>&1 ${marker}`;
|
|
10140
10272
|
entries.push(prResolverEntry);
|
|
10141
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
|
+
}
|
|
10142
10282
|
const existingEntries = new Set(
|
|
10143
10283
|
Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]))
|
|
10144
10284
|
);
|
|
@@ -10157,7 +10297,7 @@ function performInstall(projectDir, config, options) {
|
|
|
10157
10297
|
}
|
|
10158
10298
|
}
|
|
10159
10299
|
function installCommand(program2) {
|
|
10160
|
-
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("-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) => {
|
|
10161
10301
|
try {
|
|
10162
10302
|
const projectDir = process.cwd();
|
|
10163
10303
|
const config = loadConfig(projectDir);
|
|
@@ -10248,6 +10388,15 @@ function installCommand(program2) {
|
|
|
10248
10388
|
const prResolverEntry = `${prResolverSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} resolve >> ${shellQuote(prResolverLog)} 2>&1 ${marker}`;
|
|
10249
10389
|
entries.push(prResolverEntry);
|
|
10250
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
|
+
}
|
|
10251
10400
|
const existingEntrySet = new Set(existingEntries);
|
|
10252
10401
|
const currentCrontab = readCrontab();
|
|
10253
10402
|
const baseCrontab = options.force ? currentCrontab.filter((line) => !existingEntrySet.has(line) && !line.includes(marker)) : currentCrontab;
|
|
@@ -10280,6 +10429,9 @@ function installCommand(program2) {
|
|
|
10280
10429
|
if (installPrResolver && prResolverLog) {
|
|
10281
10430
|
dim(` PR Resolver: ${prResolverLog}`);
|
|
10282
10431
|
}
|
|
10432
|
+
if (installMerger && mergerLog) {
|
|
10433
|
+
dim(` Merger: ${mergerLog}`);
|
|
10434
|
+
}
|
|
10283
10435
|
console.log();
|
|
10284
10436
|
dim("To uninstall, run: night-watch uninstall");
|
|
10285
10437
|
dim("To check status, run: night-watch status");
|
|
@@ -14843,12 +14995,14 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
14843
14995
|
const auditPlan = getSchedulingPlan(projectDir, config, "audit");
|
|
14844
14996
|
const plannerPlan = getSchedulingPlan(projectDir, config, "slicer");
|
|
14845
14997
|
const analyticsPlan = getSchedulingPlan(projectDir, config, "analytics");
|
|
14998
|
+
const mergerPlan = getSchedulingPlan(projectDir, config, "merger");
|
|
14846
14999
|
const executorInstalled = installed && config.executorEnabled !== false && hasScheduledCommand(entries, "run");
|
|
14847
15000
|
const reviewerInstalled = installed && config.reviewerEnabled && hasScheduledCommand(entries, "review");
|
|
14848
15001
|
const qaInstalled = installed && config.qa.enabled && hasScheduledCommand(entries, "qa");
|
|
14849
15002
|
const auditInstalled = installed && config.audit.enabled && hasScheduledCommand(entries, "audit");
|
|
14850
15003
|
const plannerInstalled = installed && config.roadmapScanner.enabled && (hasScheduledCommand(entries, "planner") || hasScheduledCommand(entries, "slice"));
|
|
14851
15004
|
const analyticsInstalled = installed && config.analytics.enabled && hasScheduledCommand(entries, "analytics");
|
|
15005
|
+
const mergerInstalled = installed && (config.merger?.enabled ?? false) && hasScheduledCommand(entries, "merge");
|
|
14852
15006
|
return {
|
|
14853
15007
|
executor: {
|
|
14854
15008
|
schedule: config.cronSchedule,
|
|
@@ -14898,6 +15052,14 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
14898
15052
|
manualDelayMinutes: analyticsPlan.manualDelayMinutes,
|
|
14899
15053
|
balancedDelayMinutes: analyticsPlan.balancedDelayMinutes
|
|
14900
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
|
+
},
|
|
14901
15063
|
paused: !installed,
|
|
14902
15064
|
schedulingPriority: config.schedulingPriority,
|
|
14903
15065
|
entries
|
|
@@ -16007,7 +16169,7 @@ function releasePlannerLock(lockFile) {
|
|
|
16007
16169
|
}
|
|
16008
16170
|
}
|
|
16009
16171
|
function resolvePlannerIssueColumn(config) {
|
|
16010
|
-
return config.roadmapScanner.issueColumn === "
|
|
16172
|
+
return config.roadmapScanner.issueColumn === "Draft" ? "Draft" : "Ready";
|
|
16011
16173
|
}
|
|
16012
16174
|
function buildPlannerIssueBody(projectDir, config, result) {
|
|
16013
16175
|
const relativePrdPath = path38.join(config.prdDir, result.file ?? "").replace(/\\/g, "/");
|
|
@@ -16021,23 +16183,25 @@ function buildPlannerIssueBody(projectDir, config, result) {
|
|
|
16021
16183
|
}
|
|
16022
16184
|
const maxBodyChars = 6e4;
|
|
16023
16185
|
const truncated = prdContent.length > maxBodyChars;
|
|
16024
|
-
const
|
|
16186
|
+
const prdBody = truncated ? `${prdContent.slice(0, maxBodyChars)}
|
|
16025
16187
|
|
|
16026
16188
|
...[truncated]` : prdContent;
|
|
16027
|
-
const
|
|
16028
|
-
|
|
16029
|
-
`- Source
|
|
16030
|
-
sourceItem.description
|
|
16031
|
-
|
|
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
|
+
}
|
|
16032
16196
|
return [
|
|
16033
|
-
|
|
16197
|
+
prdBody,
|
|
16034
16198
|
"",
|
|
16035
|
-
|
|
16036
|
-
|
|
16199
|
+
"<details>",
|
|
16200
|
+
"<summary>Source metadata</summary>",
|
|
16037
16201
|
"",
|
|
16038
|
-
|
|
16202
|
+
...metaLines,
|
|
16039
16203
|
"",
|
|
16040
|
-
|
|
16204
|
+
"</details>"
|
|
16041
16205
|
].join("\n");
|
|
16042
16206
|
}
|
|
16043
16207
|
async function createPlannerIssue(projectDir, config, result) {
|
|
@@ -16052,9 +16216,11 @@ async function createPlannerIssue(projectDir, config, result) {
|
|
|
16052
16216
|
if (!board) {
|
|
16053
16217
|
return { created: false, skippedReason: "board-not-configured" };
|
|
16054
16218
|
}
|
|
16219
|
+
const issueTitle = `PRD: ${result.item.title}`;
|
|
16220
|
+
const normalizeTitle = (t) => t.replace(/^PRD:\s*/i, "").trim().toLowerCase();
|
|
16055
16221
|
const existingIssues = await provider.getAllIssues();
|
|
16056
16222
|
const existing = existingIssues.find(
|
|
16057
|
-
(issue2) => issue2.title
|
|
16223
|
+
(issue2) => normalizeTitle(issue2.title) === normalizeTitle(result.item.title)
|
|
16058
16224
|
);
|
|
16059
16225
|
if (existing) {
|
|
16060
16226
|
return {
|
|
@@ -16065,7 +16231,7 @@ async function createPlannerIssue(projectDir, config, result) {
|
|
|
16065
16231
|
};
|
|
16066
16232
|
}
|
|
16067
16233
|
const issue = await provider.createIssue({
|
|
16068
|
-
title:
|
|
16234
|
+
title: issueTitle,
|
|
16069
16235
|
body: buildPlannerIssueBody(projectDir, config, result),
|
|
16070
16236
|
column: resolvePlannerIssueColumn(config)
|
|
16071
16237
|
});
|
|
@@ -17525,6 +17691,128 @@ ${stderr}`);
|
|
|
17525
17691
|
});
|
|
17526
17692
|
}
|
|
17527
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
|
+
|
|
17528
17816
|
// src/cli.ts
|
|
17529
17817
|
var __filename5 = fileURLToPath6(import.meta.url);
|
|
17530
17818
|
var __dirname5 = dirname12(__filename5);
|
|
@@ -17568,4 +17856,5 @@ queueCommand(program);
|
|
|
17568
17856
|
notifyCommand(program);
|
|
17569
17857
|
summaryCommand(program);
|
|
17570
17858
|
resolveCommand(program);
|
|
17859
|
+
mergeCommand(program);
|
|
17571
17860
|
program.parse();
|