git-watchtower 1.7.1 → 1.8.1

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.
@@ -74,6 +74,7 @@ const { formatTimeAgo } = require('../src/utils/time');
74
74
  const { openInBrowser: openUrl } = require('../src/utils/browser');
75
75
  const { playSound: playSoundEffect } = require('../src/utils/sound');
76
76
  const { parseArgs: parseCliArgs, applyCliArgsToConfig: mergeCliArgs, getHelpText, PACKAGE_VERSION } = require('../src/cli/args');
77
+ const { checkForUpdate } = require('../src/utils/version-check');
77
78
  const { parseRemoteUrl, buildBranchUrl, detectPlatform, buildWebUrl, extractSessionUrl } = require('../src/git/remote');
78
79
  const { parseGitHubPr, parseGitLabMr, parseGitHubPrList, parseGitLabMrList, isBaseBranch } = require('../src/git/pr');
79
80
 
@@ -2953,6 +2954,15 @@ async function start() {
2953
2954
 
2954
2955
  // Initial render
2955
2956
  render();
2957
+
2958
+ // Check for newer version on npm (non-blocking, silent on failure)
2959
+ checkForUpdate().then((latestVersion) => {
2960
+ if (latestVersion) {
2961
+ store.setState({ updateAvailable: latestVersion });
2962
+ addLog(`New version available: ${latestVersion} \u2192 npm i -g git-watchtower`, 'update');
2963
+ render();
2964
+ }
2965
+ }).catch(() => {});
2956
2966
  }
2957
2967
 
2958
2968
  start().catch(err => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "git-watchtower",
3
- "version": "1.7.1",
3
+ "version": "1.8.1",
4
4
  "description": "Terminal-based Git branch monitor with activity sparklines and optional dev server with live reload",
5
5
  "main": "bin/git-watchtower.js",
6
6
  "bin": {
@@ -101,6 +101,7 @@
101
101
  * @property {number} maxLogEntries - Max activity log entries
102
102
  * @property {string} projectName - Project name
103
103
  * @property {number} clientCount - Connected SSE clients
104
+ * @property {string|null} updateAvailable - Latest version if update available, or null
104
105
  */
105
106
 
106
107
  /**
@@ -180,6 +181,9 @@ function getInitialState() {
180
181
  maxLogEntries: 10,
181
182
  projectName: '',
182
183
  clientCount: 0,
184
+
185
+ // Version check
186
+ updateAvailable: null,
183
187
  };
184
188
  }
185
189
 
@@ -75,6 +75,11 @@ function renderHeader(state, write) {
75
75
  badges += ' ' + ansi.bgRed + ansi.white + label + ansi.bgBlue + ansi.white;
76
76
  badgesVisibleLen += 1 + label.length;
77
77
  }
78
+ if (state.updateAvailable) {
79
+ const label = ` UPDATE v${state.updateAvailable} `;
80
+ badges += ' ' + ansi.bgMagenta + ansi.white + label + ansi.bgBlue + ansi.white;
81
+ badgesVisibleLen += 1 + label.length;
82
+ }
78
83
  write(badges);
79
84
 
80
85
  let modeLabel = '';
@@ -91,19 +96,19 @@ function renderHeader(state, write) {
91
96
  }
92
97
 
93
98
  let serverInfo = '';
94
- let serverInfoVisible = '';
99
+ let serverInfoVisibleLen = 0;
95
100
  if (state.serverMode === 'none') {
96
- serverInfoVisible = '';
101
+ serverInfoVisibleLen = 0;
97
102
  } else {
98
103
  const statusDot = state.serverRunning
99
104
  ? ansi.green + '\u25CF'
100
105
  : (state.serverCrashed ? ansi.red + '\u25CF' : ansi.gray + '\u25CB');
101
- serverInfoVisible = `localhost:${state.port} `;
106
+ serverInfoVisibleLen = 2 + `localhost:${state.port} `.length; // dot + space + "localhost:PORT "
102
107
  serverInfo = statusDot + ansi.white + ` localhost:${state.port} `;
103
108
  }
104
109
 
105
110
  const rightContent = `${modeBadge} ${serverInfo}${statusIcon}${ansi.bgBlue} ${soundIcon}${ansi.bgBlue} `;
106
- const rightVisibleLen = modeLabel.length + 1 + serverInfoVisible.length + 5;
111
+ const rightVisibleLen = modeLabel.length + 1 + serverInfoVisibleLen + 5;
107
112
 
108
113
  const usedSpace = leftVisibleLen + badgesVisibleLen + rightVisibleLen;
109
114
  const padding = Math.max(1, width - usedSpace);
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Version check utility - queries npm registry for latest version
3
+ * Uses only Node.js built-in modules (zero dependencies)
4
+ */
5
+
6
+ const https = require('https');
7
+ const { version: currentVersion } = require('../../package.json');
8
+
9
+ /**
10
+ * Compare two semver version strings
11
+ * @param {string} a - First version (e.g. '1.8.0')
12
+ * @param {string} b - Second version (e.g. '1.7.0')
13
+ * @returns {number} 1 if a > b, -1 if a < b, 0 if equal
14
+ */
15
+ function compareVersions(a, b) {
16
+ const pa = a.split('.').map(Number);
17
+ const pb = b.split('.').map(Number);
18
+ for (let i = 0; i < 3; i++) {
19
+ if (pa[i] > pb[i]) return 1;
20
+ if (pa[i] < pb[i]) return -1;
21
+ }
22
+ return 0;
23
+ }
24
+
25
+ /**
26
+ * Check npm registry for a newer version of git-watchtower
27
+ * @returns {Promise<string|null>} Latest version string if newer, or null
28
+ */
29
+ function checkForUpdate() {
30
+ return new Promise((resolve) => {
31
+ const req = https.get(
32
+ 'https://registry.npmjs.org/git-watchtower/latest',
33
+ { timeout: 5000 },
34
+ (res) => {
35
+ if (res.statusCode !== 200) {
36
+ res.resume();
37
+ resolve(null);
38
+ return;
39
+ }
40
+ let data = '';
41
+ res.on('data', (chunk) => { data += chunk; });
42
+ res.on('end', () => {
43
+ try {
44
+ const { version } = JSON.parse(data);
45
+ resolve(compareVersions(version, currentVersion) > 0 ? version : null);
46
+ } catch {
47
+ resolve(null);
48
+ }
49
+ });
50
+ }
51
+ );
52
+ req.on('error', () => resolve(null));
53
+ req.on('timeout', () => { req.destroy(); resolve(null); });
54
+ });
55
+ }
56
+
57
+ module.exports = { checkForUpdate, compareVersions };