@jonit-dev/night-watch-cli 1.1.4 → 1.2.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/README.md +49 -426
- package/dist/cli.js +9 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/dashboard.d.ts +29 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +297 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/doctor.d.ts +16 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +155 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +23 -17
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/install.d.ts +1 -1
- package/dist/commands/install.d.ts.map +1 -1
- package/dist/commands/install.js +2 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/logs.d.ts +1 -1
- package/dist/commands/logs.d.ts.map +1 -1
- package/dist/commands/logs.js +1 -1
- package/dist/commands/logs.js.map +1 -1
- package/dist/commands/prd.d.ts +24 -0
- package/dist/commands/prd.d.ts.map +1 -0
- package/dist/commands/prd.js +283 -0
- package/dist/commands/prd.js.map +1 -0
- package/dist/commands/review.d.ts +3 -3
- package/dist/commands/review.d.ts.map +1 -1
- package/dist/commands/review.js +26 -2
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/run.d.ts +22 -3
- package/dist/commands/run.d.ts.map +1 -1
- package/dist/commands/run.js +57 -8
- package/dist/commands/run.js.map +1 -1
- package/dist/commands/status.d.ts +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +21 -182
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/uninstall.d.ts +1 -1
- package/dist/commands/uninstall.d.ts.map +1 -1
- package/dist/commands/uninstall.js +2 -2
- package/dist/commands/uninstall.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +40 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +3 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -0
- package/dist/constants.js.map +1 -1
- package/dist/templates/prd-template.d.ts +11 -0
- package/dist/templates/prd-template.d.ts.map +1 -0
- package/dist/templates/prd-template.js +166 -0
- package/dist/templates/prd-template.js.map +1 -0
- package/dist/types.d.ts +14 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/crontab.js +1 -1
- package/dist/utils/crontab.js.map +1 -1
- package/dist/utils/github.d.ts +30 -0
- package/dist/utils/github.d.ts.map +1 -0
- package/dist/utils/github.js +104 -0
- package/dist/utils/github.js.map +1 -0
- package/dist/utils/notify.d.ts +63 -0
- package/dist/utils/notify.d.ts.map +1 -0
- package/dist/utils/notify.js +237 -0
- package/dist/utils/notify.js.map +1 -0
- package/dist/utils/status-data.d.ts +128 -0
- package/dist/utils/status-data.d.ts.map +1 -0
- package/dist/utils/status-data.js +403 -0
- package/dist/utils/status-data.js.map +1 -0
- package/package.json +13 -5
- package/scripts/night-watch-cron.sh +8 -1
- package/scripts/night-watch-helpers.sh +51 -0
- package/scripts/test-helpers.bats +77 -0
- package/templates/prd.md +26 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dashboard command for Night Watch CLI
|
|
3
|
+
* Renders a full-screen TUI with 4 panes using blessed
|
|
4
|
+
*/
|
|
5
|
+
import blessed from "blessed";
|
|
6
|
+
import { loadConfig } from "../config.js";
|
|
7
|
+
import { fetchStatusSnapshot, getLastLogLines, } from "../utils/status-data.js";
|
|
8
|
+
import * as fs from "fs";
|
|
9
|
+
/**
|
|
10
|
+
* Render the PRD Queue pane content from snapshot data.
|
|
11
|
+
* Each PRD gets a colored status indicator and optional dependency list.
|
|
12
|
+
*/
|
|
13
|
+
export function renderPrdPane(prds) {
|
|
14
|
+
if (prds.length === 0) {
|
|
15
|
+
return "No PRD files found";
|
|
16
|
+
}
|
|
17
|
+
const lines = [];
|
|
18
|
+
for (const prd of prds) {
|
|
19
|
+
let indicator;
|
|
20
|
+
switch (prd.status) {
|
|
21
|
+
case "ready":
|
|
22
|
+
indicator = "{green-fg}\u25cf{/green-fg}";
|
|
23
|
+
break;
|
|
24
|
+
case "blocked":
|
|
25
|
+
indicator = "{yellow-fg}\u25cf{/yellow-fg}";
|
|
26
|
+
break;
|
|
27
|
+
case "in-progress":
|
|
28
|
+
indicator = "{cyan-fg}\u25cf{/cyan-fg}";
|
|
29
|
+
break;
|
|
30
|
+
case "done":
|
|
31
|
+
indicator = "{#888888-fg}\u25cf{/#888888-fg}";
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
let line = `${indicator} ${prd.name}`;
|
|
35
|
+
if (prd.dependencies.length > 0) {
|
|
36
|
+
line += ` (deps: ${prd.dependencies.join(", ")})`;
|
|
37
|
+
}
|
|
38
|
+
lines.push(line);
|
|
39
|
+
}
|
|
40
|
+
return lines.join("\n");
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Render the Process Status pane content from snapshot data.
|
|
44
|
+
*/
|
|
45
|
+
export function renderProcessPane(processes) {
|
|
46
|
+
const lines = [];
|
|
47
|
+
for (const proc of processes) {
|
|
48
|
+
if (proc.running) {
|
|
49
|
+
lines.push(`{green-fg}\u25cf{/green-fg} ${proc.name}: Running (PID: ${proc.pid})`);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
lines.push(`{white-fg}\u25cb{/white-fg} ${proc.name}: Not running`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return lines.join("\n");
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Render the PR Status pane content from snapshot data.
|
|
59
|
+
*/
|
|
60
|
+
export function renderPrPane(prs) {
|
|
61
|
+
if (prs.length === 0) {
|
|
62
|
+
return "No matching pull requests";
|
|
63
|
+
}
|
|
64
|
+
const lines = [];
|
|
65
|
+
for (const pr of prs) {
|
|
66
|
+
let ciIndicator;
|
|
67
|
+
switch (pr.ciStatus) {
|
|
68
|
+
case "pass":
|
|
69
|
+
ciIndicator = "{green-fg}\u25cf{/green-fg}";
|
|
70
|
+
break;
|
|
71
|
+
case "fail":
|
|
72
|
+
ciIndicator = "{red-fg}\u25cf{/red-fg}";
|
|
73
|
+
break;
|
|
74
|
+
case "pending":
|
|
75
|
+
ciIndicator = "{yellow-fg}\u25cf{/yellow-fg}";
|
|
76
|
+
break;
|
|
77
|
+
default:
|
|
78
|
+
ciIndicator = "{white-fg}\u25cf{/white-fg}";
|
|
79
|
+
break;
|
|
80
|
+
}
|
|
81
|
+
const reviewLabel = pr.reviewScore !== null ? ` [Review: ${pr.reviewScore}%]` : "";
|
|
82
|
+
lines.push(`${ciIndicator} #${pr.number} ${pr.title}${reviewLabel}`);
|
|
83
|
+
lines.push(` ${pr.branch}`);
|
|
84
|
+
}
|
|
85
|
+
return lines.join("\n");
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Render the Log Tail pane content from snapshot data.
|
|
89
|
+
* Shows the last 20 lines from the most recent log file.
|
|
90
|
+
*/
|
|
91
|
+
export function renderLogPane(projectDir, logs) {
|
|
92
|
+
// Find the most recent log file that exists
|
|
93
|
+
const existingLogs = logs.filter((l) => l.exists);
|
|
94
|
+
if (existingLogs.length === 0) {
|
|
95
|
+
return "No log files found";
|
|
96
|
+
}
|
|
97
|
+
// Pick the log with the most recent modification time
|
|
98
|
+
let newestLog = existingLogs[0];
|
|
99
|
+
let newestMtime = 0;
|
|
100
|
+
for (const log of existingLogs) {
|
|
101
|
+
try {
|
|
102
|
+
const stat = fs.statSync(log.path);
|
|
103
|
+
if (stat.mtimeMs > newestMtime) {
|
|
104
|
+
newestMtime = stat.mtimeMs;
|
|
105
|
+
newestLog = log;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// Ignore stat errors
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
// Read the last 20 lines directly for the dashboard
|
|
113
|
+
const lines = getLastLogLines(newestLog.path, 20);
|
|
114
|
+
if (lines.length === 0) {
|
|
115
|
+
return `${newestLog.name}.log: (empty)`;
|
|
116
|
+
}
|
|
117
|
+
return `--- ${newestLog.name}.log ---\n${lines.join("\n")}`;
|
|
118
|
+
}
|
|
119
|
+
export function dashboardCommand(program) {
|
|
120
|
+
program
|
|
121
|
+
.command("dashboard")
|
|
122
|
+
.description("Live terminal dashboard")
|
|
123
|
+
.option("--interval <seconds>", "Refresh interval in seconds", "10")
|
|
124
|
+
.action(async (options) => {
|
|
125
|
+
const projectDir = process.cwd();
|
|
126
|
+
const config = loadConfig(projectDir);
|
|
127
|
+
// Create blessed screen
|
|
128
|
+
const screen = blessed.screen({
|
|
129
|
+
smartCSR: true,
|
|
130
|
+
title: "Night Watch Dashboard",
|
|
131
|
+
fullUnicode: true,
|
|
132
|
+
});
|
|
133
|
+
// Create header (full width, 3 rows)
|
|
134
|
+
const headerBox = blessed.box({
|
|
135
|
+
top: 0,
|
|
136
|
+
left: 0,
|
|
137
|
+
width: "100%",
|
|
138
|
+
height: 3,
|
|
139
|
+
content: "{center}Night Watch Dashboard{/center}",
|
|
140
|
+
tags: true,
|
|
141
|
+
style: { fg: "cyan", bold: true },
|
|
142
|
+
});
|
|
143
|
+
// Create 4 panes:
|
|
144
|
+
// Top-left: PRD Queue (50% width, 40% height)
|
|
145
|
+
const prdPane = blessed.box({
|
|
146
|
+
top: 3,
|
|
147
|
+
left: 0,
|
|
148
|
+
width: "50%",
|
|
149
|
+
height: "40%",
|
|
150
|
+
label: "[ PRD Queue ]",
|
|
151
|
+
border: { type: "line" },
|
|
152
|
+
scrollable: true,
|
|
153
|
+
alwaysScroll: true,
|
|
154
|
+
scrollbar: { style: { bg: "blue" } },
|
|
155
|
+
style: { border: { fg: "white" } },
|
|
156
|
+
tags: true,
|
|
157
|
+
content: "Loading...",
|
|
158
|
+
});
|
|
159
|
+
// Top-right: Process Status (50% width, 40% height)
|
|
160
|
+
const processPane = blessed.box({
|
|
161
|
+
top: 3,
|
|
162
|
+
left: "50%",
|
|
163
|
+
width: "50%",
|
|
164
|
+
height: "40%",
|
|
165
|
+
label: "[ Processes ]",
|
|
166
|
+
border: { type: "line" },
|
|
167
|
+
scrollable: true,
|
|
168
|
+
alwaysScroll: true,
|
|
169
|
+
scrollbar: { style: { bg: "blue" } },
|
|
170
|
+
style: { border: { fg: "white" } },
|
|
171
|
+
tags: true,
|
|
172
|
+
content: "Loading...",
|
|
173
|
+
});
|
|
174
|
+
// Bottom-left: PR Status (50% width, 30% height)
|
|
175
|
+
const prPane = blessed.box({
|
|
176
|
+
top: "43%",
|
|
177
|
+
left: 0,
|
|
178
|
+
width: "50%",
|
|
179
|
+
height: "30%",
|
|
180
|
+
label: "[ Pull Requests ]",
|
|
181
|
+
border: { type: "line" },
|
|
182
|
+
scrollable: true,
|
|
183
|
+
alwaysScroll: true,
|
|
184
|
+
scrollbar: { style: { bg: "blue" } },
|
|
185
|
+
style: { border: { fg: "white" } },
|
|
186
|
+
tags: true,
|
|
187
|
+
content: "Loading...",
|
|
188
|
+
});
|
|
189
|
+
// Bottom-right: Log Tail (50% width, 30% height)
|
|
190
|
+
const logPane = blessed.box({
|
|
191
|
+
top: "43%",
|
|
192
|
+
left: "50%",
|
|
193
|
+
width: "50%",
|
|
194
|
+
height: "30%",
|
|
195
|
+
label: "[ Logs ]",
|
|
196
|
+
border: { type: "line" },
|
|
197
|
+
scrollable: true,
|
|
198
|
+
alwaysScroll: true,
|
|
199
|
+
scrollbar: { style: { bg: "blue" } },
|
|
200
|
+
style: { border: { fg: "white" } },
|
|
201
|
+
tags: true,
|
|
202
|
+
content: "Loading...",
|
|
203
|
+
});
|
|
204
|
+
// Footer (full width, 1 row)
|
|
205
|
+
const footerBox = blessed.box({
|
|
206
|
+
bottom: 0,
|
|
207
|
+
left: 0,
|
|
208
|
+
width: "100%",
|
|
209
|
+
height: 1,
|
|
210
|
+
content: " q:Quit Tab:Focus r:Refresh \u2191\u2193:Scroll",
|
|
211
|
+
style: { fg: "white", bg: "blue" },
|
|
212
|
+
});
|
|
213
|
+
// Append all elements
|
|
214
|
+
screen.append(headerBox);
|
|
215
|
+
screen.append(prdPane);
|
|
216
|
+
screen.append(processPane);
|
|
217
|
+
screen.append(prPane);
|
|
218
|
+
screen.append(logPane);
|
|
219
|
+
screen.append(footerBox);
|
|
220
|
+
// Pane navigation
|
|
221
|
+
const panes = [prdPane, processPane, prPane, logPane];
|
|
222
|
+
let focusedPaneIndex = 0;
|
|
223
|
+
const updatePaneFocus = () => {
|
|
224
|
+
panes.forEach((pane, index) => {
|
|
225
|
+
if (index === focusedPaneIndex) {
|
|
226
|
+
pane.style.border = { fg: "cyan" };
|
|
227
|
+
pane.focus();
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
pane.style.border = { fg: "white" };
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
screen.render();
|
|
234
|
+
};
|
|
235
|
+
// Fetch initial data
|
|
236
|
+
let snapshot = fetchStatusSnapshot(projectDir, config);
|
|
237
|
+
// Render all panes from snapshot data
|
|
238
|
+
const renderPanes = (snap) => {
|
|
239
|
+
prdPane.setContent(renderPrdPane(snap.prds));
|
|
240
|
+
processPane.setContent(renderProcessPane(snap.processes));
|
|
241
|
+
prPane.setContent(renderPrPane(snap.prs));
|
|
242
|
+
logPane.setContent(renderLogPane(snap.projectDir, snap.logs));
|
|
243
|
+
// Auto-scroll log pane to bottom
|
|
244
|
+
logPane.setScrollPerc(100);
|
|
245
|
+
};
|
|
246
|
+
// Auto-refresh setup
|
|
247
|
+
const intervalSeconds = parseInt(options.interval, 10) || 10;
|
|
248
|
+
let countdown = intervalSeconds;
|
|
249
|
+
const updateHeader = () => {
|
|
250
|
+
headerBox.setContent(`{center}Night Watch: ${snapshot.projectName} | Provider: ${config.provider} | Last refresh: ${snapshot.timestamp.toLocaleTimeString()} | Next: ${countdown}s{/center}`);
|
|
251
|
+
};
|
|
252
|
+
const refreshData = () => {
|
|
253
|
+
snapshot = fetchStatusSnapshot(projectDir, config);
|
|
254
|
+
renderPanes(snapshot);
|
|
255
|
+
countdown = intervalSeconds;
|
|
256
|
+
updateHeader();
|
|
257
|
+
screen.render();
|
|
258
|
+
};
|
|
259
|
+
const timer = setInterval(() => {
|
|
260
|
+
countdown--;
|
|
261
|
+
updateHeader();
|
|
262
|
+
screen.render();
|
|
263
|
+
if (countdown <= 0) {
|
|
264
|
+
refreshData();
|
|
265
|
+
}
|
|
266
|
+
}, 1000);
|
|
267
|
+
// Initial render
|
|
268
|
+
renderPanes(snapshot);
|
|
269
|
+
updateHeader();
|
|
270
|
+
// Wire keyboard handlers
|
|
271
|
+
screen.key(["q", "escape"], () => {
|
|
272
|
+
clearInterval(timer);
|
|
273
|
+
screen.destroy();
|
|
274
|
+
process.exit(0);
|
|
275
|
+
});
|
|
276
|
+
screen.key(["r"], () => {
|
|
277
|
+
refreshData();
|
|
278
|
+
});
|
|
279
|
+
screen.key(["tab"], () => {
|
|
280
|
+
focusedPaneIndex = (focusedPaneIndex + 1) % panes.length;
|
|
281
|
+
updatePaneFocus();
|
|
282
|
+
});
|
|
283
|
+
screen.key(["up"], () => {
|
|
284
|
+
panes[focusedPaneIndex].scroll(-1);
|
|
285
|
+
screen.render();
|
|
286
|
+
});
|
|
287
|
+
screen.key(["down"], () => {
|
|
288
|
+
panes[focusedPaneIndex].scroll(1);
|
|
289
|
+
screen.render();
|
|
290
|
+
});
|
|
291
|
+
// Initial focus highlight
|
|
292
|
+
updatePaneFocus();
|
|
293
|
+
// Render
|
|
294
|
+
screen.render();
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
//# sourceMappingURL=dashboard.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../src/commands/dashboard.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor command for Night Watch CLI
|
|
3
|
+
* Validates webhook configuration and checks system health
|
|
4
|
+
*/
|
|
5
|
+
import { Command } from "commander";
|
|
6
|
+
import { IWebhookConfig } from "../types.js";
|
|
7
|
+
/**
|
|
8
|
+
* Validate a single webhook configuration and return a list of issues.
|
|
9
|
+
* Returns an empty array if the webhook is valid.
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateWebhook(webhook: IWebhookConfig): string[];
|
|
12
|
+
/**
|
|
13
|
+
* Register the doctor command on the program
|
|
14
|
+
*/
|
|
15
|
+
export declare function doctorCommand(program: Command): void;
|
|
16
|
+
//# sourceMappingURL=doctor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAKpC,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAU7C;;;GAGG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,cAAc,GAAG,MAAM,EAAE,CAmDjE;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAiGpD"}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Doctor command for Night Watch CLI
|
|
3
|
+
* Validates webhook configuration and checks system health
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from "fs";
|
|
6
|
+
import * as path from "path";
|
|
7
|
+
import { execSync } from "child_process";
|
|
8
|
+
import { loadConfig } from "../config.js";
|
|
9
|
+
import { header, info, step, success, error as uiError, warn, } from "../utils/ui.js";
|
|
10
|
+
/**
|
|
11
|
+
* Validate a single webhook configuration and return a list of issues.
|
|
12
|
+
* Returns an empty array if the webhook is valid.
|
|
13
|
+
*/
|
|
14
|
+
export function validateWebhook(webhook) {
|
|
15
|
+
const issues = [];
|
|
16
|
+
// Validate events
|
|
17
|
+
if (!webhook.events || webhook.events.length === 0) {
|
|
18
|
+
issues.push("No events configured");
|
|
19
|
+
}
|
|
20
|
+
else {
|
|
21
|
+
const validEvents = [
|
|
22
|
+
"run_succeeded",
|
|
23
|
+
"run_failed",
|
|
24
|
+
"run_timeout",
|
|
25
|
+
"review_completed",
|
|
26
|
+
];
|
|
27
|
+
for (const event of webhook.events) {
|
|
28
|
+
if (!validEvents.includes(event)) {
|
|
29
|
+
issues.push(`Invalid event: ${event}`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Platform-specific validation
|
|
34
|
+
switch (webhook.type) {
|
|
35
|
+
case "slack":
|
|
36
|
+
if (!webhook.url) {
|
|
37
|
+
issues.push("Missing URL");
|
|
38
|
+
}
|
|
39
|
+
else if (!webhook.url.startsWith("https://hooks.slack.com/")) {
|
|
40
|
+
issues.push("URL should start with https://hooks.slack.com/");
|
|
41
|
+
}
|
|
42
|
+
break;
|
|
43
|
+
case "discord":
|
|
44
|
+
if (!webhook.url) {
|
|
45
|
+
issues.push("Missing URL");
|
|
46
|
+
}
|
|
47
|
+
else if (!webhook.url.startsWith("https://discord.com/api/webhooks/")) {
|
|
48
|
+
issues.push("URL should start with https://discord.com/api/webhooks/");
|
|
49
|
+
}
|
|
50
|
+
break;
|
|
51
|
+
case "telegram":
|
|
52
|
+
if (!webhook.botToken) {
|
|
53
|
+
issues.push("Missing botToken");
|
|
54
|
+
}
|
|
55
|
+
if (!webhook.chatId) {
|
|
56
|
+
issues.push("Missing chatId");
|
|
57
|
+
}
|
|
58
|
+
break;
|
|
59
|
+
default:
|
|
60
|
+
issues.push(`Unknown webhook type: ${webhook.type}`);
|
|
61
|
+
}
|
|
62
|
+
return issues;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Register the doctor command on the program
|
|
66
|
+
*/
|
|
67
|
+
export function doctorCommand(program) {
|
|
68
|
+
program
|
|
69
|
+
.command("doctor")
|
|
70
|
+
.description("Check Night Watch configuration and system health")
|
|
71
|
+
.action(async () => {
|
|
72
|
+
const projectDir = process.cwd();
|
|
73
|
+
let hasErrors = false;
|
|
74
|
+
header("Night Watch Doctor");
|
|
75
|
+
// Check 1: Git repository
|
|
76
|
+
step(1, 5, "Checking git repository...");
|
|
77
|
+
try {
|
|
78
|
+
execSync("git rev-parse --is-inside-work-tree", {
|
|
79
|
+
cwd: projectDir,
|
|
80
|
+
stdio: "pipe",
|
|
81
|
+
});
|
|
82
|
+
success("Git repository detected");
|
|
83
|
+
}
|
|
84
|
+
catch {
|
|
85
|
+
uiError("Not a git repository");
|
|
86
|
+
hasErrors = true;
|
|
87
|
+
}
|
|
88
|
+
// Check 2: GitHub CLI
|
|
89
|
+
step(2, 5, "Checking GitHub CLI...");
|
|
90
|
+
try {
|
|
91
|
+
execSync("gh auth status", { stdio: "pipe" });
|
|
92
|
+
success("GitHub CLI authenticated");
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
warn("GitHub CLI not authenticated (run: gh auth login)");
|
|
96
|
+
}
|
|
97
|
+
// Check 3: Provider CLI
|
|
98
|
+
step(3, 5, "Checking provider CLI...");
|
|
99
|
+
const config = loadConfig(projectDir);
|
|
100
|
+
try {
|
|
101
|
+
execSync(`which ${config.provider}`, { stdio: "pipe" });
|
|
102
|
+
success(`Provider CLI found: ${config.provider}`);
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
uiError(`Provider CLI not found: ${config.provider}`);
|
|
106
|
+
hasErrors = true;
|
|
107
|
+
}
|
|
108
|
+
// Check 4: PRD directory
|
|
109
|
+
step(4, 5, "Checking PRD directory...");
|
|
110
|
+
const prdDir = path.join(projectDir, config.prdDir);
|
|
111
|
+
if (fs.existsSync(prdDir)) {
|
|
112
|
+
const prds = fs
|
|
113
|
+
.readdirSync(prdDir)
|
|
114
|
+
.filter((f) => f.endsWith(".md") && f !== "NIGHT-WATCH-SUMMARY.md");
|
|
115
|
+
success(`PRD directory found (${prds.length} PRDs)`);
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
warn(`PRD directory not found: ${config.prdDir} (run: night-watch init)`);
|
|
119
|
+
}
|
|
120
|
+
// Check 5: Webhook configuration
|
|
121
|
+
step(5, 5, "Checking webhook configuration...");
|
|
122
|
+
if (!config.notifications ||
|
|
123
|
+
config.notifications.webhooks.length === 0) {
|
|
124
|
+
info("No webhooks configured (optional)");
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
let webhookErrors = 0;
|
|
128
|
+
for (const webhook of config.notifications.webhooks) {
|
|
129
|
+
const issues = validateWebhook(webhook);
|
|
130
|
+
if (issues.length === 0) {
|
|
131
|
+
success(`${webhook.type} webhook: OK`);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
for (const issue of issues) {
|
|
135
|
+
warn(`${webhook.type} webhook: ${issue}`);
|
|
136
|
+
}
|
|
137
|
+
webhookErrors++;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (webhookErrors === 0) {
|
|
141
|
+
success(`All ${config.notifications.webhooks.length} webhook(s) valid`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Summary
|
|
145
|
+
console.log();
|
|
146
|
+
if (hasErrors) {
|
|
147
|
+
uiError("Issues found — fix errors above before running Night Watch");
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
success("All checks passed");
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
//# sourceMappingURL=doctor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"doctor.js","sourceRoot":"","sources":["../../src/commands/doctor.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../../src/commands/init.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAoCpC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAiEpD;AAgMD;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqNlD;AAED,eAAe,WAAW,CAAC"}
|
package/dist/commands/init.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
import { dirname, join } from 'path';
|
|
7
7
|
import * as readline from 'readline';
|
|
8
8
|
import { CONFIG_FILE_NAME, DEFAULT_PRD_DIR, LOG_DIR, VALID_PROVIDERS, } from '../constants.js';
|
|
9
|
-
import {
|
|
9
|
+
import { createTable, header, info, label, step, success, error as uiError, } from '../utils/ui.js';
|
|
10
10
|
// Get templates directory path
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = dirname(__filename);
|
|
@@ -195,25 +195,31 @@ function processTemplate(templateName, targetPath, replacements, force) {
|
|
|
195
195
|
return true;
|
|
196
196
|
}
|
|
197
197
|
/**
|
|
198
|
-
*
|
|
198
|
+
* Ensure Night Watch entries are in .gitignore
|
|
199
199
|
*/
|
|
200
200
|
function addToGitignore(cwd) {
|
|
201
201
|
const gitignorePath = path.join(cwd, '.gitignore');
|
|
202
|
+
const entries = [
|
|
203
|
+
{ pattern: '/logs/', label: '/logs/', check: (c) => c.includes('/logs/') || /^logs\//m.test(c) },
|
|
204
|
+
{ pattern: CONFIG_FILE_NAME, label: CONFIG_FILE_NAME, check: (c) => c.includes(CONFIG_FILE_NAME) },
|
|
205
|
+
{ pattern: '*.claim', label: '*.claim', check: (c) => c.includes('*.claim') },
|
|
206
|
+
];
|
|
202
207
|
if (!fs.existsSync(gitignorePath)) {
|
|
203
|
-
|
|
204
|
-
|
|
208
|
+
const lines = ['# Night Watch', ...entries.map(e => e.pattern), ''];
|
|
209
|
+
fs.writeFileSync(gitignorePath, lines.join('\n'));
|
|
210
|
+
console.log(` Created: ${gitignorePath} (with Night Watch entries)`);
|
|
205
211
|
return;
|
|
206
212
|
}
|
|
207
213
|
const content = fs.readFileSync(gitignorePath, 'utf-8');
|
|
208
|
-
|
|
209
|
-
if (
|
|
210
|
-
console.log(` Skipped (exists):
|
|
214
|
+
const missing = entries.filter(e => !e.check(content));
|
|
215
|
+
if (missing.length === 0) {
|
|
216
|
+
console.log(` Skipped (exists): Night Watch entries in .gitignore`);
|
|
211
217
|
return;
|
|
212
218
|
}
|
|
213
|
-
|
|
214
|
-
const newContent = content.trimEnd() + '\n\n# Night Watch
|
|
219
|
+
const additions = missing.map(e => e.pattern).join('\n');
|
|
220
|
+
const newContent = content.trimEnd() + '\n\n# Night Watch\n' + additions + '\n';
|
|
215
221
|
fs.writeFileSync(gitignorePath, newContent);
|
|
216
|
-
console.log(` Updated: ${gitignorePath} (added
|
|
222
|
+
console.log(` Updated: ${gitignorePath} (added ${missing.map(e => e.label).join(', ')})`);
|
|
217
223
|
}
|
|
218
224
|
/**
|
|
219
225
|
* Create NIGHT-WATCH-SUMMARY.md template if it doesn't exist
|
|
@@ -254,7 +260,7 @@ export function initCommand(program) {
|
|
|
254
260
|
step(1, 9, 'Checking git repository...');
|
|
255
261
|
if (!isGitRepo(cwd)) {
|
|
256
262
|
uiError('Current directory is not a git repository.');
|
|
257
|
-
|
|
263
|
+
console.log('Please run this command from the root of a git repository.');
|
|
258
264
|
process.exit(1);
|
|
259
265
|
}
|
|
260
266
|
success('Git repository found');
|
|
@@ -262,7 +268,7 @@ export function initCommand(program) {
|
|
|
262
268
|
step(2, 9, 'Checking GitHub CLI (gh)...');
|
|
263
269
|
if (!isGhAuthenticated()) {
|
|
264
270
|
uiError('GitHub CLI (gh) is not authenticated.');
|
|
265
|
-
|
|
271
|
+
console.log('Please run: gh auth login');
|
|
266
272
|
process.exit(1);
|
|
267
273
|
}
|
|
268
274
|
success('GitHub CLI is authenticated');
|
|
@@ -273,7 +279,7 @@ export function initCommand(program) {
|
|
|
273
279
|
// Validate provider flag
|
|
274
280
|
if (!VALID_PROVIDERS.includes(options.provider)) {
|
|
275
281
|
uiError(`Invalid provider "${options.provider}".`);
|
|
276
|
-
|
|
282
|
+
console.log(`Valid providers: ${VALID_PROVIDERS.join(', ')}`);
|
|
277
283
|
process.exit(1);
|
|
278
284
|
}
|
|
279
285
|
selectedProvider = options.provider;
|
|
@@ -284,9 +290,9 @@ export function initCommand(program) {
|
|
|
284
290
|
const detectedProviders = detectProviders();
|
|
285
291
|
if (detectedProviders.length === 0) {
|
|
286
292
|
uiError('No AI provider CLI found.');
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
293
|
+
console.log('\nPlease install one of the following:');
|
|
294
|
+
console.log(' - Claude CLI: https://docs.anthropic.com/en/docs/claude-cli');
|
|
295
|
+
console.log(' - Codex CLI: https://github.com/openai/codex');
|
|
290
296
|
process.exit(1);
|
|
291
297
|
}
|
|
292
298
|
else if (detectedProviders.length === 1) {
|
|
@@ -356,7 +362,7 @@ export function initCommand(program) {
|
|
|
356
362
|
step(8, 9, 'Creating configuration file...');
|
|
357
363
|
const configPath = path.join(cwd, CONFIG_FILE_NAME);
|
|
358
364
|
if (fs.existsSync(configPath) && !force) {
|
|
359
|
-
|
|
365
|
+
console.log(` Skipped (exists): ${configPath}`);
|
|
360
366
|
}
|
|
361
367
|
else {
|
|
362
368
|
// Read and process config template
|