@jonit-dev/night-watch-cli 1.8.12-beta.9 → 1.8.14-beta.0
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 +376 -201
- package/dist/commands/audit.d.ts.map +1 -1
- package/dist/commands/audit.js +5 -1
- package/dist/commands/audit.js.map +1 -1
- package/dist/commands/board.d.ts.map +1 -1
- package/dist/commands/board.js +2 -0
- package/dist/commands/board.js.map +1 -1
- package/dist/commands/dashboard/tab-config.d.ts.map +1 -1
- package/dist/commands/dashboard/tab-config.js +1 -11
- package/dist/commands/dashboard/tab-config.js.map +1 -1
- package/dist/commands/doctor.d.ts +1 -6
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +2 -59
- package/dist/commands/doctor.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/notify.d.ts.map +1 -1
- package/dist/commands/notify.js +3 -13
- package/dist/commands/notify.js.map +1 -1
- package/dist/commands/run.d.ts +14 -1
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +97 -40
- package/dist/commands/run.js.map +1 -1
- package/dist/scripts/night-watch-audit-cron.sh +11 -1
- package/dist/scripts/night-watch-cron.sh +4 -2
- package/dist/scripts/night-watch-merger-cron.sh +177 -32
- package/dist/scripts/night-watch-pr-reviewer-cron.sh +107 -2
- package/dist/templates/audit.md +64 -30
- package/dist/templates/night-watch-audit.md +71 -30
- package/dist/templates/night-watch-pr-reviewer.md +7 -6
- package/dist/templates/pr-reviewer.md +7 -6
- package/dist/web/assets/index-CL3Q-KB4.css +1 -0
- package/dist/web/assets/index-FDOCfjkP.js +442 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -11,9 +11,27 @@ var __export = (target, all) => {
|
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
// ../core/dist/types.js
|
|
14
|
+
var NOTIFICATION_EVENTS;
|
|
14
15
|
var init_types = __esm({
|
|
15
16
|
"../core/dist/types.js"() {
|
|
16
17
|
"use strict";
|
|
18
|
+
NOTIFICATION_EVENTS = [
|
|
19
|
+
"run_started",
|
|
20
|
+
"run_succeeded",
|
|
21
|
+
"run_failed",
|
|
22
|
+
"run_timeout",
|
|
23
|
+
"run_no_work",
|
|
24
|
+
"review_completed",
|
|
25
|
+
"review_ready_for_human",
|
|
26
|
+
"pr_auto_merged",
|
|
27
|
+
"rate_limit_fallback",
|
|
28
|
+
"qa_completed",
|
|
29
|
+
"pr_resolver_completed",
|
|
30
|
+
"pr_resolver_conflict_resolved",
|
|
31
|
+
"pr_resolver_failed",
|
|
32
|
+
"merge_completed",
|
|
33
|
+
"merge_failed"
|
|
34
|
+
];
|
|
17
35
|
}
|
|
18
36
|
});
|
|
19
37
|
|
|
@@ -290,13 +308,14 @@ var init_job_registry = __esm({
|
|
|
290
308
|
{
|
|
291
309
|
id: "audit",
|
|
292
310
|
name: "Auditor",
|
|
293
|
-
description: "Performs
|
|
311
|
+
description: "Performs consolidated architecture and code quality audits",
|
|
294
312
|
cliCommand: "audit",
|
|
295
313
|
logName: "audit",
|
|
296
314
|
lockSuffix: "-audit.lock",
|
|
297
315
|
queuePriority: 10,
|
|
298
316
|
envPrefix: "NW_AUDIT",
|
|
299
317
|
extraFields: [
|
|
318
|
+
{ name: "createIssues", type: "boolean", defaultValue: false },
|
|
300
319
|
{
|
|
301
320
|
name: "targetColumn",
|
|
302
321
|
type: "enum",
|
|
@@ -305,9 +324,10 @@ var init_job_registry = __esm({
|
|
|
305
324
|
}
|
|
306
325
|
],
|
|
307
326
|
defaultConfig: {
|
|
308
|
-
enabled:
|
|
327
|
+
enabled: false,
|
|
309
328
|
schedule: "50 3 * * 1",
|
|
310
329
|
maxRuntime: 1800,
|
|
330
|
+
createIssues: false,
|
|
311
331
|
targetColumn: "Draft"
|
|
312
332
|
}
|
|
313
333
|
},
|
|
@@ -390,7 +410,7 @@ function resolveProviderBucketKey(provider, providerEnv) {
|
|
|
390
410
|
return `claude-proxy:${baseUrl}`;
|
|
391
411
|
}
|
|
392
412
|
}
|
|
393
|
-
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_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, 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;
|
|
413
|
+
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, 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;
|
|
394
414
|
var init_constants = __esm({
|
|
395
415
|
"../core/dist/constants.js"() {
|
|
396
416
|
"use strict";
|
|
@@ -471,14 +491,16 @@ var init_constants = __esm({
|
|
|
471
491
|
validatedLabel: DEFAULT_QA_VALIDATED_LABEL
|
|
472
492
|
};
|
|
473
493
|
QA_LOG_NAME = "night-watch-qa";
|
|
474
|
-
DEFAULT_AUDIT_ENABLED =
|
|
494
|
+
DEFAULT_AUDIT_ENABLED = false;
|
|
475
495
|
DEFAULT_AUDIT_SCHEDULE = "50 3 * * 1";
|
|
476
496
|
DEFAULT_AUDIT_MAX_RUNTIME = 1800;
|
|
497
|
+
DEFAULT_AUDIT_CREATE_ISSUES = false;
|
|
477
498
|
DEFAULT_AUDIT_TARGET_COLUMN = "Draft";
|
|
478
499
|
DEFAULT_AUDIT = {
|
|
479
500
|
enabled: DEFAULT_AUDIT_ENABLED,
|
|
480
501
|
schedule: DEFAULT_AUDIT_SCHEDULE,
|
|
481
502
|
maxRuntime: DEFAULT_AUDIT_MAX_RUNTIME,
|
|
503
|
+
createIssues: DEFAULT_AUDIT_CREATE_ISSUES,
|
|
482
504
|
targetColumn: DEFAULT_AUDIT_TARGET_COLUMN
|
|
483
505
|
};
|
|
484
506
|
DEFAULT_ANALYTICS_ENABLED = false;
|
|
@@ -816,6 +838,9 @@ function normalizeConfig(rawConfig) {
|
|
|
816
838
|
if (typeof rawBoardProvider.projectNumber === "number") {
|
|
817
839
|
bp.projectNumber = rawBoardProvider.projectNumber;
|
|
818
840
|
}
|
|
841
|
+
if (typeof rawBoardProvider.projectTitle === "string") {
|
|
842
|
+
bp.projectTitle = rawBoardProvider.projectTitle;
|
|
843
|
+
}
|
|
819
844
|
if (typeof rawBoardProvider.repo === "string") {
|
|
820
845
|
bp.repo = rawBoardProvider.repo;
|
|
821
846
|
}
|
|
@@ -867,7 +892,11 @@ function normalizeConfig(rawConfig) {
|
|
|
867
892
|
continue;
|
|
868
893
|
const rawJob = readObject2(rawConfig[jobId]);
|
|
869
894
|
if (rawJob) {
|
|
870
|
-
|
|
895
|
+
const normalizedJob = normalizeJobConfig(rawJob, jobDef);
|
|
896
|
+
if (jobId === "audit" && rawJob.createIssues === void 0 && rawJob.targetColumn !== void 0) {
|
|
897
|
+
normalizedJob.createIssues = true;
|
|
898
|
+
}
|
|
899
|
+
normalized[jobId] = normalizedJob;
|
|
871
900
|
}
|
|
872
901
|
}
|
|
873
902
|
const prResolverDef = getJobDef("pr-resolver");
|
|
@@ -2926,12 +2955,18 @@ var init_github_projects_base = __esm({
|
|
|
2926
2955
|
if (this.cachedOwner && this.cachedRepositoryId)
|
|
2927
2956
|
return this.cachedOwner;
|
|
2928
2957
|
const { owner, name } = await this.getRepoParts();
|
|
2929
|
-
const data = await graphql(`
|
|
2930
|
-
|
|
2931
|
-
|
|
2932
|
-
|
|
2958
|
+
const data = await graphql(`
|
|
2959
|
+
query ResolveRepoOwner($owner: String!, $name: String!) {
|
|
2960
|
+
repository(owner: $owner, name: $name) {
|
|
2961
|
+
id
|
|
2962
|
+
owner {
|
|
2963
|
+
__typename
|
|
2964
|
+
id
|
|
2965
|
+
login
|
|
2966
|
+
}
|
|
2967
|
+
}
|
|
2933
2968
|
}
|
|
2934
|
-
|
|
2969
|
+
`, { owner, name }, this.cwd);
|
|
2935
2970
|
if (!data.repository)
|
|
2936
2971
|
throw new Error(`Repository ${owner}/${name} not found.`);
|
|
2937
2972
|
const ownerNode = data.repository.owner;
|
|
@@ -2967,24 +3002,49 @@ var init_github_projects_base = __esm({
|
|
|
2967
3002
|
}
|
|
2968
3003
|
return null;
|
|
2969
3004
|
}
|
|
3005
|
+
assertProjectUsable(projectNode) {
|
|
3006
|
+
if (projectNode.closed === true) {
|
|
3007
|
+
throw new Error(`Configured GitHub Project #${projectNode.number} is closed: "${projectNode.title}". Update boardProvider.projectNumber to an open Night Watch board or run \`night-watch board setup\`.`);
|
|
3008
|
+
}
|
|
3009
|
+
const expectedTitle = this.config.projectTitle?.trim();
|
|
3010
|
+
if (expectedTitle && projectNode.title !== expectedTitle) {
|
|
3011
|
+
throw new Error(`Configured GitHub Project #${projectNode.number} title mismatch. Expected "${expectedTitle}", got "${projectNode.title}". Update boardProvider.projectNumber/projectTitle or run \`night-watch board setup\`.`);
|
|
3012
|
+
}
|
|
3013
|
+
}
|
|
2970
3014
|
/** Try user query first, fall back to org query. */
|
|
2971
3015
|
async fetchProjectNode(login, projectNumber) {
|
|
2972
3016
|
try {
|
|
2973
|
-
const userData = await graphql(`
|
|
2974
|
-
|
|
2975
|
-
|
|
3017
|
+
const userData = await graphql(`
|
|
3018
|
+
query GetProject($login: String!, $number: Int!) {
|
|
3019
|
+
user(login: $login) {
|
|
3020
|
+
projectV2(number: $number) {
|
|
3021
|
+
id
|
|
3022
|
+
number
|
|
3023
|
+
title
|
|
3024
|
+
url
|
|
3025
|
+
closed
|
|
3026
|
+
}
|
|
3027
|
+
}
|
|
2976
3028
|
}
|
|
2977
|
-
|
|
3029
|
+
`, { login, number: projectNumber }, this.cwd);
|
|
2978
3030
|
if (userData.user?.projectV2)
|
|
2979
3031
|
return userData.user.projectV2;
|
|
2980
3032
|
} catch {
|
|
2981
3033
|
}
|
|
2982
3034
|
try {
|
|
2983
|
-
const orgData = await graphql(`
|
|
2984
|
-
|
|
2985
|
-
|
|
3035
|
+
const orgData = await graphql(`
|
|
3036
|
+
query GetOrgProject($login: String!, $number: Int!) {
|
|
3037
|
+
organization(login: $login) {
|
|
3038
|
+
projectV2(number: $number) {
|
|
3039
|
+
id
|
|
3040
|
+
number
|
|
3041
|
+
title
|
|
3042
|
+
url
|
|
3043
|
+
closed
|
|
3044
|
+
}
|
|
3045
|
+
}
|
|
2986
3046
|
}
|
|
2987
|
-
|
|
3047
|
+
`, { login, number: projectNumber }, this.cwd);
|
|
2988
3048
|
if (orgData.organization?.projectV2)
|
|
2989
3049
|
return orgData.organization.projectV2;
|
|
2990
3050
|
} catch {
|
|
@@ -2993,13 +3053,21 @@ var init_github_projects_base = __esm({
|
|
|
2993
3053
|
}
|
|
2994
3054
|
async ensureProjectCache() {
|
|
2995
3055
|
if (this.cachedProjectId !== null && this.cachedFieldId !== null && this.cachedOptionIds.size > 0) {
|
|
2996
|
-
return {
|
|
3056
|
+
return {
|
|
3057
|
+
projectId: this.cachedProjectId,
|
|
3058
|
+
fieldId: this.cachedFieldId,
|
|
3059
|
+
optionIds: this.cachedOptionIds
|
|
3060
|
+
};
|
|
2997
3061
|
}
|
|
2998
3062
|
if (this.cachedProjectId !== null) {
|
|
2999
3063
|
const statusField2 = await this.fetchStatusField(this.cachedProjectId);
|
|
3000
3064
|
this.cachedFieldId = statusField2.fieldId;
|
|
3001
3065
|
this.cachedOptionIds = statusField2.optionIds;
|
|
3002
|
-
return {
|
|
3066
|
+
return {
|
|
3067
|
+
projectId: this.cachedProjectId,
|
|
3068
|
+
fieldId: this.cachedFieldId,
|
|
3069
|
+
optionIds: this.cachedOptionIds
|
|
3070
|
+
};
|
|
3003
3071
|
}
|
|
3004
3072
|
const projectNumber = this.config.projectNumber;
|
|
3005
3073
|
if (!projectNumber) {
|
|
@@ -3009,28 +3077,38 @@ var init_github_projects_base = __esm({
|
|
|
3009
3077
|
if (!projectNode) {
|
|
3010
3078
|
throw new Error(`GitHub Project #${projectNumber} not found for repository owner "${await this.getRepoOwnerLogin()}".`);
|
|
3011
3079
|
}
|
|
3080
|
+
this.assertProjectUsable(projectNode);
|
|
3012
3081
|
this.cachedProjectId = projectNode.id;
|
|
3013
3082
|
const statusField = await this.fetchStatusField(projectNode.id);
|
|
3014
3083
|
this.cachedFieldId = statusField.fieldId;
|
|
3015
3084
|
this.cachedOptionIds = statusField.optionIds;
|
|
3016
|
-
return {
|
|
3085
|
+
return {
|
|
3086
|
+
projectId: this.cachedProjectId,
|
|
3087
|
+
fieldId: this.cachedFieldId,
|
|
3088
|
+
optionIds: this.cachedOptionIds
|
|
3089
|
+
};
|
|
3017
3090
|
}
|
|
3018
3091
|
// -------------------------------------------------------------------------
|
|
3019
3092
|
// Status field management
|
|
3020
3093
|
// -------------------------------------------------------------------------
|
|
3021
3094
|
async fetchStatusField(projectId) {
|
|
3022
|
-
const fieldData = await graphql(`
|
|
3023
|
-
|
|
3024
|
-
|
|
3025
|
-
|
|
3026
|
-
|
|
3027
|
-
|
|
3028
|
-
|
|
3095
|
+
const fieldData = await graphql(`
|
|
3096
|
+
query GetStatusField($projectId: ID!) {
|
|
3097
|
+
node(id: $projectId) {
|
|
3098
|
+
... on ProjectV2 {
|
|
3099
|
+
field(name: "Status") {
|
|
3100
|
+
... on ProjectV2SingleSelectField {
|
|
3101
|
+
id
|
|
3102
|
+
options {
|
|
3103
|
+
id
|
|
3104
|
+
name
|
|
3105
|
+
}
|
|
3106
|
+
}
|
|
3029
3107
|
}
|
|
3030
3108
|
}
|
|
3031
3109
|
}
|
|
3032
3110
|
}
|
|
3033
|
-
|
|
3111
|
+
`, { projectId }, this.cwd);
|
|
3034
3112
|
const field = fieldData.node?.field;
|
|
3035
3113
|
if (!field) {
|
|
3036
3114
|
throw new Error(`Status field not found on project ${projectId}. Run \`night-watch board setup\` to create it.`);
|
|
@@ -3038,18 +3116,23 @@ var init_github_projects_base = __esm({
|
|
|
3038
3116
|
return { fieldId: field.id, optionIds: new Map(field.options.map((o) => [o.name, o.id])) };
|
|
3039
3117
|
}
|
|
3040
3118
|
async ensureStatusColumns(projectId) {
|
|
3041
|
-
const fieldData = await graphql(`
|
|
3042
|
-
|
|
3043
|
-
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
|
|
3047
|
-
|
|
3119
|
+
const fieldData = await graphql(`
|
|
3120
|
+
query GetStatusField($projectId: ID!) {
|
|
3121
|
+
node(id: $projectId) {
|
|
3122
|
+
... on ProjectV2 {
|
|
3123
|
+
field(name: "Status") {
|
|
3124
|
+
... on ProjectV2SingleSelectField {
|
|
3125
|
+
id
|
|
3126
|
+
options {
|
|
3127
|
+
id
|
|
3128
|
+
name
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3048
3131
|
}
|
|
3049
3132
|
}
|
|
3050
3133
|
}
|
|
3051
3134
|
}
|
|
3052
|
-
|
|
3135
|
+
`, { projectId }, this.cwd);
|
|
3053
3136
|
const field = fieldData.node?.field;
|
|
3054
3137
|
if (!field)
|
|
3055
3138
|
return;
|
|
@@ -3057,31 +3140,47 @@ var init_github_projects_base = __esm({
|
|
|
3057
3140
|
const required = ["Draft", "Ready", "In Progress", "Review", "Done"];
|
|
3058
3141
|
if (required.every((n) => existing.has(n)))
|
|
3059
3142
|
return;
|
|
3060
|
-
await graphql(`
|
|
3061
|
-
|
|
3062
|
-
|
|
3063
|
-
|
|
3064
|
-
|
|
3065
|
-
|
|
3066
|
-
|
|
3067
|
-
|
|
3068
|
-
|
|
3069
|
-
|
|
3070
|
-
|
|
3071
|
-
|
|
3072
|
-
|
|
3143
|
+
await graphql(`
|
|
3144
|
+
mutation UpdateField($fieldId: ID!) {
|
|
3145
|
+
updateProjectV2Field(
|
|
3146
|
+
input: {
|
|
3147
|
+
fieldId: $fieldId
|
|
3148
|
+
singleSelectOptions: [
|
|
3149
|
+
{ name: "Draft", color: GRAY, description: "" }
|
|
3150
|
+
{ name: "Ready", color: BLUE, description: "" }
|
|
3151
|
+
{ name: "In Progress", color: YELLOW, description: "" }
|
|
3152
|
+
{ name: "Review", color: ORANGE, description: "" }
|
|
3153
|
+
{ name: "Done", color: GREEN, description: "" }
|
|
3154
|
+
]
|
|
3155
|
+
}
|
|
3156
|
+
) {
|
|
3157
|
+
projectV2Field {
|
|
3158
|
+
... on ProjectV2SingleSelectField {
|
|
3159
|
+
id
|
|
3160
|
+
options {
|
|
3161
|
+
id
|
|
3162
|
+
name
|
|
3163
|
+
}
|
|
3164
|
+
}
|
|
3165
|
+
}
|
|
3073
3166
|
}
|
|
3074
3167
|
}
|
|
3075
|
-
|
|
3168
|
+
`, { fieldId: field.id }, this.cwd);
|
|
3076
3169
|
}
|
|
3077
3170
|
async linkProjectToRepository(projectId) {
|
|
3078
3171
|
const repositoryId = await this.getRepositoryNodeId();
|
|
3079
3172
|
try {
|
|
3080
|
-
await graphql(`
|
|
3081
|
-
|
|
3082
|
-
|
|
3173
|
+
await graphql(`
|
|
3174
|
+
mutation LinkProjectToRepository($projectId: ID!, $repositoryId: ID!) {
|
|
3175
|
+
linkProjectV2ToRepository(
|
|
3176
|
+
input: { projectId: $projectId, repositoryId: $repositoryId }
|
|
3177
|
+
) {
|
|
3178
|
+
repository {
|
|
3179
|
+
id
|
|
3180
|
+
}
|
|
3181
|
+
}
|
|
3083
3182
|
}
|
|
3084
|
-
|
|
3183
|
+
`, { projectId, repositoryId }, this.cwd);
|
|
3085
3184
|
} catch (err) {
|
|
3086
3185
|
const message = err instanceof Error ? err.message : String(err);
|
|
3087
3186
|
if (message.toLowerCase().includes("already") && message.toLowerCase().includes("project"))
|
|
@@ -3162,16 +3261,22 @@ var init_github_projects_base = __esm({
|
|
|
3162
3261
|
};
|
|
3163
3262
|
}
|
|
3164
3263
|
async setItemStatus(projectId, itemId, fieldId, optionId) {
|
|
3165
|
-
await graphql(`
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3171
|
-
|
|
3172
|
-
|
|
3264
|
+
await graphql(`
|
|
3265
|
+
mutation UpdateItemField($projectId: ID!, $itemId: ID!, $fieldId: ID!, $optionId: String!) {
|
|
3266
|
+
updateProjectV2ItemFieldValue(
|
|
3267
|
+
input: {
|
|
3268
|
+
projectId: $projectId
|
|
3269
|
+
itemId: $itemId
|
|
3270
|
+
fieldId: $fieldId
|
|
3271
|
+
value: { singleSelectOptionId: $optionId }
|
|
3272
|
+
}
|
|
3273
|
+
) {
|
|
3274
|
+
projectV2Item {
|
|
3275
|
+
id
|
|
3276
|
+
}
|
|
3277
|
+
}
|
|
3173
3278
|
}
|
|
3174
|
-
|
|
3279
|
+
`, { projectId, itemId, fieldId, optionId }, this.cwd);
|
|
3175
3280
|
}
|
|
3176
3281
|
// -------------------------------------------------------------------------
|
|
3177
3282
|
// Project listing
|
|
@@ -3179,19 +3284,39 @@ var init_github_projects_base = __esm({
|
|
|
3179
3284
|
async findExistingProject(owner, title) {
|
|
3180
3285
|
try {
|
|
3181
3286
|
if (owner.type === "User") {
|
|
3182
|
-
const data2 = await graphql(`
|
|
3183
|
-
|
|
3184
|
-
|
|
3287
|
+
const data2 = await graphql(`
|
|
3288
|
+
query ListUserProjects($login: String!) {
|
|
3289
|
+
user(login: $login) {
|
|
3290
|
+
projectsV2(first: 50) {
|
|
3291
|
+
nodes {
|
|
3292
|
+
id
|
|
3293
|
+
number
|
|
3294
|
+
title
|
|
3295
|
+
url
|
|
3296
|
+
closed
|
|
3297
|
+
}
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3185
3300
|
}
|
|
3186
|
-
|
|
3187
|
-
return data2.user?.projectsV2.nodes.find((p) => p.title === title) ?? null;
|
|
3301
|
+
`, { login: owner.login }, this.cwd);
|
|
3302
|
+
return data2.user?.projectsV2.nodes.find((p) => p.title === title && p.closed !== true) ?? null;
|
|
3188
3303
|
}
|
|
3189
|
-
const data = await graphql(`
|
|
3190
|
-
|
|
3191
|
-
|
|
3304
|
+
const data = await graphql(`
|
|
3305
|
+
query ListOrgProjects($login: String!) {
|
|
3306
|
+
organization(login: $login) {
|
|
3307
|
+
projectsV2(first: 50) {
|
|
3308
|
+
nodes {
|
|
3309
|
+
id
|
|
3310
|
+
number
|
|
3311
|
+
title
|
|
3312
|
+
url
|
|
3313
|
+
closed
|
|
3314
|
+
}
|
|
3315
|
+
}
|
|
3316
|
+
}
|
|
3192
3317
|
}
|
|
3193
|
-
|
|
3194
|
-
return data.organization?.projectsV2.nodes.find((p) => p.title === title) ?? null;
|
|
3318
|
+
`, { login: owner.login }, this.cwd);
|
|
3319
|
+
return data.organization?.projectsV2.nodes.find((p) => p.title === title && p.closed !== true) ?? null;
|
|
3195
3320
|
} catch {
|
|
3196
3321
|
return null;
|
|
3197
3322
|
}
|
|
@@ -3224,7 +3349,16 @@ var init_github_projects = __esm({
|
|
|
3224
3349
|
async createRepositoryIssue(repo, input) {
|
|
3225
3350
|
const requestedLabels = input.labels ?? [];
|
|
3226
3351
|
const buildIssueArgs = (labels) => {
|
|
3227
|
-
const args = [
|
|
3352
|
+
const args = [
|
|
3353
|
+
"issue",
|
|
3354
|
+
"create",
|
|
3355
|
+
"--title",
|
|
3356
|
+
input.title,
|
|
3357
|
+
"--body",
|
|
3358
|
+
input.body,
|
|
3359
|
+
"--repo",
|
|
3360
|
+
repo
|
|
3361
|
+
];
|
|
3228
3362
|
if (labels.length > 0) {
|
|
3229
3363
|
args.push("--label", labels.join(","));
|
|
3230
3364
|
}
|
|
@@ -3330,6 +3464,11 @@ var init_github_projects = __esm({
|
|
|
3330
3464
|
const node = await this.resolveProjectNode(projectNumber);
|
|
3331
3465
|
if (!node)
|
|
3332
3466
|
return null;
|
|
3467
|
+
if (node.closed === true)
|
|
3468
|
+
return null;
|
|
3469
|
+
if (this.config.projectTitle?.trim() && node.title !== this.config.projectTitle.trim()) {
|
|
3470
|
+
return null;
|
|
3471
|
+
}
|
|
3333
3472
|
return { id: node.id, number: node.number, title: node.title, url: node.url };
|
|
3334
3473
|
} catch {
|
|
3335
3474
|
return null;
|
|
@@ -5977,12 +6116,19 @@ function formatDiscordPayload(ctx) {
|
|
|
5977
6116
|
function formatTelegramPayload(ctx) {
|
|
5978
6117
|
const emoji = getEventEmoji(ctx.event);
|
|
5979
6118
|
const title = ctx.event === "run_succeeded" ? "PR Opened" : getEventTitle(ctx.event);
|
|
5980
|
-
if (ctx.prUrl
|
|
6119
|
+
if (ctx.prUrl || ctx.prTitle || ctx.prNumber !== void 0) {
|
|
5981
6120
|
const lines = [];
|
|
6121
|
+
const prLabelParts = ["PR"];
|
|
6122
|
+
if (ctx.prNumber !== void 0) {
|
|
6123
|
+
prLabelParts.push(`#${ctx.prNumber}`);
|
|
6124
|
+
}
|
|
6125
|
+
const prLabel = ctx.prTitle ? `${prLabelParts.join(" ")}: ${ctx.prTitle}` : prLabelParts.join(" ");
|
|
5982
6126
|
lines.push(`*${escapeMarkdownV2(emoji + " " + title)}*`);
|
|
5983
6127
|
lines.push("");
|
|
5984
|
-
lines.push(`${escapeMarkdownV2("\u{1F4CB}")} *${escapeMarkdownV2(
|
|
5985
|
-
|
|
6128
|
+
lines.push(`${escapeMarkdownV2("\u{1F4CB}")} *${escapeMarkdownV2(prLabel)}*`);
|
|
6129
|
+
if (ctx.prUrl) {
|
|
6130
|
+
lines.push(`${escapeMarkdownV2("\u{1F517}")} ${escapeMarkdownV2(ctx.prUrl)}`);
|
|
6131
|
+
}
|
|
5986
6132
|
if (ctx.prBody && ctx.prBody.trim().length > 0) {
|
|
5987
6133
|
const summary = extractSummary(ctx.prBody);
|
|
5988
6134
|
if (summary) {
|
|
@@ -6030,7 +6176,16 @@ function formatTelegramPayload(ctx) {
|
|
|
6030
6176
|
}
|
|
6031
6177
|
}
|
|
6032
6178
|
lines.push("");
|
|
6033
|
-
lines.push(escapeMarkdownV2(
|
|
6179
|
+
lines.push(escapeMarkdownV2("\u2699\uFE0F Meta"));
|
|
6180
|
+
lines.push(escapeMarkdownV2(`Project: ${ctx.projectName}`));
|
|
6181
|
+
lines.push(escapeMarkdownV2(`Provider: ${ctx.provider}`));
|
|
6182
|
+
lines.push(escapeMarkdownV2(`Exit code: ${ctx.exitCode}`));
|
|
6183
|
+
if (ctx.prdName) {
|
|
6184
|
+
lines.push(escapeMarkdownV2(`PRD: ${ctx.prdName}`));
|
|
6185
|
+
}
|
|
6186
|
+
if (ctx.branchName) {
|
|
6187
|
+
lines.push(escapeMarkdownV2(`Branch: ${ctx.branchName}`));
|
|
6188
|
+
}
|
|
6034
6189
|
return {
|
|
6035
6190
|
text: lines.join("\n"),
|
|
6036
6191
|
parse_mode: "MarkdownV2"
|
|
@@ -7469,18 +7624,8 @@ function validateWebhook(webhook) {
|
|
|
7469
7624
|
if (!webhook.events || webhook.events.length === 0) {
|
|
7470
7625
|
issues.push("No events configured");
|
|
7471
7626
|
} else {
|
|
7472
|
-
const validEvents = [
|
|
7473
|
-
"run_started",
|
|
7474
|
-
"run_succeeded",
|
|
7475
|
-
"run_failed",
|
|
7476
|
-
"run_timeout",
|
|
7477
|
-
"review_completed",
|
|
7478
|
-
"pr_auto_merged",
|
|
7479
|
-
"rate_limit_fallback",
|
|
7480
|
-
"qa_completed"
|
|
7481
|
-
];
|
|
7482
7627
|
for (const event of webhook.events) {
|
|
7483
|
-
if (!
|
|
7628
|
+
if (!NOTIFICATION_EVENTS.includes(event)) {
|
|
7484
7629
|
issues.push(`Invalid event: ${event}`);
|
|
7485
7630
|
}
|
|
7486
7631
|
}
|
|
@@ -7516,6 +7661,7 @@ function validateWebhook(webhook) {
|
|
|
7516
7661
|
var init_webhook_validator = __esm({
|
|
7517
7662
|
"../core/dist/utils/webhook-validator.js"() {
|
|
7518
7663
|
"use strict";
|
|
7664
|
+
init_types();
|
|
7519
7665
|
}
|
|
7520
7666
|
});
|
|
7521
7667
|
|
|
@@ -8651,6 +8797,16 @@ function buildIssueBody(finding) {
|
|
|
8651
8797
|
async function syncAuditFindingsToBoard(config, projectDir) {
|
|
8652
8798
|
const findings = loadAuditFindings(projectDir);
|
|
8653
8799
|
const targetColumn = config.audit.targetColumn;
|
|
8800
|
+
if (!config.audit.createIssues) {
|
|
8801
|
+
return {
|
|
8802
|
+
status: "skipped",
|
|
8803
|
+
findingsCount: findings.length,
|
|
8804
|
+
issuesCreated: 0,
|
|
8805
|
+
issuesFailed: 0,
|
|
8806
|
+
targetColumn: null,
|
|
8807
|
+
summary: "audit board issue creation is disabled"
|
|
8808
|
+
};
|
|
8809
|
+
}
|
|
8654
8810
|
if (findings.length === 0) {
|
|
8655
8811
|
return {
|
|
8656
8812
|
status: "skipped",
|
|
@@ -9597,6 +9753,7 @@ __export(dist_exports, {
|
|
|
9597
9753
|
DEFAULT_ANALYTICS_SCHEDULE: () => DEFAULT_ANALYTICS_SCHEDULE,
|
|
9598
9754
|
DEFAULT_ANALYTICS_TARGET_COLUMN: () => DEFAULT_ANALYTICS_TARGET_COLUMN,
|
|
9599
9755
|
DEFAULT_AUDIT: () => DEFAULT_AUDIT,
|
|
9756
|
+
DEFAULT_AUDIT_CREATE_ISSUES: () => DEFAULT_AUDIT_CREATE_ISSUES,
|
|
9600
9757
|
DEFAULT_AUDIT_ENABLED: () => DEFAULT_AUDIT_ENABLED,
|
|
9601
9758
|
DEFAULT_AUDIT_MAX_RUNTIME: () => DEFAULT_AUDIT_MAX_RUNTIME,
|
|
9602
9759
|
DEFAULT_AUDIT_SCHEDULE: () => DEFAULT_AUDIT_SCHEDULE,
|
|
@@ -9693,6 +9850,7 @@ __export(dist_exports, {
|
|
|
9693
9850
|
MAX_HISTORY_RECORDS_PER_PRD: () => MAX_HISTORY_RECORDS_PER_PRD,
|
|
9694
9851
|
MERGER_LOG_NAME: () => MERGER_LOG_NAME,
|
|
9695
9852
|
NIGHT_WATCH_LABELS: () => NIGHT_WATCH_LABELS,
|
|
9853
|
+
NOTIFICATION_EVENTS: () => NOTIFICATION_EVENTS,
|
|
9696
9854
|
PLANNER_LOG_NAME: () => PLANNER_LOG_NAME,
|
|
9697
9855
|
PRD_STATES_FILE_NAME: () => PRD_STATES_FILE_NAME,
|
|
9698
9856
|
PRD_TEMPLATE: () => PRD_TEMPLATE,
|
|
@@ -10643,7 +10801,8 @@ function initCommand(program2) {
|
|
|
10643
10801
|
rawConfig.boardProvider = {
|
|
10644
10802
|
enabled: true,
|
|
10645
10803
|
provider: "github",
|
|
10646
|
-
projectNumber: board.number
|
|
10804
|
+
projectNumber: board.number,
|
|
10805
|
+
projectTitle: board.title
|
|
10647
10806
|
};
|
|
10648
10807
|
fs23.writeFileSync(configPath, JSON.stringify(rawConfig, null, 2) + "\n");
|
|
10649
10808
|
boardSetupStatus = `Created (#${board.number})`;
|
|
@@ -10888,6 +11047,27 @@ function resolveRunNotificationEvent(exitCode, scriptStatus) {
|
|
|
10888
11047
|
}
|
|
10889
11048
|
return null;
|
|
10890
11049
|
}
|
|
11050
|
+
function extractPrUrlFromOutput(output) {
|
|
11051
|
+
if (!output) {
|
|
11052
|
+
return void 0;
|
|
11053
|
+
}
|
|
11054
|
+
const matches = Array.from(
|
|
11055
|
+
output.matchAll(/https:\/\/github\.com\/[^\s)]+\/pull\/\d+/g),
|
|
11056
|
+
(match) => match[0]
|
|
11057
|
+
);
|
|
11058
|
+
return matches.at(-1);
|
|
11059
|
+
}
|
|
11060
|
+
function extractResultValueFromOutput(output, key) {
|
|
11061
|
+
if (!output) {
|
|
11062
|
+
return void 0;
|
|
11063
|
+
}
|
|
11064
|
+
const escapedKey = key.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
11065
|
+
const regex = new RegExp(`\\b${escapedKey}=([^\\s|]+)`, "g");
|
|
11066
|
+
const matches = Array.from(output.matchAll(regex), (match) => match[1]).filter(
|
|
11067
|
+
(value) => value !== void 0 && value.length > 0
|
|
11068
|
+
);
|
|
11069
|
+
return matches.at(-1);
|
|
11070
|
+
}
|
|
10891
11071
|
function shouldAttemptCrossProjectFallback(options, scriptStatus) {
|
|
10892
11072
|
if (options.crossProjectFallback !== true) {
|
|
10893
11073
|
return false;
|
|
@@ -10903,6 +11083,71 @@ function shouldAttemptCrossProjectFallback(options, scriptStatus) {
|
|
|
10903
11083
|
}
|
|
10904
11084
|
return scriptStatus === "skip_no_eligible_prd";
|
|
10905
11085
|
}
|
|
11086
|
+
function parsePrNumberFromUrl(prUrl) {
|
|
11087
|
+
const match = prUrl?.match(/\/pull\/(\d+)(?:\b|[/?#])/);
|
|
11088
|
+
if (!match?.[1]) {
|
|
11089
|
+
return void 0;
|
|
11090
|
+
}
|
|
11091
|
+
const parsed = parseInt(match[1], 10);
|
|
11092
|
+
return Number.isNaN(parsed) ? void 0 : parsed;
|
|
11093
|
+
}
|
|
11094
|
+
function getRunPrMetadata(scriptResult, rawOutput) {
|
|
11095
|
+
const prUrl = scriptResult?.data.pr_url ?? extractPrUrlFromOutput(rawOutput);
|
|
11096
|
+
const branchName = scriptResult?.data.branch ?? extractResultValueFromOutput(rawOutput, "branch");
|
|
11097
|
+
const prNumber = parsePrNumberFromUrl(prUrl) ?? (scriptResult?.data.pr_number ? parseInt(scriptResult.data.pr_number, 10) : void 0);
|
|
11098
|
+
return {
|
|
11099
|
+
prUrl,
|
|
11100
|
+
branchName,
|
|
11101
|
+
prNumber: prNumber !== void 0 && !Number.isNaN(prNumber) ? prNumber : void 0
|
|
11102
|
+
};
|
|
11103
|
+
}
|
|
11104
|
+
function fetchRunPrDetails(config, projectDir, metadata) {
|
|
11105
|
+
if (metadata.prNumber !== void 0) {
|
|
11106
|
+
const details = fetchPrDetailsByNumber(metadata.prNumber, projectDir);
|
|
11107
|
+
if (details) {
|
|
11108
|
+
return details;
|
|
11109
|
+
}
|
|
11110
|
+
}
|
|
11111
|
+
if (metadata.prUrl) {
|
|
11112
|
+
const details = fetchPrDetailsForBranch(metadata.prUrl, projectDir);
|
|
11113
|
+
if (details) {
|
|
11114
|
+
return details;
|
|
11115
|
+
}
|
|
11116
|
+
}
|
|
11117
|
+
if (metadata.branchName) {
|
|
11118
|
+
const details = fetchPrDetailsForBranch(metadata.branchName, projectDir);
|
|
11119
|
+
if (details) {
|
|
11120
|
+
return details;
|
|
11121
|
+
}
|
|
11122
|
+
}
|
|
11123
|
+
return fetchPrDetails(config.branchPrefix, projectDir);
|
|
11124
|
+
}
|
|
11125
|
+
function buildRunNotificationContext(config, projectDir, event, exitCode, scriptResult, prDetails, rawOutput) {
|
|
11126
|
+
const metadata = getRunPrMetadata(scriptResult, rawOutput);
|
|
11127
|
+
const timeoutDuration = event === "run_timeout" ? config.maxRuntime : void 0;
|
|
11128
|
+
const checkpointValue = scriptResult?.data.checkpoint;
|
|
11129
|
+
const checkpointStatus = checkpointValue === "created" || checkpointValue === "available" || checkpointValue === "none" ? checkpointValue : void 0;
|
|
11130
|
+
return {
|
|
11131
|
+
event,
|
|
11132
|
+
projectName: path23.basename(projectDir),
|
|
11133
|
+
exitCode,
|
|
11134
|
+
provider: config.provider,
|
|
11135
|
+
prdName: scriptResult?.data.prd ?? extractResultValueFromOutput(rawOutput, "prd"),
|
|
11136
|
+
branchName: metadata.branchName,
|
|
11137
|
+
duration: timeoutDuration,
|
|
11138
|
+
scriptStatus: scriptResult?.status,
|
|
11139
|
+
failureReason: scriptResult?.data.reason,
|
|
11140
|
+
failureDetail: scriptResult?.data.detail,
|
|
11141
|
+
checkpointStatus,
|
|
11142
|
+
prUrl: prDetails?.url || metadata.prUrl,
|
|
11143
|
+
prTitle: prDetails?.title,
|
|
11144
|
+
prBody: prDetails?.body,
|
|
11145
|
+
prNumber: prDetails?.number ?? metadata.prNumber,
|
|
11146
|
+
filesChanged: prDetails?.changedFiles,
|
|
11147
|
+
additions: prDetails?.additions,
|
|
11148
|
+
deletions: prDetails?.deletions
|
|
11149
|
+
};
|
|
11150
|
+
}
|
|
10906
11151
|
function getCrossProjectFallbackCandidates(currentProjectDir) {
|
|
10907
11152
|
const current = path23.resolve(currentProjectDir);
|
|
10908
11153
|
const { valid, invalid } = validateRegistry();
|
|
@@ -10911,7 +11156,7 @@ function getCrossProjectFallbackCandidates(currentProjectDir) {
|
|
|
10911
11156
|
}
|
|
10912
11157
|
return valid.filter((entry) => path23.resolve(entry.path) !== current);
|
|
10913
11158
|
}
|
|
10914
|
-
async function sendRunCompletionNotifications(config, projectDir, options, exitCode, scriptResult) {
|
|
11159
|
+
async function sendRunCompletionNotifications(config, projectDir, options, exitCode, scriptResult, rawOutput) {
|
|
10915
11160
|
if (isRateLimitFallbackTriggered(scriptResult?.data)) {
|
|
10916
11161
|
const nonTelegramWebhooks = (config.notifications?.webhooks ?? []).filter(
|
|
10917
11162
|
(wh) => wh.type !== "telegram"
|
|
@@ -10935,42 +11180,18 @@ async function sendRunCompletionNotifications(config, projectDir, options, exitC
|
|
|
10935
11180
|
const event = resolveRunNotificationEvent(exitCode, scriptResult?.status);
|
|
10936
11181
|
let prDetails = null;
|
|
10937
11182
|
if (event === "run_succeeded") {
|
|
10938
|
-
|
|
10939
|
-
const branch = scriptResult?.data.branch;
|
|
10940
|
-
if (prUrl) {
|
|
10941
|
-
prDetails = fetchPrDetailsForBranch(prUrl, projectDir);
|
|
10942
|
-
}
|
|
10943
|
-
if (!prDetails && branch) {
|
|
10944
|
-
prDetails = fetchPrDetailsForBranch(branch, projectDir);
|
|
10945
|
-
}
|
|
10946
|
-
if (!prDetails) {
|
|
10947
|
-
prDetails = fetchPrDetails(config.branchPrefix, projectDir);
|
|
10948
|
-
}
|
|
11183
|
+
prDetails = fetchRunPrDetails(config, projectDir, getRunPrMetadata(scriptResult, rawOutput));
|
|
10949
11184
|
}
|
|
10950
11185
|
if (event) {
|
|
10951
|
-
const
|
|
10952
|
-
|
|
10953
|
-
|
|
10954
|
-
const _ctx = {
|
|
11186
|
+
const _ctx = buildRunNotificationContext(
|
|
11187
|
+
config,
|
|
11188
|
+
projectDir,
|
|
10955
11189
|
event,
|
|
10956
|
-
projectName: path23.basename(projectDir),
|
|
10957
11190
|
exitCode,
|
|
10958
|
-
|
|
10959
|
-
|
|
10960
|
-
|
|
10961
|
-
|
|
10962
|
-
scriptStatus: scriptResult?.status,
|
|
10963
|
-
failureReason: scriptResult?.data.reason,
|
|
10964
|
-
failureDetail: scriptResult?.data.detail,
|
|
10965
|
-
checkpointStatus,
|
|
10966
|
-
prUrl: prDetails?.url,
|
|
10967
|
-
prTitle: prDetails?.title,
|
|
10968
|
-
prBody: prDetails?.body,
|
|
10969
|
-
prNumber: prDetails?.number,
|
|
10970
|
-
filesChanged: prDetails?.changedFiles,
|
|
10971
|
-
additions: prDetails?.additions,
|
|
10972
|
-
deletions: prDetails?.deletions
|
|
10973
|
-
};
|
|
11191
|
+
scriptResult,
|
|
11192
|
+
prDetails,
|
|
11193
|
+
rawOutput
|
|
11194
|
+
);
|
|
10974
11195
|
await sendNotifications(config, _ctx);
|
|
10975
11196
|
} else if (!options.dryRun) {
|
|
10976
11197
|
info("Skipping completion notification (no actionable run result)");
|
|
@@ -11021,7 +11242,9 @@ ${stderr}`);
|
|
|
11021
11242
|
candidate.path,
|
|
11022
11243
|
options,
|
|
11023
11244
|
exitCode,
|
|
11024
|
-
scriptResult
|
|
11245
|
+
scriptResult,
|
|
11246
|
+
`${stdout}
|
|
11247
|
+
${stderr}`
|
|
11025
11248
|
);
|
|
11026
11249
|
}
|
|
11027
11250
|
if (exitCode !== 0) {
|
|
@@ -11363,7 +11586,15 @@ ${stderr}`);
|
|
|
11363
11586
|
});
|
|
11364
11587
|
} catch {
|
|
11365
11588
|
}
|
|
11366
|
-
await sendRunCompletionNotifications(
|
|
11589
|
+
await sendRunCompletionNotifications(
|
|
11590
|
+
config,
|
|
11591
|
+
projectDir,
|
|
11592
|
+
options,
|
|
11593
|
+
exitCode,
|
|
11594
|
+
scriptResult,
|
|
11595
|
+
`${stdout}
|
|
11596
|
+
${stderr}`
|
|
11597
|
+
);
|
|
11367
11598
|
}
|
|
11368
11599
|
if (shouldAttemptCrossProjectFallback(options, scriptResult?.status)) {
|
|
11369
11600
|
const executedFallback = await runCrossProjectFallback(projectDir, options);
|
|
@@ -11973,6 +12204,7 @@ import * as path26 from "path";
|
|
|
11973
12204
|
function buildEnvVars4(config, options) {
|
|
11974
12205
|
const env = buildBaseEnvVars(config, "audit", options.dryRun);
|
|
11975
12206
|
env.NW_AUDIT_MAX_RUNTIME = String(config.audit.maxRuntime);
|
|
12207
|
+
env.NW_AUDIT_CREATE_ISSUES = config.audit.createIssues ? "1" : "0";
|
|
11976
12208
|
env.NW_CLAUDE_MODEL_ID = CLAUDE_MODEL_IDS[config.primaryFallbackModel ?? config.claudeModel ?? "sonnet"];
|
|
11977
12209
|
const telegramWebhooks = getTelegramStatusWebhooks(config);
|
|
11978
12210
|
if (telegramWebhooks.length > 0) {
|
|
@@ -12012,7 +12244,10 @@ function auditCommand(program2) {
|
|
|
12012
12244
|
configTable.push(["Provider", auditProvider]);
|
|
12013
12245
|
configTable.push(["Provider CLI", PROVIDER_COMMANDS[auditProvider]]);
|
|
12014
12246
|
configTable.push(["Max Runtime", `${config.audit.maxRuntime}s`]);
|
|
12015
|
-
configTable.push(["
|
|
12247
|
+
configTable.push(["Create Board Issues", config.audit.createIssues ? "yes" : "no"]);
|
|
12248
|
+
if (config.audit.createIssues) {
|
|
12249
|
+
configTable.push(["Target Column", config.audit.targetColumn]);
|
|
12250
|
+
}
|
|
12016
12251
|
configTable.push(["Report File", path26.join(projectDir, "logs", "audit-report.md")]);
|
|
12017
12252
|
console.log(configTable.toString());
|
|
12018
12253
|
header("Provider Invocation");
|
|
@@ -13781,16 +14016,6 @@ function promptTextbox(screen, label2, initialValue, cb) {
|
|
|
13781
14016
|
});
|
|
13782
14017
|
}
|
|
13783
14018
|
var WEBHOOK_TYPES = ["slack", "discord", "telegram"];
|
|
13784
|
-
var NOTIFICATION_EVENTS = [
|
|
13785
|
-
"run_started",
|
|
13786
|
-
"run_succeeded",
|
|
13787
|
-
"run_failed",
|
|
13788
|
-
"run_timeout",
|
|
13789
|
-
"review_completed",
|
|
13790
|
-
"pr_auto_merged",
|
|
13791
|
-
"rate_limit_fallback",
|
|
13792
|
-
"qa_completed"
|
|
13793
|
-
];
|
|
13794
14019
|
var CONFIG_FIELDS = [
|
|
13795
14020
|
{ key: "provider", label: "Provider", type: "enum", options: [...BUILT_IN_PRESET_IDS] },
|
|
13796
14021
|
{ key: "reviewerEnabled", label: "Reviewer Enabled", type: "boolean" },
|
|
@@ -15510,55 +15735,7 @@ function dashboardCommand(program2) {
|
|
|
15510
15735
|
|
|
15511
15736
|
// src/commands/doctor.ts
|
|
15512
15737
|
init_dist();
|
|
15513
|
-
|
|
15514
|
-
const issues = [];
|
|
15515
|
-
if (!webhook.events || webhook.events.length === 0) {
|
|
15516
|
-
issues.push("No events configured");
|
|
15517
|
-
} else {
|
|
15518
|
-
const validEvents = [
|
|
15519
|
-
"run_started",
|
|
15520
|
-
"run_succeeded",
|
|
15521
|
-
"run_failed",
|
|
15522
|
-
"run_timeout",
|
|
15523
|
-
"review_completed",
|
|
15524
|
-
"pr_auto_merged",
|
|
15525
|
-
"rate_limit_fallback",
|
|
15526
|
-
"qa_completed"
|
|
15527
|
-
];
|
|
15528
|
-
for (const event of webhook.events) {
|
|
15529
|
-
if (!validEvents.includes(event)) {
|
|
15530
|
-
issues.push(`Invalid event: ${event}`);
|
|
15531
|
-
}
|
|
15532
|
-
}
|
|
15533
|
-
}
|
|
15534
|
-
switch (webhook.type) {
|
|
15535
|
-
case "slack":
|
|
15536
|
-
if (!webhook.url) {
|
|
15537
|
-
issues.push("Missing URL");
|
|
15538
|
-
} else if (!webhook.url.startsWith("https://hooks.slack.com/")) {
|
|
15539
|
-
issues.push("URL should start with https://hooks.slack.com/");
|
|
15540
|
-
}
|
|
15541
|
-
break;
|
|
15542
|
-
case "discord":
|
|
15543
|
-
if (!webhook.url) {
|
|
15544
|
-
issues.push("Missing URL");
|
|
15545
|
-
} else if (!webhook.url.startsWith("https://discord.com/api/webhooks/")) {
|
|
15546
|
-
issues.push("URL should start with https://discord.com/api/webhooks/");
|
|
15547
|
-
}
|
|
15548
|
-
break;
|
|
15549
|
-
case "telegram":
|
|
15550
|
-
if (!webhook.botToken) {
|
|
15551
|
-
issues.push("Missing botToken");
|
|
15552
|
-
}
|
|
15553
|
-
if (!webhook.chatId) {
|
|
15554
|
-
issues.push("Missing chatId");
|
|
15555
|
-
}
|
|
15556
|
-
break;
|
|
15557
|
-
default:
|
|
15558
|
-
issues.push(`Unknown webhook type: ${webhook.type}`);
|
|
15559
|
-
}
|
|
15560
|
-
return issues;
|
|
15561
|
-
}
|
|
15738
|
+
init_dist();
|
|
15562
15739
|
function runCheck(checkNum, total, checkName, checkFn, options) {
|
|
15563
15740
|
step(checkNum, total, `Checking ${checkName}...`);
|
|
15564
15741
|
const result = checkFn();
|
|
@@ -15649,7 +15826,7 @@ function doctorCommand(program2) {
|
|
|
15649
15826
|
} else {
|
|
15650
15827
|
let webhookErrors = 0;
|
|
15651
15828
|
for (const webhook of config.notifications.webhooks) {
|
|
15652
|
-
const issues =
|
|
15829
|
+
const issues = validateWebhook(webhook);
|
|
15653
15830
|
if (issues.length === 0) {
|
|
15654
15831
|
success(`${webhook.type} webhook: OK`);
|
|
15655
15832
|
} else {
|
|
@@ -16626,6 +16803,9 @@ function validateConfigChanges(changes, currentConfig) {
|
|
|
16626
16803
|
if (audit.maxRuntime !== void 0 && (typeof audit.maxRuntime !== "number" || audit.maxRuntime < 60)) {
|
|
16627
16804
|
return "audit.maxRuntime must be a number >= 60";
|
|
16628
16805
|
}
|
|
16806
|
+
if (audit.createIssues !== void 0 && typeof audit.createIssues !== "boolean") {
|
|
16807
|
+
return "audit.createIssues must be a boolean";
|
|
16808
|
+
}
|
|
16629
16809
|
if (audit.targetColumn !== void 0 && !BOARD_COLUMNS.includes(audit.targetColumn)) {
|
|
16630
16810
|
return `audit.targetColumn must be one of: ${BOARD_COLUMNS.join(", ")}`;
|
|
16631
16811
|
}
|
|
@@ -16695,6 +16875,9 @@ function validateConfigChanges(changes, currentConfig) {
|
|
|
16695
16875
|
if (changes.boardProvider.projectNumber !== void 0 && (typeof changes.boardProvider.projectNumber !== "number" || !Number.isInteger(changes.boardProvider.projectNumber) || changes.boardProvider.projectNumber <= 0)) {
|
|
16696
16876
|
return "boardProvider.projectNumber must be an integer > 0";
|
|
16697
16877
|
}
|
|
16878
|
+
if (changes.boardProvider.projectTitle !== void 0 && (typeof changes.boardProvider.projectTitle !== "string" || changes.boardProvider.projectTitle.trim().length === 0)) {
|
|
16879
|
+
return "boardProvider.projectTitle must be a non-empty string";
|
|
16880
|
+
}
|
|
16698
16881
|
if (changes.boardProvider.repo !== void 0 && (typeof changes.boardProvider.repo !== "string" || changes.boardProvider.repo.trim().length === 0)) {
|
|
16699
16882
|
return "boardProvider.repo must be a non-empty string";
|
|
16700
16883
|
}
|
|
@@ -19245,7 +19428,8 @@ async function ensureBoardConfigured(config, cwd, provider, options) {
|
|
|
19245
19428
|
...config.boardProvider,
|
|
19246
19429
|
enabled: config.boardProvider?.enabled ?? true,
|
|
19247
19430
|
provider: config.boardProvider?.provider ?? "github",
|
|
19248
|
-
projectNumber: boardInfo.number
|
|
19431
|
+
projectNumber: boardInfo.number,
|
|
19432
|
+
projectTitle: boardInfo.title
|
|
19249
19433
|
}
|
|
19250
19434
|
});
|
|
19251
19435
|
if (!result.success) {
|
|
@@ -19342,7 +19526,8 @@ function boardCommand(program2) {
|
|
|
19342
19526
|
const result = saveConfig(cwd, {
|
|
19343
19527
|
boardProvider: {
|
|
19344
19528
|
...config.boardProvider,
|
|
19345
|
-
projectNumber: boardInfo.number
|
|
19529
|
+
projectNumber: boardInfo.number,
|
|
19530
|
+
projectTitle: boardInfo.title
|
|
19346
19531
|
}
|
|
19347
19532
|
});
|
|
19348
19533
|
if (!result.success) {
|
|
@@ -20146,22 +20331,12 @@ function queueCommand(program2) {
|
|
|
20146
20331
|
// src/commands/notify.ts
|
|
20147
20332
|
init_dist();
|
|
20148
20333
|
import { basename as basename13 } from "path";
|
|
20149
|
-
var VALID_EVENTS = [
|
|
20150
|
-
"run_started",
|
|
20151
|
-
"run_succeeded",
|
|
20152
|
-
"run_failed",
|
|
20153
|
-
"run_timeout",
|
|
20154
|
-
"review_completed",
|
|
20155
|
-
"rate_limit_fallback",
|
|
20156
|
-
"pr_auto_merged",
|
|
20157
|
-
"qa_completed"
|
|
20158
|
-
];
|
|
20159
20334
|
function notifyCommand(program2) {
|
|
20160
20335
|
program2.command("notify <event> <projectDir>").description("Send a notification event via configured webhooks").option("--prd <name>", "PRD name").option("--branch <name>", "Branch name").option("--provider <name>", "Provider name").option("--exit-code <n>", "Exit code", "0").option("--pr-number <n>", "PR number").action(
|
|
20161
20336
|
async (event, projectDir, options) => {
|
|
20162
|
-
if (!
|
|
20337
|
+
if (!NOTIFICATION_EVENTS.includes(event)) {
|
|
20163
20338
|
process.stderr.write(
|
|
20164
|
-
`Invalid event: ${event}. Must be one of: ${
|
|
20339
|
+
`Invalid event: ${event}. Must be one of: ${NOTIFICATION_EVENTS.join(", ")}
|
|
20165
20340
|
`
|
|
20166
20341
|
);
|
|
20167
20342
|
process.exit(2);
|