@jonit-dev/night-watch-cli 1.8.12-beta.0 → 1.8.12-beta.3

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.
Files changed (137) hide show
  1. package/dist/cli.d.ts +3 -0
  2. package/dist/cli.js +1812 -631
  3. package/dist/cli.js.map +1 -0
  4. package/dist/commands/agent.d.ts +12 -0
  5. package/dist/commands/agent.d.ts.map +1 -0
  6. package/dist/commands/agent.js +307 -0
  7. package/dist/commands/agent.js.map +1 -0
  8. package/dist/commands/analytics.d.ts +14 -0
  9. package/dist/commands/analytics.js +69 -0
  10. package/dist/commands/analytics.js.map +1 -0
  11. package/dist/commands/audit.d.ts +19 -0
  12. package/dist/commands/audit.js +144 -0
  13. package/dist/commands/audit.js.map +1 -0
  14. package/dist/commands/board.d.ts +9 -0
  15. package/dist/commands/board.js +702 -0
  16. package/dist/commands/board.js.map +1 -0
  17. package/dist/commands/cancel.d.ts +46 -0
  18. package/dist/commands/cancel.js +239 -0
  19. package/dist/commands/cancel.js.map +1 -0
  20. package/dist/commands/cron.d.ts +8 -0
  21. package/dist/commands/cron.js +134 -0
  22. package/dist/commands/cron.js.map +1 -0
  23. package/dist/commands/dashboard/tab-actions.d.ts +10 -0
  24. package/dist/commands/dashboard/tab-actions.js +247 -0
  25. package/dist/commands/dashboard/tab-actions.js.map +1 -0
  26. package/dist/commands/dashboard/tab-config.d.ts +21 -0
  27. package/dist/commands/dashboard/tab-config.js +874 -0
  28. package/dist/commands/dashboard/tab-config.js.map +1 -0
  29. package/dist/commands/dashboard/tab-logs.d.ts +10 -0
  30. package/dist/commands/dashboard/tab-logs.js +202 -0
  31. package/dist/commands/dashboard/tab-logs.js.map +1 -0
  32. package/dist/commands/dashboard/tab-schedules.d.ts +21 -0
  33. package/dist/commands/dashboard/tab-schedules.js +320 -0
  34. package/dist/commands/dashboard/tab-schedules.js.map +1 -0
  35. package/dist/commands/dashboard/tab-status.d.ts +32 -0
  36. package/dist/commands/dashboard/tab-status.js +424 -0
  37. package/dist/commands/dashboard/tab-status.js.map +1 -0
  38. package/dist/commands/dashboard/types.d.ts +42 -0
  39. package/dist/commands/dashboard/types.js +5 -0
  40. package/dist/commands/dashboard/types.js.map +1 -0
  41. package/dist/commands/dashboard.d.ts +11 -0
  42. package/dist/commands/dashboard.js +242 -0
  43. package/dist/commands/dashboard.js.map +1 -0
  44. package/dist/commands/doctor.d.ts +16 -0
  45. package/dist/commands/doctor.js +195 -0
  46. package/dist/commands/doctor.js.map +1 -0
  47. package/dist/commands/history.d.ts +7 -0
  48. package/dist/commands/history.js +49 -0
  49. package/dist/commands/history.js.map +1 -0
  50. package/dist/commands/init.d.ts +45 -0
  51. package/dist/commands/init.d.ts.map +1 -1
  52. package/dist/commands/init.js +12 -0
  53. package/dist/commands/init.js.map +1 -1
  54. package/dist/commands/install.d.ts +65 -0
  55. package/dist/commands/install.js +405 -0
  56. package/dist/commands/install.js.map +1 -0
  57. package/dist/commands/logs.d.ts +15 -0
  58. package/dist/commands/logs.js +155 -0
  59. package/dist/commands/logs.js.map +1 -0
  60. package/dist/commands/merge.d.ts +26 -0
  61. package/dist/commands/merge.js +159 -0
  62. package/dist/commands/merge.js.map +1 -0
  63. package/dist/commands/notify.d.ts +7 -0
  64. package/dist/commands/notify.js +43 -0
  65. package/dist/commands/notify.js.map +1 -0
  66. package/dist/commands/plan.d.ts +19 -0
  67. package/dist/commands/plan.js +88 -0
  68. package/dist/commands/plan.js.map +1 -0
  69. package/dist/commands/prd-state.d.ts +12 -0
  70. package/dist/commands/prd-state.js +47 -0
  71. package/dist/commands/prd-state.js.map +1 -0
  72. package/dist/commands/prd.d.ts +18 -0
  73. package/dist/commands/prd.js +363 -0
  74. package/dist/commands/prd.js.map +1 -0
  75. package/dist/commands/prds.d.ts +13 -0
  76. package/dist/commands/prds.js +194 -0
  77. package/dist/commands/prds.js.map +1 -0
  78. package/dist/commands/prs.d.ts +14 -0
  79. package/dist/commands/prs.js +104 -0
  80. package/dist/commands/prs.js.map +1 -0
  81. package/dist/commands/qa.d.ts +34 -0
  82. package/dist/commands/qa.js +214 -0
  83. package/dist/commands/qa.js.map +1 -0
  84. package/dist/commands/queue.d.ts +8 -0
  85. package/dist/commands/queue.d.ts.map +1 -1
  86. package/dist/commands/queue.js +398 -0
  87. package/dist/commands/queue.js.map +1 -0
  88. package/dist/commands/resolve.d.ts +26 -0
  89. package/dist/commands/resolve.js +186 -0
  90. package/dist/commands/resolve.js.map +1 -0
  91. package/dist/commands/retry.d.ts +9 -0
  92. package/dist/commands/retry.js +71 -0
  93. package/dist/commands/retry.js.map +1 -0
  94. package/dist/commands/review.d.ts +77 -0
  95. package/dist/commands/review.d.ts.map +1 -1
  96. package/dist/commands/review.js +1 -74
  97. package/dist/commands/review.js.map +1 -1
  98. package/dist/commands/run.d.ts +73 -0
  99. package/dist/commands/serve.d.ts +19 -0
  100. package/dist/commands/serve.js +142 -0
  101. package/dist/commands/serve.js.map +1 -0
  102. package/dist/commands/shared/env-builder.d.ts +49 -0
  103. package/dist/commands/shared/env-builder.d.ts.map +1 -1
  104. package/dist/commands/shared/env-builder.js +152 -0
  105. package/dist/commands/shared/env-builder.js.map +1 -0
  106. package/dist/commands/slice.d.ts +35 -0
  107. package/dist/commands/slice.js +316 -0
  108. package/dist/commands/slice.js.map +1 -0
  109. package/dist/commands/state.d.ts +8 -0
  110. package/dist/commands/state.js +54 -0
  111. package/dist/commands/state.js.map +1 -0
  112. package/dist/commands/status.d.ts +14 -0
  113. package/dist/commands/status.js +297 -0
  114. package/dist/commands/status.js.map +1 -0
  115. package/dist/commands/summary.d.ts +14 -0
  116. package/dist/commands/summary.js +193 -0
  117. package/dist/commands/summary.js.map +1 -0
  118. package/dist/commands/uninstall.d.ts +25 -0
  119. package/dist/commands/uninstall.js +134 -0
  120. package/dist/commands/uninstall.js.map +1 -0
  121. package/dist/commands/update.d.ts +22 -0
  122. package/dist/commands/update.js +90 -0
  123. package/dist/commands/update.js.map +1 -0
  124. package/dist/scripts/night-watch-audit-cron.sh +1 -0
  125. package/dist/scripts/night-watch-cron.sh +1 -0
  126. package/dist/scripts/night-watch-helpers.sh +22 -1
  127. package/dist/scripts/night-watch-merger-cron.sh +32 -3
  128. package/dist/scripts/night-watch-plan-cron.sh +2 -0
  129. package/dist/scripts/night-watch-pr-resolver-cron.sh +6 -0
  130. package/dist/scripts/night-watch-pr-reviewer-cron.sh +18 -5
  131. package/dist/scripts/night-watch-qa-cron.sh +8 -1
  132. package/dist/scripts/night-watch-slicer-cron.sh +3 -0
  133. package/dist/templates/night-watch.config.json +21 -20
  134. package/dist/web/assets/index-C-xpWpS8.css +1 -0
  135. package/dist/web/assets/index-CEYe-290.js +412 -0
  136. package/dist/web/index.html +2 -2
  137. package/package.json +1 -1
