@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.
Files changed (74) hide show
  1. package/README.md +49 -426
  2. package/dist/cli.js +9 -0
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/dashboard.d.ts +29 -0
  5. package/dist/commands/dashboard.d.ts.map +1 -0
  6. package/dist/commands/dashboard.js +297 -0
  7. package/dist/commands/dashboard.js.map +1 -0
  8. package/dist/commands/doctor.d.ts +16 -0
  9. package/dist/commands/doctor.d.ts.map +1 -0
  10. package/dist/commands/doctor.js +155 -0
  11. package/dist/commands/doctor.js.map +1 -0
  12. package/dist/commands/init.d.ts.map +1 -1
  13. package/dist/commands/init.js +23 -17
  14. package/dist/commands/init.js.map +1 -1
  15. package/dist/commands/install.d.ts +1 -1
  16. package/dist/commands/install.d.ts.map +1 -1
  17. package/dist/commands/install.js +2 -2
  18. package/dist/commands/install.js.map +1 -1
  19. package/dist/commands/logs.d.ts +1 -1
  20. package/dist/commands/logs.d.ts.map +1 -1
  21. package/dist/commands/logs.js +1 -1
  22. package/dist/commands/logs.js.map +1 -1
  23. package/dist/commands/prd.d.ts +24 -0
  24. package/dist/commands/prd.d.ts.map +1 -0
  25. package/dist/commands/prd.js +283 -0
  26. package/dist/commands/prd.js.map +1 -0
  27. package/dist/commands/review.d.ts +3 -3
  28. package/dist/commands/review.d.ts.map +1 -1
  29. package/dist/commands/review.js +26 -2
  30. package/dist/commands/review.js.map +1 -1
  31. package/dist/commands/run.d.ts +22 -3
  32. package/dist/commands/run.d.ts.map +1 -1
  33. package/dist/commands/run.js +57 -8
  34. package/dist/commands/run.js.map +1 -1
  35. package/dist/commands/status.d.ts +1 -1
  36. package/dist/commands/status.d.ts.map +1 -1
  37. package/dist/commands/status.js +21 -182
  38. package/dist/commands/status.js.map +1 -1
  39. package/dist/commands/uninstall.d.ts +1 -1
  40. package/dist/commands/uninstall.d.ts.map +1 -1
  41. package/dist/commands/uninstall.js +2 -2
  42. package/dist/commands/uninstall.js.map +1 -1
  43. package/dist/config.d.ts.map +1 -1
  44. package/dist/config.js +40 -1
  45. package/dist/config.js.map +1 -1
  46. package/dist/constants.d.ts +3 -1
  47. package/dist/constants.d.ts.map +1 -1
  48. package/dist/constants.js +3 -0
  49. package/dist/constants.js.map +1 -1
  50. package/dist/templates/prd-template.d.ts +11 -0
  51. package/dist/templates/prd-template.d.ts.map +1 -0
  52. package/dist/templates/prd-template.js +166 -0
  53. package/dist/templates/prd-template.js.map +1 -0
  54. package/dist/types.d.ts +14 -0
  55. package/dist/types.d.ts.map +1 -1
  56. package/dist/utils/crontab.js +1 -1
  57. package/dist/utils/crontab.js.map +1 -1
  58. package/dist/utils/github.d.ts +30 -0
  59. package/dist/utils/github.d.ts.map +1 -0
  60. package/dist/utils/github.js +104 -0
  61. package/dist/utils/github.js.map +1 -0
  62. package/dist/utils/notify.d.ts +63 -0
  63. package/dist/utils/notify.d.ts.map +1 -0
  64. package/dist/utils/notify.js +237 -0
  65. package/dist/utils/notify.js.map +1 -0
  66. package/dist/utils/status-data.d.ts +128 -0
  67. package/dist/utils/status-data.d.ts.map +1 -0
  68. package/dist/utils/status-data.js +403 -0
  69. package/dist/utils/status-data.js.map +1 -0
  70. package/package.json +13 -5
  71. package/scripts/night-watch-cron.sh +8 -1
  72. package/scripts/night-watch-helpers.sh +51 -0
  73. package/scripts/test-helpers.bats +77 -0
  74. 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;AAqCpC;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAiEpD;AAyLD;;GAEG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAqNlD;AAED,eAAe,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"}
@@ -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 { success, error as uiError, info, header, dim, label, step, createTable, } from '../utils/ui.js';
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
- * Add /logs/ to .gitignore if not already there
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
- fs.writeFileSync(gitignorePath, '# Night Watch logs\n/logs/\n');
204
- console.log(` Created: ${gitignorePath} (with /logs/ entry)`);
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
- // Check if /logs/ or logs/ already exists
209
- if (content.includes('/logs/') || /^logs\//m.test(content)) {
210
- console.log(` Skipped (exists): /logs/ in .gitignore`);
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
- // Append /logs/ to .gitignore
214
- const newContent = content.trimEnd() + '\n\n# Night Watch logs\n/logs/\n';
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 /logs/ entry)`);
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
- dim('Please run this command from the root of a git repository.');
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
- dim('Please run: gh auth login');
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
- dim(`Valid providers: ${VALID_PROVIDERS.join(', ')}`);
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
- dim('\nPlease install one of the following:');
288
- dim(' - Claude CLI: https://docs.anthropic.com/en/docs/claude-cli');
289
- dim(' - Codex CLI: https://github.com/openai/codex');
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
- dim(` Skipped (exists): ${configPath}`);
365
+ console.log(` Skipped (exists): ${configPath}`);
360
366
  }
361
367
  else {
362
368
  // Read and process config template