@jonit-dev/night-watch-cli 1.8.14-beta.4 → 1.8.14-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +1342 -397
- package/dist/cli.js.map +1 -1
- package/dist/commands/dashboard/tab-config.js +2 -2
- package/dist/commands/dashboard/tab-config.js.map +1 -1
- package/dist/commands/dashboard/tab-schedules.d.ts +1 -1
- package/dist/commands/dashboard/tab-schedules.d.ts.map +1 -1
- package/dist/commands/dashboard/tab-schedules.js +15 -6
- package/dist/commands/dashboard/tab-schedules.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +1 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +4 -0
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +26 -1
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +14 -4
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/manager.d.ts +23 -0
- package/dist/commands/manager.d.ts.map +1 -0
- package/dist/commands/manager.js +220 -0
- package/dist/commands/manager.js.map +1 -0
- package/dist/commands/plan.js +1 -1
- package/dist/commands/plan.js.map +1 -1
- package/dist/commands/prd.d.ts.map +1 -1
- package/dist/commands/prd.js +6 -4
- package/dist/commands/prd.js.map +1 -1
- package/dist/commands/queue.d.ts.map +1 -1
- package/dist/commands/queue.js +5 -1
- package/dist/commands/queue.js.map +1 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +2 -1
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +2 -0
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/scripts/night-watch-audit-cron.sh +3 -3
- package/dist/scripts/night-watch-cron.sh +8 -7
- package/dist/scripts/night-watch-helpers.sh +23 -0
- package/dist/scripts/night-watch-manager-cron.sh +61 -0
- package/dist/scripts/night-watch-merger-cron.sh +18 -12
- package/dist/scripts/night-watch-plan-cron.sh +3 -3
- package/dist/scripts/night-watch-pr-resolver-cron.sh +8 -11
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +31 -21
- package/dist/scripts/night-watch-qa-cron.sh +3 -3
- package/dist/scripts/night-watch-slicer-cron.sh +3 -3
- package/dist/web/assets/index-DatF4suf.css +1 -0
- package/dist/web/assets/index-Q3IYCcdZ.js +447 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -30,7 +30,9 @@ var init_types = __esm({
|
|
|
30
30
|
"pr_resolver_conflict_resolved",
|
|
31
31
|
"pr_resolver_failed",
|
|
32
32
|
"merge_completed",
|
|
33
|
-
"merge_failed"
|
|
33
|
+
"merge_failed",
|
|
34
|
+
"manager_blocked",
|
|
35
|
+
"manager_weekly_summary"
|
|
34
36
|
];
|
|
35
37
|
}
|
|
36
38
|
});
|
|
@@ -144,7 +146,7 @@ function buildJobEnvOverrides(envPrefix, currentBase, extraFields) {
|
|
|
144
146
|
const maxRuntimeVal = process.env[`${envPrefix}_MAX_RUNTIME`];
|
|
145
147
|
if (maxRuntimeVal) {
|
|
146
148
|
const v = parseInt(maxRuntimeVal, 10);
|
|
147
|
-
if (!isNaN(v) && v
|
|
149
|
+
if (!isNaN(v) && v >= 0) {
|
|
148
150
|
result.maxRuntime = v;
|
|
149
151
|
changed = true;
|
|
150
152
|
}
|
|
@@ -169,7 +171,7 @@ function buildJobEnvOverrides(envPrefix, currentBase, extraFields) {
|
|
|
169
171
|
break;
|
|
170
172
|
case "number": {
|
|
171
173
|
const v = parseInt(envVal, 10);
|
|
172
|
-
if (!isNaN(v) && v
|
|
174
|
+
if (!isNaN(v) && v >= 0) {
|
|
173
175
|
result[field.name] = v;
|
|
174
176
|
changed = true;
|
|
175
177
|
}
|
|
@@ -211,7 +213,7 @@ var init_job_registry = __esm({
|
|
|
211
213
|
defaultConfig: {
|
|
212
214
|
enabled: true,
|
|
213
215
|
schedule: "5 */2 * * *",
|
|
214
|
-
maxRuntime:
|
|
216
|
+
maxRuntime: 0
|
|
215
217
|
}
|
|
216
218
|
},
|
|
217
219
|
{
|
|
@@ -226,7 +228,7 @@ var init_job_registry = __esm({
|
|
|
226
228
|
defaultConfig: {
|
|
227
229
|
enabled: true,
|
|
228
230
|
schedule: "25 */3 * * *",
|
|
229
|
-
maxRuntime:
|
|
231
|
+
maxRuntime: 0
|
|
230
232
|
}
|
|
231
233
|
},
|
|
232
234
|
{
|
|
@@ -241,7 +243,7 @@ var init_job_registry = __esm({
|
|
|
241
243
|
extraFields: [
|
|
242
244
|
{ name: "branchPatterns", type: "string[]", defaultValue: [] },
|
|
243
245
|
{ name: "maxPrsPerRun", type: "number", defaultValue: 0 },
|
|
244
|
-
{ name: "perPrTimeout", type: "number", defaultValue:
|
|
246
|
+
{ name: "perPrTimeout", type: "number", defaultValue: 0 },
|
|
245
247
|
{ name: "aiConflictResolution", type: "boolean", defaultValue: true },
|
|
246
248
|
{ name: "aiReviewResolution", type: "boolean", defaultValue: false },
|
|
247
249
|
{ name: "readyLabel", type: "string", defaultValue: "ready-to-merge" }
|
|
@@ -249,10 +251,10 @@ var init_job_registry = __esm({
|
|
|
249
251
|
defaultConfig: {
|
|
250
252
|
enabled: true,
|
|
251
253
|
schedule: "15 6,14,22 * * *",
|
|
252
|
-
maxRuntime:
|
|
254
|
+
maxRuntime: 0,
|
|
253
255
|
branchPatterns: [],
|
|
254
256
|
maxPrsPerRun: 0,
|
|
255
|
-
perPrTimeout:
|
|
257
|
+
perPrTimeout: 0,
|
|
256
258
|
aiConflictResolution: true,
|
|
257
259
|
aiReviewResolution: false,
|
|
258
260
|
readyLabel: "ready-to-merge"
|
|
@@ -270,7 +272,53 @@ var init_job_registry = __esm({
|
|
|
270
272
|
defaultConfig: {
|
|
271
273
|
enabled: true,
|
|
272
274
|
schedule: "35 */6 * * *",
|
|
273
|
-
maxRuntime:
|
|
275
|
+
maxRuntime: 0
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
{
|
|
279
|
+
id: "manager",
|
|
280
|
+
name: "Manager",
|
|
281
|
+
description: "Monitors roadmap progress and creates draft PRDs for project gaps",
|
|
282
|
+
cliCommand: "manager",
|
|
283
|
+
logName: "manager",
|
|
284
|
+
lockSuffix: "-manager.lock",
|
|
285
|
+
queuePriority: 25,
|
|
286
|
+
envPrefix: "NW_MANAGER",
|
|
287
|
+
extraFields: [
|
|
288
|
+
{
|
|
289
|
+
name: "authority",
|
|
290
|
+
type: "enum",
|
|
291
|
+
enumValues: ["draft", "ready", "workflow"],
|
|
292
|
+
defaultValue: "draft"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
name: "outputMode",
|
|
296
|
+
type: "enum",
|
|
297
|
+
enumValues: ["board-draft", "filesystem-prd", "report-only"],
|
|
298
|
+
defaultValue: "board-draft"
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
name: "targetColumn",
|
|
302
|
+
type: "enum",
|
|
303
|
+
enumValues: [...BOARD_COLUMNS],
|
|
304
|
+
defaultValue: "Draft"
|
|
305
|
+
},
|
|
306
|
+
{ name: "memoryPath", type: "string", defaultValue: ".night-watch/manager/memory.md" },
|
|
307
|
+
{ name: "docsDir", type: "string", defaultValue: ".night-watch/manager/docs" },
|
|
308
|
+
{ name: "weeklySummaryEnabled", type: "boolean", defaultValue: true },
|
|
309
|
+
{ name: "weeklySummaryDay", type: "number", defaultValue: 1 }
|
|
310
|
+
],
|
|
311
|
+
defaultConfig: {
|
|
312
|
+
enabled: true,
|
|
313
|
+
schedule: "15 7 * * *",
|
|
314
|
+
maxRuntime: 0,
|
|
315
|
+
authority: "draft",
|
|
316
|
+
outputMode: "board-draft",
|
|
317
|
+
targetColumn: "Draft",
|
|
318
|
+
memoryPath: ".night-watch/manager/memory.md",
|
|
319
|
+
docsDir: ".night-watch/manager/docs",
|
|
320
|
+
weeklySummaryEnabled: true,
|
|
321
|
+
weeklySummaryDay: 1
|
|
274
322
|
}
|
|
275
323
|
},
|
|
276
324
|
{
|
|
@@ -297,7 +345,7 @@ var init_job_registry = __esm({
|
|
|
297
345
|
defaultConfig: {
|
|
298
346
|
enabled: true,
|
|
299
347
|
schedule: "45 2,10,18 * * *",
|
|
300
|
-
maxRuntime:
|
|
348
|
+
maxRuntime: 0,
|
|
301
349
|
branchPatterns: [],
|
|
302
350
|
artifacts: "both",
|
|
303
351
|
skipLabel: "skip-qa",
|
|
@@ -326,7 +374,7 @@ var init_job_registry = __esm({
|
|
|
326
374
|
defaultConfig: {
|
|
327
375
|
enabled: false,
|
|
328
376
|
schedule: "50 3 * * 1",
|
|
329
|
-
maxRuntime:
|
|
377
|
+
maxRuntime: 0,
|
|
330
378
|
createIssues: false,
|
|
331
379
|
targetColumn: "Draft"
|
|
332
380
|
}
|
|
@@ -353,7 +401,7 @@ var init_job_registry = __esm({
|
|
|
353
401
|
defaultConfig: {
|
|
354
402
|
enabled: false,
|
|
355
403
|
schedule: "0 6 * * 1",
|
|
356
|
-
maxRuntime:
|
|
404
|
+
maxRuntime: 0,
|
|
357
405
|
lookbackDays: 7,
|
|
358
406
|
targetColumn: "Draft",
|
|
359
407
|
analysisPrompt: ""
|
|
@@ -394,7 +442,7 @@ var init_job_registry = __esm({
|
|
|
394
442
|
defaultConfig: {
|
|
395
443
|
enabled: false,
|
|
396
444
|
schedule: "55 */4 * * *",
|
|
397
|
-
maxRuntime:
|
|
445
|
+
maxRuntime: 0,
|
|
398
446
|
mergeMethod: "squash",
|
|
399
447
|
minReviewScore: 80,
|
|
400
448
|
branchPatterns: [],
|
|
@@ -423,7 +471,7 @@ function resolveProviderBucketKey(provider, providerEnv) {
|
|
|
423
471
|
return `claude-proxy:${baseUrl}`;
|
|
424
472
|
}
|
|
425
473
|
}
|
|
426
|
-
var DEFAULT_DEFAULT_BRANCH, DEFAULT_PRD_DIR, DEFAULT_SUMMARY_WINDOW_HOURS, DEFAULT_MAX_RUNTIME, DEFAULT_REVIEWER_MAX_RUNTIME, DEFAULT_CRON_SCHEDULE, DEFAULT_REVIEWER_SCHEDULE, DEFAULT_CRON_SCHEDULE_OFFSET, DEFAULT_MAX_RETRIES, DEFAULT_REVIEWER_MAX_RETRIES, DEFAULT_REVIEWER_RETRY_DELAY, DEFAULT_REVIEWER_MAX_PRS_PER_RUN, DEFAULT_FEEDBACK, DEFAULT_BRANCH_PREFIX, DEFAULT_BRANCH_PATTERNS, DEFAULT_MIN_REVIEW_SCORE, DEFAULT_MAX_LOG_SIZE, DEFAULT_PROVIDER, DEFAULT_EXECUTOR_ENABLED, DEFAULT_REVIEWER_ENABLED, DEFAULT_PROVIDER_ENV, DEFAULT_FALLBACK_ON_RATE_LIMIT, DEFAULT_CLAUDE_MODEL, DEFAULT_PRIMARY_FALLBACK_MODEL, DEFAULT_SECONDARY_FALLBACK_MODEL, VALID_CLAUDE_MODELS, CLAUDE_MODEL_IDS, DEFAULT_NOTIFICATIONS, DEFAULT_PRD_PRIORITY, DEFAULT_SLICER_SCHEDULE, DEFAULT_SLICER_MAX_RUNTIME, DEFAULT_ROADMAP_SCANNER, DEFAULT_TEMPLATES_DIR, DEFAULT_BOARD_PROVIDER, DEFAULT_LOCAL_BOARD_INFO, DEFAULT_AUTO_MERGE, DEFAULT_AUTO_MERGE_METHOD, VALID_MERGE_METHODS, DEFAULT_QA_ENABLED, DEFAULT_QA_SCHEDULE, DEFAULT_QA_MAX_RUNTIME, DEFAULT_QA_ARTIFACTS, DEFAULT_QA_SKIP_LABEL, DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT, DEFAULT_QA_VALIDATED_LABEL, DEFAULT_QA, QA_LOG_NAME, DEFAULT_AUDIT_ENABLED, DEFAULT_AUDIT_SCHEDULE, DEFAULT_AUDIT_MAX_RUNTIME, DEFAULT_AUDIT_CREATE_ISSUES, DEFAULT_AUDIT_TARGET_COLUMN, DEFAULT_AUDIT, DEFAULT_ANALYTICS_ENABLED, DEFAULT_ANALYTICS_SCHEDULE, DEFAULT_ANALYTICS_MAX_RUNTIME, DEFAULT_ANALYTICS_LOOKBACK_DAYS, DEFAULT_ANALYTICS_TARGET_COLUMN, DEFAULT_ANALYTICS_PROMPT, DEFAULT_ANALYTICS, DEFAULT_PR_RESOLVER_ENABLED, DEFAULT_PR_RESOLVER_SCHEDULE, DEFAULT_PR_RESOLVER_MAX_RUNTIME, DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN, DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT, DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION, DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION, DEFAULT_PR_RESOLVER_READY_LABEL, DEFAULT_PR_RESOLVER, DEFAULT_MERGER_ENABLED, DEFAULT_MERGER_SCHEDULE, DEFAULT_MERGER_MAX_RUNTIME, DEFAULT_MERGER_MERGE_METHOD, DEFAULT_MERGER_MIN_REVIEW_SCORE, DEFAULT_MERGER_REBASE_BEFORE_MERGE, DEFAULT_MERGER_MAX_PRS_PER_RUN, DEFAULT_MERGER_CI_POLICY, DEFAULT_MERGER_LOCAL_CHECK_COMMAND, DEFAULT_MERGER, MERGER_LOG_NAME, AUDIT_LOG_NAME, PLANNER_LOG_NAME, ANALYTICS_LOG_NAME, PR_RESOLVER_LOG_NAME, VALID_PROVIDERS, VALID_JOB_TYPES, DEFAULT_JOB_PROVIDERS, DEFAULT_PROVIDER_SCHEDULE_OVERRIDES, DEFAULT_WEBHOOK_TRIGGER_SECRET_ENV, DEFAULT_WEBHOOK_TRIGGER_MAX_SKEW_SECONDS, DEFAULT_WEBHOOK_TRIGGERS, BUILT_IN_PRESETS, BUILT_IN_PRESET_IDS, PROVIDER_COMMANDS, CONFIG_FILE_NAME, LOCK_FILE_PREFIX, LOG_DIR, CLAIM_FILE_EXTENSION, EXECUTOR_LOG_NAME, REVIEWER_LOG_NAME, EXECUTOR_LOG_FILE, REVIEWER_LOG_FILE, LOG_FILE_NAMES, GLOBAL_CONFIG_DIR, REGISTRY_FILE_NAME, HISTORY_FILE_NAME, PRD_STATES_FILE_NAME, STATE_DB_FILE_NAME, GLOBAL_NOTIFICATIONS_FILE_NAME, MAX_HISTORY_RECORDS_PER_PRD, DEFAULT_QUEUE_ENABLED, DEFAULT_QUEUE_MODE, DEFAULT_QUEUE_MAX_CONCURRENCY, DEFAULT_QUEUE_MAX_WAIT_TIME, DEFAULT_QUEUE_PRIORITY, DEFAULT_QUEUE, DEFAULT_SCHEDULING_PRIORITY;
|
|
474
|
+
var DEFAULT_DEFAULT_BRANCH, DEFAULT_PRD_DIR, DEFAULT_SUMMARY_WINDOW_HOURS, DEFAULT_MAX_RUNTIME, DEFAULT_REVIEWER_MAX_RUNTIME, DEFAULT_CRON_SCHEDULE, DEFAULT_REVIEWER_SCHEDULE, DEFAULT_CRON_SCHEDULE_OFFSET, DEFAULT_MAX_RETRIES, DEFAULT_REVIEWER_MAX_RETRIES, DEFAULT_REVIEWER_RETRY_DELAY, DEFAULT_REVIEWER_MAX_PRS_PER_RUN, DEFAULT_FEEDBACK, DEFAULT_BRANCH_PREFIX, DEFAULT_BRANCH_PATTERNS, DEFAULT_MIN_REVIEW_SCORE, DEFAULT_MAX_LOG_SIZE, DEFAULT_PROVIDER, DEFAULT_EXECUTOR_ENABLED, DEFAULT_REVIEWER_ENABLED, DEFAULT_PROVIDER_ENV, DEFAULT_FALLBACK_ON_RATE_LIMIT, DEFAULT_CLAUDE_MODEL, DEFAULT_PRIMARY_FALLBACK_MODEL, DEFAULT_SECONDARY_FALLBACK_MODEL, VALID_CLAUDE_MODELS, CLAUDE_MODEL_IDS, DEFAULT_NOTIFICATIONS, DEFAULT_PRD_PRIORITY, DEFAULT_SLICER_SCHEDULE, DEFAULT_SLICER_MAX_RUNTIME, DEFAULT_ROADMAP_SCANNER, DEFAULT_TEMPLATES_DIR, DEFAULT_BOARD_PROVIDER, DEFAULT_LOCAL_BOARD_INFO, DEFAULT_AUTO_MERGE, DEFAULT_AUTO_MERGE_METHOD, VALID_MERGE_METHODS, DEFAULT_QA_ENABLED, DEFAULT_QA_SCHEDULE, DEFAULT_QA_MAX_RUNTIME, DEFAULT_QA_ARTIFACTS, DEFAULT_QA_SKIP_LABEL, DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT, DEFAULT_QA_VALIDATED_LABEL, DEFAULT_QA, QA_LOG_NAME, DEFAULT_AUDIT_ENABLED, DEFAULT_AUDIT_SCHEDULE, DEFAULT_AUDIT_MAX_RUNTIME, DEFAULT_AUDIT_CREATE_ISSUES, DEFAULT_AUDIT_TARGET_COLUMN, DEFAULT_AUDIT, DEFAULT_ANALYTICS_ENABLED, DEFAULT_ANALYTICS_SCHEDULE, DEFAULT_ANALYTICS_MAX_RUNTIME, DEFAULT_ANALYTICS_LOOKBACK_DAYS, DEFAULT_ANALYTICS_TARGET_COLUMN, DEFAULT_ANALYTICS_PROMPT, DEFAULT_ANALYTICS, DEFAULT_MANAGER_ENABLED, DEFAULT_MANAGER_SCHEDULE, DEFAULT_MANAGER_MAX_RUNTIME, DEFAULT_MANAGER_AUTHORITY, DEFAULT_MANAGER_OUTPUT_MODE, DEFAULT_MANAGER_TARGET_COLUMN, DEFAULT_MANAGER_MEMORY_PATH, DEFAULT_MANAGER_DOCS_DIR, DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED, DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY, DEFAULT_MANAGER, DEFAULT_PR_RESOLVER_ENABLED, DEFAULT_PR_RESOLVER_SCHEDULE, DEFAULT_PR_RESOLVER_MAX_RUNTIME, DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN, DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT, DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION, DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION, DEFAULT_PR_RESOLVER_READY_LABEL, DEFAULT_PR_RESOLVER, DEFAULT_MERGER_ENABLED, DEFAULT_MERGER_SCHEDULE, DEFAULT_MERGER_MAX_RUNTIME, DEFAULT_MERGER_MERGE_METHOD, DEFAULT_MERGER_MIN_REVIEW_SCORE, DEFAULT_MERGER_REBASE_BEFORE_MERGE, DEFAULT_MERGER_MAX_PRS_PER_RUN, DEFAULT_MERGER_CI_POLICY, DEFAULT_MERGER_LOCAL_CHECK_COMMAND, DEFAULT_MERGER, MERGER_LOG_NAME, AUDIT_LOG_NAME, PLANNER_LOG_NAME, ANALYTICS_LOG_NAME, PR_RESOLVER_LOG_NAME, MANAGER_LOG_NAME, VALID_PROVIDERS, VALID_JOB_TYPES, DEFAULT_JOB_PROVIDERS, DEFAULT_PROVIDER_SCHEDULE_OVERRIDES, DEFAULT_WEBHOOK_TRIGGER_SECRET_ENV, DEFAULT_WEBHOOK_TRIGGER_MAX_SKEW_SECONDS, DEFAULT_WEBHOOK_TRIGGERS, BUILT_IN_PRESETS, BUILT_IN_PRESET_IDS, PROVIDER_COMMANDS, CONFIG_FILE_NAME, LOCK_FILE_PREFIX, LOG_DIR, CLAIM_FILE_EXTENSION, EXECUTOR_LOG_NAME, REVIEWER_LOG_NAME, EXECUTOR_LOG_FILE, REVIEWER_LOG_FILE, LOG_FILE_NAMES, GLOBAL_CONFIG_DIR, REGISTRY_FILE_NAME, HISTORY_FILE_NAME, PRD_STATES_FILE_NAME, STATE_DB_FILE_NAME, GLOBAL_NOTIFICATIONS_FILE_NAME, MAX_HISTORY_RECORDS_PER_PRD, DEFAULT_QUEUE_ENABLED, DEFAULT_QUEUE_MODE, DEFAULT_QUEUE_MAX_CONCURRENCY, DEFAULT_QUEUE_MAX_WAIT_TIME, DEFAULT_QUEUE_PRIORITY, DEFAULT_QUEUE, DEFAULT_SCHEDULING_PRIORITY;
|
|
427
475
|
var init_constants = __esm({
|
|
428
476
|
"../core/dist/constants.js"() {
|
|
429
477
|
"use strict";
|
|
@@ -431,8 +479,8 @@ var init_constants = __esm({
|
|
|
431
479
|
DEFAULT_DEFAULT_BRANCH = "";
|
|
432
480
|
DEFAULT_PRD_DIR = "docs/prds";
|
|
433
481
|
DEFAULT_SUMMARY_WINDOW_HOURS = 12;
|
|
434
|
-
DEFAULT_MAX_RUNTIME =
|
|
435
|
-
DEFAULT_REVIEWER_MAX_RUNTIME =
|
|
482
|
+
DEFAULT_MAX_RUNTIME = 0;
|
|
483
|
+
DEFAULT_REVIEWER_MAX_RUNTIME = 0;
|
|
436
484
|
DEFAULT_CRON_SCHEDULE = "5 * * * *";
|
|
437
485
|
DEFAULT_REVIEWER_SCHEDULE = "25 */3 * * *";
|
|
438
486
|
DEFAULT_CRON_SCHEDULE_OFFSET = 0;
|
|
@@ -467,7 +515,7 @@ var init_constants = __esm({
|
|
|
467
515
|
DEFAULT_NOTIFICATIONS = { webhooks: [] };
|
|
468
516
|
DEFAULT_PRD_PRIORITY = [];
|
|
469
517
|
DEFAULT_SLICER_SCHEDULE = "35 */6 * * *";
|
|
470
|
-
DEFAULT_SLICER_MAX_RUNTIME =
|
|
518
|
+
DEFAULT_SLICER_MAX_RUNTIME = 0;
|
|
471
519
|
DEFAULT_ROADMAP_SCANNER = {
|
|
472
520
|
enabled: true,
|
|
473
521
|
roadmapPath: "ROADMAP.md",
|
|
@@ -488,7 +536,7 @@ var init_constants = __esm({
|
|
|
488
536
|
VALID_MERGE_METHODS = ["squash", "merge", "rebase"];
|
|
489
537
|
DEFAULT_QA_ENABLED = true;
|
|
490
538
|
DEFAULT_QA_SCHEDULE = "45 2,10,18 * * *";
|
|
491
|
-
DEFAULT_QA_MAX_RUNTIME =
|
|
539
|
+
DEFAULT_QA_MAX_RUNTIME = 0;
|
|
492
540
|
DEFAULT_QA_ARTIFACTS = "both";
|
|
493
541
|
DEFAULT_QA_SKIP_LABEL = "skip-qa";
|
|
494
542
|
DEFAULT_QA_AUTO_INSTALL_PLAYWRIGHT = true;
|
|
@@ -506,7 +554,7 @@ var init_constants = __esm({
|
|
|
506
554
|
QA_LOG_NAME = "night-watch-qa";
|
|
507
555
|
DEFAULT_AUDIT_ENABLED = false;
|
|
508
556
|
DEFAULT_AUDIT_SCHEDULE = "50 3 * * 1";
|
|
509
|
-
DEFAULT_AUDIT_MAX_RUNTIME =
|
|
557
|
+
DEFAULT_AUDIT_MAX_RUNTIME = 0;
|
|
510
558
|
DEFAULT_AUDIT_CREATE_ISSUES = false;
|
|
511
559
|
DEFAULT_AUDIT_TARGET_COLUMN = "Draft";
|
|
512
560
|
DEFAULT_AUDIT = {
|
|
@@ -518,7 +566,7 @@ var init_constants = __esm({
|
|
|
518
566
|
};
|
|
519
567
|
DEFAULT_ANALYTICS_ENABLED = false;
|
|
520
568
|
DEFAULT_ANALYTICS_SCHEDULE = "0 6 * * 1";
|
|
521
|
-
DEFAULT_ANALYTICS_MAX_RUNTIME =
|
|
569
|
+
DEFAULT_ANALYTICS_MAX_RUNTIME = 0;
|
|
522
570
|
DEFAULT_ANALYTICS_LOOKBACK_DAYS = 7;
|
|
523
571
|
DEFAULT_ANALYTICS_TARGET_COLUMN = "Draft";
|
|
524
572
|
DEFAULT_ANALYTICS_PROMPT = `You are an analytics reviewer. Analyze the following Amplitude product analytics data.
|
|
@@ -534,11 +582,33 @@ If no issues are warranted, output an empty array: []`;
|
|
|
534
582
|
targetColumn: DEFAULT_ANALYTICS_TARGET_COLUMN,
|
|
535
583
|
analysisPrompt: DEFAULT_ANALYTICS_PROMPT
|
|
536
584
|
};
|
|
585
|
+
DEFAULT_MANAGER_ENABLED = true;
|
|
586
|
+
DEFAULT_MANAGER_SCHEDULE = "15 7 * * *";
|
|
587
|
+
DEFAULT_MANAGER_MAX_RUNTIME = 0;
|
|
588
|
+
DEFAULT_MANAGER_AUTHORITY = "draft";
|
|
589
|
+
DEFAULT_MANAGER_OUTPUT_MODE = "board-draft";
|
|
590
|
+
DEFAULT_MANAGER_TARGET_COLUMN = "Draft";
|
|
591
|
+
DEFAULT_MANAGER_MEMORY_PATH = ".night-watch/manager/memory.md";
|
|
592
|
+
DEFAULT_MANAGER_DOCS_DIR = ".night-watch/manager/docs";
|
|
593
|
+
DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED = true;
|
|
594
|
+
DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY = 1;
|
|
595
|
+
DEFAULT_MANAGER = {
|
|
596
|
+
enabled: DEFAULT_MANAGER_ENABLED,
|
|
597
|
+
schedule: DEFAULT_MANAGER_SCHEDULE,
|
|
598
|
+
maxRuntime: DEFAULT_MANAGER_MAX_RUNTIME,
|
|
599
|
+
authority: DEFAULT_MANAGER_AUTHORITY,
|
|
600
|
+
outputMode: DEFAULT_MANAGER_OUTPUT_MODE,
|
|
601
|
+
targetColumn: DEFAULT_MANAGER_TARGET_COLUMN,
|
|
602
|
+
memoryPath: DEFAULT_MANAGER_MEMORY_PATH,
|
|
603
|
+
docsDir: DEFAULT_MANAGER_DOCS_DIR,
|
|
604
|
+
weeklySummaryEnabled: DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED,
|
|
605
|
+
weeklySummaryDay: DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY
|
|
606
|
+
};
|
|
537
607
|
DEFAULT_PR_RESOLVER_ENABLED = true;
|
|
538
608
|
DEFAULT_PR_RESOLVER_SCHEDULE = "15 6,14,22 * * *";
|
|
539
|
-
DEFAULT_PR_RESOLVER_MAX_RUNTIME =
|
|
609
|
+
DEFAULT_PR_RESOLVER_MAX_RUNTIME = 0;
|
|
540
610
|
DEFAULT_PR_RESOLVER_MAX_PRS_PER_RUN = 0;
|
|
541
|
-
DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT =
|
|
611
|
+
DEFAULT_PR_RESOLVER_PER_PR_TIMEOUT = 0;
|
|
542
612
|
DEFAULT_PR_RESOLVER_AI_CONFLICT_RESOLUTION = true;
|
|
543
613
|
DEFAULT_PR_RESOLVER_AI_REVIEW_RESOLUTION = false;
|
|
544
614
|
DEFAULT_PR_RESOLVER_READY_LABEL = "ready-to-merge";
|
|
@@ -555,7 +625,7 @@ If no issues are warranted, output an empty array: []`;
|
|
|
555
625
|
};
|
|
556
626
|
DEFAULT_MERGER_ENABLED = false;
|
|
557
627
|
DEFAULT_MERGER_SCHEDULE = "55 */4 * * *";
|
|
558
|
-
DEFAULT_MERGER_MAX_RUNTIME =
|
|
628
|
+
DEFAULT_MERGER_MAX_RUNTIME = 0;
|
|
559
629
|
DEFAULT_MERGER_MERGE_METHOD = "squash";
|
|
560
630
|
DEFAULT_MERGER_MIN_REVIEW_SCORE = 80;
|
|
561
631
|
DEFAULT_MERGER_REBASE_BEFORE_MERGE = true;
|
|
@@ -579,6 +649,7 @@ If no issues are warranted, output an empty array: []`;
|
|
|
579
649
|
PLANNER_LOG_NAME = "slicer";
|
|
580
650
|
ANALYTICS_LOG_NAME = "analytics";
|
|
581
651
|
PR_RESOLVER_LOG_NAME = "pr-resolver";
|
|
652
|
+
MANAGER_LOG_NAME = "manager";
|
|
582
653
|
VALID_PROVIDERS = ["claude", "codex"];
|
|
583
654
|
VALID_JOB_TYPES = getValidJobTypes();
|
|
584
655
|
DEFAULT_JOB_PROVIDERS = {};
|
|
@@ -903,7 +974,7 @@ function normalizeConfig(rawConfig) {
|
|
|
903
974
|
if (mergeMethod && VALID_MERGE_METHODS.includes(mergeMethod)) {
|
|
904
975
|
normalized.autoMergeMethod = mergeMethod;
|
|
905
976
|
}
|
|
906
|
-
for (const jobId of ["qa", "audit", "analytics", "merger"]) {
|
|
977
|
+
for (const jobId of ["qa", "audit", "analytics", "merger", "manager"]) {
|
|
907
978
|
const jobDef = getJobDef(jobId);
|
|
908
979
|
if (!jobDef)
|
|
909
980
|
continue;
|
|
@@ -1203,7 +1274,7 @@ function buildEnvOverrideConfig(fileConfig) {
|
|
|
1203
1274
|
}
|
|
1204
1275
|
if (process.env.NW_SLICER_MAX_RUNTIME) {
|
|
1205
1276
|
const v = parseInt(process.env.NW_SLICER_MAX_RUNTIME, 10);
|
|
1206
|
-
if (!isNaN(v) && v
|
|
1277
|
+
if (!isNaN(v) && v >= 0) {
|
|
1207
1278
|
env.roadmapScanner = { ...roadmapBase(), slicerMaxRuntime: v };
|
|
1208
1279
|
}
|
|
1209
1280
|
}
|
|
@@ -1254,7 +1325,7 @@ function buildEnvOverrideConfig(fileConfig) {
|
|
|
1254
1325
|
env.claudeModel = model;
|
|
1255
1326
|
}
|
|
1256
1327
|
}
|
|
1257
|
-
for (const jobId of ["qa", "audit", "analytics"]) {
|
|
1328
|
+
for (const jobId of ["qa", "audit", "analytics", "manager"]) {
|
|
1258
1329
|
const jobDef = getJobDef(jobId);
|
|
1259
1330
|
if (!jobDef)
|
|
1260
1331
|
continue;
|
|
@@ -1381,6 +1452,7 @@ function getDefaultConfig() {
|
|
|
1381
1452
|
qa: { ...DEFAULT_QA },
|
|
1382
1453
|
audit: { ...DEFAULT_AUDIT },
|
|
1383
1454
|
analytics: { ...DEFAULT_ANALYTICS },
|
|
1455
|
+
manager: { ...DEFAULT_MANAGER },
|
|
1384
1456
|
feedback: { ...DEFAULT_FEEDBACK },
|
|
1385
1457
|
prResolver: { ...DEFAULT_PR_RESOLVER },
|
|
1386
1458
|
merger: { ...DEFAULT_MERGER },
|
|
@@ -1580,7 +1652,7 @@ function mergeConfigLayer(base, layer) {
|
|
|
1580
1652
|
})
|
|
1581
1653
|
}
|
|
1582
1654
|
};
|
|
1583
|
-
} else if (_key === "providerEnv" || _key === "boardProvider" || _key === "qa" || _key === "audit" || _key === "analytics" || _key === "feedback" || _key === "prResolver" || _key === "merger") {
|
|
1655
|
+
} else if (_key === "providerEnv" || _key === "boardProvider" || _key === "qa" || _key === "audit" || _key === "analytics" || _key === "manager" || _key === "feedback" || _key === "prResolver" || _key === "merger") {
|
|
1584
1656
|
base[_key] = {
|
|
1585
1657
|
...base[_key],
|
|
1586
1658
|
...value
|
|
@@ -1621,6 +1693,10 @@ function mergeConfigs(base, fileConfig, envConfig) {
|
|
|
1621
1693
|
maxActiveAugmentations: Math.max(0, Math.min(10, Math.floor(merged.feedback.maxActiveAugmentations))),
|
|
1622
1694
|
successStreakToExpire: Math.max(0, Math.min(20, Math.floor(merged.feedback.successStreakToExpire)))
|
|
1623
1695
|
};
|
|
1696
|
+
merged.manager = {
|
|
1697
|
+
...merged.manager,
|
|
1698
|
+
weeklySummaryDay: Math.max(0, Math.min(6, Math.floor(merged.manager.weeklySummaryDay)))
|
|
1699
|
+
};
|
|
1624
1700
|
if (merged.secondaryFallbackModel === void 0) {
|
|
1625
1701
|
merged.secondaryFallbackModel = merged.primaryFallbackModel === void 0 ? DEFAULT_SECONDARY_FALLBACK_MODEL : merged.primaryFallbackModel;
|
|
1626
1702
|
}
|
|
@@ -4431,7 +4507,8 @@ var init_crontab = __esm({
|
|
|
4431
4507
|
"night-watch-qa-cron.sh",
|
|
4432
4508
|
"night-watch-audit-cron.sh",
|
|
4433
4509
|
"night-watch-slice-cron.sh",
|
|
4434
|
-
"night-watch-slicer-cron.sh"
|
|
4510
|
+
"night-watch-slicer-cron.sh",
|
|
4511
|
+
"night-watch-manager-cron.sh"
|
|
4435
4512
|
];
|
|
4436
4513
|
}
|
|
4437
4514
|
});
|
|
@@ -4484,6 +4561,9 @@ function prResolverLockPath(projectDir) {
|
|
|
4484
4561
|
function mergerLockPath(projectDir) {
|
|
4485
4562
|
return `${LOCK_FILE_PREFIX}merger-${projectRuntimeKey(projectDir)}.lock`;
|
|
4486
4563
|
}
|
|
4564
|
+
function managerLockPath(projectDir) {
|
|
4565
|
+
return `${LOCK_FILE_PREFIX}manager-${projectRuntimeKey(projectDir)}.lock`;
|
|
4566
|
+
}
|
|
4487
4567
|
function isProcessRunning(pid) {
|
|
4488
4568
|
if (pid <= 0)
|
|
4489
4569
|
return false;
|
|
@@ -4565,6 +4645,7 @@ function releaseLock(lockPath) {
|
|
|
4565
4645
|
}
|
|
4566
4646
|
}
|
|
4567
4647
|
function countPRDs(projectDir, prdDir, maxRuntime) {
|
|
4648
|
+
const claimStaleAfter = maxRuntime > 0 ? maxRuntime : 14400;
|
|
4568
4649
|
const fullPrdPath = path6.join(projectDir, prdDir);
|
|
4569
4650
|
if (!fs6.existsSync(fullPrdPath)) {
|
|
4570
4651
|
return { pending: 0, claimed: 0, done: 0 };
|
|
@@ -4593,7 +4674,7 @@ function countPRDs(projectDir, prdDir, maxRuntime) {
|
|
|
4593
4674
|
const content = fs6.readFileSync(claimPath, "utf-8");
|
|
4594
4675
|
const claimData = JSON.parse(content);
|
|
4595
4676
|
const age = Math.floor(Date.now() / 1e3) - claimData.timestamp;
|
|
4596
|
-
if (age <
|
|
4677
|
+
if (age < claimStaleAfter) {
|
|
4597
4678
|
claimed++;
|
|
4598
4679
|
} else {
|
|
4599
4680
|
pending++;
|
|
@@ -4625,6 +4706,7 @@ function parsePrdDependencies(prdPath) {
|
|
|
4625
4706
|
}
|
|
4626
4707
|
}
|
|
4627
4708
|
function collectPrdInfo(projectDir, prdDir, maxRuntime) {
|
|
4709
|
+
const claimStaleAfter = maxRuntime > 0 ? maxRuntime : 14400;
|
|
4628
4710
|
const fullPrdPath = path6.join(projectDir, prdDir);
|
|
4629
4711
|
const prds = [];
|
|
4630
4712
|
if (!fs6.existsSync(fullPrdPath)) {
|
|
@@ -4669,7 +4751,7 @@ function collectPrdInfo(projectDir, prdDir, maxRuntime) {
|
|
|
4669
4751
|
const content = fs6.readFileSync(claimPath, "utf-8");
|
|
4670
4752
|
const claimData = JSON.parse(content);
|
|
4671
4753
|
const age = Math.floor(Date.now() / 1e3) - claimData.timestamp;
|
|
4672
|
-
if (age <
|
|
4754
|
+
if (age < claimStaleAfter) {
|
|
4673
4755
|
if (executorLock.running) {
|
|
4674
4756
|
status = "in-progress";
|
|
4675
4757
|
} else {
|
|
@@ -4872,7 +4954,8 @@ function collectLogInfo(projectDir) {
|
|
|
4872
4954
|
{ name: "planner", fileName: `${PLANNER_LOG_NAME}.log` },
|
|
4873
4955
|
{ name: "analytics", fileName: `${ANALYTICS_LOG_NAME}.log` },
|
|
4874
4956
|
{ name: "pr-resolver", fileName: `${PR_RESOLVER_LOG_NAME}.log` },
|
|
4875
|
-
{ name: "merger", fileName: `${MERGER_LOG_NAME}.log` }
|
|
4957
|
+
{ name: "merger", fileName: `${MERGER_LOG_NAME}.log` },
|
|
4958
|
+
{ name: "manager", fileName: `${MANAGER_LOG_NAME}.log` }
|
|
4876
4959
|
];
|
|
4877
4960
|
return logEntries.map(({ name, fileName }) => {
|
|
4878
4961
|
const logPath = path6.join(projectDir, LOG_DIR, fileName);
|
|
@@ -4904,6 +4987,7 @@ async function fetchStatusSnapshot(projectDir, config) {
|
|
|
4904
4987
|
const analyticsLock = checkLockFile(analyticsLockPath(projectDir));
|
|
4905
4988
|
const prResolverLock = checkLockFile(prResolverLockPath(projectDir));
|
|
4906
4989
|
const mergerLock = checkLockFile(mergerLockPath(projectDir));
|
|
4990
|
+
const managerLock = checkLockFile(managerLockPath(projectDir));
|
|
4907
4991
|
const processes = [
|
|
4908
4992
|
{ name: "executor", running: executorLock.running, pid: executorLock.pid },
|
|
4909
4993
|
{ name: "reviewer", running: reviewerLock.running, pid: reviewerLock.pid },
|
|
@@ -4912,7 +4996,8 @@ async function fetchStatusSnapshot(projectDir, config) {
|
|
|
4912
4996
|
{ name: "planner", running: plannerLock.running, pid: plannerLock.pid },
|
|
4913
4997
|
{ name: "analytics", running: analyticsLock.running, pid: analyticsLock.pid },
|
|
4914
4998
|
{ name: "pr-resolver", running: prResolverLock.running, pid: prResolverLock.pid },
|
|
4915
|
-
{ name: "merger", running: mergerLock.running, pid: mergerLock.pid }
|
|
4999
|
+
{ name: "merger", running: mergerLock.running, pid: mergerLock.pid },
|
|
5000
|
+
{ name: "manager", running: managerLock.running, pid: managerLock.pid }
|
|
4916
5001
|
];
|
|
4917
5002
|
const prds = collectPrdInfo(projectDir, config.prdDir, config.maxRuntime);
|
|
4918
5003
|
const prs = await collectPrInfo(projectDir, config.branchPatterns);
|
|
@@ -4953,7 +5038,7 @@ function getLockFilePaths(projectDir) {
|
|
|
4953
5038
|
};
|
|
4954
5039
|
}
|
|
4955
5040
|
function sleep(ms) {
|
|
4956
|
-
return new Promise((
|
|
5041
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
4957
5042
|
}
|
|
4958
5043
|
async function cancelProcess(processType, lockPath, force = false) {
|
|
4959
5044
|
const lockStatus = checkLockFile(lockPath);
|
|
@@ -5949,6 +6034,10 @@ function getEventEmoji(event) {
|
|
|
5949
6034
|
return "\u{1F500}";
|
|
5950
6035
|
case "merge_failed":
|
|
5951
6036
|
return "\u274C";
|
|
6037
|
+
case "manager_blocked":
|
|
6038
|
+
return "\u26A0\uFE0F";
|
|
6039
|
+
case "manager_weekly_summary":
|
|
6040
|
+
return "\u{1F4CA}";
|
|
5952
6041
|
}
|
|
5953
6042
|
}
|
|
5954
6043
|
function getEventTitle(event) {
|
|
@@ -5983,6 +6072,10 @@ function getEventTitle(event) {
|
|
|
5983
6072
|
return "PR Merged";
|
|
5984
6073
|
case "merge_failed":
|
|
5985
6074
|
return "Merge Failed";
|
|
6075
|
+
case "manager_blocked":
|
|
6076
|
+
return "Manager Needs Human Input";
|
|
6077
|
+
case "manager_weekly_summary":
|
|
6078
|
+
return "Manager Weekly Summary";
|
|
5986
6079
|
}
|
|
5987
6080
|
}
|
|
5988
6081
|
function getEventColor(event) {
|
|
@@ -6017,6 +6110,10 @@ function getEventColor(event) {
|
|
|
6017
6110
|
return 10181046;
|
|
6018
6111
|
case "merge_failed":
|
|
6019
6112
|
return 16711680;
|
|
6113
|
+
case "manager_blocked":
|
|
6114
|
+
return 16753920;
|
|
6115
|
+
case "manager_weekly_summary":
|
|
6116
|
+
return 3447003;
|
|
6020
6117
|
}
|
|
6021
6118
|
}
|
|
6022
6119
|
function buildDescription(ctx) {
|
|
@@ -7271,7 +7368,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
|
|
|
7271
7368
|
const logStream = fs18.createWriteStream(logFile, { flags: "w" });
|
|
7272
7369
|
logStream.on("error", () => {
|
|
7273
7370
|
});
|
|
7274
|
-
return new Promise((
|
|
7371
|
+
return new Promise((resolve11) => {
|
|
7275
7372
|
const childEnv = {
|
|
7276
7373
|
...process.env,
|
|
7277
7374
|
...config.providerEnv,
|
|
@@ -7290,7 +7387,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
|
|
|
7290
7387
|
});
|
|
7291
7388
|
child.on("error", (error2) => {
|
|
7292
7389
|
logStream.end();
|
|
7293
|
-
|
|
7390
|
+
resolve11({
|
|
7294
7391
|
sliced: false,
|
|
7295
7392
|
error: `Failed to spawn provider: ${error2.message}`,
|
|
7296
7393
|
item
|
|
@@ -7299,7 +7396,7 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
|
|
|
7299
7396
|
child.on("close", (code) => {
|
|
7300
7397
|
logStream.end();
|
|
7301
7398
|
if (code !== 0) {
|
|
7302
|
-
|
|
7399
|
+
resolve11({
|
|
7303
7400
|
sliced: false,
|
|
7304
7401
|
error: `Provider exited with code ${code ?? 1}`,
|
|
7305
7402
|
item
|
|
@@ -7307,14 +7404,14 @@ async function sliceRoadmapItem(projectDir, prdDir, item, config) {
|
|
|
7307
7404
|
return;
|
|
7308
7405
|
}
|
|
7309
7406
|
if (!fs18.existsSync(filePath)) {
|
|
7310
|
-
|
|
7407
|
+
resolve11({
|
|
7311
7408
|
sliced: false,
|
|
7312
7409
|
error: `Provider did not create expected file: ${filePath}`,
|
|
7313
7410
|
item
|
|
7314
7411
|
});
|
|
7315
7412
|
return;
|
|
7316
7413
|
}
|
|
7317
|
-
|
|
7414
|
+
resolve11({
|
|
7318
7415
|
sliced: true,
|
|
7319
7416
|
file: filename,
|
|
7320
7417
|
item
|
|
@@ -7476,7 +7573,7 @@ async function executeScript(scriptPath, args = [], env = {}, options = {}) {
|
|
|
7476
7573
|
return result.exitCode;
|
|
7477
7574
|
}
|
|
7478
7575
|
async function executeScriptWithOutput(scriptPath, args = [], env = {}, options = {}) {
|
|
7479
|
-
return new Promise((
|
|
7576
|
+
return new Promise((resolve11, reject) => {
|
|
7480
7577
|
const childEnv = {
|
|
7481
7578
|
...process.env,
|
|
7482
7579
|
...env
|
|
@@ -7502,7 +7599,7 @@ async function executeScriptWithOutput(scriptPath, args = [], env = {}, options
|
|
|
7502
7599
|
reject(error2);
|
|
7503
7600
|
});
|
|
7504
7601
|
child.on("close", (code) => {
|
|
7505
|
-
|
|
7602
|
+
resolve11({
|
|
7506
7603
|
exitCode: code ?? 1,
|
|
7507
7604
|
stdout: stdoutChunks.join(""),
|
|
7508
7605
|
stderr: stderrChunks.join("")
|
|
@@ -7539,6 +7636,8 @@ function isJobTypeEnabled(config, jobType) {
|
|
|
7539
7636
|
return config.roadmapScanner.enabled;
|
|
7540
7637
|
case "analytics":
|
|
7541
7638
|
return config.analytics.enabled;
|
|
7639
|
+
case "manager":
|
|
7640
|
+
return config.manager.enabled;
|
|
7542
7641
|
default:
|
|
7543
7642
|
return true;
|
|
7544
7643
|
}
|
|
@@ -7547,6 +7646,8 @@ function getJobSchedule(config, jobType) {
|
|
|
7547
7646
|
switch (jobType) {
|
|
7548
7647
|
case "reviewer":
|
|
7549
7648
|
return config.reviewerSchedule ?? "";
|
|
7649
|
+
case "manager":
|
|
7650
|
+
return config.manager.schedule ?? "";
|
|
7550
7651
|
case "executor":
|
|
7551
7652
|
default:
|
|
7552
7653
|
return config.cronSchedule ?? "";
|
|
@@ -7907,6 +8008,8 @@ function getLockPathForJob(projectPath, jobType) {
|
|
|
7907
8008
|
return prResolverLockPath(projectPath);
|
|
7908
8009
|
case "merger":
|
|
7909
8010
|
return mergerLockPath(projectPath);
|
|
8011
|
+
case "manager":
|
|
8012
|
+
return managerLockPath(projectPath);
|
|
7910
8013
|
}
|
|
7911
8014
|
}
|
|
7912
8015
|
function isRunningEntryStale(entry) {
|
|
@@ -8927,6 +9030,562 @@ var init_audit = __esm({
|
|
|
8927
9030
|
}
|
|
8928
9031
|
});
|
|
8929
9032
|
|
|
9033
|
+
// ../core/dist/manager/manager-memory.js
|
|
9034
|
+
import * as fs23 from "fs";
|
|
9035
|
+
import * as path22 from "path";
|
|
9036
|
+
import { createHash as createHash4 } from "crypto";
|
|
9037
|
+
function createFindingFingerprint(parts) {
|
|
9038
|
+
const normalized = parts.map((part) => part.toLowerCase().trim().replace(/\s+/g, " ")).join("|");
|
|
9039
|
+
return createHash4("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
9040
|
+
}
|
|
9041
|
+
function loadManagerMemory(memoryPath) {
|
|
9042
|
+
if (!fs23.existsSync(memoryPath)) {
|
|
9043
|
+
return { fingerprints: /* @__PURE__ */ new Set(), lastWeeklySummaryAt: null, raw: "" };
|
|
9044
|
+
}
|
|
9045
|
+
const raw = fs23.readFileSync(memoryPath, "utf-8");
|
|
9046
|
+
const fingerprints = /* @__PURE__ */ new Set();
|
|
9047
|
+
let match;
|
|
9048
|
+
while ((match = FINGERPRINT_PATTERN.exec(raw)) !== null) {
|
|
9049
|
+
fingerprints.add(match[1]);
|
|
9050
|
+
}
|
|
9051
|
+
const weeklyMatch = raw.match(WEEKLY_SUMMARY_PATTERN);
|
|
9052
|
+
const lastWeeklySummaryAt = weeklyMatch ? parseDate(weeklyMatch[1].trim()) : null;
|
|
9053
|
+
return { fingerprints, lastWeeklySummaryAt, raw };
|
|
9054
|
+
}
|
|
9055
|
+
function isKnownFinding(memory, fingerprint) {
|
|
9056
|
+
return memory.fingerprints.has(fingerprint);
|
|
9057
|
+
}
|
|
9058
|
+
function renderManagerMemory(result, previous) {
|
|
9059
|
+
const lines = [
|
|
9060
|
+
"# Night Watch Manager Memory",
|
|
9061
|
+
"",
|
|
9062
|
+
`Last run: ${(/* @__PURE__ */ new Date()).toISOString()}`,
|
|
9063
|
+
`Last weekly summary: ${getLastWeeklySummary(result, previous)}`,
|
|
9064
|
+
"",
|
|
9065
|
+
"## Latest Run",
|
|
9066
|
+
"",
|
|
9067
|
+
`- Findings: ${result.findings.length}`,
|
|
9068
|
+
`- Proposed drafts: ${result.proposedDrafts.length}`,
|
|
9069
|
+
`- Created drafts: ${result.createdDrafts.length}`,
|
|
9070
|
+
`- Skipped duplicates: ${result.skippedFindings.length}`,
|
|
9071
|
+
"",
|
|
9072
|
+
"## Findings",
|
|
9073
|
+
""
|
|
9074
|
+
];
|
|
9075
|
+
for (const finding of result.findings) {
|
|
9076
|
+
lines.push(...renderFinding(finding));
|
|
9077
|
+
}
|
|
9078
|
+
lines.push("## Created Drafts", "");
|
|
9079
|
+
for (const draft of result.createdDrafts) {
|
|
9080
|
+
lines.push(`- ${draft.title} (#${draft.issue.number}) - fingerprint: \`${draft.fingerprint}\``);
|
|
9081
|
+
}
|
|
9082
|
+
if (result.createdDrafts.length === 0) {
|
|
9083
|
+
lines.push("- None");
|
|
9084
|
+
}
|
|
9085
|
+
lines.push("", "## Skipped Duplicates", "");
|
|
9086
|
+
for (const skipped of result.skippedFindings) {
|
|
9087
|
+
lines.push(`- ${skipped.title} (${skipped.reason}) - fingerprint: \`${skipped.fingerprint}\``);
|
|
9088
|
+
}
|
|
9089
|
+
if (result.skippedFindings.length === 0) {
|
|
9090
|
+
lines.push("- None");
|
|
9091
|
+
}
|
|
9092
|
+
const previousFingerprints = [...previous.fingerprints].filter((fingerprint) => !result.findings.some((finding) => finding.fingerprint === fingerprint));
|
|
9093
|
+
if (previousFingerprints.length > 0) {
|
|
9094
|
+
lines.push("", "## Previous Fingerprints", "");
|
|
9095
|
+
for (const fingerprint of previousFingerprints) {
|
|
9096
|
+
lines.push(`- fingerprint: \`${fingerprint}\``);
|
|
9097
|
+
}
|
|
9098
|
+
}
|
|
9099
|
+
return `${lines.join("\n")}
|
|
9100
|
+
`;
|
|
9101
|
+
}
|
|
9102
|
+
function writeManagerMemory(memoryPath, result, previous) {
|
|
9103
|
+
fs23.mkdirSync(path22.dirname(memoryPath), { recursive: true });
|
|
9104
|
+
fs23.writeFileSync(memoryPath, renderManagerMemory(result, previous), "utf-8");
|
|
9105
|
+
}
|
|
9106
|
+
function renderFinding(finding) {
|
|
9107
|
+
return [
|
|
9108
|
+
`### ${finding.title}`,
|
|
9109
|
+
"",
|
|
9110
|
+
`- kind: ${finding.kind}`,
|
|
9111
|
+
`- severity: ${finding.severity}`,
|
|
9112
|
+
`- source: ${finding.source}`,
|
|
9113
|
+
`- fingerprint: \`${finding.fingerprint}\``,
|
|
9114
|
+
"",
|
|
9115
|
+
finding.body,
|
|
9116
|
+
""
|
|
9117
|
+
];
|
|
9118
|
+
}
|
|
9119
|
+
function getLastWeeklySummary(result, previous) {
|
|
9120
|
+
const weekly = result.notificationDecisions.find((decision) => decision.event === "manager_weekly_summary" && decision.shouldNotify);
|
|
9121
|
+
if (weekly) {
|
|
9122
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
9123
|
+
}
|
|
9124
|
+
return previous.lastWeeklySummaryAt?.toISOString() ?? "never";
|
|
9125
|
+
}
|
|
9126
|
+
function parseDate(value) {
|
|
9127
|
+
if (value === "never")
|
|
9128
|
+
return null;
|
|
9129
|
+
const date = new Date(value);
|
|
9130
|
+
return Number.isNaN(date.getTime()) ? null : date;
|
|
9131
|
+
}
|
|
9132
|
+
function summarizeCreatedDrafts(drafts) {
|
|
9133
|
+
if (drafts.length === 0)
|
|
9134
|
+
return "No board drafts created.";
|
|
9135
|
+
return `${drafts.length} board draft${drafts.length === 1 ? "" : "s"} created.`;
|
|
9136
|
+
}
|
|
9137
|
+
function summarizeSkippedFindings(skipped) {
|
|
9138
|
+
if (skipped.length === 0)
|
|
9139
|
+
return "No duplicate findings skipped.";
|
|
9140
|
+
return `${skipped.length} duplicate finding${skipped.length === 1 ? "" : "s"} skipped.`;
|
|
9141
|
+
}
|
|
9142
|
+
var FINGERPRINT_PATTERN, WEEKLY_SUMMARY_PATTERN;
|
|
9143
|
+
var init_manager_memory = __esm({
|
|
9144
|
+
"../core/dist/manager/manager-memory.js"() {
|
|
9145
|
+
"use strict";
|
|
9146
|
+
FINGERPRINT_PATTERN = /fingerprint:\s*`([^`]+)`/g;
|
|
9147
|
+
WEEKLY_SUMMARY_PATTERN = /Last weekly summary:\s*([^\n]+)/;
|
|
9148
|
+
}
|
|
9149
|
+
});
|
|
9150
|
+
|
|
9151
|
+
// ../core/dist/manager/manager-analysis.js
|
|
9152
|
+
import * as fs24 from "fs";
|
|
9153
|
+
import * as path23 from "path";
|
|
9154
|
+
function analyzeManagerInputs(context) {
|
|
9155
|
+
const roadmapPath = path23.resolve(context.projectDir, context.config.roadmapScanner?.roadmapPath || "ROADMAP.md");
|
|
9156
|
+
const roadmapContent = fs24.existsSync(roadmapPath) ? fs24.readFileSync(roadmapPath, "utf-8") : "";
|
|
9157
|
+
const roadmapItems = roadmapContent ? parseRoadmap(roadmapContent) : [];
|
|
9158
|
+
const prds = collectPrdFiles(path23.resolve(context.projectDir, context.config.prdDir || "docs/prds"));
|
|
9159
|
+
const findings = [];
|
|
9160
|
+
const searchableWork = buildSearchableWork(context.boardIssues.map((issue) => issue.title), prds);
|
|
9161
|
+
for (const item of roadmapItems.filter((roadmapItem) => !roadmapItem.checked)) {
|
|
9162
|
+
if (searchableWork.has(slugify(item.title))) {
|
|
9163
|
+
continue;
|
|
9164
|
+
}
|
|
9165
|
+
const fingerprint = createFindingFingerprint(["roadmap_gap", item.hash, item.title]);
|
|
9166
|
+
findings.push({
|
|
9167
|
+
kind: "roadmap_gap",
|
|
9168
|
+
severity: "warning",
|
|
9169
|
+
title: `Roadmap item needs an owner: ${item.title}`,
|
|
9170
|
+
body: item.description || `The roadmap item "${item.title}" is still unchecked and does not appear to have a matching board issue or PRD.`,
|
|
9171
|
+
fingerprint,
|
|
9172
|
+
requiresHuman: false,
|
|
9173
|
+
source: `roadmap:${item.section}`,
|
|
9174
|
+
labels: ["manager", "roadmap"]
|
|
9175
|
+
});
|
|
9176
|
+
}
|
|
9177
|
+
for (const prd of context.statusSnapshot?.prds ?? []) {
|
|
9178
|
+
if (prd.status !== "blocked")
|
|
9179
|
+
continue;
|
|
9180
|
+
const fingerprint = createFindingFingerprint(["blocked_prd", prd.name, ...prd.unmetDependencies]);
|
|
9181
|
+
findings.push({
|
|
9182
|
+
kind: "blocked_prd",
|
|
9183
|
+
severity: "blocker",
|
|
9184
|
+
title: `Blocked PRD needs human triage: ${prd.name}`,
|
|
9185
|
+
body: `PRD "${prd.name}" is blocked by unmet dependencies: ${prd.unmetDependencies.join(", ") || "unknown"}.`,
|
|
9186
|
+
fingerprint,
|
|
9187
|
+
requiresHuman: true,
|
|
9188
|
+
source: "status:prds",
|
|
9189
|
+
labels: ["manager", "blocked"]
|
|
9190
|
+
});
|
|
9191
|
+
}
|
|
9192
|
+
const oldestPendingAge = context.queueStatus?.oldestPendingAge;
|
|
9193
|
+
if (oldestPendingAge !== null && oldestPendingAge !== void 0 && oldestPendingAge > 6 * 60 * 60) {
|
|
9194
|
+
const fingerprint = createFindingFingerprint(["stale_queue", String(Math.floor(oldestPendingAge / 3600))]);
|
|
9195
|
+
findings.push({
|
|
9196
|
+
kind: "stale_queue",
|
|
9197
|
+
severity: "blocker",
|
|
9198
|
+
title: "Queue has stale pending work",
|
|
9199
|
+
body: `The oldest pending queue item has waited ${oldestPendingAge} seconds. This may need human capacity or credentials.`,
|
|
9200
|
+
fingerprint,
|
|
9201
|
+
requiresHuman: true,
|
|
9202
|
+
source: "queue",
|
|
9203
|
+
labels: ["manager", "queue", "blocked"]
|
|
9204
|
+
});
|
|
9205
|
+
}
|
|
9206
|
+
if (!fs24.existsSync(path23.join(context.managerConfig.docsDirectory, "overview.md"))) {
|
|
9207
|
+
const fingerprint = createFindingFingerprint(["missing_manager_doc", context.managerConfig.docsDirectory]);
|
|
9208
|
+
findings.push({
|
|
9209
|
+
kind: "missing_manager_doc",
|
|
9210
|
+
severity: "info",
|
|
9211
|
+
title: "Manager overview document is missing",
|
|
9212
|
+
body: "The Manager has not written its generated overview document yet.",
|
|
9213
|
+
fingerprint,
|
|
9214
|
+
requiresHuman: false,
|
|
9215
|
+
source: "manager-docs",
|
|
9216
|
+
labels: ["manager", "docs"]
|
|
9217
|
+
});
|
|
9218
|
+
}
|
|
9219
|
+
return { findings, roadmapItems: roadmapItems.length, prds };
|
|
9220
|
+
}
|
|
9221
|
+
function collectPrdFiles(prdDir) {
|
|
9222
|
+
if (!fs24.existsSync(prdDir))
|
|
9223
|
+
return [];
|
|
9224
|
+
const files = [];
|
|
9225
|
+
const visit = (dir) => {
|
|
9226
|
+
for (const entry of fs24.readdirSync(dir, { withFileTypes: true })) {
|
|
9227
|
+
const fullPath = path23.join(dir, entry.name);
|
|
9228
|
+
if (entry.isDirectory()) {
|
|
9229
|
+
visit(fullPath);
|
|
9230
|
+
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
9231
|
+
files.push({
|
|
9232
|
+
name: entry.name.replace(/\.md$/, ""),
|
|
9233
|
+
path: fullPath,
|
|
9234
|
+
content: fs24.readFileSync(fullPath, "utf-8")
|
|
9235
|
+
});
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
9238
|
+
};
|
|
9239
|
+
visit(prdDir);
|
|
9240
|
+
return files;
|
|
9241
|
+
}
|
|
9242
|
+
function buildSearchableWork(boardTitles, prds) {
|
|
9243
|
+
const values = /* @__PURE__ */ new Set();
|
|
9244
|
+
for (const title of boardTitles) {
|
|
9245
|
+
values.add(slugify(stripManagerPrefix(title)));
|
|
9246
|
+
}
|
|
9247
|
+
for (const prd of prds) {
|
|
9248
|
+
values.add(slugify(prd.name));
|
|
9249
|
+
const heading = prd.content.split("\n").find((line) => line.startsWith("# "))?.slice(2).trim();
|
|
9250
|
+
if (heading)
|
|
9251
|
+
values.add(slugify(heading));
|
|
9252
|
+
}
|
|
9253
|
+
return values;
|
|
9254
|
+
}
|
|
9255
|
+
function stripManagerPrefix(title) {
|
|
9256
|
+
let normalized = title.trim();
|
|
9257
|
+
if (normalized.toLowerCase().startsWith("[manager] ")) {
|
|
9258
|
+
normalized = normalized.slice("[manager] ".length).trim();
|
|
9259
|
+
}
|
|
9260
|
+
const roadmapPrefix = "roadmap item needs an owner: ";
|
|
9261
|
+
if (normalized.toLowerCase().startsWith(roadmapPrefix)) {
|
|
9262
|
+
normalized = normalized.slice(roadmapPrefix.length).trim();
|
|
9263
|
+
}
|
|
9264
|
+
return normalized;
|
|
9265
|
+
}
|
|
9266
|
+
var init_manager_analysis = __esm({
|
|
9267
|
+
"../core/dist/manager/manager-analysis.js"() {
|
|
9268
|
+
"use strict";
|
|
9269
|
+
init_roadmap_parser();
|
|
9270
|
+
init_prd_utils();
|
|
9271
|
+
init_manager_memory();
|
|
9272
|
+
}
|
|
9273
|
+
});
|
|
9274
|
+
|
|
9275
|
+
// ../core/dist/manager/manager-prompts.js
|
|
9276
|
+
function buildManagerDraftTitle(finding) {
|
|
9277
|
+
return `[Manager] ${finding.title}`;
|
|
9278
|
+
}
|
|
9279
|
+
function buildManagerDraftBody(finding) {
|
|
9280
|
+
return [
|
|
9281
|
+
"# PRD: Manager Draft",
|
|
9282
|
+
"",
|
|
9283
|
+
"## 1. Context",
|
|
9284
|
+
"",
|
|
9285
|
+
finding.body,
|
|
9286
|
+
"",
|
|
9287
|
+
`Source: ${finding.source}`,
|
|
9288
|
+
`Manager fingerprint: \`${finding.fingerprint}\``,
|
|
9289
|
+
"",
|
|
9290
|
+
"## 2. Proposed Outcome",
|
|
9291
|
+
"",
|
|
9292
|
+
"Turn this finding into a reviewed, executable PRD or close it with a short rationale.",
|
|
9293
|
+
"",
|
|
9294
|
+
"## 3. Acceptance Criteria",
|
|
9295
|
+
"",
|
|
9296
|
+
"- [ ] Confirm the finding is still relevant.",
|
|
9297
|
+
"- [ ] Define the implementation scope and ownership.",
|
|
9298
|
+
"- [ ] Add or update tests appropriate for the selected implementation.",
|
|
9299
|
+
"- [ ] Close this draft when the work is represented by an approved PRD or issue.",
|
|
9300
|
+
"",
|
|
9301
|
+
"## 4. Manager Notes",
|
|
9302
|
+
"",
|
|
9303
|
+
`- Kind: ${finding.kind}`,
|
|
9304
|
+
`- Severity: ${finding.severity}`,
|
|
9305
|
+
`- Requires human input: ${finding.requiresHuman ? "yes" : "no"}`
|
|
9306
|
+
].join("\n");
|
|
9307
|
+
}
|
|
9308
|
+
var init_manager_prompts = __esm({
|
|
9309
|
+
"../core/dist/manager/manager-prompts.js"() {
|
|
9310
|
+
"use strict";
|
|
9311
|
+
}
|
|
9312
|
+
});
|
|
9313
|
+
|
|
9314
|
+
// ../core/dist/manager/manager-board.js
|
|
9315
|
+
function prepareManagerDrafts(input) {
|
|
9316
|
+
const boardTitles = new Set(input.boardIssues.map((issue) => normalizeTitle(issue.title)));
|
|
9317
|
+
const drafts = [];
|
|
9318
|
+
const skipped = [];
|
|
9319
|
+
for (const finding of input.findings) {
|
|
9320
|
+
const title = buildManagerDraftTitle(finding);
|
|
9321
|
+
if (isKnownFinding(input.memory, finding.fingerprint)) {
|
|
9322
|
+
skipped.push({ fingerprint: finding.fingerprint, title, reason: "memory" });
|
|
9323
|
+
continue;
|
|
9324
|
+
}
|
|
9325
|
+
if (boardTitles.has(normalizeTitle(title)) || boardTitles.has(normalizeTitle(finding.title))) {
|
|
9326
|
+
skipped.push({ fingerprint: finding.fingerprint, title, reason: "board" });
|
|
9327
|
+
continue;
|
|
9328
|
+
}
|
|
9329
|
+
drafts.push({
|
|
9330
|
+
title,
|
|
9331
|
+
body: buildManagerDraftBody(finding),
|
|
9332
|
+
labels: finding.labels,
|
|
9333
|
+
column: input.managerConfig.targetColumn,
|
|
9334
|
+
fingerprint: finding.fingerprint
|
|
9335
|
+
});
|
|
9336
|
+
}
|
|
9337
|
+
return { drafts, skipped };
|
|
9338
|
+
}
|
|
9339
|
+
async function createManagerBoardDrafts(input) {
|
|
9340
|
+
if (input.dryRun || input.outputMode !== "board-draft" || !input.provider) {
|
|
9341
|
+
return [];
|
|
9342
|
+
}
|
|
9343
|
+
const created = [];
|
|
9344
|
+
for (const draft of input.drafts) {
|
|
9345
|
+
const issue = await input.provider.createIssue({
|
|
9346
|
+
title: draft.title,
|
|
9347
|
+
body: draft.body,
|
|
9348
|
+
column: draft.column,
|
|
9349
|
+
labels: draft.labels
|
|
9350
|
+
});
|
|
9351
|
+
created.push({ ...draft, issue });
|
|
9352
|
+
}
|
|
9353
|
+
return created;
|
|
9354
|
+
}
|
|
9355
|
+
function normalizeTitle(title) {
|
|
9356
|
+
return title.toLowerCase().replace(/^\[manager\]\s*/i, "").replace(/\s+/g, " ").trim();
|
|
9357
|
+
}
|
|
9358
|
+
var init_manager_board = __esm({
|
|
9359
|
+
"../core/dist/manager/manager-board.js"() {
|
|
9360
|
+
"use strict";
|
|
9361
|
+
init_manager_prompts();
|
|
9362
|
+
init_manager_memory();
|
|
9363
|
+
}
|
|
9364
|
+
});
|
|
9365
|
+
|
|
9366
|
+
// ../core/dist/manager/manager-notifications.js
|
|
9367
|
+
function prepareManagerNotificationDecisions(input) {
|
|
9368
|
+
const blockers = input.findings.filter((finding) => finding.requiresHuman || finding.severity === "blocker");
|
|
9369
|
+
const decisions = [
|
|
9370
|
+
{
|
|
9371
|
+
event: "manager_blocked",
|
|
9372
|
+
shouldNotify: blockers.length > 0,
|
|
9373
|
+
title: blockers.length === 1 ? "Manager found 1 blocker" : `Manager found ${blockers.length} blockers`,
|
|
9374
|
+
body: blockers.map((finding) => `- ${finding.title}`).join("\n") || "No blockers found.",
|
|
9375
|
+
findings: blockers
|
|
9376
|
+
}
|
|
9377
|
+
];
|
|
9378
|
+
const weeklyDue = isWeeklySummaryDue(input.managerConfig.weeklySummaryEnabled, input.managerConfig.weeklySummaryDay, input.memory.lastWeeklySummaryAt, input.now);
|
|
9379
|
+
decisions.push({
|
|
9380
|
+
event: "manager_weekly_summary",
|
|
9381
|
+
shouldNotify: weeklyDue,
|
|
9382
|
+
title: "Manager weekly summary",
|
|
9383
|
+
body: [
|
|
9384
|
+
`Findings: ${input.findings.length}`,
|
|
9385
|
+
`Blockers: ${blockers.length}`,
|
|
9386
|
+
`Generated at: ${input.now.toISOString()}`
|
|
9387
|
+
].join("\n"),
|
|
9388
|
+
findings: input.findings
|
|
9389
|
+
});
|
|
9390
|
+
return decisions;
|
|
9391
|
+
}
|
|
9392
|
+
function isWeeklySummaryDue(enabled, configuredDay, lastWeeklySummaryAt, now) {
|
|
9393
|
+
if (!enabled || now.getDay() !== configuredDay) {
|
|
9394
|
+
return false;
|
|
9395
|
+
}
|
|
9396
|
+
if (!lastWeeklySummaryAt) {
|
|
9397
|
+
return true;
|
|
9398
|
+
}
|
|
9399
|
+
const elapsedMs = now.getTime() - lastWeeklySummaryAt.getTime();
|
|
9400
|
+
return elapsedMs >= 6.5 * 24 * 60 * 60 * 1e3;
|
|
9401
|
+
}
|
|
9402
|
+
var init_manager_notifications = __esm({
|
|
9403
|
+
"../core/dist/manager/manager-notifications.js"() {
|
|
9404
|
+
"use strict";
|
|
9405
|
+
}
|
|
9406
|
+
});
|
|
9407
|
+
|
|
9408
|
+
// ../core/dist/manager/manager-runner.js
|
|
9409
|
+
import * as fs25 from "fs";
|
|
9410
|
+
import * as path24 from "path";
|
|
9411
|
+
async function runManager(projectDir, config, options = {}) {
|
|
9412
|
+
const dryRun = options.dryRun ?? false;
|
|
9413
|
+
const now = options.now ?? /* @__PURE__ */ new Date();
|
|
9414
|
+
const managerConfig = resolveManagerConfig(config, projectDir);
|
|
9415
|
+
const memory = loadManagerMemory(managerConfig.memoryFile);
|
|
9416
|
+
const boardProvider = await resolveBoardProvider(config, projectDir, options.boardProvider);
|
|
9417
|
+
const boardIssues = await readBoardIssues(boardProvider);
|
|
9418
|
+
const queueStatus = resolveQueueStatus(options.queueStatus);
|
|
9419
|
+
const statusSnapshot = await resolveStatusSnapshot(projectDir, config, options.statusSnapshot);
|
|
9420
|
+
const context = {
|
|
9421
|
+
projectDir,
|
|
9422
|
+
config,
|
|
9423
|
+
managerConfig,
|
|
9424
|
+
dryRun,
|
|
9425
|
+
now,
|
|
9426
|
+
boardIssues,
|
|
9427
|
+
statusSnapshot,
|
|
9428
|
+
queueStatus
|
|
9429
|
+
};
|
|
9430
|
+
const analysis = analyzeManagerInputs(context);
|
|
9431
|
+
const { drafts, skipped } = prepareManagerDrafts({
|
|
9432
|
+
findings: analysis.findings,
|
|
9433
|
+
memory,
|
|
9434
|
+
boardIssues,
|
|
9435
|
+
managerConfig
|
|
9436
|
+
});
|
|
9437
|
+
const createdDrafts = await createManagerBoardDrafts({
|
|
9438
|
+
provider: boardProvider,
|
|
9439
|
+
drafts,
|
|
9440
|
+
dryRun,
|
|
9441
|
+
outputMode: managerConfig.outputMode
|
|
9442
|
+
});
|
|
9443
|
+
const notificationDecisions = prepareManagerNotificationDecisions({
|
|
9444
|
+
findings: analysis.findings,
|
|
9445
|
+
memory,
|
|
9446
|
+
managerConfig,
|
|
9447
|
+
now
|
|
9448
|
+
});
|
|
9449
|
+
const result = {
|
|
9450
|
+
dryRun,
|
|
9451
|
+
projectDir,
|
|
9452
|
+
config: managerConfig,
|
|
9453
|
+
analyzed: {
|
|
9454
|
+
roadmapItems: analysis.roadmapItems,
|
|
9455
|
+
boardIssues: boardIssues.length,
|
|
9456
|
+
prds: analysis.prds.length
|
|
9457
|
+
},
|
|
9458
|
+
findings: analysis.findings,
|
|
9459
|
+
proposedDrafts: drafts,
|
|
9460
|
+
createdDrafts,
|
|
9461
|
+
skippedFindings: skipped,
|
|
9462
|
+
docsWritten: [],
|
|
9463
|
+
memoryWritten: false,
|
|
9464
|
+
notificationDecisions,
|
|
9465
|
+
summary: ""
|
|
9466
|
+
};
|
|
9467
|
+
if (!dryRun) {
|
|
9468
|
+
result.docsWritten = writeManagerDocs(managerConfig.docsDirectory, result, now);
|
|
9469
|
+
writeManagerMemory(managerConfig.memoryFile, result, memory);
|
|
9470
|
+
result.memoryWritten = true;
|
|
9471
|
+
}
|
|
9472
|
+
result.summary = [
|
|
9473
|
+
`${analysis.findings.length} finding${analysis.findings.length === 1 ? "" : "s"} found.`,
|
|
9474
|
+
summarizeCreatedDrafts(createdDrafts),
|
|
9475
|
+
summarizeSkippedFindings(skipped)
|
|
9476
|
+
].join(" ");
|
|
9477
|
+
return result;
|
|
9478
|
+
}
|
|
9479
|
+
function resolveManagerConfig(config, projectDir) {
|
|
9480
|
+
const raw = config.manager ?? {};
|
|
9481
|
+
const merged = {
|
|
9482
|
+
...DEFAULT_MANAGER,
|
|
9483
|
+
...raw,
|
|
9484
|
+
authority: raw.authority === "draft" || raw.authority === "ready" || raw.authority === "workflow" ? raw.authority : DEFAULT_MANAGER.authority,
|
|
9485
|
+
outputMode: raw.outputMode === "board-draft" || raw.outputMode === "filesystem-prd" || raw.outputMode === "report-only" ? raw.outputMode : DEFAULT_MANAGER.outputMode,
|
|
9486
|
+
targetColumn: raw.targetColumn ?? DEFAULT_MANAGER.targetColumn,
|
|
9487
|
+
weeklySummaryDay: typeof raw.weeklySummaryDay === "number" && raw.weeklySummaryDay >= 0 && raw.weeklySummaryDay <= 6 ? raw.weeklySummaryDay : DEFAULT_MANAGER.weeklySummaryDay
|
|
9488
|
+
};
|
|
9489
|
+
const weeklySummaryDay = Math.floor(merged.weeklySummaryDay);
|
|
9490
|
+
return {
|
|
9491
|
+
...merged,
|
|
9492
|
+
weeklySummaryDay,
|
|
9493
|
+
memoryFile: path24.resolve(projectDir, merged.memoryPath),
|
|
9494
|
+
docsDirectory: path24.resolve(projectDir, merged.docsDir)
|
|
9495
|
+
};
|
|
9496
|
+
}
|
|
9497
|
+
async function resolveBoardProvider(config, projectDir, injected) {
|
|
9498
|
+
if (injected !== void 0) {
|
|
9499
|
+
return injected;
|
|
9500
|
+
}
|
|
9501
|
+
if (!config.boardProvider?.enabled) {
|
|
9502
|
+
return null;
|
|
9503
|
+
}
|
|
9504
|
+
return createBoardProvider(config.boardProvider, projectDir);
|
|
9505
|
+
}
|
|
9506
|
+
async function readBoardIssues(provider) {
|
|
9507
|
+
if (!provider)
|
|
9508
|
+
return [];
|
|
9509
|
+
try {
|
|
9510
|
+
return await provider.getAllIssues();
|
|
9511
|
+
} catch {
|
|
9512
|
+
return [];
|
|
9513
|
+
}
|
|
9514
|
+
}
|
|
9515
|
+
function resolveQueueStatus(injected) {
|
|
9516
|
+
if (injected !== void 0) {
|
|
9517
|
+
return injected;
|
|
9518
|
+
}
|
|
9519
|
+
try {
|
|
9520
|
+
return getQueueStatus();
|
|
9521
|
+
} catch {
|
|
9522
|
+
return null;
|
|
9523
|
+
}
|
|
9524
|
+
}
|
|
9525
|
+
async function resolveStatusSnapshot(projectDir, config, injected) {
|
|
9526
|
+
if (injected !== void 0) {
|
|
9527
|
+
return injected;
|
|
9528
|
+
}
|
|
9529
|
+
try {
|
|
9530
|
+
return await fetchStatusSnapshot(projectDir, config);
|
|
9531
|
+
} catch {
|
|
9532
|
+
return null;
|
|
9533
|
+
}
|
|
9534
|
+
}
|
|
9535
|
+
function writeManagerDocs(docsDir, result, now) {
|
|
9536
|
+
const overviewPath = path24.resolve(docsDir, "overview.md");
|
|
9537
|
+
const relative3 = path24.relative(docsDir, overviewPath);
|
|
9538
|
+
if (relative3.startsWith("..") || path24.isAbsolute(relative3)) {
|
|
9539
|
+
throw new Error(`Refusing to write Manager docs outside docsDir: ${overviewPath}`);
|
|
9540
|
+
}
|
|
9541
|
+
fs25.mkdirSync(docsDir, { recursive: true });
|
|
9542
|
+
fs25.writeFileSync(overviewPath, [
|
|
9543
|
+
"# Manager Overview",
|
|
9544
|
+
"",
|
|
9545
|
+
`Generated: ${now.toISOString()}`,
|
|
9546
|
+
"",
|
|
9547
|
+
`Findings: ${result.findings.length}`,
|
|
9548
|
+
`Proposed drafts: ${result.proposedDrafts.length}`,
|
|
9549
|
+
`Created drafts: ${result.createdDrafts.length}`,
|
|
9550
|
+
""
|
|
9551
|
+
].join("\n"), "utf-8");
|
|
9552
|
+
return [overviewPath];
|
|
9553
|
+
}
|
|
9554
|
+
var init_manager_runner = __esm({
|
|
9555
|
+
"../core/dist/manager/manager-runner.js"() {
|
|
9556
|
+
"use strict";
|
|
9557
|
+
init_factory();
|
|
9558
|
+
init_constants();
|
|
9559
|
+
init_job_queue();
|
|
9560
|
+
init_status_data();
|
|
9561
|
+
init_manager_analysis();
|
|
9562
|
+
init_manager_board();
|
|
9563
|
+
init_manager_memory();
|
|
9564
|
+
init_manager_notifications();
|
|
9565
|
+
}
|
|
9566
|
+
});
|
|
9567
|
+
|
|
9568
|
+
// ../core/dist/manager/manager-types.js
|
|
9569
|
+
var init_manager_types = __esm({
|
|
9570
|
+
"../core/dist/manager/manager-types.js"() {
|
|
9571
|
+
"use strict";
|
|
9572
|
+
}
|
|
9573
|
+
});
|
|
9574
|
+
|
|
9575
|
+
// ../core/dist/manager/index.js
|
|
9576
|
+
var init_manager = __esm({
|
|
9577
|
+
"../core/dist/manager/index.js"() {
|
|
9578
|
+
"use strict";
|
|
9579
|
+
init_manager_analysis();
|
|
9580
|
+
init_manager_board();
|
|
9581
|
+
init_manager_memory();
|
|
9582
|
+
init_manager_notifications();
|
|
9583
|
+
init_manager_prompts();
|
|
9584
|
+
init_manager_runner();
|
|
9585
|
+
init_manager_types();
|
|
9586
|
+
}
|
|
9587
|
+
});
|
|
9588
|
+
|
|
8930
9589
|
// ../core/dist/feedback/outcome-parser.js
|
|
8931
9590
|
function stripAnsi2(value) {
|
|
8932
9591
|
return value.replace(ANSI_PATTERN, "");
|
|
@@ -9789,6 +10448,17 @@ __export(dist_exports, {
|
|
|
9789
10448
|
DEFAULT_FEEDBACK: () => DEFAULT_FEEDBACK,
|
|
9790
10449
|
DEFAULT_JOB_PROVIDERS: () => DEFAULT_JOB_PROVIDERS,
|
|
9791
10450
|
DEFAULT_LOCAL_BOARD_INFO: () => DEFAULT_LOCAL_BOARD_INFO,
|
|
10451
|
+
DEFAULT_MANAGER: () => DEFAULT_MANAGER,
|
|
10452
|
+
DEFAULT_MANAGER_AUTHORITY: () => DEFAULT_MANAGER_AUTHORITY,
|
|
10453
|
+
DEFAULT_MANAGER_DOCS_DIR: () => DEFAULT_MANAGER_DOCS_DIR,
|
|
10454
|
+
DEFAULT_MANAGER_ENABLED: () => DEFAULT_MANAGER_ENABLED,
|
|
10455
|
+
DEFAULT_MANAGER_MAX_RUNTIME: () => DEFAULT_MANAGER_MAX_RUNTIME,
|
|
10456
|
+
DEFAULT_MANAGER_MEMORY_PATH: () => DEFAULT_MANAGER_MEMORY_PATH,
|
|
10457
|
+
DEFAULT_MANAGER_OUTPUT_MODE: () => DEFAULT_MANAGER_OUTPUT_MODE,
|
|
10458
|
+
DEFAULT_MANAGER_SCHEDULE: () => DEFAULT_MANAGER_SCHEDULE,
|
|
10459
|
+
DEFAULT_MANAGER_TARGET_COLUMN: () => DEFAULT_MANAGER_TARGET_COLUMN,
|
|
10460
|
+
DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY: () => DEFAULT_MANAGER_WEEKLY_SUMMARY_DAY,
|
|
10461
|
+
DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED: () => DEFAULT_MANAGER_WEEKLY_SUMMARY_ENABLED,
|
|
9792
10462
|
DEFAULT_MAX_LOG_SIZE: () => DEFAULT_MAX_LOG_SIZE,
|
|
9793
10463
|
DEFAULT_MAX_RETRIES: () => DEFAULT_MAX_RETRIES,
|
|
9794
10464
|
DEFAULT_MAX_RUNTIME: () => DEFAULT_MAX_RUNTIME,
|
|
@@ -9866,6 +10536,7 @@ __export(dist_exports, {
|
|
|
9866
10536
|
LOG_FILE_NAMES: () => LOG_FILE_NAMES,
|
|
9867
10537
|
LocalKanbanProvider: () => LocalKanbanProvider,
|
|
9868
10538
|
Logger: () => Logger,
|
|
10539
|
+
MANAGER_LOG_NAME: () => MANAGER_LOG_NAME,
|
|
9869
10540
|
MAX_HISTORY_RECORDS_PER_PRD: () => MAX_HISTORY_RECORDS_PER_PRD,
|
|
9870
10541
|
MERGER_LOG_NAME: () => MERGER_LOG_NAME,
|
|
9871
10542
|
NIGHT_WATCH_LABELS: () => NIGHT_WATCH_LABELS,
|
|
@@ -9894,9 +10565,12 @@ __export(dist_exports, {
|
|
|
9894
10565
|
addEntry: () => addEntry,
|
|
9895
10566
|
analyticsLockPath: () => analyticsLockPath,
|
|
9896
10567
|
analyzeFeedbackOutcome: () => analyzeFeedbackOutcome,
|
|
10568
|
+
analyzeManagerInputs: () => analyzeManagerInputs,
|
|
9897
10569
|
auditLockPath: () => auditLockPath,
|
|
9898
10570
|
buildDescription: () => buildDescription,
|
|
9899
10571
|
buildJobEnvOverrides: () => buildJobEnvOverrides,
|
|
10572
|
+
buildManagerDraftBody: () => buildManagerDraftBody,
|
|
10573
|
+
buildManagerDraftTitle: () => buildManagerDraftTitle,
|
|
9900
10574
|
buildProjectFeedbackPromptBlock: () => buildProjectFeedbackPromptBlock,
|
|
9901
10575
|
buildSessionOutcomeInput: () => buildSessionOutcomeInput,
|
|
9902
10576
|
calculateStringSimilarity: () => calculateStringSimilarity,
|
|
@@ -9931,7 +10605,9 @@ __export(dist_exports, {
|
|
|
9931
10605
|
createBoardProvider: () => createBoardProvider,
|
|
9932
10606
|
createDbForDir: () => createDbForDir,
|
|
9933
10607
|
createEmptyState: () => createEmptyState,
|
|
10608
|
+
createFindingFingerprint: () => createFindingFingerprint,
|
|
9934
10609
|
createLogger: () => createLogger,
|
|
10610
|
+
createManagerBoardDrafts: () => createManagerBoardDrafts,
|
|
9935
10611
|
createSlicerPromptVars: () => createSlicerPromptVars,
|
|
9936
10612
|
createSpinner: () => createSpinner,
|
|
9937
10613
|
createTable: () => createTable,
|
|
@@ -10025,20 +10701,24 @@ __export(dist_exports, {
|
|
|
10025
10701
|
isInCooldown: () => isInCooldown,
|
|
10026
10702
|
isItemProcessed: () => isItemProcessed,
|
|
10027
10703
|
isJobTypeEnabled: () => isJobTypeEnabled,
|
|
10704
|
+
isKnownFinding: () => isKnownFinding,
|
|
10028
10705
|
isProcessAliveSince: () => isProcessAliveSince,
|
|
10029
10706
|
isProcessRunning: () => isProcessRunning,
|
|
10030
10707
|
isValidCategory: () => isValidCategory,
|
|
10031
10708
|
isValidHorizon: () => isValidHorizon,
|
|
10032
10709
|
isValidPriority: () => isValidPriority,
|
|
10710
|
+
isWeeklySummaryDue: () => isWeeklySummaryDue,
|
|
10033
10711
|
label: () => label,
|
|
10034
10712
|
listPrdStatesByStatus: () => listPrdStatesByStatus,
|
|
10035
10713
|
loadAuditFindings: () => loadAuditFindings,
|
|
10036
10714
|
loadConfig: () => loadConfig,
|
|
10037
10715
|
loadGlobalNotificationsConfig: () => loadGlobalNotificationsConfig,
|
|
10038
10716
|
loadHistory: () => loadHistory,
|
|
10717
|
+
loadManagerMemory: () => loadManagerMemory,
|
|
10039
10718
|
loadRegistry: () => loadRegistry,
|
|
10040
10719
|
loadRoadmapState: () => loadRoadmapState,
|
|
10041
10720
|
loadSlicerTemplate: () => loadSlicerTemplate,
|
|
10721
|
+
managerLockPath: () => managerLockPath,
|
|
10042
10722
|
markItemProcessed: () => markItemProcessed,
|
|
10043
10723
|
markJobRunning: () => markJobRunning,
|
|
10044
10724
|
markPrdDone: () => markPrdDone,
|
|
@@ -10058,6 +10738,8 @@ __export(dist_exports, {
|
|
|
10058
10738
|
prResolverLockPath: () => prResolverLockPath,
|
|
10059
10739
|
prepareBranchWorktree: () => prepareBranchWorktree,
|
|
10060
10740
|
prepareDetachedWorktree: () => prepareDetachedWorktree,
|
|
10741
|
+
prepareManagerDrafts: () => prepareManagerDrafts,
|
|
10742
|
+
prepareManagerNotificationDecisions: () => prepareManagerNotificationDecisions,
|
|
10061
10743
|
projectRuntimeKey: () => projectRuntimeKey,
|
|
10062
10744
|
pruneProjectData: () => pruneProjectData,
|
|
10063
10745
|
qaLockPath: () => qaLockPath,
|
|
@@ -10072,11 +10754,13 @@ __export(dist_exports, {
|
|
|
10072
10754
|
removeEntriesForProject: () => removeEntriesForProject,
|
|
10073
10755
|
removeJob: () => removeJob,
|
|
10074
10756
|
removeProject: () => removeProject,
|
|
10757
|
+
renderManagerMemory: () => renderManagerMemory,
|
|
10075
10758
|
renderPrdTemplate: () => renderPrdTemplate,
|
|
10076
10759
|
renderProjectFeedbackBlock: () => renderProjectFeedbackBlock,
|
|
10077
10760
|
renderSlicerPrompt: () => renderSlicerPrompt,
|
|
10078
10761
|
resetRepositories: () => resetRepositories,
|
|
10079
10762
|
resolveJobProvider: () => resolveJobProvider,
|
|
10763
|
+
resolveManagerConfig: () => resolveManagerConfig,
|
|
10080
10764
|
resolvePreset: () => resolvePreset,
|
|
10081
10765
|
resolveProviderBucketKey: () => resolveProviderBucketKey,
|
|
10082
10766
|
resolveWorktreeBaseRef: () => resolveWorktreeBaseRef,
|
|
@@ -10084,6 +10768,7 @@ __export(dist_exports, {
|
|
|
10084
10768
|
rotateLog: () => rotateLog,
|
|
10085
10769
|
runAllChecks: () => runAllChecks,
|
|
10086
10770
|
runAnalytics: () => runAnalytics,
|
|
10771
|
+
runManager: () => runManager,
|
|
10087
10772
|
runMigrations: () => runMigrations,
|
|
10088
10773
|
saveConfig: () => saveConfig,
|
|
10089
10774
|
saveGlobalNotificationsConfig: () => saveGlobalNotificationsConfig,
|
|
@@ -10102,6 +10787,8 @@ __export(dist_exports, {
|
|
|
10102
10787
|
sortByPriority: () => sortByPriority,
|
|
10103
10788
|
step: () => step,
|
|
10104
10789
|
success: () => success,
|
|
10790
|
+
summarizeCreatedDrafts: () => summarizeCreatedDrafts,
|
|
10791
|
+
summarizeSkippedFindings: () => summarizeSkippedFindings,
|
|
10105
10792
|
syncAuditFindingsToBoard: () => syncAuditFindingsToBoard,
|
|
10106
10793
|
unmarkItemProcessed: () => unmarkItemProcessed,
|
|
10107
10794
|
unregisterProject: () => unregisterProject,
|
|
@@ -10111,6 +10798,7 @@ __export(dist_exports, {
|
|
|
10111
10798
|
validateWebhook: () => validateWebhook,
|
|
10112
10799
|
warn: () => warn,
|
|
10113
10800
|
writeCrontab: () => writeCrontab,
|
|
10801
|
+
writeManagerMemory: () => writeManagerMemory,
|
|
10114
10802
|
writePrdState: () => writePrdState
|
|
10115
10803
|
});
|
|
10116
10804
|
var init_dist = __esm({
|
|
@@ -10161,6 +10849,7 @@ var init_dist = __esm({
|
|
|
10161
10849
|
init_summary();
|
|
10162
10850
|
init_analytics();
|
|
10163
10851
|
init_audit();
|
|
10852
|
+
init_manager();
|
|
10164
10853
|
init_outcome_parser();
|
|
10165
10854
|
init_pattern_analyzer();
|
|
10166
10855
|
init_prompt_augmenter();
|
|
@@ -10173,30 +10862,30 @@ var init_dist = __esm({
|
|
|
10173
10862
|
// src/cli.ts
|
|
10174
10863
|
import "reflect-metadata";
|
|
10175
10864
|
import { Command as Command3 } from "commander";
|
|
10176
|
-
import { existsSync as
|
|
10865
|
+
import { existsSync as existsSync36, readFileSync as readFileSync23 } from "fs";
|
|
10177
10866
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
10178
|
-
import { dirname as
|
|
10867
|
+
import { dirname as dirname13, join as join39 } from "path";
|
|
10179
10868
|
|
|
10180
10869
|
// src/commands/init.ts
|
|
10181
10870
|
init_dist();
|
|
10182
|
-
import
|
|
10183
|
-
import
|
|
10871
|
+
import fs26 from "fs";
|
|
10872
|
+
import path25 from "path";
|
|
10184
10873
|
import { execSync as execSync3 } from "child_process";
|
|
10185
10874
|
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
10186
|
-
import { dirname as
|
|
10875
|
+
import { dirname as dirname7, join as join21 } from "path";
|
|
10187
10876
|
import * as readline from "readline";
|
|
10188
10877
|
var __filename2 = fileURLToPath3(import.meta.url);
|
|
10189
|
-
var __dirname2 =
|
|
10878
|
+
var __dirname2 = dirname7(__filename2);
|
|
10190
10879
|
function findTemplatesDir(startDir) {
|
|
10191
10880
|
let d = startDir;
|
|
10192
10881
|
for (let i = 0; i < 8; i++) {
|
|
10193
|
-
const candidate =
|
|
10194
|
-
if (
|
|
10882
|
+
const candidate = join21(d, "templates");
|
|
10883
|
+
if (fs26.existsSync(candidate) && fs26.statSync(candidate).isDirectory()) {
|
|
10195
10884
|
return candidate;
|
|
10196
10885
|
}
|
|
10197
|
-
d =
|
|
10886
|
+
d = dirname7(d);
|
|
10198
10887
|
}
|
|
10199
|
-
return
|
|
10888
|
+
return join21(startDir, "templates");
|
|
10200
10889
|
}
|
|
10201
10890
|
var TEMPLATES_DIR = findTemplatesDir(__dirname2);
|
|
10202
10891
|
var NW_SKILLS = [
|
|
@@ -10208,12 +10897,12 @@ var NW_SKILLS = [
|
|
|
10208
10897
|
"nw-review"
|
|
10209
10898
|
];
|
|
10210
10899
|
function hasPlaywrightDependency(cwd) {
|
|
10211
|
-
const packageJsonPath =
|
|
10212
|
-
if (!
|
|
10900
|
+
const packageJsonPath = path25.join(cwd, "package.json");
|
|
10901
|
+
if (!fs26.existsSync(packageJsonPath)) {
|
|
10213
10902
|
return false;
|
|
10214
10903
|
}
|
|
10215
10904
|
try {
|
|
10216
|
-
const packageJson2 = JSON.parse(
|
|
10905
|
+
const packageJson2 = JSON.parse(fs26.readFileSync(packageJsonPath, "utf-8"));
|
|
10217
10906
|
return Boolean(
|
|
10218
10907
|
packageJson2.dependencies?.["@playwright/test"] || packageJson2.dependencies?.playwright || packageJson2.devDependencies?.["@playwright/test"] || packageJson2.devDependencies?.playwright
|
|
10219
10908
|
);
|
|
@@ -10225,7 +10914,7 @@ function detectPlaywright(cwd) {
|
|
|
10225
10914
|
if (hasPlaywrightDependency(cwd)) {
|
|
10226
10915
|
return true;
|
|
10227
10916
|
}
|
|
10228
|
-
if (
|
|
10917
|
+
if (fs26.existsSync(path25.join(cwd, "node_modules", ".bin", "playwright"))) {
|
|
10229
10918
|
return true;
|
|
10230
10919
|
}
|
|
10231
10920
|
try {
|
|
@@ -10241,10 +10930,10 @@ function detectPlaywright(cwd) {
|
|
|
10241
10930
|
}
|
|
10242
10931
|
}
|
|
10243
10932
|
function resolvePlaywrightInstallCommand(cwd) {
|
|
10244
|
-
if (
|
|
10933
|
+
if (fs26.existsSync(path25.join(cwd, "pnpm-lock.yaml"))) {
|
|
10245
10934
|
return "pnpm add -D @playwright/test";
|
|
10246
10935
|
}
|
|
10247
|
-
if (
|
|
10936
|
+
if (fs26.existsSync(path25.join(cwd, "yarn.lock"))) {
|
|
10248
10937
|
return "yarn add -D @playwright/test";
|
|
10249
10938
|
}
|
|
10250
10939
|
return "npm install -D @playwright/test";
|
|
@@ -10253,7 +10942,7 @@ function promptYesNo(question, defaultNo = true) {
|
|
|
10253
10942
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
10254
10943
|
return Promise.resolve(false);
|
|
10255
10944
|
}
|
|
10256
|
-
return new Promise((
|
|
10945
|
+
return new Promise((resolve11) => {
|
|
10257
10946
|
const rl = readline.createInterface({
|
|
10258
10947
|
input: process.stdin,
|
|
10259
10948
|
output: process.stdout
|
|
@@ -10263,10 +10952,10 @@ function promptYesNo(question, defaultNo = true) {
|
|
|
10263
10952
|
rl.close();
|
|
10264
10953
|
const normalized = answer.trim().toLowerCase();
|
|
10265
10954
|
if (normalized === "") {
|
|
10266
|
-
|
|
10955
|
+
resolve11(!defaultNo);
|
|
10267
10956
|
return;
|
|
10268
10957
|
}
|
|
10269
|
-
|
|
10958
|
+
resolve11(normalized === "y" || normalized === "yes");
|
|
10270
10959
|
});
|
|
10271
10960
|
});
|
|
10272
10961
|
}
|
|
@@ -10369,7 +11058,7 @@ function getDefaultBranch(cwd) {
|
|
|
10369
11058
|
}
|
|
10370
11059
|
}
|
|
10371
11060
|
function promptProviderSelection(providers) {
|
|
10372
|
-
return new Promise((
|
|
11061
|
+
return new Promise((resolve11, reject) => {
|
|
10373
11062
|
const rl = readline.createInterface({
|
|
10374
11063
|
input: process.stdin,
|
|
10375
11064
|
output: process.stdout
|
|
@@ -10385,13 +11074,13 @@ function promptProviderSelection(providers) {
|
|
|
10385
11074
|
reject(new Error("Invalid selection. Please run init again and select a valid number."));
|
|
10386
11075
|
return;
|
|
10387
11076
|
}
|
|
10388
|
-
|
|
11077
|
+
resolve11(providers[selection - 1]);
|
|
10389
11078
|
});
|
|
10390
11079
|
});
|
|
10391
11080
|
}
|
|
10392
11081
|
function ensureDir(dirPath) {
|
|
10393
|
-
if (!
|
|
10394
|
-
|
|
11082
|
+
if (!fs26.existsSync(dirPath)) {
|
|
11083
|
+
fs26.mkdirSync(dirPath, { recursive: true });
|
|
10395
11084
|
}
|
|
10396
11085
|
}
|
|
10397
11086
|
function buildInitConfig(params) {
|
|
@@ -10441,6 +11130,7 @@ function buildInitConfig(params) {
|
|
|
10441
11130
|
},
|
|
10442
11131
|
audit: { ...defaults.audit },
|
|
10443
11132
|
analytics: { ...defaults.analytics },
|
|
11133
|
+
manager: { ...defaults.manager },
|
|
10444
11134
|
feedback: { ...defaults.feedback },
|
|
10445
11135
|
merger: { ...defaults.merger },
|
|
10446
11136
|
prResolver: { ...defaults.prResolver },
|
|
@@ -10465,30 +11155,30 @@ function buildInitConfig(params) {
|
|
|
10465
11155
|
}
|
|
10466
11156
|
function resolveTemplatePath(templateName, customTemplatesDir, bundledTemplatesDir) {
|
|
10467
11157
|
if (customTemplatesDir !== null) {
|
|
10468
|
-
const customPath =
|
|
10469
|
-
if (
|
|
11158
|
+
const customPath = join21(customTemplatesDir, templateName);
|
|
11159
|
+
if (fs26.existsSync(customPath)) {
|
|
10470
11160
|
return { path: customPath, source: "custom" };
|
|
10471
11161
|
}
|
|
10472
11162
|
}
|
|
10473
|
-
return { path:
|
|
11163
|
+
return { path: join21(bundledTemplatesDir, templateName), source: "bundled" };
|
|
10474
11164
|
}
|
|
10475
11165
|
function processTemplate(templateName, targetPath, replacements, force, sourcePath, source) {
|
|
10476
|
-
if (
|
|
11166
|
+
if (fs26.existsSync(targetPath) && !force) {
|
|
10477
11167
|
console.log(` Skipped (exists): ${targetPath}`);
|
|
10478
11168
|
return { created: false, source: source ?? "bundled" };
|
|
10479
11169
|
}
|
|
10480
|
-
const templatePath = sourcePath ??
|
|
11170
|
+
const templatePath = sourcePath ?? join21(TEMPLATES_DIR, templateName);
|
|
10481
11171
|
const resolvedSource = source ?? "bundled";
|
|
10482
|
-
let content =
|
|
11172
|
+
let content = fs26.readFileSync(templatePath, "utf-8");
|
|
10483
11173
|
for (const [key, value] of Object.entries(replacements)) {
|
|
10484
11174
|
content = content.replaceAll(key, value);
|
|
10485
11175
|
}
|
|
10486
|
-
|
|
11176
|
+
fs26.writeFileSync(targetPath, content);
|
|
10487
11177
|
console.log(` Created: ${targetPath} (${resolvedSource})`);
|
|
10488
11178
|
return { created: true, source: resolvedSource };
|
|
10489
11179
|
}
|
|
10490
11180
|
function addToGitignore(cwd) {
|
|
10491
|
-
const gitignorePath =
|
|
11181
|
+
const gitignorePath = path25.join(cwd, ".gitignore");
|
|
10492
11182
|
const entries = [
|
|
10493
11183
|
{
|
|
10494
11184
|
pattern: "/logs/",
|
|
@@ -10502,13 +11192,13 @@ function addToGitignore(cwd) {
|
|
|
10502
11192
|
},
|
|
10503
11193
|
{ pattern: "*.claim", label: "*.claim", check: (c) => c.includes("*.claim") }
|
|
10504
11194
|
];
|
|
10505
|
-
if (!
|
|
11195
|
+
if (!fs26.existsSync(gitignorePath)) {
|
|
10506
11196
|
const lines = ["# Night Watch", ...entries.map((e) => e.pattern), ""];
|
|
10507
|
-
|
|
11197
|
+
fs26.writeFileSync(gitignorePath, lines.join("\n"));
|
|
10508
11198
|
console.log(` Created: ${gitignorePath} (with Night Watch entries)`);
|
|
10509
11199
|
return;
|
|
10510
11200
|
}
|
|
10511
|
-
const content =
|
|
11201
|
+
const content = fs26.readFileSync(gitignorePath, "utf-8");
|
|
10512
11202
|
const missing = entries.filter((e) => !e.check(content));
|
|
10513
11203
|
if (missing.length === 0) {
|
|
10514
11204
|
console.log(` Skipped (exists): Night Watch entries in .gitignore`);
|
|
@@ -10516,59 +11206,59 @@ function addToGitignore(cwd) {
|
|
|
10516
11206
|
}
|
|
10517
11207
|
const additions = missing.map((e) => e.pattern).join("\n");
|
|
10518
11208
|
const newContent = content.trimEnd() + "\n\n# Night Watch\n" + additions + "\n";
|
|
10519
|
-
|
|
11209
|
+
fs26.writeFileSync(gitignorePath, newContent);
|
|
10520
11210
|
console.log(` Updated: ${gitignorePath} (added ${missing.map((e) => e.label).join(", ")})`);
|
|
10521
11211
|
}
|
|
10522
11212
|
function installSkills(cwd, provider, force, templatesDir) {
|
|
10523
|
-
const skillsTemplatesDir =
|
|
10524
|
-
if (!
|
|
11213
|
+
const skillsTemplatesDir = path25.join(templatesDir, "skills");
|
|
11214
|
+
if (!fs26.existsSync(skillsTemplatesDir)) {
|
|
10525
11215
|
return { location: "", installed: 0, skipped: 0, type: "none" };
|
|
10526
11216
|
}
|
|
10527
11217
|
const isClaudeProvider = provider === "claude" || provider.startsWith("claude");
|
|
10528
11218
|
const isCodexProvider = provider === "codex";
|
|
10529
|
-
const claudeDir =
|
|
10530
|
-
if (isClaudeProvider ||
|
|
11219
|
+
const claudeDir = path25.join(cwd, ".claude");
|
|
11220
|
+
if (isClaudeProvider || fs26.existsSync(claudeDir)) {
|
|
10531
11221
|
ensureDir(claudeDir);
|
|
10532
|
-
const skillsDir =
|
|
11222
|
+
const skillsDir = path25.join(claudeDir, "skills");
|
|
10533
11223
|
ensureDir(skillsDir);
|
|
10534
11224
|
let installed = 0;
|
|
10535
11225
|
let skipped = 0;
|
|
10536
11226
|
for (const skillName of NW_SKILLS) {
|
|
10537
|
-
const templateFile =
|
|
10538
|
-
if (!
|
|
10539
|
-
const skillDir =
|
|
11227
|
+
const templateFile = path25.join(skillsTemplatesDir, `${skillName}.md`);
|
|
11228
|
+
if (!fs26.existsSync(templateFile)) continue;
|
|
11229
|
+
const skillDir = path25.join(skillsDir, skillName);
|
|
10540
11230
|
ensureDir(skillDir);
|
|
10541
|
-
const target =
|
|
10542
|
-
if (
|
|
11231
|
+
const target = path25.join(skillDir, "SKILL.md");
|
|
11232
|
+
if (fs26.existsSync(target) && !force) {
|
|
10543
11233
|
skipped++;
|
|
10544
11234
|
continue;
|
|
10545
11235
|
}
|
|
10546
|
-
|
|
11236
|
+
fs26.copyFileSync(templateFile, target);
|
|
10547
11237
|
installed++;
|
|
10548
11238
|
}
|
|
10549
11239
|
return { location: ".claude/skills/", installed, skipped, type: "claude" };
|
|
10550
11240
|
}
|
|
10551
11241
|
if (isCodexProvider) {
|
|
10552
|
-
const agentsFile =
|
|
10553
|
-
const blockFile =
|
|
10554
|
-
if (!
|
|
11242
|
+
const agentsFile = path25.join(cwd, "AGENTS.md");
|
|
11243
|
+
const blockFile = path25.join(skillsTemplatesDir, "_codex-block.md");
|
|
11244
|
+
if (!fs26.existsSync(blockFile)) {
|
|
10555
11245
|
return { location: "", installed: 0, skipped: 0, type: "none" };
|
|
10556
11246
|
}
|
|
10557
|
-
const block =
|
|
11247
|
+
const block = fs26.readFileSync(blockFile, "utf-8");
|
|
10558
11248
|
const marker = "## Night Watch Skills";
|
|
10559
|
-
if (!
|
|
10560
|
-
|
|
11249
|
+
if (!fs26.existsSync(agentsFile)) {
|
|
11250
|
+
fs26.writeFileSync(agentsFile, block);
|
|
10561
11251
|
return { location: "AGENTS.md", installed: NW_SKILLS.length, skipped: 0, type: "codex" };
|
|
10562
11252
|
}
|
|
10563
|
-
const existing =
|
|
11253
|
+
const existing = fs26.readFileSync(agentsFile, "utf-8");
|
|
10564
11254
|
if (existing.includes(marker)) {
|
|
10565
11255
|
if (!force) {
|
|
10566
11256
|
return { location: "AGENTS.md", installed: 0, skipped: NW_SKILLS.length, type: "codex" };
|
|
10567
11257
|
}
|
|
10568
11258
|
const withoutSection = existing.replace(/\n\n## Night Watch Skills[\s\S]*$/, "");
|
|
10569
|
-
|
|
11259
|
+
fs26.writeFileSync(agentsFile, withoutSection + "\n\n" + block);
|
|
10570
11260
|
} else {
|
|
10571
|
-
|
|
11261
|
+
fs26.appendFileSync(agentsFile, "\n\n" + block);
|
|
10572
11262
|
}
|
|
10573
11263
|
return { location: "AGENTS.md", installed: NW_SKILLS.length, skipped: 0, type: "codex" };
|
|
10574
11264
|
}
|
|
@@ -10692,28 +11382,28 @@ function initCommand(program2) {
|
|
|
10692
11382
|
"${DEFAULT_BRANCH}": defaultBranch
|
|
10693
11383
|
};
|
|
10694
11384
|
step(6, totalSteps, "Creating PRD directory structure...");
|
|
10695
|
-
const prdDirPath =
|
|
10696
|
-
const doneDirPath =
|
|
11385
|
+
const prdDirPath = path25.join(cwd, prdDir);
|
|
11386
|
+
const doneDirPath = path25.join(prdDirPath, "done");
|
|
10697
11387
|
ensureDir(doneDirPath);
|
|
10698
11388
|
success(`Created ${prdDirPath}/`);
|
|
10699
11389
|
success(`Created ${doneDirPath}/`);
|
|
10700
11390
|
step(7, totalSteps, "Creating logs directory...");
|
|
10701
|
-
const logsPath =
|
|
11391
|
+
const logsPath = path25.join(cwd, LOG_DIR);
|
|
10702
11392
|
ensureDir(logsPath);
|
|
10703
11393
|
success(`Created ${logsPath}/`);
|
|
10704
11394
|
addToGitignore(cwd);
|
|
10705
11395
|
step(8, totalSteps, "Creating instructions directory...");
|
|
10706
|
-
const instructionsDir =
|
|
11396
|
+
const instructionsDir = path25.join(cwd, "instructions");
|
|
10707
11397
|
ensureDir(instructionsDir);
|
|
10708
11398
|
success(`Created ${instructionsDir}/`);
|
|
10709
11399
|
const existingConfig = loadConfig(cwd);
|
|
10710
|
-
const customTemplatesDirPath =
|
|
10711
|
-
const customTemplatesDir =
|
|
11400
|
+
const customTemplatesDirPath = path25.join(cwd, existingConfig.templatesDir);
|
|
11401
|
+
const customTemplatesDir = fs26.existsSync(customTemplatesDirPath) ? customTemplatesDirPath : null;
|
|
10712
11402
|
const templateSources = [];
|
|
10713
11403
|
const nwResolution = resolveTemplatePath("executor.md", customTemplatesDir, TEMPLATES_DIR);
|
|
10714
11404
|
const nwResult = processTemplate(
|
|
10715
11405
|
"executor.md",
|
|
10716
|
-
|
|
11406
|
+
path25.join(instructionsDir, "executor.md"),
|
|
10717
11407
|
replacements,
|
|
10718
11408
|
force,
|
|
10719
11409
|
nwResolution.path,
|
|
@@ -10727,7 +11417,7 @@ function initCommand(program2) {
|
|
|
10727
11417
|
);
|
|
10728
11418
|
const peResult = processTemplate(
|
|
10729
11419
|
"prd-executor.md",
|
|
10730
|
-
|
|
11420
|
+
path25.join(instructionsDir, "prd-executor.md"),
|
|
10731
11421
|
replacements,
|
|
10732
11422
|
force,
|
|
10733
11423
|
peResolution.path,
|
|
@@ -10737,7 +11427,7 @@ function initCommand(program2) {
|
|
|
10737
11427
|
const prResolution = resolveTemplatePath("pr-reviewer.md", customTemplatesDir, TEMPLATES_DIR);
|
|
10738
11428
|
const prResult = processTemplate(
|
|
10739
11429
|
"pr-reviewer.md",
|
|
10740
|
-
|
|
11430
|
+
path25.join(instructionsDir, "pr-reviewer.md"),
|
|
10741
11431
|
replacements,
|
|
10742
11432
|
force,
|
|
10743
11433
|
prResolution.path,
|
|
@@ -10747,7 +11437,7 @@ function initCommand(program2) {
|
|
|
10747
11437
|
const qaResolution = resolveTemplatePath("qa.md", customTemplatesDir, TEMPLATES_DIR);
|
|
10748
11438
|
const qaResult = processTemplate(
|
|
10749
11439
|
"qa.md",
|
|
10750
|
-
|
|
11440
|
+
path25.join(instructionsDir, "qa.md"),
|
|
10751
11441
|
replacements,
|
|
10752
11442
|
force,
|
|
10753
11443
|
qaResolution.path,
|
|
@@ -10757,7 +11447,7 @@ function initCommand(program2) {
|
|
|
10757
11447
|
const auditResolution = resolveTemplatePath("audit.md", customTemplatesDir, TEMPLATES_DIR);
|
|
10758
11448
|
const auditResult = processTemplate(
|
|
10759
11449
|
"audit.md",
|
|
10760
|
-
|
|
11450
|
+
path25.join(instructionsDir, "audit.md"),
|
|
10761
11451
|
replacements,
|
|
10762
11452
|
force,
|
|
10763
11453
|
auditResolution.path,
|
|
@@ -10771,7 +11461,7 @@ function initCommand(program2) {
|
|
|
10771
11461
|
);
|
|
10772
11462
|
const plannerResult = processTemplate(
|
|
10773
11463
|
"prd-creator.md",
|
|
10774
|
-
|
|
11464
|
+
path25.join(instructionsDir, "prd-creator.md"),
|
|
10775
11465
|
replacements,
|
|
10776
11466
|
force,
|
|
10777
11467
|
plannerResolution.path,
|
|
@@ -10779,8 +11469,8 @@ function initCommand(program2) {
|
|
|
10779
11469
|
);
|
|
10780
11470
|
templateSources.push({ name: "prd-creator.md", source: plannerResult.source });
|
|
10781
11471
|
step(9, totalSteps, "Creating configuration file...");
|
|
10782
|
-
const configPath =
|
|
10783
|
-
if (
|
|
11472
|
+
const configPath = path25.join(cwd, CONFIG_FILE_NAME);
|
|
11473
|
+
if (fs26.existsSync(configPath) && !force) {
|
|
10784
11474
|
console.log(` Skipped (exists): ${configPath}`);
|
|
10785
11475
|
} else {
|
|
10786
11476
|
const config = buildInitConfig({
|
|
@@ -10790,11 +11480,11 @@ function initCommand(program2) {
|
|
|
10790
11480
|
reviewerEnabled,
|
|
10791
11481
|
prdDir
|
|
10792
11482
|
});
|
|
10793
|
-
|
|
11483
|
+
fs26.writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
|
10794
11484
|
success(`Created ${configPath}`);
|
|
10795
11485
|
}
|
|
10796
11486
|
step(10, totalSteps, "Setting up GitHub Project board...");
|
|
10797
|
-
const existingRaw = JSON.parse(
|
|
11487
|
+
const existingRaw = JSON.parse(fs26.readFileSync(configPath, "utf-8"));
|
|
10798
11488
|
const existingBoard = existingRaw.boardProvider;
|
|
10799
11489
|
let boardSetupStatus = "Skipped";
|
|
10800
11490
|
if (existingBoard?.projectNumber && !force) {
|
|
@@ -10816,14 +11506,14 @@ function initCommand(program2) {
|
|
|
10816
11506
|
const provider = createBoardProvider({ enabled: true, provider: "github" }, cwd);
|
|
10817
11507
|
const boardTitle = `${projectName} Night Watch`;
|
|
10818
11508
|
const board = await provider.setupBoard(boardTitle);
|
|
10819
|
-
const rawConfig = JSON.parse(
|
|
11509
|
+
const rawConfig = JSON.parse(fs26.readFileSync(configPath, "utf-8"));
|
|
10820
11510
|
rawConfig.boardProvider = {
|
|
10821
11511
|
enabled: true,
|
|
10822
11512
|
provider: "github",
|
|
10823
11513
|
projectNumber: board.number,
|
|
10824
11514
|
projectTitle: board.title
|
|
10825
11515
|
};
|
|
10826
|
-
|
|
11516
|
+
fs26.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n");
|
|
10827
11517
|
boardSetupStatus = `Created (#${board.number})`;
|
|
10828
11518
|
success(`GitHub Project board "${boardTitle}" ready (#${board.number})`);
|
|
10829
11519
|
} catch (boardErr) {
|
|
@@ -10985,7 +11675,7 @@ async function maybeApplyCronSchedulingDelay(config, jobType, projectDir) {
|
|
|
10985
11675
|
return plan;
|
|
10986
11676
|
}
|
|
10987
11677
|
if (plan.totalDelayMinutes > 0) {
|
|
10988
|
-
await new Promise((
|
|
11678
|
+
await new Promise((resolve11) => setTimeout(resolve11, plan.totalDelayMinutes * 6e4));
|
|
10989
11679
|
}
|
|
10990
11680
|
return getSchedulingPlan(projectDir, config, jobType);
|
|
10991
11681
|
}
|
|
@@ -11049,8 +11739,8 @@ function recordJobOutcome(input) {
|
|
|
11049
11739
|
}
|
|
11050
11740
|
|
|
11051
11741
|
// src/commands/run.ts
|
|
11052
|
-
import * as
|
|
11053
|
-
import * as
|
|
11742
|
+
import * as fs27 from "fs";
|
|
11743
|
+
import * as path26 from "path";
|
|
11054
11744
|
function resolveRunNotificationEvent(exitCode, scriptStatus) {
|
|
11055
11745
|
if (exitCode === 124) {
|
|
11056
11746
|
return "run_timeout";
|
|
@@ -11148,7 +11838,7 @@ function buildRunNotificationContext(config, projectDir, event, exitCode, script
|
|
|
11148
11838
|
const checkpointStatus = checkpointValue === "created" || checkpointValue === "available" || checkpointValue === "none" ? checkpointValue : void 0;
|
|
11149
11839
|
return {
|
|
11150
11840
|
event,
|
|
11151
|
-
projectName:
|
|
11841
|
+
projectName: path26.basename(projectDir),
|
|
11152
11842
|
exitCode,
|
|
11153
11843
|
provider: config.provider,
|
|
11154
11844
|
prdName: scriptResult?.data.prd ?? extractResultValueFromOutput(rawOutput, "prd"),
|
|
@@ -11168,12 +11858,12 @@ function buildRunNotificationContext(config, projectDir, event, exitCode, script
|
|
|
11168
11858
|
};
|
|
11169
11859
|
}
|
|
11170
11860
|
function getCrossProjectFallbackCandidates(currentProjectDir) {
|
|
11171
|
-
const current =
|
|
11861
|
+
const current = path26.resolve(currentProjectDir);
|
|
11172
11862
|
const { valid, invalid } = validateRegistry();
|
|
11173
11863
|
for (const entry of invalid) {
|
|
11174
11864
|
warn(`Skipping invalid registry entry: ${entry.path}`);
|
|
11175
11865
|
}
|
|
11176
|
-
return valid.filter((entry) =>
|
|
11866
|
+
return valid.filter((entry) => path26.resolve(entry.path) !== current);
|
|
11177
11867
|
}
|
|
11178
11868
|
async function sendRunCompletionNotifications(config, projectDir, options, exitCode, scriptResult, rawOutput) {
|
|
11179
11869
|
if (isRateLimitFallbackTriggered(scriptResult?.data)) {
|
|
@@ -11183,7 +11873,7 @@ async function sendRunCompletionNotifications(config, projectDir, options, exitC
|
|
|
11183
11873
|
if (nonTelegramWebhooks.length > 0) {
|
|
11184
11874
|
const _rateLimitCtx = {
|
|
11185
11875
|
event: "rate_limit_fallback",
|
|
11186
|
-
projectName:
|
|
11876
|
+
projectName: path26.basename(projectDir),
|
|
11187
11877
|
exitCode,
|
|
11188
11878
|
provider: config.provider
|
|
11189
11879
|
};
|
|
@@ -11420,23 +12110,24 @@ function applyCliOverrides(config, options) {
|
|
|
11420
12110
|
return overridden;
|
|
11421
12111
|
}
|
|
11422
12112
|
function scanPrdDirectory(projectDir, prdDir, maxRuntime) {
|
|
11423
|
-
const
|
|
11424
|
-
const
|
|
12113
|
+
const claimStaleAfter = maxRuntime > 0 ? maxRuntime : 14400;
|
|
12114
|
+
const absolutePrdDir = path26.join(projectDir, prdDir);
|
|
12115
|
+
const doneDir = path26.join(absolutePrdDir, "done");
|
|
11425
12116
|
const pending = [];
|
|
11426
12117
|
const completed = [];
|
|
11427
|
-
if (
|
|
11428
|
-
const entries =
|
|
12118
|
+
if (fs27.existsSync(absolutePrdDir)) {
|
|
12119
|
+
const entries = fs27.readdirSync(absolutePrdDir, { withFileTypes: true });
|
|
11429
12120
|
for (const entry of entries) {
|
|
11430
12121
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
11431
|
-
const claimPath =
|
|
12122
|
+
const claimPath = path26.join(absolutePrdDir, entry.name + CLAIM_FILE_EXTENSION);
|
|
11432
12123
|
let claimed = false;
|
|
11433
12124
|
let claimInfo = null;
|
|
11434
|
-
if (
|
|
12125
|
+
if (fs27.existsSync(claimPath)) {
|
|
11435
12126
|
try {
|
|
11436
|
-
const content =
|
|
12127
|
+
const content = fs27.readFileSync(claimPath, "utf-8");
|
|
11437
12128
|
const data = JSON.parse(content);
|
|
11438
12129
|
const age = Math.floor(Date.now() / 1e3) - data.timestamp;
|
|
11439
|
-
if (age <
|
|
12130
|
+
if (age < claimStaleAfter) {
|
|
11440
12131
|
claimed = true;
|
|
11441
12132
|
claimInfo = { hostname: data.hostname, pid: data.pid, timestamp: data.timestamp };
|
|
11442
12133
|
}
|
|
@@ -11447,8 +12138,8 @@ function scanPrdDirectory(projectDir, prdDir, maxRuntime) {
|
|
|
11447
12138
|
}
|
|
11448
12139
|
}
|
|
11449
12140
|
}
|
|
11450
|
-
if (
|
|
11451
|
-
const entries =
|
|
12141
|
+
if (fs27.existsSync(doneDir)) {
|
|
12142
|
+
const entries = fs27.readdirSync(doneDir, { withFileTypes: true });
|
|
11452
12143
|
for (const entry of entries) {
|
|
11453
12144
|
if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
11454
12145
|
completed.push(entry.name);
|
|
@@ -11633,7 +12324,7 @@ ${stderr}`
|
|
|
11633
12324
|
// src/commands/review.ts
|
|
11634
12325
|
init_dist();
|
|
11635
12326
|
import { execFileSync as execFileSync5 } from "child_process";
|
|
11636
|
-
import * as
|
|
12327
|
+
import * as path27 from "path";
|
|
11637
12328
|
function shouldSendReviewNotification(scriptStatus) {
|
|
11638
12329
|
if (!scriptStatus) {
|
|
11639
12330
|
return true;
|
|
@@ -11961,7 +12652,7 @@ ${stderr}`);
|
|
|
11961
12652
|
const reviewEvent = legacyNoChangesNeeded ? "review_ready_for_human" : "review_completed";
|
|
11962
12653
|
await sendNotifications(config, {
|
|
11963
12654
|
event: reviewEvent,
|
|
11964
|
-
projectName:
|
|
12655
|
+
projectName: path27.basename(projectDir),
|
|
11965
12656
|
exitCode,
|
|
11966
12657
|
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL),
|
|
11967
12658
|
prUrl: fallbackPrDetails?.url,
|
|
@@ -11980,7 +12671,7 @@ ${stderr}`);
|
|
|
11980
12671
|
const reviewEvent = target.noChangesNeeded ? "review_ready_for_human" : "review_completed";
|
|
11981
12672
|
await sendNotifications(config, {
|
|
11982
12673
|
event: reviewEvent,
|
|
11983
|
-
projectName:
|
|
12674
|
+
projectName: path27.basename(projectDir),
|
|
11984
12675
|
exitCode,
|
|
11985
12676
|
provider: formatProviderDisplay(
|
|
11986
12677
|
envVars.NW_PROVIDER_CMD,
|
|
@@ -12005,7 +12696,7 @@ ${stderr}`);
|
|
|
12005
12696
|
const autoMergedPrDetails = fetchPrDetailsByNumber(autoMergedPrNumber, projectDir);
|
|
12006
12697
|
const _mergeCtx = {
|
|
12007
12698
|
event: "pr_auto_merged",
|
|
12008
|
-
projectName:
|
|
12699
|
+
projectName: path27.basename(projectDir),
|
|
12009
12700
|
exitCode,
|
|
12010
12701
|
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL),
|
|
12011
12702
|
prNumber: autoMergedPrDetails?.number ?? autoMergedPrNumber,
|
|
@@ -12030,7 +12721,7 @@ ${stderr}`);
|
|
|
12030
12721
|
|
|
12031
12722
|
// src/commands/qa.ts
|
|
12032
12723
|
init_dist();
|
|
12033
|
-
import * as
|
|
12724
|
+
import * as path28 from "path";
|
|
12034
12725
|
function shouldSendQaNotification(scriptStatus) {
|
|
12035
12726
|
if (!scriptStatus) {
|
|
12036
12727
|
return true;
|
|
@@ -12192,7 +12883,7 @@ ${stderr}`);
|
|
|
12192
12883
|
const qaScreenshotUrls = primaryQaPr !== void 0 ? fetchQaScreenshotUrlsForPr(primaryQaPr, projectDir, repo) : [];
|
|
12193
12884
|
const _qaCtx = {
|
|
12194
12885
|
event: "qa_completed",
|
|
12195
|
-
projectName:
|
|
12886
|
+
projectName: path28.basename(projectDir),
|
|
12196
12887
|
exitCode,
|
|
12197
12888
|
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL),
|
|
12198
12889
|
prNumber: prDetails?.number ?? primaryQaPr,
|
|
@@ -12218,8 +12909,8 @@ ${stderr}`);
|
|
|
12218
12909
|
|
|
12219
12910
|
// src/commands/audit.ts
|
|
12220
12911
|
init_dist();
|
|
12221
|
-
import * as
|
|
12222
|
-
import * as
|
|
12912
|
+
import * as fs28 from "fs";
|
|
12913
|
+
import * as path29 from "path";
|
|
12223
12914
|
function buildEnvVars4(config, options) {
|
|
12224
12915
|
const env = buildBaseEnvVars(config, "audit", options.dryRun);
|
|
12225
12916
|
env.NW_AUDIT_MAX_RUNTIME = String(config.audit.maxRuntime);
|
|
@@ -12267,7 +12958,7 @@ function auditCommand(program2) {
|
|
|
12267
12958
|
if (config.audit.createIssues) {
|
|
12268
12959
|
configTable.push(["Target Column", config.audit.targetColumn]);
|
|
12269
12960
|
}
|
|
12270
|
-
configTable.push(["Report File",
|
|
12961
|
+
configTable.push(["Report File", path29.join(projectDir, "logs", "audit-report.md")]);
|
|
12271
12962
|
console.log(configTable.toString());
|
|
12272
12963
|
header("Provider Invocation");
|
|
12273
12964
|
const providerCmd = PROVIDER_COMMANDS[auditProvider];
|
|
@@ -12325,8 +13016,8 @@ ${stderr}`);
|
|
|
12325
13016
|
} else if (scriptResult?.status?.startsWith("skip_")) {
|
|
12326
13017
|
spinner.succeed("Code audit skipped");
|
|
12327
13018
|
} else {
|
|
12328
|
-
const reportPath =
|
|
12329
|
-
if (!
|
|
13019
|
+
const reportPath = path29.join(projectDir, "logs", "audit-report.md");
|
|
13020
|
+
if (!fs28.existsSync(reportPath)) {
|
|
12330
13021
|
spinner.fail("Code audit finished without a report file");
|
|
12331
13022
|
process.exit(1);
|
|
12332
13023
|
}
|
|
@@ -12343,9 +13034,9 @@ ${stderr}`);
|
|
|
12343
13034
|
const providerExit = scriptResult?.data?.provider_exit;
|
|
12344
13035
|
const exitDetail = providerExit && providerExit !== String(exitCode) ? `, provider exit ${providerExit}` : "";
|
|
12345
13036
|
spinner.fail(`Code audit exited with code ${exitCode}${statusSuffix}${exitDetail}`);
|
|
12346
|
-
const logPath =
|
|
12347
|
-
if (
|
|
12348
|
-
const logLines =
|
|
13037
|
+
const logPath = path29.join(projectDir, "logs", "audit.log");
|
|
13038
|
+
if (fs28.existsSync(logPath)) {
|
|
13039
|
+
const logLines = fs28.readFileSync(logPath, "utf-8").split("\n").filter((l) => l.trim()).slice(-8);
|
|
12349
13040
|
if (logLines.length > 0) {
|
|
12350
13041
|
process.stderr.write(logLines.join("\n") + "\n");
|
|
12351
13042
|
}
|
|
@@ -12489,16 +13180,16 @@ function analyticsCommand(program2) {
|
|
|
12489
13180
|
// src/commands/install.ts
|
|
12490
13181
|
init_dist();
|
|
12491
13182
|
import { execSync as execSync4 } from "child_process";
|
|
12492
|
-
import * as
|
|
12493
|
-
import * as
|
|
13183
|
+
import * as path30 from "path";
|
|
13184
|
+
import * as fs29 from "fs";
|
|
12494
13185
|
function shellQuote(value) {
|
|
12495
13186
|
return `'${value.replace(/'/g, `'"'"'`)}'`;
|
|
12496
13187
|
}
|
|
12497
13188
|
function getNightWatchBinPath() {
|
|
12498
13189
|
try {
|
|
12499
13190
|
const npmBin = execSync4("npm bin -g", { encoding: "utf-8" }).trim();
|
|
12500
|
-
const binPath =
|
|
12501
|
-
if (
|
|
13191
|
+
const binPath = path30.join(npmBin, "night-watch");
|
|
13192
|
+
if (fs29.existsSync(binPath)) {
|
|
12502
13193
|
return binPath;
|
|
12503
13194
|
}
|
|
12504
13195
|
} catch {
|
|
@@ -12511,17 +13202,17 @@ function getNightWatchBinPath() {
|
|
|
12511
13202
|
}
|
|
12512
13203
|
function getNodeBinDir() {
|
|
12513
13204
|
if (process.execPath && process.execPath !== "node") {
|
|
12514
|
-
return
|
|
13205
|
+
return path30.dirname(process.execPath);
|
|
12515
13206
|
}
|
|
12516
13207
|
try {
|
|
12517
13208
|
const nodePath = execSync4("which node", { encoding: "utf-8" }).trim();
|
|
12518
|
-
return
|
|
13209
|
+
return path30.dirname(nodePath);
|
|
12519
13210
|
} catch {
|
|
12520
13211
|
return "";
|
|
12521
13212
|
}
|
|
12522
13213
|
}
|
|
12523
13214
|
function buildCronPathPrefix(nodeBinDir, nightWatchBin) {
|
|
12524
|
-
const nightWatchBinDir = nightWatchBin.includes("/") || nightWatchBin.includes("\\") ?
|
|
13215
|
+
const nightWatchBinDir = nightWatchBin.includes("/") || nightWatchBin.includes("\\") ? path30.dirname(nightWatchBin) : "";
|
|
12525
13216
|
const pathParts = Array.from(
|
|
12526
13217
|
new Set([nodeBinDir, nightWatchBinDir].filter((part) => part.length > 0))
|
|
12527
13218
|
);
|
|
@@ -12537,12 +13228,12 @@ function performInstall(projectDir, config, options) {
|
|
|
12537
13228
|
const nightWatchBin = getNightWatchBinPath();
|
|
12538
13229
|
const projectName = getProjectName(projectDir);
|
|
12539
13230
|
const marker = generateMarker(projectName);
|
|
12540
|
-
const logDir =
|
|
12541
|
-
if (!
|
|
12542
|
-
|
|
13231
|
+
const logDir = path30.join(projectDir, LOG_DIR);
|
|
13232
|
+
if (!fs29.existsSync(logDir)) {
|
|
13233
|
+
fs29.mkdirSync(logDir, { recursive: true });
|
|
12543
13234
|
}
|
|
12544
|
-
const executorLog =
|
|
12545
|
-
const reviewerLog =
|
|
13235
|
+
const executorLog = path30.join(logDir, "executor.log");
|
|
13236
|
+
const reviewerLog = path30.join(logDir, "reviewer.log");
|
|
12546
13237
|
if (!options?.force) {
|
|
12547
13238
|
const existingEntries2 = Array.from(
|
|
12548
13239
|
/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)])
|
|
@@ -12579,7 +13270,7 @@ function performInstall(projectDir, config, options) {
|
|
|
12579
13270
|
const installSlicer = options?.noSlicer === true ? false : config.roadmapScanner.enabled;
|
|
12580
13271
|
if (installSlicer) {
|
|
12581
13272
|
const slicerSchedule = config.roadmapScanner.slicerSchedule;
|
|
12582
|
-
const slicerLog =
|
|
13273
|
+
const slicerLog = path30.join(logDir, "slicer.log");
|
|
12583
13274
|
const slicerEntry = `${slicerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} planner >> ${shellQuote(slicerLog)} 2>&1 ${marker}`;
|
|
12584
13275
|
entries.push(slicerEntry);
|
|
12585
13276
|
}
|
|
@@ -12587,7 +13278,7 @@ function performInstall(projectDir, config, options) {
|
|
|
12587
13278
|
const installQa = disableQa ? false : config.qa.enabled;
|
|
12588
13279
|
if (installQa) {
|
|
12589
13280
|
const qaSchedule = config.qa.schedule;
|
|
12590
|
-
const qaLog =
|
|
13281
|
+
const qaLog = path30.join(logDir, "qa.log");
|
|
12591
13282
|
const qaEntry = `${qaSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} qa >> ${shellQuote(qaLog)} 2>&1 ${marker}`;
|
|
12592
13283
|
entries.push(qaEntry);
|
|
12593
13284
|
}
|
|
@@ -12595,7 +13286,7 @@ function performInstall(projectDir, config, options) {
|
|
|
12595
13286
|
const installAudit = disableAudit ? false : config.audit.enabled;
|
|
12596
13287
|
if (installAudit) {
|
|
12597
13288
|
const auditSchedule = config.audit.schedule;
|
|
12598
|
-
const auditLog =
|
|
13289
|
+
const auditLog = path30.join(logDir, "audit.log");
|
|
12599
13290
|
const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
|
|
12600
13291
|
entries.push(auditEntry);
|
|
12601
13292
|
}
|
|
@@ -12603,7 +13294,7 @@ function performInstall(projectDir, config, options) {
|
|
|
12603
13294
|
const installAnalytics = disableAnalytics ? false : config.analytics.enabled;
|
|
12604
13295
|
if (installAnalytics) {
|
|
12605
13296
|
const analyticsSchedule = config.analytics.schedule;
|
|
12606
|
-
const analyticsLog =
|
|
13297
|
+
const analyticsLog = path30.join(logDir, "analytics.log");
|
|
12607
13298
|
const analyticsEntry = `${analyticsSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} analytics >> ${shellQuote(analyticsLog)} 2>&1 ${marker}`;
|
|
12608
13299
|
entries.push(analyticsEntry);
|
|
12609
13300
|
}
|
|
@@ -12611,7 +13302,7 @@ function performInstall(projectDir, config, options) {
|
|
|
12611
13302
|
const installPrResolver = disablePrResolver ? false : config.prResolver.enabled;
|
|
12612
13303
|
if (installPrResolver) {
|
|
12613
13304
|
const prResolverSchedule = config.prResolver.schedule;
|
|
12614
|
-
const prResolverLog =
|
|
13305
|
+
const prResolverLog = path30.join(logDir, "pr-resolver.log");
|
|
12615
13306
|
const prResolverEntry = `${prResolverSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} resolve >> ${shellQuote(prResolverLog)} 2>&1 ${marker}`;
|
|
12616
13307
|
entries.push(prResolverEntry);
|
|
12617
13308
|
}
|
|
@@ -12619,10 +13310,18 @@ function performInstall(projectDir, config, options) {
|
|
|
12619
13310
|
const installMerger = disableMerger ? false : config.merger?.enabled ?? false;
|
|
12620
13311
|
if (installMerger) {
|
|
12621
13312
|
const mergerSchedule = config.merger.schedule;
|
|
12622
|
-
const mergerLog =
|
|
13313
|
+
const mergerLog = path30.join(logDir, "merger.log");
|
|
12623
13314
|
const mergerEntry = `${mergerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} merge >> ${shellQuote(mergerLog)} 2>&1 ${marker}`;
|
|
12624
13315
|
entries.push(mergerEntry);
|
|
12625
13316
|
}
|
|
13317
|
+
const disableManager = options?.noManager === true || options?.manager === false;
|
|
13318
|
+
const installManager = disableManager ? false : config.manager?.enabled ?? false;
|
|
13319
|
+
if (installManager) {
|
|
13320
|
+
const managerSchedule = config.manager.schedule;
|
|
13321
|
+
const managerLog = path30.join(logDir, `${MANAGER_LOG_NAME}.log`);
|
|
13322
|
+
const managerEntry = `${managerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} manager >> ${shellQuote(managerLog)} 2>&1 ${marker}`;
|
|
13323
|
+
entries.push(managerEntry);
|
|
13324
|
+
}
|
|
12626
13325
|
const existingEntries = new Set(
|
|
12627
13326
|
Array.from(/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)]))
|
|
12628
13327
|
);
|
|
@@ -12641,7 +13340,7 @@ function performInstall(projectDir, config, options) {
|
|
|
12641
13340
|
}
|
|
12642
13341
|
}
|
|
12643
13342
|
function installCommand(program2) {
|
|
12644
|
-
program2.command("install").description("Add crontab entries for automated execution").option("-s, --schedule <cron>", "Cron schedule for PRD executor").option("--reviewer-schedule <cron>", "Cron schedule for reviewer").option("--no-reviewer", "Skip installing reviewer cron").option("--no-slicer", "Skip installing slicer cron").option("--no-qa", "Skip installing QA cron").option("--no-audit", "Skip installing audit cron").option("--no-analytics", "Skip installing analytics cron").option("--no-pr-resolver", "Skip installing PR resolver cron").option("--no-merger", "Skip installing merger cron").option("-f, --force", "Replace existing cron entries for this project").action(async (options) => {
|
|
13343
|
+
program2.command("install").description("Add crontab entries for automated execution").option("-s, --schedule <cron>", "Cron schedule for PRD executor").option("--reviewer-schedule <cron>", "Cron schedule for reviewer").option("--no-reviewer", "Skip installing reviewer cron").option("--no-slicer", "Skip installing slicer cron").option("--no-qa", "Skip installing QA cron").option("--no-audit", "Skip installing audit cron").option("--no-analytics", "Skip installing analytics cron").option("--no-pr-resolver", "Skip installing PR resolver cron").option("--no-merger", "Skip installing merger cron").option("--no-manager", "Skip installing manager cron").option("-f, --force", "Replace existing cron entries for this project").action(async (options) => {
|
|
12645
13344
|
try {
|
|
12646
13345
|
const projectDir = process.cwd();
|
|
12647
13346
|
const config = loadConfig(projectDir);
|
|
@@ -12650,12 +13349,12 @@ function installCommand(program2) {
|
|
|
12650
13349
|
const nightWatchBin = getNightWatchBinPath();
|
|
12651
13350
|
const projectName = getProjectName(projectDir);
|
|
12652
13351
|
const marker = generateMarker(projectName);
|
|
12653
|
-
const logDir =
|
|
12654
|
-
if (!
|
|
12655
|
-
|
|
13352
|
+
const logDir = path30.join(projectDir, LOG_DIR);
|
|
13353
|
+
if (!fs29.existsSync(logDir)) {
|
|
13354
|
+
fs29.mkdirSync(logDir, { recursive: true });
|
|
12656
13355
|
}
|
|
12657
|
-
const executorLog =
|
|
12658
|
-
const reviewerLog =
|
|
13356
|
+
const executorLog = path30.join(logDir, "executor.log");
|
|
13357
|
+
const reviewerLog = path30.join(logDir, "reviewer.log");
|
|
12659
13358
|
const existingEntries = Array.from(
|
|
12660
13359
|
/* @__PURE__ */ new Set([...getEntries(marker), ...getProjectEntries(projectDir)])
|
|
12661
13360
|
);
|
|
@@ -12691,7 +13390,7 @@ function installCommand(program2) {
|
|
|
12691
13390
|
const installSlicer = options.noSlicer === true ? false : config.roadmapScanner.enabled;
|
|
12692
13391
|
let slicerLog;
|
|
12693
13392
|
if (installSlicer) {
|
|
12694
|
-
slicerLog =
|
|
13393
|
+
slicerLog = path30.join(logDir, "slicer.log");
|
|
12695
13394
|
const slicerSchedule = config.roadmapScanner.slicerSchedule;
|
|
12696
13395
|
const slicerEntry = `${slicerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} planner >> ${shellQuote(slicerLog)} 2>&1 ${marker}`;
|
|
12697
13396
|
entries.push(slicerEntry);
|
|
@@ -12700,7 +13399,7 @@ function installCommand(program2) {
|
|
|
12700
13399
|
const installQa = disableQa ? false : config.qa.enabled;
|
|
12701
13400
|
let qaLog;
|
|
12702
13401
|
if (installQa) {
|
|
12703
|
-
qaLog =
|
|
13402
|
+
qaLog = path30.join(logDir, "qa.log");
|
|
12704
13403
|
const qaSchedule = config.qa.schedule;
|
|
12705
13404
|
const qaEntry = `${qaSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} qa >> ${shellQuote(qaLog)} 2>&1 ${marker}`;
|
|
12706
13405
|
entries.push(qaEntry);
|
|
@@ -12709,7 +13408,7 @@ function installCommand(program2) {
|
|
|
12709
13408
|
const installAudit = disableAudit ? false : config.audit.enabled;
|
|
12710
13409
|
let auditLog;
|
|
12711
13410
|
if (installAudit) {
|
|
12712
|
-
auditLog =
|
|
13411
|
+
auditLog = path30.join(logDir, "audit.log");
|
|
12713
13412
|
const auditSchedule = config.audit.schedule;
|
|
12714
13413
|
const auditEntry = `${auditSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} audit >> ${shellQuote(auditLog)} 2>&1 ${marker}`;
|
|
12715
13414
|
entries.push(auditEntry);
|
|
@@ -12718,7 +13417,7 @@ function installCommand(program2) {
|
|
|
12718
13417
|
const installAnalytics = disableAnalytics ? false : config.analytics.enabled;
|
|
12719
13418
|
let analyticsLog;
|
|
12720
13419
|
if (installAnalytics) {
|
|
12721
|
-
analyticsLog =
|
|
13420
|
+
analyticsLog = path30.join(logDir, "analytics.log");
|
|
12722
13421
|
const analyticsSchedule = config.analytics.schedule;
|
|
12723
13422
|
const analyticsEntry = `${analyticsSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} analytics >> ${shellQuote(analyticsLog)} 2>&1 ${marker}`;
|
|
12724
13423
|
entries.push(analyticsEntry);
|
|
@@ -12727,7 +13426,7 @@ function installCommand(program2) {
|
|
|
12727
13426
|
const installPrResolver = disablePrResolver ? false : config.prResolver.enabled;
|
|
12728
13427
|
let prResolverLog;
|
|
12729
13428
|
if (installPrResolver) {
|
|
12730
|
-
prResolverLog =
|
|
13429
|
+
prResolverLog = path30.join(logDir, "pr-resolver.log");
|
|
12731
13430
|
const prResolverSchedule = config.prResolver.schedule;
|
|
12732
13431
|
const prResolverEntry = `${prResolverSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} resolve >> ${shellQuote(prResolverLog)} 2>&1 ${marker}`;
|
|
12733
13432
|
entries.push(prResolverEntry);
|
|
@@ -12736,11 +13435,20 @@ function installCommand(program2) {
|
|
|
12736
13435
|
const installMerger = disableMerger ? false : config.merger?.enabled ?? false;
|
|
12737
13436
|
let mergerLog;
|
|
12738
13437
|
if (installMerger) {
|
|
12739
|
-
mergerLog =
|
|
13438
|
+
mergerLog = path30.join(logDir, "merger.log");
|
|
12740
13439
|
const mergerSchedule = config.merger.schedule;
|
|
12741
13440
|
const mergerEntry = `${mergerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} merge >> ${shellQuote(mergerLog)} 2>&1 ${marker}`;
|
|
12742
13441
|
entries.push(mergerEntry);
|
|
12743
13442
|
}
|
|
13443
|
+
const disableManager = options.noManager === true || options.manager === false;
|
|
13444
|
+
const installManager = disableManager ? false : config.manager?.enabled ?? false;
|
|
13445
|
+
let managerLog;
|
|
13446
|
+
if (installManager) {
|
|
13447
|
+
managerLog = path30.join(logDir, `${MANAGER_LOG_NAME}.log`);
|
|
13448
|
+
const managerSchedule = config.manager.schedule;
|
|
13449
|
+
const managerEntry = `${managerSchedule} ${pathPrefix}${providerEnvPrefix}${cliBinPrefix}${cronTriggerPrefix}cd ${shellQuote(projectDir)} && ${shellQuote(nightWatchBin)} manager >> ${shellQuote(managerLog)} 2>&1 ${marker}`;
|
|
13450
|
+
entries.push(managerEntry);
|
|
13451
|
+
}
|
|
12744
13452
|
const existingEntrySet = new Set(existingEntries);
|
|
12745
13453
|
const currentCrontab = readCrontab();
|
|
12746
13454
|
const baseCrontab = options.force ? currentCrontab.filter((line) => !existingEntrySet.has(line) && !line.includes(marker)) : currentCrontab;
|
|
@@ -12776,6 +13484,9 @@ function installCommand(program2) {
|
|
|
12776
13484
|
if (installMerger && mergerLog) {
|
|
12777
13485
|
dim(` Merger: ${mergerLog}`);
|
|
12778
13486
|
}
|
|
13487
|
+
if (installManager && managerLog) {
|
|
13488
|
+
dim(` Manager: ${managerLog}`);
|
|
13489
|
+
}
|
|
12779
13490
|
console.log();
|
|
12780
13491
|
dim("To uninstall, run: night-watch uninstall");
|
|
12781
13492
|
dim("To check status, run: night-watch status");
|
|
@@ -12790,8 +13501,8 @@ function installCommand(program2) {
|
|
|
12790
13501
|
|
|
12791
13502
|
// src/commands/uninstall.ts
|
|
12792
13503
|
init_dist();
|
|
12793
|
-
import * as
|
|
12794
|
-
import * as
|
|
13504
|
+
import * as path31 from "path";
|
|
13505
|
+
import * as fs30 from "fs";
|
|
12795
13506
|
function performUninstall(projectDir, options) {
|
|
12796
13507
|
try {
|
|
12797
13508
|
const projectName = getProjectName(projectDir);
|
|
@@ -12806,25 +13517,26 @@ function performUninstall(projectDir, options) {
|
|
|
12806
13517
|
const removedCount = removeEntriesForProject(projectDir, marker);
|
|
12807
13518
|
unregisterProject(projectDir);
|
|
12808
13519
|
if (!options?.keepLogs) {
|
|
12809
|
-
const logDir =
|
|
12810
|
-
if (
|
|
13520
|
+
const logDir = path31.join(projectDir, "logs");
|
|
13521
|
+
if (fs30.existsSync(logDir)) {
|
|
12811
13522
|
const logFiles = [
|
|
12812
13523
|
"executor.log",
|
|
12813
13524
|
"reviewer.log",
|
|
12814
13525
|
"slicer.log",
|
|
12815
13526
|
"audit.log",
|
|
12816
|
-
"pr-resolver.log"
|
|
13527
|
+
"pr-resolver.log",
|
|
13528
|
+
"manager.log"
|
|
12817
13529
|
];
|
|
12818
13530
|
logFiles.forEach((logFile) => {
|
|
12819
|
-
const logPath =
|
|
12820
|
-
if (
|
|
12821
|
-
|
|
13531
|
+
const logPath = path31.join(logDir, logFile);
|
|
13532
|
+
if (fs30.existsSync(logPath)) {
|
|
13533
|
+
fs30.unlinkSync(logPath);
|
|
12822
13534
|
}
|
|
12823
13535
|
});
|
|
12824
13536
|
try {
|
|
12825
|
-
const remainingFiles =
|
|
13537
|
+
const remainingFiles = fs30.readdirSync(logDir);
|
|
12826
13538
|
if (remainingFiles.length === 0) {
|
|
12827
|
-
|
|
13539
|
+
fs30.rmdirSync(logDir);
|
|
12828
13540
|
}
|
|
12829
13541
|
} catch {
|
|
12830
13542
|
}
|
|
@@ -12857,27 +13569,28 @@ function uninstallCommand(program2) {
|
|
|
12857
13569
|
existingEntries.forEach((entry) => dim(` ${entry}`));
|
|
12858
13570
|
const removedCount = removeEntriesForProject(projectDir, marker);
|
|
12859
13571
|
if (!options.keepLogs) {
|
|
12860
|
-
const logDir =
|
|
12861
|
-
if (
|
|
13572
|
+
const logDir = path31.join(projectDir, "logs");
|
|
13573
|
+
if (fs30.existsSync(logDir)) {
|
|
12862
13574
|
const logFiles = [
|
|
12863
13575
|
"executor.log",
|
|
12864
13576
|
"reviewer.log",
|
|
12865
13577
|
"slicer.log",
|
|
12866
13578
|
"audit.log",
|
|
12867
|
-
"pr-resolver.log"
|
|
13579
|
+
"pr-resolver.log",
|
|
13580
|
+
"manager.log"
|
|
12868
13581
|
];
|
|
12869
13582
|
let logsRemoved = 0;
|
|
12870
13583
|
logFiles.forEach((logFile) => {
|
|
12871
|
-
const logPath =
|
|
12872
|
-
if (
|
|
12873
|
-
|
|
13584
|
+
const logPath = path31.join(logDir, logFile);
|
|
13585
|
+
if (fs30.existsSync(logPath)) {
|
|
13586
|
+
fs30.unlinkSync(logPath);
|
|
12874
13587
|
logsRemoved++;
|
|
12875
13588
|
}
|
|
12876
13589
|
});
|
|
12877
13590
|
try {
|
|
12878
|
-
const remainingFiles =
|
|
13591
|
+
const remainingFiles = fs30.readdirSync(logDir);
|
|
12879
13592
|
if (remainingFiles.length === 0) {
|
|
12880
|
-
|
|
13593
|
+
fs30.rmdirSync(logDir);
|
|
12881
13594
|
}
|
|
12882
13595
|
} catch {
|
|
12883
13596
|
}
|
|
@@ -13163,14 +13876,14 @@ function statusCommand(program2) {
|
|
|
13163
13876
|
// src/commands/logs.ts
|
|
13164
13877
|
init_dist();
|
|
13165
13878
|
import { spawn as spawn3 } from "child_process";
|
|
13166
|
-
import * as
|
|
13167
|
-
import * as
|
|
13879
|
+
import * as path32 from "path";
|
|
13880
|
+
import * as fs31 from "fs";
|
|
13168
13881
|
function getLastLines(filePath, lineCount) {
|
|
13169
|
-
if (!
|
|
13882
|
+
if (!fs31.existsSync(filePath)) {
|
|
13170
13883
|
return `Log file not found: ${filePath}`;
|
|
13171
13884
|
}
|
|
13172
13885
|
try {
|
|
13173
|
-
const content =
|
|
13886
|
+
const content = fs31.readFileSync(filePath, "utf-8");
|
|
13174
13887
|
const lines = content.trim().split("\n");
|
|
13175
13888
|
return lines.slice(-lineCount).join("\n");
|
|
13176
13889
|
} catch (error2) {
|
|
@@ -13178,7 +13891,7 @@ function getLastLines(filePath, lineCount) {
|
|
|
13178
13891
|
}
|
|
13179
13892
|
}
|
|
13180
13893
|
function followLog(filePath) {
|
|
13181
|
-
if (!
|
|
13894
|
+
if (!fs31.existsSync(filePath)) {
|
|
13182
13895
|
console.log(`Log file not found: ${filePath}`);
|
|
13183
13896
|
console.log("The log file will be created when the first execution runs.");
|
|
13184
13897
|
return;
|
|
@@ -13197,20 +13910,21 @@ function followLog(filePath) {
|
|
|
13197
13910
|
function logsCommand(program2) {
|
|
13198
13911
|
program2.command("logs").description("View night-watch log output").option("-n, --lines <count>", "Number of lines to show", "50").option("-f, --follow", "Follow log output (tail -f)").option(
|
|
13199
13912
|
"-t, --type <type>",
|
|
13200
|
-
"Log type to view (executor|reviewer|qa|audit|planner|analytics|merger|all)",
|
|
13913
|
+
"Log type to view (executor|reviewer|qa|audit|planner|analytics|merger|manager|all)",
|
|
13201
13914
|
"all"
|
|
13202
13915
|
).action(async (options) => {
|
|
13203
13916
|
try {
|
|
13204
13917
|
const projectDir = process.cwd();
|
|
13205
|
-
const logDir =
|
|
13918
|
+
const logDir = path32.join(projectDir, LOG_DIR);
|
|
13206
13919
|
const lineCount = parseInt(options.lines || "50", 10);
|
|
13207
|
-
const executorLog =
|
|
13208
|
-
const reviewerLog =
|
|
13209
|
-
const qaLog =
|
|
13210
|
-
const auditLog =
|
|
13211
|
-
const plannerLog =
|
|
13212
|
-
const analyticsLog =
|
|
13213
|
-
const mergerLog =
|
|
13920
|
+
const executorLog = path32.join(logDir, EXECUTOR_LOG_FILE);
|
|
13921
|
+
const reviewerLog = path32.join(logDir, REVIEWER_LOG_FILE);
|
|
13922
|
+
const qaLog = path32.join(logDir, `${QA_LOG_NAME}.log`);
|
|
13923
|
+
const auditLog = path32.join(logDir, `${AUDIT_LOG_NAME}.log`);
|
|
13924
|
+
const plannerLog = path32.join(logDir, `${PLANNER_LOG_NAME}.log`);
|
|
13925
|
+
const analyticsLog = path32.join(logDir, `${ANALYTICS_LOG_NAME}.log`);
|
|
13926
|
+
const mergerLog = path32.join(logDir, `${MERGER_LOG_NAME}.log`);
|
|
13927
|
+
const managerLog = path32.join(logDir, `${MANAGER_LOG_NAME}.log`);
|
|
13214
13928
|
const logType = options.type?.toLowerCase() || "all";
|
|
13215
13929
|
const showExecutor = logType === "all" || logType === "run" || logType === "executor";
|
|
13216
13930
|
const showReviewer = logType === "all" || logType === "review" || logType === "reviewer";
|
|
@@ -13219,10 +13933,11 @@ function logsCommand(program2) {
|
|
|
13219
13933
|
const showPlanner = logType === "all" || logType === "planner" || logType === "slice" || logType === "slicer";
|
|
13220
13934
|
const showAnalytics = logType === "all" || logType === "analytics";
|
|
13221
13935
|
const showMerger = logType === "all" || logType === "merge" || logType === "merger";
|
|
13936
|
+
const showManager = logType === "all" || logType === "manager";
|
|
13222
13937
|
if (options.follow) {
|
|
13223
13938
|
if (logType === "all") {
|
|
13224
13939
|
dim("Note: Following all logs is not supported. Showing executor log.");
|
|
13225
|
-
dim("Use --type reviewer|qa|audit|planner|analytics|merger for other logs.\n");
|
|
13940
|
+
dim("Use --type reviewer|qa|audit|planner|analytics|merger|manager for other logs.\n");
|
|
13226
13941
|
}
|
|
13227
13942
|
let targetLog = executorLog;
|
|
13228
13943
|
if (showReviewer) targetLog = reviewerLog;
|
|
@@ -13231,6 +13946,7 @@ function logsCommand(program2) {
|
|
|
13231
13946
|
else if (showPlanner) targetLog = plannerLog;
|
|
13232
13947
|
else if (showAnalytics) targetLog = analyticsLog;
|
|
13233
13948
|
else if (showMerger) targetLog = mergerLog;
|
|
13949
|
+
else if (showManager) targetLog = managerLog;
|
|
13234
13950
|
followLog(targetLog);
|
|
13235
13951
|
return;
|
|
13236
13952
|
}
|
|
@@ -13277,11 +13993,17 @@ function logsCommand(program2) {
|
|
|
13277
13993
|
console.log();
|
|
13278
13994
|
console.log(getLastLines(mergerLog, lineCount));
|
|
13279
13995
|
}
|
|
13996
|
+
if (showManager) {
|
|
13997
|
+
header("Manager Log");
|
|
13998
|
+
dim(`File: ${managerLog}`);
|
|
13999
|
+
console.log();
|
|
14000
|
+
console.log(getLastLines(managerLog, lineCount));
|
|
14001
|
+
}
|
|
13280
14002
|
console.log();
|
|
13281
14003
|
dim("---");
|
|
13282
14004
|
dim("Tip: Use -f to follow logs in real-time");
|
|
13283
14005
|
dim(
|
|
13284
|
-
" Use --type executor|reviewer|qa|audit|planner|analytics|merger to view specific logs"
|
|
14006
|
+
" Use --type executor|reviewer|qa|audit|planner|analytics|merger|manager to view specific logs"
|
|
13285
14007
|
);
|
|
13286
14008
|
} catch (err) {
|
|
13287
14009
|
console.error(`Error reading logs: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -13293,30 +14015,30 @@ function logsCommand(program2) {
|
|
|
13293
14015
|
// src/commands/prd.ts
|
|
13294
14016
|
init_dist();
|
|
13295
14017
|
import { execSync as execSync5, spawn as spawn4, spawnSync } from "child_process";
|
|
13296
|
-
import * as
|
|
13297
|
-
import * as
|
|
14018
|
+
import * as fs32 from "fs";
|
|
14019
|
+
import * as path33 from "path";
|
|
13298
14020
|
import { fileURLToPath as fileURLToPath4 } from "url";
|
|
13299
|
-
import { dirname as
|
|
14021
|
+
import { dirname as dirname10 } from "path";
|
|
13300
14022
|
var __filename3 = fileURLToPath4(import.meta.url);
|
|
13301
|
-
var __dirname3 =
|
|
14023
|
+
var __dirname3 = dirname10(__filename3);
|
|
13302
14024
|
function findTemplatesDir2(startDir) {
|
|
13303
14025
|
let current = startDir;
|
|
13304
14026
|
for (let i = 0; i < 8; i++) {
|
|
13305
|
-
const candidate =
|
|
13306
|
-
if (
|
|
14027
|
+
const candidate = path33.join(current, "templates");
|
|
14028
|
+
if (fs32.existsSync(candidate) && fs32.statSync(candidate).isDirectory()) {
|
|
13307
14029
|
return candidate;
|
|
13308
14030
|
}
|
|
13309
|
-
current =
|
|
14031
|
+
current = path33.dirname(current);
|
|
13310
14032
|
}
|
|
13311
|
-
return
|
|
14033
|
+
return path33.join(startDir, "templates");
|
|
13312
14034
|
}
|
|
13313
14035
|
var TEMPLATES_DIR2 = findTemplatesDir2(__dirname3);
|
|
13314
14036
|
function slugify2(name) {
|
|
13315
14037
|
return name.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
13316
14038
|
}
|
|
13317
14039
|
function getNextPrdNumber2(prdDir) {
|
|
13318
|
-
if (!
|
|
13319
|
-
const files =
|
|
14040
|
+
if (!fs32.existsSync(prdDir)) return 1;
|
|
14041
|
+
const files = fs32.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
|
|
13320
14042
|
const numbers = files.map((f) => {
|
|
13321
14043
|
const match = f.match(/^(\d+)-/);
|
|
13322
14044
|
return match ? parseInt(match[1], 10) : 0;
|
|
@@ -13397,13 +14119,13 @@ function resolveGitHubBlobUrl(projectDir, relPath) {
|
|
|
13397
14119
|
return null;
|
|
13398
14120
|
}
|
|
13399
14121
|
const ref = branch && branch !== "HEAD" ? branch : "main";
|
|
13400
|
-
return `${httpsBase}/blob/${encodeURIComponent(ref).replace(/%2F/g, "/")}/${relPath.split(
|
|
14122
|
+
return `${httpsBase}/blob/${encodeURIComponent(ref).replace(/%2F/g, "/")}/${relPath.split(path33.sep).map((segment) => encodeURIComponent(segment)).join("/")}`;
|
|
13401
14123
|
} catch {
|
|
13402
14124
|
return null;
|
|
13403
14125
|
}
|
|
13404
14126
|
}
|
|
13405
14127
|
function buildGithubIssueBody(prdPath, projectDir, prdContent) {
|
|
13406
|
-
const relPath =
|
|
14128
|
+
const relPath = path33.relative(projectDir, prdPath);
|
|
13407
14129
|
const blobUrl = resolveGitHubBlobUrl(projectDir, relPath);
|
|
13408
14130
|
const fileLine = blobUrl ? `PRD file: [\`${relPath}\`](${blobUrl})` : `PRD file: \`${relPath}\``;
|
|
13409
14131
|
return `${fileLine}
|
|
@@ -13414,17 +14136,17 @@ ${prdContent}
|
|
|
13414
14136
|
Created via \`night-watch prd create\`.`;
|
|
13415
14137
|
}
|
|
13416
14138
|
async function generatePrdWithClaude(description, projectDir, model) {
|
|
13417
|
-
const bundledTemplatePath =
|
|
13418
|
-
const installedTemplatePath =
|
|
13419
|
-
const templatePath =
|
|
13420
|
-
if (!
|
|
14139
|
+
const bundledTemplatePath = path33.join(TEMPLATES_DIR2, "prd-creator.md");
|
|
14140
|
+
const installedTemplatePath = path33.join(projectDir, "instructions", "prd-creator.md");
|
|
14141
|
+
const templatePath = fs32.existsSync(installedTemplatePath) ? installedTemplatePath : bundledTemplatePath;
|
|
14142
|
+
if (!fs32.existsSync(templatePath)) {
|
|
13421
14143
|
return null;
|
|
13422
14144
|
}
|
|
13423
|
-
const planningPrinciples =
|
|
14145
|
+
const planningPrinciples = fs32.readFileSync(templatePath, "utf-8");
|
|
13424
14146
|
const prompt = buildPrdPrompt(description, projectDir, planningPrinciples);
|
|
13425
14147
|
const modelId = model ?? CLAUDE_MODEL_IDS.opus;
|
|
13426
14148
|
const env = buildNativeClaudeEnv(process.env);
|
|
13427
|
-
return await new Promise((
|
|
14149
|
+
return await new Promise((resolve11) => {
|
|
13428
14150
|
const child = spawn4(
|
|
13429
14151
|
"claude",
|
|
13430
14152
|
[
|
|
@@ -13477,9 +14199,9 @@ async function generatePrdWithClaude(description, projectDir, model) {
|
|
|
13477
14199
|
}
|
|
13478
14200
|
}
|
|
13479
14201
|
process.stdout.write("\n");
|
|
13480
|
-
|
|
14202
|
+
resolve11(code === 0 && finalResult ? extractPrdMarkdown(finalResult) : null);
|
|
13481
14203
|
});
|
|
13482
|
-
child.on("error", () =>
|
|
14204
|
+
child.on("error", () => resolve11(null));
|
|
13483
14205
|
});
|
|
13484
14206
|
}
|
|
13485
14207
|
function runGh(args, cwd) {
|
|
@@ -13488,17 +14210,17 @@ function runGh(args, cwd) {
|
|
|
13488
14210
|
return null;
|
|
13489
14211
|
}
|
|
13490
14212
|
function createGithubIssue(title, prdPath, projectDir, prdContent) {
|
|
13491
|
-
const tmpFile =
|
|
14213
|
+
const tmpFile = path33.join(projectDir, `.prd-issue-body-${Date.now()}.tmp`);
|
|
13492
14214
|
try {
|
|
13493
14215
|
const body = buildGithubIssueBody(prdPath, projectDir, prdContent);
|
|
13494
|
-
|
|
14216
|
+
fs32.writeFileSync(tmpFile, body, "utf-8");
|
|
13495
14217
|
const baseArgs = ["issue", "create", "--title", `PRD: ${title}`, "--body-file", tmpFile];
|
|
13496
14218
|
return runGh([...baseArgs, "--label", "prd"], projectDir) ?? runGh(baseArgs, projectDir);
|
|
13497
14219
|
} catch {
|
|
13498
14220
|
return null;
|
|
13499
14221
|
} finally {
|
|
13500
14222
|
try {
|
|
13501
|
-
|
|
14223
|
+
fs32.unlinkSync(tmpFile);
|
|
13502
14224
|
} catch {
|
|
13503
14225
|
}
|
|
13504
14226
|
}
|
|
@@ -13509,14 +14231,15 @@ function parseDependencies(content) {
|
|
|
13509
14231
|
return match[1].split(",").map((d) => d.replace(/`/g, "").trim()).filter(Boolean);
|
|
13510
14232
|
}
|
|
13511
14233
|
function isClaimActive(claimPath, maxRuntime) {
|
|
14234
|
+
const claimStaleAfter = maxRuntime > 0 ? maxRuntime : 14400;
|
|
13512
14235
|
try {
|
|
13513
|
-
if (!
|
|
14236
|
+
if (!fs32.existsSync(claimPath)) {
|
|
13514
14237
|
return { active: false };
|
|
13515
14238
|
}
|
|
13516
|
-
const content =
|
|
14239
|
+
const content = fs32.readFileSync(claimPath, "utf-8");
|
|
13517
14240
|
const claim = JSON.parse(content);
|
|
13518
14241
|
const age = Math.floor(Date.now() / 1e3) - claim.timestamp;
|
|
13519
|
-
if (age <
|
|
14242
|
+
if (age < claimStaleAfter) {
|
|
13520
14243
|
return { active: true, hostname: claim.hostname, pid: claim.pid };
|
|
13521
14244
|
}
|
|
13522
14245
|
return { active: false };
|
|
@@ -13528,14 +14251,16 @@ function prdCommand(program2) {
|
|
|
13528
14251
|
const prd = program2.command("prd").description("Manage PRD files");
|
|
13529
14252
|
prd.command("create").description("Generate a new PRD markdown file using Claude").argument("<name>", "PRD description").option("--number", "Add auto-numbering prefix to the filename", false).option("--model <model>", "Claude model to use (e.g. sonnet, opus, or a full model ID)").action(async (name, options) => {
|
|
13530
14253
|
const projectDir = process.cwd();
|
|
13531
|
-
const prdDir =
|
|
13532
|
-
if (!
|
|
13533
|
-
|
|
14254
|
+
const prdDir = path33.join(projectDir, resolvePrdCreateDir());
|
|
14255
|
+
if (!fs32.existsSync(prdDir)) {
|
|
14256
|
+
fs32.mkdirSync(prdDir, { recursive: true });
|
|
13534
14257
|
}
|
|
13535
14258
|
const resolvedModel = options.model ? CLAUDE_MODEL_IDS[options.model] ?? options.model : void 0;
|
|
13536
14259
|
const modelLabel = resolvedModel ?? CLAUDE_MODEL_IDS.opus;
|
|
13537
|
-
dim(
|
|
13538
|
-
`)
|
|
14260
|
+
dim(
|
|
14261
|
+
`Calling Claude (${modelLabel}) to generate the PRD. It can take several minutes, please hang on!
|
|
14262
|
+
`
|
|
14263
|
+
);
|
|
13539
14264
|
const generated = await generatePrdWithClaude(name, projectDir, resolvedModel);
|
|
13540
14265
|
if (!generated) {
|
|
13541
14266
|
error("Claude generation failed. Is the provider configured and available?");
|
|
@@ -13544,13 +14269,13 @@ function prdCommand(program2) {
|
|
|
13544
14269
|
const prdTitle = extractPrdTitle(generated) ?? name;
|
|
13545
14270
|
const slug = slugify2(prdTitle);
|
|
13546
14271
|
const filename = options.number ? `${String(getNextPrdNumber2(prdDir)).padStart(2, "0")}-${slug}.md` : `${slug}.md`;
|
|
13547
|
-
const filePath =
|
|
13548
|
-
if (
|
|
14272
|
+
const filePath = path33.join(prdDir, filename);
|
|
14273
|
+
if (fs32.existsSync(filePath)) {
|
|
13549
14274
|
error(`File already exists: ${filePath}`);
|
|
13550
14275
|
dim("Use a different name or remove the existing file.");
|
|
13551
14276
|
process.exit(1);
|
|
13552
14277
|
}
|
|
13553
|
-
|
|
14278
|
+
fs32.writeFileSync(filePath, generated, "utf-8");
|
|
13554
14279
|
header("PRD Created");
|
|
13555
14280
|
success(`Created: ${filePath}`);
|
|
13556
14281
|
const issueUrl = createGithubIssue(prdTitle, filePath, projectDir, generated);
|
|
@@ -13563,15 +14288,15 @@ function prdCommand(program2) {
|
|
|
13563
14288
|
prd.command("list").description("List all PRDs with status").option("--json", "Output as JSON").action(async (options) => {
|
|
13564
14289
|
const projectDir = process.cwd();
|
|
13565
14290
|
const config = loadConfig(projectDir);
|
|
13566
|
-
const absolutePrdDir =
|
|
13567
|
-
const doneDir =
|
|
14291
|
+
const absolutePrdDir = path33.join(projectDir, config.prdDir);
|
|
14292
|
+
const doneDir = path33.join(absolutePrdDir, "done");
|
|
13568
14293
|
const pending = [];
|
|
13569
|
-
if (
|
|
13570
|
-
const files =
|
|
14294
|
+
if (fs32.existsSync(absolutePrdDir)) {
|
|
14295
|
+
const files = fs32.readdirSync(absolutePrdDir).filter((f) => f.endsWith(".md"));
|
|
13571
14296
|
for (const file of files) {
|
|
13572
|
-
const content =
|
|
14297
|
+
const content = fs32.readFileSync(path33.join(absolutePrdDir, file), "utf-8");
|
|
13573
14298
|
const deps = parseDependencies(content);
|
|
13574
|
-
const claimPath =
|
|
14299
|
+
const claimPath = path33.join(absolutePrdDir, file + CLAIM_FILE_EXTENSION);
|
|
13575
14300
|
const claimStatus = isClaimActive(claimPath, config.maxRuntime);
|
|
13576
14301
|
pending.push({
|
|
13577
14302
|
name: file,
|
|
@@ -13582,10 +14307,10 @@ function prdCommand(program2) {
|
|
|
13582
14307
|
}
|
|
13583
14308
|
}
|
|
13584
14309
|
const done = [];
|
|
13585
|
-
if (
|
|
13586
|
-
const files =
|
|
14310
|
+
if (fs32.existsSync(doneDir)) {
|
|
14311
|
+
const files = fs32.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
|
|
13587
14312
|
for (const file of files) {
|
|
13588
|
-
const content =
|
|
14313
|
+
const content = fs32.readFileSync(path33.join(doneDir, file), "utf-8");
|
|
13589
14314
|
const deps = parseDependencies(content);
|
|
13590
14315
|
done.push({ name: file, dependencies: deps });
|
|
13591
14316
|
}
|
|
@@ -13622,7 +14347,7 @@ import blessed6 from "blessed";
|
|
|
13622
14347
|
// src/commands/dashboard/tab-status.ts
|
|
13623
14348
|
init_dist();
|
|
13624
14349
|
import blessed from "blessed";
|
|
13625
|
-
import * as
|
|
14350
|
+
import * as fs33 from "fs";
|
|
13626
14351
|
function sortPrdsByPriority(prds, priority) {
|
|
13627
14352
|
if (priority.length === 0) return prds;
|
|
13628
14353
|
const priorityMap = /* @__PURE__ */ new Map();
|
|
@@ -13718,7 +14443,7 @@ function renderLogPane(projectDir, logs) {
|
|
|
13718
14443
|
let newestMtime = 0;
|
|
13719
14444
|
for (const log of existingLogs) {
|
|
13720
14445
|
try {
|
|
13721
|
-
const stat =
|
|
14446
|
+
const stat = fs33.statSync(log.path);
|
|
13722
14447
|
if (stat.mtimeMs > newestMtime) {
|
|
13723
14448
|
newestMtime = stat.mtimeMs;
|
|
13724
14449
|
newestLog = log;
|
|
@@ -14051,7 +14776,7 @@ var CONFIG_FIELDS = [
|
|
|
14051
14776
|
type: "number",
|
|
14052
14777
|
validate: (v) => {
|
|
14053
14778
|
const n = parseInt(v, 10);
|
|
14054
|
-
return isNaN(n) || n
|
|
14779
|
+
return isNaN(n) || n < 0 ? "Must be 0 (no timeout) or a positive integer" : null;
|
|
14055
14780
|
}
|
|
14056
14781
|
},
|
|
14057
14782
|
{
|
|
@@ -14060,7 +14785,7 @@ var CONFIG_FIELDS = [
|
|
|
14060
14785
|
type: "number",
|
|
14061
14786
|
validate: (v) => {
|
|
14062
14787
|
const n = parseInt(v, 10);
|
|
14063
|
-
return isNaN(n) || n
|
|
14788
|
+
return isNaN(n) || n < 0 ? "Must be 0 (no timeout) or a positive integer" : null;
|
|
14064
14789
|
}
|
|
14065
14790
|
},
|
|
14066
14791
|
{
|
|
@@ -14894,6 +15619,7 @@ function createSchedulesTab() {
|
|
|
14894
15619
|
const { config } = ctx;
|
|
14895
15620
|
const executorHuman = cronToHuman(config.cronSchedule);
|
|
14896
15621
|
const reviewerHuman = cronToHuman(config.reviewerSchedule);
|
|
15622
|
+
const managerHuman = cronToHuman(config.manager.schedule);
|
|
14897
15623
|
const lines = [
|
|
14898
15624
|
`{bold}Executor Schedule:{/bold} ${config.cronSchedule}`,
|
|
14899
15625
|
` ${executorHuman}`,
|
|
@@ -14901,14 +15627,19 @@ function createSchedulesTab() {
|
|
|
14901
15627
|
`{bold}Reviewer Schedule:{/bold} ${config.reviewerSchedule}`,
|
|
14902
15628
|
` ${reviewerHuman}`,
|
|
14903
15629
|
"",
|
|
15630
|
+
`{bold}Manager Schedule:{/bold} ${config.manager.schedule}`,
|
|
15631
|
+
` ${managerHuman}`,
|
|
15632
|
+
"",
|
|
14904
15633
|
`{bold}Reviewer Enabled:{/bold} ${config.reviewerEnabled ? "{green-fg}Yes{/green-fg}" : "{red-fg}No{/red-fg}"}`,
|
|
15634
|
+
`{bold}Manager Enabled:{/bold} ${config.manager.enabled ? "{green-fg}Yes{/green-fg}" : "{red-fg}No{/red-fg}"}`,
|
|
14905
15635
|
"",
|
|
14906
|
-
"{#888888-fg}Keys: e:Edit Executor v:Edit Reviewer i:Install x:Uninstall R:Reinstall{/#888888-fg}"
|
|
15636
|
+
"{#888888-fg}Keys: e:Edit Executor v:Edit Reviewer m:Edit Manager i:Install x:Uninstall R:Reinstall{/#888888-fg}"
|
|
14907
15637
|
];
|
|
14908
15638
|
scheduleBox.setContent(lines.join("\n"));
|
|
14909
15639
|
}
|
|
14910
15640
|
function applySchedule(ctx, field, cronExpr) {
|
|
14911
|
-
const
|
|
15641
|
+
const patch = field === "manager.schedule" ? { manager: { ...ctx.config.manager, schedule: cronExpr } } : { [field]: cronExpr };
|
|
15642
|
+
const result = saveConfig(ctx.projectDir, patch);
|
|
14912
15643
|
if (!result.success) {
|
|
14913
15644
|
ctx.showMessage(`Save failed: ${result.error}`, "error");
|
|
14914
15645
|
return;
|
|
@@ -14933,7 +15664,7 @@ function createSchedulesTab() {
|
|
|
14933
15664
|
});
|
|
14934
15665
|
}
|
|
14935
15666
|
function showCustomCronInput(ctx, field, label2) {
|
|
14936
|
-
const currentValue = ctx.config[field];
|
|
15667
|
+
const currentValue = field === "manager.schedule" ? ctx.config.manager.schedule : ctx.config[field];
|
|
14937
15668
|
const inputBox = blessed3.textbox({
|
|
14938
15669
|
top: "center",
|
|
14939
15670
|
left: "center",
|
|
@@ -14987,7 +15718,7 @@ function createSchedulesTab() {
|
|
|
14987
15718
|
interactive: true
|
|
14988
15719
|
});
|
|
14989
15720
|
selectorList.setItems(presetItems);
|
|
14990
|
-
const currentCron = ctx.config[field];
|
|
15721
|
+
const currentCron = field === "manager.schedule" ? ctx.config.manager.schedule : ctx.config[field];
|
|
14991
15722
|
const matchIdx = SCHEDULE_PRESETS.findIndex((p) => p.cron === currentCron);
|
|
14992
15723
|
if (matchIdx >= 0) {
|
|
14993
15724
|
selectorList.select(matchIdx);
|
|
@@ -15019,6 +15750,7 @@ function createSchedulesTab() {
|
|
|
15019
15750
|
const handlers = [
|
|
15020
15751
|
[["e"], () => editSchedule(ctx, "cronSchedule", "Executor Schedule")],
|
|
15021
15752
|
[["v"], () => editSchedule(ctx, "reviewerSchedule", "Reviewer Schedule")],
|
|
15753
|
+
[["m"], () => editSchedule(ctx, "manager.schedule", "Manager Schedule")],
|
|
15022
15754
|
[
|
|
15023
15755
|
["i"],
|
|
15024
15756
|
() => {
|
|
@@ -15092,7 +15824,7 @@ function createSchedulesTab() {
|
|
|
15092
15824
|
name: "Schedules",
|
|
15093
15825
|
container: container2,
|
|
15094
15826
|
activate(ctx) {
|
|
15095
|
-
ctx.setFooter(" e:Executor v:Reviewer i:Install x:Uninstall R:Reinstall q:Quit");
|
|
15827
|
+
ctx.setFooter(" e:Executor v:Reviewer m:Manager i:Install x:Uninstall R:Reinstall q:Quit");
|
|
15096
15828
|
renderCrontab(ctx);
|
|
15097
15829
|
renderScheduleSettings(ctx);
|
|
15098
15830
|
activeCtx = ctx;
|
|
@@ -15364,8 +16096,8 @@ function createActionsTab() {
|
|
|
15364
16096
|
// src/commands/dashboard/tab-logs.ts
|
|
15365
16097
|
init_dist();
|
|
15366
16098
|
import blessed5 from "blessed";
|
|
15367
|
-
import * as
|
|
15368
|
-
import * as
|
|
16099
|
+
import * as fs34 from "fs";
|
|
16100
|
+
import * as path34 from "path";
|
|
15369
16101
|
var LOG_NAMES = ["executor", "reviewer"];
|
|
15370
16102
|
var LOG_LINES = 200;
|
|
15371
16103
|
function createLogsTab() {
|
|
@@ -15406,7 +16138,7 @@ function createLogsTab() {
|
|
|
15406
16138
|
let activeKeyHandlers = [];
|
|
15407
16139
|
let activeCtx = null;
|
|
15408
16140
|
function getLogPath(projectDir, logName) {
|
|
15409
|
-
return
|
|
16141
|
+
return path34.join(projectDir, "logs", `${logName}.log`);
|
|
15410
16142
|
}
|
|
15411
16143
|
function updateSelector() {
|
|
15412
16144
|
const tabs = LOG_NAMES.map((name, idx) => {
|
|
@@ -15420,7 +16152,7 @@ function createLogsTab() {
|
|
|
15420
16152
|
function loadLog(ctx) {
|
|
15421
16153
|
const logName = LOG_NAMES[selectedLogIndex];
|
|
15422
16154
|
const logPath = getLogPath(ctx.projectDir, logName);
|
|
15423
|
-
if (!
|
|
16155
|
+
if (!fs34.existsSync(logPath)) {
|
|
15424
16156
|
logContent.setContent(
|
|
15425
16157
|
`{yellow-fg}No ${logName}.log file found{/yellow-fg}
|
|
15426
16158
|
|
|
@@ -15430,7 +16162,7 @@ Log will appear here once the ${logName} runs.`
|
|
|
15430
16162
|
return;
|
|
15431
16163
|
}
|
|
15432
16164
|
try {
|
|
15433
|
-
const stat =
|
|
16165
|
+
const stat = fs34.statSync(logPath);
|
|
15434
16166
|
const sizeKB = (stat.size / 1024).toFixed(1);
|
|
15435
16167
|
logContent.setLabel(`[ ${logName}.log - ${sizeKB} KB ]`);
|
|
15436
16168
|
} catch {
|
|
@@ -15880,13 +16612,13 @@ function doctorCommand(program2) {
|
|
|
15880
16612
|
|
|
15881
16613
|
// src/commands/serve.ts
|
|
15882
16614
|
init_dist();
|
|
15883
|
-
import * as
|
|
16615
|
+
import * as fs39 from "fs";
|
|
15884
16616
|
|
|
15885
16617
|
// ../server/dist/index.js
|
|
15886
16618
|
init_dist();
|
|
15887
|
-
import * as
|
|
15888
|
-
import * as
|
|
15889
|
-
import { dirname as
|
|
16619
|
+
import * as fs38 from "fs";
|
|
16620
|
+
import * as path40 from "path";
|
|
16621
|
+
import { dirname as dirname12 } from "path";
|
|
15890
16622
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
15891
16623
|
import cors from "cors";
|
|
15892
16624
|
import express from "express";
|
|
@@ -15970,8 +16702,8 @@ function setupGracefulShutdown(server, beforeClose) {
|
|
|
15970
16702
|
|
|
15971
16703
|
// ../server/dist/middleware/project-resolver.middleware.js
|
|
15972
16704
|
init_dist();
|
|
15973
|
-
import * as
|
|
15974
|
-
import * as
|
|
16705
|
+
import * as fs35 from "fs";
|
|
16706
|
+
import * as path35 from "path";
|
|
15975
16707
|
function resolveProject(req, res, next) {
|
|
15976
16708
|
const projectId = req.params.projectId;
|
|
15977
16709
|
const decodedId = decodeURIComponent(projectId).replace(/~/g, "/");
|
|
@@ -15981,7 +16713,7 @@ function resolveProject(req, res, next) {
|
|
|
15981
16713
|
res.status(404).json({ error: `Project not found: ${decodedId}` });
|
|
15982
16714
|
return;
|
|
15983
16715
|
}
|
|
15984
|
-
if (!
|
|
16716
|
+
if (!fs35.existsSync(entry.path) || !fs35.existsSync(path35.join(entry.path, CONFIG_FILE_NAME))) {
|
|
15985
16717
|
res.status(404).json({ error: `Project path invalid or missing config: ${entry.path}` });
|
|
15986
16718
|
return;
|
|
15987
16719
|
}
|
|
@@ -16026,8 +16758,8 @@ function startSseStatusWatcher(clients, projectDir, getConfig) {
|
|
|
16026
16758
|
|
|
16027
16759
|
// ../server/dist/routes/action.routes.js
|
|
16028
16760
|
init_dist();
|
|
16029
|
-
import * as
|
|
16030
|
-
import * as
|
|
16761
|
+
import * as fs36 from "fs";
|
|
16762
|
+
import * as path36 from "path";
|
|
16031
16763
|
import { execSync as execSync6, spawn as spawn6 } from "child_process";
|
|
16032
16764
|
import { Router } from "express";
|
|
16033
16765
|
|
|
@@ -16065,17 +16797,17 @@ function getBoardProvider(config, projectDir) {
|
|
|
16065
16797
|
function cleanOrphanedClaims(dir) {
|
|
16066
16798
|
let entries;
|
|
16067
16799
|
try {
|
|
16068
|
-
entries =
|
|
16800
|
+
entries = fs36.readdirSync(dir, { withFileTypes: true });
|
|
16069
16801
|
} catch {
|
|
16070
16802
|
return;
|
|
16071
16803
|
}
|
|
16072
16804
|
for (const entry of entries) {
|
|
16073
|
-
const fullPath =
|
|
16805
|
+
const fullPath = path36.join(dir, entry.name);
|
|
16074
16806
|
if (entry.isDirectory() && entry.name !== "done") {
|
|
16075
16807
|
cleanOrphanedClaims(fullPath);
|
|
16076
16808
|
} else if (entry.name.endsWith(CLAIM_FILE_EXTENSION)) {
|
|
16077
16809
|
try {
|
|
16078
|
-
|
|
16810
|
+
fs36.unlinkSync(fullPath);
|
|
16079
16811
|
} catch {
|
|
16080
16812
|
}
|
|
16081
16813
|
}
|
|
@@ -16230,19 +16962,19 @@ function createActionRouteHandlers(ctx) {
|
|
|
16230
16962
|
res.status(400).json({ error: "Invalid PRD name" });
|
|
16231
16963
|
return;
|
|
16232
16964
|
}
|
|
16233
|
-
const prdDir =
|
|
16965
|
+
const prdDir = path36.join(projectDir, config.prdDir);
|
|
16234
16966
|
const normalized = prdName.endsWith(".md") ? prdName : `${prdName}.md`;
|
|
16235
|
-
const pendingPath =
|
|
16236
|
-
const donePath =
|
|
16237
|
-
if (
|
|
16967
|
+
const pendingPath = path36.join(prdDir, normalized);
|
|
16968
|
+
const donePath = path36.join(prdDir, "done", normalized);
|
|
16969
|
+
if (fs36.existsSync(pendingPath)) {
|
|
16238
16970
|
res.json({ message: `"${normalized}" is already pending` });
|
|
16239
16971
|
return;
|
|
16240
16972
|
}
|
|
16241
|
-
if (!
|
|
16973
|
+
if (!fs36.existsSync(donePath)) {
|
|
16242
16974
|
res.status(404).json({ error: `PRD "${normalized}" not found in done/` });
|
|
16243
16975
|
return;
|
|
16244
16976
|
}
|
|
16245
|
-
|
|
16977
|
+
fs36.renameSync(donePath, pendingPath);
|
|
16246
16978
|
res.json({ message: `Moved "${normalized}" back to pending` });
|
|
16247
16979
|
} catch (error2) {
|
|
16248
16980
|
res.status(500).json({
|
|
@@ -16260,11 +16992,11 @@ function createActionRouteHandlers(ctx) {
|
|
|
16260
16992
|
res.status(409).json({ error: "Executor is actively running \u2014 use Stop instead" });
|
|
16261
16993
|
return;
|
|
16262
16994
|
}
|
|
16263
|
-
if (
|
|
16264
|
-
|
|
16995
|
+
if (fs36.existsSync(lockPath)) {
|
|
16996
|
+
fs36.unlinkSync(lockPath);
|
|
16265
16997
|
}
|
|
16266
|
-
const prdDir =
|
|
16267
|
-
if (
|
|
16998
|
+
const prdDir = path36.join(projectDir, config.prdDir);
|
|
16999
|
+
if (fs36.existsSync(prdDir)) {
|
|
16268
17000
|
cleanOrphanedClaims(prdDir);
|
|
16269
17001
|
}
|
|
16270
17002
|
broadcastSSE(ctx.getSseClients(req), "status_changed", await fetchStatusSnapshot(projectDir, config));
|
|
@@ -17032,8 +17764,8 @@ function createProjectConfigRoutes() {
|
|
|
17032
17764
|
|
|
17033
17765
|
// ../server/dist/routes/doctor.routes.js
|
|
17034
17766
|
init_dist();
|
|
17035
|
-
import * as
|
|
17036
|
-
import * as
|
|
17767
|
+
import * as fs37 from "fs";
|
|
17768
|
+
import * as path37 from "path";
|
|
17037
17769
|
import { execSync as execSync7 } from "child_process";
|
|
17038
17770
|
import { Router as Router4 } from "express";
|
|
17039
17771
|
function runDoctorChecks(projectDir, config) {
|
|
@@ -17066,7 +17798,7 @@ function runDoctorChecks(projectDir, config) {
|
|
|
17066
17798
|
});
|
|
17067
17799
|
}
|
|
17068
17800
|
try {
|
|
17069
|
-
const projectName =
|
|
17801
|
+
const projectName = path37.basename(projectDir);
|
|
17070
17802
|
const marker = generateMarker(projectName);
|
|
17071
17803
|
const crontabEntries = [...getEntries(marker), ...getProjectEntries(projectDir)];
|
|
17072
17804
|
if (crontabEntries.length > 0) {
|
|
@@ -17089,8 +17821,8 @@ function runDoctorChecks(projectDir, config) {
|
|
|
17089
17821
|
detail: "Failed to check crontab"
|
|
17090
17822
|
});
|
|
17091
17823
|
}
|
|
17092
|
-
const configPath =
|
|
17093
|
-
if (
|
|
17824
|
+
const configPath = path37.join(projectDir, CONFIG_FILE_NAME);
|
|
17825
|
+
if (fs37.existsSync(configPath)) {
|
|
17094
17826
|
checks.push({ name: "config", status: "pass", detail: "Config file exists" });
|
|
17095
17827
|
} else {
|
|
17096
17828
|
checks.push({
|
|
@@ -17099,9 +17831,9 @@ function runDoctorChecks(projectDir, config) {
|
|
|
17099
17831
|
detail: "Config file not found (using defaults)"
|
|
17100
17832
|
});
|
|
17101
17833
|
}
|
|
17102
|
-
const prdDir =
|
|
17103
|
-
if (
|
|
17104
|
-
const prds =
|
|
17834
|
+
const prdDir = path37.join(projectDir, config.prdDir);
|
|
17835
|
+
if (fs37.existsSync(prdDir)) {
|
|
17836
|
+
const prds = fs37.readdirSync(prdDir).filter((f) => f.endsWith(".md"));
|
|
17105
17837
|
checks.push({
|
|
17106
17838
|
name: "prdDir",
|
|
17107
17839
|
status: "pass",
|
|
@@ -17542,6 +18274,8 @@ function getLockPathForJob2(projectDir, jobId) {
|
|
|
17542
18274
|
return prResolverLockPath(projectDir);
|
|
17543
18275
|
case "merger":
|
|
17544
18276
|
return mergerLockPath(projectDir);
|
|
18277
|
+
case "manager":
|
|
18278
|
+
return managerLockPath(projectDir);
|
|
17545
18279
|
}
|
|
17546
18280
|
}
|
|
17547
18281
|
function createJobRouteHandlers(ctx) {
|
|
@@ -17668,7 +18402,7 @@ function createProjectJobRoutes() {
|
|
|
17668
18402
|
|
|
17669
18403
|
// ../server/dist/routes/log.routes.js
|
|
17670
18404
|
init_dist();
|
|
17671
|
-
import * as
|
|
18405
|
+
import * as path38 from "path";
|
|
17672
18406
|
import { Router as Router7 } from "express";
|
|
17673
18407
|
function createLogRoutes(deps) {
|
|
17674
18408
|
const { projectDir } = deps;
|
|
@@ -17687,7 +18421,7 @@ function createLogRoutes(deps) {
|
|
|
17687
18421
|
const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
|
|
17688
18422
|
const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
|
|
17689
18423
|
const fileName = LOG_FILE_NAMES[name] || name;
|
|
17690
|
-
const logPath =
|
|
18424
|
+
const logPath = path38.join(projectDir, LOG_DIR, `${fileName}.log`);
|
|
17691
18425
|
const logLines = getLastLogLines(logPath, linesToRead);
|
|
17692
18426
|
res.json({ name, lines: logLines });
|
|
17693
18427
|
} catch (error2) {
|
|
@@ -17713,7 +18447,7 @@ function createProjectLogRoutes() {
|
|
|
17713
18447
|
const lines = typeof linesParam === "string" ? parseInt(linesParam, 10) : 200;
|
|
17714
18448
|
const linesToRead = isNaN(lines) || lines < 1 ? 200 : Math.min(lines, 1e4);
|
|
17715
18449
|
const fileName = LOG_FILE_NAMES[name] || name;
|
|
17716
|
-
const logPath =
|
|
18450
|
+
const logPath = path38.join(projectDir, LOG_DIR, `${fileName}.log`);
|
|
17717
18451
|
const logLines = getLastLogLines(logPath, linesToRead);
|
|
17718
18452
|
res.json({ name, lines: logLines });
|
|
17719
18453
|
} catch (error2) {
|
|
@@ -17748,7 +18482,7 @@ function createProjectPrdRoutes() {
|
|
|
17748
18482
|
|
|
17749
18483
|
// ../server/dist/routes/roadmap.routes.js
|
|
17750
18484
|
init_dist();
|
|
17751
|
-
import * as
|
|
18485
|
+
import * as path39 from "path";
|
|
17752
18486
|
import { Router as Router9 } from "express";
|
|
17753
18487
|
function createRoadmapRouteHandlers(ctx) {
|
|
17754
18488
|
const router = Router9({ mergeParams: true });
|
|
@@ -17758,7 +18492,7 @@ function createRoadmapRouteHandlers(ctx) {
|
|
|
17758
18492
|
const config = ctx.getConfig(req);
|
|
17759
18493
|
const projectDir = ctx.getProjectDir(req);
|
|
17760
18494
|
const status = getRoadmapStatus(projectDir, config);
|
|
17761
|
-
const prdDir =
|
|
18495
|
+
const prdDir = path39.join(projectDir, config.prdDir);
|
|
17762
18496
|
const state = loadRoadmapState(prdDir);
|
|
17763
18497
|
res.json({
|
|
17764
18498
|
...status,
|
|
@@ -17885,6 +18619,7 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
17885
18619
|
const analyticsPlan = getSchedulingPlan(projectDir, config, "analytics");
|
|
17886
18620
|
const prResolverPlan = getSchedulingPlan(projectDir, config, "pr-resolver");
|
|
17887
18621
|
const mergerPlan = getSchedulingPlan(projectDir, config, "merger");
|
|
18622
|
+
const managerPlan = getSchedulingPlan(projectDir, config, "manager");
|
|
17888
18623
|
const executorInstalled = installed && config.executorEnabled !== false && hasScheduledCommand(entries, "run");
|
|
17889
18624
|
const reviewerInstalled = installed && config.reviewerEnabled && hasScheduledCommand(entries, "review");
|
|
17890
18625
|
const qaInstalled = installed && config.qa.enabled && hasScheduledCommand(entries, "qa");
|
|
@@ -17893,6 +18628,7 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
17893
18628
|
const analyticsInstalled = installed && config.analytics.enabled && hasScheduledCommand(entries, "analytics");
|
|
17894
18629
|
const prResolverInstalled = installed && (config.prResolver?.enabled ?? true) && hasScheduledCommand(entries, "resolve");
|
|
17895
18630
|
const mergerInstalled = installed && (config.merger?.enabled ?? false) && hasScheduledCommand(entries, "merge");
|
|
18631
|
+
const managerInstalled = installed && (config.manager?.enabled ?? true) && hasScheduledCommand(entries, "manager");
|
|
17896
18632
|
return {
|
|
17897
18633
|
executor: {
|
|
17898
18634
|
schedule: config.cronSchedule,
|
|
@@ -17958,6 +18694,14 @@ function buildScheduleInfoResponse(projectDir, config, entries, installed) {
|
|
|
17958
18694
|
manualDelayMinutes: mergerPlan.manualDelayMinutes,
|
|
17959
18695
|
balancedDelayMinutes: mergerPlan.balancedDelayMinutes
|
|
17960
18696
|
},
|
|
18697
|
+
manager: {
|
|
18698
|
+
schedule: config.manager?.schedule ?? "15 7 * * *",
|
|
18699
|
+
installed: managerInstalled,
|
|
18700
|
+
nextRun: managerInstalled ? addDelayToIsoString(computeNextRun(config.manager?.schedule ?? "15 7 * * *"), managerPlan.totalDelayMinutes) : null,
|
|
18701
|
+
delayMinutes: managerPlan.totalDelayMinutes,
|
|
18702
|
+
manualDelayMinutes: managerPlan.manualDelayMinutes,
|
|
18703
|
+
balancedDelayMinutes: managerPlan.balancedDelayMinutes
|
|
18704
|
+
},
|
|
17961
18705
|
paused: !installed,
|
|
17962
18706
|
schedulingPriority: config.schedulingPriority,
|
|
17963
18707
|
entries
|
|
@@ -18110,31 +18854,31 @@ function createQueueRoutes(deps) {
|
|
|
18110
18854
|
|
|
18111
18855
|
// ../server/dist/index.js
|
|
18112
18856
|
var __filename4 = fileURLToPath5(import.meta.url);
|
|
18113
|
-
var __dirname4 =
|
|
18857
|
+
var __dirname4 = dirname12(__filename4);
|
|
18114
18858
|
var JOB_RAW_BODY_LIMIT = "1mb";
|
|
18115
18859
|
function setupJobRawBodyParsing(app) {
|
|
18116
18860
|
app.use("/api/jobs", express.raw({ type: "*/*", limit: JOB_RAW_BODY_LIMIT }));
|
|
18117
18861
|
app.use("/api/projects/:projectId/jobs", express.raw({ type: "*/*", limit: JOB_RAW_BODY_LIMIT }));
|
|
18118
18862
|
}
|
|
18119
18863
|
function resolveWebDistPath() {
|
|
18120
|
-
const bundled =
|
|
18121
|
-
if (
|
|
18864
|
+
const bundled = path40.join(__dirname4, "web");
|
|
18865
|
+
if (fs38.existsSync(path40.join(bundled, "index.html")))
|
|
18122
18866
|
return bundled;
|
|
18123
18867
|
let d = __dirname4;
|
|
18124
18868
|
for (let i = 0; i < 8; i++) {
|
|
18125
|
-
if (
|
|
18126
|
-
const dev =
|
|
18127
|
-
if (
|
|
18869
|
+
if (fs38.existsSync(path40.join(d, "turbo.json"))) {
|
|
18870
|
+
const dev = path40.join(d, "web/dist");
|
|
18871
|
+
if (fs38.existsSync(path40.join(dev, "index.html")))
|
|
18128
18872
|
return dev;
|
|
18129
18873
|
break;
|
|
18130
18874
|
}
|
|
18131
|
-
d =
|
|
18875
|
+
d = dirname12(d);
|
|
18132
18876
|
}
|
|
18133
18877
|
return bundled;
|
|
18134
18878
|
}
|
|
18135
18879
|
function setupStaticFiles(app) {
|
|
18136
18880
|
const webDistPath = resolveWebDistPath();
|
|
18137
|
-
if (
|
|
18881
|
+
if (fs38.existsSync(webDistPath)) {
|
|
18138
18882
|
app.use(express.static(webDistPath));
|
|
18139
18883
|
}
|
|
18140
18884
|
app.use((req, res, next) => {
|
|
@@ -18142,8 +18886,8 @@ function setupStaticFiles(app) {
|
|
|
18142
18886
|
next();
|
|
18143
18887
|
return;
|
|
18144
18888
|
}
|
|
18145
|
-
const indexPath =
|
|
18146
|
-
if (
|
|
18889
|
+
const indexPath = path40.resolve(webDistPath, "index.html");
|
|
18890
|
+
if (fs38.existsSync(indexPath)) {
|
|
18147
18891
|
res.sendFile(indexPath, (err) => {
|
|
18148
18892
|
if (err)
|
|
18149
18893
|
next();
|
|
@@ -18282,7 +19026,7 @@ function createGlobalApp() {
|
|
|
18282
19026
|
return app;
|
|
18283
19027
|
}
|
|
18284
19028
|
function bootContainer() {
|
|
18285
|
-
initContainer(
|
|
19029
|
+
initContainer(path40.dirname(getDbPath()));
|
|
18286
19030
|
}
|
|
18287
19031
|
function startServer(projectDir, port) {
|
|
18288
19032
|
bootContainer();
|
|
@@ -18335,8 +19079,8 @@ function isProcessRunning2(pid) {
|
|
|
18335
19079
|
}
|
|
18336
19080
|
function readPid(lockPath) {
|
|
18337
19081
|
try {
|
|
18338
|
-
if (!
|
|
18339
|
-
const raw =
|
|
19082
|
+
if (!fs39.existsSync(lockPath)) return null;
|
|
19083
|
+
const raw = fs39.readFileSync(lockPath, "utf-8").trim();
|
|
18340
19084
|
const pid = parseInt(raw, 10);
|
|
18341
19085
|
return Number.isFinite(pid) ? pid : null;
|
|
18342
19086
|
} catch {
|
|
@@ -18348,10 +19092,10 @@ function acquireServeLock(mode, port) {
|
|
|
18348
19092
|
let stalePidCleaned;
|
|
18349
19093
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
18350
19094
|
try {
|
|
18351
|
-
const fd =
|
|
18352
|
-
|
|
19095
|
+
const fd = fs39.openSync(lockPath, "wx");
|
|
19096
|
+
fs39.writeFileSync(fd, `${process.pid}
|
|
18353
19097
|
`);
|
|
18354
|
-
|
|
19098
|
+
fs39.closeSync(fd);
|
|
18355
19099
|
return { acquired: true, lockPath, stalePidCleaned };
|
|
18356
19100
|
} catch (error2) {
|
|
18357
19101
|
const err = error2;
|
|
@@ -18372,7 +19116,7 @@ function acquireServeLock(mode, port) {
|
|
|
18372
19116
|
};
|
|
18373
19117
|
}
|
|
18374
19118
|
try {
|
|
18375
|
-
|
|
19119
|
+
fs39.unlinkSync(lockPath);
|
|
18376
19120
|
if (existingPid) {
|
|
18377
19121
|
stalePidCleaned = existingPid;
|
|
18378
19122
|
}
|
|
@@ -18395,10 +19139,10 @@ function acquireServeLock(mode, port) {
|
|
|
18395
19139
|
}
|
|
18396
19140
|
function releaseServeLock(lockPath) {
|
|
18397
19141
|
try {
|
|
18398
|
-
if (!
|
|
19142
|
+
if (!fs39.existsSync(lockPath)) return;
|
|
18399
19143
|
const lockPid = readPid(lockPath);
|
|
18400
19144
|
if (lockPid !== null && lockPid !== process.pid) return;
|
|
18401
|
-
|
|
19145
|
+
fs39.unlinkSync(lockPath);
|
|
18402
19146
|
} catch {
|
|
18403
19147
|
}
|
|
18404
19148
|
}
|
|
@@ -18494,14 +19238,14 @@ function historyCommand(program2) {
|
|
|
18494
19238
|
// src/commands/update.ts
|
|
18495
19239
|
init_dist();
|
|
18496
19240
|
import { spawnSync as spawnSync2 } from "child_process";
|
|
18497
|
-
import * as
|
|
18498
|
-
import * as
|
|
19241
|
+
import * as fs40 from "fs";
|
|
19242
|
+
import * as path41 from "path";
|
|
18499
19243
|
var DEFAULT_GLOBAL_SPEC = "@jonit-dev/night-watch-cli@latest";
|
|
18500
19244
|
function parseProjectDirs(projects, cwd) {
|
|
18501
19245
|
if (!projects || projects.trim().length === 0) {
|
|
18502
19246
|
return [cwd];
|
|
18503
19247
|
}
|
|
18504
|
-
const dirs = projects.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) =>
|
|
19248
|
+
const dirs = projects.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => path41.resolve(cwd, entry));
|
|
18505
19249
|
return Array.from(new Set(dirs));
|
|
18506
19250
|
}
|
|
18507
19251
|
function shouldInstallGlobal(options) {
|
|
@@ -18543,7 +19287,7 @@ function updateCommand(program2) {
|
|
|
18543
19287
|
}
|
|
18544
19288
|
const nightWatchBin = resolveNightWatchBin();
|
|
18545
19289
|
for (const projectDir of projectDirs) {
|
|
18546
|
-
if (!
|
|
19290
|
+
if (!fs40.existsSync(projectDir) || !fs40.statSync(projectDir).isDirectory()) {
|
|
18547
19291
|
warn(`Skipping invalid project directory: ${projectDir}`);
|
|
18548
19292
|
continue;
|
|
18549
19293
|
}
|
|
@@ -18587,8 +19331,8 @@ function prdStateCommand(program2) {
|
|
|
18587
19331
|
|
|
18588
19332
|
// src/commands/retry.ts
|
|
18589
19333
|
init_dist();
|
|
18590
|
-
import * as
|
|
18591
|
-
import * as
|
|
19334
|
+
import * as fs41 from "fs";
|
|
19335
|
+
import * as path42 from "path";
|
|
18592
19336
|
function normalizePrdName(name) {
|
|
18593
19337
|
if (!name.endsWith(".md")) {
|
|
18594
19338
|
return `${name}.md`;
|
|
@@ -18596,26 +19340,26 @@ function normalizePrdName(name) {
|
|
|
18596
19340
|
return name;
|
|
18597
19341
|
}
|
|
18598
19342
|
function getDonePrds(doneDir) {
|
|
18599
|
-
if (!
|
|
19343
|
+
if (!fs41.existsSync(doneDir)) {
|
|
18600
19344
|
return [];
|
|
18601
19345
|
}
|
|
18602
|
-
return
|
|
19346
|
+
return fs41.readdirSync(doneDir).filter((f) => f.endsWith(".md"));
|
|
18603
19347
|
}
|
|
18604
19348
|
function retryCommand(program2) {
|
|
18605
19349
|
program2.command("retry <prdName>").description("Move a completed PRD from done/ back to pending").action((prdName) => {
|
|
18606
19350
|
const projectDir = process.cwd();
|
|
18607
19351
|
const config = loadConfig(projectDir);
|
|
18608
|
-
const prdDir =
|
|
18609
|
-
const doneDir =
|
|
19352
|
+
const prdDir = path42.join(projectDir, config.prdDir);
|
|
19353
|
+
const doneDir = path42.join(prdDir, "done");
|
|
18610
19354
|
const normalizedPrdName = normalizePrdName(prdName);
|
|
18611
|
-
const pendingPath =
|
|
18612
|
-
if (
|
|
19355
|
+
const pendingPath = path42.join(prdDir, normalizedPrdName);
|
|
19356
|
+
if (fs41.existsSync(pendingPath)) {
|
|
18613
19357
|
info(`"${normalizedPrdName}" is already pending, nothing to retry.`);
|
|
18614
19358
|
return;
|
|
18615
19359
|
}
|
|
18616
|
-
const donePath =
|
|
18617
|
-
if (
|
|
18618
|
-
|
|
19360
|
+
const donePath = path42.join(doneDir, normalizedPrdName);
|
|
19361
|
+
if (fs41.existsSync(donePath)) {
|
|
19362
|
+
fs41.renameSync(donePath, pendingPath);
|
|
18619
19363
|
success(`Moved "${normalizedPrdName}" back to pending.`);
|
|
18620
19364
|
dim(`From: ${donePath}`);
|
|
18621
19365
|
dim(`To: ${pendingPath}`);
|
|
@@ -18867,7 +19611,7 @@ function prdsCommand(program2) {
|
|
|
18867
19611
|
|
|
18868
19612
|
// src/commands/cancel.ts
|
|
18869
19613
|
init_dist();
|
|
18870
|
-
import * as
|
|
19614
|
+
import * as fs42 from "fs";
|
|
18871
19615
|
import * as readline2 from "readline";
|
|
18872
19616
|
function getLockFilePaths2(projectDir) {
|
|
18873
19617
|
const runtimeKey = projectRuntimeKey(projectDir);
|
|
@@ -18884,16 +19628,16 @@ async function promptConfirmation(prompt) {
|
|
|
18884
19628
|
input: process.stdin,
|
|
18885
19629
|
output: process.stdout
|
|
18886
19630
|
});
|
|
18887
|
-
return new Promise((
|
|
19631
|
+
return new Promise((resolve11) => {
|
|
18888
19632
|
rl.question(`${prompt} `, (answer) => {
|
|
18889
19633
|
rl.close();
|
|
18890
19634
|
const normalized = answer.toLowerCase().trim();
|
|
18891
|
-
|
|
19635
|
+
resolve11(normalized === "y" || normalized === "yes");
|
|
18892
19636
|
});
|
|
18893
19637
|
});
|
|
18894
19638
|
}
|
|
18895
19639
|
function sleep2(ms) {
|
|
18896
|
-
return new Promise((
|
|
19640
|
+
return new Promise((resolve11) => setTimeout(resolve11, ms));
|
|
18897
19641
|
}
|
|
18898
19642
|
function isProcessRunning3(pid) {
|
|
18899
19643
|
try {
|
|
@@ -18914,7 +19658,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
18914
19658
|
const pid = lockStatus.pid;
|
|
18915
19659
|
if (!lockStatus.running) {
|
|
18916
19660
|
try {
|
|
18917
|
-
|
|
19661
|
+
fs42.unlinkSync(lockPath);
|
|
18918
19662
|
return {
|
|
18919
19663
|
success: true,
|
|
18920
19664
|
message: `${processType} is not running (cleaned up stale lock file for PID ${pid})`,
|
|
@@ -18952,7 +19696,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
18952
19696
|
await sleep2(3e3);
|
|
18953
19697
|
if (!isProcessRunning3(pid)) {
|
|
18954
19698
|
try {
|
|
18955
|
-
|
|
19699
|
+
fs42.unlinkSync(lockPath);
|
|
18956
19700
|
} catch {
|
|
18957
19701
|
}
|
|
18958
19702
|
return {
|
|
@@ -18987,7 +19731,7 @@ async function cancelProcess2(processType, lockPath, force = false) {
|
|
|
18987
19731
|
await sleep2(500);
|
|
18988
19732
|
if (!isProcessRunning3(pid)) {
|
|
18989
19733
|
try {
|
|
18990
|
-
|
|
19734
|
+
fs42.unlinkSync(lockPath);
|
|
18991
19735
|
} catch {
|
|
18992
19736
|
}
|
|
18993
19737
|
return {
|
|
@@ -19048,31 +19792,31 @@ function cancelCommand(program2) {
|
|
|
19048
19792
|
|
|
19049
19793
|
// src/commands/slice.ts
|
|
19050
19794
|
init_dist();
|
|
19051
|
-
import * as
|
|
19052
|
-
import * as
|
|
19795
|
+
import * as fs43 from "fs";
|
|
19796
|
+
import * as path43 from "path";
|
|
19053
19797
|
function plannerLockPath2(projectDir) {
|
|
19054
19798
|
return `${LOCK_FILE_PREFIX}slicer-${projectRuntimeKey(projectDir)}.lock`;
|
|
19055
19799
|
}
|
|
19056
19800
|
function acquirePlannerLock(projectDir) {
|
|
19057
19801
|
const lockFile = plannerLockPath2(projectDir);
|
|
19058
|
-
if (
|
|
19059
|
-
const pidRaw =
|
|
19802
|
+
if (fs43.existsSync(lockFile)) {
|
|
19803
|
+
const pidRaw = fs43.readFileSync(lockFile, "utf-8").trim();
|
|
19060
19804
|
const pid = parseInt(pidRaw, 10);
|
|
19061
19805
|
if (!Number.isNaN(pid) && isProcessRunning(pid)) {
|
|
19062
19806
|
return { acquired: false, lockFile, pid };
|
|
19063
19807
|
}
|
|
19064
19808
|
try {
|
|
19065
|
-
|
|
19809
|
+
fs43.unlinkSync(lockFile);
|
|
19066
19810
|
} catch {
|
|
19067
19811
|
}
|
|
19068
19812
|
}
|
|
19069
|
-
|
|
19813
|
+
fs43.writeFileSync(lockFile, String(process.pid));
|
|
19070
19814
|
return { acquired: true, lockFile };
|
|
19071
19815
|
}
|
|
19072
19816
|
function releasePlannerLock(lockFile) {
|
|
19073
19817
|
try {
|
|
19074
|
-
if (
|
|
19075
|
-
|
|
19818
|
+
if (fs43.existsSync(lockFile)) {
|
|
19819
|
+
fs43.unlinkSync(lockFile);
|
|
19076
19820
|
}
|
|
19077
19821
|
} catch {
|
|
19078
19822
|
}
|
|
@@ -19081,12 +19825,12 @@ function resolvePlannerIssueColumn(config) {
|
|
|
19081
19825
|
return config.roadmapScanner.issueColumn === "Draft" ? "Draft" : "Ready";
|
|
19082
19826
|
}
|
|
19083
19827
|
function buildPlannerIssueBody(projectDir, config, result) {
|
|
19084
|
-
const relativePrdPath =
|
|
19085
|
-
const absolutePrdPath =
|
|
19828
|
+
const relativePrdPath = path43.join(config.prdDir, result.file ?? "").replace(/\\/g, "/");
|
|
19829
|
+
const absolutePrdPath = path43.join(projectDir, config.prdDir, result.file ?? "");
|
|
19086
19830
|
const sourceItem = result.item;
|
|
19087
19831
|
let prdContent;
|
|
19088
19832
|
try {
|
|
19089
|
-
prdContent =
|
|
19833
|
+
prdContent = fs43.readFileSync(absolutePrdPath, "utf-8");
|
|
19090
19834
|
} catch {
|
|
19091
19835
|
prdContent = `Unable to read generated PRD file at \`${relativePrdPath}\`.`;
|
|
19092
19836
|
}
|
|
@@ -19126,10 +19870,10 @@ async function createPlannerIssue(projectDir, config, result) {
|
|
|
19126
19870
|
return { created: false, skippedReason: "board-not-configured" };
|
|
19127
19871
|
}
|
|
19128
19872
|
const issueTitle = `PRD: ${result.item.title}`;
|
|
19129
|
-
const
|
|
19873
|
+
const normalizeTitle2 = (t) => t.replace(/^PRD:\s*/i, "").trim().toLowerCase();
|
|
19130
19874
|
const existingIssues = await provider.getAllIssues();
|
|
19131
19875
|
const existing = existingIssues.find(
|
|
19132
|
-
(issue2) =>
|
|
19876
|
+
(issue2) => normalizeTitle2(issue2.title) === normalizeTitle2(result.item.title)
|
|
19133
19877
|
);
|
|
19134
19878
|
if (existing) {
|
|
19135
19879
|
return {
|
|
@@ -19316,7 +20060,7 @@ function sliceCommand(program2) {
|
|
|
19316
20060
|
if (!options.dryRun && result.sliced) {
|
|
19317
20061
|
await sendNotifications(config, {
|
|
19318
20062
|
event: "run_succeeded",
|
|
19319
|
-
projectName:
|
|
20063
|
+
projectName: path43.basename(projectDir),
|
|
19320
20064
|
exitCode,
|
|
19321
20065
|
provider: config.provider,
|
|
19322
20066
|
prTitle: result.item?.title
|
|
@@ -19324,7 +20068,7 @@ function sliceCommand(program2) {
|
|
|
19324
20068
|
} else if (!options.dryRun && !nothingPending) {
|
|
19325
20069
|
await sendNotifications(config, {
|
|
19326
20070
|
event: "run_failed",
|
|
19327
|
-
projectName:
|
|
20071
|
+
projectName: path43.basename(projectDir),
|
|
19328
20072
|
exitCode,
|
|
19329
20073
|
provider: config.provider
|
|
19330
20074
|
});
|
|
@@ -19357,20 +20101,20 @@ function sliceCommand(program2) {
|
|
|
19357
20101
|
// src/commands/state.ts
|
|
19358
20102
|
init_dist();
|
|
19359
20103
|
import * as os9 from "os";
|
|
19360
|
-
import * as
|
|
20104
|
+
import * as path44 from "path";
|
|
19361
20105
|
import chalk5 from "chalk";
|
|
19362
20106
|
import { Command } from "commander";
|
|
19363
20107
|
function createStateCommand() {
|
|
19364
20108
|
const state = new Command("state");
|
|
19365
20109
|
state.description("Manage Night Watch state");
|
|
19366
20110
|
state.command("migrate").description("Migrate legacy JSON state files to SQLite").option("--dry-run", "Show what would be migrated without making changes").action((opts) => {
|
|
19367
|
-
const nightWatchHome = process.env.NIGHT_WATCH_HOME ||
|
|
20111
|
+
const nightWatchHome = process.env.NIGHT_WATCH_HOME || path44.join(os9.homedir(), GLOBAL_CONFIG_DIR);
|
|
19368
20112
|
if (opts.dryRun) {
|
|
19369
20113
|
console.log(chalk5.cyan("Dry-run mode: no changes will be made.\n"));
|
|
19370
20114
|
console.log(`Legacy JSON files that would be migrated from: ${chalk5.bold(nightWatchHome)}`);
|
|
19371
|
-
console.log(` ${
|
|
19372
|
-
console.log(` ${
|
|
19373
|
-
console.log(` ${
|
|
20115
|
+
console.log(` ${path44.join(nightWatchHome, "projects.json")}`);
|
|
20116
|
+
console.log(` ${path44.join(nightWatchHome, "history.json")}`);
|
|
20117
|
+
console.log(` ${path44.join(nightWatchHome, "prd-states.json")}`);
|
|
19374
20118
|
console.log(` <project>/<prdDir>/.roadmap-state.json (per project)`);
|
|
19375
20119
|
console.log(chalk5.dim("\nRun without --dry-run to apply the migration."));
|
|
19376
20120
|
return;
|
|
@@ -19408,8 +20152,8 @@ function createStateCommand() {
|
|
|
19408
20152
|
init_dist();
|
|
19409
20153
|
init_dist();
|
|
19410
20154
|
import { execFileSync as execFileSync6 } from "child_process";
|
|
19411
|
-
import * as
|
|
19412
|
-
import * as
|
|
20155
|
+
import * as fs44 from "fs";
|
|
20156
|
+
import * as path45 from "path";
|
|
19413
20157
|
import * as readline3 from "readline";
|
|
19414
20158
|
import chalk6 from "chalk";
|
|
19415
20159
|
async function run(fn) {
|
|
@@ -19431,7 +20175,7 @@ function getProvider(config, cwd) {
|
|
|
19431
20175
|
return createBoardProvider(bp, cwd);
|
|
19432
20176
|
}
|
|
19433
20177
|
function defaultBoardTitle(cwd) {
|
|
19434
|
-
return `${
|
|
20178
|
+
return `${path45.basename(cwd)} Night Watch`;
|
|
19435
20179
|
}
|
|
19436
20180
|
async function ensureBoardConfigured(config, cwd, provider, options) {
|
|
19437
20181
|
if (config.boardProvider?.projectNumber) {
|
|
@@ -19463,10 +20207,10 @@ async function confirmPrompt(question) {
|
|
|
19463
20207
|
input: process.stdin,
|
|
19464
20208
|
output: process.stdout
|
|
19465
20209
|
});
|
|
19466
|
-
return new Promise((
|
|
20210
|
+
return new Promise((resolve11) => {
|
|
19467
20211
|
rl.question(question, (answer) => {
|
|
19468
20212
|
rl.close();
|
|
19469
|
-
|
|
20213
|
+
resolve11(answer.trim().toLowerCase() === "y");
|
|
19470
20214
|
});
|
|
19471
20215
|
});
|
|
19472
20216
|
}
|
|
@@ -19632,11 +20376,11 @@ function boardCommand(program2) {
|
|
|
19632
20376
|
let body = options.body ?? "";
|
|
19633
20377
|
if (options.bodyFile) {
|
|
19634
20378
|
const filePath = options.bodyFile;
|
|
19635
|
-
if (!
|
|
20379
|
+
if (!fs44.existsSync(filePath)) {
|
|
19636
20380
|
console.error(`File not found: ${filePath}`);
|
|
19637
20381
|
process.exit(1);
|
|
19638
20382
|
}
|
|
19639
|
-
body =
|
|
20383
|
+
body = fs44.readFileSync(filePath, "utf-8");
|
|
19640
20384
|
}
|
|
19641
20385
|
const labels = [];
|
|
19642
20386
|
if (options.label) {
|
|
@@ -19877,12 +20621,12 @@ function boardCommand(program2) {
|
|
|
19877
20621
|
const config = loadConfig(cwd);
|
|
19878
20622
|
const provider = getProvider(config, cwd);
|
|
19879
20623
|
await ensureBoardConfigured(config, cwd, provider);
|
|
19880
|
-
const roadmapPath = options.roadmap ??
|
|
19881
|
-
if (!
|
|
20624
|
+
const roadmapPath = options.roadmap ?? path45.join(cwd, "ROADMAP.md");
|
|
20625
|
+
if (!fs44.existsSync(roadmapPath)) {
|
|
19882
20626
|
console.error(`Roadmap file not found: ${roadmapPath}`);
|
|
19883
20627
|
process.exit(1);
|
|
19884
20628
|
}
|
|
19885
|
-
const roadmapContent =
|
|
20629
|
+
const roadmapContent = fs44.readFileSync(roadmapPath, "utf-8");
|
|
19886
20630
|
const items = parseRoadmap(roadmapContent);
|
|
19887
20631
|
const uncheckedItems = getUncheckedItems(items);
|
|
19888
20632
|
if (uncheckedItems.length === 0) {
|
|
@@ -20006,7 +20750,7 @@ function boardCommand(program2) {
|
|
|
20006
20750
|
// src/commands/queue.ts
|
|
20007
20751
|
init_dist();
|
|
20008
20752
|
init_dist();
|
|
20009
|
-
import * as
|
|
20753
|
+
import * as path46 from "path";
|
|
20010
20754
|
import { spawn as spawn8 } from "child_process";
|
|
20011
20755
|
import chalk7 from "chalk";
|
|
20012
20756
|
import { Command as Command2 } from "commander";
|
|
@@ -20019,7 +20763,8 @@ var VALID_JOB_TYPES2 = [
|
|
|
20019
20763
|
"slicer",
|
|
20020
20764
|
"planner",
|
|
20021
20765
|
"pr-resolver",
|
|
20022
|
-
"merger"
|
|
20766
|
+
"merger",
|
|
20767
|
+
"manager"
|
|
20023
20768
|
];
|
|
20024
20769
|
function formatTimestamp(unixTs) {
|
|
20025
20770
|
if (unixTs === null) return "-";
|
|
@@ -20142,7 +20887,7 @@ function createQueueCommand() {
|
|
|
20142
20887
|
process.exit(1);
|
|
20143
20888
|
}
|
|
20144
20889
|
}
|
|
20145
|
-
const projectName =
|
|
20890
|
+
const projectName = path46.basename(projectDir);
|
|
20146
20891
|
const queueConfig = loadConfig(projectDir).queue;
|
|
20147
20892
|
if (isJobPaused(projectDir, jobType)) {
|
|
20148
20893
|
logger6.info(`Skipping enqueue for paused job: ${jobType}`);
|
|
@@ -20160,7 +20905,7 @@ function createQueueCommand() {
|
|
|
20160
20905
|
});
|
|
20161
20906
|
queue.command("resolve-key").description("Resolve the provider bucket key for a given project and job type").requiredOption("--project <dir>", "Project directory").requiredOption(
|
|
20162
20907
|
"--job-type <type>",
|
|
20163
|
-
"Job type (executor, reviewer, qa, audit, slicer, planner, pr-resolver, merger)"
|
|
20908
|
+
"Job type (executor, reviewer, qa, audit, slicer, planner, pr-resolver, merger, manager)"
|
|
20164
20909
|
).action((opts) => {
|
|
20165
20910
|
try {
|
|
20166
20911
|
const config = loadConfig(opts.project);
|
|
@@ -20243,7 +20988,7 @@ function createQueueCommand() {
|
|
|
20243
20988
|
if (isJobPaused(projectDir, jobType)) {
|
|
20244
20989
|
process.exit(2);
|
|
20245
20990
|
}
|
|
20246
|
-
const projectName =
|
|
20991
|
+
const projectName = path46.basename(projectDir);
|
|
20247
20992
|
const callerPid = opts.pid ? parseInt(opts.pid, 10) : void 0;
|
|
20248
20993
|
const result = claimJobSlot(
|
|
20249
20994
|
projectDir,
|
|
@@ -20310,7 +21055,8 @@ var QUEUE_MARKER_KEYS = /* @__PURE__ */ new Set([
|
|
|
20310
21055
|
"NW_AUTO_MERGE",
|
|
20311
21056
|
"NW_AUTO_MERGE_METHOD",
|
|
20312
21057
|
"NW_MAX_RUNTIME",
|
|
20313
|
-
"NW_QA_MAX_RUNTIME"
|
|
21058
|
+
"NW_QA_MAX_RUNTIME",
|
|
21059
|
+
"NW_MANAGER_MAX_RUNTIME"
|
|
20314
21060
|
]);
|
|
20315
21061
|
function filterQueueMarkers(envJson) {
|
|
20316
21062
|
const result = {};
|
|
@@ -20339,6 +21085,8 @@ function getScriptNameForJobType(jobType) {
|
|
|
20339
21085
|
return "night-watch-pr-resolver-cron.sh";
|
|
20340
21086
|
case "merger":
|
|
20341
21087
|
return "night-watch-merger-cron.sh";
|
|
21088
|
+
case "manager":
|
|
21089
|
+
return "night-watch-manager-cron.sh";
|
|
20342
21090
|
default:
|
|
20343
21091
|
return null;
|
|
20344
21092
|
}
|
|
@@ -20376,7 +21124,7 @@ function notifyCommand(program2) {
|
|
|
20376
21124
|
|
|
20377
21125
|
// src/commands/summary.ts
|
|
20378
21126
|
init_dist();
|
|
20379
|
-
import
|
|
21127
|
+
import path47 from "path";
|
|
20380
21128
|
import chalk8 from "chalk";
|
|
20381
21129
|
function formatDuration2(seconds) {
|
|
20382
21130
|
if (seconds === null) return "-";
|
|
@@ -20406,7 +21154,7 @@ function formatJobStatus(status) {
|
|
|
20406
21154
|
return chalk8.dim(status);
|
|
20407
21155
|
}
|
|
20408
21156
|
function getProjectName2(projectPath) {
|
|
20409
|
-
return
|
|
21157
|
+
return path47.basename(projectPath) || projectPath;
|
|
20410
21158
|
}
|
|
20411
21159
|
function formatProvider(providerKey) {
|
|
20412
21160
|
return providerKey.split(":")[0] || providerKey;
|
|
@@ -20525,7 +21273,7 @@ function summaryCommand(program2) {
|
|
|
20525
21273
|
// src/commands/resolve.ts
|
|
20526
21274
|
init_dist();
|
|
20527
21275
|
import { execFileSync as execFileSync7 } from "child_process";
|
|
20528
|
-
import * as
|
|
21276
|
+
import * as path48 from "path";
|
|
20529
21277
|
function buildEnvVars6(config, options) {
|
|
20530
21278
|
const env = buildBaseEnvVars(config, "pr-resolver", options.dryRun);
|
|
20531
21279
|
env.NW_PR_RESOLVER_MAX_RUNTIME = String(config.prResolver.maxRuntime);
|
|
@@ -20678,7 +21426,7 @@ ${stderr}`);
|
|
|
20678
21426
|
}
|
|
20679
21427
|
await sendNotifications(config, {
|
|
20680
21428
|
event: notificationEvent,
|
|
20681
|
-
projectName:
|
|
21429
|
+
projectName: path48.basename(projectDir),
|
|
20682
21430
|
exitCode,
|
|
20683
21431
|
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL)
|
|
20684
21432
|
});
|
|
@@ -20693,7 +21441,7 @@ ${stderr}`);
|
|
|
20693
21441
|
|
|
20694
21442
|
// src/commands/merge.ts
|
|
20695
21443
|
init_dist();
|
|
20696
|
-
import * as
|
|
21444
|
+
import * as path49 from "path";
|
|
20697
21445
|
function buildEnvVars7(config, options) {
|
|
20698
21446
|
const env = buildBaseEnvVars(config, "merger", options.dryRun);
|
|
20699
21447
|
env.NW_MERGER_MAX_RUNTIME = String(config.merger.maxRuntime);
|
|
@@ -20829,7 +21577,7 @@ ${stderr}`);
|
|
|
20829
21577
|
if (notificationEvent) {
|
|
20830
21578
|
await sendNotifications(config, {
|
|
20831
21579
|
event: notificationEvent,
|
|
20832
|
-
projectName:
|
|
21580
|
+
projectName: path49.basename(projectDir),
|
|
20833
21581
|
exitCode,
|
|
20834
21582
|
provider: formatProviderDisplay(envVars.NW_PROVIDER_CMD, envVars.NW_PROVIDER_LABEL)
|
|
20835
21583
|
});
|
|
@@ -20843,12 +21591,208 @@ ${stderr}`);
|
|
|
20843
21591
|
});
|
|
20844
21592
|
}
|
|
20845
21593
|
|
|
21594
|
+
// src/commands/manager.ts
|
|
21595
|
+
init_dist();
|
|
21596
|
+
import * as path50 from "path";
|
|
21597
|
+
function resolveRunManager() {
|
|
21598
|
+
const runManager2 = runManager;
|
|
21599
|
+
if (typeof runManager2 !== "function") {
|
|
21600
|
+
throw new Error(
|
|
21601
|
+
"Manager runner is not available in @night-watch/core. Update core to include runManager(projectDir, config, options)."
|
|
21602
|
+
);
|
|
21603
|
+
}
|
|
21604
|
+
return runManager2;
|
|
21605
|
+
}
|
|
21606
|
+
function writeJson(value) {
|
|
21607
|
+
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
21608
|
+
`);
|
|
21609
|
+
}
|
|
21610
|
+
function parseTimeout(timeout) {
|
|
21611
|
+
if (!timeout) return void 0;
|
|
21612
|
+
const parsed = parseInt(timeout, 10);
|
|
21613
|
+
return Number.isNaN(parsed) || parsed < 0 ? void 0 : parsed;
|
|
21614
|
+
}
|
|
21615
|
+
function buildManagerRunOptions(options) {
|
|
21616
|
+
const timeout = parseTimeout(options.timeout);
|
|
21617
|
+
return {
|
|
21618
|
+
dryRun: options.dryRun === true,
|
|
21619
|
+
...timeout !== void 0 ? { timeout } : {},
|
|
21620
|
+
...options.provider ? { provider: options.provider } : {}
|
|
21621
|
+
};
|
|
21622
|
+
}
|
|
21623
|
+
function applyManagerCliOverrides(config, options) {
|
|
21624
|
+
const timeout = parseTimeout(options.timeout);
|
|
21625
|
+
let overridden = config;
|
|
21626
|
+
if (timeout !== void 0) {
|
|
21627
|
+
overridden = {
|
|
21628
|
+
...overridden,
|
|
21629
|
+
manager: {
|
|
21630
|
+
...overridden.manager,
|
|
21631
|
+
maxRuntime: timeout
|
|
21632
|
+
}
|
|
21633
|
+
};
|
|
21634
|
+
}
|
|
21635
|
+
if (options.provider) {
|
|
21636
|
+
overridden = {
|
|
21637
|
+
...overridden,
|
|
21638
|
+
_cliProviderOverride: options.provider
|
|
21639
|
+
};
|
|
21640
|
+
}
|
|
21641
|
+
return overridden;
|
|
21642
|
+
}
|
|
21643
|
+
function getManagerConfig(config) {
|
|
21644
|
+
return config.manager;
|
|
21645
|
+
}
|
|
21646
|
+
function buildJsonResult(result, options) {
|
|
21647
|
+
if (result && typeof result === "object" && !Array.isArray(result)) {
|
|
21648
|
+
return {
|
|
21649
|
+
...result,
|
|
21650
|
+
dryRun: options.dryRun
|
|
21651
|
+
};
|
|
21652
|
+
}
|
|
21653
|
+
return {
|
|
21654
|
+
dryRun: options.dryRun,
|
|
21655
|
+
result
|
|
21656
|
+
};
|
|
21657
|
+
}
|
|
21658
|
+
function resultExitCode(result) {
|
|
21659
|
+
if (!result || typeof result !== "object") return 0;
|
|
21660
|
+
const record = result;
|
|
21661
|
+
if (record.ok === false || record.success === false || typeof record.error === "string") {
|
|
21662
|
+
return 1;
|
|
21663
|
+
}
|
|
21664
|
+
return 0;
|
|
21665
|
+
}
|
|
21666
|
+
async function sendManagerNotifications(config, projectDir, result) {
|
|
21667
|
+
if (!result || typeof result !== "object") return;
|
|
21668
|
+
const decisions = result.notificationDecisions;
|
|
21669
|
+
if (!Array.isArray(decisions)) return;
|
|
21670
|
+
for (const decision of decisions) {
|
|
21671
|
+
if (!decision || typeof decision !== "object") continue;
|
|
21672
|
+
const item = decision;
|
|
21673
|
+
if (!item.shouldNotify || !item.event) continue;
|
|
21674
|
+
await sendNotifications(config, {
|
|
21675
|
+
event: item.event,
|
|
21676
|
+
projectName: path50.basename(projectDir),
|
|
21677
|
+
provider: resolveJobProvider(config, "manager"),
|
|
21678
|
+
exitCode: 0,
|
|
21679
|
+
failureReason: item.title,
|
|
21680
|
+
failureDetail: item.body
|
|
21681
|
+
});
|
|
21682
|
+
}
|
|
21683
|
+
}
|
|
21684
|
+
function printHumanResult(result, options) {
|
|
21685
|
+
const payload = buildJsonResult(result, options);
|
|
21686
|
+
header(options.dryRun ? "Dry Run: Manager" : "Manager Result");
|
|
21687
|
+
const table = createTable({ head: ["Metric", "Value"] });
|
|
21688
|
+
for (const key of [
|
|
21689
|
+
"summary",
|
|
21690
|
+
"findings",
|
|
21691
|
+
"createdIssues",
|
|
21692
|
+
"createdDrafts",
|
|
21693
|
+
"skippedDuplicates",
|
|
21694
|
+
"blockedItems"
|
|
21695
|
+
]) {
|
|
21696
|
+
const value = payload[key];
|
|
21697
|
+
if (value === void 0) continue;
|
|
21698
|
+
table.push([key, Array.isArray(value) ? String(value.length) : String(value)]);
|
|
21699
|
+
}
|
|
21700
|
+
console.log(table.length > 0 ? table.toString() : JSON.stringify(payload, null, 2));
|
|
21701
|
+
}
|
|
21702
|
+
function managerCommand(program2) {
|
|
21703
|
+
program2.command("manager").description("Run Manager to analyze roadmap, board, job status, and docs alignment").option("--dry-run", "Analyze without writing memory, docs, board issues, or notifications").option("--json", "Output structured JSON").option("--timeout <seconds>", "Override max runtime in seconds").option("--provider <string>", "AI provider to use (claude or codex)").action(async (options) => {
|
|
21704
|
+
const projectDir = process.cwd();
|
|
21705
|
+
let config = loadConfig(projectDir);
|
|
21706
|
+
config = applyManagerCliOverrides(config, options);
|
|
21707
|
+
const managerConfig = getManagerConfig(config);
|
|
21708
|
+
const runOptions = buildManagerRunOptions(options);
|
|
21709
|
+
if (!managerConfig.enabled && !runOptions.dryRun) {
|
|
21710
|
+
if (options.json) {
|
|
21711
|
+
writeJson({ dryRun: false, skipped: true, reason: "manager-disabled" });
|
|
21712
|
+
} else {
|
|
21713
|
+
info("Manager is disabled in config; skipping run.");
|
|
21714
|
+
}
|
|
21715
|
+
process.exit(0);
|
|
21716
|
+
}
|
|
21717
|
+
const startedAt = Date.now();
|
|
21718
|
+
let exitCode = 0;
|
|
21719
|
+
const run2 = async () => {
|
|
21720
|
+
if (!runOptions.dryRun) {
|
|
21721
|
+
await maybeApplyCronSchedulingDelay(config, "manager", projectDir);
|
|
21722
|
+
}
|
|
21723
|
+
const runner = resolveRunManager();
|
|
21724
|
+
return runner(projectDir, config, runOptions);
|
|
21725
|
+
};
|
|
21726
|
+
try {
|
|
21727
|
+
const spinner = options.json ? null : createSpinner("Running Manager...");
|
|
21728
|
+
spinner?.start();
|
|
21729
|
+
const result = await run2();
|
|
21730
|
+
exitCode = resultExitCode(result);
|
|
21731
|
+
if (!runOptions.dryRun) {
|
|
21732
|
+
await sendManagerNotifications(config, projectDir, result);
|
|
21733
|
+
try {
|
|
21734
|
+
recordJobOutcome({
|
|
21735
|
+
config,
|
|
21736
|
+
exitCode,
|
|
21737
|
+
finishedAt: Date.now(),
|
|
21738
|
+
jobType: "manager",
|
|
21739
|
+
metadata: buildJsonResult(result, runOptions),
|
|
21740
|
+
projectDir,
|
|
21741
|
+
providerKey: resolveJobProvider(config, "manager"),
|
|
21742
|
+
startedAt,
|
|
21743
|
+
stdout: typeof result === "string" ? result : JSON.stringify(result)
|
|
21744
|
+
});
|
|
21745
|
+
} catch {
|
|
21746
|
+
}
|
|
21747
|
+
}
|
|
21748
|
+
if (options.json) {
|
|
21749
|
+
writeJson(buildJsonResult(result, runOptions));
|
|
21750
|
+
} else {
|
|
21751
|
+
if (exitCode === 0) {
|
|
21752
|
+
spinner?.succeed("Manager completed successfully");
|
|
21753
|
+
} else {
|
|
21754
|
+
spinner?.fail("Manager completed with errors");
|
|
21755
|
+
}
|
|
21756
|
+
printHumanResult(result, runOptions);
|
|
21757
|
+
}
|
|
21758
|
+
} catch (err) {
|
|
21759
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
21760
|
+
if (!runOptions.dryRun) {
|
|
21761
|
+
try {
|
|
21762
|
+
recordJobOutcome({
|
|
21763
|
+
config,
|
|
21764
|
+
exitCode: 1,
|
|
21765
|
+
finishedAt: Date.now(),
|
|
21766
|
+
jobType: "manager",
|
|
21767
|
+
metadata: { error: message },
|
|
21768
|
+
projectDir,
|
|
21769
|
+
providerKey: resolveJobProvider(config, "manager"),
|
|
21770
|
+
startedAt,
|
|
21771
|
+
stderr: message
|
|
21772
|
+
});
|
|
21773
|
+
} catch {
|
|
21774
|
+
}
|
|
21775
|
+
}
|
|
21776
|
+
if (options.json) {
|
|
21777
|
+
process.stderr.write(
|
|
21778
|
+
`${JSON.stringify({ dryRun: runOptions.dryRun, ok: false, error: message }, null, 2)}
|
|
21779
|
+
`
|
|
21780
|
+
);
|
|
21781
|
+
} else {
|
|
21782
|
+
error(`Manager failed: ${message}`);
|
|
21783
|
+
}
|
|
21784
|
+
process.exit(1);
|
|
21785
|
+
}
|
|
21786
|
+
process.exit(exitCode);
|
|
21787
|
+
});
|
|
21788
|
+
}
|
|
21789
|
+
|
|
20846
21790
|
// src/commands/agent.ts
|
|
20847
21791
|
init_dist();
|
|
20848
21792
|
var SCHEMA_VERSION2 = 1;
|
|
20849
21793
|
var JSON_OPTION = "--json";
|
|
20850
21794
|
var JSON_OPTION_DESCRIPTION = "Output as JSON";
|
|
20851
|
-
function
|
|
21795
|
+
function writeJson2(value) {
|
|
20852
21796
|
process.stdout.write(`${JSON.stringify(value, null, 2)}
|
|
20853
21797
|
`);
|
|
20854
21798
|
}
|
|
@@ -21005,7 +21949,7 @@ function normalizeJobType(job) {
|
|
|
21005
21949
|
function agentCommand(program2) {
|
|
21006
21950
|
const agent = program2.command("agent").description("Machine-readable agent operations");
|
|
21007
21951
|
agent.command("status").description("Print a stable machine-readable project snapshot").requiredOption(JSON_OPTION, "Output status as JSON").action(async () => {
|
|
21008
|
-
|
|
21952
|
+
writeJson2(await buildAgentStatus(process.cwd()));
|
|
21009
21953
|
});
|
|
21010
21954
|
}
|
|
21011
21955
|
function configCommand(program2) {
|
|
@@ -21013,18 +21957,18 @@ function configCommand(program2) {
|
|
|
21013
21957
|
config.command("list").description("Print resolved config").option(JSON_OPTION, JSON_OPTION_DESCRIPTION).action((options) => {
|
|
21014
21958
|
const value = loadConfig(process.cwd());
|
|
21015
21959
|
if (options.json) {
|
|
21016
|
-
|
|
21960
|
+
writeJson2({ schemaVersion: SCHEMA_VERSION2, config: value });
|
|
21017
21961
|
} else {
|
|
21018
|
-
|
|
21962
|
+
writeJson2(value);
|
|
21019
21963
|
}
|
|
21020
21964
|
});
|
|
21021
21965
|
config.command("get <path>").description("Read a resolved config value by dot path").option(JSON_OPTION, JSON_OPTION_DESCRIPTION).action((dotPath, options) => {
|
|
21022
21966
|
try {
|
|
21023
21967
|
const result = getConfigValue(process.cwd(), dotPath);
|
|
21024
21968
|
if (options.json) {
|
|
21025
|
-
|
|
21969
|
+
writeJson2({ schemaVersion: SCHEMA_VERSION2, ...result });
|
|
21026
21970
|
} else {
|
|
21027
|
-
|
|
21971
|
+
writeJson2(result.value);
|
|
21028
21972
|
}
|
|
21029
21973
|
} catch (error2) {
|
|
21030
21974
|
fail(error2 instanceof Error ? error2.message : String(error2), options);
|
|
@@ -21034,7 +21978,7 @@ function configCommand(program2) {
|
|
|
21034
21978
|
try {
|
|
21035
21979
|
const result = setConfigValue(process.cwd(), dotPath, parseConfigValue(rawValue));
|
|
21036
21980
|
if (options.json) {
|
|
21037
|
-
|
|
21981
|
+
writeJson2({ schemaVersion: SCHEMA_VERSION2, ok: true, ...result });
|
|
21038
21982
|
} else {
|
|
21039
21983
|
process.stdout.write(`Updated ${result.path}
|
|
21040
21984
|
`);
|
|
@@ -21050,7 +21994,7 @@ function healthCommand(program2) {
|
|
|
21050
21994
|
const snapshot = await fetchStatusSnapshot(process.cwd(), config);
|
|
21051
21995
|
const health = buildHealth(snapshot, config);
|
|
21052
21996
|
if (options.json) {
|
|
21053
|
-
|
|
21997
|
+
writeJson2(health);
|
|
21054
21998
|
} else {
|
|
21055
21999
|
for (const check of health.checks) {
|
|
21056
22000
|
process.stdout.write(`${check.ok ? "ok" : "fail"} ${check.name}: ${check.message}
|
|
@@ -21069,7 +22013,7 @@ function jobCommand(program2) {
|
|
|
21069
22013
|
const jobType = normalizeJobType(jobName);
|
|
21070
22014
|
const result = setConfigValue(process.cwd(), `pausedJobs.${jobType}`, true);
|
|
21071
22015
|
if (options.json) {
|
|
21072
|
-
|
|
22016
|
+
writeJson2({
|
|
21073
22017
|
schemaVersion: SCHEMA_VERSION2,
|
|
21074
22018
|
ok: true,
|
|
21075
22019
|
job: jobType,
|
|
@@ -21088,7 +22032,7 @@ function jobCommand(program2) {
|
|
|
21088
22032
|
const jobType = normalizeJobType(jobName);
|
|
21089
22033
|
const result = setConfigValue(process.cwd(), `pausedJobs.${jobType}`, false);
|
|
21090
22034
|
if (options.json) {
|
|
21091
|
-
|
|
22035
|
+
writeJson2({
|
|
21092
22036
|
schemaVersion: SCHEMA_VERSION2,
|
|
21093
22037
|
ok: true,
|
|
21094
22038
|
job: jobType,
|
|
@@ -21115,17 +22059,17 @@ function jobCommand(program2) {
|
|
|
21115
22059
|
|
|
21116
22060
|
// src/cli.ts
|
|
21117
22061
|
var __filename5 = fileURLToPath6(import.meta.url);
|
|
21118
|
-
var __dirname5 =
|
|
22062
|
+
var __dirname5 = dirname13(__filename5);
|
|
21119
22063
|
function findPackageRoot(dir) {
|
|
21120
22064
|
let d = dir;
|
|
21121
22065
|
for (let i = 0; i < 5; i++) {
|
|
21122
|
-
if (
|
|
21123
|
-
d =
|
|
22066
|
+
if (existsSync36(join39(d, "package.json"))) return d;
|
|
22067
|
+
d = dirname13(d);
|
|
21124
22068
|
}
|
|
21125
22069
|
return dir;
|
|
21126
22070
|
}
|
|
21127
22071
|
var packageRoot = findPackageRoot(__dirname5);
|
|
21128
|
-
var packageJson = JSON.parse(
|
|
22072
|
+
var packageJson = JSON.parse(readFileSync23(join39(packageRoot, "package.json"), "utf-8"));
|
|
21129
22073
|
var program = new Command3();
|
|
21130
22074
|
program.name("night-watch").description("Autonomous PRD execution using Claude CLI + cron").version(packageJson.version);
|
|
21131
22075
|
initCommand(program);
|
|
@@ -21157,6 +22101,7 @@ notifyCommand(program);
|
|
|
21157
22101
|
summaryCommand(program);
|
|
21158
22102
|
resolveCommand(program);
|
|
21159
22103
|
mergeCommand(program);
|
|
22104
|
+
managerCommand(program);
|
|
21160
22105
|
agentCommand(program);
|
|
21161
22106
|
configCommand(program);
|
|
21162
22107
|
healthCommand(program);
|