@@ -0,0 +1,134 @@
1
+ /**
2
+ * Uninstall command for Night Watch CLI
3
+ * Removes crontab entries for the current project
4
+ */
5
+ import * as path from 'path';
6
+ import * as fs from 'fs';
7
+ import { dim, generateMarker, getEntries, getProjectEntries, getProjectName, removeEntriesForProject, success, error as uiError, unregisterProject, warn, } from '@night-watch/core';
8
+ /**
9
+ * Core uninstall logic, reusable from dashboard.
10
+ * Returns result without printing to console.
11
+ */
12
+ export function performUninstall(projectDir, options) {
13
+ try {
14
+ const projectName = getProjectName(projectDir);
15
+ const marker = generateMarker(projectName);
16
+ const existingEntries = Array.from(new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
17
+ if (existingEntries.length === 0) {
18
+ unregisterProject(projectDir);
19
+ return { success: true, removedCount: 0 };
20
+ }
21
+ const removedCount = removeEntriesForProject(projectDir, marker);
22
+ unregisterProject(projectDir);
23
+ if (!options?.keepLogs) {
24
+ const logDir = path.join(projectDir, 'logs');
25
+ if (fs.existsSync(logDir)) {
26
+ const logFiles = [
27
+ 'executor.log',
28
+ 'reviewer.log',
29
+ 'slicer.log',
30
+ 'audit.log',
31
+ 'pr-resolver.log',
32
+ ];
33
+ logFiles.forEach((logFile) => {
34
+ const logPath = path.join(logDir, logFile);
35
+ if (fs.existsSync(logPath)) {
36
+ fs.unlinkSync(logPath);
37
+ }
38
+ });
39
+ try {
40
+ const remainingFiles = fs.readdirSync(logDir);
41
+ if (remainingFiles.length === 0) {
42
+ fs.rmdirSync(logDir);
43
+ }
44
+ }
45
+ catch {
46
+ // Ignore errors removing directory
47
+ }
48
+ }
49
+ }
50
+ return { success: true, removedCount };
51
+ }
52
+ catch (err) {
53
+ return {
54
+ success: false,
55
+ removedCount: 0,
56
+ error: err instanceof Error ? err.message : String(err),
57
+ };
58
+ }
59
+ }
60
+ /**
61
+ * Uninstall crontab entries for night-watch
62
+ */
63
+ export function uninstallCommand(program) {
64
+ program
65
+ .command('uninstall')
66
+ .description('Remove crontab entries')
67
+ .option('--keep-logs', 'Preserve log files')
68
+ .action(async (options) => {
69
+ try {
70
+ // Get project directory
71
+ const projectDir = process.cwd();
72
+ // Get project name
73
+ const projectName = getProjectName(projectDir);
74
+ const marker = generateMarker(projectName);
75
+ // Check if there are entries to remove
76
+ const existingEntries = Array.from(new Set([...getEntries(marker), ...getProjectEntries(projectDir)]));
77
+ if (existingEntries.length === 0) {
78
+ warn(`No Night Watch crontab entries found for ${projectName}.`);
79
+ dim('Nothing to uninstall.');
80
+ return;
81
+ }
82
+ // Show entries that will be removed
83
+ dim(`Removing Night Watch crontab entries for ${projectName}:`);
84
+ existingEntries.forEach((entry) => dim(` ${entry}`));
85
+ // Remove entries
86
+ const removedCount = removeEntriesForProject(projectDir, marker);
87
+ // Handle log files
88
+ if (!options.keepLogs) {
89
+ const logDir = path.join(projectDir, 'logs');
90
+ if (fs.existsSync(logDir)) {
91
+ const logFiles = [
92
+ 'executor.log',
93
+ 'reviewer.log',
94
+ 'slicer.log',
95
+ 'audit.log',
96
+ 'pr-resolver.log',
97
+ ];
98
+ let logsRemoved = 0;
99
+ logFiles.forEach((logFile) => {
100
+ const logPath = path.join(logDir, logFile);
101
+ if (fs.existsSync(logPath)) {
102
+ fs.unlinkSync(logPath);
103
+ logsRemoved++;
104
+ }
105
+ });
106
+ // Try to remove log directory if empty
107
+ try {
108
+ const remainingFiles = fs.readdirSync(logDir);
109
+ if (remainingFiles.length === 0) {
110
+ fs.rmdirSync(logDir);
111
+ }
112
+ }
113
+ catch {
114
+ // Ignore errors removing directory
115
+ }
116
+ if (logsRemoved > 0) {
117
+ console.log();
118
+ dim(`Removed ${logsRemoved} log file(s).`);
119
+ }
120
+ }
121
+ }
122
+ else {
123
+ console.log();
124
+ dim('Log files preserved.');
125
+ }
126
+ success(`Successfully removed ${removedCount} crontab entry/entries.`);
127
+ }
128
+ catch (err) {
129
+ uiError(`Error uninstalling Night Watch: ${err instanceof Error ? err.message : String(err)}`);
130
+ process.exit(1);
131
+ }
132
+ });
133
+ }
134
+ //# sourceMappingURL=uninstall.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uninstall.js","sourceRoot":"","sources":["../../src/commands/uninstall.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAGH,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,EAAE,MAAM,IAAI,CAAC;AACzB,OAAO,EACL,GAAG,EACH,cAAc,EACd,UAAU,EACV,iBAAiB,EACjB,cAAc,EACd,uBAAuB,EACvB,OAAO,EACP,KAAK,IAAI,OAAO,EAChB,iBAAiB,EACjB,IAAI,GACL,MAAM,mBAAmB,CAAC;AAY3B;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAC9B,UAAkB,EAClB,OAAgC;IAEhC,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;QAC/C,MAAM,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;QAE3C,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAChC,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,CACnE,CAAC;QACF,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACjC,iBAAiB,CAAC,UAAU,CAAC,CAAC;YAC9B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC,EAAE,CAAC;QAC5C,CAAC;QAED,MAAM,YAAY,GAAG,uBAAuB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QACjE,iBAAiB,CAAC,UAAU,CAAC,CAAC;QAE9B,IAAI,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC;YACvB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAC7C,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;gBAC1B,MAAM,QAAQ,GAAG;oBACf,cAAc;oBACd,cAAc;oBACd,YAAY;oBACZ,WAAW;oBACX,iBAAiB;iBAClB,CAAC;gBACF,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;oBAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;oBAC3C,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;wBAC3B,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;oBACzB,CAAC;gBACH,CAAC,CAAC,CAAC;gBAEH,IAAI,CAAC;oBACH,MAAM,cAAc,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;oBAC9C,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;wBAChC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;oBACvB,CAAC;gBACH,CAAC;gBAAC,MAAM,CAAC;oBACP,mCAAmC;gBACrC,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO;YACL,OAAO,EAAE,KAAK;YACd,YAAY,EAAE,CAAC;YACf,KAAK,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;SACxD,CAAC;IACJ,CAAC;AACH,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gBAAgB,CAAC,OAAgB;IAC/C,OAAO;SACJ,OAAO,CAAC,WAAW,CAAC;SACpB,WAAW,CAAC,wBAAwB,CAAC;SACrC,MAAM,CAAC,aAAa,EAAE,oBAAoB,CAAC;SAC3C,MAAM,CAAC,KAAK,EAAE,OAA0B,EAAE,EAAE;QAC3C,IAAI,CAAC;YACH,wBAAwB;YACxB,MAAM,UAAU,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC;YAEjC,mBAAmB;YACnB,MAAM,WAAW,GAAG,cAAc,CAAC,UAAU,CAAC,CAAC;YAC/C,MAAM,MAAM,GAAG,cAAc,CAAC,WAAW,CAAC,CAAC;YAE3C,uCAAuC;YACvC,MAAM,eAAe,GAAG,KAAK,CAAC,IAAI,CAChC,IAAI,GAAG,CAAC,CAAC,GAAG,UAAU,CAAC,MAAM,CAAC,EAAE,GAAG,iBAAiB,CAAC,UAAU,CAAC,CAAC,CAAC,CACnE,CAAC;YACF,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC,4CAA4C,WAAW,GAAG,CAAC,CAAC;gBACjE,GAAG,CAAC,uBAAuB,CAAC,CAAC;gBAC7B,OAAO;YACT,CAAC;YAED,oCAAoC;YACpC,GAAG,CAAC,4CAA4C,WAAW,GAAG,CAAC,CAAC;YAChE,eAAe,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,KAAK,KAAK,EAAE,CAAC,CAAC,CAAC;YAEtD,iBAAiB;YACjB,MAAM,YAAY,GAAG,uBAAuB,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;YAEjE,mBAAmB;YACnB,IAAI,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;gBACtB,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;gBAC7C,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;oBAC1B,MAAM,QAAQ,GAAG;wBACf,cAAc;wBACd,cAAc;wBACd,YAAY;wBACZ,WAAW;wBACX,iBAAiB;qBAClB,CAAC;oBACF,IAAI,WAAW,GAAG,CAAC,CAAC;oBAEpB,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;wBAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;wBAC3C,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;4BAC3B,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;4BACvB,WAAW,EAAE,CAAC;wBAChB,CAAC;oBACH,CAAC,CAAC,CAAC;oBAEH,uCAAuC;oBACvC,IAAI,CAAC;wBACH,MAAM,cAAc,GAAG,EAAE,CAAC,WAAW,CAAC,MAAM,CAAC,CAAC;wBAC9C,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;4BAChC,EAAE,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;wBACvB,CAAC;oBACH,CAAC;oBAAC,MAAM,CAAC;wBACP,mCAAmC;oBACrC,CAAC;oBAED,IAAI,WAAW,GAAG,CAAC,EAAE,CAAC;wBACpB,OAAO,CAAC,GAAG,EAAE,CAAC;wBACd,GAAG,CAAC,WAAW,WAAW,eAAe,CAAC,CAAC;oBAC7C,CAAC;gBACH,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,GAAG,EAAE,CAAC;gBACd,GAAG,CAAC,sBAAsB,CAAC,CAAC;YAC9B,CAAC;YAED,OAAO,CAAC,wBAAwB,YAAY,yBAAyB,CAAC,CAAC;QACzE,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CACL,mCAAmC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,CACtF,CAAC;YACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;IACH,CAAC,CAAC,CAAC;AACP,CAAC"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Update command for Night Watch CLI
3
+ * Reinstalls global CLI and refreshes cron entries for one or more projects.
4
+ */
5
+ import { Command } from 'commander';
6
+ export declare const DEFAULT_GLOBAL_SPEC = "@jonit-dev/night-watch-cli@latest";
7
+ export interface IUpdateOptions {
8
+ projects?: string;
9
+ globalSpec: string;
10
+ global?: boolean;
11
+ }
12
+ /**
13
+ * Parse project directories from a comma-separated CLI option.
14
+ * Defaults to current working directory when option is omitted.
15
+ */
16
+ export declare function parseProjectDirs(projects: string | undefined, cwd: string): string[];
17
+ export declare function shouldInstallGlobal(options: Pick<IUpdateOptions, 'global'>): boolean;
18
+ /**
19
+ * Register update command.
20
+ */
21
+ export declare function updateCommand(program: Command): void;
22
+ //# sourceMappingURL=update.d.ts.map
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Update command for Night Watch CLI
3
+ * Reinstalls global CLI and refreshes cron entries for one or more projects.
4
+ */
5
+ import { spawnSync } from 'child_process';
6
+ import * as fs from 'fs';
7
+ import * as path from 'path';
8
+ import { dim, success, error as uiError, warn } from '@night-watch/core';
9
+ export const DEFAULT_GLOBAL_SPEC = '@jonit-dev/night-watch-cli@latest';
10
+ /**
11
+ * Parse project directories from a comma-separated CLI option.
12
+ * Defaults to current working directory when option is omitted.
13
+ */
14
+ export function parseProjectDirs(projects, cwd) {
15
+ if (!projects || projects.trim().length === 0) {
16
+ return [cwd];
17
+ }
18
+ const dirs = projects
19
+ .split(',')
20
+ .map((entry) => entry.trim())
21
+ .filter((entry) => entry.length > 0)
22
+ .map((entry) => path.resolve(cwd, entry));
23
+ return Array.from(new Set(dirs));
24
+ }
25
+ export function shouldInstallGlobal(options) {
26
+ return options.global !== false;
27
+ }
28
+ function runCommand(command, args, cwd) {
29
+ const result = spawnSync(command, args, {
30
+ cwd,
31
+ env: process.env,
32
+ stdio: 'inherit',
33
+ });
34
+ if (result.error) {
35
+ throw result.error;
36
+ }
37
+ if ((result.status ?? 1) !== 0) {
38
+ const location = cwd ? ` in ${cwd}` : '';
39
+ throw new Error(`Command failed${location}: ${command} ${args.join(' ')}`);
40
+ }
41
+ }
42
+ function resolveNightWatchBin() {
43
+ const result = spawnSync('which', ['night-watch'], {
44
+ encoding: 'utf-8',
45
+ env: process.env,
46
+ });
47
+ if (result.status === 0 && typeof result.stdout === 'string' && result.stdout.trim().length > 0) {
48
+ return result.stdout.trim();
49
+ }
50
+ return 'night-watch';
51
+ }
52
+ /**
53
+ * Register update command.
54
+ */
55
+ export function updateCommand(program) {
56
+ program
57
+ .command('update')
58
+ .description('Update global CLI and refresh cron for project(s)')
59
+ .option('--projects <dirs>', 'Comma-separated project directories (default: current directory)')
60
+ .option('--global-spec <spec>', 'npm package spec used for global install', DEFAULT_GLOBAL_SPEC)
61
+ .option('--no-global', 'Skip global npm install and only refresh project cron')
62
+ .action(async (options) => {
63
+ try {
64
+ const cwd = process.cwd();
65
+ const projectDirs = parseProjectDirs(options.projects, cwd);
66
+ if (shouldInstallGlobal(options)) {
67
+ dim(`Updating global install: npm install -g ${options.globalSpec}`);
68
+ runCommand('npm', ['install', '-g', options.globalSpec]);
69
+ success('Global CLI update completed.');
70
+ }
71
+ const nightWatchBin = resolveNightWatchBin();
72
+ for (const projectDir of projectDirs) {
73
+ if (!fs.existsSync(projectDir) || !fs.statSync(projectDir).isDirectory()) {
74
+ warn(`Skipping invalid project directory: ${projectDir}`);
75
+ continue;
76
+ }
77
+ dim(`Refreshing cron in ${projectDir}`);
78
+ runCommand(nightWatchBin, ['uninstall'], projectDir);
79
+ runCommand(nightWatchBin, ['install'], projectDir);
80
+ success(`Refreshed project: ${projectDir}`);
81
+ }
82
+ success('Update completed.');
83
+ }
84
+ catch (err) {
85
+ uiError(`Update failed: ${err instanceof Error ? err.message : String(err)}`);
86
+ process.exit(1);
87
+ }
88
+ });
89
+ }
90
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"update.js","sourceRoot":"","sources":["../../src/commands/update.ts"],"names":[],"mappings":"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"}
@@ -28,6 +28,7 @@ mkdir -p "${LOG_DIR}"
28
28
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
29
29
  # shellcheck source=night-watch-helpers.sh
30
30
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
31
+ skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
31
32
 
32
33
  # emit_result helper - must be defined before use
33
34
  emit_result() {
@@ -50,6 +50,7 @@ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
50
50
  # NOTE: Lock file path must match executorLockPath() in src/utils/status-data.ts
51
51
  LOCK_FILE="/tmp/night-watch-${PROJECT_RUNTIME_KEY}.lock"
52
52
  SCRIPT_TYPE="executor"
53
+ skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
53
54
 
54
55
  emit_result() {
55
56
  local status="${1:?status required}"
@@ -126,6 +126,7 @@ project_git_push_command() {
126
126
  NW_EXECUTOR_PARTIAL_LABEL="${NW_EXECUTOR_PARTIAL_LABEL:-nw:partial}"
127
127
  NW_EXECUTOR_RESUMABLE_LABEL="${NW_EXECUTOR_RESUMABLE_LABEL:-nw:resumable}"
128
128
  NW_EXECUTOR_READY_REVIEW_LABEL="${NW_EXECUTOR_READY_REVIEW_LABEL:-nw:ready-review}"
129
+ NW_PR_RESOLVER_READY_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
129
130
 
130
131
  csv_has_label() {
131
132
  local csv="${1:-}"
@@ -188,7 +189,8 @@ find_executor_resume_pr() {
188
189
  printf '%s' "${pr_list}" \
189
190
  | jq -c \
190
191
  --arg primary_prefix "${branch_prefix}/" \
191
- --arg resumable_label "${NW_EXECUTOR_RESUMABLE_LABEL}" '
192
+ --arg resumable_label "${NW_EXECUTOR_RESUMABLE_LABEL}" \
193
+ --arg ready_label "${NW_PR_RESOLVER_READY_LABEL}" '
192
194
  [
193
195
  .[]
194
196
  | select(
@@ -198,6 +200,7 @@ find_executor_resume_pr() {
198
200
  )
199
201
  | .labelNames = ((.labels // []) | map(.name))
200
202
  | select((.labelNames | index($resumable_label)) != null)
203
+ | select((.labelNames | index($ready_label)) == null)
201
204
  ]
202
205
  | sort_by(.createdAt // "")
203
206
  | .[0] // empty
@@ -1335,6 +1338,22 @@ arm_global_queue_cleanup() {
1335
1338
  append_exit_trap "__night_watch_queue_cleanup \$?"
1336
1339
  }
1337
1340
 
1341
+ skip_if_job_paused() {
1342
+ local script_type="${1:?script_type required}"
1343
+ local project_dir="${2:?project_dir required}"
1344
+
1345
+ local cli_bin
1346
+ cli_bin=$(resolve_night_watch_cli) || return 0
1347
+
1348
+ if (cd "${project_dir}" && "${cli_bin}" job is-paused "${script_type}" >/dev/null 2>&1); then
1349
+ log "SKIP: ${script_type} is paused in night-watch.config.json"
1350
+ if command -v emit_result >/dev/null 2>&1; then
1351
+ emit_result "skip_paused"
1352
+ fi
1353
+ exit 0
1354
+ fi
1355
+ }
1356
+
1338
1357
  # Atomically claim a queue slot or enqueue for later dispatch.
1339
1358
  # Uses DB transaction (via `queue claim` CLI) for atomicity — no flock needed.
1340
1359
  # Sets NW_QUEUE_ENTRY_ID on success and arms the cleanup trap.
@@ -1342,6 +1361,8 @@ arm_global_queue_cleanup() {
1342
1361
  claim_or_enqueue() {
1343
1362
  local script_type="${1:?script_type required}"
1344
1363
  local project_dir="${2:?project_dir required}"
1364
+ skip_if_job_paused "${script_type}" "${project_dir}"
1365
+
1345
1366
  local provider_key
1346
1367
  provider_key=$(resolve_provider_key "${project_dir}" "${script_type}")
1347
1368
 
@@ -27,6 +27,7 @@ MIN_REVIEW_SCORE="${NW_MERGER_MIN_REVIEW_SCORE:-80}"
27
27
  REBASE_BEFORE_MERGE="${NW_MERGER_REBASE_BEFORE_MERGE:-1}"
28
28
  MAX_PRS_PER_RUN="${NW_MERGER_MAX_PRS_PER_RUN:-0}"
29
29
  BRANCH_PATTERNS_RAW="${NW_MERGER_BRANCH_PATTERNS:-}"
30
+ READY_TO_MERGE_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
30
31
  SCRIPT_START_TIME=$(date +%s)
31
32
  DRY_RUN="${NW_DRY_RUN:-0}"
32
33
  PROVIDER_CMD="${NW_PROVIDER_CMD:-claude}"
@@ -55,6 +56,7 @@ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
55
56
  # NOTE: Lock file path must match mergerLockPath() in src/utils/status-data.ts
56
57
  LOCK_FILE="/tmp/night-watch-merger-${PROJECT_RUNTIME_KEY}.lock"
57
58
  SCRIPT_TYPE="merger"
59
+ skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
58
60
 
59
61
  MERGED_PRS=0
60
62
  FAILED_PRS=0
@@ -140,6 +142,23 @@ rebase_pr() {
140
142
  return $?
141
143
  }
142
144
 
145
+ cleanup_watchdog() {
146
+ local pid="${1:-}"
147
+ local child_pids=""
148
+
149
+ if [ -z "${pid}" ]; then
150
+ return 0
151
+ fi
152
+
153
+ child_pids=$(pgrep -P "${pid}" 2>/dev/null || true)
154
+ if [ -n "${child_pids}" ]; then
155
+ kill ${child_pids} 2>/dev/null || true
156
+ fi
157
+
158
+ kill "${pid}" 2>/dev/null || true
159
+ wait "${pid}" 2>/dev/null || true
160
+ }
161
+
143
162
  log() {
144
163
  echo "[$(date '+%Y-%m-%dT%H:%M:%S')] $*" | tee -a "${LOG_FILE}"
145
164
  }
@@ -158,7 +177,7 @@ cd "${PROJECT_DIR}"
158
177
 
159
178
  log "========================================"
160
179
  log "RUN-START: merger invoked project=${PROJECT_DIR} dry_run=${DRY_RUN}"
161
- log "CONFIG: merge_method=${MERGE_METHOD} min_review_score=${MIN_REVIEW_SCORE} rebase_before_merge=${REBASE_BEFORE_MERGE} max_prs=${MAX_PRS_PER_RUN} max_runtime=${MAX_RUNTIME}s branch_patterns=${BRANCH_PATTERNS_RAW:-<all>}"
180
+ log "CONFIG: merge_method=${MERGE_METHOD} min_review_score=${MIN_REVIEW_SCORE} rebase_before_merge=${REBASE_BEFORE_MERGE} max_prs=${MAX_PRS_PER_RUN} max_runtime=${MAX_RUNTIME}s ready_label=${READY_TO_MERGE_LABEL} branch_patterns=${BRANCH_PATTERNS_RAW:-<all>}"
162
181
  log "========================================"
163
182
 
164
183
  if ! acquire_lock "${LOCK_FILE}"; then
@@ -187,7 +206,7 @@ fi
187
206
  kill -TERM $$ 2>/dev/null || true
188
207
  ) &
189
208
  WATCHDOG_PID=$!
190
- append_exit_trap "kill ${WATCHDOG_PID} 2>/dev/null || true"
209
+ append_exit_trap "cleanup_watchdog ${WATCHDOG_PID}"
191
210
 
192
211
  # Discover open PRs sorted by creation date (oldest first = FIFO)
193
212
  log "INFO: Scanning open PRs..."
@@ -224,6 +243,11 @@ while IFS= read -r pr_json; do
224
243
  continue
225
244
  fi
226
245
 
246
+ if csv_has_label "${pr_labels:-}" "${READY_TO_MERGE_LABEL}"; then
247
+ log "INFO: PR #${pr_number} (${pr_branch}): Skipping PR labeled ${READY_TO_MERGE_LABEL}"
248
+ continue
249
+ fi
250
+
227
251
  # Check branch pattern filter
228
252
  if ! matches_branch_patterns "${pr_branch}"; then
229
253
  log "DEBUG: PR #${pr_number} (${pr_branch}): Branch pattern mismatch, skipping"
@@ -284,12 +308,17 @@ while IFS= read -r pr_json; do
284
308
  # Rebase remaining PRs after each successful merge
285
309
  log "INFO: Rebasing remaining open PRs after merging #${pr_number}..."
286
310
  REMAINING_JSON=$(gh pr list --state open \
287
- --json number,headRefName \
311
+ --json number,headRefName,labels \
288
312
  2>/dev/null || echo "[]")
289
313
  while IFS= read -r remaining_pr; do
290
314
  remaining_number=$(echo "${remaining_pr}" | jq -r '.number')
291
315
  remaining_branch=$(echo "${remaining_pr}" | jq -r '.headRefName')
316
+ remaining_labels=$(echo "${remaining_pr}" | jq -r '[.labels[]?.name] | join(",")')
292
317
  if [ "${remaining_number}" != "${pr_number}" ]; then
318
+ if csv_has_label "${remaining_labels:-}" "${READY_TO_MERGE_LABEL}"; then
319
+ log "INFO: Skipping post-merge rebase for PR #${remaining_number} (${remaining_branch}) because it is labeled ${READY_TO_MERGE_LABEL}"
320
+ continue
321
+ fi
293
322
  log "INFO: Rebasing remaining PR #${remaining_number} (${remaining_branch})"
294
323
  gh pr update-branch --rebase "${remaining_number}" 2>/dev/null || \
295
324
  log "WARN: PR #${remaining_number}: Rebase failed (continuing)"
@@ -61,6 +61,8 @@ PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PRO
61
61
  PRD_DIR="${NW_PRD_DIR:-docs/PRDs}"
62
62
  PLAN_TASK="${NW_PLAN_TASK:-}"
63
63
 
64
+ skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
65
+
64
66
  rotate_log
65
67
  log_separator
66
68
  log "RUN-START: planner invoked project=${PROJECT_DIR} provider=${PROVIDER_CMD} dry_run=${NW_DRY_RUN:-0}"
@@ -63,6 +63,7 @@ PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PRO
63
63
  # NOTE: Lock file path must match resolverLockPath() in src/utils/status-data.ts
64
64
  LOCK_FILE="/tmp/night-watch-pr-resolver-${PROJECT_RUNTIME_KEY}.lock"
65
65
  SCRIPT_TYPE="pr-resolver"
66
+ skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
66
67
 
67
68
  emit_result() {
68
69
  local status="${1:?status required}"
@@ -359,6 +360,11 @@ while IFS= read -r pr_line; do
359
360
  continue
360
361
  fi
361
362
 
363
+ if csv_has_label "${labels:-}" "${READY_LABEL}"; then
364
+ log "INFO: Skipping PR #${pr_number} (${pr_branch}) because it is labeled ${READY_LABEL}"
365
+ continue
366
+ fi
367
+
362
368
  # Apply branch pattern filter
363
369
  if ! matches_branch_patterns "${pr_branch}"; then
364
370
  log "DEBUG: Skipping PR #${pr_number} — branch '${pr_branch}' does not match patterns" "patterns=${BRANCH_PATTERNS_RAW}"
@@ -63,11 +63,6 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
63
63
  # shellcheck source=night-watch-helpers.sh
64
64
  source "${SCRIPT_DIR}/night-watch-helpers.sh"
65
65
 
66
- # Ensure provider CLI is on PATH (nvm, fnm, volta, common bin dirs)
67
- if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
68
- echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
69
- exit 127
70
- fi
71
66
  PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
72
67
  PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
73
68
  GLOBAL_LOCK_FILE="/tmp/night-watch-pr-reviewer-${PROJECT_RUNTIME_KEY}.lock"
@@ -79,8 +74,10 @@ else
79
74
  fi
80
75
 
81
76
  SCRIPT_TYPE="reviewer"
77
+ skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
82
78
  READY_FOR_REVIEW_LABEL="${NW_READY_FOR_REVIEW_LABEL:-ready-for-review}"
83
79
  READY_FOR_REVIEW_MARKER_NAME="night-watch-ready-for-review"
80
+ READY_TO_MERGE_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
84
81
 
85
82
  emit_result() {
86
83
  local status="${1:?status required}"
@@ -92,6 +89,13 @@ emit_result() {
92
89
  fi
93
90
  }
94
91
 
92
+ require_provider_on_path() {
93
+ if ! ensure_provider_on_path "${PROVIDER_CMD}"; then
94
+ echo "ERROR: Provider '${PROVIDER_CMD}' not found in PATH or common installation locations" >&2
95
+ exit 127
96
+ fi
97
+ }
98
+
95
99
  extract_review_score_from_text() {
96
100
  local review_text="${1:-}"
97
101
  printf '%s' "${review_text}" \
@@ -701,6 +705,11 @@ while IFS=$'\t' read -r pr_number pr_branch pr_labels; do
701
705
  continue
702
706
  fi
703
707
 
708
+ if csv_has_label "${pr_labels:-}" "${READY_TO_MERGE_LABEL}"; then
709
+ log "INFO: PR #${pr_number} (${pr_branch}) is labeled ${READY_TO_MERGE_LABEL}; skipping automated review"
710
+ continue
711
+ fi
712
+
704
713
  if printf '%s\n' "${pr_labels:-}" | tr ',' '\n' | grep -Fxq 'needs-human-review'; then
705
714
  log "INFO: PR #${pr_number} (${pr_branch}) is labeled needs-human-review; skipping automated review"
706
715
  continue
@@ -847,6 +856,8 @@ if [ -z "${TARGET_PR}" ] && [ "${WORKER_MODE}" != "1" ] && [ "${PARALLEL_ENABLED
847
856
  exit 0
848
857
  fi
849
858
 
859
+ require_provider_on_path
860
+
850
861
  log "PARALLEL: Launching ${#PR_NUMBER_ARRAY[@]} reviewer worker(s)"
851
862
 
852
863
  declare -a WORKER_PIDS=()
@@ -1016,6 +1027,8 @@ if [ "${NW_DRY_RUN:-0}" = "1" ]; then
1016
1027
  exit 0
1017
1028
  fi
1018
1029
 
1030
+ require_provider_on_path
1031
+
1019
1032
  if ! prepare_detached_worktree "${PROJECT_DIR}" "${REVIEW_WORKTREE_DIR}" "${DEFAULT_BRANCH}" "${LOG_FILE}"; then
1020
1033
  log "FAIL: Unable to create isolated reviewer worktree ${REVIEW_WORKTREE_DIR}"
1021
1034
  exit 1
@@ -26,6 +26,7 @@ PROVIDER_LABEL="${NW_PROVIDER_LABEL:-}"
26
26
  BRANCH_PATTERNS_RAW="${NW_BRANCH_PATTERNS:-feat/,night-watch/}"
27
27
  SKIP_LABEL="${NW_QA_SKIP_LABEL:-skip-qa}"
28
28
  VALIDATED_LABEL="${NW_QA_VALIDATED_LABEL:-e2e-validated}"
29
+ READY_TO_MERGE_LABEL="${NW_PR_RESOLVER_READY_LABEL:-ready-to-merge}"
29
30
  QA_ARTIFACTS="${NW_QA_ARTIFACTS:-both}"
30
31
  QA_AUTO_INSTALL_PLAYWRIGHT="${NW_QA_AUTO_INSTALL_PLAYWRIGHT:-1}"
31
32
  SCRIPT_START_TIME=$(date +%s)
@@ -45,6 +46,7 @@ PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
45
46
  # NOTE: Lock file path must match qaLockPath() in src/utils/status-data.ts
46
47
  LOCK_FILE="/tmp/night-watch-qa-${PROJECT_RUNTIME_KEY}.lock"
47
48
  SCRIPT_TYPE="qa"
49
+ skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
48
50
 
49
51
  emit_result() {
50
52
  local status="${1:?status required}"
@@ -366,7 +368,7 @@ fi
366
368
  rotate_log
367
369
  log_separator
368
370
  log "RUN-START: qa invoked project=${PROJECT_DIR} provider=${PROVIDER_CMD} dry_run=${NW_DRY_RUN:-0}"
369
- log "CONFIG: max_runtime=${MAX_RUNTIME}s artifacts=${QA_ARTIFACTS} skip_label=${SKIP_LABEL} branch_patterns=${BRANCH_PATTERNS_RAW}"
371
+ log "CONFIG: max_runtime=${MAX_RUNTIME}s artifacts=${QA_ARTIFACTS} skip_label=${SKIP_LABEL} ready_label=${READY_TO_MERGE_LABEL} branch_patterns=${BRANCH_PATTERNS_RAW}"
370
372
 
371
373
  if ! acquire_lock "${LOCK_FILE}"; then
372
374
  emit_result "skip_locked"
@@ -431,6 +433,11 @@ while IFS=$'\t' read -r pr_number pr_branch pr_title pr_labels; do
431
433
  continue
432
434
  fi
433
435
 
436
+ if csv_has_label "${pr_labels:-}" "${READY_TO_MERGE_LABEL}"; then
437
+ log "SKIP-QA: PR #${pr_number} (${pr_branch}) is labeled ${READY_TO_MERGE_LABEL}"
438
+ continue
439
+ fi
440
+
434
441
  # Skip PRs with the skip label
435
442
  if echo "${pr_labels}" | grep -q "${SKIP_LABEL}"; then
436
443
  log "SKIP-QA: PR #${pr_number} (${pr_branch}) has '${SKIP_LABEL}' label"
@@ -35,6 +35,7 @@ source "${SCRIPT_DIR}/night-watch-helpers.sh"
35
35
  ensure_provider_on_path "${PROVIDER_CMD}" || true
36
36
  PROJECT_RUNTIME_KEY=$(project_runtime_key "${PROJECT_DIR}")
37
37
  LOCK_FILE="/tmp/night-watch-slicer-${PROJECT_RUNTIME_KEY}.lock"
38
+ SCRIPT_TYPE="slicer"
38
39
  PROVIDER_MODEL_DISPLAY=$(resolve_provider_model_display "${PROVIDER_CMD}" "${PROVIDER_LABEL}")
39
40
 
40
41
  emit_result() {
@@ -53,6 +54,8 @@ if ! validate_provider "${PROVIDER_CMD}"; then
53
54
  exit 1
54
55
  fi
55
56
 
57
+ skip_if_job_paused "${SCRIPT_TYPE}" "${PROJECT_DIR}"
58
+
56
59
  rotate_log
57
60
  log_separator
58
61
  log "RUN-START: slicer invoked project=${PROJECT_DIR} provider=${PROVIDER_CMD} dry_run=${NW_DRY_RUN:-0}"
@@ -55,9 +55,28 @@
55
55
  "audit": 10
56
56
  }
57
57
  },
58
- "jobProviders": {
59
- "reviewer": "g51claude"
58
+ "webhookTriggers": {
59
+ "enabled": false,
60
+ "secretEnv": "NIGHT_WATCH_WEBHOOK_SECRET",
61
+ "allowedJobIds": [
62
+ "executor",
63
+ "reviewer",
64
+ "pr-resolver",
65
+ "slicer",
66
+ "qa",
67
+ "audit",
68
+ "analytics",
69
+ "merger"
70
+ ],
71
+ "requireTimestamp": false,
72
+ "maxSkewSeconds": 300,
73
+ "github": {
74
+ "enabled": false,
75
+ "events": [],
76
+ "rules": []
77
+ }
60
78
  },
79
+ "jobProviders": {},
61
80
  "autoMerge": false,
62
81
  "autoMergeMethod": "squash",
63
82
  "fallbackOnRateLimit": true,
@@ -75,23 +94,5 @@
75
94
  "enabled": true,
76
95
  "schedule": "50 3 * * 1",
77
96
  "maxRuntime": 1800
78
- },
79
- "providerPresets": {
80
- "g51claude": {
81
- "name": "GLM-5.1 Claude",
82
- "command": "claude",
83
- "promptFlag": "-p",
84
- "autoApproveFlag": "--dangerously-skip-permissions",
85
- "modelFlag": "--model",
86
- "model": "glm-5.1",
87
- "envVars": {
88
- "ANTHROPIC_BASE_URL": "https://api.z.ai/api/anthropic",
89
- "API_TIMEOUT_MS": "3000000",
90
- "ANTHROPIC_DEFAULT_OPUS_MODEL": "glm-5.1",
91
- "ANTHROPIC_DEFAULT_SONNET_MODEL": "glm-5.1",
92
- "ANTHROPIC_AUTH_TOKEN": "efaeca456dab4ae69cb9ce1fa8d99a1a.KSTifNTWPS6NgC6L",
93
- "ANTHROPIC_API_KEY": "efaeca456dab4ae69cb9ce1fa8d99a1a.KSTifNTWPS6NgC6L"
94
- }
95
- }
96
97
  }
97
98
  }